=== modified file 'Makefile' --- Makefile 2019-07-30 18:44:31 +0000 +++ Makefile 2019-08-02 07:19:32 +0000 @@ -44,12 +44,15 @@ htmldir:=man version:=1.8.5 SED:=sed +PKG_CONFIG?=pkg-config USER:=$(firstword $(subst :, ,$(shell getent passwd _mandos \ || getent passwd nobody || echo 65534))) GROUP:=$(firstword $(subst :, ,$(shell getent group _mandos \ || getent group nogroup || echo 65534))) +LINUXVERSION:=$(shell uname --kernel-release) + ## Use these settings for a traditional /usr/local install # PREFIX:=$(DESTDIR)/usr/local # CONFDIR:=$(DESTDIR)/etc/mandos @@ -71,7 +74,8 @@ STATEDIR:=$(DESTDIR)/var/lib/mandos LIBDIR:=$(shell \ for d in \ - "/usr/lib/`dpkg-architecture -qDEB_HOST_MULTIARCH 2>/dev/null`" \ + "/usr/lib/`dpkg-architecture \ + -qDEB_HOST_MULTIARCH 2>/dev/null`" \ "`rpm --eval='%{_libdir}' 2>/dev/null`" /usr/lib; do \ if [ -d "$$d" -a "$$d" = "$${d%/}" ]; then \ echo "$(DESTDIR)$$d"; \ @@ -80,20 +84,22 @@ done) ## -SYSTEMD:=$(DESTDIR)$(shell pkg-config systemd --variable=systemdsystemunitdir) -TMPFILES:=$(DESTDIR)$(shell pkg-config systemd --variable=tmpfilesdir) +SYSTEMD:=$(DESTDIR)$(shell $(PKG_CONFIG) systemd \ + --variable=systemdsystemunitdir) +TMPFILES:=$(DESTDIR)$(shell $(PKG_CONFIG) systemd \ + --variable=tmpfilesdir) -GNUTLS_CFLAGS:=$(shell pkg-config --cflags-only-I gnutls) -GNUTLS_LIBS:=$(shell pkg-config --libs gnutls) -AVAHI_CFLAGS:=$(shell pkg-config --cflags-only-I avahi-core) -AVAHI_LIBS:=$(shell pkg-config --libs avahi-core) +GNUTLS_CFLAGS:=$(shell $(PKG_CONFIG) --cflags-only-I gnutls) +GNUTLS_LIBS:=$(shell $(PKG_CONFIG) --libs gnutls) +AVAHI_CFLAGS:=$(shell $(PKG_CONFIG) --cflags-only-I avahi-core) +AVAHI_LIBS:=$(shell $(PKG_CONFIG) --libs avahi-core) GPGME_CFLAGS:=$(shell gpgme-config --cflags; getconf LFS_CFLAGS) GPGME_LIBS:=$(shell gpgme-config --libs; getconf LFS_LIBS; \ getconf LFS_LDFLAGS) -LIBNL3_CFLAGS:=$(shell pkg-config --cflags-only-I libnl-route-3.0) -LIBNL3_LIBS:=$(shell pkg-config --libs libnl-route-3.0) -GLIB_CFLAGS:=$(shell pkg-config --cflags glib-2.0) -GLIB_LIBS:=$(shell pkg-config --libs glib-2.0) +LIBNL3_CFLAGS:=$(shell $(PKG_CONFIG) --cflags-only-I libnl-route-3.0) +LIBNL3_LIBS:=$(shell $(PKG_CONFIG) --libs libnl-route-3.0) +GLIB_CFLAGS:=$(shell $(PKG_CONFIG) --cflags glib-2.0) +GLIB_LIBS:=$(shell $(PKG_CONFIG) --libs glib-2.0) # Do not change these two CFLAGS+=$(WARN) $(DEBUG) $(FORTIFY) $(COVERAGE) \ @@ -312,21 +318,27 @@ ./dracut-module/password-agent --test # Run the client with a local config and key -run-client: all keydir/seckey.txt keydir/pubkey.txt keydir/tls-privkey.pem keydir/tls-pubkey.pem - @echo "###################################################################" - @echo "# The following error messages are harmless and can be safely #" - @echo "# ignored: #" - @echo "# From plugin-runner: setgid: Operation not permitted #" - @echo "# setuid: Operation not permitted #" - @echo "# From askpass-fifo: mkfifo: Permission denied #" - @echo "# From mandos-client: #" - @echo "# Failed to raise privileges: Operation not permitted #" - @echo "# Warning: network hook \"*\" exited with status * #" - @echo "# #" - @echo "# (The messages are caused by not running as root, but you should #" - @echo "# NOT run \"make run-client\" as root unless you also unpacked and #" - @echo "# compiled Mandos as root, which is also NOT recommended.) #" - @echo "###################################################################" +run-client: all keydir/seckey.txt keydir/pubkey.txt \ + keydir/tls-privkey.pem keydir/tls-pubkey.pem + @echo '######################################################' + @echo '# The following error messages are harmless and can #' + @echo '# be safely ignored: #' + @echo '## From plugin-runner: #' + @echo '# setgid: Operation not permitted #' + @echo '# setuid: Operation not permitted #' + @echo '## From askpass-fifo: #' + @echo '# mkfifo: Permission denied #' + @echo '## From mandos-client: #' + @echo '# Failed to raise privileges: Operation not permi... #' + @echo '# Warning: network hook "*" exited with status * #' + @echo '# ioctl SIOCSIFFLAGS +IFF_UP: Operation not permi... #' + @echo '# Failed to bring up interface "*": Operation not... #' + @echo '# #' + @echo '# (The messages are caused by not running as root, #' + @echo '# but you should NOT run "make run-client" as root #' + @echo '# unless you also unpacked and compiled Mandos as #' + @echo '# root, which is also NOT recommended.) #' + @echo '######################################################' # We set GNOME_KEYRING_CONTROL to block pam_gnome_keyring ./plugin-runner --plugin-dir=plugins.d \ --plugin-helper-dir=plugin-helpers \ @@ -372,7 +384,8 @@ elif install --directory --mode=u=rwx $(STATEDIR); then \ chown -- $(USER):$(GROUP) $(STATEDIR) || :; \ fi - if [ "$(TMPFILES)" != "$(DESTDIR)" -a -d "$(TMPFILES)" ]; then \ + if [ "$(TMPFILES)" != "$(DESTDIR)" \ + -a -d "$(TMPFILES)" ]; then \ install --mode=u=rw,go=r tmpfiles.d-mandos.conf \ $(TMPFILES)/mandos.conf; \ fi @@ -425,7 +438,8 @@ install --mode=u=rwx,go=rx \ --target-directory=$(LIBDIR)/mandos plugin-runner install --mode=u=rwx,go=rx \ - --target-directory=$(LIBDIR)/mandos mandos-to-cryptroot-unlock + --target-directory=$(LIBDIR)/mandos \ + mandos-to-cryptroot-unlock install --mode=u=rwx,go=rx --target-directory=$(PREFIX)/sbin \ mandos-keygen install --mode=u=rwx,go=rx \ @@ -494,7 +508,7 @@ if command -v update-initramfs >/dev/null; then \ update-initramfs -k all -u; \ elif command -v dracut >/dev/null; then \ - for initrd in $(DESTDIR)/boot/initr*-$(shell uname --kernel-release); do \ + for initrd in $(DESTDIR)/boot/initr*-$(LINUXVERSION); do \ if [ -w "$$initrd" ]; then \ chmod go-r "$$initrd"; \ dracut --force "$$initrd"; \ @@ -553,7 +567,7 @@ if command -v update-initramfs >/dev/null; then \ update-initramfs -k all -u; \ elif command -v dracut >/dev/null; then \ - for initrd in $(DESTDIR)/boot/initr*-$(shell uname --kernel-release); do \ + for initrd in $(DESTDIR)/boot/initr*-$(LINUXVERSION); do \ test -w "$$initrd" && dracut --force "$$initrd"; \ done; \ fi === modified file 'debian/mandos.postinst' --- debian/mandos.postinst 2019-02-10 08:41:14 +0000 +++ debian/mandos.postinst 2019-08-02 22:16:53 +0000 @@ -50,6 +50,10 @@ invoke-rc.d mandos start fi fi + # Reload D-Bus daemon to be aware of the _mandos user & group + if [ -x /etc/init.d/dbus ]; then + invoke-rc.d dbus force-reload || : + fi if ! dpkg-statoverride --list "/var/lib/mandos" >/dev/null \ 2>&1; then chown _mandos:_mandos /var/lib/mandos === modified file 'dracut-module/password-agent.c' --- dracut-module/password-agent.c 2019-07-27 10:11:45 +0000 +++ dracut-module/password-agent.c 2019-08-03 12:31:22 +0000 @@ -48,9 +48,9 @@ #include /* error() */ #include /* EX_USAGE, EX_OSERR, EX_OSFILE */ #include /* errno, error_t, EACCES, - ENAMETOOLONG, ENOENT, EEXIST, - ECHILD, EPERM, ENOMEM, EAGAIN, - EINTR, ENOBUFS, EADDRINUSE, + ENAMETOOLONG, ENOENT, ENOTDIR, + EEXIST, ECHILD, EPERM, ENOMEM, + EAGAIN, EINTR, ENOBUFS, EADDRINUSE, ECONNREFUSED, ECONNRESET, ETOOMANYREFS, EMSGSIZE, EBADF, EINVAL */ @@ -83,14 +83,17 @@ #include /* munlock(), mlock() */ #include /* O_CLOEXEC, O_NONBLOCK, fcntl(), F_GETFD, F_GETFL, FD_CLOEXEC, - open(), O_WRONLY, O_RDONLY */ + open(), O_WRONLY, O_NOCTTY, + O_RDONLY, O_NOFOLLOW */ #include /* waitpid(), WNOHANG, WIFEXITED(), WEXITSTATUS() */ #include /* PIPE_BUF, NAME_MAX, INT_MAX */ #include /* inotify_init1(), IN_NONBLOCK, IN_CLOEXEC, inotify_add_watch(), IN_CLOSE_WRITE, IN_MOVED_TO, - IN_DELETE, struct inotify_event */ + IN_MOVED_FROM, IN_DELETE, + IN_EXCL_UNLINK, IN_ONLYDIR, + struct inotify_event */ #include /* fnmatch(), FNM_FILE_NAME */ #include /* asprintf(), FILE, fopen(), getline(), sscanf(), feof(), @@ -431,6 +434,7 @@ case EACCES: case ENAMETOOLONG: case ENOENT: + case ENOTDIR: return EX_OSFILE; default: return EX_OSERR; @@ -1017,8 +1021,9 @@ return false; } - if(inotify_add_watch(fd, dir, IN_CLOSE_WRITE - | IN_MOVED_TO | IN_DELETE) + if(inotify_add_watch(fd, dir, IN_CLOSE_WRITE | IN_MOVED_TO + | IN_MOVED_FROM| IN_DELETE | IN_EXCL_UNLINK + | IN_ONLYDIR) == -1){ error(0, errno, "Failed to create inotify watch on %s", dir); return false; @@ -1071,9 +1076,11 @@ /* "sufficient to read at least one event." - inotify(7) */ const size_t ievent_size = (sizeof(struct inotify_event) + NAME_MAX + 1); - char ievent_buffer[sizeof(struct inotify_event) + NAME_MAX + 1]; - struct inotify_event *ievent = ((struct inotify_event *) - ievent_buffer); + struct { + struct inotify_event event; + char name_buffer[NAME_MAX + 1]; + } ievent_buffer; + struct inotify_event *const ievent = &ievent_buffer.event; const ssize_t read_length = read(fd, ievent, ievent_size); if(read_length == 0){ /* EOF */ @@ -1117,7 +1124,7 @@ immediately */ queue->next_run = 1; } - } else if(ievent->mask & IN_DELETE){ + } else if(ievent->mask & (IN_MOVED_FROM | IN_DELETE)){ if(not string_set_add(cancelled_filenames, question_filename)){ error(0, errno, "Could not add question %s to" @@ -2220,7 +2227,8 @@ { __attribute__((cleanup(cleanup_close))) - const int devnull_fd = open("/dev/null", O_WRONLY | O_CLOEXEC); + const int devnull_fd = open("/dev/null", + O_WRONLY | O_CLOEXEC | O_NOCTTY); g_assert_cmpint(devnull_fd, >=, 0); __attribute__((cleanup(cleanup_close))) const int real_stderr_fd = dup(STDERR_FILENO); @@ -2250,7 +2258,7 @@ { __attribute__((cleanup(cleanup_close))) const int devnull_fd = open("/dev/null", - O_WRONLY | O_CLOEXEC); + O_WRONLY | O_CLOEXEC | O_NOCTTY); g_assert_cmpint(devnull_fd, >=, 0); __attribute__((cleanup(cleanup_close))) const int real_stderr_fd = dup(STDERR_FILENO); @@ -2901,7 +2909,7 @@ __attribute__((cleanup(cleanup_close))) const int devnull_fd = open("/dev/null", - O_WRONLY | O_CLOEXEC); + O_WRONLY | O_CLOEXEC | O_NOCTTY); g_assert_cmpint(devnull_fd, >=, 0); __attribute__((cleanup(cleanup_close))) const int real_stderr_fd = dup(STDERR_FILENO); @@ -2972,7 +2980,7 @@ __attribute__((cleanup(cleanup_close))) const int devnull_fd = open("/dev/null", - O_WRONLY | O_CLOEXEC); + O_WRONLY | O_CLOEXEC, O_NOCTTY); g_assert_cmpint(devnull_fd, >=, 0); __attribute__((cleanup(cleanup_close))) const int real_stderr_fd = dup(STDERR_FILENO); @@ -3016,7 +3024,8 @@ buffer password = {}; /* Reading /proc/self/mem from offset 0 will always give EIO */ - const int fd = open("/proc/self/mem", O_RDONLY | O_CLOEXEC); + const int fd = open("/proc/self/mem", + O_RDONLY | O_CLOEXEC | O_NOCTTY); bool password_is_read = false; bool quit_now = false; @@ -3450,6 +3459,44 @@ g_assert_cmpuint((unsigned int)queue->length, ==, 0); } +static void test_add_inotify_dir_watch_nondir(__attribute__((unused)) + test_fixture *fixture, + __attribute__((unused)) + gconstpointer + user_data){ + __attribute__((cleanup(cleanup_close))) + const int epoll_fd = epoll_create1(EPOLL_CLOEXEC); + g_assert_cmpint(epoll_fd, >=, 0); + __attribute__((cleanup(cleanup_queue))) + task_queue *queue = create_queue(); + g_assert_nonnull(queue); + __attribute__((cleanup(string_set_clear))) + string_set cancelled_filenames = {}; + const mono_microsecs current_time = 0; + + bool quit_now = false; + buffer password = {}; + bool mandos_client_exited = false; + bool password_is_read = false; + + const char not_a_directory[] = "/dev/tty"; + + FILE *real_stderr = stderr; + FILE *devnull = fopen("/dev/null", "we"); + g_assert_nonnull(devnull); + stderr = devnull; + g_assert_false(add_inotify_dir_watch(queue, epoll_fd, &quit_now, + &password, not_a_directory, + &cancelled_filenames, + ¤t_time, + &mandos_client_exited, + &password_is_read)); + stderr = real_stderr; + g_assert_cmpint(fclose(devnull), ==, 0); + + g_assert_cmpuint((unsigned int)queue->length, ==, 0); +} + static void test_add_inotify_dir_watch_EAGAIN(__attribute__((unused)) test_fixture *fixture, __attribute__((unused)) @@ -3670,6 +3717,78 @@ } static +void test_add_inotify_dir_watch_IN_MOVED_FROM(__attribute__((unused)) + test_fixture *fixture, + __attribute__((unused)) + gconstpointer + user_data){ + __attribute__((cleanup(cleanup_close))) + const int epoll_fd = epoll_create1(EPOLL_CLOEXEC); + g_assert_cmpint(epoll_fd, >=, 0); + __attribute__((cleanup(cleanup_queue))) + task_queue *queue = create_queue(); + g_assert_nonnull(queue); + __attribute__((cleanup(string_set_clear))) + string_set cancelled_filenames = {}; + const mono_microsecs current_time = 0; + + bool quit_now = false; + buffer password = {}; + bool mandos_client_exited = false; + bool password_is_read = false; + + __attribute__((cleanup(cleanup_string))) + char *tempdir = make_temporary_directory(); + g_assert_nonnull(tempdir); + + __attribute__((cleanup(cleanup_string))) + char *tempfilename = make_temporary_file_in_directory(tempdir); + g_assert_nonnull(tempfilename); + + __attribute__((cleanup(cleanup_string))) + char *targetdir = make_temporary_directory(); + g_assert_nonnull(targetdir); + + __attribute__((cleanup(cleanup_string))) + char *targetfilename = NULL; + g_assert_cmpint(asprintf(&targetfilename, "%s/%s", targetdir, + basename(tempfilename)), >, 0); + g_assert_nonnull(targetfilename); + + g_assert_true(add_inotify_dir_watch(queue, epoll_fd, &quit_now, + &password, tempdir, + &cancelled_filenames, + ¤t_time, + &mandos_client_exited, + &password_is_read)); + + g_assert_cmpint(rename(tempfilename, targetfilename), ==, 0); + + const task_context *const added_read_task + = find_matching_task(queue, + (task_context){ .func=read_inotify_event }); + g_assert_nonnull(added_read_task); + + /* "sufficient to read at least one event." - inotify(7) */ + const size_t ievent_size = (sizeof(struct inotify_event) + + NAME_MAX + 1); + struct inotify_event *ievent = malloc(ievent_size); + g_assert_nonnull(ievent); + + ssize_t read_size = read(added_read_task->fd, ievent, ievent_size); + + g_assert_cmpint((int)read_size, >, 0); + g_assert_true(ievent->mask & IN_MOVED_FROM); + g_assert_cmpstr(ievent->name, ==, basename(tempfilename)); + + free(ievent); + + g_assert_cmpint(unlink(targetfilename), ==, 0); + g_assert_cmpint(rmdir(targetdir), ==, 0); + g_assert_cmpint(rmdir(tempdir), ==, 0); +} + +static void test_add_inotify_dir_watch_IN_DELETE(__attribute__((unused)) test_fixture *fixture, __attribute__((unused)) @@ -3733,6 +3852,82 @@ g_assert_cmpint(rmdir(tempdir), ==, 0); } +static +void test_add_inotify_dir_watch_IN_EXCL_UNLINK(__attribute__((unused)) + test_fixture *fixture, + __attribute__((unused)) + gconstpointer + user_data){ + __attribute__((cleanup(cleanup_close))) + const int epoll_fd = epoll_create1(EPOLL_CLOEXEC); + g_assert_cmpint(epoll_fd, >=, 0); + __attribute__((cleanup(cleanup_queue))) + task_queue *queue = create_queue(); + g_assert_nonnull(queue); + __attribute__((cleanup(string_set_clear))) + string_set cancelled_filenames = {}; + const mono_microsecs current_time = 0; + + bool quit_now = false; + buffer password = {}; + bool mandos_client_exited = false; + bool password_is_read = false; + + __attribute__((cleanup(cleanup_string))) + char *tempdir = make_temporary_directory(); + g_assert_nonnull(tempdir); + + __attribute__((cleanup(cleanup_string))) + char *tempfile = make_temporary_file_in_directory(tempdir); + g_assert_nonnull(tempfile); + int tempfile_fd = open(tempfile, O_WRONLY | O_CLOEXEC | O_NOCTTY + | O_NOFOLLOW); + g_assert_cmpint(tempfile_fd, >, 2); + + g_assert_true(add_inotify_dir_watch(queue, epoll_fd, &quit_now, + &password, tempdir, + &cancelled_filenames, + ¤t_time, + &mandos_client_exited, + &password_is_read)); + g_assert_cmpint(unlink(tempfile), ==, 0); + + g_assert_cmpuint((unsigned int)queue->length, >, 0); + + const task_context *const added_read_task + = find_matching_task(queue, + (task_context){ .func=read_inotify_event }); + g_assert_nonnull(added_read_task); + + g_assert_cmpint(added_read_task->fd, >, 2); + g_assert_true(fd_has_cloexec_and_nonblock(added_read_task->fd)); + + /* "sufficient to read at least one event." - inotify(7) */ + const size_t ievent_size = (sizeof(struct inotify_event) + + NAME_MAX + 1); + struct inotify_event *ievent = malloc(ievent_size); + g_assert_nonnull(ievent); + + ssize_t read_size = 0; + read_size = read(added_read_task->fd, ievent, ievent_size); + + g_assert_cmpint((int)read_size, >, 0); + g_assert_true(ievent->mask & IN_DELETE); + g_assert_cmpstr(ievent->name, ==, basename(tempfile)); + + g_assert_cmpint(close(tempfile_fd), ==, 0); + + /* IN_EXCL_UNLINK should make the closing of the previously unlinked + file not appear as an ievent, so we should not see it now. */ + read_size = read(added_read_task->fd, ievent, ievent_size); + g_assert_cmpint((int)read_size, ==, -1); + g_assert_true(errno == EAGAIN); + + free(ievent); + + g_assert_cmpint(rmdir(tempdir), ==, 0); +} + static void test_read_inotify_event_readerror(__attribute__((unused)) test_fixture *fixture, __attribute__((unused)) @@ -3744,7 +3939,8 @@ const mono_microsecs current_time = 0; /* Reading /proc/self/mem from offset 0 will always result in EIO */ - const int fd = open("/proc/self/mem", O_RDONLY | O_CLOEXEC); + const int fd = open("/proc/self/mem", + O_RDONLY | O_CLOEXEC | O_NOCTTY); bool quit_now = false; __attribute__((cleanup(cleanup_queue))) @@ -3929,9 +4125,11 @@ const size_t ievent_max_size = (sizeof(struct inotify_event) + NAME_MAX + 1); g_assert_cmpint(ievent_max_size, <=, PIPE_BUF); - char ievent_buffer[sizeof(struct inotify_event) + NAME_MAX + 1]; - struct inotify_event *ievent = ((struct inotify_event *) - ievent_buffer); + struct { + struct inotify_event event; + char name_buffer[NAME_MAX + 1]; + } ievent_buffer; + struct inotify_event *const ievent = &ievent_buffer.event; const char dummy_file_name[] = "ask.dummy_file_name"; ievent->mask = IN_CLOSE_WRITE; @@ -3939,7 +4137,7 @@ memcpy(ievent->name, dummy_file_name, sizeof(dummy_file_name)); const size_t ievent_size = (sizeof(struct inotify_event) + sizeof(dummy_file_name)); - g_assert_cmpint(write(pipefds[1], ievent_buffer, ievent_size), + g_assert_cmpint(write(pipefds[1], (char *)ievent, ievent_size), ==, ievent_size); g_assert_cmpint(close(pipefds[1]), ==, 0); @@ -4022,9 +4220,11 @@ const size_t ievent_max_size = (sizeof(struct inotify_event) + NAME_MAX + 1); g_assert_cmpint(ievent_max_size, <=, PIPE_BUF); - char ievent_buffer[sizeof(struct inotify_event) + NAME_MAX + 1]; - struct inotify_event *ievent = ((struct inotify_event *) - ievent_buffer); + struct { + struct inotify_event event; + char name_buffer[NAME_MAX + 1]; + } ievent_buffer; + struct inotify_event *const ievent = &ievent_buffer.event; const char dummy_file_name[] = "ask.dummy_file_name"; ievent->mask = IN_MOVED_TO; @@ -4032,7 +4232,7 @@ memcpy(ievent->name, dummy_file_name, sizeof(dummy_file_name)); const size_t ievent_size = (sizeof(struct inotify_event) + sizeof(dummy_file_name)); - g_assert_cmpint(write(pipefds[1], ievent_buffer, ievent_size), + g_assert_cmpint(write(pipefds[1], (char *)ievent, ievent_size), ==, ievent_size); g_assert_cmpint(close(pipefds[1]), ==, 0); @@ -4098,6 +4298,91 @@ })); } +static +void test_read_inotify_event_IN_MOVED_FROM(__attribute__((unused)) + test_fixture *fixture, + __attribute__((unused)) + gconstpointer user_data){ + __attribute__((cleanup(cleanup_close))) + const int epoll_fd = epoll_create1(EPOLL_CLOEXEC); + g_assert_cmpint(epoll_fd, >=, 0); + __attribute__((cleanup(string_set_clear))) + string_set cancelled_filenames = {}; + const mono_microsecs current_time = 0; + + int pipefds[2]; + g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0); + + /* "sufficient to read at least one event." - inotify(7) */ + const size_t ievent_max_size = (sizeof(struct inotify_event) + + NAME_MAX + 1); + g_assert_cmpint(ievent_max_size, <=, PIPE_BUF); + struct { + struct inotify_event event; + char name_buffer[NAME_MAX + 1]; + } ievent_buffer; + struct inotify_event *const ievent = &ievent_buffer.event; + + const char dummy_file_name[] = "ask.dummy_file_name"; + ievent->mask = IN_MOVED_FROM; + ievent->len = sizeof(dummy_file_name); + memcpy(ievent->name, dummy_file_name, sizeof(dummy_file_name)); + const size_t ievent_size = (sizeof(struct inotify_event) + + sizeof(dummy_file_name)); + g_assert_cmpint(write(pipefds[1], (char *)ievent, ievent_size), + ==, ievent_size); + g_assert_cmpint(close(pipefds[1]), ==, 0); + + bool quit_now = false; + buffer password = {}; + bool mandos_client_exited = false; + bool password_is_read = false; + __attribute__((cleanup(cleanup_queue))) + task_queue *queue = create_queue(); + g_assert_nonnull(queue); + + task_context task = { + .func=read_inotify_event, + .epoll_fd=epoll_fd, + .fd=pipefds[0], + .quit_now=&quit_now, + .password=&password, + .filename=strdup("/nonexistent"), + .cancelled_filenames=&cancelled_filenames, + .current_time=¤t_time, + .mandos_client_exited=&mandos_client_exited, + .password_is_read=&password_is_read, + }; + task.func(task, queue); + g_assert_false(quit_now); + g_assert_true(queue->next_run == 0); + g_assert_cmpuint((unsigned int)queue->length, ==, 1); + + g_assert_nonnull(find_matching_task(queue, (task_context){ + .func=read_inotify_event, + .epoll_fd=epoll_fd, + .fd=pipefds[0], + .quit_now=&quit_now, + .password=&password, + .filename=task.filename, + .cancelled_filenames=&cancelled_filenames, + .current_time=¤t_time, + .mandos_client_exited=&mandos_client_exited, + .password_is_read=&password_is_read, + })); + + g_assert_true(epoll_set_contains(epoll_fd, pipefds[0], + EPOLLIN | EPOLLRDHUP)); + + __attribute__((cleanup(cleanup_string))) + char *filename = NULL; + g_assert_cmpint(asprintf(&filename, "%s/%s", task.filename, + dummy_file_name), >, 0); + g_assert_nonnull(filename); + g_assert_true(string_set_contains(*task.cancelled_filenames, + filename)); +} + static void test_read_inotify_event_IN_DELETE(__attribute__((unused)) test_fixture *fixture, __attribute__((unused)) @@ -4117,9 +4402,11 @@ const size_t ievent_max_size = (sizeof(struct inotify_event) + NAME_MAX + 1); g_assert_cmpint(ievent_max_size, <=, PIPE_BUF); - char ievent_buffer[sizeof(struct inotify_event) + NAME_MAX + 1]; - struct inotify_event *ievent = ((struct inotify_event *) - ievent_buffer); + struct { + struct inotify_event event; + char name_buffer[NAME_MAX + 1]; + } ievent_buffer; + struct inotify_event *const ievent = &ievent_buffer.event; const char dummy_file_name[] = "ask.dummy_file_name"; ievent->mask = IN_DELETE; @@ -4127,7 +4414,7 @@ memcpy(ievent->name, dummy_file_name, sizeof(dummy_file_name)); const size_t ievent_size = (sizeof(struct inotify_event) + sizeof(dummy_file_name)); - g_assert_cmpint(write(pipefds[1], ievent_buffer, ievent_size), + g_assert_cmpint(write(pipefds[1], (char *)ievent, ievent_size), ==, ievent_size); g_assert_cmpint(close(pipefds[1]), ==, 0); @@ -4199,9 +4486,11 @@ const size_t ievent_max_size = (sizeof(struct inotify_event) + NAME_MAX + 1); g_assert_cmpint(ievent_max_size, <=, PIPE_BUF); - char ievent_buffer[sizeof(struct inotify_event) + NAME_MAX + 1]; - struct inotify_event *ievent = ((struct inotify_event *) - ievent_buffer); + struct { + struct inotify_event event; + char name_buffer[NAME_MAX + 1]; + } ievent_buffer; + struct inotify_event *const ievent = &ievent_buffer.event; const char dummy_file_name[] = "ignored.dummy_file_name"; ievent->mask = IN_CLOSE_WRITE; @@ -4209,7 +4498,7 @@ memcpy(ievent->name, dummy_file_name, sizeof(dummy_file_name)); const size_t ievent_size = (sizeof(struct inotify_event) + sizeof(dummy_file_name)); - g_assert_cmpint(write(pipefds[1], ievent_buffer, ievent_size), + g_assert_cmpint(write(pipefds[1], (char *)ievent, ievent_size), ==, ievent_size); g_assert_cmpint(close(pipefds[1]), ==, 0); @@ -4273,9 +4562,11 @@ const size_t ievent_max_size = (sizeof(struct inotify_event) + NAME_MAX + 1); g_assert_cmpint(ievent_max_size, <=, PIPE_BUF); - char ievent_buffer[sizeof(struct inotify_event) + NAME_MAX + 1]; - struct inotify_event *ievent = ((struct inotify_event *) - ievent_buffer); + struct { + struct inotify_event event; + char name_buffer[NAME_MAX + 1]; + } ievent_buffer; + struct inotify_event *const ievent = &ievent_buffer.event; const char dummy_file_name[] = "ignored.dummy_file_name"; ievent->mask = IN_MOVED_TO; @@ -4283,7 +4574,7 @@ memcpy(ievent->name, dummy_file_name, sizeof(dummy_file_name)); const size_t ievent_size = (sizeof(struct inotify_event) + sizeof(dummy_file_name)); - g_assert_cmpint(write(pipefds[1], ievent_buffer, ievent_size), + g_assert_cmpint(write(pipefds[1], (char *)ievent, ievent_size), ==, ievent_size); g_assert_cmpint(close(pipefds[1]), ==, 0); @@ -4330,6 +4621,91 @@ EPOLLIN | EPOLLRDHUP)); } +static void +test_read_inotify_event_IN_MOVED_FROM_badname(__attribute__((unused)) + test_fixture *fixture, + __attribute__((unused)) + gconstpointer + user_data){ + __attribute__((cleanup(cleanup_close))) + const int epoll_fd = epoll_create1(EPOLL_CLOEXEC); + g_assert_cmpint(epoll_fd, >=, 0); + __attribute__((cleanup(string_set_clear))) + string_set cancelled_filenames = {}; + const mono_microsecs current_time = 0; + + int pipefds[2]; + g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0); + + /* "sufficient to read at least one event." - inotify(7) */ + const size_t ievent_max_size = (sizeof(struct inotify_event) + + NAME_MAX + 1); + g_assert_cmpint(ievent_max_size, <=, PIPE_BUF); + struct { + struct inotify_event event; + char name_buffer[NAME_MAX + 1]; + } ievent_buffer; + struct inotify_event *const ievent = &ievent_buffer.event; + + const char dummy_file_name[] = "ignored.dummy_file_name"; + ievent->mask = IN_MOVED_FROM; + ievent->len = sizeof(dummy_file_name); + memcpy(ievent->name, dummy_file_name, sizeof(dummy_file_name)); + const size_t ievent_size = (sizeof(struct inotify_event) + + sizeof(dummy_file_name)); + g_assert_cmpint(write(pipefds[1], (char *)ievent, ievent_size), + ==, ievent_size); + g_assert_cmpint(close(pipefds[1]), ==, 0); + + bool quit_now = false; + buffer password = {}; + bool mandos_client_exited = false; + bool password_is_read = false; + __attribute__((cleanup(cleanup_queue))) + task_queue *queue = create_queue(); + g_assert_nonnull(queue); + + task_context task = { + .func=read_inotify_event, + .epoll_fd=epoll_fd, + .fd=pipefds[0], + .quit_now=&quit_now, + .password=&password, + .filename=strdup("/nonexistent"), + .cancelled_filenames=&cancelled_filenames, + .current_time=¤t_time, + .mandos_client_exited=&mandos_client_exited, + .password_is_read=&password_is_read, + }; + task.func(task, queue); + g_assert_false(quit_now); + g_assert_true(queue->next_run == 0); + g_assert_cmpuint((unsigned int)queue->length, ==, 1); + + g_assert_nonnull(find_matching_task(queue, (task_context){ + .func=read_inotify_event, + .epoll_fd=epoll_fd, + .fd=pipefds[0], + .quit_now=&quit_now, + .password=&password, + .filename=task.filename, + .cancelled_filenames=&cancelled_filenames, + .current_time=¤t_time, + .mandos_client_exited=&mandos_client_exited, + .password_is_read=&password_is_read, + })); + + g_assert_true(epoll_set_contains(epoll_fd, pipefds[0], + EPOLLIN | EPOLLRDHUP)); + + __attribute__((cleanup(cleanup_string))) + char *filename = NULL; + g_assert_cmpint(asprintf(&filename, "%s/%s", task.filename, + dummy_file_name), >, 0); + g_assert_nonnull(filename); + g_assert_false(string_set_contains(cancelled_filenames, filename)); +} + static void test_read_inotify_event_IN_DELETE_badname(__attribute__((unused)) test_fixture *fixture, @@ -4350,9 +4726,11 @@ const size_t ievent_max_size = (sizeof(struct inotify_event) + NAME_MAX + 1); g_assert_cmpint(ievent_max_size, <=, PIPE_BUF); - char ievent_buffer[sizeof(struct inotify_event) + NAME_MAX + 1]; - struct inotify_event *ievent = ((struct inotify_event *) - ievent_buffer); + struct { + struct inotify_event event; + char name_buffer[NAME_MAX + 1]; + } ievent_buffer; + struct inotify_event *const ievent = &ievent_buffer.event; const char dummy_file_name[] = "ignored.dummy_file_name"; ievent->mask = IN_DELETE; @@ -4360,7 +4738,7 @@ memcpy(ievent->name, dummy_file_name, sizeof(dummy_file_name)); const size_t ievent_size = (sizeof(struct inotify_event) + sizeof(dummy_file_name)); - g_assert_cmpint(write(pipefds[1], ievent_buffer, ievent_size), + g_assert_cmpint(write(pipefds[1], (char *)ievent, ievent_size), ==, ievent_size); g_assert_cmpint(close(pipefds[1]), ==, 0); @@ -5253,7 +5631,8 @@ __attribute__((unused)) gconstpointer user_data){ __attribute__((cleanup(cleanup_close))) - const int epoll_fd = open("/dev/null", O_WRONLY | O_CLOEXEC); + const int epoll_fd = open("/dev/null", + O_WRONLY | O_CLOEXEC | O_NOCTTY); __attribute__((cleanup(cleanup_string))) char *const question_filename = strdup("/nonexistent/question"); g_assert_nonnull(question_filename); @@ -5663,7 +6042,8 @@ __attribute__((unused)) gconstpointer user_data){ __attribute__((cleanup(cleanup_close))) - const int epoll_fd = open("/dev/null", O_WRONLY | O_CLOEXEC); + const int epoll_fd = open("/dev/null", + O_WRONLY | O_CLOEXEC | O_NOCTTY); __attribute__((cleanup(cleanup_string))) char *const question_filename = strdup("/nonexistent/question"); g_assert_nonnull(question_filename); @@ -5932,7 +6312,8 @@ const char *const dirname){ __attribute__((cleanup(cleanup_close))) - const int devnull_fd = open("/dev/null", O_WRONLY | O_CLOEXEC); + const int devnull_fd = open("/dev/null", + O_WRONLY | O_CLOEXEC | O_NOCTTY); g_assert_cmpint(devnull_fd, >=, 0); __attribute__((cleanup(cleanup_close))) const int real_stderr_fd = dup(STDERR_FILENO); @@ -7573,12 +7954,18 @@ test_add_inotify_dir_watch); test_add_st("/task-creators/add_inotify_dir_watch/fail", test_add_inotify_dir_watch_fail); + test_add_st("/task-creators/add_inotify_dir_watch/not-a-directory", + test_add_inotify_dir_watch_nondir); test_add_st("/task-creators/add_inotify_dir_watch/EAGAIN", test_add_inotify_dir_watch_EAGAIN); test_add_st("/task-creators/add_inotify_dir_watch/IN_CLOSE_WRITE", test_add_inotify_dir_watch_IN_CLOSE_WRITE); test_add_st("/task-creators/add_inotify_dir_watch/IN_MOVED_TO", test_add_inotify_dir_watch_IN_MOVED_TO); + test_add_st("/task-creators/add_inotify_dir_watch/IN_MOVED_FROM", + test_add_inotify_dir_watch_IN_MOVED_FROM); + test_add_st("/task-creators/add_inotify_dir_watch/IN_EXCL_UNLINK", + test_add_inotify_dir_watch_IN_EXCL_UNLINK); test_add_st("/task-creators/add_inotify_dir_watch/IN_DELETE", test_add_inotify_dir_watch_IN_DELETE); test_add_st("/task/read_inotify_event/readerror", @@ -7593,12 +7980,16 @@ test_read_inotify_event_IN_CLOSE_WRITE); test_add_st("/task/read_inotify_event/IN_MOVED_TO", test_read_inotify_event_IN_MOVED_TO); + test_add_st("/task/read_inotify_event/IN_MOVED_FROM", + test_read_inotify_event_IN_MOVED_FROM); test_add_st("/task/read_inotify_event/IN_DELETE", test_read_inotify_event_IN_DELETE); test_add_st("/task/read_inotify_event/IN_CLOSE_WRITE/badname", test_read_inotify_event_IN_CLOSE_WRITE_badname); test_add_st("/task/read_inotify_event/IN_MOVED_TO/badname", test_read_inotify_event_IN_MOVED_TO_badname); + test_add_st("/task/read_inotify_event/IN_MOVED_FROM/badname", + test_read_inotify_event_IN_MOVED_FROM_badname); test_add_st("/task/read_inotify_event/IN_DELETE/badname", test_read_inotify_event_IN_DELETE_badname); test_add_st("/task/open_and_parse_question/ENOENT", === modified file 'mandos' --- mandos 2019-07-30 18:44:31 +0000 +++ mandos 2019-08-02 23:02:58 +0000 @@ -80,6 +80,7 @@ import dbus import dbus.service +import gi from gi.repository import GLib from dbus.mainloop.glib import DBusGMainLoop import ctypes @@ -115,6 +116,9 @@ if sys.version_info.major == 2: str = unicode +if sys.version_info < (3, 2): + configparser.Configparser = configparser.SafeConfigParser + version = "1.8.5" stored_state_file = "clients.pickle" @@ -2998,10 +3002,10 @@ del priority # Parse config file for server-global settings - server_config = configparser.SafeConfigParser(server_defaults) + server_config = configparser.ConfigParser(server_defaults) del server_defaults server_config.read(os.path.join(options.configdir, "mandos.conf")) - # Convert the SafeConfigParser object to a dict + # Convert the ConfigParser object to a dict server_settings = server_config.defaults() # Use the appropriate methods on the non-string config options for option in ("debug", "use_dbus", "use_ipv6", "restore", @@ -3079,8 +3083,7 @@ server_settings["servicename"]))) # Parse config file with clients - client_config = configparser.SafeConfigParser(Client - .client_defaults) + client_config = configparser.ConfigParser(Client.client_defaults) client_config.read(os.path.join(server_settings["configdir"], "clients.conf")) @@ -3157,9 +3160,10 @@ # Close all input and output, do double fork, etc. daemon() - # multiprocessing will use threads, so before we use GLib we need - # to inform GLib that threads will be used. - GLib.threads_init() + if gi.version_info < (3, 10, 2): + # multiprocessing will use threads, so before we use GLib we + # need to inform GLib that threads will be used. + GLib.threads_init() global main_loop # From the Avahi example code