/mandos/release

To get this branch, use:
bzr branch http://bzr.recompile.se/loggerhead/mandos/release

« back to all changes in this revision

Viewing changes to dracut-module/password-agent.c

Merge from release branch.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/* -*- coding: utf-8; lexical-binding: t -*- */
2
 
/*
3
 
 * Mandos password agent - Simple password agent to run Mandos client
4
 
 *
5
 
 * Copyright © 2019-2022 Teddy Hogeborn
6
 
 * Copyright © 2019-2022 Björn Påhlsson
7
 
 * 
8
 
 * This file is part of Mandos.
9
 
 * 
10
 
 * Mandos is free software: you can redistribute it and/or modify it
11
 
 * under the terms of the GNU General Public License as published by
12
 
 * the Free Software Foundation, either version 3 of the License, or
13
 
 * (at your option) any later version.
14
 
 * 
15
 
 * Mandos is distributed in the hope that it will be useful, but
16
 
 * WITHOUT ANY WARRANTY; without even the implied warranty of
17
 
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
18
 
 * General Public License for more details.
19
 
 * 
20
 
 * You should have received a copy of the GNU General Public License
21
 
 * along with Mandos.  If not, see <http://www.gnu.org/licenses/>.
22
 
 * 
23
 
 * Contact the authors at <mandos@recompile.se>.
24
 
 */
25
 
 
26
 
#define _GNU_SOURCE             /* pipe2(), O_CLOEXEC, setresgid(),
27
 
                                   setresuid(), asprintf(), getline(),
28
 
                                   basename() */
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(),
34
 
                                   getpid() */
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(),
55
 
                                   mkostemp() */
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,
65
 
                                   EINVAL */
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(),
72
 
                                   argz_add() */
73
 
#include <sys/epoll.h>          /* epoll_create1(), EPOLL_CLOEXEC,
74
 
                                   epoll_ctl(), EPOLL_CTL_ADD,
75
 
                                   struct epoll_event, EPOLLIN,
76
 
                                   EPOLLRDHUP, EPOLLOUT,
77
 
                                   epoll_pwait() */
78
 
#include <time.h>               /* struct timespec, clock_gettime(),
79
 
                                   CLOCK_MONOTONIC */
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(),
84
 
                                   ARGP_NO_EXIT */
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(),
92
 
                                   WEXITSTATUS() */
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(),
131
 
                                   socketpair() */
132
 
#include <glob.h>               /* globfree(), glob_t, glob(),
133
 
                                   GLOB_ERR, GLOB_NOSORT, GLOB_MARK,
134
 
                                   GLOB_ABORTED, GLOB_NOMATCH,
135
 
                                   GLOB_NOSPACE */
136
 
 
137
 
/* End of includes */
138
 
 
139
 
/* Start of declarations of private types and functions */
140
 
 
141
 
/* microseconds of CLOCK_MONOTONIC absolute time; 0 means unset */
142
 
typedef uintmax_t mono_microsecs;
143
 
 
144
 
/* "task_queue" - A queue of tasks to be run */
145
 
typedef struct {
146
 
  struct task_struct *tasks;    /* Tasks in this queue */
147
 
  size_t length;                /* Number of tasks */
148
 
  /* Memory allocated for "tasks", in bytes */
149
 
  size_t allocated;             
150
 
  /* Time when this queue should be run, at the latest */
151
 
  mono_microsecs next_run;
152
 
} __attribute__((designated_init)) task_queue;
153
 
 
154
 
/* "task_func" - A function type for task functions
155
 
 
156
 
   I.e. functions for the code which runs when a task is run, all have
157
 
   this type */
158
 
typedef void (task_func) (const struct task_struct,
159
 
                          task_queue *const)
160
 
  __attribute__((nonnull));
161
 
 
162
 
/* "buffer" - A data buffer for a growing array of bytes
163
 
 
164
 
   Used for the "password" variable */
165
 
typedef struct {
166
 
  char *data;
167
 
  size_t length;
168
 
  size_t allocated;
169
 
} __attribute__((designated_init)) buffer;
170
 
 
171
 
/* "string_set" - A set type which can contain strings
172
 
 
173
 
   Used by the "cancelled_filenames" variable */
174
 
typedef struct {
175
 
  char *argz;                   /* Do not access these except in */
176
 
  size_t argz_len;              /* the string_set_* functions */
177
 
} __attribute__((designated_init)) string_set;
178
 
 
179
 
/* "task_context" - local variables for tasks
180
 
 
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.
184
 
 
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;
205
 
 
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,
268
 
                            const char *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 *);
286
 
 
287
 
/* End of declarations of private types and functions */
288
 
 
289
 
/* Start of "main" section; this section LACKS TESTS!
290
 
 
291
 
   Code here should be as simple as possible. */
292
 
 
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>";
296
 
 
297
 
int main(int argc, char *argv[]){
298
 
 
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 */
305
 
    }
306
 
    return EXIT_FAILURE;        /* Some test(s) failed */
307
 
  }
308
 
 
309
 
  __attribute__((cleanup(cleanup_string)))
310
 
    char *agent_directory = NULL;
311
 
 
312
 
  __attribute__((cleanup(cleanup_string)))
313
 
    char *helper_directory = NULL;
314
 
 
315
 
  uid_t user = 0;
316
 
  gid_t group = 0;
317
 
 
318
 
  __attribute__((cleanup(cleanup_string)))
319
 
    char *mandos_argz = NULL;
320
 
  size_t mandos_argz_length = 0;
321
 
 
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");
329
 
  }
330
 
 
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 };
336
 
 
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()");
342
 
    }
343
 
  }
344
 
  if(helper_directory == NULL){
345
 
    helper_directory = strdup(default_helper_directory);
346
 
    if(helper_directory == NULL){
347
 
      error(EX_OSERR, errno, "Failed strdup()");
348
 
    }
349
 
  }
350
 
  if(user == 0){
351
 
    user = 65534;               /* nobody */
352
 
  }
353
 
  if(group == 0){
354
 
    group = 65534;              /* nogroup */
355
 
  }
356
 
  /* If parse_opt did not create an argz vector, create one with
357
 
     default values */
358
 
  if(mandos_argz == NULL){
359
 
#ifdef __GNUC__
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"
365
 
#endif
366
 
    errno = argz_create((char *const *)default_argv, &mandos_argz,
367
 
                        &mandos_argz_length);
368
 
#ifdef __GNUC__
369
 
#pragma GCC diagnostic pop
370
 
#endif
371
 
    if(errno != 0){
372
 
      error(EX_OSERR, errno, "Failed argz_create()");
373
 
    }
374
 
  }
375
 
  /* Use argz vector to create a normal argv, usable by execv() */
376
 
 
377
 
  char **mandos_argv = malloc((argz_count(mandos_argz,
378
 
                                          mandos_argz_length)
379
 
                               + 1) * sizeof(char *));
380
 
  if(mandos_argv == NULL){
381
 
    error_t saved_errno = errno;
382
 
    free(mandos_argz);
383
 
    error(EX_OSERR, saved_errno, "Failed malloc()");
384
 
  }
385
 
  argz_extract(mandos_argz, mandos_argz_length, mandos_argv);
386
 
 
387
 
  sigset_t orig_sigmask;
388
 
  if(not block_sigchld(&orig_sigmask)){
389
 
    return EX_OSERR;
390
 
  }
391
 
 
392
 
  struct sigaction old_sigchld_action;
393
 
  if(not setup_signal_handler(&old_sigchld_action)){
394
 
    return EX_OSERR;
395
 
  }
396
 
 
397
 
  mono_microsecs current_time = 0;
398
 
 
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);
403
 
  if(epoll_fd < 0){
404
 
    error(EX_OSERR, errno, "Failed to create epoll set fd");
405
 
  }
406
 
  __attribute__((cleanup(cleanup_queue)))
407
 
    task_queue *queue = create_queue();
408
 
  if(queue == NULL){
409
 
    error(EX_OSERR, errno, "Failed to create task queue");
410
 
  }
411
 
 
412
 
  __attribute__((cleanup(cleanup_buffer)))
413
 
    buffer password = {};
414
 
  bool password_is_read = false;
415
 
 
416
 
  __attribute__((cleanup(string_set_clear)))
417
 
    string_set cancelled_filenames = {};
418
 
 
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 */
426
 
  }
427
 
  /* These variables were only for start_mandos_client() and are not
428
 
     needed anymore */
429
 
  free(mandos_argv);
430
 
  free(mandos_argz);
431
 
  mandos_argz = NULL;
432
 
  if(not add_inotify_dir_watch(queue, epoll_fd, &quit_now, &password,
433
 
                               agent_directory, &cancelled_filenames,
434
 
                               &current_time, &mandos_client_exited,
435
 
                               &password_is_read)){
436
 
    switch(errno){              /* Error has already been printed */
437
 
    case EACCES:
438
 
    case ENAMETOOLONG:
439
 
    case ENOENT:
440
 
    case ENOTDIR:
441
 
      return EX_OSFILE;
442
 
    default:
443
 
      return EX_OSERR;
444
 
    }
445
 
  }
446
 
  if(not add_existing_questions(queue, epoll_fd, &password,
447
 
                                &cancelled_filenames, &current_time,
448
 
                                &mandos_client_exited,
449
 
                                &password_is_read, agent_directory)){
450
 
    return EXIT_FAILURE;        /* Error has already been printed */
451
 
  }
452
 
 
453
 
  /* Run queue */
454
 
  do {
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"
459
 
            " events");
460
 
    }
461
 
 
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");
466
 
    }
467
 
 
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));
471
 
 
472
 
  restore_signal_handler(&old_sigchld_action);
473
 
  restore_sigmask(&orig_sigmask);
474
 
 
475
 
  return EXIT_SUCCESS;
476
 
}
477
 
 
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");
483
 
    return 0;
484
 
  }
485
 
  return ((mono_microsecs)currtime.tv_sec * 1000000) /* seconds */
486
 
    + ((mono_microsecs)currtime.tv_nsec / 1000);     /* nanoseconds */
487
 
}
488
 
 
489
 
/* End of "main" section */
490
 
 
491
 
/* Start of regular code section; ALL this code has tests */
492
 
 
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){
498
 
 
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"
508
 
      " user" },
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"
513
 
      " group" },
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, },
519
 
    { NULL },
520
 
  };
521
 
 
522
 
  __attribute__((nonnull(3)))
523
 
    error_t parse_opt(int key, char *arg, struct argp_state *state){
524
 
    errno = 0;
525
 
    switch(key){
526
 
    case 'd':                   /* --agent-directory */
527
 
      *agent_directory = strdup(arg);
528
 
      break;
529
 
    case 128:                   /* --helper-directory */
530
 
    case 129:                   /* --plugin-helper-dir */
531
 
      *helper_directory = strdup(arg);
532
 
      break;
533
 
    case 'u':                   /* --user */
534
 
    case 130:                   /* --userid */
535
 
      {
536
 
        char *tmp;
537
 
        uintmax_t tmp_id = 0;
538
 
        errno = 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;
543
 
        }
544
 
        *user = (uid_t)tmp_id;
545
 
        errno = 0;
546
 
        break;
547
 
      }
548
 
    case 'g':                   /* --group */
549
 
    case 131:                   /* --groupid */
550
 
      {
551
 
        char *tmp;
552
 
        uintmax_t tmp_id = 0;
553
 
        errno = 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;
558
 
        }
559
 
        *group = (gid_t)tmp_id;
560
 
        errno = 0;
561
 
        break;
562
 
      }
563
 
    case ARGP_KEY_ARGS:
564
 
      /* Copy arguments into argz vector */
565
 
      return argz_create(state->argv + state->next, mandos_argz,
566
 
                         mandos_argz_length);
567
 
    default:
568
 
      return ARGP_ERR_UNKNOWN;
569
 
    }
570
 
    return errno;
571
 
  }
572
 
 
573
 
  const struct argp argp = {
574
 
    .options=options,
575
 
    .parser=parse_opt,
576
 
    .args_doc="[MANDOS_CLIENT [OPTION...]]\n--test",
577
 
    .doc = "Mandos password agent -- runs Mandos client as a"
578
 
    " systemd password agent",
579
 
  };
580
 
 
581
 
  errno = argp_parse(&argp, argc, argv,
582
 
                     exit_failure ? 0 : ARGP_NO_EXIT, NULL, NULL);
583
 
 
584
 
  return errno == 0;
585
 
}
586
 
 
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");
592
 
    return false;
593
 
  }
594
 
  if(sigaddset(&sigchld_sigmask, SIGCHLD) < 0){
595
 
    error(0, errno, "Failed to add SIGCHLD to signal set");
596
 
    return false;
597
 
  }
598
 
  if(pthread_sigmask(SIG_BLOCK, &sigchld_sigmask, orig_sigmask) != 0){
599
 
    error(0, errno, "Failed to block SIGCHLD signal");
600
 
    return false;
601
 
  }
602
 
  return true;
603
 
}
604
 
 
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");
609
 
    return false;
610
 
  }
611
 
  return true;
612
 
}
613
 
 
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,
619
 
  };
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()");
624
 
    return false;
625
 
  }
626
 
  if(sigaction(SIGCHLD, &sigchld_action, old_sigchld_action) != 0){
627
 
    error(0, errno, "Failed to set SIGCHLD signal handler");
628
 
    return false;
629
 
  }
630
 
  return true;
631
 
}
632
 
 
633
 
__attribute__((nonnull, warn_unused_result))
634
 
bool restore_signal_handler(const struct sigaction *const
635
 
                            old_sigchld_action){
636
 
  if(sigaction(SIGCHLD, old_sigchld_action, NULL) != 0){
637
 
    error(0, errno, "Failed to restore signal handler");
638
 
    return false;
639
 
  }
640
 
  return true;
641
 
}
642
 
 
643
 
__attribute__((warn_unused_result, malloc))
644
 
task_queue *create_queue(void){
645
 
  task_queue *queue = malloc(sizeof(task_queue));
646
 
  if(queue){
647
 
    queue->tasks = NULL;
648
 
    queue->length = 0;
649
 
    queue->allocated = 0;
650
 
    queue->next_run = 0;
651
 
  }
652
 
  return queue;
653
 
}
654
 
 
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))){
658
 
    /* overflow */
659
 
    error(0, ENOMEM, "Failed to allocate %" PRIuMAX
660
 
          " tasks for queue->tasks", (uintmax_t)(queue->length + 1));
661
 
    errno = ENOMEM;
662
 
    return false;
663
 
  }
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,
667
 
                                            needed_size);
668
 
    if(new_tasks == NULL){
669
 
      error(0, errno, "Failed to allocate %" PRIuMAX
670
 
            " bytes for queue->tasks", (uintmax_t)needed_size);
671
 
      return false;
672
 
    }
673
 
    queue->tasks = new_tasks;
674
 
    queue->allocated = needed_size;
675
 
  }
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));
681
 
  return true;
682
 
}
683
 
 
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);
691
 
  }
692
 
  if(task->pid > 0){
693
 
    kill(task->pid, SIGTERM);
694
 
  }
695
 
  if(task->fd > 0){
696
 
    close(task->fd);
697
 
  }
698
 
  errno = saved_errno;
699
 
}
700
 
 
701
 
__attribute__((nonnull))
702
 
void free_queue(task_queue *const queue){
703
 
  free(queue->tasks);
704
 
  free(queue);
705
 
}
706
 
 
707
 
__attribute__((nonnull))
708
 
void cleanup_queue(task_queue *const *const queue){
709
 
  if(*queue == NULL){
710
 
    return;
711
 
  }
712
 
  for(size_t i = 0; i < (*queue)->length; i++){
713
 
    const task_context *const task = ((*queue)->tasks)+i;
714
 
    cleanup_task(task);
715
 
  }
716
 
  free_queue(*queue);
717
 
}
718
 
 
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){
723
 
      return true;
724
 
    }
725
 
  }
726
 
  return false;
727
 
}
728
 
 
729
 
__attribute__((nonnull))
730
 
void cleanup_close(const int *const fd){
731
 
  const error_t saved_errno = errno;
732
 
  close(*fd);
733
 
  errno = saved_errno;
734
 
}
735
 
 
736
 
__attribute__((nonnull))
737
 
void cleanup_string(char *const *const ptr){
738
 
  free(*ptr);
739
 
}
740
 
 
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);
746
 
#else
747
 
    memset(buf->data, '\0', buf->allocated);
748
 
#endif
749
 
  }
750
 
  if(buf->data != NULL){
751
 
    if(munlock(buf->data, buf->allocated) != 0){
752
 
      error(0, errno, "Failed to unlock memory of old buffer");
753
 
    }
754
 
    free(buf->data);
755
 
    buf->data = NULL;
756
 
  }
757
 
  buf->length = 0;
758
 
  buf->allocated = 0;
759
 
}
760
 
 
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){
766
 
      return true;
767
 
    }
768
 
  }
769
 
  return false;
770
 
}
771
 
 
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)){
775
 
    return true;
776
 
  }
777
 
  error_t error = argz_add(&set->argz, &set->argz_len, str);
778
 
  if(error == 0){
779
 
    return true;
780
 
  }
781
 
  errno = error;
782
 
  return false;
783
 
}
784
 
 
785
 
__attribute__((nonnull))
786
 
void string_set_clear(string_set *set){
787
 
  free(set->argz);
788
 
  set->argz = NULL;
789
 
  set->argz_len = 0;
790
 
}
791
 
 
792
 
__attribute__((nonnull))
793
 
void string_set_swap(string_set *const set1, string_set *const set2){
794
 
  /* Swap contents of two string sets */
795
 
  {
796
 
    char *const tmp_argz = set1->argz;
797
 
    set1->argz = set2->argz;
798
 
    set2->argz = tmp_argz;
799
 
  }
800
 
  {
801
 
    const size_t tmp_argz_len = set1->argz_len;
802
 
    set1->argz_len = set2->argz_len;
803
 
    set2->argz_len = tmp_argz_len;
804
 
  }
805
 
}
806
 
 
807
 
__attribute__((nonnull, warn_unused_result))
808
 
bool start_mandos_client(task_queue *const queue,
809
 
                         const int epoll_fd,
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){
818
 
  int pipefds[2];
819
 
  if(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK) != 0){
820
 
    error(0, errno, "Failed to pipe2(..., O_CLOEXEC | O_NONBLOCK)");
821
 
    return false;
822
 
  }
823
 
 
824
 
  const pid_t pid = fork();
825
 
  if(pid == 0){
826
 
    if(not restore_signal_handler(old_sigchld_action)){
827
 
      _exit(EXIT_FAILURE);
828
 
    }
829
 
    if(not restore_sigmask(&sigmask)){
830
 
      _exit(EXIT_FAILURE);
831
 
    }
832
 
    if(close(pipefds[0]) != 0){
833
 
      error(0, errno, "Failed to close() parent pipe fd");
834
 
      _exit(EXIT_FAILURE);
835
 
    }
836
 
    if(dup2(pipefds[1], STDOUT_FILENO) == -1){
837
 
      error(0, errno, "Failed to dup2() pipe fd to stdout");
838
 
      _exit(EXIT_FAILURE);
839
 
    }
840
 
    if(close(pipefds[1]) != 0){
841
 
      error(0, errno, "Failed to close() old child pipe fd");
842
 
      _exit(EXIT_FAILURE);
843
 
    }
844
 
    if(setenv("MANDOSPLUGINHELPERDIR", helper_directory, 1) != 0){
845
 
      error(0, errno, "Failed to setenv(\"MANDOSPLUGINHELPERDIR\","
846
 
            " \"%s\", 1)", helper_directory);
847
 
      _exit(EXIT_FAILURE);
848
 
    }
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);
852
 
      _exit(EXIT_FAILURE);
853
 
    }
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);
857
 
      _exit(EXIT_FAILURE);
858
 
    }
859
 
#ifdef __GNUC__
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"
864
 
#endif
865
 
    execv(argv[0], (char **)argv);
866
 
#ifdef __GNUC__
867
 
#pragma GCC diagnostic pop
868
 
#endif
869
 
    error(0, errno, "execv(\"%s\", ...) failed", argv[0]);
870
 
    _exit(EXIT_FAILURE);
871
 
  }
872
 
  close(pipefds[1]);
873
 
 
874
 
  if(pid == -1){
875
 
    error(0, errno, "Failed to fork()");
876
 
    close(pipefds[0]);
877
 
    return false;
878
 
  }
879
 
 
880
 
  if(not add_to_queue(queue, (task_context){
881
 
        .func=wait_for_mandos_client_exit,
882
 
        .pid=pid,
883
 
        .mandos_client_exited=mandos_client_exited,
884
 
        .quit_now=quit_now,
885
 
      })){
886
 
    error(0, errno, "Failed to add wait_for_mandos_client to queue");
887
 
    close(pipefds[0]);
888
 
    return false;
889
 
  }
890
 
 
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");
896
 
    close(pipefds[0]);
897
 
    return false;
898
 
  }
899
 
 
900
 
  return add_to_queue(queue, (task_context){
901
 
      .func=read_mandos_client_output,
902
 
      .epoll_fd=epoll_fd,
903
 
      .fd=pipefds[0],
904
 
      .quit_now=quit_now,
905
 
      .password=password,
906
 
      .password_is_read=password_is_read,
907
 
    });
908
 
}
909
 
 
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;
916
 
 
917
 
  int status;
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");
922
 
      *quit_now = true;
923
 
    }
924
 
    break;
925
 
  case -1:                      /* Error */
926
 
    error(0, errno, "waitpid(%" PRIdMAX ") failed", (intmax_t)pid);
927
 
    if(errno != ECHILD){
928
 
      kill(pid, SIGTERM);
929
 
    }
930
 
    *quit_now = true;
931
 
    break;
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");
937
 
      *quit_now = true;
938
 
    }
939
 
  }
940
 
}
941
 
 
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;
950
 
 
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);
957
 
      *quit_now = true;
958
 
      close(fd);
959
 
      return;
960
 
    }
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");
965
 
      }
966
 
    }
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);
971
 
#else
972
 
      memset(password->data, '\0', password->allocated);
973
 
#endif
974
 
    }
975
 
    if(password->data != NULL){
976
 
      if(munlock(password->data, password->allocated) != 0){
977
 
        error(0, errno, "Failed to unlock memory of old buffer");
978
 
      }
979
 
      free(password->data);
980
 
    }
981
 
    password->data = new_buffer;
982
 
    password->allocated = new_potential_size;
983
 
  }
984
 
 
985
 
  const ssize_t read_length = read(fd, password->data
986
 
                                   + password->length, PIPE_BUF);
987
 
 
988
 
  if(read_length == 0){ /* EOF */
989
 
    *password_is_read = true;
990
 
    close(fd);
991
 
    return;
992
 
  }
993
 
  if(read_length < 0 and errno != EAGAIN){ /* Actual error */
994
 
    error(0, errno, "Failed to read password from Mandos client");
995
 
    *quit_now = true;
996
 
    close(fd);
997
 
    return;
998
 
  }
999
 
  if(read_length > 0){          /* Data has been read */
1000
 
    password->length += (size_t)read_length;
1001
 
  }
1002
 
 
1003
 
  /* Either data was read, or EAGAIN was indicated, meaning no data
1004
 
     available yet */
1005
 
 
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");
1012
 
    *quit_now = true;
1013
 
    close(fd);
1014
 
    return;
1015
 
  }
1016
 
 
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");
1020
 
    *quit_now = true;
1021
 
    close(fd);
1022
 
  }
1023
 
}
1024
 
 
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);
1035
 
  if(fd == -1){
1036
 
    error(0, errno, "Failed to create inotify instance");
1037
 
    return false;
1038
 
  }
1039
 
 
1040
 
  if(inotify_add_watch(fd, dir, IN_CLOSE_WRITE | IN_MOVED_TO
1041
 
                       | IN_MOVED_FROM| IN_DELETE | IN_EXCL_UNLINK
1042
 
                       | IN_ONLYDIR)
1043
 
     == -1){
1044
 
    error(0, errno, "Failed to create inotify watch on %s", dir);
1045
 
    return false;
1046
 
  }
1047
 
 
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");
1054
 
    close(fd);
1055
 
    return false;
1056
 
  }
1057
 
 
1058
 
  const task_context read_inotify_event_task = {
1059
 
    .func=read_inotify_event,
1060
 
    .epoll_fd=epoll_fd,
1061
 
    .quit_now=quit_now,
1062
 
    .password=password,
1063
 
    .fd=fd,
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,
1069
 
  };
