]> git.ipfire.org Git - thirdparty/systemd.git/blobdiff - src/shared/pager.c
pager: make pager secure when under euid is changed or explicitly requested
[thirdparty/systemd.git] / src / shared / pager.c
index 9c21881241f5ff7dff33decab1c4372329785521..9a14d44d696bb07a045f16fba1d8c00f5c547c89 100644 (file)
@@ -8,6 +8,8 @@
 #include <sys/prctl.h>
 #include <unistd.h>
 
+#include "sd-login.h"
+
 #include "copy.h"
 #include "env-util.h"
 #include "fd-util.h"
@@ -165,25 +167,42 @@ int pager_open(PagerFlags flags) {
                 }
 
                 /* People might invoke us from sudo, don't needlessly allow less to be a way to shell out
-                 * privileged stuff. */
-                r = getenv_bool("SYSTEMD_LESSSECURE");
-                if (r == 0) { /* Remove env var if off */
-                        if (unsetenv("LESSSECURE") < 0) {
-                                log_error_errno(errno, "Failed to uset environment variable LESSSECURE: %m");
-                                _exit(EXIT_FAILURE);
-                        }
-                } else {
-                        /* Set env var otherwise */
+                 * privileged stuff. If the user set $SYSTEMD_PAGERSECURE, trust their configuration of the
+                 * pager. If they didn't, use secure mode when under euid is changed. If $SYSTEMD_PAGERSECURE
+                 * wasn't explicitly set, and we autodetect the need for secure mode, only use the pager we
+                 * know to be good. */
+                int use_secure_mode = getenv_bool("SYSTEMD_PAGERSECURE");
+                bool trust_pager = use_secure_mode >= 0;
+                if (use_secure_mode == -ENXIO) {
+                        uid_t uid;
+
+                        r = sd_pid_get_owner_uid(0, &uid);
                         if (r < 0)
-                                log_warning_errno(r, "Unable to parse $SYSTEMD_LESSSECURE, ignoring: %m");
+                                log_debug_errno(r, "sd_pid_get_owner_uid() failed, enabling pager secure mode: %m");
 
-                        if (setenv("LESSSECURE", "1", 1) < 0) {
-                                log_error_errno(errno, "Failed to set environment variable LESSSECURE: %m");
-                                _exit(EXIT_FAILURE);
-                        }
+                        use_secure_mode = r < 0 || uid != geteuid();
+
+                } else if (use_secure_mode < 0) {
+                        log_warning_errno(use_secure_mode, "Unable to parse $SYSTEMD_PAGERSECURE, assuming true: %m");
+                        use_secure_mode = true;
                 }
 
-                if (pager_args) {
+                /* We generally always set variables used by less, even if we end up using a different pager.
+                 * They shouldn't hurt in any case, and ideally other pagers would look at them too. */
+                if (use_secure_mode)
+                        r = setenv("LESSSECURE", "1", 1);
+                else
+                        r = unsetenv("LESSSECURE");
+                if (r < 0) {
+                        log_error_errno(errno, "Failed to adjust environment variable LESSSECURE: %m");
+                        _exit(EXIT_FAILURE);
+                }
+
+                if (trust_pager && pager_args) { /* The pager config might be set globally, and we cannot
+                                                  * know if the user adjusted it to be appropriate for the
+                                                  * secure mode. Thus, start the pager specified through
+                                                  * envvars only when $SYSTEMD_PAGERSECURE was explicitly set
+                                                  * as well. */
                         r = loop_write(exe_name_pipe[1], pager_args[0], strlen(pager_args[0]) + 1, false);
                         if (r < 0) {
                                 log_error_errno(r, "Failed to write pager name to socket: %m");
@@ -195,13 +214,14 @@ int pager_open(PagerFlags flags) {
                                        "Failed to execute '%s', using fallback pagers: %m", pager_args[0]);
                 }
 
-                /* Debian's alternatives command for pagers is
-                 * called 'pager'. Note that we do not call
-                 * sensible-pagers here, since that is just a
-                 * shell script that implements a logic that
-                 * is similar to this one anyway, but is
-                 * Debian-specific. */
+                /* Debian's alternatives command for pagers is called 'pager'. Note that we do not call
+                 * sensible-pagers here, since that is just a shell script that implements a logic that is
+                 * similar to this one anyway, but is Debian-specific. */
                 FOREACH_STRING(exe, "pager", "less", "more") {
+                        /* Only less implements secure mode right now. */
+                        if (use_secure_mode && !streq(exe, "less"))
+                                continue;
+
                         r = loop_write(exe_name_pipe[1], exe, strlen(exe) + 1, false);
                         if (r  < 0) {
                                 log_error_errno(r, "Failed to write pager name to socket: %m");
@@ -212,6 +232,7 @@ int pager_open(PagerFlags flags) {
                                        "Failed to execute '%s', using next fallback pager: %m", exe);
                 }
 
+                /* Our builtin is also very secure. */
                 r = loop_write(exe_name_pipe[1], "(built-in)", strlen("(built-in)") + 1, false);
                 if (r < 0) {
                         log_error_errno(r, "Failed to write pager name to socket: %m");