/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: 2017-08-20 18:53:16 UTC
  • mfrom: (237.7.458 trunk)
  • Revision ID: teddy@recompile.se-20170820185316-3i5080cpg08f0e8q
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
 
*/