1070
 
  if(read_inotify_event_task.filename == NULL){
1071
 
    error(0, errno, "Failed to strdup(\"%s\")", dir);
1072
 
    close(fd);
1073
 
    return false;
1074
 
  }
1075
 
 
1076
 
  return add_to_queue(queue, read_inotify_event_task);
1077
 
}
1078
 
 
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;
1091
 
 
1092
 
  /* "sufficient to read at least one event." - inotify(7) */
1093
 
  const size_t ievent_size = (sizeof(struct inotify_event)
1094
 
                              + NAME_MAX + 1);
1095
 
  struct {
1096
 
    struct inotify_event event;
1097
 
    char name_buffer[NAME_MAX + 1];
1098
 
  } ievent_buffer;
1099
 
  struct inotify_event *const ievent = &ievent_buffer.event;
1100
 
 
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);
1104
 
    *quit_now = true;
1105
 
    cleanup_task(&task);
1106
 
    return;
1107
 
  }
1108
 
  if(read_length < 0 and errno != EAGAIN){ /* Actual error */
1109
 
    error(0, errno, "Failed to read from inotify fd for directory %s",
1110
 
          filename);
1111
 
    *quit_now = true;
1112
 
    cleanup_task(&task);
1113
 
    return;
1114
 
  }
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);
1123
 
    } else {
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,
1127
 
              .epoll_fd=epoll_fd,
1128
 
              .question_filename=question_filename,
1129
 
              .filename=question_filename,
1130
 
              .password=password,
1131
 
              .cancelled_filenames=cancelled_filenames,
1132
 
              .current_time=current_time,
1133
 
              .mandos_client_exited=mandos_client_exited,
1134
 
              .password_is_read=password_is_read,
1135
 
            })){
1136
 
          error(0, errno, "Failed to add open_and_parse_question task"
1137
 
                " for file name %s to queue", filename);
1138
 
        } else {
1139
 
          /* Force the added task (open_and_parse_question) to run
1140
 
             immediately */
1141
 
          queue->next_run = 1;
1142
 
        }
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);
1148
 
          *quit_now = true;
1149
 
          free(question_filename);
1150
 
          cleanup_task(&task);
1151
 
          return;
1152
 
        }
1153
 
        free(question_filename);
1154
 
      }
1155
 
    }
1156
 
  }
1157
 
 
1158
 
  /* Either data was read, or EAGAIN was indicated, meaning no data
1159
 
     available yet */
1160
 
 
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);
1165
 
    *quit_now = true;
1166
 
    cleanup_task(&task);
1167
 
    return;
1168
 
  }
1169
 
 
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;
1182
 
    }
1183
 
  }
1184
 
}
1185
 
 
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;
1197
 
 
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);
1205
 
    }
1206
 
  }
1207
 
 
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\"",
1212
 
          question_filename);
1213
 
    return;
1214
 
  }
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);
1223
 
    }
1224
 
    return;
1225
 
  }
1226
 
 
1227
 
  __attribute__((cleanup(cleanup_string)))
1228
 
    char *socket_name = g_key_file_get_string(key_file, "Ask",
1229
 
                                              "Socket",
1230
 
                                              &glib_error);
1231
 
  if(socket_name == NULL){
1232
 
    error(0, 0, "Question file \"%s\" did not contain \"Socket\": %s",
1233
 
          question_filename, glib_error->message);
1234
 
    return;
1235
 
  }
1236
 
 
1237
 
  if(strlen(socket_name) == 0){
1238
 
    error(0, 0, "Question file \"%s\" had empty \"Socket\" value",
1239
 
          question_filename);
1240
 
    return;
1241
 
  }
1242
 
 
1243
 
  const guint64 pid = g_key_file_get_uint64(key_file, "Ask", "PID",
1244
 
                                            &glib_error);
1245
 
  if(glib_error != NULL){
1246
 
    error(0, 0, "Question file \"%s\" contained bad \"PID\": %s",
1247
 
          question_filename, glib_error->message);
1248
 
    return;
1249
 
  }
1250
 
 
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);
1255
 
    return;
1256
 
  }
1257
 
 
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);
1264
 
    }
1265
 
    notafter = 0;
1266
 
  }
1267
 
  if(notafter != 0){
1268
 
    if(queue->next_run == 0 or (queue->next_run > notafter)){
1269
 
      queue->next_run = notafter;
1270
 
    }
1271
 
    if(*current_time >= notafter){
1272
 
      return;
1273
 
    }
1274
 
  }
1275
 
 
1276
 
  const task_context connect_question_socket_task = {
1277
 
    .func=connect_question_socket,
1278
 
    .question_filename=strdup(question_filename),
1279
 
    .epoll_fd=epoll_fd,
1280
 
    .password=password,
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,
1286
 
  };
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,
1292
 
          question_filename);
1293
 
    cleanup_task(&connect_question_socket_task);
1294
 
    return;
1295
 
  }
1296
 
  /* Force the added task (connect_question_socket) to run
1297
 
     immediately */
1298
 
  queue->next_run = 1;
1299
 
 
1300
 
  if(notafter > 0){
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,
1305
 
      .notafter=notafter,
1306
 
      .filename=dup_filename,
1307
 
      .cancelled_filenames=cancelled_filenames,
1308
 
      .current_time=current_time,
1309
 
    };
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);
1315
 
      return;
1316
 
    }
1317
 
  }
1318
 
}
1319
 
 
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;
1327
 
 
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",
1331
 
            question_filename);
1332
 
    }
1333
 
    cleanup_task(&task);
1334
 
    return;
1335
 
  }
1336
 
 
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);
1341
 
    return;
1342
 
  }
1343
 
 
1344
 
  if((queue->next_run == 0) or (queue->next_run > notafter)){
1345
 
    queue->next_run = notafter;
1346
 
  }
1347
 
}
1348
 
 
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;
1360
 
 
1361
 
  struct sockaddr_un sock_name = { .sun_family=AF_LOCAL };
1362
 
 
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",
1369
 
            question_filename);
1370
 
    }
1371
 
    cleanup_task(&task);
1372
 
    return;
1373
 
  }
1374
 
 
1375
 
  const int fd = socket(PF_LOCAL, SOCK_DGRAM
1376
 
                        | SOCK_NONBLOCK | SOCK_CLOEXEC, 0);
1377
 
  if(fd < 0){
1378
 
    error(0, errno,
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,
1383
 
            filename);
1384
 
      cleanup_task(&task);
1385
 
    } else {
1386
 
      /* Force the added task (connect_question_socket) to run
1387
 
         immediately */
1388
 
      queue->next_run = 1;
1389
 
    }
1390
 
    return;
1391
 
  }
1392
 
 
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,
1400
 
            filename);
1401
 
      cleanup_task(&task);
1402
 
    } else {
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;
1408
 
      }
1409
 
    }
1410
 
    return;
1411
 
  }
1412
 
 
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\"",
1416
 
          filename);
1417
 
  }
1418
 
 
1419
 
  /* Add the fd to the epoll set */
1420
 
  if(epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd,
1421
 
               &(struct epoll_event){ .events=EPOLLOUT })
1422
 
     != 0){
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,
1428
 
            filename);
1429
 
      cleanup_task(&task);
1430
 
    } else {
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;
1436
 
      }
1437
 
    }
1438
 
    return;
1439
 
  }
1440
 
 
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,
1445
 
    .filename=filename,
1446
 
    .epoll_fd=epoll_fd,
1447
 
    .fd=fd,
1448
 
    .password=password,
1449
 
    .cancelled_filenames=cancelled_filenames,
1450
 
    .mandos_client_exited=mandos_client_exited,
1451
 
    .password_is_read=password_is_read,
1452
 
    .current_time=current_time,
1453
 
  };
1454
 
 
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);
1460
 
  }
1461
 
}
1462
 
 
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;
1475
 
 
1476
 
  if(*mandos_client_exited and *password_is_read){
1477
 
 
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");
1482
 
    } else {
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"
1487
 
                " buffer");
1488
 
        }
1489
 
      }
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
1494
 
         your message.
1495
 
 
1496
 
         — <https://systemd.io/PASSWORD_AGENTS/> (Tue, 15 Sep 2020
1497
 
         14:24:20 GMT)
1498
 
      */
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);
1504
 
      }
1505
 
      errno = 0;
1506
 
      ssize_t ssret = send(fd, send_buffer, send_buffer_length,
1507
 
                           MSG_NOSIGNAL);
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);
1511
 
#else
1512
 
      memset(send_buffer, '\0', send_buffer_length);
1513
 
#endif
1514
 
      if(munlock(send_buffer, send_buffer_length) != 0){
1515
 
        error(0, errno, "Failed to unlock memory of send buffer");
1516
 
      }
1517
 
      free(send_buffer);
1518
 
      if(ssret < 0 or ssret < (ssize_t)send_buffer_length){
1519
 
        switch(saved_errno){
1520
 
        case EINTR:
1521
 
        case ENOBUFS:
1522
 
        case ENOMEM:
1523
 
        case EADDRINUSE:
1524
 
        case ECONNREFUSED:
1525
 
        case ECONNRESET:
1526
 
        case ENOENT:
1527
 
        case ETOOMANYREFS:
1528
 
        case EAGAIN:
1529
 
          /* Retry, below */
1530
 
          break;
1531
 
        case EMSGSIZE:
1532
 
          error(0, saved_errno, "Password of size %" PRIuMAX
1533
 
                " is too big", (uintmax_t)password->length);
1534
 
#if __GNUC__ < 7
1535
 
          /* FALLTHROUGH */
1536
 
#else
1537
 
          __attribute__((fallthrough));
1538
 
#endif
1539
 
        case 0:
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);
1544
 
          }
1545
 
#if __GNUC__ < 7
1546
 
          /* FALLTHROUGH */
1547
 
#else
1548
 
          __attribute__((fallthrough));
1549
 
#endif
1550
 
        default:
1551
 
          error(0, saved_errno, "Failed to send() to socket %s",
1552
 
                filename);
1553
 
          if(not string_set_add(cancelled_filenames,
1554
 
                                question_filename)){
1555
 
            error(0, errno, "Failed to cancel question for file %s",
1556
 
                  question_filename);
1557
 
          }
1558
 
          cleanup_task(&task);
1559
 
          return;
1560
 
        }
1561
 
      } else {
1562
 
        /* Success */
1563
 
        cleanup_task(&task);
1564
 
        return;
1565
 
      }
1566
 
    }
1567
 
  }
1568
 
 
1569
 
  /* We failed or are not ready yet; retry later */
1570
 
 
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,
1574
 
          filename);
1575
 
    cleanup_task(&task);
1576
 
  }
1577
 
 
1578
 
  /* Add the fd to the epoll set */
1579
 
  if(epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd,
1580
 
               &(struct epoll_event){ .events=EPOLLOUT })
1581
 
     != 0){
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;
1589
 
    }
1590
 
  }
1591
 
}
1592
 
 
1593
 
__attribute__((warn_unused_result))
1594
 
bool add_existing_questions(task_queue *const queue,
1595
 
                            const int epoll_fd,
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",
1607
 
          dirname);
1608
 
    return false;
1609
 
  }
1610
 
  __attribute__((cleanup(globfree)))
1611
 
    glob_t question_filenames = {};
1612
 
  switch(glob(dir_pattern, GLOB_ERR | GLOB_NOSORT | GLOB_MARK,
1613
 
              NULL, &question_filenames)){
1614
 
  case GLOB_ABORTED:
1615
 
  default:
1616
 
    error(0, errno, "Failed to open directory %s", dirname);
1617
 
    return false;
1618
 
  case GLOB_NOMATCH:
1619
 
    error(0, errno, "There are no question files in %s", dirname);
1620
 
    return false;
1621
 
  case GLOB_NOSPACE:
1622
 
    error(0, errno, "Could not allocate memory for question file"
1623
 
          " names in %s", dirname);
1624
 
#if __GNUC__ < 7
1625
 
    /* FALLTHROUGH */
1626
 
#else
1627
 
    __attribute__((fallthrough));
1628
 
#endif
1629
 
  case 0:
1630
 
    for(size_t i = 0; i < question_filenames.gl_pathc; i++){
1631
 
      char *const question_filename = strdup(question_filenames
1632
 
                                             .gl_pathv[i]);
1633
 
      const task_context task = {
1634
 
        .func=open_and_parse_question,
1635
 
        .epoll_fd=epoll_fd,
1636
 
        .question_filename=question_filename,
1637
 
        .filename=question_filename,
1638
 
        .password=password,
1639
 
        .cancelled_filenames=cancelled_filenames,
1640
 
        .current_time=current_time,
1641
 
        .mandos_client_exited=mandos_client_exited,
1642
 
        .password_is_read=password_is_read,
1643
 
      };
1644
 
 
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);
1651
 
      } else {
1652
 
        queue->next_run = 1;
1653
 
      }
1654
 
    }
1655
 
    return true;
1656
 
  }
1657
 
}
1658
 
 
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){
1667
 
      return 0;
1668
 
    }
1669
 
    const uintmax_t wait_time_ms = (nextrun - currtime) / 1000;
1670
 
    if(wait_time_ms > (uintmax_t)INT_MAX){
1671
 
      return INT_MAX;
1672
 
    }
1673
 
    return (int)wait_time_ms;
1674
 
  }
1675
 
 
1676
 
  const int wait_time_ms = milliseconds_to_wait(current_time,
1677
 
                                                queue_next_run);
1678
 
 
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){
1683
 
    return false;
1684
 
  }
1685
 
  /* Remove SIGCHLD from the signal mask */
1686
 
  if(sigdelset(&temporary_unblocked_sigmask, SIGCHLD) != 0){
1687
 
    return false;
1688
 
  }
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,"
1696
 
          " ...", epoll_fd,
1697
 
          queue_next_run == 0 ? -1 : (int)wait_time_ms);
1698
 
    return false;
1699
 
  }
1700
 
  return clear_all_fds_from_epoll_set(epoll_fd);
1701
 
}
1702
 
 
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){
1708
 
    return false;
1709
 
  }
1710
 
  /* dup3() the new epoll set fd over the old one, replacing it */
1711
 
  if(dup3(new_epoll_fd, epoll_fd, O_CLOEXEC) < 0){
1712
 
    return false;
1713
 
  }
1714
 
  return true;
1715
 
}
1716
 
 
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){
1721
 
 
1722
 
  task_queue *new_queue = create_queue();
1723
 
  if(new_queue == NULL){
1724
 
    return false;
1725
 
  }
1726
 
 
1727
 
  __attribute__((cleanup(string_set_clear)))
1728
 
    string_set old_cancelled_filenames = {};
1729
 
  string_set_swap(cancelled_filenames, &old_cancelled_filenames);
1730
 
 
1731
 
  /* Declare i outside the for loop, since we might need i after the
1732
 
     loop in case we aborted in the middle */
1733
 
  size_t i;
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)){
1741
 
      cleanup_task(task);
1742
 
      continue;
1743
 
    }
1744
 
    task->func(*task, new_queue);
1745
 
  }
1746
 
 
1747
 
  if(*quit_now){
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]));
1752
 
    }
1753
 
    free_queue(*queue);
1754
 
    *queue = new_queue;
1755
 
    new_queue = NULL;
1756
 
    return false;
1757
 
  }
1758
 
  free_queue(*queue);
1759
 
  *queue = new_queue;
1760
 
  new_queue = NULL;
1761
 
 
1762
 
  return true;
1763
 
}
1764
 
 
1765
 
/* End of regular code section */
1766
 
 
1767
 
/* Start of tests section; here are the tests for the above code */
1768
 
 
1769
 
/* This "fixture" data structure is used by the test setup and
1770
 
   teardown functions */
1771
 
typedef struct {
1772
 
  struct sigaction orig_sigaction;
1773
 
  sigset_t orig_sigmask;
1774
 
} test_fixture;
1775
 
 
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));
1781
 
}
1782
 
 
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));
1788
 
}
1789
 
 
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 */
1801
 
 
1802
 
    /* task_func *const func */
1803
 
    if(task.func != NULL and current_task->func != task.func){
1804
 
      continue;
1805
 
    }
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)){
1811
 
      continue;
1812
 
    }
1813
 
    /* const pid_t pid; */
1814
 
    if(task.pid != 0 and current_task->pid != task.pid){
1815
 
      continue;
1816
 
    }
1817
 
    /* const int epoll_fd; */
1818
 
    if(task.epoll_fd != 0
1819
 
       and current_task->epoll_fd != task.epoll_fd){
1820
 
      continue;
1821
 
    }
1822
 
    /* bool *const quit_now; */
1823
 
    if(task.quit_now != NULL
1824
 
       and current_task->quit_now != task.quit_now){
1825
 
      continue;
1826
 
    }
1827
 
    /* const int fd; */
1828
 
    if(task.fd != 0 and current_task->fd != task.fd){
1829
 
      continue;
1830
 
    }
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){
1835
 
      continue;
1836
 
    }
1837
 
    /* buffer *const password; */
1838
 
    if(task.password != NULL
1839
 
       and current_task->password != task.password){
1840
 
      continue;
1841
 
    }
1842
 
    /* bool *const password_is_read; */
1843
 
    if(task.password_is_read != NULL
1844
 
       and current_task->password_is_read != task.password_is_read){
1845
 
      continue;
1846
 
    }
1847
 
    /* char *filename; */
1848
 
    if(task.filename != NULL
1849
 
       and (current_task->filename == NULL
1850
 
            or strcmp(current_task->filename, task.filename) != 0)){
1851
 
      continue;
1852
 
    }
1853
 
    /* string_set *const cancelled_filenames; */
1854
 
    if(task.cancelled_filenames != NULL
1855
 
       and current_task->cancelled_filenames
1856
 
       != task.cancelled_filenames){
1857
 
      continue;
1858
 
    }
1859
 
    /* const mono_microsecs notafter; */
1860
 
    if(task.notafter != 0
1861
 
       and current_task->notafter != task.notafter){
1862
 
      continue;
1863
 
    }
1864
 
    /* const mono_microsecs *const current_time; */
1865
 
    if(task.current_time != NULL
1866
 
       and current_task->current_time != task.current_time){
1867
 
      continue;
1868
 
    }
1869
 
    /* Current task matches all members; return it */
1870
 
    return current_task;
1871
 
  }
1872
 
  /* No task in queue matches passed pattern task */
1873
 
  return NULL;
1874
 
}
1875
 
 
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);
1886
 
}
1887
 
 
1888
 
static task_func dummy_func;
1889
 
 
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);
1897
 
 
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);
1903
 
}
1904
 
 
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 */
1914
 
 
1915
 
  FILE *real_stderr = stderr;
1916
 
  FILE *devnull = fopen("/dev/null", "we");
1917
 
  g_assert_nonnull(devnull);
1918
 
  stderr = 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 */
1926
 
}
1927
 
 
1928
 
static void dummy_func(__attribute__((unused))
1929
 
                       const task_context task,
1930
 
                       __attribute__((unused))
1931
 
                       task_queue *const queue){
1932
 
}
1933
 
 
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));
1942
 
}
1943
 
 
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));
1954
 
}
1955
 
 
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 = {
1967
 
    .func=dummy_func,
1968
 
    .question_filename=question_filename,
1969
 
  };
1970
 
  g_assert_true(add_to_queue(queue, task));
1971
 
  g_assert_true(queue_has_question(queue));
1972
 
}
1973
 
 
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));
1986
 
}
1987
 
 
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 = {
2001
 
    .func=dummy_func,
2002
 
    .question_filename=question_filename,
2003
 
  };
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));
2007
 
}
2008
 
 
2009
 
static void test_cleanup_buffer(__attribute__((unused))
2010
 
                                test_fixture *fixture,
2011
 
                                __attribute__((unused))
2012
 
                                gconstpointer user_data){
2013
 
  buffer buf = {};
2014
 
 
2015
 
  const size_t buffersize = 10;
2016
 
 
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);
2021
 
  }
2022
 
 
2023
 
  cleanup_buffer(&buf);
2024
 
  g_assert_null(buf.data);
2025
 
}
2026
 
 
2027
 
static
2028
 
void test_string_set_new_set_contains_nothing(__attribute__((unused))
2029
 
                                              test_fixture *fixture,
2030
 
                                              __attribute__((unused))
2031
 
                                              gconstpointer
2032
 
                                              user_data){
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"));
2037
 
}
2038
 
 
2039
 
static void
2040
 
test_string_set_with_added_string_contains_it(__attribute__((unused))
2041
 
                                              test_fixture *fixture,
2042
 
                                              __attribute__((unused))
2043
 
                                              gconstpointer
2044
 
                                              user_data){
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"));
2049
 
}
2050
 
 
2051
 
static void
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"));
2061
 
}
2062
 
 
2063
 
static
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"));
2076
 
}
2077
 
 
2078
 
static
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"));
2091
 
}
2092
 
 
2093
 
static void test_string_set_swap_one_with_one(__attribute__((unused))
2094
 
                                              test_fixture *fixture,
2095
 
                                              __attribute__((unused))
2096
 
                                              gconstpointer
2097
 
                                              user_data){
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"));
2109
 
}
2110
 
 
2111
 
static bool fd_has_cloexec_and_nonblock(const int);
2112
 
 
2113
 
static bool epoll_set_contains(int, int, uint32_t);
2114
 
 
2115
 
static void test_start_mandos_client(test_fixture *fixture,
2116
 
                                     __attribute__((unused))
2117
 
                                     gconstpointer user_data){
2118
 
 
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 };
2131
 
 
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));
2138
 
 
2139
 
  g_assert_cmpuint((unsigned int)queue->length, >=, 2);
2140
 
 
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,
2146
 
      });
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);
2151
 
 
2152
 
  const task_context *const added_read_task
2153
 
    = find_matching_task(queue, (task_context){
2154
 
        .func=read_mandos_client_output,
2155
 
        .epoll_fd=epoll_fd,
2156
 
        .password=&password,
2157
 
        .password_is_read=&password_is_read,
2158
 
        .quit_now=&quit_now,
2159
 
      });
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));
2165
 
}
2166
 
 
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));
2174
 
}
2175
 
 
2176
 
__attribute__((const))
2177
 
bool is_privileged(void){
2178
 
  uid_t user = getuid() + 1;
2179
 
  if(user == 0){                /* Overflow check */
2180
 
    user++;
2181
 
  }
2182
 
  gid_t group = getuid() + 1;
2183
 
  if(group == 0){               /* Overflow check */
2184
 
    group++;
2185
 
  }
2186
 
  const pid_t pid = fork();
2187
 
  if(pid == 0){                 /* Child */
2188
 
    if(setresgid((uid_t)-1, group, group) == -1){
2189
 
      if(errno != EPERM){
2190
 
        error(EXIT_FAILURE, errno, "Failed to setresgid(-1, %" PRIuMAX
2191
 
              ", %" PRIuMAX")", (uintmax_t)group, (uintmax_t)group);
2192
 
      }
2193
 
      exit(EXIT_FAILURE);
2194
 
    }
2195
 
    if(setresuid((uid_t)-1, user, user) == -1){
2196
 
      if(errno != EPERM){
2197
 
        error(EXIT_FAILURE, errno, "Failed to setresuid(-1, %" PRIuMAX
2198
 
              ", %" PRIuMAX")", (uintmax_t)user, (uintmax_t)user);
2199
 
      }
2200
 
      exit(EXIT_FAILURE);
2201
 
    }
2202
 
    exit(EXIT_SUCCESS);
2203
 
  }
2204
 
  if(pid == -1){
2205
 
    error(EXIT_FAILURE, errno, "Failed to fork()");
2206
 
  }
2207
 
 
2208
 
  int status;
2209
 
  waitpid(pid, &status, 0);
2210
 
  if(WIFEXITED(status) and (WEXITSTATUS(status) == EXIT_SUCCESS)){
2211
 
    return true;
2212
 
  }
2213
 
  return false;
2214
 
}
2215
 
 
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);
2224
 
 
2225
 
  FILE *fdinfo = fopen(fdinfo_name, "r");
2226
 
  g_assert_nonnull(fdinfo);
2227
 
  uint32_t reported_events;
2228
 
  buffer line = {};
2229
 
  int found_fd = -1;
2230
 
 
2231
 
  do {
2232
 
    if(getline(&line.data, &line.allocated, fdinfo) < 0){
2233
 
      break;
2234
 
    }
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){
2238
 
      if(found_fd == fd){
2239
 
        break;
2240
 
      }
2241
 
    }
