I found more occurences of unchecked values for set*id() functions in other
inetutils programs: ftpd, rcp.

It has different security impact if it can be triggered:

* rcp: local privilege escalation to the user running the binary
* ftpd: undefined behaviour without privilege escalation as all calls are
to seteuid(0) (gaining root privileges, not dropping it)

I am attaching a consolidated patch to fix these and the previous ones.

---

>From 05bca16ab557abe18c9deca0e64e2ce5a43cb875 Mon Sep 17 00:00:00 2001
From: Jeffrey Bencteux <jeffbenct...@gmail.com>
Date: Fri, 30 Jun 2023 19:02:45 +0200
Subject: [PATCH] ftpd,rcp,rlogin,rsh,rshd,uucpd: fix: check set*id() return
 values

Several setuid(), setgid(), seteuid() and setguid() return values
 were not checked in ftpd/rcp/rlogin/rsh/rshd/uucpd code potentially
leading to potential security issues.
---
 ftpd/ftpd.c  | 10 +++++++---
 src/rcp.c    | 39 +++++++++++++++++++++++++++++++++------
 src/rlogin.c | 11 +++++++++--
 src/rsh.c    | 25 +++++++++++++++++++++----
 src/rshd.c   | 20 +++++++++++++++++---
 src/uucpd.c  | 15 +++++++++++++--
 6 files changed, 100 insertions(+), 20 deletions(-)

diff --git a/ftpd/ftpd.c b/ftpd/ftpd.c
index 92b2cca..28dd523 100644
--- a/ftpd/ftpd.c
+++ b/ftpd/ftpd.c
@@ -862,7 +862,9 @@ end_login (struct credentials *pcred)
   char *remotehost = pcred->remotehost;
   int atype = pcred->auth_type;

-  seteuid ((uid_t) 0);
+  if (seteuid ((uid_t) 0) == -1)
+    _exit (EXIT_FAILURE);
+
   if (pcred->logged_in)
     {
       logwtmp_keep_open (ttyline, "", "");
@@ -1151,7 +1153,8 @@ getdatasock (const char *mode)

   if (data >= 0)
     return fdopen (data, mode);
-  seteuid ((uid_t) 0);
+  if (seteuid ((uid_t) 0) == -1)
+    _exit (EXIT_FAILURE);
   s = socket (ctrl_addr.ss_family, SOCK_STREAM, 0);
   if (s < 0)
     goto bad;
@@ -1978,7 +1981,8 @@ passive (int epsv, int af)
   else /* !AF_INET6 */
     ((struct sockaddr_in *) &pasv_addr)->sin_port = 0;

-  seteuid ((uid_t) 0);
+  if (seteuid ((uid_t) 0) == -1)
+    _exit (EXIT_FAILURE);
   if (bind (pdata, (struct sockaddr *) &pasv_addr, pasv_addrlen) < 0)
     {
       if (seteuid ((uid_t) cred.uid))
diff --git a/src/rcp.c b/src/rcp.c
index 75adb25..cdcf850 100644
--- a/src/rcp.c
+++ b/src/rcp.c
@@ -345,14 +345,23 @@ main (int argc, char *argv[])
   if (from_option)
     { /* Follow "protocol", send data. */
       response ();
-      setuid (userid);
+
+      if (setuid (userid) == -1)
+      {
+        error (EXIT_FAILURE, 0, "Could not drop privileges (setuid()
failed)");
+      }
+
       source (argc, argv);
       exit (errs);
     }

   if (to_option)
     { /* Receive data. */
-      setuid (userid);
+      if (setuid (userid) == -1)
+      {
+        error (EXIT_FAILURE, 0, "Could not drop privileges (setuid()
failed)");
+      }
+
       sink (argc, argv);
       exit (errs);
     }
@@ -537,7 +546,11 @@ toremote (char *targ, int argc, char *argv[])
        if (response () < 0)
  exit (EXIT_FAILURE);
        free (bp);
-       setuid (userid);
+
+       if (setuid (userid) == -1)
+              {
+                error (EXIT_FAILURE, 0, "Could not drop privileges
(setuid() failed)");
+              }
      }
    source (1, argv + i);
    close (rem);
@@ -630,7 +643,12 @@ tolocal (int argc, char *argv[])
    ++errs;
    continue;
  }
-      seteuid (userid);
+
+      if (seteuid (userid) == -1)
+      {
+        error (EXIT_FAILURE, 0, "Could not drop privileges (seteuid()
failed)");
+      }
+
 #if defined IP_TOS && defined IPPROTO_IP && defined IPTOS_THROUGHPUT
       sslen = sizeof (ss);
       (void) getpeername (rem, (struct sockaddr *) &ss, &sslen);
@@ -643,7 +661,12 @@ tolocal (int argc, char *argv[])
 #endif
       vect[0] = target;
       sink (1, vect);
-      seteuid (effuid);
+
+      if (seteuid (effuid) == -1)
+      {
+        error (EXIT_FAILURE, 0, "Could not drop privileges (seteuid()
failed)");
+      }
+
       close (rem);
       rem = -1;
 #ifdef SHISHI
@@ -1441,7 +1464,11 @@ susystem (char *s, int userid)
       return (127);

     case 0:
-      setuid (userid);
+      if (setuid (userid) == -1)
+      {
+        error (EXIT_FAILURE, 0, "Could not drop privileges (setuid()
failed)");
+      }
+
       execl (PATH_BSHELL, "sh", "-c", s, NULL);
       _exit (127);
     }
