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