2242
 
  } while(not feof(fdinfo) and not ferror(fdinfo));
2243
 
  g_assert_cmpint(fclose(fdinfo), ==, 0);
2244
 
  free(line.data);
2245
 
  if(found_fd != fd){
2246
 
    return false;
2247
 
  }
2248
 
 
2249
 
  if(events == 0){
2250
 
    /* Don't check events if none are given */
2251
 
    return true;
2252
 
  }
2253
 
  return (reported_events & eventmask) == (events & eventmask);
2254
 
}
2255
 
 
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 };
2272
 
 
2273
 
  {
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);
2282
 
 
2283
 
    const bool success = start_mandos_client(queue, epoll_fd,
2284
 
                                             &mandos_client_exited,
2285
 
                                             &quit_now,
2286
 
                                             &password,
2287
 
                                             (bool[]){false},
2288
 
                                             &fixture->orig_sigaction,
2289
 
                                             fixture->orig_sigmask,
2290
 
                                             helper_directory, 0, 0,
2291
 
                                             argv);
2292
 
    dup2(real_stderr_fd, STDERR_FILENO);
2293
 
    g_assert_true(success);
2294
 
  }
2295
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 2);
2296
 
 
2297
 
  struct timespec starttime, currtime;
2298
 
  g_assert_true(clock_gettime(CLOCK_MONOTONIC, &starttime) == 0);
2299
 
  do {
2300
 
    queue->next_run = 0;
2301
 
    string_set cancelled_filenames = {};
2302
 
 
2303
 
    {
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,
2314
 
                                     &quit_now);
2315
 
      dup2(real_stderr_fd, STDERR_FILENO);
2316
 
      if(not success){
2317
 
        break;
2318
 
      }
2319
 
    }
2320
 
    g_assert_true(clock_gettime(CLOCK_MONOTONIC, &currtime) == 0);
2321
 
  } while(((queue->length) > 0)
2322
 
          and (not quit_now)
2323
 
          and ((currtime.tv_sec - starttime.tv_sec) < 10));
2324
 
 
2325
 
  g_assert_true(quit_now);
2326
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
2327
 
  g_assert_true(mandos_client_exited);
2328
 
}
2329
 
 
2330
 
static void test_start_mandos_client_suid_euid(test_fixture *fixture,
2331
 
                                               __attribute__((unused))
2332
 
                                               gconstpointer
2333
 
                                               user_data){
2334
 
  if(not is_privileged()){
2335
 
    g_test_skip("Not privileged");
2336
 
    return;
2337
 
  }
2338
 
 
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 };
2352
 
  uid_t user = 1000;
2353
 
  gid_t group = 1001;
2354
 
 
2355
 
  const bool success = start_mandos_client(queue, epoll_fd,
2356
 
                                           &mandos_client_exited,
2357
 
                                           &quit_now, &password,
2358
 
                                           &password_is_read,
2359
 
                                           &fixture->orig_sigaction,
2360
 
                                           fixture->orig_sigmask,
2361
 
                                           helper_directory, user,
2362
 
                                           group, argv);
2363
 
  g_assert_true(success);
2364
 
  g_assert_cmpuint((unsigned int)queue->length, >, 0);
2365
 
 
2366
 
  struct timespec starttime, currtime;
2367
 
  g_assert_true(clock_gettime(CLOCK_MONOTONIC, &starttime) == 0);
2368
 
  do {
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)
2375
 
          and (not quit_now)
2376
 
          and ((currtime.tv_sec - starttime.tv_sec) < 10));
2377
 
 
2378
 
  g_assert_false(quit_now);
2379
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
2380
 
  g_assert_true(mandos_client_exited);
2381
 
 
2382
 
  g_assert_true(password_is_read);
2383
 
  g_assert_nonnull(password.data);
2384
 
 
2385
 
  uintmax_t id;
2386
 
  g_assert_cmpint(sscanf(password.data, "%" SCNuMAX "\n", &id),
2387
 
                  ==, 1);
2388
 
  g_assert_true((uid_t)id == id);
2389
 
 
2390
 
  g_assert_cmpuint((unsigned int)id, ==, 0);
2391
 
}
2392
 
 
2393
 
static void test_start_mandos_client_suid_egid(test_fixture *fixture,
2394
 
                                               __attribute__((unused))
2395
 
                                               gconstpointer
2396
 
                                               user_data){
2397
 
  if(not is_privileged()){
2398
 
    g_test_skip("Not privileged");
2399
 
    return;
2400
 
  }
2401
 
 
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 };
2415
 
  uid_t user = 1000;
2416
 
  gid_t group = 1001;
2417
 
 
2418
 
  const bool success = start_mandos_client(queue, epoll_fd,
2419
 
                                           &mandos_client_exited,
2420
 
                                           &quit_now, &password,
2421
 
                                           &password_is_read,
2422
 
                                           &fixture->orig_sigaction,
2423
 
                                           fixture->orig_sigmask,
2424
 
                                           helper_directory, user,
2425
 
                                           group, argv);
2426
 
  g_assert_true(success);
2427
 
  g_assert_cmpuint((unsigned int)queue->length, >, 0);
2428
 
 
2429
 
  struct timespec starttime, currtime;
2430
 
  g_assert_true(clock_gettime(CLOCK_MONOTONIC, &starttime) == 0);
2431
 
  do {
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)
2438
 
          and (not quit_now)
2439
 
          and ((currtime.tv_sec - starttime.tv_sec) < 10));
2440
 
 
2441
 
  g_assert_false(quit_now);
2442
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
2443
 
  g_assert_true(mandos_client_exited);
2444
 
 
2445
 
  g_assert_true(password_is_read);
2446
 
  g_assert_nonnull(password.data);
2447
 
 
2448
 
  uintmax_t id;
2449
 
  g_assert_cmpint(sscanf(password.data, "%" SCNuMAX "\n", &id),
2450
 
                  ==, 1);
2451
 
  g_assert_true((gid_t)id == id);
2452
 
 
2453
 
  g_assert_cmpuint((unsigned int)id, ==, 0);
2454
 
}
2455
 
 
2456
 
static void test_start_mandos_client_suid_ruid(test_fixture *fixture,
2457
 
                                               __attribute__((unused))
2458
 
                                               gconstpointer
2459
 
                                               user_data){
2460
 
  if(not is_privileged()){
2461
 
    g_test_skip("Not privileged");
2462
 
    return;
2463
 
  }
2464
 
 
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",
2478
 
    NULL };
2479
 
  uid_t user = 1000;
2480
 
  gid_t group = 1001;
2481
 
 
2482
 
  const bool success = start_mandos_client(queue, epoll_fd,
2483
 
                                           &mandos_client_exited,
2484
 
                                           &quit_now, &password,
2485
 
                                           &password_is_read,
2486
 
                                           &fixture->orig_sigaction,
2487
 
                                           fixture->orig_sigmask,
2488
 
                                           helper_directory, user,
2489
 
                                           group, argv);
2490
 
  g_assert_true(success);
2491
 
  g_assert_cmpuint((unsigned int)queue->length, >, 0);
2492
 
 
2493
 
  struct timespec starttime, currtime;
2494
 
  g_assert_true(clock_gettime(CLOCK_MONOTONIC, &starttime) == 0);
2495
 
  do {
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)
2502
 
          and (not quit_now)
2503
 
          and ((currtime.tv_sec - starttime.tv_sec) < 10));
2504
 
 
2505
 
  g_assert_false(quit_now);
2506
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
2507
 
  g_assert_true(mandos_client_exited);
2508
 
 
2509
 
  g_assert_true(password_is_read);
2510
 
  g_assert_nonnull(password.data);
2511
 
 
2512
 
  uintmax_t id;
2513
 
  g_assert_cmpint(sscanf(password.data, "%" SCNuMAX "\n", &id),
2514
 
                  ==, 1);
2515
 
  g_assert_true((uid_t)id == id);
2516
 
 
2517
 
  g_assert_cmpuint((unsigned int)id, ==, user);
2518
 
}
2519
 
 
2520
 
static void test_start_mandos_client_suid_rgid(test_fixture *fixture,
2521
 
                                               __attribute__((unused))
2522
 
                                               gconstpointer
2523
 
                                               user_data){
2524
 
  if(not is_privileged()){
2525
 
    g_test_skip("Not privileged");
2526
 
    return;
2527
 
  }
2528
 
 
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",
2542
 
    NULL };
2543
 
  uid_t user = 1000;
2544
 
  gid_t group = 1001;
2545
 
 
2546
 
  const bool success = start_mandos_client(queue, epoll_fd,
2547
 
                                           &mandos_client_exited,
2548
 
                                           &quit_now, &password,
2549
 
                                           &password_is_read,
2550
 
                                           &fixture->orig_sigaction,
2551
 
                                           fixture->orig_sigmask,
2552
 
                                           helper_directory, user,
2553
 
                                           group, argv);
2554
 
  g_assert_true(success);
2555
 
  g_assert_cmpuint((unsigned int)queue->length, >, 0);
2556
 
 
2557
 
  struct timespec starttime, currtime;
2558
 
  g_assert_true(clock_gettime(CLOCK_MONOTONIC, &starttime) == 0);
2559
 
  do {
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)
2566
 
          and (not quit_now)
2567
 
          and ((currtime.tv_sec - starttime.tv_sec) < 10));
2568
 
 
2569
 
  g_assert_false(quit_now);
2570
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
2571
 
  g_assert_true(mandos_client_exited);
2572
 
 
2573
 
  g_assert_true(password_is_read);
2574
 
  g_assert_nonnull(password.data);
2575
 
 
2576
 
  uintmax_t id;
2577
 
  g_assert_cmpint(sscanf(password.data, "%" SCNuMAX "\n", &id),
2578
 
                  ==, 1);
2579
 
  g_assert_true((gid_t)id == id);
2580
 
 
2581
 
  g_assert_cmpuint((unsigned int)id, ==, group);
2582
 
}
2583
 
 
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,
2601
 
    NULL };
2602
 
 
2603
 
  const bool success = start_mandos_client(queue, epoll_fd,
2604
 
                                           &mandos_client_exited,
2605
 
                                           &quit_now, &password,
2606
 
                                           &password_is_read,
2607
 
                                           &fixture->orig_sigaction,
2608
 
                                           fixture->orig_sigmask,
2609
 
                                           helper_directory, 0, 0,
2610
 
                                           argv);
2611
 
  g_assert_true(success);
2612
 
  g_assert_cmpuint((unsigned int)queue->length, >, 0);
2613
 
 
2614
 
  struct timespec starttime, currtime;
2615
 
  g_assert_true(clock_gettime(CLOCK_MONOTONIC, &starttime) == 0);
2616
 
  do {
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)
2623
 
          and (not quit_now)
2624
 
          and ((currtime.tv_sec - starttime.tv_sec) < 10));
2625
 
 
2626
 
  g_assert_false(quit_now);
2627
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
2628
 
  g_assert_true(mandos_client_exited);
2629
 
 
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);
2636
 
}
2637
 
 
2638
 
static
2639
 
void test_start_mandos_client_helper_directory(test_fixture *fixture,
2640
 
                                               __attribute__((unused))
2641
 
                                               gconstpointer
2642
 
                                               user_data){
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 };
2657
 
 
2658
 
  const bool success = start_mandos_client(queue, epoll_fd,
2659
 
                                           &mandos_client_exited,
2660
 
                                           &quit_now, &password,
2661
 
                                           &password_is_read,
2662
 
                                           &fixture->orig_sigaction,
2663
 
                                           fixture->orig_sigmask,
2664
 
                                           helper_directory, 0, 0,
2665
 
                                           argv);
2666
 
  g_assert_true(success);
2667
 
  g_assert_cmpuint((unsigned int)queue->length, >, 0);
2668
 
 
2669
 
  struct timespec starttime, currtime;
2670
 
  g_assert_true(clock_gettime(CLOCK_MONOTONIC, &starttime) == 0);
2671
 
  do {
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)
2678
 
          and (not quit_now)
2679
 
          and ((currtime.tv_sec - starttime.tv_sec) < 10));
2680
 
 
2681
 
  g_assert_false(quit_now);
2682
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
2683
 
  g_assert_true(mandos_client_exited);
2684
 
 
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);
2691
 
}
2692
 
 
2693
 
__attribute__((nonnull, warn_unused_result))
2694
 
static bool proc_status_sigblk_to_sigset(const char *const,
2695
 
                                         sigset_t *const);
2696
 
 
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 };
2715
 
 
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));
2722
 
 
2723
 
  struct timespec starttime, currtime;
2724
 
  g_assert_true(clock_gettime(CLOCK_MONOTONIC, &starttime) == 0);
2725
 
  do {
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))
2732
 
          and (not quit_now)
2733
 
          and ((currtime.tv_sec - starttime.tv_sec) < 10));
2734
 
  g_assert_true(mandos_client_exited);
2735
 
  g_assert_true(password_is_read);
2736
 
 
2737
 
  sigset_t parsed_sigmask;
2738
 
  g_assert_true(proc_status_sigblk_to_sigset(password.data,
2739
 
                                             &parsed_sigmask));
2740
 
 
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);
2745
 
    } else {
2746
 
      g_assert_false(has_signal);
2747
 
    }
2748
 
  }
2749
 
}
2750
 
 
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){
2757
 
    return false;
2758
 
  }
2759
 
  if(sigemptyset(sigmask) != 0){
2760
 
    return false;
2761
 
  }
2762
 
  for(int signum = 1; signum < NSIG; signum++){
2763
 
    if(scanned_sigmask & ((uintmax_t)1 << (signum-1))){
2764
 
      if(sigaddset(sigmask, signum) != 0){
2765
 
        return false;
2766
 
      }
2767
 
    }
2768
 
  }
2769
 
  return true;
2770
 
}
2771
 
 
2772
 
static void run_task_with_stderr_to_dev_null(const task_context task,
2773
 
                                             task_queue *const queue);
2774
 
 
2775
 
static
2776
 
void test_wait_for_mandos_client_exit_badpid(__attribute__((unused))
2777
 
                                             test_fixture *fixture,
2778
 
                                             __attribute__((unused))
2779
 
                                             gconstpointer user_data){
2780
 
 
2781
 
  bool mandos_client_exited = false;
2782
 
  bool quit_now = false;
2783
 
 
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,
2789
 
    .pid=1,
2790
 
    .mandos_client_exited=&mandos_client_exited,
2791
 
    .quit_now=&quit_now,
2792
 
  };
2793
 
  run_task_with_stderr_to_dev_null(task, queue);
2794
 
 
2795
 
  g_assert_false(mandos_client_exited);
2796
 
  g_assert_true(quit_now);
2797
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
2798
 
}
2799
 
 
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);
2805
 
 
2806
 
  stderr = devnull;
2807
 
  task.func(task, queue);
2808
 
  stderr = real_stderr;
2809
 
 
2810
 
  g_assert_cmpint(fclose(devnull), ==, 0);
2811
 
}
2812
 
 
2813
 
static
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;
2819
 
 
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);
2825
 
      }
2826
 
      if(not restore_sigmask(&fixture->orig_sigmask)){
2827
 
        _exit(EXIT_FAILURE);
2828
 
      }
2829
 
      while(true){
2830
 
        pause();
2831
 
      }
2832
 
    }
2833
 
    return pid;
2834
 
  }
2835
 
  pid_t pid = create_eternal_process();
2836
 
  g_assert_true(pid != -1);
2837
 
 
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,
2843
 
    .pid=pid,
2844
 
    .mandos_client_exited=&mandos_client_exited,
2845
 
    .quit_now=&quit_now,
2846
 
  };
2847
 
  task.func(task, queue);
2848
 
 
2849
 
  g_assert_false(mandos_client_exited);
2850
 
  g_assert_false(quit_now);
2851
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 1);
2852
 
 
2853
 
  g_assert_nonnull(find_matching_task(queue, (task_context){
2854
 
        .func=wait_for_mandos_client_exit,
2855
 
        .pid=task.pid,
2856
 
        .mandos_client_exited=&mandos_client_exited,
2857
 
        .quit_now=&quit_now,
2858
 
      }));
2859
 
}
2860
 
 
2861
 
static
2862
 
void test_wait_for_mandos_client_exit_success(test_fixture *fixture,
2863
 
                                              __attribute__((unused))
2864
 
                                              gconstpointer
2865
 
                                              user_data){
2866
 
  bool mandos_client_exited = false;
2867
 
  bool quit_now = false;
2868
 
 
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);
2874
 
      }
2875
 
      if(not restore_sigmask(&fixture->orig_sigmask)){
2876
 
        _exit(EXIT_FAILURE);
2877
 
      }
2878
 
      exit(EXIT_SUCCESS);
2879
 
    }
2880
 
    return pid;
2881
 
  }
2882
 
  const pid_t pid = create_successful_process();
2883
 
  g_assert_true(pid != -1);
2884
 
 
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,
2890
 
    .pid=pid,
2891
 
    .mandos_client_exited=&mandos_client_exited,
2892
 
    .quit_now=&quit_now,
2893
 
  };
2894
 
  g_assert_true(add_to_queue(queue, initial_task));
2895
 
 
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);
2900
 
  do {
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)
2906
 
          and (not quit_now)
2907
 
          and ((currtime.tv_sec - starttime.tv_sec) < 10));
2908
 
 
2909
 
  g_assert_true(mandos_client_exited);
2910
 
  g_assert_false(quit_now);
2911
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
2912
 
}
2913
 
 
2914
 
static
2915
 
void test_wait_for_mandos_client_exit_failure(test_fixture *fixture,
2916
 
                                              __attribute__((unused))
2917
 
                                              gconstpointer
2918
 
                                              user_data){
2919
 
  bool mandos_client_exited = false;
2920
 
  bool quit_now = false;
2921
 
 
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);
2927
 
      }
2928
 
      if(not restore_sigmask(&fixture->orig_sigmask)){
2929
 
        _exit(EXIT_FAILURE);
2930
 
      }
2931
 
      exit(EXIT_FAILURE);
2932
 
    }
2933
 
    return pid;
2934
 
  }
2935
 
  const pid_t pid = create_failing_process();
2936
 
  g_assert_true(pid != -1);
2937
 
 
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,
2948
 
        .pid=pid,
2949
 
        .mandos_client_exited=&mandos_client_exited,
2950
 
        .quit_now=&quit_now,
2951
 
      }));
2952
 
 
2953
 
  g_assert_true(sigismember(&fixture->orig_sigmask, SIGCHLD) == 0);
2954
 
 
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);
2962
 
 
2963
 
  struct timespec starttime, currtime;
2964
 
  g_assert_true(clock_gettime(CLOCK_MONOTONIC, &starttime) == 0);
2965
 
  do {
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,
2969
 
                                   &quit_now);
2970
 
    dup2(real_stderr_fd, STDERR_FILENO);
2971
 
    if(not success){
2972
 
      break;
2973
 
    }
2974
 
 
2975
 
    g_assert_true(clock_gettime(CLOCK_MONOTONIC, &currtime) == 0);
2976
 
  } while((not mandos_client_exited)
2977
 
          and (not quit_now)
2978
 
          and ((currtime.tv_sec - starttime.tv_sec) < 10));
2979
 
 
2980
 
  g_assert_true(quit_now);
2981
 
  g_assert_true(mandos_client_exited);
2982
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
2983
 
}
2984
 
 
2985
 
static
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;
2991
 
 
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);
2997
 
      }
2998
 
      if(not restore_sigmask(&fixture->orig_sigmask)){
2999
 
        _exit(EXIT_FAILURE);
3000
 
      }
3001
 
      while(true){
3002
 
        pause();
3003
 
      }
3004
 
    }
3005
 
    kill(pid, SIGKILL);
3006
 
    return pid;
3007
 
  }
3008
 
  const pid_t pid = create_killed_process();
3009
 
  g_assert_true(pid != -1);
3010
 
 
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,
3021
 
        .pid=pid,
3022
 
        .mandos_client_exited=&mandos_client_exited,
3023
 
        .quit_now=&quit_now,
3024
 
      }));
3025
 
 
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);
3033
 
 
3034
 
  struct timespec starttime, currtime;
3035
 
  g_assert_true(clock_gettime(CLOCK_MONOTONIC, &starttime) == 0);
3036
 
  do {
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,
3040
 
                                   &quit_now);
3041
 
    dup2(real_stderr_fd, STDERR_FILENO);
3042
 
    if(not success){
3043
 
      break;
3044
 
    }
3045
 
 
3046
 
    g_assert_true(clock_gettime(CLOCK_MONOTONIC, &currtime) == 0);
3047
 
  } while((not mandos_client_exited)
3048
 
          and (not quit_now)
3049
 
          and ((currtime.tv_sec - starttime.tv_sec) < 10));
3050
 
 
3051
 
  g_assert_true(mandos_client_exited);
3052
 
  g_assert_true(quit_now);
3053
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
3054
 
}
3055
 
 
3056
 
static bool epoll_set_does_not_contain(int, int);
3057
 
 
3058
 
static
3059
 
void test_read_mandos_client_output_readerror(__attribute__((unused))
3060
 
                                              test_fixture *fixture,
3061
 
                                              __attribute__((unused))
3062
 
                                              gconstpointer
3063
 
                                              user_data){
3064
 
  __attribute__((cleanup(cleanup_close)))
3065
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
3066
 
  g_assert_cmpint(epoll_fd, >=, 0);
3067
 
 
3068
 
  __attribute__((cleanup(cleanup_buffer)))
3069
 
    buffer password = {};
3070
 
 
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);
3074
 
 
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);
3080
 
 
3081
 
  task_context task = {
3082
 
    .func=read_mandos_client_output,
3083
 
    .epoll_fd=epoll_fd,
3084
 
    .fd=fd,
3085
 
    .password=&password,
3086
 
    .password_is_read=&password_is_read,
3087
 
    .quit_now=&quit_now,
3088
 
  };
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);
3094
 
 
3095
 
  g_assert_true(epoll_set_does_not_contain(epoll_fd, fd));
3096
 
 
3097
 
  g_assert_cmpint(close(fd), ==, -1);
3098
 
}
3099
 
 
3100
 
static bool epoll_set_does_not_contain(int epoll_fd, int fd){
3101
 
  return not epoll_set_contains(epoll_fd, fd, 0);
3102
 
}
3103
 
 
3104
 
static
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);
3112
 
 
3113
 
  int pipefds[2];
3114
 
  g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
3115
 
 
3116
 
  __attribute__((cleanup(cleanup_buffer)))
3117
 
    buffer password = {};
3118
 
 
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);
3124
 
 
3125
 
  task_context task = {
3126
 
    .func=read_mandos_client_output,
3127
 
    .epoll_fd=epoll_fd,
3128
 
    .fd=pipefds[0],
3129
 
    .password=&password,
3130
 
    .password_is_read=&password_is_read,
3131
 
    .quit_now=&quit_now,
3132
 
  };
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);
3138
 
 
3139
 
  g_assert_nonnull(find_matching_task(queue, (task_context){
3140
 
        .func=read_mandos_client_output,
3141
 
        .epoll_fd=epoll_fd,
3142
 
        .fd=pipefds[0],
3143
 
        .password=&password,
3144
 
        .password_is_read=&password_is_read,
3145
 
        .quit_now=&quit_now,
3146
 
      }));
3147
 
 
3148
 
  g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
3149
 
                                   EPOLLIN | EPOLLRDHUP));
3150
 
 
3151
 
  g_assert_cmpint(close(pipefds[1]), ==, 0);
3152
 
}
3153
 
 
3154
 
static void test_read_mandos_client_output_eof(__attribute__((unused))
3155
 
                                               test_fixture *fixture,
3156
 
                                               __attribute__((unused))
3157
 
                                               gconstpointer
3158
 
                                               user_data){
3159
 
  __attribute__((cleanup(cleanup_close)))
3160
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
3161
 
  g_assert_cmpint(epoll_fd, >=, 0);
3162
 
 
3163
 
  int pipefds[2];
3164
 
  g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
3165
 
  g_assert_cmpint(close(pipefds[1]), ==, 0);
3166
 
 
3167
 
  __attribute__((cleanup(cleanup_buffer)))
3168
 
    buffer password = {};
3169
 
 
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);
3175
 
 
3176
 
  task_context task = {
3177
 
    .func=read_mandos_client_output,
3178
 
    .epoll_fd=epoll_fd,
3179
 
    .fd=pipefds[0],
3180
 
    .password=&password,
3181
 
    .password_is_read=&password_is_read,
3182
 
    .quit_now=&quit_now,
3183
 
  };
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);
3189
 
 
3190
 
  g_assert_true(epoll_set_does_not_contain(epoll_fd, pipefds[0]));