diff --git a/src/rlogin.c b/src/rlogin.c
index aa6426f..c543de0 100644
--- a/src/rlogin.c
+++ b/src/rlogin.c
@@ -647,8 +647,15 @@ try_connect:
   /* Now change to the real user ID.  We have to be set-user-ID root
      to get the privileged port that rcmd () uses.  We now want, however,
      to run as the real user who invoked us.  */
-  seteuid (uid);
-  setuid (uid);
+  if (seteuid (uid) == -1)
+  {
+    error (EXIT_FAILURE, 0, "Could not drop privileges (seteuid()
failed)");
+  }
+
+  if (setuid (uid) == -1)
+  {
+    error (EXIT_FAILURE, 0, "Could not drop privileges (setuid() failed)");
+  }

   doit (&osmask); /* The old mask will activate SIGURG and SIGUSR1!  */

diff --git a/src/rsh.c b/src/rsh.c
index 2d622ca..6f60667 100644
--- a/src/rsh.c
+++ b/src/rsh.c
@@ -276,8 +276,17 @@ main (int argc, char **argv)
     {
       if (asrsh)
  *argv = (char *) "rlogin";
-      seteuid (getuid ());
-      setuid (getuid ());
+
+      if (seteuid (getuid ()) == -1)
+      {
+        error (EXIT_FAILURE, errno, "seteuid() failed");
+      }
+
+      if (setuid (getuid ()) == -1)
+      {
+        error (EXIT_FAILURE, errno, "setuid() failed");
+      }
+
       execv (PATH_RLOGIN, argv);
       error (EXIT_FAILURE, errno, "cannot execute %s", PATH_RLOGIN);
     }
@@ -541,8 +550,16 @@ try_connect:
  error (0, errno, "setsockopt DEBUG (ignored)");
     }

-  seteuid (uid);
-  setuid (uid);
+  if (seteuid (uid) == -1)
+  {
+    error (EXIT_FAILURE, errno, "seteuid() failed");
+  }
+
+  if (setuid (uid) == -1)
+  {
+    error (EXIT_FAILURE, errno, "setuid() failed");
+  }
+
 #ifdef HAVE_SIGACTION
   sigemptyset (&sigs);
   sigaddset (&sigs, SIGINT);
diff --git a/src/rshd.c b/src/rshd.c
index d1c0d0c..707790e 100644
--- a/src/rshd.c
+++ b/src/rshd.c
@@ -1847,8 +1847,18 @@ doit (int sockfd, struct sockaddr *fromp, socklen_t
fromlen)
     pwd->pw_shell = PATH_BSHELL;

   /* Set the gid, then uid to become the user specified by "locuser" */
-  setegid ((gid_t) pwd->pw_gid);
-  setgid ((gid_t) pwd->pw_gid);
+  if (setegid ((gid_t) pwd->pw_gid) == -1)
+  {
+    rshd_error ("Cannot drop privileges (setegid() failed)\n");
+    exit (EXIT_FAILURE);
+  }
+
+  if (setgid ((gid_t) pwd->pw_gid) == -1)
+  {
+    rshd_error ("Cannot drop privileges (setgid() failed)\n");
+    exit (EXIT_FAILURE);
+  }
+
 #ifdef HAVE_INITGROUPS
   initgroups (pwd->pw_name, pwd->pw_gid); /* BSD groups */
 #endif
