Pablo Greco 48fc63
From 18f3bad3ba1ffc5a363d188da8b0653f9b527a18 Mon Sep 17 00:00:00 2001
Pablo Greco 48fc63
From: Lennart Poettering <lennart@poettering.net>
Pablo Greco 48fc63
Date: Fri, 5 Jan 2018 13:26:38 +0100
Pablo Greco 48fc63
Subject: [PATCH] notify: add new --uid= command
Pablo Greco 48fc63
Pablo Greco 48fc63
The new --uid= switch allows selecting the UID from which the
Pablo Greco 48fc63
notificaiton messages shall originate.
Pablo Greco 48fc63
Pablo Greco 48fc63
This is primarily useful for testing purposes, but might have other
Pablo Greco 48fc63
uses.
Pablo Greco 48fc63
Pablo Greco 48fc63
(cherry picked from commit 65c6b99094580afa186199d8091cd7536900526c)
Pablo Greco 48fc63
Pablo Greco 48fc63
Related: #1663143
Pablo Greco 48fc63
---
Pablo Greco 48fc63
 man/systemd-notify.xml |  9 +++++++++
Pablo Greco 48fc63
 src/notify/notify.c    | 39 ++++++++++++++++++++++++++++++++++++---
Pablo Greco 48fc63
 2 files changed, 45 insertions(+), 3 deletions(-)
Pablo Greco 48fc63
Pablo Greco 48fc63
diff --git a/man/systemd-notify.xml b/man/systemd-notify.xml
Pablo Greco 48fc63
index 46ede1ab8f..4048783611 100644
Pablo Greco 48fc63
--- a/man/systemd-notify.xml
Pablo Greco 48fc63
+++ b/man/systemd-notify.xml
Pablo Greco 48fc63
@@ -106,6 +106,15 @@
Pablo Greco 48fc63
         <citerefentry><refentrytitle>sd_notify</refentrytitle><manvolnum>3</manvolnum></citerefentry>.</para></listitem>
Pablo Greco 48fc63
       </varlistentry>
Pablo Greco 48fc63
 
Pablo Greco 48fc63
+      <varlistentry>
Pablo Greco 48fc63
+        <term><option>--uid=</option><replaceable>USER</replaceable></term>
Pablo Greco 48fc63
+
Pablo Greco 48fc63
+        <listitem><para>Set the user ID to send the notification from. Takes a UNIX user name or numeric UID. When
Pablo Greco 48fc63
+        specified the notification message will be sent with the specified UID as sender, in place of the user the
Pablo Greco 48fc63
+        command was invoked as. This option requires sufficient privileges in order to be able manipulate the user
Pablo Greco 48fc63
+        identity of the process.</para></listitem>
Pablo Greco 48fc63
+      </varlistentry>
Pablo Greco 48fc63
+
Pablo Greco 48fc63
       <varlistentry>
Pablo Greco 48fc63
         <term><option>--status=</option></term>
Pablo Greco 48fc63
 
Pablo Greco 48fc63
diff --git a/src/notify/notify.c b/src/notify/notify.c
Pablo Greco 48fc63
index 0d382992a5..659ba7c54b 100644
Pablo Greco 48fc63
--- a/src/notify/notify.c
Pablo Greco 48fc63
+++ b/src/notify/notify.c
Pablo Greco 48fc63
@@ -40,6 +40,8 @@ static pid_t arg_pid = 0;
Pablo Greco 48fc63
 static const char *arg_status = NULL;
Pablo Greco 48fc63
 static bool arg_booted = false;
Pablo Greco 48fc63
 static const char *arg_readahead = NULL;
Pablo Greco 48fc63
+static uid_t arg_uid = UID_INVALID;
Pablo Greco 48fc63
+static gid_t arg_gid = GID_INVALID;
Pablo Greco 48fc63
 