3191
 
 
3192
 
  g_assert_cmpint(close(pipefds[0]), ==, -1);
3193
 
}
3194
 
 
3195
 
static
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);
3203
 
 
3204
 
  int pipefds[2];
3205
 
  g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
3206
 
 
3207
 
  const char dummy_test_password[] = "dummy test password";
3208
 
  /* Start with a pre-allocated buffer */
3209
 
  __attribute__((cleanup(cleanup_buffer)))
3210
 
    buffer password = {
3211
 
    .data=malloc(sizeof(dummy_test_password)),
3212
 
    .length=0,
3213
 
    .allocated=sizeof(dummy_test_password),
3214
 
  };
3215
 
  g_assert_nonnull(password.data);
3216
 
  if(mlock(password.data, password.allocated) != 0){
3217
 
    g_assert_true(errno == EPERM or errno == ENOMEM);
3218
 
  }
3219
 
 
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);
3225
 
 
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));
3230
 
 
3231
 
  task_context task = {
3232
 
    .func=read_mandos_client_output,
3233
 
    .epoll_fd=epoll_fd,
3234
 
    .fd=pipefds[0],
3235
 
    .password=&password,
3236
 
    .password_is_read=&password_is_read,
3237
 
    .quit_now=&quit_now,
3238
 
  };
3239
 
  task.func(task, queue);
3240
 
 
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);
3247
 
 
3248
 
  g_assert_false(quit_now);
3249
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 1);
3250
 
 
3251
 
  g_assert_nonnull(find_matching_task(queue, (task_context){
3252
 
        .func=read_mandos_client_output,
3253
 
        .epoll_fd=epoll_fd,
3254
 
        .fd=pipefds[0],
3255
 
        .password=&password,
3256
 
        .password_is_read=&password_is_read,
3257
 
        .quit_now=&quit_now,
3258
 
      }));
3259
 
 
3260
 
  g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
3261
 
                                   EPOLLIN | EPOLLRDHUP));
3262
 
 
3263
 
  g_assert_cmpint(close(pipefds[1]), ==, 0);
3264
 
}
3265
 
 
3266
 
static
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);
3274
 
 
3275
 
  int pipefds[2];
3276
 
  g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
3277
 
 
3278
 
  const char dummy_test_password[] = "dummy test password";
3279
 
  /* Start with an empty buffer */
3280
 
  __attribute__((cleanup(cleanup_buffer)))
3281
 
    buffer password = {};
3282
 
 
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);
3288
 
 
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));
3293
 
 
3294
 
  task_context task = {
3295
 
    .func=read_mandos_client_output,
3296
 
    .epoll_fd=epoll_fd,
3297
 
    .fd=pipefds[0],
3298
 
    .password=&password,
3299
 
    .password_is_read=&password_is_read,
3300
 
    .quit_now=&quit_now,
3301
 
  };
3302
 
  task.func(task, queue);
3303
 
 
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);
3310
 
 
3311
 
  g_assert_false(quit_now);
3312
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 1);
3313
 
 
3314
 
  g_assert_nonnull(find_matching_task(queue, (task_context){
3315
 
        .func=read_mandos_client_output,
3316
 
        .epoll_fd=epoll_fd,
3317
 
        .fd=pipefds[0],
3318
 
        .password=&password,
3319
 
        .password_is_read=&password_is_read,
3320
 
        .quit_now=&quit_now,
3321
 
      }));
3322
 
 
3323
 
  g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
3324
 
                                   EPOLLIN | EPOLLRDHUP));
3325
 
 
3326
 
  g_assert_cmpint(close(pipefds[1]), ==, 0);
3327
 
}
3328
 
 
3329
 
static
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);
3337
 
 
3338
 
  int pipefds[2];
3339
 
  g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
3340
 
 
3341
 
  const char dummy_test_password[] = "dummy test password";
3342
 
  __attribute__((cleanup(cleanup_buffer)))
3343
 
    buffer password = {
3344
 
    .data=malloc(PIPE_BUF),
3345
 
    .length=PIPE_BUF,
3346
 
    .allocated=PIPE_BUF,
3347
 
  };
3348
 
  g_assert_nonnull(password.data);
3349
 
  if(mlock(password.data, password.allocated) != 0){
3350
 
    g_assert_true(errno == EPERM or errno == ENOMEM);
3351
 
  }
3352
 
 
3353
 
  memset(password.data, 'x', PIPE_BUF);
3354
 
  char password_expected[PIPE_BUF];
3355
 
  memcpy(password_expected, password.data, PIPE_BUF);
3356
 
 
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);
3362
 
 
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));
3367
 
 
3368
 
  task_context task = {
3369
 
    .func=read_mandos_client_output,
3370
 
    .epoll_fd=epoll_fd,
3371
 
    .fd=pipefds[0],
3372
 
    .password=&password,
3373
 
    .password_is_read=&password_is_read,
3374
 
    .quit_now=&quit_now,
3375
 
  };
3376
 
  task.func(task, queue);
3377
 
 
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),
3383
 
                  ==, 0);
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);
3389
 
 
3390
 
  g_assert_nonnull(find_matching_task(queue, (task_context){
3391
 
        .func=read_mandos_client_output,
3392
 
        .epoll_fd=epoll_fd,
3393
 
        .fd=pipefds[0],
3394
 
        .password=&password,
3395
 
        .password_is_read=&password_is_read,
3396
 
        .quit_now=&quit_now,
3397
 
      }));
3398
 
 
3399
 
  g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
3400
 
                                   EPOLLIN | EPOLLRDHUP));
3401
 
}
3402
 
 
3403
 
static char *make_temporary_directory(void);
3404
 
 
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;
3418
 
 
3419
 
  bool quit_now = false;
3420
 
  buffer password = {};
3421
 
  bool mandos_client_exited = false;
3422
 
  bool password_is_read = false;
3423
 
 
3424
 
  __attribute__((cleanup(cleanup_string)))
3425
 
    char *tempdir = make_temporary_directory();
3426
 
  g_assert_nonnull(tempdir);
3427
 
 
3428
 
  g_assert_true(add_inotify_dir_watch(queue, epoll_fd, &quit_now,
3429
 
                                      &password, tempdir,
3430
 
                                      &cancelled_filenames,
3431
 
                                      &current_time,
3432
 
                                      &mandos_client_exited,
3433
 
                                      &password_is_read));
3434
 
 
3435
 
  g_assert_cmpuint((unsigned int)queue->length, >, 0);
3436
 
 
3437
 
  const task_context *const added_read_task
3438
 
    = find_matching_task(queue, (task_context){
3439
 
        .func=read_inotify_event,
3440
 
        .epoll_fd=epoll_fd,
3441
 
        .quit_now=&quit_now,
3442
 
        .password=&password,
3443
 
        .filename=tempdir,
3444
 
        .cancelled_filenames=&cancelled_filenames,
3445
 
        .current_time=&current_time,
3446
 
        .mandos_client_exited=&mandos_client_exited,
3447
 
        .password_is_read=&password_is_read,
3448
 
      });
3449
 
  g_assert_nonnull(added_read_task);
3450
 
 
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));
3456
 
 
3457
 
  g_assert_cmpint(rmdir(tempdir), ==, 0);
3458
 
}
3459
 
 
3460
 
static char *make_temporary_directory(void){
3461
 
  char *name = strdup("/tmp/mandosXXXXXX");
3462
 
  g_assert_nonnull(name);
3463
 
  char *result = mkdtemp(name);
3464
 
  if(result == NULL){
3465
 
    free(name);
3466
 
  }
3467
 
  return result;
3468
 
}
3469
 
 
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;
3483
 
 
3484
 
  bool quit_now = false;
3485
 
  buffer password = {};
3486
 
  bool mandos_client_exited = false;
3487
 
  bool password_is_read = false;
3488
 
 
3489
 
  const char nonexistent_dir[] = "/nonexistent";
3490
 
 
3491
 
  FILE *real_stderr = stderr;
3492
 
  FILE *devnull = fopen("/dev/null", "we");
3493
 
  g_assert_nonnull(devnull);
3494
 
  stderr = devnull;
3495
 
  g_assert_false(add_inotify_dir_watch(queue, epoll_fd, &quit_now,
3496
 
                                       &password, nonexistent_dir,
3497
 
                                       &cancelled_filenames,
3498
 
                                       &current_time,
3499
 
                                       &mandos_client_exited,
3500
 
                                       &password_is_read));
3501
 
  stderr = real_stderr;
3502
 
  g_assert_cmpint(fclose(devnull), ==, 0);
3503
 
 
3504
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
3505
 
}
3506
 
 
3507
 
static void test_add_inotify_dir_watch_nondir(__attribute__((unused))
3508
 
                                              test_fixture *fixture,
3509
 
                                            __attribute__((unused))
3510
 
                                              gconstpointer
3511
 
                                              user_data){
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;
3521
 
 
3522
 
  bool quit_now = false;
3523
 
  buffer password = {};
3524
 
  bool mandos_client_exited = false;
3525
 
  bool password_is_read = false;
3526
 
 
3527
 
  const char not_a_directory[] = "/dev/tty";
3528
 
 
3529
 
  FILE *real_stderr = stderr;
3530
 
  FILE *devnull = fopen("/dev/null", "we");
3531
 
  g_assert_nonnull(devnull);
3532
 
  stderr = devnull;
3533
 
  g_assert_false(add_inotify_dir_watch(queue, epoll_fd, &quit_now,
3534
 
                                       &password, not_a_directory,
3535
 
                                       &cancelled_filenames,
3536
 
                                       &current_time,
3537
 
                                       &mandos_client_exited,
3538
 
                                       &password_is_read));
3539
 
  stderr = real_stderr;
3540
 
  g_assert_cmpint(fclose(devnull), ==, 0);
3541
 
 
3542
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
3543
 
}
3544
 
 
3545
 
static void test_add_inotify_dir_watch_EAGAIN(__attribute__((unused))
3546
 
                                              test_fixture *fixture,
3547
 
                                              __attribute__((unused))
3548
 
                                              gconstpointer
3549
 
                                              user_data){
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;
3559
 
 
3560
 
  bool quit_now = false;
3561
 
  buffer password = {};
3562
 
  bool mandos_client_exited = false;
3563
 
  bool password_is_read = false;
3564
 
 
3565
 
  __attribute__((cleanup(cleanup_string)))
3566
 
    char *tempdir = make_temporary_directory();
3567
 
  g_assert_nonnull(tempdir);
3568
 
 
3569
 
  g_assert_true(add_inotify_dir_watch(queue, epoll_fd, &quit_now,
3570
 
                                      &password, tempdir,
3571
 
                                      &cancelled_filenames,
3572
 
                                      &current_time,
3573
 
                                      &mandos_client_exited,
3574
 
                                      &password_is_read));
3575
 
 
3576
 
  g_assert_cmpuint((unsigned int)queue->length, >, 0);
3577
 
 
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);
3582
 
 
3583
 
  g_assert_cmpint(added_read_task->fd, >, 2);
3584
 
  g_assert_true(fd_has_cloexec_and_nonblock(added_read_task->fd));
3585
 
 
3586
 
  /* "sufficient to read at least one event." - inotify(7) */
3587
 
  const size_t ievent_size = (sizeof(struct inotify_event)
3588
 
                              + NAME_MAX + 1);
3589
 
  struct inotify_event *ievent = malloc(ievent_size);
3590
 
  g_assert_nonnull(ievent);
3591
 
 
3592
 
  g_assert_cmpint(read(added_read_task->fd, ievent, ievent_size), ==,
3593
 
                  -1);
3594
 
  g_assert_cmpint(errno, ==, EAGAIN);
3595
 
 
3596
 
  free(ievent);
3597
 
 
3598
 
  g_assert_cmpint(rmdir(tempdir), ==, 0);
3599
 
}
3600
 
 
3601
 
static char *make_temporary_file_in_directory(const char
3602
 
                                              *const dir);
3603
 
 
3604
 
static
3605
 
void test_add_inotify_dir_watch_IN_CLOSE_WRITE(__attribute__((unused))
3606
 
                                               test_fixture *fixture,
3607
 
                                               __attribute__((unused))
3608
 
                                               gconstpointer
3609
 
                                               user_data){
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;
3619
 
 
3620
 
  bool quit_now = false;
3621
 
  buffer password = {};
3622
 
  bool mandos_client_exited = false;
3623
 
  bool password_is_read = false;
3624
 
 
3625
 
  __attribute__((cleanup(cleanup_string)))
3626
 
    char *tempdir = make_temporary_directory();
3627
 
  g_assert_nonnull(tempdir);
3628
 
 
3629
 
  g_assert_true(add_inotify_dir_watch(queue, epoll_fd, &quit_now,
3630
 
                                      &password, tempdir,
3631
 
                                      &cancelled_filenames,
3632
 
                                      &current_time,
3633
 
                                      &mandos_client_exited,
3634
 
                                      &password_is_read));
3635
 
 
3636
 
  g_assert_cmpuint((unsigned int)queue->length, >, 0);
3637
 
 
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);
3642
 
 
3643
 
  g_assert_cmpint(added_read_task->fd, >, 2);
3644
 
  g_assert_true(fd_has_cloexec_and_nonblock(added_read_task->fd));
3645
 
 
3646
 
  __attribute__((cleanup(cleanup_string)))
3647
 
    char *filename = make_temporary_file_in_directory(tempdir);
3648
 
  g_assert_nonnull(filename);
3649
 
 
3650
 
  /* "sufficient to read at least one event." - inotify(7) */
3651
 
  const size_t ievent_size = (sizeof(struct inotify_event)
3652
 
                              + NAME_MAX + 1);
3653
 
  struct inotify_event *ievent = malloc(ievent_size);
3654
 
  g_assert_nonnull(ievent);
3655
 
 
3656
 
  ssize_t read_size = 0;
3657
 
  read_size = read(added_read_task->fd, ievent, ievent_size);
3658
 
 
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));
3662
 
 
3663
 
  free(ievent);
3664
 
 
3665
 
  g_assert_cmpint(unlink(filename), ==, 0);
3666
 
  g_assert_cmpint(rmdir(tempdir), ==, 0);
3667
 
}
3668
 
 
3669
 
static char *make_temporary_prefixed_file_in_directory(const char
3670
 
                                                       *const prefix,
3671
 
                                                       const char
3672
 
                                                       *const dir){
3673
 
  char *filename = NULL;
3674
 
  g_assert_cmpint(asprintf(&filename, "%s/%sXXXXXX", dir, prefix),
3675
 
                  >, 0);
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);
3680
 
  return filename;
3681
 
}
3682
 
 
3683
 
static char *make_temporary_file_in_directory(const char
3684
 
                                              *const dir){
3685
 
  return make_temporary_prefixed_file_in_directory("temp", dir);
3686
 
}
3687
 
 
3688
 
static
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;
3702
 
 
3703
 
  bool quit_now = false;
3704
 
  buffer password = {};
3705
 
  bool mandos_client_exited = false;
3706
 
  bool password_is_read = false;
3707
 
 
3708
 
  __attribute__((cleanup(cleanup_string)))
3709
 
    char *watchdir = make_temporary_directory();
3710
 
  g_assert_nonnull(watchdir);
3711
 
 
3712
 
  g_assert_true(add_inotify_dir_watch(queue, epoll_fd, &quit_now,
3713
 
                                      &password, watchdir,
3714
 
                                      &cancelled_filenames,
3715
 
                                      &current_time,
3716
 
                                      &mandos_client_exited,
3717
 
                                      &password_is_read));
3718
 
 
3719
 
  g_assert_cmpuint((unsigned int)queue->length, >, 0);
3720
 
 
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);
3725
 
 
3726
 
  g_assert_cmpint(added_read_task->fd, >, 2);
3727
 
  g_assert_true(fd_has_cloexec_and_nonblock(added_read_task->fd));
3728
 
 
3729
 
  char *sourcedir = make_temporary_directory();
3730
 
  g_assert_nonnull(sourcedir);
3731
 
 
3732
 
  __attribute__((cleanup(cleanup_string)))
3733
 
    char *filename = make_temporary_file_in_directory(sourcedir);
3734
 
  g_assert_nonnull(filename);
3735
 
 
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);
3741
 
 
3742
 
  g_assert_cmpint(rename(filename, targetfilename), ==, 0);
3743
 
  g_assert_cmpint(rmdir(sourcedir), ==, 0);
3744
 
  free(sourcedir);
3745
 
 
3746
 
  /* "sufficient to read at least one event." - inotify(7) */
3747
 
  const size_t ievent_size = (sizeof(struct inotify_event)
3748
 
                              + NAME_MAX + 1);
3749
 
  struct inotify_event *ievent = malloc(ievent_size);
3750
 
  g_assert_nonnull(ievent);
3751
 
 
3752
 
  ssize_t read_size = read(added_read_task->fd, ievent, ievent_size);
3753
 
 
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));
3757
 
 
3758
 
  free(ievent);
3759
 
 
3760
 
  g_assert_cmpint(unlink(targetfilename), ==, 0);
3761
 
  g_assert_cmpint(rmdir(watchdir), ==, 0);
3762
 
}
3763
 
 
3764
 
static
3765
 
void test_add_inotify_dir_watch_IN_MOVED_FROM(__attribute__((unused))
3766
 
                                              test_fixture *fixture,
3767
 
                                              __attribute__((unused))
3768
 
                                              gconstpointer
3769
 
                                              user_data){
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;
3779
 
 
3780
 
  bool quit_now = false;
3781
 
  buffer password = {};
3782
 
  bool mandos_client_exited = false;
3783
 
  bool password_is_read = false;
3784
 
 
3785
 
  __attribute__((cleanup(cleanup_string)))
3786
 
    char *tempdir = make_temporary_directory();
3787
 
  g_assert_nonnull(tempdir);
3788
 
 
3789
 
  __attribute__((cleanup(cleanup_string)))
3790
 
    char *tempfilename = make_temporary_file_in_directory(tempdir);
3791
 
  g_assert_nonnull(tempfilename);
3792
 
 
3793
 
  __attribute__((cleanup(cleanup_string)))
3794
 
    char *targetdir = make_temporary_directory();
3795
 
  g_assert_nonnull(targetdir);
3796
 
 
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);
3802
 
 
3803
 
  g_assert_true(add_inotify_dir_watch(queue, epoll_fd, &quit_now,
3804
 
                                      &password, tempdir,
3805
 
                                      &cancelled_filenames,
3806
 
                                      &current_time,
3807
 
                                      &mandos_client_exited,
3808
 
                                      &password_is_read));
3809
 
 
3810
 
  g_assert_cmpint(rename(tempfilename, targetfilename), ==, 0);
3811
 
 
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);
3816
 
 
3817
 
  /* "sufficient to read at least one event." - inotify(7) */
3818
 
  const size_t ievent_size = (sizeof(struct inotify_event)
3819
 
                              + NAME_MAX + 1);
3820
 
  struct inotify_event *ievent = malloc(ievent_size);
3821
 
  g_assert_nonnull(ievent);
3822
 
 
3823
 
  ssize_t read_size = read(added_read_task->fd, ievent, ievent_size);
3824
 
 
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));
3828
 
 
3829
 
  free(ievent);
3830
 
 
3831
 
  g_assert_cmpint(unlink(targetfilename), ==, 0);
3832
 
  g_assert_cmpint(rmdir(targetdir), ==, 0);
3833
 
  g_assert_cmpint(rmdir(tempdir), ==, 0);
3834
 
}
3835
 
 
3836
 
static
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;
3850
 
 
3851
 
  bool quit_now = false;
3852
 
  buffer password = {};
3853
 
  bool mandos_client_exited = false;
3854
 
  bool password_is_read = false;
3855
 
 
3856
 
  __attribute__((cleanup(cleanup_string)))
3857
 
    char *tempdir = make_temporary_directory();
3858
 
  g_assert_nonnull(tempdir);
3859
 
 
3860
 
  __attribute__((cleanup(cleanup_string)))
3861
 
    char *tempfile = make_temporary_file_in_directory(tempdir);
3862
 
  g_assert_nonnull(tempfile);
3863
 
 
3864
 
  g_assert_true(add_inotify_dir_watch(queue, epoll_fd, &quit_now,
3865
 
                                      &password, tempdir,
3866
 
                                      &cancelled_filenames,
3867
 
                                      &current_time,
3868
 
                                      &mandos_client_exited,
3869
 
                                      &password_is_read));
3870
 
  g_assert_cmpint(unlink(tempfile), ==, 0);
3871
 
 
3872
 
  g_assert_cmpuint((unsigned int)queue->length, >, 0);
3873
 
 
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);
3878
 
 
3879
 
  g_assert_cmpint(added_read_task->fd, >, 2);
3880
 
  g_assert_true(fd_has_cloexec_and_nonblock(added_read_task->fd));
3881
 
 
3882
 
  /* "sufficient to read at least one event." - inotify(7) */
3883
 
  const size_t ievent_size = (sizeof(struct inotify_event)
3884
 
                              + NAME_MAX + 1);
3885
 
  struct inotify_event *ievent = malloc(ievent_size);
3886
 
  g_assert_nonnull(ievent);
3887
 
 
3888
 
  ssize_t read_size = 0;
3889
 
  read_size = read(added_read_task->fd, ievent, ievent_size);
3890
 
 
3891
 
  g_assert_cmpint((int)read_size, >, 0);
3892
 
  g_assert_true(ievent->mask & IN_DELETE);
3893
 
  g_assert_cmpstr(ievent->name, ==, basename(tempfile));
3894
 
 
3895
 
  free(ievent);
3896
 
 
3897
 
  g_assert_cmpint(rmdir(tempdir), ==, 0);
3898
 
}
3899
 
 
3900
 
static
3901
 
void test_add_inotify_dir_watch_IN_EXCL_UNLINK(__attribute__((unused))
3902
 
                                               test_fixture *fixture,
3903
 
                                               __attribute__((unused))
3904
 
                                               gconstpointer
3905
 
                                               user_data){
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;
3915
 
 
3916
 
  bool quit_now = false;
3917
 
  buffer password = {};
3918
 
  bool mandos_client_exited = false;
3919
 
  bool password_is_read = false;
3920
 
 
3921
 
  __attribute__((cleanup(cleanup_string)))
3922
 
    char *tempdir = make_temporary_directory();
3923
 
  g_assert_nonnull(tempdir);
3924
 
 
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
3929
 
                         | O_NOFOLLOW);
3930
 
  g_assert_cmpint(tempfile_fd, >, 2);
3931
 
 
3932
 
  g_assert_true(add_inotify_dir_watch(queue, epoll_fd, &quit_now,
3933
 
                                      &password, tempdir,
3934
 
                                      &cancelled_filenames,
3935
 
                                      &current_time,
3936
 
                                      &mandos_client_exited,
3937
 
                                      &password_is_read));
3938
 
  g_assert_cmpint(unlink(tempfile), ==, 0);
3939
 
 
3940
 
  g_assert_cmpuint((unsigned int)queue->length, >, 0);
3941
 
 
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);
3946
 
 
3947
 
  g_assert_cmpint(added_read_task->fd, >, 2);
3948
 
  g_assert_true(fd_has_cloexec_and_nonblock(added_read_task->fd));
3949
 
 
3950
 
  /* "sufficient to read at least one event." - inotify(7) */
3951
 
  const size_t ievent_size = (sizeof(struct inotify_event)
3952
 
                              + NAME_MAX + 1);
3953
 
  struct inotify_event *ievent = malloc(ievent_size);
3954
 
  g_assert_nonnull(ievent);
3955
 
 
3956
 
  ssize_t read_size = 0;
3957
 
  read_size = read(added_read_task->fd, ievent, ievent_size);
3958
 
 
3959
 
  g_assert_cmpint((int)read_size, >, 0);
3960
 
  g_assert_true(ievent->mask & IN_DELETE);
3961
 
  g_assert_cmpstr(ievent->name, ==, basename(tempfile));
3962
 
 
3963
 
  g_assert_cmpint(close(tempfile_fd), ==, 0);
3964
 
 
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);
3970
 
 
3971
 
  free(ievent);
