Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package restorecond for openSUSE:Factory checked in at 2026-07-13 14:25:14 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/restorecond (Old) and /work/SRC/openSUSE:Factory/.restorecond.new.1991 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "restorecond" Mon Jul 13 14:25:14 2026 rev:23 rq:1364572 version:3.11 Changes: -------- --- /work/SRC/openSUSE:Factory/restorecond/restorecond.changes 2026-02-09 11:43:32.446203314 +0100 +++ /work/SRC/openSUSE:Factory/.restorecond.new.1991/restorecond.changes 2026-07-13 14:25:16.365684948 +0200 @@ -1,0 +2,19 @@ +Mon Jul 6 15:08:26 UTC 2026 - Cathy Hu <[email protected]> + +- Update to version 3.11 + https://github.com/SELinuxProject/selinux/releases/tag/3.11 + - User-visible changes: + - Improved `restorecond.service` to use new `restorecond -F` option to run in + foreground + - Rewrote restorecond and sandbox/seunshare to eliminate TOCTOU issues on their + other path-based operations via /proc/self/fd and the use of a safe_open() + helper. + - Multiple documentation improvements. + - Development-relevant changes: + - Reformatted entire tree based on .clang-format and added new + check-format/format make targets to check and/or reformat code to match. + This is now a requirement for new patches. +- Packaging changes: + - Refresh harden_restorecond.service.patch to the new systemd service + +------------------------------------------------------------------- Old: ---- restorecond-3.10.tar.gz restorecond-3.10.tar.gz.asc New: ---- restorecond-3.11.tar.gz restorecond-3.11.tar.gz.asc ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ restorecond.spec ++++++ --- /var/tmp/diff_new_pack.V6orCb/_old 2026-07-13 14:25:18.361753196 +0200 +++ /var/tmp/diff_new_pack.V6orCb/_new 2026-07-13 14:25:18.365753332 +0200 @@ -16,9 +16,9 @@ # -%define libselinux_ver 3.10 +%define libselinux_ver 3.11 Name: restorecond -Version: 3.10 +Version: 3.11 Release: 0 Summary: Daemon to restore SELinux contexts License: GPL-2.0-or-later ++++++ harden_restorecond.service.patch ++++++ --- /var/tmp/diff_new_pack.V6orCb/_old 2026-07-13 14:25:18.397754426 +0200 +++ /var/tmp/diff_new_pack.V6orCb/_new 2026-07-13 14:25:18.401754563 +0200 @@ -1,7 +1,7 @@ -Index: restorecond-3.2/restorecond.service +Index: restorecond-3.11/restorecond.service =================================================================== ---- restorecond-3.2.orig/restorecond.service -+++ restorecond-3.2/restorecond.service +--- restorecond-3.11.orig/restorecond.service ++++ restorecond-3.11/restorecond.service @@ -5,6 +5,15 @@ ConditionPathExists=/etc/selinux/restore ConditionSecurity=selinux @@ -15,7 +15,7 @@ +ProtectControlGroups=true +RestrictRealtime=true +# end of automatic additions - Type=forking - ExecStart=/usr/sbin/restorecond - PIDFile=/run/restorecond.pid + Type=simple + ExecStart=/usr/sbin/restorecond -F + ++++++ restorecond-3.10.tar.gz -> restorecond-3.11.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/restorecond-3.10/Makefile new/restorecond-3.11/Makefile --- old/restorecond-3.10/Makefile 2026-02-02 03:01:16.000000000 +0100 +++ new/restorecond-3.11/Makefile 2026-07-01 18:51:05.000000000 +0200 @@ -18,8 +18,8 @@ GIO_LIBS = $(shell $(PKG_CONFIG) --libs gio-2.0) CFLAGS ?= -g -Werror -Wall -W -override CFLAGS += -I../../libselinux/include $(GIO_CFLAGS) -override LDFLAGS+= -L../../libselinux/src +override CFLAGS += -I../libselinux/include $(GIO_CFLAGS) +override LDFLAGS+= -L../libselinux/src override LDLIBS += -lselinux $(GIO_LIBS) all: restorecond diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/restorecond-3.10/VERSION new/restorecond-3.11/VERSION --- old/restorecond-3.10/VERSION 2026-02-02 03:01:16.000000000 +0100 +++ new/restorecond-3.11/VERSION 2026-07-01 18:51:05.000000000 +0200 @@ -1 +1 @@ -3.10 +3.11 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/restorecond-3.10/restore.c new/restorecond-3.11/restore.c --- old/restorecond-3.10/restore.c 2026-02-02 03:01:16.000000000 +0100 +++ new/restorecond-3.11/restore.c 2026-07-01 18:51:05.000000000 +0200 @@ -1,16 +1,4 @@ #include "restore.h" -#include <glob.h> - -#ifndef GLOB_TILDE -#define GLOB_TILDE 0 -#endif - -#ifndef GLOB_BRACE -#define GLOB_BRACE 0 -#endif - -char **exclude_list; -int exclude_count; void restore_init(struct restore_opts *opts) { @@ -29,13 +17,13 @@ } opts->restorecon_flags = 0; - opts->restorecon_flags = opts->nochange | opts->verbose | - opts->progress | opts->set_specctx | - opts->add_assoc | opts->ignore_digest | - opts->recurse | opts->userealpath | - opts->xdev | opts->abort_on_error | - opts->syslog_changes | opts->log_matches | - opts->ignore_noent | opts->ignore_mounts; + opts->restorecon_flags = + opts->nochange | opts->verbose | opts->progress | + opts->set_specctx | opts->add_assoc | opts->ignore_digest | + opts->recurse | opts->userealpath | opts->xdev | + opts->abort_on_error | opts->syslog_changes | + opts->log_matches | opts->ignore_noent | opts->ignore_mounts | + opts->skip_multilink; /* Use setfiles, restorecon and restorecond own handles */ selinux_restorecon_set_sehandle(opts->hnd); @@ -49,80 +37,4 @@ exit(-1); } } - - if (exclude_list) - selinux_restorecon_set_exclude_list - ((const char **)exclude_list); -} - -void restore_finish(void) -{ - int i; - - if (exclude_list) { - for (i = 0; exclude_list[i]; i++) - free(exclude_list[i]); - free(exclude_list); - } -} - -int process_glob(char *name, struct restore_opts *opts) -{ - glob_t globbuf; - size_t i = 0; - int len, rc, errors; - - memset(&globbuf, 0, sizeof(globbuf)); - - errors = glob(name, GLOB_TILDE | GLOB_PERIOD | - GLOB_NOCHECK | GLOB_BRACE, NULL, &globbuf); - if (errors) - return errors; - - for (i = 0; i < globbuf.gl_pathc; i++) { - len = strlen(globbuf.gl_pathv[i]) - 2; - if (len > 0 && strcmp(&globbuf.gl_pathv[i][len--], "/.") == 0) - continue; - if (len > 0 && strcmp(&globbuf.gl_pathv[i][len], "/..") == 0) - continue; - rc = selinux_restorecon(globbuf.gl_pathv[i], - opts->restorecon_flags); - if (rc < 0) - errors = rc; - } - - globfree(&globbuf); - - return errors; -} - -void add_exclude(const char *directory) -{ - char **tmp_list; - - if (directory == NULL || directory[0] != '/') { - fprintf(stderr, "Full path required for exclude: %s.\n", - directory); - exit(-1); - } - - /* Add another two entries, one for directory, and the other to - * terminate the list. - */ - tmp_list = realloc(exclude_list, sizeof(char *) * (exclude_count + 2)); - if (!tmp_list) { - fprintf(stderr, "realloc failed while excluding %s.\n", - directory); - exit(-1); - } - exclude_list = tmp_list; - - exclude_list[exclude_count] = strdup(directory); - if (!exclude_list[exclude_count]) { - fprintf(stderr, "strdup failed while excluding %s.\n", - directory); - exit(-1); - } - exclude_count++; - exclude_list[exclude_count] = NULL; } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/restorecond-3.10/restore.h new/restorecond-3.11/restore.h --- old/restorecond-3.10/restore.h 2026-02-02 03:01:16.000000000 +0100 +++ new/restorecond-3.11/restore.h 2026-07-01 18:51:05.000000000 +0200 @@ -3,13 +3,11 @@ #ifndef _GNU_SOURCE #define _GNU_SOURCE #endif -#include <fts.h> #include <errno.h> #include <string.h> #include <stdio.h> #include <syslog.h> #include <sys/stat.h> -#include <sepol/sepol.h> #include <selinux/selinux.h> #include <selinux/label.h> #include <selinux/restorecon.h> @@ -17,13 +15,6 @@ #include <limits.h> #include <stdint.h> -/* - * STAR_COUNT is also defined in libselinux/src/selinux_restorecon.c where it - * is used to output "*" for each number of files processed. Defined here for - * inclusion in man pages. -*/ -#define STAR_COUNT 1000 - /* Things that need to be init'd */ struct restore_opts { unsigned int nochange; @@ -40,6 +31,7 @@ unsigned int log_matches; unsigned int ignore_noent; unsigned int ignore_mounts; + unsigned int skip_multilink; /* restorecon_flags holds | of above for restore_init() */ unsigned int restorecon_flags; char *rootpath; @@ -53,9 +45,5 @@ }; void restore_init(struct restore_opts *opts); -void restore_finish(void); -void add_exclude(const char *directory); -int process_glob(char *name, struct restore_opts *opts); -extern char **exclude_list; #endif diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/restorecond-3.10/restorecond.8 new/restorecond-3.11/restorecond.8 --- old/restorecond-3.10/restorecond.8 2026-02-02 03:01:16.000000000 +0100 +++ new/restorecond-3.11/restorecond.8 2026-07-01 18:51:05.000000000 +0200 @@ -3,7 +3,7 @@ restorecond \- daemon that watches for file creation and then sets the default SELinux file context .SH "SYNOPSIS" -.B restorecond [\-d] [-h] [\-f restorecond_file ] [\-u] [\-v] +.B restorecond [\-d] [-h] [\-f restorecond_file ] [\-F] [\-u] [\-v] .P .SH "DESCRIPTION" @@ -26,6 +26,9 @@ .B \-f restorecond_file Use alternative restorecond.conf file. .TP +.B \-F +Run in foreground, do not become a daemon. +.TP .B \-u Turns on user mode. Runs restorecond in the user session and reads /etc/selinux/restorecond_user.conf. Uses dbus to make sure only one restorecond is running per user session. .TP diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/restorecond-3.10/restorecond.c new/restorecond-3.11/restorecond.c --- old/restorecond-3.10/restorecond.c 2026-02-02 03:01:16.000000000 +0100 +++ new/restorecond-3.11/restorecond.c 2026-07-01 18:51:05.000000000 +0200 @@ -65,8 +65,8 @@ const char *homedir; static int master_fd = -1; -static const char *server_watch_file = "/etc/selinux/restorecond.conf"; -static const char *user_watch_file = "/etc/selinux/restorecond_user.conf"; +static const char *server_watch_file = "/etc/selinux/restorecond.conf"; +static const char *user_watch_file = "/etc/selinux/restorecond_user.conf"; static const char *watch_file; struct restore_opts r_opts; @@ -76,8 +76,10 @@ int terminate = 0; int master_wd = -1; int run_as_user = 0; +int foreground_mode = 0; -static void done(void) { +static void done(void) +{ watch_list_free(master_fd); close(master_fd); utmpwatcher_free(); @@ -94,17 +96,18 @@ len = snprintf(val, sizeof(val), "%u\n", getpid()); if (len < 0) { syslog(LOG_ERR, "Pid error (%s)", strerror(errno)); - pidfile = 0; + pidfile = NULL; return 1; } pidfd = open(pidfile, O_CREAT | O_TRUNC | O_NOFOLLOW | O_WRONLY, 0644); if (pidfd < 0) { syslog(LOG_ERR, "Unable to set pidfile (%s)", strerror(errno)); - pidfile = 0; + pidfile = NULL; return 1; } if (write(pidfd, val, (unsigned int)len) != len) { - syslog(LOG_ERR, "Unable to write to pidfile (%s)", strerror(errno)); + syslog(LOG_ERR, "Unable to write to pidfile (%s)", + strerror(errno)); close(pidfd); return 1; } @@ -115,7 +118,7 @@ /* * SIGTERM handler */ -static void term_handler(int s __attribute__ ((unused))) +static void term_handler(int s __attribute__((unused))) { terminate = 1; /* trigger a failure in the watch */ @@ -124,7 +127,8 @@ static void usage(char *program) { - printf("%s [-d] [-f restorecond_file ] [-u] [-v] \n", program); + printf("%s [-d] [-f restorecond_file ] [-F] [-n] [-u] [-v] \n", + program); } void exitApp(const char *msg) @@ -150,10 +154,11 @@ watch_file = server_watch_file; - /* Set all options to zero/NULL except for ignore_noent & digest. */ + /* Set all options to zero/NULL except for ignore_noent, digest, and skip_multilink. */ memset(&r_opts, 0, sizeof(r_opts)); r_opts.ignore_noent = SELINUX_RESTORECON_IGNORE_NOENTRY; r_opts.ignore_digest = SELINUX_RESTORECON_IGNORE_DIGEST; + r_opts.skip_multilink = SELINUX_RESTORECON_SKIP_MULTILINK; /* As r_opts.selabel_opt_digest = NULL, no digest will be requested. */ restore_init(&r_opts); @@ -164,8 +169,8 @@ sigemptyset(&sa.sa_mask); sigaction(SIGTERM, &sa, NULL); - atexit( done ); - while ((opt = getopt(argc, argv, "hdf:uv")) > 0) { + atexit(done); + while ((opt = getopt(argc, argv, "hdf:Fuv")) > 0) { switch (opt) { case 'd': debug_mode = 1; @@ -173,6 +178,9 @@ case 'f': watch_file = optarg; break; + case 'F': + foreground_mode = 1; + break; case 'u': run_as_user = 1; break; @@ -209,13 +217,14 @@ read_config(master_fd, watch_file); - if (!debug_mode) { + if (!debug_mode && !foreground_mode) { if (daemon(0, 0) < 0) exitApp("daemon"); + write_pid_file(); + } else { + pidfile = NULL; } - write_pid_file(); - while (watch(master_fd, watch_file) == 0) { } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/restorecond-3.10/restorecond.h new/restorecond-3.11/restorecond.h --- old/restorecond-3.10/restorecond.h 2026-02-02 03:01:16.000000000 +0100 +++ new/restorecond-3.11/restorecond.h 2026-07-01 18:51:05.000000000 +0200 @@ -34,7 +34,7 @@ extern int server(int, const char *watch_file); extern void exitApp(const char *msg) __attribute__((__noreturn__)); -extern void read_config(int fd, const char *watch_file); +extern void read_config(int fd, const char *watch_file); extern int watch(int fd, const char *watch_file); extern void watch_list_add(int inotify_fd, const char *path); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/restorecond-3.10/restorecond.service new/restorecond-3.11/restorecond.service --- old/restorecond-3.10/restorecond.service 2026-02-02 03:01:16.000000000 +0100 +++ new/restorecond-3.11/restorecond.service 2026-07-01 18:51:05.000000000 +0200 @@ -5,9 +5,8 @@ ConditionSecurity=selinux [Service] -Type=forking -ExecStart=/usr/sbin/restorecond -PIDFile=/run/restorecond.pid +Type=simple +ExecStart=/usr/sbin/restorecond -F [Install] WantedBy=multi-user.target diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/restorecond-3.10/stringslist.c new/restorecond-3.11/stringslist.c --- old/restorecond-3.10/stringslist.c 2026-02-02 03:01:16.000000000 +0100 +++ new/restorecond-3.11/stringslist.c 2026-07-01 18:51:05.000000000 +0200 @@ -38,9 +38,9 @@ while (ptr) { int cmp = strcmp(string, ptr->string); if (cmp < 0) - break; /* Not on list break out to add */ + break; /* Not on list break out to add */ if (cmp == 0) - return; /* Already on list */ + return; /* Already on list */ prev = ptr; ptr = ptr->next; } @@ -62,8 +62,8 @@ while (ptr) { *exact = strcmp(ptr->string, string) == 0; int cmp = fnmatch(ptr->string, string, 0); - if (cmp == 0) - return 0; /* Match found */ + if (cmp == 0) + return 0; /* Match found */ ptr = ptr->next; } return -1; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/restorecond-3.10/user.c new/restorecond-3.11/user.c --- old/restorecond-3.10/user.c 2026-02-02 03:01:16.000000000 +0100 +++ new/restorecond-3.11/user.c 2026-07-01 18:51:05.000000000 +0200 @@ -64,8 +64,7 @@ } static void on_name_lost(GDBusConnection *connection G_GNUC_UNUSED, - const gchar *name, - gpointer user_data) + const gchar *name, gpointer user_data) { /* Exit when the D-Bus connection closes */ GMainLoop *loop = user_data; @@ -88,14 +87,10 @@ if (!bus) return -1; - client_id = g_bus_own_name_on_connection( - bus, - DBUS_NAME, - G_BUS_NAME_OWNER_FLAGS_NONE, - on_name_acquired, - on_name_lost, - loop, - NULL); + client_id = g_bus_own_name_on_connection(bus, DBUS_NAME, + G_BUS_NAME_OWNER_FLAGS_NONE, + on_name_acquired, on_name_lost, + loop, NULL); g_object_unref(bus); if (client_id == 0) return -1; @@ -106,70 +101,88 @@ #endif /* size of the event structure, not counting name */ -#define EVENT_SIZE (sizeof (struct inotify_event)) +#define EVENT_SIZE (sizeof(struct inotify_event)) /* reasonable guess as to size of 1024 events */ -#define BUF_LEN (1024 * (EVENT_SIZE + 16)) +#define BUF_LEN (1024 * (EVENT_SIZE + 16)) -static gboolean -io_channel_callback - (GIOChannel *source, - GIOCondition condition, - gpointer data __attribute__((__unused__))) -{ - - char buffer[BUF_LEN+1]; - gsize bytes_read; - unsigned int i = 0; - - if (condition & G_IO_IN) { - /* Data is available. */ - g_io_channel_read_chars - (source, buffer, - sizeof (buffer), - &bytes_read, NULL); - - if (! bytes_read) { - /* Session/Terminal Ended */ - exit(0); - } - - while (i < bytes_read) { - struct inotify_event *event; - event = (struct inotify_event *)&buffer[i]; - if (debug_mode) - printf("wd=%d mask=%u cookie=%u len=%u\n", - event->wd, event->mask, - event->cookie, event->len); - if (event->len) - watch_list_find(event->wd, event->name); - - i += EVENT_SIZE + event->len; - } - } +static gboolean io_channel_callback(GIOChannel *source, GIOCondition condition, + gpointer data __attribute__((__unused__))) +{ + char buffer[BUF_LEN + 1]; + gsize bytes_read; + unsigned int i = 0; + + if (condition & G_IO_IN) { + /* Data is available. */ + g_io_channel_read_chars(source, buffer, sizeof(buffer), + &bytes_read, NULL); + + if (!bytes_read) { + /* Session/Terminal Ended */ + exit(0); + } + + while (i < bytes_read) { + struct inotify_event *event; + event = (struct inotify_event *)&buffer[i]; + if (debug_mode) + printf("wd=%d mask=%u cookie=%u len=%u\n", + event->wd, event->mask, event->cookie, + event->len); + if (event->len) + watch_list_find(event->wd, event->name); + + i += EVENT_SIZE + event->len; + } + } - /* An error happened while reading + /* An error happened while reading the file. */ - if (condition & G_IO_NVAL) - return FALSE; + if (condition & G_IO_NVAL) + return FALSE; - /* We have reached the end of the + /* We have reached the end of the file. */ - if (condition & G_IO_HUP) { - g_io_channel_shutdown (source, 0, NULL); - exit(0); - return FALSE; - } + if (condition & G_IO_HUP) { + g_io_channel_shutdown(source, 0, NULL); + exit(0); + return FALSE; + } - /* Returning TRUE will make sure + /* Returning TRUE will make sure the callback remains associated to the channel. */ - return TRUE; + return TRUE; } -int start(void) { +static gboolean stdin_callback(GIOChannel *source, GIOCondition condition, + gpointer data __attribute__((__unused__))) +{ + char buffer[256]; + gsize bytes_read; + + if (condition & G_IO_IN) { + /* Data is available. */ + while (g_io_channel_read_chars(source, buffer, sizeof(buffer), + &bytes_read, + NULL) == G_IO_STATUS_NORMAL && + bytes_read > 0) + ; + if (bytes_read == 0) + exit(0); + } + + if (condition & (G_IO_HUP | G_IO_ERR | G_IO_NVAL)) + exit(0); + + return TRUE; +} + +int start(void) +{ #ifdef HAVE_DBUS GDBusConnection *bus; GError *err = NULL; @@ -179,7 +192,8 @@ bus = g_bus_get_sync(G_BUS_TYPE_SESSION, NULL, &err); if (!bus) { if (debug_mode) - g_warning("Failed to connect to the D-BUS daemon: %s", err->message); + g_warning("Failed to connect to the D-BUS daemon: %s", + err->message); g_error_free(err); return 1; } @@ -188,21 +202,20 @@ * * https://dbus.freedesktop.org/doc/dbus-specification.html#standard-interfaces-peer */ - result = g_dbus_connection_call_sync(bus, - DBUS_NAME, /* bus name */ - "/", /* object path */ - "org.freedesktop.DBus.Peer", /* interface */ - "Ping", /* method */ - NULL, /* parameters */ - NULL, /* reply_type */ - G_DBUS_CALL_FLAGS_NONE, - -1, /* timeout_msec */ - NULL, - &err); + result = g_dbus_connection_call_sync( + bus, DBUS_NAME, /* bus name */ + "/", /* object path */ + "org.freedesktop.DBus.Peer", /* interface */ + "Ping", /* method */ + NULL, /* parameters */ + NULL, /* reply_type */ + G_DBUS_CALL_FLAGS_NONE, -1, /* timeout_msec */ + NULL, &err); if (!result) { g_object_unref(bus); if (debug_mode) - g_warning("Failed to start %s: %s", DBUS_NAME, err->message); + g_warning("Failed to start %s: %s", DBUS_NAME, + err->message); g_error_free(err); return 1; } @@ -211,17 +224,19 @@ return 0; } -static int local_server(void) { +static int local_server(void) +{ // ! dbus, run as local service - char *ptr=NULL; + char *ptr = NULL; if (asprintf(&ptr, "%s/.restorecond", homedir) < 0) { if (debug_mode) perror("asprintf"); return -1; } - local_lock_fd = open(ptr, O_CREAT | O_WRONLY | O_NOFOLLOW | O_CLOEXEC, S_IRUSR | S_IWUSR); + local_lock_fd = open(ptr, O_CREAT | O_WRONLY | O_NOFOLLOW | O_CLOEXEC, + S_IRUSR | S_IWUSR); if (debug_mode) - g_warning ("Lock file: %s", ptr); + g_warning("Lock file: %s", ptr); free(ptr); if (local_lock_fd < 0) { @@ -239,16 +254,16 @@ /* watch for stdin/terminal going away */ GIOChannel *in = g_io_channel_unix_new(0); g_io_channel_set_encoding(in, NULL, NULL); - g_io_channel_set_flags(in, g_io_channel_get_flags(in) | G_IO_FLAG_NONBLOCK, NULL); - g_io_add_watch_full( in, - G_PRIORITY_HIGH, - G_IO_IN|G_IO_ERR|G_IO_HUP, - io_channel_callback, NULL, NULL); + g_io_channel_set_flags( + in, g_io_channel_get_flags(in) | G_IO_FLAG_NONBLOCK, NULL); + g_io_add_watch_full(in, G_PRIORITY_HIGH, G_IO_IN | G_IO_ERR | G_IO_HUP, + stdin_callback, NULL, NULL); return 0; } -static void end_local_server(void) { +static void end_local_server(void) +{ if (local_lock_fd >= 0) close(local_lock_fd); local_lock_fd = -1; @@ -264,11 +279,11 @@ return FALSE; } - -int server(int master_fd, const char *watch_file) { +int server(int master_fd, const char *watch_file) +{ GMainLoop *loop; - loop = g_main_loop_new (NULL, FALSE); + loop = g_main_loop_new(NULL, FALSE); #ifdef HAVE_DBUS if (dbus_server(loop) != 0) @@ -285,25 +300,20 @@ GIOChannel *c = g_io_channel_unix_new(master_fd); g_io_channel_set_encoding(c, NULL, NULL); - g_io_channel_set_flags(c, g_io_channel_get_flags(c) | G_IO_FLAG_NONBLOCK, NULL); + g_io_channel_set_flags( + c, g_io_channel_get_flags(c) | G_IO_FLAG_NONBLOCK, NULL); - g_io_add_watch_full(c, - G_PRIORITY_HIGH, - G_IO_IN|G_IO_ERR|G_IO_HUP, + g_io_add_watch_full(c, G_PRIORITY_HIGH, G_IO_IN | G_IO_ERR | G_IO_HUP, io_channel_callback, NULL, NULL); /* Handle SIGTERM */ - g_unix_signal_add_full(G_PRIORITY_DEFAULT, - SIGTERM, - sigterm_handler, - loop, - NULL); + g_unix_signal_add_full(G_PRIORITY_DEFAULT, SIGTERM, sigterm_handler, + loop, NULL); - g_main_loop_run (loop); + g_main_loop_run(loop); end: end_local_server(); - g_main_loop_unref (loop); + g_main_loop_unref(loop); return 0; } - diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/restorecond-3.10/utmpwatcher.c new/restorecond-3.11/utmpwatcher.c --- old/restorecond-3.10/utmpwatcher.c 2026-02-02 03:01:16.000000000 +0100 +++ new/restorecond-3.11/utmpwatcher.c 2026-07-01 18:51:05.000000000 +0200 @@ -49,6 +49,7 @@ { int changed = 0; struct utmp u; + char name[UT_NAMESIZE + 1]; const char *utmp_path = "/run/utmp"; struct stringsList *prev_utmp_ptr = utmp_ptr; if (wd != utmp_wd) @@ -60,15 +61,18 @@ exitApp("Error reading utmp file."); while (fread(&u, sizeof(struct utmp), 1, cfg) > 0) { - if (u.ut_type == USER_PROCESS) - strings_list_add(&utmp_ptr, u.ut_user); + if (u.ut_type == USER_PROCESS) { + strncpy(name, u.ut_user, UT_NAMESIZE); + name[UT_NAMESIZE] = '\0'; + strings_list_add(&utmp_ptr, name); + } } fclose(cfg); if (utmp_wd >= 0) inotify_rm_watch(inotify_fd, utmp_wd); - utmp_wd = - inotify_add_watch(inotify_fd, utmp_path, IN_MOVED_FROM | IN_MODIFY); + utmp_wd = inotify_add_watch(inotify_fd, utmp_path, + IN_MOVED_FROM | IN_MODIFY | IN_DONT_FOLLOW); if (utmp_wd == -1) exitApp("Error watching utmp file."); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/restorecond-3.10/watch.c new/restorecond-3.11/watch.c --- old/restorecond-3.10/watch.c 2026-02-02 03:01:16.000000000 +0100 +++ new/restorecond-3.11/watch.c 2026-07-01 18:51:05.000000000 +0200 @@ -15,6 +15,7 @@ #include <string.h> #include <stdio.h> #include <fcntl.h> +#include <dirent.h> #include <selinux/selinux.h> #include "restorecond.h" #include "stringslist.h" @@ -24,14 +25,10 @@ #define GLOB_TILDE 0 #endif -#ifndef GLOB_BRACE -#define GLOB_BRACE 0 -#endif - /* size of the event structure, not counting name */ -#define EVENT_SIZE (sizeof (struct inotify_event)) +#define EVENT_SIZE (sizeof(struct inotify_event)) /* reasonable guess as to size of 1024 events */ -#define BUF_LEN (1024 * (EVENT_SIZE + 16)) +#define BUF_LEN (1024 * (EVENT_SIZE + 16)) struct watchList { struct watchList *next; @@ -39,40 +36,181 @@ char *dir; struct stringsList *files; }; -struct watchList *firstDir = NULL; +static struct watchList *firstDir = NULL; -int watch_list_isempty(void) { +int watch_list_isempty(void) +{ return firstDir == NULL; } +static int open_final(int dfd, const char *name, struct stat *sb) +{ + int fd; + + if (name) + fd = openat(dfd, name, O_PATH | O_NOFOLLOW | O_CLOEXEC); + else + fd = fcntl(dfd, F_DUPFD_CLOEXEC, 0); + if (fd < 0) + return -1; + + if (fstat(fd, sb) < 0) { + close(fd); + return -1; + } + + return fd; +} + +static int safe_open(const char *path, struct stat *sb) +{ + char *copy, *cur, *slash; + int dfd, nfd; + + if (!path || path[0] == '\0') { + errno = ENOENT; + return -1; + } + + copy = strdup(path); + if (!copy) + return -1; + + if (copy[0] == '/') { + dfd = open("/", O_PATH | O_DIRECTORY | O_CLOEXEC); + cur = copy + 1; + } else { + dfd = open(".", O_PATH | O_DIRECTORY | O_CLOEXEC); + cur = copy; + } + if (dfd < 0) { + free(copy); + return -1; + } + + while (*cur == '/') + cur++; + + while (*cur != '\0') { + slash = strchr(cur, '/'); + if (slash) { + *slash = '\0'; + char *next = slash + 1; + while (*next == '/') + next++; + if (*next != '\0') { + nfd = openat(dfd, cur, + O_PATH | O_NOFOLLOW | O_DIRECTORY | + O_CLOEXEC); + close(dfd); + if (nfd < 0) { + free(copy); + return -1; + } + dfd = nfd; + cur = next; + continue; + } + } + + nfd = open_final(dfd, cur, sb); + close(dfd); + free(copy); + return nfd; + } + + nfd = open_final(dfd, NULL, sb); + close(dfd); + free(copy); + return nfd; +} + +static void *nofollow_opendir(const char *name) +{ + struct stat sb; + int fd, rdfd; + + fd = safe_open(name, &sb); + if (fd < 0) + return NULL; + + if (!S_ISDIR(sb.st_mode)) { + close(fd); + errno = ENOTDIR; + return NULL; + } + + rdfd = openat(fd, ".", O_RDONLY | O_DIRECTORY | O_NOFOLLOW | O_CLOEXEC); + close(fd); + if (rdfd < 0) + return NULL; + return fdopendir(rdfd); +} + +static struct dirent *nofollow_readdir(void *d) +{ + return readdir((DIR *)d); +} + +static void nofollow_closedir(void *d) +{ + closedir((DIR *)d); +} + +static int nofollow_lstat(const char *path, struct stat *sb) +{ + return lstat(path, sb); +} + void watch_list_add(int fd, const char *path) { + char procfd[32]; + struct stat sb; + int dirfd; struct watchList *ptr = NULL; size_t i = 0; struct watchList *prev = NULL; glob_t globbuf; - char *x = strdup(path); - if (!x) exitApp("Out of Memory"); - char *file = basename(x); - char *dir = dirname(x); + char *xb = strdup(path); + if (!xb) + exitApp("Out of Memory"); + char *xd = strdup(path); + if (!xd) + exitApp("Out of Memory"); + char *file = basename(xb); + char *dir = dirname(xd); ptr = firstDir; int len; - globbuf.gl_offs = 1; - if (glob(path, - GLOB_TILDE | GLOB_PERIOD, - NULL, - &globbuf) >= 0) { + memset(&globbuf, 0, sizeof(globbuf)); + globbuf.gl_opendir = nofollow_opendir; + globbuf.gl_readdir = nofollow_readdir; + globbuf.gl_closedir = nofollow_closedir; + globbuf.gl_lstat = nofollow_lstat; + globbuf.gl_stat = nofollow_lstat; /* never follow symlinks */ + if (glob(path, GLOB_TILDE | GLOB_PERIOD | GLOB_ALTDIRFUNC, NULL, + &globbuf) == 0) { for (i = 0; i < globbuf.gl_pathc; i++) { - len = strlen(globbuf.gl_pathv[i]) - 2; - if (len > 0 && - strcmp(&globbuf.gl_pathv[i][len--], "/.") == 0) + const char *p = globbuf.gl_pathv[i]; + + len = strlen(p) - 2; + if (len > 0 && strcmp(&p[len--], "/.") == 0) continue; - if (len > 0 && - strcmp(&globbuf.gl_pathv[i][len], "/..") == 0) + if (len > 0 && strcmp(&p[len], "/..") == 0) continue; - selinux_restorecon(globbuf.gl_pathv[i], - r_opts.restorecon_flags); + if (selinux_restorecon(p, r_opts.restorecon_flags) < + 0) { + if (errno != ENOENT) { + if (!run_as_user) + syslog(LOG_ERR, + "Unable to relabel (%s) %s\n", + p, strerror(errno)); + else + fprintf(stderr, + "Unable to relabel (%s) %s\n", + p, strerror(errno)); + } + } } globfree(&globbuf); } @@ -87,14 +225,31 @@ } ptr = calloc(1, sizeof(struct watchList)); - if (!ptr) exitApp("Out of Memory"); + if (!ptr) + exitApp("Out of Memory"); + + dirfd = safe_open(dir, &sb); + if (dirfd < 0 || !S_ISDIR(sb.st_mode)) { + if (dirfd >= 0) { + close(dirfd); + errno = ENOTDIR; + } + ptr->wd = -1; + } else { + snprintf(procfd, sizeof(procfd), "/proc/self/fd/%d", dirfd); + ptr->wd = + inotify_add_watch(fd, procfd, IN_CREATE | IN_MOVED_TO); + close(dirfd); + } - ptr->wd = inotify_add_watch(fd, dir, IN_CREATE | IN_MOVED_TO); if (ptr->wd == -1) { free(ptr); - if (! run_as_user) - syslog(LOG_ERR, "Unable to watch (%s) %s\n", - path, strerror(errno)); + if (!run_as_user) + syslog(LOG_ERR, "Unable to watch (%s) %s\n", path, + strerror(errno)); + else + fprintf(stderr, "Unable to watch (%s) %s\n", path, + strerror(errno)); goto end; } @@ -112,7 +267,8 @@ printf("%d: Dir=%s, File=%s\n", ptr->wd, ptr->dir, file); end: - free(x); + free(xb); + free(xd); return; } @@ -131,15 +287,31 @@ printf("%d: File=%s\n", wd, file); while (ptr != NULL) { if (ptr->wd == wd) { - int exact=0; + int exact = 0; if (strings_list_find(ptr->files, file, &exact) == 0) { char *path = NULL; if (asprintf(&path, "%s/%s", ptr->dir, file) < 0) exitApp("Error allocating memory."); - selinux_restorecon(path, - r_opts.restorecon_flags); + if (selinux_restorecon( + path, r_opts.restorecon_flags) < + 0) { + if (errno != ENOENT) { + if (!run_as_user) + syslog(LOG_ERR, + "Unable to relabel (%s) %s\n", + path, + strerror(errno)); + else + fprintf(stderr, + "Unable to relabel (%s) %s\n", + path, + strerror( + errno)); + } + } + free(path); return 0; } @@ -179,7 +351,6 @@ { char buf[BUF_LEN]; int len, i = 0; - if (firstDir == NULL) return 0; len = read(fd, buf, BUF_LEN); if (len < 0) { @@ -196,23 +367,23 @@ struct inotify_event *event; event = (struct inotify_event *)&buf[i]; if (debug_mode) - printf("wd=%d mask=%u cookie=%u len=%u\n", - event->wd, event->mask, - event->cookie, event->len); + printf("wd=%d mask=%u cookie=%u len=%u\n", event->wd, + event->mask, event->cookie, event->len); if (event->mask & ~IN_IGNORED) { if (event->wd == master_wd) read_config(fd, watch_file); else { switch (utmpwatcher_handle(fd, event->wd)) { - case -1: /* Message was not for utmpwatcher */ + case -1: /* Message was not for utmpwatcher */ if (event->len) - watch_list_find(event->wd, event->name); + watch_list_find(event->wd, + event->name); break; - case 1: /* utmp has changed need to reload */ + case 1: /* utmp has changed need to reload */ read_config(fd, watch_file); break; - default: /* No users logged in or out */ + default: /* No users logged in or out */ break; } } @@ -223,7 +394,7 @@ return 0; } -static void process_config(int fd, FILE * cfg) +static void process_config(int fd, FILE *cfg) { char *line_buf = NULL; size_t len = 0; @@ -237,11 +408,13 @@ int l = strlen(buffer) - 1; if (l <= 0) continue; - buffer[l] = 0; + if (buffer[l] == '\n') + buffer[l] = 0; if (buffer[0] == '~') { if (run_as_user) { - char *ptr=NULL; - if (asprintf(&ptr, "%s%s", homedir, &buffer[1]) < 0) + char *ptr = NULL; + if (asprintf(&ptr, "%s%s", homedir, + &buffer[1]) < 0) exitApp("Error allocating memory."); watch_list_add(fd, ptr); @@ -264,7 +437,6 @@ void read_config(int fd, const char *watch_file_path) { - FILE *cfg = NULL; if (debug_mode) printf("Read Config\n"); @@ -272,7 +444,7 @@ watch_list_free(fd); cfg = fopen(watch_file_path, "r"); - if (!cfg){ + if (!cfg) { perror(watch_file_path); exitApp("Error reading config file"); } @@ -281,7 +453,8 @@ inotify_rm_watch(fd, master_wd); master_wd = - inotify_add_watch(fd, watch_file_path, IN_MOVED_FROM | IN_MODIFY); + inotify_add_watch(fd, watch_file_path, + IN_MOVED_FROM | IN_MODIFY | IN_DONT_FOLLOW); if (master_wd == -1) exitApp("Error watching config file."); }
