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