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