/mandos/trunk

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

« back to all changes in this revision

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

  • Committer: teddy at recompile
  • Date: 2011-12-31 13:25:58 UTC
  • mto: This revision was merged to the branch mainline in revision 541.
  • Revision ID: teddy@recompile.se-20111231132558-z0dh7qgofgctmgri
* network-hooks.s/bridge: Don't use interface names directly; search
                          for interface names using their address.
  (addrtoif): New function.
* network-hooks.s/bridge.conf (PORTS): Removed.
  (PORT_ADDRESSES): New.
* network-hooks.s/wireless: Don't use interface names directly; search
                            for interface names using their address.
  (addrtoif): New function.
* network-hooks.s/wireless.conf: Specify address.

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