Re: pkg and root privileges

From: <niko.nastonen_at_icloud.com>
Date: Thu, 28 Jul 2022 15:02:09 UTC
Did a little bugfixing and cleanup, now looks much better.


diff --git a/fetch.c b/fetch.c
index a310fbc..a6d1fb8 100644
--- a/fetch.c
+++ b/fetch.c
@@ -34,6 +34,7 @@
 #include <ctype.h>
 #include <fcntl.h>
 #include <errno.h>
+#include <pwd.h>
 #include <stdio.h>
 #include <string.h>
 #include <fetch.h>
@@ -48,6 +49,8 @@
 #include "private/utils.h"
 #include "private/fetch.h"
 
+extern void drop_privileges(void);
+
 static struct fetcher {
        const char *scheme;
        int (*open)(struct pkg_repo *, struct url *, off_t *);
@@ -82,7 +85,6 @@ static struct fetcher {
        },
 };
 
-
 int
 pkg_fetch_file_tmp(struct pkg_repo *repo, const char *url, char *dest,
        time_t t)
@@ -175,6 +177,8 @@ pkg_fetch_file_to_fd(struct pkg_repo *repo, const char *url, int dest,
        off_t            r;
        char             buf[8192];
        int              retcode = EPKG_OK;
+       int              pstat;
+       pid_t            pid;
        off_t            sz = 0;
        size_t           buflen = 0;
        size_t           left = 0;
@@ -197,6 +201,22 @@ pkg_fetch_file_to_fd(struct pkg_repo *repo, const char *url, int dest,
         * Error if using plain http://, https:// etc with SRV
         */
 
+       pid = fork();
+
+       switch (pid) {
+       case -1:
+               pkg_emit_error("Unable to fork");
+               return (EPKG_FATAL);
+       case 0:
+               drop_privileges();
+               break;
+       default:
+               while (waitpid(pid, &pstat, 0) == -1 && errno == EINTR)
+                       ;
+
+               return (WEXITSTATUS(pstat));
+       }
+
        pkg_debug(1, "Request to fetch %s", url);
        if (repo != NULL &&
                strncmp(URL_SCHEME_PREFIX, url, strlen(URL_SCHEME_PREFIX)) == 0) {
@@ -256,6 +276,7 @@ pkg_fetch_file_to_fd(struct pkg_repo *repo, const char *url, int dest,
                        break;
                }
        }
+
        if (fetcher == NULL) {
                pkg_emit_error("Unknown scheme: %s", u->scheme);
                return (EPKG_FATAL);
@@ -283,6 +304,7 @@ pkg_fetch_file_to_fd(struct pkg_repo *repo, const char *url, int dest,
        left = sizeof(buf);
        if (sz > 0)
                left = sz - done;
+
        while ((r = fread(buf, 1, left < buflen ? left : buflen, remote)) > 0) {
                if (write(dest, buf, r) != r) {
                        pkg_emit_errno("write", "");
@@ -354,5 +376,6 @@ cleanup:
        /* restore original doc */
        fetchFreeURL(u);
 
-       return (retcode);
+       /* exit child */
+       exit(retcode);
 }

> On 26. Jul 2022, at 19.15, niko.nastonen@icloud.com wrote:
> 
> Hi.
> 
> There was a recent discussion on the FreeBSD forum about security of pkg and its ability to drop root privileges when fetching packages.
> 
> I couldn’t help but notice that there was a git commit
> 
> fcceab3f with comment "drop privileges when using libfetch”
> 
> and another one
> 
> f3b0469e with comment "Stop dropping privileges when fetching as it causes more issues than it solved”.
> 
> Can I ask what kind of issues the first commit introduces and why pkg still goes out to the internet unprotected?
> 
> In case the issues are already solved by later commits, let me present a silly patch (mostly copied from fcceab3f) for branch "release-1.18” which makes fetch use nobody instead of root.
> 
> Feel free to modify it to match “the real BSD hacker standards, if applicable” :-)
> 
> 
> 
> diff --git a/libpkg/fetch.c b/libpkg/fetch.c
> index a310fbc3..c8e02f5b 100644
> --- a/libpkg/fetch.c
> +++ b/libpkg/fetch.c
> @@ -30,10 +30,14 @@
>  #include <sys/wait.h>
>  #include <sys/socket.h>
>  #include <sys/time.h>
> +#include <sys/types.h>
>  
>  #include <ctype.h>
>  #include <fcntl.h>
> +#include <err.h>
>  #include <errno.h>
> +#include <pwd.h>
> +#include <signal.h>
>  #include <stdio.h>
>  #include <string.h>
>  #include <fetch.h>
> @@ -48,6 +52,10 @@
>  #include "private/utils.h"
>  #include "private/fetch.h"
>  
> +void sig_handler(int signal);
> +extern void drop_privileges(void);
> +int stop = 0;
> +
>  static struct fetcher {
>         const char *scheme;
>         int (*open)(struct pkg_repo *, struct url *, off_t *);
> @@ -82,7 +90,6 @@ static struct fetcher {
>         },
>  };
>  
> -
>  int
>  pkg_fetch_file_tmp(struct pkg_repo *repo, const char *url, char *dest,
>         time_t t)
> @@ -160,6 +167,13 @@ pkg_fetch_file(struct pkg_repo *repo, const char *url, char *dest, time_t t,
>         return (retcode);
>  }
>  
> +void sig_handler(int signal)
> +{
> +    if (signal == SIGINT)
> +           stop = 1;
> +}
> +
> +
>  #define URL_SCHEME_PREFIX      "pkg+"
>  
>  int
> @@ -175,6 +189,8 @@ pkg_fetch_file_to_fd(struct pkg_repo *repo, const char *url, int dest,
>         off_t            r;
>         char             buf[8192];
>         int              retcode = EPKG_OK;
> +       int              pstat;
> +       pid_t            pid;
>         off_t            sz = 0;
>         size_t           buflen = 0;
>         size_t           left = 0;
> @@ -197,6 +213,25 @@ pkg_fetch_file_to_fd(struct pkg_repo *repo, const char *url, int dest,
>          * Error if using plain http://, https:// etc with SRV
>          */
>  
> +       pid = fork();
> +
> +       switch (pid) {
> +       case -1:
> +               pkg_emit_error("Unable to fork");
> +               return (EPKG_FATAL);
> +       case 0:
> +               sigset(SIGINT, sig_handler);
> +               drop_privileges();
> +               break;
> +       default:
> +               waitpid(pid, &pstat, 0);
> +
> +               if (WEXITSTATUS(pstat) != 0)
> +                       return (EPKG_FATAL);
> +
> +               return (EPKG_OK);
> +       }
> +
>         pkg_debug(1, "Request to fetch %s", url);
>         if (repo != NULL &&
>                 strncmp(URL_SCHEME_PREFIX, url, strlen(URL_SCHEME_PREFIX)) == 0) {
> @@ -256,6 +291,7 @@ pkg_fetch_file_to_fd(struct pkg_repo *repo, const char *url, int dest,
>                         break;
>                 }
>         }
> +
>         if (fetcher == NULL) {
>                 pkg_emit_error("Unknown scheme: %s", u->scheme);
>                 return (EPKG_FATAL);
> @@ -283,7 +319,14 @@ pkg_fetch_file_to_fd(struct pkg_repo *repo, const char *url, int dest,
>         left = sizeof(buf);
>         if (sz > 0)
>                 left = sz - done;
> +
>         while ((r = fread(buf, 1, left < buflen ? left : buflen, remote)) > 0) {
> +
> +               if (stop)  {
> +                       retcode = EPKG_FATAL;
> +                       goto cleanup;
> +               }
> +
>                 if (write(dest, buf, r) != r) {
>                         pkg_emit_errno("write", "");
>                         retcode = EPKG_FATAL;
> @@ -351,6 +394,13 @@ cleanup:
>                 futimes(dest, ftimes);
>         }
>  
> +       if (strncmp(u->scheme, "ssh", 3) != 0) {
> +               if (retcode == EPKG_OK)
> +                       exit(0);
> +
> +               exit(EXIT_FAILURE);
> +       }
> +
>         /* restore original doc */
>         fetchFreeURL(u);