Pablo Greco 48fc63
 static void help(void) {
Pablo Greco 48fc63
         printf("%s [OPTIONS...] [VARIABLE=VALUE...]\n\n"
Pablo Greco 48fc63
@@ -47,7 +49,8 @@ static void help(void) {
Pablo Greco 48fc63
                "  -h --help            Show this help\n"
Pablo Greco 48fc63
                "     --version         Show package version\n"
Pablo Greco 48fc63
                "     --ready           Inform the init system about service start-up completion\n"
Pablo Greco 48fc63
-               "     --pid[=PID]       Set main pid of daemon\n"
Pablo Greco 48fc63
+               "     --pid[=PID]       Set main PID of daemon\n"
Pablo Greco 48fc63
+               "     --uid=USER        Set user to send from\n"
Pablo Greco 48fc63
                "     --status=TEXT     Set status text\n"
Pablo Greco 48fc63
                "     --booted          Check if the system was booted up with systemd\n"
Pablo Greco 48fc63
                "     --readahead=ACTION Controls read-ahead operations\n",
Pablo Greco 48fc63
@@ -62,7 +65,8 @@ static int parse_argv(int argc, char *argv[]) {
Pablo Greco 48fc63
                 ARG_PID,
Pablo Greco 48fc63
                 ARG_STATUS,
Pablo Greco 48fc63
                 ARG_BOOTED,
Pablo Greco 48fc63
-                ARG_READAHEAD
Pablo Greco 48fc63
+                ARG_READAHEAD,
Pablo Greco 48fc63
+                ARG_UID,
Pablo Greco 48fc63
         };
Pablo Greco 48fc63
 
Pablo Greco 48fc63
         static const struct option options[] = {
Pablo Greco 48fc63
@@ -73,10 +77,11 @@ static int parse_argv(int argc, char *argv[]) {
Pablo Greco 48fc63
                 { "status",    required_argument, NULL, ARG_STATUS    },
Pablo Greco 48fc63
                 { "booted",    no_argument,       NULL, ARG_BOOTED    },
Pablo Greco 48fc63
                 { "readahead", required_argument, NULL, ARG_READAHEAD },
Pablo Greco 48fc63
+                { "uid",       required_argument, NULL, ARG_UID       },
Pablo Greco 48fc63
                 {}
Pablo Greco 48fc63
         };
Pablo Greco 48fc63
 
Pablo Greco 48fc63
-        int c;
Pablo Greco 48fc63
+        int c, r;
Pablo Greco 48fc63
 
Pablo Greco 48fc63
         assert(argc >= 0);
Pablo Greco 48fc63
         assert(argv);
Pablo Greco 48fc63
@@ -122,6 +127,18 @@ static int parse_argv(int argc, char *argv[]) {
Pablo Greco 48fc63
                         arg_readahead = optarg;
Pablo Greco 48fc63
                         break;
Pablo Greco 48fc63
 
Pablo Greco 48fc63
+                case ARG_UID: {
Pablo Greco 48fc63
+                        const char *u = optarg;
Pablo Greco 48fc63
+
Pablo Greco 48fc63
+                        r = get_user_creds(&u, &arg_uid, &arg_gid, NULL, NULL);
Pablo Greco 48fc63
+                        if (r == -ESRCH) /* If the user doesn't exist, then accept it anyway as numeric */
Pablo Greco 48fc63
+                                r = parse_uid(u, &arg_uid);
Pablo Greco 48fc63
+                        if (r < 0)
Pablo Greco 48fc63
+                                return log_error_errno(r, "Can't resolve user %s: %m", optarg);
Pablo Greco 48fc63
+
Pablo Greco 48fc63
+                        break;
Pablo Greco 48fc63
+                }
Pablo Greco 48fc63
+
Pablo Greco 48fc63
                 case '?':
Pablo Greco 48fc63
                         return -EINVAL;
Pablo Greco 48fc63
 
Pablo Greco 48fc63
@@ -209,6 +226,22 @@ int main(int argc, char* argv[]) {
Pablo Greco 48fc63
                 goto finish;
Pablo Greco 48fc63
         }
Pablo Greco 48fc63
 
Pablo Greco 48fc63
+        /* If this is requested change to the requested UID/GID. Note thta we only change the real UID here, and leave
Pablo Greco 48fc63
+           the effective UID in effect (which is 0 for this to work). That's because we want the privileges to fake the
Pablo Greco 48fc63
+           ucred data, and sd_pid_notify() uses the real UID for filling in ucred. */
Pablo Greco 48fc63
+
Pablo Greco 48fc63
+        if (arg_gid != GID_INVALID)
Pablo Greco 48fc63
+                if (setregid(arg_gid, (gid_t) -1) < 0) {
Pablo Greco 48fc63
+                        r = log_error_errno(errno, "Failed to change GID: %m");
Pablo Greco 48fc63
+                        goto finish;
Pablo Greco 48fc63
+                }
Pablo Greco 48fc63
+
Pablo Greco 48fc63
+        if (arg_uid != UID_INVALID)
Pablo Greco 48fc63
+                if (setreuid(arg_uid, (uid_t) -1) < 0) {
Pablo Greco 48fc63
+                        r = log_error_errno(errno, "Failed to change UID: %m");
Pablo Greco 48fc63
+                        goto finish;
Pablo Greco 48fc63
+                }
Pablo Greco 48fc63
+
Pablo Greco 48fc63
         r = sd_pid_notify(arg_pid ? arg_pid : getppid(), false, n);
Pablo Greco 48fc63
         if (r < 0) {
Pablo Greco 48fc63
                 log_error_errno(r, "Failed to notify init system: %m");