/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

  • Committer: Teddy Hogeborn
  • Date: 2024-11-24 14:41:36 UTC
  • mfrom: (237.7.863 trunk)
  • Revision ID: teddy@recompile.se-20241124144136-0fej6fm6woitsooj
Merge from trunk

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