/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 Hogeborn
  • Date: 2023-02-08 00:05:18 UTC
  • mto: This revision was merged to the branch mainline in revision 1272.
  • Revision ID: teddy@recompile.se-20230208000518-8p1ajaga9szbln3o
Tags: version-1.8.16-1
Version 1.8.16-1

* Makefile (version): Change to "1.8.16".
* NEWS (Version 1.8.16): Add new entry.
* debian/changelog (1.8.16-1): - '' -

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
    "printf %s \"${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
*/