3972
 
 
3973
 
  g_assert_cmpint(rmdir(tempdir), ==, 0);
3974
 
}
3975
 
 
3976
 
static void test_read_inotify_event_readerror(__attribute__((unused))
3977
 
                                              test_fixture *fixture,
3978
 
                                              __attribute__((unused))
3979
 
                                              gconstpointer
3980
 
                                              user_data){
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;
3985
 
 
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);
3989
 
 
3990
 
  bool quit_now = false;
3991
 
  __attribute__((cleanup(cleanup_queue)))
3992
 
    task_queue *queue = create_queue();
3993
 
  g_assert_nonnull(queue);
3994
 
 
3995
 
  task_context task = {
3996
 
    .func=read_inotify_event,
3997
 
    .epoll_fd=epoll_fd,
3998
 
    .fd=fd,
3999
 
    .quit_now=&quit_now,
4000
 
    .filename=strdup("/nonexistent"),
4001
 
    .cancelled_filenames = &(string_set){},
4002
 
    .notafter=0,
4003
 
    .current_time=&current_time,
4004
 
  };
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);
4010
 
 
4011
 
  g_assert_true(epoll_set_does_not_contain(epoll_fd, fd));
4012
 
 
4013
 
  g_assert_cmpint(close(fd), ==, -1);
4014
 
}
4015
 
 
4016
 
static void test_read_inotify_event_bad_epoll(__attribute__((unused))
4017
 
                                              test_fixture *fixture,
4018
 
                                              __attribute__((unused))
4019
 
                                              gconstpointer
4020
 
                                              user_data){
4021
 
  const mono_microsecs current_time = 17;
4022
 
 
4023
 
  int pipefds[2];
4024
 
  g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
4025
 
  const int epoll_fd = pipefds[0]; /* This will obviously fail */
4026
 
 
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);
4034
 
 
4035
 
  task_context task = {
4036
 
    .func=read_inotify_event,
4037
 
    .epoll_fd=epoll_fd,
4038
 
    .fd=pipefds[0],
4039
 
    .quit_now=&quit_now,
4040
 
    .password=&password,
4041
 
    .filename=strdup("/nonexistent"),
4042
 
    .cancelled_filenames = &(string_set){},
4043
 
    .notafter=0,
4044
 
    .current_time=&current_time,
4045
 
    .mandos_client_exited=&mandos_client_exited,
4046
 
    .password_is_read=&password_is_read,
4047
 
  };
4048
 
  g_assert_nonnull(task.filename);
4049
 
  run_task_with_stderr_to_dev_null(task, queue);
4050
 
 
4051
 
  g_assert_nonnull(find_matching_task(queue, task));
4052
 
  g_assert_true(queue->next_run == 1000000 + current_time);
4053
 
 
4054
 
  g_assert_cmpint(close(pipefds[0]), ==, 0);
4055
 
  g_assert_cmpint(close(pipefds[1]), ==, 0);
4056
 
}
4057
 
 
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;
4066
 
 
4067
 
  int pipefds[2];
4068
 
  g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
4069
 
 
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);
4077
 
 
4078
 
  task_context task = {
4079
 
    .func=read_inotify_event,
4080
 
    .epoll_fd=epoll_fd,
4081
 
    .fd=pipefds[0],
4082
 
    .quit_now=&quit_now,
4083
 
    .password=&password,
4084
 
    .filename=strdup("/nonexistent"),
4085
 
    .cancelled_filenames = &(string_set){},
4086
 
    .notafter=0,
4087
 
    .current_time=&current_time,
4088
 
    .mandos_client_exited=&mandos_client_exited,
4089
 
    .password_is_read=&password_is_read,
4090
 
  };
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);
4096
 
 
4097
 
  g_assert_nonnull(find_matching_task(queue, (task_context){
4098
 
        .func=read_inotify_event,
4099
 
        .epoll_fd=epoll_fd,
4100
 
        .fd=pipefds[0],
4101
 
        .quit_now=&quit_now,
4102
 
        .password=&password,
4103
 
        .filename=task.filename,
4104
 
        .cancelled_filenames=task.cancelled_filenames,
4105
 
        .current_time=&current_time,
4106
 
        .mandos_client_exited=&mandos_client_exited,
4107
 
        .password_is_read=&password_is_read,
4108
 
      }));
4109
 
 
4110
 
  g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
4111
 
                                   EPOLLIN | EPOLLRDHUP));
4112
 
 
4113
 
  g_assert_cmpint(close(pipefds[1]), ==, 0);
4114
 
}
4115
 
 
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;
4124
 
 
4125
 
  int pipefds[2];
4126
 
  g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
4127
 
  g_assert_cmpint(close(pipefds[1]), ==, 0);
4128
 
 
4129
 
  bool quit_now = false;
4130
 
  buffer password = {};
4131
 
  __attribute__((cleanup(cleanup_queue)))
4132
 
    task_queue *queue = create_queue();
4133
 
  g_assert_nonnull(queue);
4134
 
 
4135
 
  task_context task = {
4136
 
    .func=read_inotify_event,
4137
 
    .epoll_fd=epoll_fd,
4138
 
    .fd=pipefds[0],
4139
 
    .quit_now=&quit_now,
4140
 
    .password=&password,
4141
 
    .filename=strdup("/nonexistent"),
4142
 
    .cancelled_filenames = &(string_set){},
4143
 
    .notafter=0,
4144
 
    .current_time=&current_time,
4145
 
  };
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);
4150
 
 
4151
 
  g_assert_true(epoll_set_does_not_contain(epoll_fd, pipefds[0]));
4152
 
 
4153
 
  g_assert_cmpint(close(pipefds[0]), ==, -1);
4154
 
}
4155
 
 
4156
 
static
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;
4165
 
 
4166
 
  int pipefds[2];
4167
 
  g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
4168
 
 
4169
 
  /* "sufficient to read at least one event." - inotify(7) */
4170
 
  const size_t ievent_max_size = (sizeof(struct inotify_event)
4171
 
                                  + NAME_MAX + 1);
4172
 
  g_assert_cmpint(ievent_max_size, <=, PIPE_BUF);
4173
 
  struct {
4174
 
    struct inotify_event event;
4175
 
    char name_buffer[NAME_MAX + 1];
4176
 
  } ievent_buffer;
4177
 
  struct inotify_event *const ievent = &ievent_buffer.event;
4178
 
 
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),
4186
 
                  ==, ievent_size);
4187
 
  g_assert_cmpint(close(pipefds[1]), ==, 0);
4188
 
 
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);
4196
 
 
4197
 
  task_context task = {
4198
 
    .func=read_inotify_event,
4199
 
    .epoll_fd=epoll_fd,
4200
 
    .fd=pipefds[0],
4201
 
    .quit_now=&quit_now,
4202
 
    .password=&password,
4203
 
    .filename=strdup("/nonexistent"),
4204
 
    .cancelled_filenames = &(string_set){},
4205
 
    .notafter=0,
4206
 
    .current_time=&current_time,
4207
 
    .mandos_client_exited=&mandos_client_exited,
4208
 
    .password_is_read=&password_is_read,
4209
 
  };
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);
4214
 
 
4215
 
  g_assert_nonnull(find_matching_task(queue, (task_context){
4216
 
        .func=read_inotify_event,
4217
 
        .epoll_fd=epoll_fd,
4218
 
        .fd=pipefds[0],
4219
 
        .quit_now=&quit_now,
4220
 
        .password=&password,
4221
 
        .filename=task.filename,
4222
 
        .cancelled_filenames=task.cancelled_filenames,
4223
 
        .current_time=&current_time,
4224
 
        .mandos_client_exited=&mandos_client_exited,
4225
 
        .password_is_read=&password_is_read,
4226
 
      }));
4227
 
 
4228
 
  g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
4229
 
                                   EPOLLIN | EPOLLRDHUP));
4230
 
 
4231
 
  g_assert_cmpuint((unsigned int)queue->length, >=, 2);
4232
 
 
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,
4240
 
        .epoll_fd=epoll_fd,
4241
 
        .filename=filename,
4242
 
        .question_filename=filename,
4243
 
        .password=&password,
4244
 
        .cancelled_filenames=task.cancelled_filenames,
4245
 
        .current_time=&current_time,
4246
 
        .mandos_client_exited=&mandos_client_exited,
4247
 
        .password_is_read=&password_is_read,
4248
 
      }));
4249
 
}
4250
 
 
4251
 
static
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;
4260
 
 
4261
 
  int pipefds[2];
4262
 
  g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
4263
 
 
4264
 
  /* "sufficient to read at least one event." - inotify(7) */
4265
 
  const size_t ievent_max_size = (sizeof(struct inotify_event)
4266
 
                                  + NAME_MAX + 1);
4267
 
  g_assert_cmpint(ievent_max_size, <=, PIPE_BUF);
4268
 
  struct {
4269
 
    struct inotify_event event;
4270
 
    char name_buffer[NAME_MAX + 1];
4271
 
  } ievent_buffer;
4272
 
  struct inotify_event *const ievent = &ievent_buffer.event;
4273
 
 
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),
4281
 
                  ==, ievent_size);
4282
 
  g_assert_cmpint(close(pipefds[1]), ==, 0);
4283
 
 
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);
4291
 
 
4292
 
  task_context task = {
4293
 
    .func=read_inotify_event,
4294
 
    .epoll_fd=epoll_fd,
4295
 
    .fd=pipefds[0],
4296
 
    .quit_now=&quit_now,
4297
 
    .password=&password,
4298
 
    .filename=strdup("/nonexistent"),
4299
 
    .cancelled_filenames = &(string_set){},
4300
 
    .notafter=0,
4301
 
    .current_time=&current_time,
4302
 
    .mandos_client_exited=&mandos_client_exited,
4303
 
    .password_is_read=&password_is_read,
4304
 
  };
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);
4309
 
 
4310
 
  g_assert_nonnull(find_matching_task(queue, (task_context){
4311
 
        .func=read_inotify_event,
4312
 
        .epoll_fd=epoll_fd,
4313
 
        .fd=pipefds[0],
4314
 
        .quit_now=&quit_now,
4315
 
        .password=&password,
4316
 
        .filename=task.filename,
4317
 
        .cancelled_filenames=task.cancelled_filenames,
4318
 
        .current_time=&current_time,
4319
 
        .mandos_client_exited=&mandos_client_exited,
4320
 
        .password_is_read=&password_is_read,
4321
 
      }));
4322
 
 
4323
 
  g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
4324
 
                                   EPOLLIN | EPOLLRDHUP));
4325
 
 
4326
 
  g_assert_cmpuint((unsigned int)queue->length, >=, 2);
4327
 
 
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,
4335
 
        .epoll_fd=epoll_fd,
4336
 
        .filename=filename,
4337
 
        .question_filename=filename,
4338
 
        .password=&password,
4339
 
        .cancelled_filenames=task.cancelled_filenames,
4340
 
        .current_time=&current_time,
4341
 
        .mandos_client_exited=&mandos_client_exited,
4342
 
        .password_is_read=&password_is_read,
4343
 
      }));
4344
 
}
4345
 
 
4346
 
static
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;
4357
 
 
4358
 
  int pipefds[2];
4359
 
  g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
4360
 
 
4361
 
  /* "sufficient to read at least one event." - inotify(7) */
4362
 
  const size_t ievent_max_size = (sizeof(struct inotify_event)
4363
 
                                  + NAME_MAX + 1);
4364
 
  g_assert_cmpint(ievent_max_size, <=, PIPE_BUF);
4365
 
  struct {
4366
 
    struct inotify_event event;
4367
 
    char name_buffer[NAME_MAX + 1];
4368
 
  } ievent_buffer;
4369
 
  struct inotify_event *const ievent = &ievent_buffer.event;
4370
 
 
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),
4378
 
                  ==, ievent_size);
4379
 
  g_assert_cmpint(close(pipefds[1]), ==, 0);
4380
 
 
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);
4388
 
 
4389
 
  task_context task = {
4390
 
    .func=read_inotify_event,
4391
 
    .epoll_fd=epoll_fd,
4392
 
    .fd=pipefds[0],
4393
 
    .quit_now=&quit_now,
4394
 
    .password=&password,
4395
 
    .filename=strdup("/nonexistent"),
4396
 
    .cancelled_filenames=&cancelled_filenames,
4397
 
    .current_time=&current_time,
4398
 
    .mandos_client_exited=&mandos_client_exited,
4399
 
    .password_is_read=&password_is_read,
4400
 
  };
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);
4405
 
 
4406
 
  g_assert_nonnull(find_matching_task(queue, (task_context){
4407
 
        .func=read_inotify_event,
4408
 
        .epoll_fd=epoll_fd,
4409
 
        .fd=pipefds[0],
4410
 
        .quit_now=&quit_now,
4411
 
        .password=&password,
4412
 
        .filename=task.filename,
4413
 
        .cancelled_filenames=&cancelled_filenames,
4414
 
        .current_time=&current_time,
4415
 
        .mandos_client_exited=&mandos_client_exited,
4416
 
        .password_is_read=&password_is_read,
4417
 
      }));
4418
 
 
4419
 
  g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
4420
 
                                   EPOLLIN | EPOLLRDHUP));
4421
 
 
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,
4428
 
                                    filename));
4429
 
}
4430
 
 
4431
 
static void test_read_inotify_event_IN_DELETE(__attribute__((unused))
4432
 
                                              test_fixture *fixture,
4433
 
                                              __attribute__((unused))
4434
 
                                              gconstpointer
4435
 
                                              user_data){
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;
4442
 
 
4443
 
  int pipefds[2];
4444
 
  g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
4445
 
 
4446
 
  /* "sufficient to read at least one event." - inotify(7) */
4447
 
  const size_t ievent_max_size = (sizeof(struct inotify_event)
4448
 
                                  + NAME_MAX + 1);
4449
 
  g_assert_cmpint(ievent_max_size, <=, PIPE_BUF);
4450
 
  struct {
4451
 
    struct inotify_event event;
4452
 
    char name_buffer[NAME_MAX + 1];
4453
 
  } ievent_buffer;
4454
 
  struct inotify_event *const ievent = &ievent_buffer.event;
4455
 
 
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),
4463
 
                  ==, ievent_size);
4464
 
  g_assert_cmpint(close(pipefds[1]), ==, 0);
4465
 
 
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);
4473
 
 
4474
 
  task_context task = {
4475
 
    .func=read_inotify_event,
4476
 
    .epoll_fd=epoll_fd,
4477
 
    .fd=pipefds[0],
4478
 
    .quit_now=&quit_now,
4479
 
    .password=&password,
4480
 
    .filename=strdup("/nonexistent"),
4481
 
    .cancelled_filenames=&cancelled_filenames,
4482
 
    .current_time=&current_time,
4483
 
    .mandos_client_exited=&mandos_client_exited,
4484
 
    .password_is_read=&password_is_read,
4485
 
  };
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);
4490
 
 
4491
 
  g_assert_nonnull(find_matching_task(queue, (task_context){
4492
 
        .func=read_inotify_event,
4493
 
        .epoll_fd=epoll_fd,
4494
 
        .fd=pipefds[0],
4495
 
        .quit_now=&quit_now,
4496
 
        .password=&password,
4497
 
        .filename=task.filename,
4498
 
        .cancelled_filenames=&cancelled_filenames,
4499
 
        .current_time=&current_time,
4500
 
        .mandos_client_exited=&mandos_client_exited,
4501
 
        .password_is_read=&password_is_read,
4502
 
      }));
4503
 
 
4504
 
  g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
4505
 
                                   EPOLLIN | EPOLLRDHUP));
4506
 
 
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,
4513
 
                                    filename));
4514
 
}
4515
 
 
4516
 
static void
4517
 
test_read_inotify_event_IN_CLOSE_WRITE_badname(__attribute__((unused))
4518
 
                                               test_fixture *fixture,
4519
 
                                               __attribute__((unused))
4520
 
                                               gconstpointer
4521
 
                                               user_data){
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;
4526
 
 
4527
 
  int pipefds[2];
4528
 
  g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
4529
 
 
4530
 
  /* "sufficient to read at least one event." - inotify(7) */
4531
 
  const size_t ievent_max_size = (sizeof(struct inotify_event)
4532
 
                                  + NAME_MAX + 1);
4533
 
  g_assert_cmpint(ievent_max_size, <=, PIPE_BUF);
4534
 
  struct {
4535
 
    struct inotify_event event;
4536
 
    char name_buffer[NAME_MAX + 1];
4537
 
  } ievent_buffer;
4538
 
  struct inotify_event *const ievent = &ievent_buffer.event;
4539
 
 
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),
4547
 
                  ==, ievent_size);
4548
 
  g_assert_cmpint(close(pipefds[1]), ==, 0);
4549
 
 
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);
4557
 
 
4558
 
  task_context task = {
4559
 
    .func=read_inotify_event,
4560
 
    .epoll_fd=epoll_fd,
4561
 
    .fd=pipefds[0],
4562
 
    .quit_now=&quit_now,
4563
 
    .password=&password,
4564
 
    .filename=strdup("/nonexistent"),
4565
 
    .cancelled_filenames = &(string_set){},
4566
 
    .notafter=0,
4567
 
    .current_time=&current_time,
4568
 
    .mandos_client_exited=&mandos_client_exited,
4569
 
    .password_is_read=&password_is_read,
4570
 
  };
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);
4575
 
 
4576
 
  g_assert_nonnull(find_matching_task(queue, (task_context){
4577
 
        .func=read_inotify_event,
4578
 
        .epoll_fd=epoll_fd,
4579
 
        .fd=pipefds[0],
4580
 
        .quit_now=&quit_now,
4581
 
        .password=&password,
4582
 
        .filename=task.filename,
4583
 
        .cancelled_filenames=task.cancelled_filenames,
4584
 
        .current_time=&current_time,
4585
 
        .mandos_client_exited=&mandos_client_exited,
4586
 
        .password_is_read=&password_is_read,
4587
 
      }));
4588
 
 
4589
 
  g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
4590
 
                                   EPOLLIN | EPOLLRDHUP));
4591
 
}
4592
 
 
4593
 
static void
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;
4602
 
 
4603
 
  int pipefds[2];
4604
 
  g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
4605
 
 
4606
 
  /* "sufficient to read at least one event." - inotify(7) */
4607
 
  const size_t ievent_max_size = (sizeof(struct inotify_event)
4608
 
                                  + NAME_MAX + 1);
4609
 
  g_assert_cmpint(ievent_max_size, <=, PIPE_BUF);
4610
 
  struct {
4611
 
    struct inotify_event event;
4612
 
    char name_buffer[NAME_MAX + 1];
4613
 
  } ievent_buffer;
4614
 
  struct inotify_event *const ievent = &ievent_buffer.event;
4615
 
 
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),
4623
 
                  ==, ievent_size);
4624
 
  g_assert_cmpint(close(pipefds[1]), ==, 0);
4625
 
 
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);
4633
 
 
4634
 
  task_context task = {
4635
 
    .func=read_inotify_event,
4636
 
    .epoll_fd=epoll_fd,
4637
 
    .fd=pipefds[0],
4638
 
    .quit_now=&quit_now,
4639
 
    .password=&password,
4640
 
    .filename=strdup("/nonexistent"),
4641
 
    .cancelled_filenames = &(string_set){},
4642
 
    .notafter=0,
4643
 
    .current_time=&current_time,
4644
 
    .mandos_client_exited=&mandos_client_exited,
4645
 
    .password_is_read=&password_is_read,
4646
 
  };
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);
4651
 
 
4652
 
  g_assert_nonnull(find_matching_task(queue, (task_context){
4653
 
        .func=read_inotify_event,
4654
 
        .epoll_fd=epoll_fd,
4655
 
        .fd=pipefds[0],
4656
 
        .quit_now=&quit_now,
4657
 
        .password=&password,
4658
 
        .filename=task.filename,
4659
 
        .cancelled_filenames=task.cancelled_filenames,
4660
 
        .current_time=&current_time,
4661
 
        .mandos_client_exited=&mandos_client_exited,
4662
 
        .password_is_read=&password_is_read,
4663
 
      }));
4664
 
 
4665
 
  g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
4666
 
                                   EPOLLIN | EPOLLRDHUP));
4667
 
}
4668
 
 
4669
 
static void
4670
 
test_read_inotify_event_IN_MOVED_FROM_badname(__attribute__((unused))
4671
 
                                              test_fixture *fixture,
4672
 
                                              __attribute__((unused))
4673
 
                                              gconstpointer
4674
 
                                              user_data){
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;
4681
 
 
4682
 
  int pipefds[2];
4683
 
  g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
4684
 
 
4685
 
  /* "sufficient to read at least one event." - inotify(7) */
4686
 
  const size_t ievent_max_size = (sizeof(struct inotify_event)
4687
 
                                  + NAME_MAX + 1);
4688
 
  g_assert_cmpint(ievent_max_size, <=, PIPE_BUF);
4689
 
  struct {
4690
 
    struct inotify_event event;
4691
 
    char name_buffer[NAME_MAX + 1];
4692
 
  } ievent_buffer;
4693
 
  struct inotify_event *const ievent = &ievent_buffer.event;
4694
 
 
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),
4702
 
                  ==, ievent_size);
4703
 
  g_assert_cmpint(close(pipefds[1]), ==, 0);
4704
 
 
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);
4712
 
 
4713
 
  task_context task = {
4714
 
    .func=read_inotify_event,
4715
 
    .epoll_fd=epoll_fd,
4716
 
    .fd=pipefds[0],
4717
 
    .quit_now=&quit_now,
4718
 
    .password=&password,
4719
 
    .filename=strdup("/nonexistent"),
4720
 
    .cancelled_filenames=&cancelled_filenames,
4721
 
    .current_time=&current_time,
4722
 
    .mandos_client_exited=&mandos_client_exited,
4723
 
    .password_is_read=&password_is_read,
4724
 
  };
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);
4729
 
 
4730
 
  g_assert_nonnull(find_matching_task(queue, (task_context){
4731
 
        .func=read_inotify_event,
4732
 
        .epoll_fd=epoll_fd,
4733
 
        .fd=pipefds[0],
4734
 
        .quit_now=&quit_now,
4735
 
        .password=&password,
4736
 
        .filename=task.filename,
4737
 
        .cancelled_filenames=&cancelled_filenames,
4738
 
        .current_time=&current_time,
4739
 
        .mandos_client_exited=&mandos_client_exited,
4740
 
        .password_is_read=&password_is_read,
4741
 
      }));
4742
 
 
4743
 
  g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
4744
 
                                   EPOLLIN | EPOLLRDHUP));
4745
 
 
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));
4752
 
}
4753
 
 
4754
 
static
4755
 
void test_read_inotify_event_IN_DELETE_badname(__attribute__((unused))
4756
 
                                               test_fixture *fixture,
4757
 
                                               __attribute__((unused))
4758
 
                                               gconstpointer
4759
 
                                               user_data){
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;
4766
 
 
4767
 
  int pipefds[2];
4768
 
  g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
4769
 
 
4770
 
  /* "sufficient to read at least one event." - inotify(7) */
4771
 
  const size_t ievent_max_size = (sizeof(struct inotify_event)
4772
 
                                  + NAME_MAX + 1);
4773
 
  g_assert_cmpint(ievent_max_size, <=, PIPE_BUF);
4774
 
  struct {
4775
 
    struct inotify_event event;
4776
 
    char name_buffer[NAME_MAX + 1];
4777
 
  } ievent_buffer;
4778
 
  struct inotify_event *const ievent = &ievent_buffer.event;
4779
 
 
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),
4787
 
                  ==, ievent_size);
4788
 
  g_assert_cmpint(close(pipefds[1]), ==, 0);
4789
 
 
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);
4797
 
 
4798
 
  task_context task = {
4799
 
    .func=read_inotify_event,
4800
 
    .epoll_fd=epoll_fd,
4801
 
    .fd=pipefds[0],
4802
 
    .quit_now=&quit_now,
4803
 
    .password=&password,
4804
 
    .filename=strdup("/nonexistent"),
4805
 
    .cancelled_filenames=&cancelled_filenames,
4806
 
    .current_time=&current_time,
4807
 
    .mandos_client_exited=&mandos_client_exited,
4808
 
    .password_is_read=&password_is_read,
4809
 
  };
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);
4814
 
 
4815
 
  g_assert_nonnull(find_matching_task(queue, (task_context){
4816
 
        .func=read_inotify_event,
4817
 
        .epoll_fd=epoll_fd,
4818
 
        .fd=pipefds[0],
4819
 
        .quit_now=&quit_now,
4820
 
        .password=&password,
4821
 
        .filename=task.filename,
4822
 
        .cancelled_filenames=&cancelled_filenames,
4823
 
        .current_time=&current_time,
4824
 
        .mandos_client_exited=&mandos_client_exited,
4825
 
        .password_is_read=&password_is_read,
4826
 
      }));
