/mandos/trunk

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