@@ -1870,7 +1880,11 @@ doit (int sockfd, struct sockaddr *fromp, socklen_t
fromlen)
     }
 #endif /* WITH_PAM */

-  setuid ((uid_t) pwd->pw_uid);
+  if (setuid ((uid_t) pwd->pw_uid) == -1)
+  {
+    rshd_error ("Cannot drop privileges (setuid() failed)\n");
+    exit (EXIT_FAILURE);
+  }

   /* We'll execute the client's command in the home directory
    * of locuser. Note, that the chdir must be executed after
diff --git a/src/uucpd.c b/src/uucpd.c
index 107589e..29cfce3 100644
--- a/src/uucpd.c
+++ b/src/uucpd.c
@@ -252,7 +252,12 @@ doit (struct sockaddr *sap, socklen_t salen)
   snprintf (Username, sizeof (Username), "USER=%s", user);
   snprintf (Logname, sizeof (Logname), "LOGNAME=%s", user);
   dologin (pw, sap, salen);
-  setgid (pw->pw_gid);
+
+  if (setgid (pw->pw_gid) == -1)
+  {
+    fprintf (stderr, "setgid() failed");
+    return;
+  }
 #ifdef HAVE_INITGROUPS
   initgroups (pw->pw_name, pw->pw_gid);
 #endif
@@ -261,7 +266,13 @@ doit (struct sockaddr *sap, socklen_t salen)
       fprintf (stderr, "Login incorrect.");
       return;
     }
-  setuid (pw->pw_uid);
+
+  if (setuid (pw->pw_uid) == -1)
+  {
+    fprintf (stderr, "setuid() failed");
+    return;
+  }
+
   execl (uucico_location, "uucico", NULL);
   perror ("uucico server: execl");
 }
-- 
2.35.1

-- 
Jeffrey BENCTEUX


Le sam. 1 juil. 2023 à 10:30, Jeffrey <jeffbenct...@gmail.com> a écrit :

> Hi,
>
> Several setuid(), setgid(), seteuid() and setguid() return values are not
> checked in rlogin/rsh/rshd/uucpd code:
>
> rlogin.c:
>
>     647   /* Now change to the real user ID.  We have to be set-user-ID
> root
>     648      to get the privileged port that rcmd () uses.  We now want,
> however,
>     649      to run as the real user who invoked us.  */
>     650   seteuid (uid);
>     651   setuid (uid);
>     652
>     653   doit (&osmask);       /* The old mask will activate SIGURG and
> SIGUSR1!  */
>
> rsh.c:
>
>     274   /* If no further arguments, must have been called as rlogin. */
>     275   if (!argv[index])
>     276     {
>     277       if (asrsh)
>     278         *argv = (char *) "rlogin";
>     279       seteuid (getuid ());
>     280       setuid (getuid ());
>     281       execv (PATH_RLOGIN, argv);
>     282       error (EXIT_FAILURE, errno, "cannot execute %s",
> PATH_RLOGIN);
>     283     }
>
> [...]
>
>     541         error (0, errno, "setsockopt DEBUG (ignored)");
>     542     }
>     543
>     544   seteuid (uid);
>     545   setuid (uid);
>     546 #ifdef HAVE_SIGACTION
>     547   sigemptyset (&sigs);
>
> rshd.c:
>
>    1846   if (*pwd->pw_shell == '\0')
>    1847     pwd->pw_shell = PATH_BSHELL;
>    1848
>    1849   /* Set the gid, then uid to become the user specified by
> "locuser" */
>    1850   setegid ((gid_t) pwd->pw_gid);
>    1851   setgid ((gid_t) pwd->pw_gid);
>    1852 #ifdef HAVE_INITGROUPS
>    1853   initgroups (pwd->pw_name, pwd->pw_gid);       /* BSD groups */
>
> [...]
>
>    1871 #endif /* WITH_PAM */
>    1872
>    1873   setuid ((uid_t) pwd->pw_uid);
>    1874
>    1875   /* We'll execute the client's command in the home directory
>    1876    * of locuser. Note, that the chdir must be executed after
>    1877    * setuid(), otherwise it may fail on NFS mounted directories
>    1878    * (root mapped to nobody).
>    1879    */
>    1880   if (chdir (pwd->pw_dir) < 0)
>
> uucpd.c:
>
>     252   snprintf (Username, sizeof (Username), "USER=%s", user);
>     253   snprintf (Logname, sizeof (Logname), "LOGNAME=%s", user);
>     254   dologin (pw, sap, salen);
>     255   setgid (pw->pw_gid); <=========
>     256 #ifdef HAVE_INITGROUPS
>     257   initgroups (pw->pw_name, pw->pw_gid);
>     258 #endif
>     259   if (chdir (pw->pw_dir) < 0)
>     260     {
>     261       fprintf (stderr, "Login incorrect.");
>     262       return;
>     263     }
>     264   setuid (pw->pw_uid); <=========
>     265   execl (uucico_location, "uucico", NULL);
>     266   perror ("uucico server: execl");
>
>
> There are cases where set*id() functions can fail, for example multiple
> calls to the clone() function can cause setuid() to fail when the user
> process limit is reached.
>
> man 2 setuid():
>
> RETURN VALUE
>        On success, zero is returned.  On error, -1 is returned, and errno
> is set to indicate the error.
>
>        Note: there are cases where setuid() can fail even when the caller
> is UID 0; it is a grave security error to omit checking for a failure
> return from setuid().
>
> The above code could be abused in different ways to trigger such failures,
> potentially remotely in the case of rshd and uucpd.
>
> Here are some example scenarios:
>
> * rshd: if daemon run as root, privilege escalation is possible as any
> user logging in after a set*id() failure would have its session started as
> root.
> * rlogin: potential local privilege escalation as the binary is setUID root
> * uucpd: potential remote privilege escalation to root for already valid
> users
>
> I believe the below patch mitigates the issue, let me know if that suits
> you.
>
> Regards,
>
> ---
>
> From: Jeffrey Bencteux <jeffbenct...@gmail.com>
> Date: Fri, 30 Jun 2023 19:02:45 +0200
> Subject: [PATCH] rlogin,rsh,rshd,uucpd: fix: check set*id() return values
>
> Several setuid(), setgid(), seteuid() and setguid() return values
>  were not checked in rlogin/rsh/rshd/uucpd code potentially
> leading to security issues.
> ---
>  src/rlogin.c | 11 +++++++++--
>  src/rsh.c    | 25 +++++++++++++++++++++----
>  src/rshd.c   | 20 +++++++++++++++++---
>  src/uucpd.c  | 15 +++++++++++++--
>  4 files changed, 60 insertions(+), 11 deletions(-)
>
> diff --git a/src/rlogin.c b/src/rlogin.c
> index aa6426f..c543de0 100644
> --- a/src/rlogin.c
> +++ b/src/rlogin.c
> @@ -647,8 +647,15 @@ try_connect:
>    /* Now change to the real user ID.  We have to be set-user-ID root
>       to get the privileged port that rcmd () uses.  We now want, however,
>       to run as the real user who invoked us.  */
> -  seteuid (uid);
> -  setuid (uid);
> +  if (seteuid (uid) == -1)
> +  {
> +    error (EXIT_FAILURE, 0, "Could not drop privileges (seteuid()
> failed)");
> +  }
> +
> +  if (setuid (uid) == -1)
> +  {
> +    error (EXIT_FAILURE, 0, "Could not drop privileges (setuid()
> failed)");
> +  }
>
>    doit (&osmask); /* The old mask will activate SIGURG and SIGUSR1!  */
>
> diff --git a/src/rsh.c b/src/rsh.c
> index 2d622ca..6f60667 100644
> --- a/src/rsh.c
> +++ b/src/rsh.c
> @@ -276,8 +276,17 @@ main (int argc, char **argv)
>      {
>        if (asrsh)
>   *argv = (char *) "rlogin";
> -      seteuid (getuid ());
> -      setuid (getuid ());
> +
> +      if (seteuid (getuid ()) == -1)
> +      {
> +        error (EXIT_FAILURE, errno, "seteuid() failed");
> +      }
> +
> +      if (setuid (getuid ()) == -1)
> +      {
> +        error (EXIT_FAILURE, errno, "setuid() failed");
> +      }
> +
>        execv (PATH_RLOGIN, argv);
>        error (EXIT_FAILURE, errno, "cannot execute %s", PATH_RLOGIN);
>      }
> @@ -541,8 +550,16 @@ try_connect:
>   error (0, errno, "setsockopt DEBUG (ignored)");
>      }
>
> -  seteuid (uid);
> -  setuid (uid);
> +  if (seteuid (uid) == -1)
> +  {
> +    error (EXIT_FAILURE, errno, "seteuid() failed");
> +  }
> +
> +  if (setuid (uid) == -1)
> +  {
> +    error (EXIT_FAILURE, errno, "setuid() failed");
> +  }
> +
>  #ifdef HAVE_SIGACTION
>    sigemptyset (&sigs);
>    sigaddset (&sigs, SIGINT);
> diff --git a/src/rshd.c b/src/rshd.c
> index d1c0d0c..707790e 100644
> --- a/src/rshd.c
> +++ b/src/rshd.c
> @@ -1847,8 +1847,18 @@ doit (int sockfd, struct sockaddr *fromp, socklen_t
> fromlen)
>      pwd->pw_shell = PATH_BSHELL;
>
>    /* Set the gid, then uid to become the user specified by "locuser" */
> -  setegid ((gid_t) pwd->pw_gid);
> -  setgid ((gid_t) pwd->pw_gid);
> +  if (setegid ((gid_t) pwd->pw_gid) == -1)
> +  {
> +    rshd_error ("Cannot drop privileges (setegid() failed)\n");
> +    exit (EXIT_FAILURE);
> +  }
> +
> +  if (setgid ((gid_t) pwd->pw_gid) == -1)
> +  {
> +    rshd_error ("Cannot drop privileges (setgid() failed)\n");
> +    exit (EXIT_FAILURE);
> +  }
> +
>  #ifdef HAVE_INITGROUPS
>    initgroups (pwd->pw_name, pwd->pw_gid); /* BSD groups */
>  #endif
> @@ -1870,7 +1880,11 @@ doit (int sockfd, struct sockaddr *fromp, socklen_t
> fromlen)
>      }
>  #endif /* WITH_PAM */
>
> -  setuid ((uid_t) pwd->pw_uid);
> +  if (setuid ((uid_t) pwd->pw_uid) == -1)
> +  {
> +    rshd_error ("Cannot drop privileges (setuid() failed)\n");
> +    exit (EXIT_FAILURE);
> +  }
>
>    /* We'll execute the client's command in the home directory
>     * of locuser. Note, that the chdir must be executed after
> diff --git a/src/uucpd.c b/src/uucpd.c
> index 107589e..29cfce3 100644
> --- a/src/uucpd.c
> +++ b/src/uucpd.c
> @@ -252,7 +252,12 @@ doit (struct sockaddr *sap, socklen_t salen)
>    snprintf (Username, sizeof (Username), "USER=%s", user);
>    snprintf (Logname, sizeof (Logname), "LOGNAME=%s", user);
>    dologin (pw, sap, salen);
> -  setgid (pw->pw_gid);
> +
> +  if (setgid (pw->pw_gid) == -1)
> +  {
> +    fprintf (stderr, "setgid() failed");
> +    return;
> +  }
>  #ifdef HAVE_INITGROUPS
>    initgroups (pw->pw_name, pw->pw_gid);
>  #endif
> @@ -261,7 +266,13 @@ doit (struct sockaddr *sap, socklen_t salen)
>        fprintf (stderr, "Login incorrect.");
>        return;
>      }
> -  setuid (pw->pw_uid);
> +
> +  if (setuid (pw->pw_uid) == -1)
> +  {
> +    fprintf (stderr, "setuid() failed");
> +    return;
> +  }
> +
>    execl (uucico_location, "uucico", NULL);
>    perror ("uucico server: execl");
>  }
> --
> 2.35.1
>
> --
> Jeffrey BENCTEUX
>
  • setuid/setg... Jeffrey
    • Re: se... Jeffrey
      • Re... Simon Josefsson via Bug reports for the GNU Internet utilities
      • Re... Simon Josefsson via Bug reports for the GNU Internet utilities
        • ... Jeffrey
          • ... Simon Josefsson via Bug reports for the GNU Internet utilities

Reply via email to