4827
 
 
4828
 
  g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
4829
 
                                   EPOLLIN | EPOLLRDHUP));
4830
 
 
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));
4837
 
}
4838
 
 
4839
 
static
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);
4854
 
 
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,
4860
 
    .epoll_fd=epoll_fd,
4861
 
    .password=(buffer[]){{}},
4862
 
    .filename=filename,
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,
4867
 
  };
4868
 
  task.func(task, queue);
4869
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
4870
 
}
4871
 
 
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;
4888
 
 
4889
 
  char *filename = strdup("/proc/self/mem");
4890
 
  task_context task = {
4891
 
    .func=open_and_parse_question,
4892
 
    .question_filename=filename,
4893
 
    .epoll_fd=epoll_fd,
4894
 
    .password=&password,
4895
 
    .filename=filename,
4896
 
    .cancelled_filenames=&cancelled_filenames,
4897
 
    .current_time=&current_time,
4898
 
    .mandos_client_exited=&mandos_client_exited,
4899
 
    .password_is_read=&password_is_read,
4900
 
  };
4901
 
  run_task_with_stderr_to_dev_null(task, queue);
4902
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
4903
 
}
4904
 
 
4905
 
static void
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);
4918
 
 
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);
4928
 
 
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,
4934
 
    .epoll_fd=epoll_fd,
4935
 
    .password=(buffer[]){{}},
4936
 
    .filename=filename,
4937
 
    .cancelled_filenames=&cancelled_filenames,
4938
 
    .current_time=(mono_microsecs[]){0},
4939
 
    .mandos_client_exited=(bool[]){false},
4940
 
    .password_is_read=(bool[]){false},
4941
 
  };
4942
 
  run_task_with_stderr_to_dev_null(task, queue);
4943
 
 
4944
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
4945
 
 
4946
 
  g_assert_cmpint(unlink(tempfilename), ==, 0);
4947
 
}
4948
 
 
4949
 
static
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);
4962
 
 
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);
4971
 
 
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,
4977
 
    .epoll_fd=epoll_fd,
4978
 
    .password=(buffer[]){{}},
4979
 
    .filename=filename,
4980
 
    .cancelled_filenames=&cancelled_filenames,
4981
 
    .current_time=(mono_microsecs[]){0},
4982
 
    .mandos_client_exited=(bool[]){false},
4983
 
    .password_is_read=(bool[]){false},
4984
 
  };
4985
 
  run_task_with_stderr_to_dev_null(task, queue);
4986
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
4987
 
 
4988
 
  g_assert_cmpint(unlink(tempfilename), ==, 0);
4989
 
}
4990
 
 
4991
 
static
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);
5004
 
 
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);
5013
 
 
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,
5019
 
    .epoll_fd=epoll_fd,
5020
 
    .password=(buffer[]){{}},
5021
 
    .filename=filename,
5022
 
    .cancelled_filenames=&cancelled_filenames,
5023
 
    .current_time=(mono_microsecs[]){0},
5024
 
    .mandos_client_exited=(bool[]){false},
5025
 
    .password_is_read=(bool[]){false},
5026
 
  };
5027
 
  run_task_with_stderr_to_dev_null(task, queue);
5028
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
5029
 
 
5030
 
  g_assert_cmpint(unlink(tempfilename), ==, 0);
5031
 
}
5032
 
 
5033
 
static
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);
5046
 
 
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);
5055
 
 
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,
5061
 
    .epoll_fd=epoll_fd,
5062
 
    .password=(buffer[]){{}},
5063
 
    .filename=filename,
5064
 
    .cancelled_filenames=&cancelled_filenames,
5065
 
    .current_time=(mono_microsecs[]){0},
5066
 
    .mandos_client_exited=(bool[]){false},
5067
 
    .password_is_read=(bool[]){false},
5068
 
  };
5069
 
  run_task_with_stderr_to_dev_null(task, queue);
5070
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
5071
 
 
5072
 
  g_assert_cmpint(unlink(tempfilename), ==, 0);
5073
 
}
5074
 
 
5075
 
static
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);
5088
 
 
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"),
5096
 
                  >, 0);
5097
 
  g_assert_cmpint(fclose(qf), ==, 0);
5098
 
 
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,
5104
 
    .epoll_fd=epoll_fd,
5105
 
    .password=(buffer[]){{}},
5106
 
    .filename=filename,
5107
 
    .cancelled_filenames=&cancelled_filenames,
5108
 
    .current_time=(mono_microsecs[]){0},
5109
 
    .mandos_client_exited=(bool[]){false},
5110
 
    .password_is_read=(bool[]){false},
5111
 
  };
5112
 
  run_task_with_stderr_to_dev_null(task, queue);
5113
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
5114
 
 
5115
 
  g_assert_cmpint(unlink(tempfilename), ==, 0);
5116
 
}
5117
 
 
5118
 
static void
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;
5135
 
 
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),
5141
 
                  ==, 1);
5142
 
  g_assert_cmpint(fclose(sysctl_pid_max), ==, 0);
5143
 
 
5144
 
  pid_t nonexisting_pid = ((pid_t)pid_max)+1;
5145
 
  g_assert_true(nonexisting_pid > 0); /* Overflow check */
5146
 
 
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),
5155
 
                  >, 0);
5156
 
  g_assert_cmpint(fclose(qf), ==, 0);
5157
 
 
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,
5163
 
    .epoll_fd=epoll_fd,
5164
 
    .password=&password,
5165
 
    .filename=question_filename,
5166
 
    .cancelled_filenames=&cancelled_filenames,
5167
 
    .current_time=&current_time,
5168
 
    .mandos_client_exited=&mandos_client_exited,
5169
 
    .password_is_read=&password_is_read,
5170
 
  };
5171
 
  run_task_with_stderr_to_dev_null(task, queue);
5172
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
5173
 
 
5174
 
  g_assert_cmpint(unlink(tempfilename), ==, 0);
5175
 
}
5176
 
 
5177
 
static void
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;
5194
 
 
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);
5204
 
 
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,
5210
 
    .epoll_fd=epoll_fd,
5211
 
    .password=&password,
5212
 
    .filename=filename,
5213
 
    .cancelled_filenames=&cancelled_filenames,
5214
 
    .current_time=&current_time,
5215
 
    .mandos_client_exited=&mandos_client_exited,
5216
 
    .password_is_read=&password_is_read,
5217
 
  };
5218
 
  task.func(task, queue);
5219
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 1);
5220
 
 
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,
5228
 
        .epoll_fd=epoll_fd,
5229
 
        .password=&password,
5230
 
        .current_time=&current_time,
5231
 
        .mandos_client_exited=&mandos_client_exited,
5232
 
        .password_is_read=&password_is_read,
5233
 
      }));
5234
 
 
5235
 
  g_assert_true(queue->next_run != 0);
5236
 
 
5237
 
  g_assert_cmpint(unlink(tempfilename), ==, 0);
5238
 
}
5239
 
 
5240
 
static void
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;
5257
 
 
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);
5268
 
 
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,
5274
 
    .epoll_fd=epoll_fd,
5275
 
    .password=&password,
5276
 
    .filename=filename,
5277
 
    .cancelled_filenames=&cancelled_filenames,
5278
 
    .current_time=&current_time,
5279
 
    .mandos_client_exited=&mandos_client_exited,
5280
 
    .password_is_read=&password_is_read,
5281
 
  };
5282
 
  run_task_with_stderr_to_dev_null(task, queue);
5283
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 1);
5284
 
 
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,
5291
 
        .epoll_fd=epoll_fd,
5292
 
        .password=&password,
5293
 
        .current_time=&current_time,
5294
 
        .mandos_client_exited=&mandos_client_exited,
5295
 
        .password_is_read=&password_is_read,
5296
 
      }));
5297
 
  g_assert_true(queue->next_run != 0);
5298
 
 
5299
 
  g_assert_cmpint(unlink(tempfilename), ==, 0);
5300
 
}
5301
 
 
5302
 
static
5303
 
void assert_open_and_parse_question_with_notafter(const mono_microsecs
5304
 
                                                  current_time,
5305
 
                                                  const mono_microsecs
5306
 
                                                  notafter,
5307
 
                                                  const mono_microsecs
5308
 
                                                  next_queue_run){
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;
5321
 
 
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);
5332
 
 
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,
5338
 
    .epoll_fd=epoll_fd,
5339
 
    .password=&password,
5340
 
    .filename=filename,
5341
 
    .cancelled_filenames=&cancelled_filenames,
5342
 
    .current_time=&current_time,
5343
 
    .mandos_client_exited=&mandos_client_exited,
5344
 
    .password_is_read=&password_is_read,
5345
 
  };
5346
 
  task.func(task, queue);
5347
 
 
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,
5354
 
          .epoll_fd=epoll_fd,
5355
 
          .password=&password,
5356
 
          .current_time=&current_time,
5357
 
          .cancelled_filenames=&cancelled_filenames,
5358
 
          .mandos_client_exited=&mandos_client_exited,
5359
 
          .password_is_read=&password_is_read,
5360
 
        }));
5361
 
    g_assert_true(queue->next_run != 0);
5362
 
  }
5363
 
 
5364
 
  if(notafter == 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);
5368
 
  } else {
5369
 
    g_assert_nonnull(find_matching_task(queue, (task_context){
5370
 
          .func=cancel_old_question,
5371
 
          .question_filename=tempfilename,
5372
 
          .filename=tempfilename,
5373
 
          .notafter=notafter,
5374
 
          .cancelled_filenames=&cancelled_filenames,
5375
 
          .current_time=&current_time,
5376
 
        }));
5377
 
  }
5378
 
  g_assert_true(queue->next_run == 1);
5379
 
 
5380
 
  g_assert_cmpint(unlink(tempfilename), ==, 0);
5381
 
}
5382
 
 
5383
 
static void
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);
5390
 
}
5391
 
 
5392
 
static void
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);
5399
 
}
5400
 
 
5401
 
static void
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);
5408
 
}
5409
 
 
5410
 
static void
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);
5417
 
}
5418
 
 
5419
 
static void
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);
5426
 
}
5427
 
 
5428
 
static void
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);
5435
 
}
5436
 
 
5437
 
static void assert_cancel_old_question_param(const mono_microsecs
5438
 
                                             next_queue_run,
5439
 
                                             const mono_microsecs
5440
 
                                             notafter,
5441
 
                                             const mono_microsecs
5442
 
                                             current_time,
5443
 
                                             const mono_microsecs
5444
 
                                             next_set_to){
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;
5451
 
 
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,
5458
 
    .notafter=notafter,
5459
 
    .cancelled_filenames=&cancelled_filenames,
5460
 
    .current_time=&current_time,
5461
 
  };
5462
 
  task.func(task, queue);
5463
 
 
5464
 
  if(current_time >= notafter){
5465
 
    g_assert_cmpuint((unsigned int)queue->length, ==, 0);
5466
 
    g_assert_true(string_set_contains(cancelled_filenames,
5467
 
                                      "/nonexistent"));
5468
 
  } else {
5469
 
    g_assert_nonnull(find_matching_task(queue, (task_context){
5470
 
          .func=cancel_old_question,
5471
 
          .question_filename=question_filename,
5472
 
          .filename=question_filename,
5473
 
          .notafter=notafter,
5474
 
          .cancelled_filenames=&cancelled_filenames,
5475
 
          .current_time=&current_time,
5476
 
        }));
5477
 
 
5478
 
    g_assert_false(string_set_contains(cancelled_filenames,
5479
 
                                       question_filename));
5480
 
  }
5481
 
  g_assert_cmpuint((unsigned int)queue->next_run, ==,
5482
 
                   (unsigned int)next_set_to);
5483
 
}
5484
 
 
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);
5494
 
}
5495
 
 
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);
5505
 
}
5506
 
 
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);
5516
 
}
5517
 
 
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);
5528
 
}
5529
 
 
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);
5539
 
}
5540
 
 
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);
5551
 
}
5552
 
 
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);
5562
 
}
5563
 
 
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);
5574
 
}
5575
 
 
5576
 
static void
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),
5599
 
                  >, 0);
5600
 
  g_assert_nonnull(filename);
5601
 
 
5602
 
  task_context task = {
5603
 
    .func=connect_question_socket,
5604
 
    .question_filename=strdup(question_filename),
5605
 
    .epoll_fd=epoll_fd,
5606
 
    .password=(buffer[]){{}},
5607
 
    .filename=filename,
5608
 
    .cancelled_filenames=&cancelled_filenames,
5609
 
    .mandos_client_exited=(bool[]){false},
5610
 
    .password_is_read=(bool[]){false},
5611
 
    .current_time=(mono_microsecs[]){0},
5612
 
  };
5613
 
  g_assert_nonnull(task.question_filename);
5614
 
  run_task_with_stderr_to_dev_null(task, queue);
5615
 
 
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);
5620
 
 
5621
 
  g_assert_cmpint(rmdir(tempdir), ==, 0);
5622
 
}
5623
 
 
5624
 
static
5625
 
void test_connect_question_socket_connect_fail(__attribute__((unused))
5626
 
                                               test_fixture *fixture,
5627
 
                                               __attribute__((unused))
5628
 
                                               gconstpointer
5629
 
                                               user_data){
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),
5646
 
                  >, 0);
5647
 
  g_assert_nonnull(filename);
5648
 
 
5649
 
  task_context task = {
5650
 
    .func=connect_question_socket,
5651
 
    .question_filename=strdup(question_filename),
5652
 
    .epoll_fd=epoll_fd,
5653
 
    .password=(buffer[]){{}},
5654
 
    .filename=filename,
5655
 
    .cancelled_filenames=&cancelled_filenames,
5656
 
    .mandos_client_exited=(bool[]){false},
5657
 
    .password_is_read=(bool[]){false},
5658
 
    .current_time=&current_time,
5659
 
  };
5660
 
  g_assert_nonnull(task.question_filename);
5661
 
  run_task_with_stderr_to_dev_null(task, queue);
5662
 
 
5663
 
  g_assert_nonnull(find_matching_task(queue, task));
5664
 
 
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);
5669
 
 
5670
 
  g_assert_cmpint(rmdir(tempdir), ==, 0);
5671
 
}
5672
 
 
5673
 
static
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),
5702
 
                  >, 0);
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),
5713
 
    .epoll_fd=epoll_fd,
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=&current_time,
5720
 
  };
5721
 
  g_assert_nonnull(task.question_filename);
5722
 
  run_task_with_stderr_to_dev_null(task, queue);
5723
 
 
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);
5729
 
 
5730
 
  g_assert_cmpint(unlink(filename), ==, 0);
5731
 
  g_assert_cmpint(rmdir(tempdir), ==, 0);
5732
 
}
5733
 
 
5734
 
static
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),
5766
 
                  >, 0);
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),
5777
 
    .epoll_fd=epoll_fd,
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=&current_time,
5784
 
  };
5785
 
  g_assert_nonnull(task.question_filename);
5786
 
  task.func(task, queue);
5787
 
 
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,
5793
 
        .filename=filename,
5794
 
        .epoll_fd=epoll_fd,
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=&current_time,
5800
 
      });
5801
 
  g_assert_nonnull(added_task);
5802
 
  g_assert_cmpint(added_task->fd, >, 0);
5803
 
 
5804
 
  g_assert_true(epoll_set_contains(epoll_fd, added_task->fd,
5805
 
                                   EPOLLOUT));
5806
 
 
5807
 
  const int fd = added_task->fd;
5808
 
  g_assert_cmpint(fd, >, 0);
5809
 
  g_assert_true(fd_has_cloexec_and_nonblock(fd));
5810
 
 
5811
 
  /* write to fd */
5812
 
  char write_data[PIPE_BUF];
5813
 
  {
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));
5825
 
  }
5826
 
 
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));
5831
 
 
5832
 
  g_assert_true(memcmp(write_data, read_data, sizeof(write_data))
5833
 
                == 0);
5834
 
 
5835
 
  /* writing to sock_fd should fail */
5836
 
  g_assert_cmpint(send(sock_fd, write_data, sizeof(write_data),
5837
 
                       MSG_NOSIGNAL), <, 0);
5838
 
 
5839
 
  /* reading from fd should fail */
5840
 
  g_assert_cmpint((int)recv(fd, read_data, sizeof(read_data),
5841
 
                            MSG_NOSIGNAL), <, 0);
5842
 
 
5843
 
  g_assert_cmpint(unlink(filename), ==, 0);
5844
 
  g_assert_cmpint(rmdir(tempdir), ==, 0);
5845
 
}
5846
 
 
5847
 
static void
5848
 
test_send_password_to_socket_client_not_exited(__attribute__((unused))
5849
 
                                               test_fixture *fixture,
5850
 
                                               __attribute__((unused))
5851
 
                                               gconstpointer
5852
 
                                               user_data){
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);
5869
 
  int socketfds[2];
5870
 
  g_assert_cmpint(socketpair(PF_LOCAL, SOCK_DGRAM
5871
 
                             | SOCK_NONBLOCK | SOCK_CLOEXEC, 0,
5872
 
                             socketfds), ==, 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),
5880
 
    .epoll_fd=epoll_fd,
5881
 
    .fd=write_socket,
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},
5887
 
  };
5888
 
  g_assert_nonnull(task.question_filename);
5889
 
 
5890
 
  task.func(task, queue);
5891
 
 
5892
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 1);
5893
 
 
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);
5899
 
 
5900
 
  g_assert_cmpint(added_task->fd, >, 0);
5901
 
  g_assert_true(epoll_set_contains(epoll_fd, added_task->fd,
5902
 
                                   EPOLLOUT));
5903
 
}
5904
 
 
5905
 
static void
5906
 
test_send_password_to_socket_password_not_read(__attribute__((unused))
5907
 
                                               test_fixture *fixture,
5908
 
                                               __attribute__((unused))
5909
 
                                               gconstpointer
5910
 
                                               user_data){
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);
5925
 
  int socketfds[2];
5926
 
  g_assert_cmpint(socketpair(PF_LOCAL, SOCK_DGRAM
5927
 
                             | SOCK_NONBLOCK | SOCK_CLOEXEC, 0,
5928
 
                             socketfds), ==, 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),
5936
 
    .epoll_fd=epoll_fd,
5937
 
    .fd=write_socket,
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},
5943
 
  };
5944
 
  g_assert_nonnull(task.question_filename);
5945
 
 
5946
 
  task.func(task, queue);
5947
 
 
5948
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 1);
5949
 
 
5950
 
  const task_context *const added_task = find_matching_task(queue,
5951
 
                                                            task);
5952
 
  g_assert_nonnull(added_task);
5953
 
  g_assert_cmpuint((unsigned int)password.length, ==, 0);
5954
 
  g_assert_true(queue->next_run == 0);
5955
 
 
5956
 
  g_assert_cmpint(added_task->fd, >, 0);
5957
 
  g_assert_true(epoll_set_contains(epoll_fd, added_task->fd,
5958
 
                                   EPOLLOUT));
5959
 
}
5960
 
 
5961
 
static
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 = {};
5973
 
  int socketfds[2];
5974
 
 
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");
5982
 
      return;
5983
 
    }
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);
5989
 
      return;
5990
 
    }
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
5995
 
    */
5996
 
    g_assert_cmpint(socketpair(PF_LOCAL, SOCK_DGRAM
5997
 
                               | SOCK_NONBLOCK | SOCK_CLOEXEC, 0,
5998
 
                               socketfds), ==, 0);
5999
 
    ssret = send(socketfds[1], message_buffer, message_size,
6000
 
                 MSG_NOSIGNAL);
6001
 
    error_t saved_errno = errno;
6002
 
    g_assert_cmpint(close(socketfds[0]), ==, 0);
6003
 
    g_assert_cmpint(close(socketfds[1]), ==, 0);
6004
 
 
6005
 
    if(ssret < 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));
6011
 
        return;
6012
 
      }
6013
 
      break;
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);
6019
 
      return;
6020
 
    }
6021
 
  }
6022
 
  g_test_message("EMSGSIZE triggered by %" PRIdMAX " bytes",
6023
 
                 (intmax_t)message_size);
6024
 
 
6025
 
  buffer password = {
6026
 
    .data=message_buffer,
6027
 
    .length=message_size - 2,   /* Compensate for added '+' and NUL */
6028
 
    .allocated=message_size,
6029
 
  };
6030
 
  if(mlock(password.data, password.allocated) != 0){
6031
 
    g_assert_true(errno == EPERM or errno == ENOMEM);
6032
 
  }
6033
 
 
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,
6039
 
                             socketfds), ==, 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),
6047
 
    .filename=filename,
6048
 
    .epoll_fd=epoll_fd,
6049
 
    .fd=write_socket,
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},
6055
 
  };
6056
 
  g_assert_nonnull(task.question_filename);
6057
 
 
6058
 
  run_task_with_stderr_to_dev_null(task, queue);
6059
 
 
6060
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
6061
 
  g_assert_true(string_set_contains(cancelled_filenames,
6062
 
                                    question_filename));
6063
 
}
6064
 
 
6065
 
static void test_send_password_to_socket_retry(__attribute__((unused))
6066
 
                                               test_fixture *fixture,
6067
 
                                               __attribute__((unused))
6068
 
                                               gconstpointer
6069
 
                                               user_data){
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 = {};
6083
 
 
6084
 
  __attribute__((cleanup(cleanup_queue)))
6085
 
    task_queue *queue = create_queue();
6086
 
  g_assert_nonnull(queue);
6087
 
  int socketfds[2];
6088
 
  g_assert_cmpint(socketpair(PF_LOCAL, SOCK_DGRAM
6089
 
                             | SOCK_NONBLOCK | SOCK_CLOEXEC, 0,
6090
 
                             socketfds), ==, 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),
6100
 
    .epoll_fd=epoll_fd,
6101
 
    .fd=write_socket,
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},
6107
 
  };
6108
 
  g_assert_nonnull(task.question_filename);
6109
 
 
6110
 
  task.func(task, queue);
6111
 
 
6112
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 1);
6113
 
 
6114
 
  const task_context *const added_task = find_matching_task(queue,
6115
 
                                                            task);
6116
 
  g_assert_nonnull(added_task);
6117
 
  g_assert_cmpuint((unsigned int)password.length, ==, 0);
6118
 
 
6119
 
  g_assert_true(epoll_set_contains(epoll_fd, added_task->fd,
6120
 
                                   EPOLLOUT));
6121
 
}
6122
 
 
6123
 
static
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 = {};
6141
 
 
6142
 
  const mono_microsecs current_time = 11;
6143
 
  __attribute__((cleanup(cleanup_queue)))
6144
 
    task_queue *queue = create_queue();
6145
 
  g_assert_nonnull(queue);
6146
 
  int socketfds[2];
6147
 
  g_assert_cmpint(socketpair(PF_LOCAL, SOCK_DGRAM
6148
 
                             | SOCK_NONBLOCK | SOCK_CLOEXEC, 0,
6149
 
                             socketfds), ==, 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),
6159
 
    .epoll_fd=epoll_fd,
6160
 
    .fd=write_socket,
6161
 
    .password=&password,
6162
 
    .cancelled_filenames=&cancelled_filenames,
6163
 
    .mandos_client_exited=(bool[]){true},
6164
 
    .password_is_read=(bool[]){true},
6165
 
    .current_time=&current_time,
6166
 
  };
6167
 
  g_assert_nonnull(task.question_filename);
6168
 
 
6169
 
  run_task_with_stderr_to_dev_null(task, queue);
6170
 
 
6171
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 1);
6172
 
 
6173
 
  const task_context *const added_task = find_matching_task(queue,
6174
 
                                                            task);
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);
6178
 
}
6179
 
 
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 = {};
6190
 
 
6191
 
  __attribute__((cleanup(cleanup_queue)))
6192
 
    task_queue *queue = create_queue();
6193
 
  g_assert_nonnull(queue);
6194
 
  int socketfds[2];
6195
 
  g_assert_cmpint(socketpair(PF_LOCAL, SOCK_DGRAM
6196
 
                             | SOCK_NONBLOCK | SOCK_CLOEXEC, 0,
6197
 
                             socketfds), ==, 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,
6204
 
    .filename=filename,
6205
 
    .epoll_fd=epoll_fd,
6206
 
    .fd=write_socket,
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},
6212
 
  };
6213
 
 
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);
6221
 
  }
6222
 
 
6223
 
  task.func(task, queue);
6224
 
 
6225
 
  char buf[PIPE_BUF];
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);
6229
 
 
6230
 
  g_assert_true(memcmp(expected_written_data, buf,
6231
 
                       password.length + 2) == 0);
6232
 
 
6233
 
  g_assert_true(epoll_set_does_not_contain(epoll_fd, write_socket));
6234
 
 
6235
 
  free(expected_written_data);
6236
 
}
6237
 
 
6238
 
static void
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);
6246
 
}
6247
 
 
6248
 
static void
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)))
6254
 
    buffer password = {
6255
 
    .data=malloc(1),           /* because malloc(0) may return NULL */
6256
 
    .length=0,
6257
 
    .allocated=0,               /* deliberate lie */
6258
 
  };
6259
 
  g_assert_nonnull(password.data);
6260
 
  assert_send_password_to_socket_password(password);
6261
 
}
6262
 
 
6263
 
static void
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)))
6269
 
    buffer password = {
6270
 
    .data=strdup(""),
6271
 
    .length=0,
6272
 
    .allocated=1,
6273
 
  };
6274
 
  if(mlock(password.data, password.allocated) != 0){
6275
 
    g_assert_true(errno == EPERM or errno == ENOMEM);
6276
 
  }
6277
 
  assert_send_password_to_socket_password(password);
6278
 
}
6279
 
 
6280
 
static void
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)))
6287
 
    buffer password = {
6288
 
    .data = strdup(dummy_test_password),
6289
 
    .length = strlen(dummy_test_password),
6290
 
    .allocated = sizeof(dummy_test_password),
6291
 
  };
6292
 
  if(mlock(password.data, password.allocated) != 0){
6293
 
    g_assert_true(errno == EPERM or errno == ENOMEM);
6294
 
  }
6295
 
  assert_send_password_to_socket_password(password);
6296
 
}
6297
 
 
6298
 
static void
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)))
6304
 
    buffer password = {
6305
 
    .data=malloc(255),
6306
 
    .length=255,
6307
 
    .allocated=255,
6308
 
  };
6309
 
  g_assert_nonnull(password.data);
6310
 
  if(mlock(password.data, password.allocated) != 0){
6311
 
    g_assert_true(errno == EPERM or errno == ENOMEM);
6312
 
  }
6313
 
  char c = 1;                   /* Start at 1, avoiding NUL */
6314
 
  for(int i=0; i < 255; i++){
6315
 
    password.data[i] = c++;
6316
 
  }
6317
 
  assert_send_password_to_socket_password(password);
6318
 
}
6319
 
 
6320
 
static void
6321
 
test_send_password_to_socket_nuls_in_password(__attribute__((unused))
6322
 
                                              test_fixture *fixture,
6323
 
                                              __attribute__((unused))
6324
 
                                              gconstpointer
6325
 
                                              user_data){
6326
 
  char test_password[] = {'\0', 'a', '\0', 'b', '\0', 'c', '\0'};
6327
 
  __attribute__((cleanup(cleanup_buffer)))
6328
 
    buffer password = {
6329
 
    .data=malloc(sizeof(test_password)),
6330
 
    .length=sizeof(test_password),
6331
 
    .allocated=sizeof(test_password),
6332
 
  };
6333
 
  g_assert_nonnull(password.data);
6334
 
  if(mlock(password.data, password.allocated) !=0){
6335
 
    g_assert_true(errno == EPERM or errno == ENOMEM);
6336
 
  }
6337
 
  memcpy(password.data, test_password, password.allocated);
6338
 
  assert_send_password_to_socket_password(password);
6339
 
}
6340
 
 
6341
 
static bool assert_add_existing_questions_to_devnull(task_queue
6342
 
                                                     *const,
6343
 
                                                     const int,
6344
 
                                                     buffer *const,
6345
 
                                                     string_set *,
6346
 
                                                     const
6347
 
                                                     mono_microsecs
6348
 
                                                     *const,
6349
 
                                                     bool *const,
6350
 
                                                     bool *const,
6351
 
                                                     const char
6352
 
                                                     *const);
6353
 
 
6354
 
static void test_add_existing_questions_ENOENT(__attribute__((unused))
6355
 
                                               test_fixture *fixture,
6356
 
                                               __attribute__((unused))
6357
 
                                               gconstpointer
6358
 
                                               user_data){
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 = {};
6367
 
 
6368
 
  g_assert_false(assert_add_existing_questions_to_devnull
6369
 
                 (queue,
6370
 
                  epoll_fd,
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 */
6377
 
 
6378
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
6379
 
}
6380
 
 
6381
 
static
6382
 
bool assert_add_existing_questions_to_devnull(task_queue
6383
 
                                              *const queue,
6384
 
                                              const int
6385
 
                                              epoll_fd,
6386
 
                                              buffer *const
6387
 
                                              password,
6388
 
                                              string_set
6389
 
                                              *cancelled_filenames,
6390
 
                                              const mono_microsecs
6391
 
                                              *const current_time,
6392
 
                                              bool *const
6393
 
                                              mandos_client_exited,
6394
 
                                              bool *const
6395
 
                                              password_is_read,
6396
 
                                              const char *const
6397
 
                                              dirname){
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,
6408
 
                                          current_time,
6409
 
                                          mandos_client_exited,
6410
 
                                          password_is_read, dirname);
6411
 
  dup2(real_stderr_fd, STDERR_FILENO);
6412
 
  return ret;
6413
 
}
6414
 
 
6415
 
static
6416
 
void test_add_existing_questions_no_questions(__attribute__((unused))
6417
 
                                              test_fixture *fixture,
6418
 
                                              __attribute__((unused))
6419
 
                                              gconstpointer
6420
 
                                              user_data){
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);
6432
 
 
6433
 
  g_assert_false(assert_add_existing_questions_to_devnull
6434
 
                 (queue,
6435
 
                  epoll_fd,
6436
 
                  (buffer[]){{}}, /* password */
6437
 
                  &cancelled_filenames,
6438
 
                  (mono_microsecs[]){0}, /* current_time */
6439
 
                  (bool[]){false},       /* mandos_client_exited */
6440
 
                  (bool[]){false},       /* password_is_read */
6441
 
                  tempdir));
6442
 
 
6443
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
6444
 
 
6445
 
  g_assert_cmpint(rmdir(tempdir), ==, 0);
6446
 
}
6447
 
 
6448
 
static char *make_question_file_in_directory(const char *const);
6449
 
 
6450
 
static
6451
 
void test_add_existing_questions_one_question(__attribute__((unused))
6452
 
                                              test_fixture *fixture,
6453
 
                                              __attribute__((unused))
6454
 
                                              gconstpointer
6455
 
                                              user_data){
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);
6476
 
 
6477
 
  g_assert_true(assert_add_existing_questions_to_devnull
6478
 
                (queue,
6479
 
                 epoll_fd,
6480
 
                 &password,
6481
 
                 &cancelled_filenames,
6482
 
                 &current_time,
6483
 
                 &mandos_client_exited,
6484
 
                 &password_is_read,
6485
 
                 tempdir));
6486
 
 
6487
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 1);
6488
 
 
6489
 
  g_assert_nonnull(find_matching_task(queue, (task_context){
6490
 
        .func=open_and_parse_question,
6491
 
        .epoll_fd=epoll_fd,
6492
 
        .filename=question_filename,
6493
 
        .question_filename=question_filename,
6494
 
        .password=&password,
6495
 
        .cancelled_filenames=&cancelled_filenames,
6496
 
        .current_time=&current_time,
6497
 
        .mandos_client_exited=&mandos_client_exited,
6498
 
        .password_is_read=&password_is_read,
6499
 
      }));
6500
 
 
6501
 
  g_assert_true(queue->next_run == 1);
6502
 
 
6503
 
  g_assert_cmpint(unlink(question_filename), ==, 0);
6504
 
  g_assert_cmpint(rmdir(tempdir), ==, 0);
6505
 
}
6506
 
 
6507
 
static char *make_question_file_in_directory(const char
6508
 
                                             *const dir){
6509
 
  return make_temporary_prefixed_file_in_directory("ask.", dir);
6510
 
}
6511
 
 
6512
 
static
6513
 
void test_add_existing_questions_two_questions(__attribute__((unused))
6514
 
                                               test_fixture *fixture,
6515
 
                                               __attribute__((unused))
6516
 
                                               gconstpointer
6517
 
                                               user_data){
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);
6542
 
 
6543
 
  g_assert_true(assert_add_existing_questions_to_devnull
6544
 
                (queue,
6545
 
                 epoll_fd,
6546
 
                 &password,
6547
 
                 &cancelled_filenames,
6548
 
                 &current_time,
6549
 
                 &mandos_client_exited,
6550
 
                 &password_is_read,
6551
 
                 tempdir));
6552
 
 
6553
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 2);
6554
 
 
6555
 
  g_assert_true(queue->next_run == 1);
6556
 
 
6557
 
  __attribute__((cleanup(string_set_clear)))
6558
 
    string_set seen_questions = {};
6559
 
 
6560
 
  bool queue_contains_question_opener(char *const question_filename){
6561
 
    return(find_matching_task(queue, (task_context){
6562
 
          .func=open_and_parse_question,
6563
 
          .epoll_fd=epoll_fd,
6564
 
          .question_filename=question_filename,
6565
 
          .password=&password,
6566
 
          .cancelled_filenames=&cancelled_filenames,
6567
 
          .current_time=&current_time,
6568
 
          .mandos_client_exited=&mandos_client_exited,
6569
 
          .password_is_read=&password_is_read,
6570
 
        }) != NULL);
6571
 
  }
6572
 
 
6573
 
  g_assert_true(queue_contains_question_opener(question_filename1));
6574
 
  g_assert_true(queue_contains_question_opener(question_filename2));
6575
 
 
6576
 
  g_assert_true(queue->next_run == 1);
6577
 
 
6578
 
  g_assert_cmpint(unlink(question_filename1), ==, 0);
6579
 
  g_assert_cmpint(unlink(question_filename2), ==, 0);
6580
 
  g_assert_cmpint(rmdir(tempdir), ==, 0);
6581
 
}
6582
 
 
6583
 
static void
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);
6607
 
 
6608
 
  g_assert_false(assert_add_existing_questions_to_devnull
6609
 
                 (queue,
6610
 
                  epoll_fd,
6611
 
                  (buffer[]){{}}, /* password */
6612
 
                  &cancelled_filenames,
6613
 
                  (mono_microsecs[]){0}, /* current_time */
6614
 
                  (bool[]){false},       /* mandos_client_exited */
6615
 
                  (bool[]){false},       /* password_is_read */
6616
 
                  tempdir));
6617
 
 
6618
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
6619
 
 
6620
 
  g_assert_cmpint(unlink(question_filename1), ==, 0);
6621
 
  g_assert_cmpint(unlink(question_filename2), ==, 0);
6622
 
  g_assert_cmpint(rmdir(tempdir), ==, 0);
6623
 
}
6624
 
 
6625
 
static void
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);
6656
 
 
6657
 
  g_assert_true(assert_add_existing_questions_to_devnull
6658
 
                (queue,
6659
 
                 epoll_fd,
6660
 
                 &password,
6661
 
                 &cancelled_filenames,
6662
 
                 &current_time,
6663
 
                 &mandos_client_exited,
6664
 
                 &password_is_read,
6665
 
                 tempdir));
6666
 
 
6667
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 1);
6668
 
 
6669
 
  g_assert_nonnull(find_matching_task(queue, (task_context){
6670
 
        .func=open_and_parse_question,
6671
 
        .epoll_fd=epoll_fd,
6672
 
        .filename=question_filename,
6673
 
        .question_filename=question_filename,
6674
 
        .password=&password,
6675
 
        .cancelled_filenames=&cancelled_filenames,
6676
 
        .current_time=&current_time,
6677
 
        .mandos_client_exited=&mandos_client_exited,
6678
 
        .password_is_read=&password_is_read,
6679
 
      }));
6680
 
 
6681
 
  g_assert_true(queue->next_run == 1);
6682
 
 
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);
6687
 
}
6688
 
 
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);
6696
 
 
6697
 
  g_assert_true(wait_for_event(epoll_fd, 1, 0));
6698
 
}
6699
 
 
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);
6707
 
  int pipefds[2];
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);
6717
 
 
6718
 
  g_assert_true(wait_for_event(epoll_fd, 0, 0));
6719
 
}
6720
 
 
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);
6728
 
    }
6729
 
    if(not restore_sigmask(&fixture->orig_sigmask)){
6730
 
      _exit(EXIT_FAILURE);
6731
 
    }
6732
 
    exit(EXIT_SUCCESS);
6733
 
  }
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);
6738
 
 
6739
 
  g_assert_true(wait_for_event(epoll_fd, 0, 0));
6740
 
 
6741
 
  int status;
6742
 
  g_assert_true(waitpid(pid, &status, 0) == pid);
6743
 
  g_assert_true(WIFEXITED(status));
6744
 
  g_assert_cmpint(WEXITSTATUS(status), ==, EXIT_SUCCESS);
6745
 
}
6746
 
 
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;
6760
 
 
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);
6764
 
}
6765
 
 
6766
 
static
6767
 
void test_run_queue_clears_cancelled_filenames(__attribute__((unused))
6768
 
                                               test_fixture *fixture,
6769
 
                                               __attribute__((unused))
6770
 
                                               gconstpointer
6771
 
                                               user_data){
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));
6781
 
 
6782
 
  g_assert_true(add_to_queue(queue,
6783
 
                             (task_context){ .func=dummy_func }));
6784
 
 
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));
6790
 
}
6791
 
 
6792
 
static
6793
 
void test_run_queue_skips_cancelled_filenames(__attribute__((unused))
6794
 
                                              test_fixture *fixture,
6795
 
                                              __attribute__((unused))
6796
 
                                              gconstpointer
6797
 
                                              user_data){
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;
6804
 
  int pipefds[2];
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;
6817
 
  }
6818
 
  task_context task = {
6819
 
    .func=quit_func,
6820
 
    .question_filename=strdup(question_filename),
6821
 
    .quit_now=&quit_now,
6822
 
    .fd=read_pipe,
6823
 
  };
6824
 
  g_assert_nonnull(task.question_filename);
6825
 
 
6826
 
  g_assert_true(add_to_queue(queue, task));
6827
 
 
6828
 
  g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
6829
 
  g_assert_false(quit_now);
6830
 
 
6831
 
  /* read_pipe should be closed already */
6832
 
  errno = 0;
6833
 
  bool read_pipe_closed = (close(read_pipe) == -1);
6834
 
  read_pipe_closed &= (errno == EBADF);
6835
 
  g_assert_true(read_pipe_closed);
6836
 
}
6837
 
 
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;
6848
 
 
6849
 
  __attribute__((nonnull))
6850
 
    void next_run_func(__attribute__((unused))
6851
 
                       const task_context task,
6852
 
                       task_queue *const q){
6853
 
    q->next_run = 1;
6854
 
  }
6855
 
 
6856
 
  task_context task = {
6857
 
    .func=next_run_func,
6858
 
  };
6859
 
  g_assert_true(add_to_queue(queue, task));
6860
 
 
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);
6864
 
}
6865
 
 
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;
6878
 
 
6879
 
  __attribute__((nonnull))
6880
 
    void next_run_func(__attribute__((unused))
6881
 
                       const task_context task,
6882
 
                       task_queue *const q){
6883
 
    q->next_run = 1;
6884
 
  }
6885
 
 
6886
 
  __attribute__((nonnull))
6887
 
    void exited_func(const task_context task,
6888
 
                     __attribute__((unused)) task_queue *const q){
6889
 
    *task.mandos_client_exited = true;
6890
 
  }
6891
 
 
6892
 
  task_context task1 = {
6893
 
    .func=next_run_func,
6894
 
  };
6895
 
  g_assert_true(add_to_queue(queue, task1));
6896
 
 
6897
 
  task_context task2 = {
6898
 
    .func=exited_func,
6899
 
    .mandos_client_exited=&mandos_client_exited,
6900
 
  };
6901
 
  g_assert_true(add_to_queue(queue, task2));
6902
 
 
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);
6908
 
}
6909
 
 
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;
6922
 
 
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;
6928
 
  }
6929
 
  task_context task1 = {
6930
 
    .func=set_exited_func,
6931
 
    .quit_now=&quit_now,
6932
 
    .mandos_client_exited=&mandos_client_exited,
6933
 
  };
6934
 
  g_assert_true(add_to_queue(queue, task1));
6935
 
 
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;
6941
 
  }
6942
 
  task_context task2 = {
6943
 
    .func=set_read_func,
6944
 
    .quit_now=&quit_now,
6945
 
    .password_is_read=&password_is_read,
6946
 
  };
6947
 
  g_assert_true(add_to_queue(queue, task2));
6948
 
 
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);
6953
 
}
6954
 
 
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 = {};
6964
 
  int pipefds[2];
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;
6971
 
 
6972
 
  __attribute__((nonnull))
6973
 
    void read_func(const task_context task,
6974
 
                   __attribute__((unused)) task_queue *const q){
6975
 
    *task.quit_now = true;
6976
 
  }
6977
 
  task_context task1 = {
6978
 
    .func=read_func,
6979
 
    .quit_now=&quit_now,
6980
 
    .fd=read_pipe,
6981
 
  };
6982
 
  g_assert_true(add_to_queue(queue, task1));
6983
 
 
6984
 
  __attribute__((nonnull))
6985
 
    void write_func(const task_context task,
6986
 
                    __attribute__((unused)) task_queue *const q){
6987
 
    *task.quit_now = true;
6988
 
  }
6989
 
  task_context task2 = {
6990
 
    .func=write_func,
6991
 
    .quit_now=&quit_now,
6992
 
    .fd=write_pipe,
6993
 
  };
6994
 
  g_assert_true(add_to_queue(queue, task2));
6995
 
 
6996
 
  g_assert_false(run_queue(&queue, &cancelled_filenames, &quit_now));
6997
 
  g_assert_true(quit_now);
6998
 
 
6999
 
  /* Either read_pipe or write_pipe should be closed already */
7000
 
  errno = 0;
7001
 
  bool close_read_pipe = (close(read_pipe) == -1);
7002
 
  close_read_pipe &= (errno == EBADF);
7003
 
  errno = 0;
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);
7008
 
}
7009
 
 
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),
7017
 
                  ==, 0);
7018
 
 
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));
7022
 
 
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);
7039
 
  }
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)));
7045
 
 
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
7056
 
                == handle_sigchld);
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;
7061
 
    switch(signum){
7062
 
    case SIGTERM:
7063
 
    case SIGINT:
7064
 
    case SIGQUIT:
7065
 
    case SIGHUP:
7066
 
      actual_new_block_state
7067
 
        = sigismember(&actual_new_sigchld_action.sa_mask, signum);
7068
 
      g_assert_cmpint(actual_new_block_state, ==, 1);
7069
 
      continue;
7070
 
    case SIGKILL:               /* non-blockable */
7071
 
    case SIGSTOP:               /* non-blockable */
7072
 
    case SIGCHLD:               /* always blocked */
7073
 
    default:
7074
 
      continue;
7075
 
    }
7076
 
  }
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));
7081
 
 
7082
 
  /* Restore signal handler */
7083
 
  g_assert_cmpint(sigaction(SIGCHLD, &expected_sigchld_action, NULL),
7084
 
                  ==, 0);
7085
 
}
7086
 
 
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),
7094
 
                  ==, 0);
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);
7102
 
 
7103
 
  /* Establish a signal handler */
7104
 
  struct sigaction sigchld_action = {
7105
 
    .sa_handler=handle_sigchld,
7106
 
    .sa_flags=SA_RESTART | SA_NOCLDSTOP,
7107
 
  };
7108
 
  g_assert_cmpint(sigfillset(&sigchld_action.sa_mask), ==, 0);
7109
 
  g_assert_cmpint(sigaction(SIGCHLD, &sigchld_action, NULL), ==, 0);
7110
 
 
7111
 
  /* Act; i.e. run the restore_signal_handler() function */
7112
 
  g_assert_true(restore_signal_handler(&expected_sigchld_action));
7113
 
 
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);
7118
 
 
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);
7135
 
  }
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)));
7141
 
}
7142
 
 
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),
7150
 
                  ==, 0);
7151
 
 
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,
7157
 
                                  NULL), ==, 0);
7158
 
 
7159
 
  /* Act; i.e. run the block_sigchld() function */
7160
 
  sigset_t actual_old_sigmask;
7161
 
  g_assert_true(block_sigchld(&actual_old_sigmask));
7162
 
 
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);
7174
 
  }
7175
 
 
7176
 
  /* Retrieve the newly set signal mask */
7177
 
  sigset_t actual_sigmask;
7178
 
  g_assert_cmpint(pthread_sigmask(-1, NULL, &actual_sigmask), ==, 0);
7179
 
 
7180
 
  /* SIGCHLD should be blocked */
7181
 
  g_assert_cmpint(sigismember(&actual_sigmask, SIGCHLD), ==, 1);
7182
 
 
7183
 
  /* Restore signal mask */
7184
 
  g_assert_cmpint(pthread_sigmask(SIG_SETMASK, &expected_sigmask,
7185
 
                                  NULL), ==, 0);
7186
 
}
7187
 
 
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);
7195
 
 
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,
7201
 
                                  NULL), ==, 0);
7202
 
 
7203
 
  /* Act; i.e. run the restore_sigmask() function */
7204
 
  g_assert_true(restore_sigmask(&orig_sigmask));
7205
 
 
7206
 
  /* Retrieve the newly restored signal mask */
7207
 
  sigset_t restored_sigmask;
7208
 
  g_assert_cmpint(pthread_sigmask(-1, NULL, &restored_sigmask),
7209
 
                  ==, 0);
7210
 
 
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,
7217
 
                                                 signum);
7218
 
    g_assert_cmpint(restored_block_state, >=, 0);
7219
 
    g_assert_cmpint(restored_block_state, ==, orig_block_state);
7220
 
  }
7221
 
 
7222
 
  /* Restore signal mask */
7223
 
  g_assert_cmpint(pthread_sigmask(SIG_SETMASK, &orig_sigmask,
7224
 
                                  NULL), ==, 0);
7225
 
}
7226
 
 
7227
 
static void test_parse_arguments_noargs(__attribute__((unused))
7228
 
                                        test_fixture *fixture,
7229
 
                                        __attribute__((unused))
7230
 
                                        gconstpointer user_data){
7231
 
  char *argv[] = {
7232
 
    strdup("prgname"),
7233
 
    NULL };
7234
 
  const int argc = (sizeof(argv) / sizeof(char *)) - 1;
7235
 
 
7236
 
  char *agent_directory = NULL;
7237
 
  char *helper_directory = NULL;
7238
 
  uid_t user = 0;
7239
 
  gid_t group = 0;
7240
 
  char *mandos_argz = NULL;
7241
 
  size_t mandos_argz_length = 0;
7242
 
 
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);
7252
 
 
7253
 
  for(char **arg = argv; *arg != NULL; arg++){
7254
 
    free(*arg);
7255
 
  }
7256
 
}
7257
 
 
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,
7263
 
                                    uid_t *const user,
7264
 
                                    gid_t *const group,
7265
 
                                    char **mandos_argz,
7266
 
                                    size_t *mandos_argz_length){
7267
 
 
7268
 
  FILE *real_stderr = stderr;
7269
 
  FILE *devnull = fopen("/dev/null", "we");
7270
 
  g_assert_nonnull(devnull);
7271
 
  stderr = devnull;
7272
 
 
7273
 
  const bool ret = parse_arguments(argc, argv, exit_failure,
7274
 
                                   agent_directory,
7275
 
                                   helper_directory, user, group,
7276
 
                                   mandos_argz, mandos_argz_length);
7277
 
  const error_t saved_errno = errno;
7278
 
 
7279
 
  stderr = real_stderr;
7280
 
  g_assert_cmpint(fclose(devnull), ==, 0);
7281
 
 
7282
 
  errno = saved_errno;
7283
 
 
7284
 
  return ret;
7285
 
}
7286
 
 
7287
 
static void test_parse_arguments_invalid(__attribute__((unused))
7288
 
                                         test_fixture *fixture,
7289
 
                                         __attribute__((unused))
7290
 
                                         gconstpointer user_data){
7291
 
  char *argv[] = {
7292
 
    strdup("prgname"),
7293
 
    strdup("--invalid"),
7294
 
    NULL };
7295
 
  const int argc = (sizeof(argv) / sizeof(char *)) - 1;
7296
 
 
7297
 
  char *agent_directory = NULL;
7298
 
  char *helper_directory = NULL;
7299
 
  uid_t user = 0;
7300
 
  gid_t group = 0;
7301
 
  char *mandos_argz = NULL;
7302
 
  size_t mandos_argz_length = 0;
7303
 
 
7304
 
  g_assert_false(parse_arguments_devnull(argc, argv, false,
7305
 
                                         &agent_directory,
7306
 
                                         &helper_directory, &user,
7307
 
                                         &group, &mandos_argz,
7308
 
                                         &mandos_argz_length));
7309
 
 
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);
7315
 
 
7316
 
  for(char **arg = argv; *arg != NULL; arg++){
7317
 
    free(*arg);
7318
 
  }
7319
 
}
7320
 
 
7321
 
static void test_parse_arguments_long_dir(__attribute__((unused))
7322
 
                                          test_fixture *fixture,
7323
 
                                          __attribute__((unused))
7324
 
                                          gconstpointer user_data){
7325
 
  char *argv[] = {
7326
 
    strdup("prgname"),
7327
 
    strdup("--agent-directory"),
7328
 
    strdup("/tmp"),
7329
 
    NULL };
7330
 
  const int argc = (sizeof(argv) / sizeof(char *)) - 1;
7331
 
 
7332
 
  __attribute__((cleanup(cleanup_string)))
7333
 
    char *agent_directory = NULL;
7334
 
  char *helper_directory = NULL;
7335
 
  uid_t user = 0;
7336
 
  gid_t group = 0;
7337
 
  __attribute__((cleanup(cleanup_string)))
7338
 
    char *mandos_argz = NULL;
7339
 
  size_t mandos_argz_length = 0;
7340
 
 
7341
 
  g_assert_true(parse_arguments(argc, argv, false, &agent_directory,
7342
 
                                &helper_directory, &user, &group,
7343
 
                                &mandos_argz, &mandos_argz_length));
7344
 
 
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);
7351
 
 
7352
 
  for(char **arg = argv; *arg != NULL; arg++){
7353
 
    free(*arg);
7354
 
  }
7355
 
}
7356
 
 
7357
 
static void test_parse_arguments_short_dir(__attribute__((unused))
7358
 
                                           test_fixture *fixture,
7359
 
                                           __attribute__((unused))
7360
 
                                           gconstpointer user_data){
7361
 
  char *argv[] = {
7362
 
    strdup("prgname"),
7363
 
    strdup("-d"),
7364
 
    strdup("/tmp"),
7365
 
    NULL };
7366
 
  const int argc = (sizeof(argv) / sizeof(char *)) - 1;
7367
 
 
7368
 
  __attribute__((cleanup(cleanup_string)))
7369
 
    char *agent_directory = NULL;
7370
 
  char *helper_directory = NULL;
7371
 
  uid_t user = 0;
7372
 
  gid_t group = 0;
7373
 
  __attribute__((cleanup(cleanup_string)))
7374
 
    char *mandos_argz = NULL;
7375
 
  size_t mandos_argz_length = 0;
7376
 
 
7377
 
  g_assert_true(parse_arguments(argc, argv, false, &agent_directory,
7378
 
                                &helper_directory, &user, &group,
7379
 
                                &mandos_argz, &mandos_argz_length));
7380
 
 
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);
7387
 
 
7388
 
  for(char **arg = argv; *arg != NULL; arg++){
7389
 
    free(*arg);
7390
 
  }
7391
 
}
7392
 
 
7393
 
static
7394
 
void test_parse_arguments_helper_directory(__attribute__((unused))
7395
 
                                           test_fixture *fixture,
7396
 
                                           __attribute__((unused))
7397
 
                                           gconstpointer user_data){
7398
 
  char *argv[] = {
7399
 
    strdup("prgname"),
7400
 
    strdup("--helper-directory"),
7401
 
    strdup("/tmp"),
7402
 
    NULL };
7403
 
  const int argc = (sizeof(argv) / sizeof(char *)) - 1;
7404
 
 
7405
 
  char *agent_directory = NULL;
7406
 
  __attribute__((cleanup(cleanup_string)))
7407
 
    char *helper_directory = NULL;
7408
 
  uid_t user = 0;
7409
 
  gid_t group = 0;
7410
 
  __attribute__((cleanup(cleanup_string)))
7411
 
    char *mandos_argz = NULL;
7412
 
  size_t mandos_argz_length = 0;
7413
 
 
7414
 
  g_assert_true(parse_arguments(argc, argv, false, &agent_directory,
7415
 
                                &helper_directory, &user, &group,
7416
 
                                &mandos_argz, &mandos_argz_length));
7417
 
 
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);
7424
 
 
7425
 
  for(char **arg = argv; *arg != NULL; arg++){
7426
 
    free(*arg);
7427
 
  }
7428
 
}
7429
 
 
7430
 
static
7431
 
void test_parse_arguments_plugin_helper_dir(__attribute__((unused))
7432
 
                                            test_fixture *fixture,
7433
 
                                            __attribute__((unused))
7434
 
                                            gconstpointer user_data){
7435
 
  char *argv[] = {
7436
 
    strdup("prgname"),
7437
 
    strdup("--plugin-helper-dir"),
7438
 
    strdup("/tmp"),
7439
 
    NULL };
7440
 
  const int argc = (sizeof(argv) / sizeof(char *)) - 1;
7441
 
 
7442
 
  char *agent_directory = NULL;
7443
 
  __attribute__((cleanup(cleanup_string)))
7444
 
    char *helper_directory = NULL;
7445
 
  uid_t user = 0;
7446
 
  gid_t group = 0;
7447
 
  __attribute__((cleanup(cleanup_string)))
7448
 
    char *mandos_argz = NULL;
7449
 
  size_t mandos_argz_length = 0;
7450
 
 
7451
 
  g_assert_true(parse_arguments(argc, argv, false, &agent_directory,
7452
 
                                &helper_directory, &user, &group,
7453
 
                                &mandos_argz, &mandos_argz_length));
7454
 
 
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);
7461
 
 
7462
 
  for(char **arg = argv; *arg != NULL; arg++){
7463
 
    free(*arg);
7464
 
  }
7465
 
}
7466
 
 
7467
 
static void test_parse_arguments_user(__attribute__((unused))
7468
 
                                      test_fixture *fixture,
7469
 
                                      __attribute__((unused))
7470
 
                                      gconstpointer user_data){
7471
 
  char *argv[] = {
7472
 
    strdup("prgname"),
7473
 
    strdup("--user"),
7474
 
    strdup("1000"),
7475
 
    NULL };
7476
 
  const int argc = (sizeof(argv) / sizeof(char *)) - 1;
7477
 
 
7478
 
  char *agent_directory = NULL;
7479
 
  __attribute__((cleanup(cleanup_string)))
7480
 
    char *helper_directory = NULL;
7481
 
  uid_t user = 0;
7482
 
  gid_t group = 0;
7483
 
  __attribute__((cleanup(cleanup_string)))
7484
 
    char *mandos_argz = NULL;
7485
 
  size_t mandos_argz_length = 0;
7486
 
 
7487
 
  g_assert_true(parse_arguments(argc, argv, false, &agent_directory,
7488
 
                                &helper_directory, &user, &group,
7489
 
                                &mandos_argz, &mandos_argz_length));
7490
 
 
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);
7497
 
 
7498
 
  for(char **arg = argv; *arg != NULL; arg++){
7499
 
    free(*arg);
7500
 
  }
7501
 
}
7502
 
 
7503
 
static void test_parse_arguments_user_invalid(__attribute__((unused))
7504
 
                                              test_fixture *fixture,
7505
 
                                              __attribute__((unused))
7506
 
                                              gconstpointer
7507
 
                                              user_data){
7508
 
  char *argv[] = {
7509
 
    strdup("prgname"),
7510
 
    strdup("--user"),
7511
 
    strdup("invalid"),
7512
 
    NULL };
7513
 
  const int argc = (sizeof(argv) / sizeof(char *)) - 1;
7514
 
 
7515
 
  char *agent_directory = NULL;
7516
 
  __attribute__((cleanup(cleanup_string)))
7517
 
    char *helper_directory = NULL;
7518
 
  uid_t user = 0;
7519
 
  gid_t group = 0;
7520
 
  __attribute__((cleanup(cleanup_string)))
7521
 
    char *mandos_argz = NULL;
7522
 
  size_t mandos_argz_length = 0;
7523
 
 
7524
 
  g_assert_false(parse_arguments_devnull(argc, argv, false,
7525
 
                                         &agent_directory,
7526
 
                                         &helper_directory, &user,
7527
 
                                         &group, &mandos_argz,
7528
 
                                         &mandos_argz_length));
7529
 
 
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);
7536
 
 
7537
 
  for(char **arg = argv; *arg != NULL; arg++){
7538
 
    free(*arg);
7539
 
  }
7540
 
}
7541
 
 
7542
 
static
7543
 
void test_parse_arguments_user_zero_invalid(__attribute__((unused))
7544
 
                                            test_fixture *fixture,
7545
 
                                            __attribute__((unused))
7546
 
                                            gconstpointer user_data){
7547
 
  char *argv[] = {
7548
 
    strdup("prgname"),
7549
 
    strdup("--user"),
7550
 
    strdup("0"),
7551
 
    NULL };
7552
 
  const int argc = (sizeof(argv) / sizeof(char *)) - 1;
7553
 
 
7554
 
  char *agent_directory = NULL;
7555
 
  __attribute__((cleanup(cleanup_string)))
7556
 
    char *helper_directory = NULL;
7557
 
  uid_t user = 0;
7558
 
  gid_t group = 0;
7559
 
  __attribute__((cleanup(cleanup_string)))
7560
 
    char *mandos_argz = NULL;
7561
 
  size_t mandos_argz_length = 0;
7562
 
 
7563
 
  g_assert_false(parse_arguments_devnull(argc, argv, false,
7564
 
                                         &agent_directory,
7565
 
                                         &helper_directory, &user,
7566
 
                                         &group, &mandos_argz,
7567
 
                                         &mandos_argz_length));
7568
 
 
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);
7575
 
 
7576
 
  for(char **arg = argv; *arg != NULL; arg++){
7577
 
    free(*arg);
7578
 
  }
7579
 
}
7580
 
 
7581
 
static void test_parse_arguments_group(__attribute__((unused))
7582
 
                                       test_fixture *fixture,
7583
 
                                       __attribute__((unused))
7584
 
                                       gconstpointer user_data){
7585
 
  char *argv[] = {
7586
 
    strdup("prgname"),
7587
 
    strdup("--group"),
7588
 
    strdup("1000"),
7589
 
    NULL };
7590
 
  const int argc = (sizeof(argv) / sizeof(char *)) - 1;
7591
 
 
7592
 
  char *agent_directory = NULL;
7593
 
  __attribute__((cleanup(cleanup_string)))
7594
 
    char *helper_directory = NULL;
7595
 
  uid_t user = 0;
7596
 
  gid_t group = 0;
7597
 
  __attribute__((cleanup(cleanup_string)))
7598
 
    char *mandos_argz = NULL;
7599
 
  size_t mandos_argz_length = 0;
7600
 
 
7601
 
  g_assert_true(parse_arguments(argc, argv, false, &agent_directory,
7602
 
                                &helper_directory, &user, &group,
7603
 
                                &mandos_argz, &mandos_argz_length));
7604
 
 
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);
7611
 
 
7612
 
  for(char **arg = argv; *arg != NULL; arg++){
7613
 
    free(*arg);
7614
 
  }
7615
 
}
7616
 
 
7617
 
static void test_parse_arguments_group_invalid(__attribute__((unused))
7618
 
                                               test_fixture *fixture,
7619
 
                                               __attribute__((unused))
7620
 
                                               gconstpointer
7621
 
                                               user_data){
7622
 
  char *argv[] = {
7623
 
    strdup("prgname"),
7624
 
    strdup("--group"),
7625
 
    strdup("invalid"),
7626
 
    NULL };
7627
 
  const int argc = (sizeof(argv) / sizeof(char *)) - 1;
7628
 
 
7629
 
  char *agent_directory = NULL;
7630
 
  __attribute__((cleanup(cleanup_string)))
7631
 
    char *helper_directory = NULL;
7632
 
  uid_t user = 0;
7633
 
  gid_t group = 0;
7634
 
  __attribute__((cleanup(cleanup_string)))
7635
 
    char *mandos_argz = NULL;
7636
 
  size_t mandos_argz_length = 0;
7637
 
 
7638
 
  g_assert_false(parse_arguments_devnull(argc, argv, false,
7639
 
                                         &agent_directory,
7640
 
                                         &helper_directory, &user,
7641
 
                                         &group, &mandos_argz,
7642
 
                                         &mandos_argz_length));
7643
 
 
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);
7650
 
 
7651
 
  for(char **arg = argv; *arg != NULL; arg++){
7652
 
    free(*arg);
7653
 
  }
7654
 
}
7655
 
 
7656
 
static
7657
 
void test_parse_arguments_group_zero_invalid(__attribute__((unused))
7658
 
                                             test_fixture *fixture,
7659
 
                                             __attribute__((unused))
7660
 
                                             gconstpointer user_data){
7661
 
  char *argv[] = {
7662
 
    strdup("prgname"),
7663
 
    strdup("--group"),
7664
 
    strdup("0"),
7665
 
    NULL };
7666
 
  const int argc = (sizeof(argv) / sizeof(char *)) - 1;
7667
 
 
7668
 
  char *agent_directory = NULL;
7669
 
  __attribute__((cleanup(cleanup_string)))
7670
 
    char *helper_directory = NULL;
7671
 
  uid_t user = 0;
7672
 
  gid_t group = 0;
7673
 
  __attribute__((cleanup(cleanup_string)))
7674
 
    char *mandos_argz = NULL;
7675
 
  size_t mandos_argz_length = 0;
7676
 
 
7677
 
  g_assert_false(parse_arguments_devnull(argc, argv, false,
7678
 
                                         &agent_directory,
7679
 
                                         &helper_directory, &user,
7680
 
                                         &group, &mandos_argz,
7681
 
                                         &mandos_argz_length));
7682
 
 
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);
7689
 
 
7690
 
  for(char **arg = argv; *arg != NULL; arg++){
7691
 
    free(*arg);
7692
 
  }
7693
 
}
7694
 
 
7695
 
static void test_parse_arguments_mandos_noargs(__attribute__((unused))
7696
 
                                               test_fixture *fixture,
7697
 
                                               __attribute__((unused))
7698
 
                                               gconstpointer
7699
 
                                               user_data){
7700
 
  char *argv[] = {
7701
 
    strdup("prgname"),
7702
 
    strdup("mandos-client"),
7703
 
    NULL };
7704
 
  const int argc = (sizeof(argv) / sizeof(char *)) - 1;
7705
 
 
7706
 
  __attribute__((cleanup(cleanup_string)))
7707
 
    char *agent_directory = NULL;
7708
 
  __attribute__((cleanup(cleanup_string)))
7709
 
    char *helper_directory = NULL;
7710
 
  uid_t user = 0;
7711
 
  gid_t group = 0;
7712
 
  __attribute__((cleanup(cleanup_string)))
7713
 
    char *mandos_argz = NULL;
7714
 
  size_t mandos_argz_length = 0;
7715
 
 
7716
 
  g_assert_true(parse_arguments(argc, argv, false, &agent_directory,
7717
 
                                &helper_directory, &user, &group,
7718
 
                                &mandos_argz, &mandos_argz_length));
7719
 
 
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),
7727
 
                   ==, 1);
7728
 
 
7729
 
  for(char **arg = argv; *arg != NULL; arg++){
7730
 
    free(*arg);
7731
 
  }
7732
 
}
7733
 
 
7734
 
static void test_parse_arguments_mandos_args(__attribute__((unused))
7735
 
                                             test_fixture *fixture,
7736
 
                                             __attribute__((unused))
7737
 
                                             gconstpointer user_data){
7738
 
  char *argv[] = {
7739
 
    strdup("prgname"),
7740
 
    strdup("mandos-client"),
7741
 
    strdup("one"),
7742
 
    strdup("two"),
7743
 
    strdup("three"),
7744
 
    NULL };
7745
 
  const int argc = (sizeof(argv) / sizeof(char *)) - 1;
7746
 
 
7747
 
  __attribute__((cleanup(cleanup_string)))
7748
 
    char *agent_directory = NULL;
7749
 
  __attribute__((cleanup(cleanup_string)))
7750
 
    char *helper_directory = NULL;
7751
 
  uid_t user = 0;
7752
 
  gid_t group = 0;
7753
 
  __attribute__((cleanup(cleanup_string)))
7754
 
    char *mandos_argz = NULL;
7755
 
  size_t mandos_argz_length = 0;
7756
 
 
7757
 
  g_assert_true(parse_arguments(argc, argv, false, &agent_directory,
7758
 
                                &helper_directory, &user, &group,
7759
 
                                &mandos_argz, &mandos_argz_length));
7760
 
 
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),
7775
 
                   ==, 4);
7776
 
 
7777
 
  for(char **arg = argv; *arg != NULL; arg++){
7778
 
    free(*arg);
7779
 
  }
7780
 
}
7781
 
 
7782
 
static void test_parse_arguments_all_args(__attribute__((unused))
7783
 
                                          test_fixture *fixture,
7784
 
                                          __attribute__((unused))
7785
 
                                          gconstpointer user_data){
7786
 
  char *argv[] = {
7787
 
    strdup("prgname"),
7788
 
    strdup("--agent-directory"),
7789
 
    strdup("/tmp"),
7790
 
    strdup("--helper-directory"),
7791
 
    strdup("/var/tmp"),
7792
 
    strdup("--user"),
7793
 
    strdup("1"),
7794
 
    strdup("--group"),
7795
 
    strdup("2"),
7796
 
    strdup("mandos-client"),
7797
 
    strdup("one"),
7798
 
    strdup("two"),
7799
 
    strdup("three"),
7800
 
    NULL };
7801
 
  const int argc = (sizeof(argv) / sizeof(char *)) - 1;
7802
 
 
7803
 
  __attribute__((cleanup(cleanup_string)))
7804
 
    char *agent_directory = NULL;
7805
 
  __attribute__((cleanup(cleanup_string)))
7806
 
    char *helper_directory = NULL;
7807
 
  uid_t user = 0;
7808
 
  gid_t group = 0;
7809
 
  __attribute__((cleanup(cleanup_string)))
7810
 
    char *mandos_argz = NULL;
7811
 
  size_t mandos_argz_length = 0;
7812
 
 
7813
 
  g_assert_true(parse_arguments(argc, argv, false, &agent_directory,
7814
 
                                &helper_directory, &user, &group,
7815
 
                                &mandos_argz, &mandos_argz_length));
7816
 
 
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),
7831
 
                   ==, 4);
7832
 
 
7833
 
  for(char **arg = argv; *arg != NULL; arg++){
7834
 
    free(*arg);
7835
 
  }
7836
 
}
7837
 
 
7838
 
static void test_parse_arguments_mixed(__attribute__((unused))
7839
 
                                       test_fixture *fixture,
7840
 
                                       __attribute__((unused))
7841
 
                                       gconstpointer user_data){
7842
 
  char *argv[] = {
7843
 
    strdup("prgname"),
7844
 
    strdup("mandos-client"),
7845
 
    strdup("--user"),
7846
 
    strdup("1"),
7847
 
    strdup("one"),
7848
 
    strdup("--agent-directory"),
7849
 
    strdup("/tmp"),
7850
 
    strdup("two"),
7851
 
    strdup("three"),
7852
 
    strdup("--helper-directory=/var/tmp"),
7853
 
    NULL };
7854
 
  const int argc = (sizeof(argv) / sizeof(char *)) - 1;
7855
 
 
7856
 
  __attribute__((cleanup(cleanup_string)))
7857
 
    char *agent_directory = NULL;
7858
 
  __attribute__((cleanup(cleanup_string)))
7859
 
    char *helper_directory = NULL;
7860
 
  uid_t user = 0;
7861
 
  gid_t group = 0;
7862
 
  __attribute__((cleanup(cleanup_string)))
7863
 
    char *mandos_argz = NULL;
7864
 
  size_t mandos_argz_length = 0;
7865
 
 
7866
 
  g_assert_true(parse_arguments(argc, argv, false, &agent_directory,
7867
 
                                &helper_directory, &user, &group,
7868
 
                                &mandos_argz, &mandos_argz_length));
7869
 
 
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),
7884
 
                   ==, 4);
7885
 
 
7886
 
  for(char **arg = argv; *arg != NULL; arg++){
7887
 
    free(*arg);
7888
 
  }
7889
 
}
7890
 
 
7891
 
/* End of tests section */
7892
 
 
7893
 
/* Test boilerplate section; New tests should be added to the test
7894
 
   suite definition here, in the "run_tests" function.
7895
 
 
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
7898
 
   start normally. */
7899
 
 
7900
 
__attribute__((cold))
7901
 
static bool run_tests(int argc, char *argv[]){
7902
 
  g_test_init(&argc, &argv, NULL);
7903
 
 
7904
 
  /* A macro to add a test with no setup or teardown functions */
7905
 
#define test_add(testpath, testfunc)                    \
7906
 
  do {                                                  \
7907
 
    g_test_add((testpath), test_fixture, NULL, NULL,    \
7908
 
               (testfunc), NULL);                       \
7909
 
  } while(false)
7910
 
 
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);
7917
 
 
7918
 
  /* Regular non-signal-related tests; these use no setups or
7919
 
     teardowns */
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);
7972
 
 
7973
 
  /* A macro to add a test using the setup and teardown functions */
7974
 
#define test_add_st(path, func)                                 \
7975
 
  do {                                                          \
7976
 
    g_test_add((path), test_fixture, NULL, test_setup, (func),  \
7977
 
               test_teardown);                                  \
7978
 
  } while(false)
7979
 
 
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);
8170
 
 
8171
 
  return g_test_run() == 0;
8172
 
}
8173
 
 
8174
 
static bool should_only_run_tests(int *argc_p, char **argv_p[]){
8175
 
  GOptionContext *context = g_option_context_new("");
8176
 
 
8177
 
  g_option_context_set_help_enabled(context, FALSE);
8178
 
  g_option_context_set_ignore_unknown_options(context, TRUE);
8179
 
 
8180
 
  gboolean should_run_tests = FALSE;
8181
 
  GOptionEntry entries[] = {
8182
 
    { "test", 0, 0, G_OPTION_ARG_NONE,
8183
 
      &should_run_tests, "Run tests", NULL },
8184
 
    { NULL }
8185
 
  };
8186
 
  g_option_context_add_main_entries(context, entries, NULL);
8187
 
 
8188
 
  GError *error = NULL;
8189
 
 
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);
8193
 
  }
8194
 
 
8195
 
  g_option_context_free(context);
8196
 
  return should_run_tests != FALSE;
8197
 
}
8198
 
 
8199
 
/*
8200
 
Local Variables:
8201
 
run-tests:
8202
 
(lambda ()
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:
8207
 
(lambda (dir)
8208
 
  (with-current-buffer (get-buffer-create "*Test*")
8209
 
    (setq buffer-read-only nil
8210
 
          default-directory dir)
8211
 
    (erase-buffer)
8212
 
    (compilation-mode))
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))))
8219
 
get-command-line:
8220
 
(lambda ()
8221
 
  (let*
8222
 
      ((build-directory
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)
8228
 
              build-directory)))
8229
 
       (command
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)
8240
 
          ((file-readable-p
8241
 
            (concat (file-name-as-directory try-directory)
8242
 
                    "Makefile")) try-directory)
8243
 
          ((funcall find-build-directory
8244
 
                    (directory-file-name (file-name-directory
8245
 
                                          try-directory))
8246
 
                    base-directory)))))
8247
 
show-test-buffer-in-test-window:
8248
 
(lambda ()
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*"))))
8257
 
remove-test-window:
8258
 
(lambda ()
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)
8262
 
End:
8263
 
*/