> Whoops, you're almost right here. You get EPERM on chown after pledge if
> you try to change the group ID of a file to the ID of a group the
> current process is not a member of.

Oh, you're right...  I shouldn't have trusted the man page alone :/

Your solution makes sense to me, but if anything in this direction is
the way to go, I'd like to suggest the following variant of your idea:

* We can make a pledge("id") at the start.  Drop this after setrlimit(2)
* Try to find the kmem group early on and use setegid(2) instead of
  initgroups(2).  Pass kmem's gid as an argument to kvm_mkdb().
* If the kmem group wasn't found, don't try to chown in kvm_mkdb()


Index: usr.sbin/kvm_mkdb/kvm_mkdb.c
===================================================================
RCS file: /var/cvs/src/usr.sbin/kvm_mkdb/kvm_mkdb.c,v
retrieving revision 1.25
diff -u -p -r1.25 kvm_mkdb.c
--- usr.sbin/kvm_mkdb/kvm_mkdb.c        5 Nov 2015 16:15:47 -0000       1.25
+++ usr.sbin/kvm_mkdb/kvm_mkdb.c        8 Nov 2015 10:15:50 -0000
@@ -48,8 +48,8 @@
 
 #include "extern.h"
 
-void usage(void);
-int kvm_mkdb(int, const char *, char *, char *, int);
+__dead void usage(void);
+int kvm_mkdb(int, const char *, char *, char *, gid_t, int);
 
 HASHINFO openinfo = {
        4096,           /* bsize */
@@ -64,10 +64,25 @@ int
 main(int argc, char *argv[])
 {
        struct rlimit rl;
+       struct group *gr;
+       gid_t egid;
        int fd, rval, ch, verbose = 0;
        char *nlistpath, *nlistname;
        char dbdir[PATH_MAX];
 
+       if (pledge("stdio rpath wpath cpath fattr flock id", NULL) == -1)
+               err(1, "pledge");
+
+       /* Try to use the kmem group to be able to fchown() in kvm_mkdb(). */
+       egid = -1;
+       if ((gr = getgrnam("kmem")) == NULL) {
+               warn("can't find kmem group");
+       } else {
+               egid = gr->gr_gid;
+               if (setegid(egid) == -1)
+                       err(1, "setegid");
+       }
+
        /* Increase our data size to the max if we can. */
        if (getrlimit(RLIMIT_DATA, &rl) == 0) {
                rl.rlim_cur = rl.rlim_max;
@@ -75,6 +90,9 @@ main(int argc, char *argv[])
                        warn("can't set rlimit data size");
        }
 
+       if (pledge("stdio rpath wpath cpath fattr flock", NULL) == -1)
+               err(1, "pledge");
+
        strlcpy(dbdir, _PATH_VARDB, sizeof(dbdir));
        while ((ch = getopt(argc, argv, "vo:")) != -1)
                switch (ch) {
@@ -98,20 +116,17 @@ main(int argc, char *argv[])
        if (argc > 1)
                usage();
 
-       if (pledge("stdio rpath wpath cpath fattr flock", NULL) == -1)
-               err(1, "pledge");
-
        /* If no kernel specified use _PATH_KSYMS and fall back to _PATH_UNIX */
        if (argc > 0) {
                nlistpath = argv[0];
                nlistname = basename(nlistpath);
                if ((fd = open(nlistpath, O_RDONLY, 0)) == -1)
                        err(1, "can't open %s", nlistpath);
-               rval = kvm_mkdb(fd, dbdir, nlistpath, nlistname, verbose);
+               rval = kvm_mkdb(fd, dbdir, nlistpath, nlistname, egid, verbose);
        } else {
                nlistname = basename(_PATH_UNIX);
                if ((fd = open((nlistpath = _PATH_KSYMS), O_RDONLY, 0)) == -1 ||
-                   (rval = kvm_mkdb(fd, dbdir, nlistpath, nlistname, 
+                   (rval = kvm_mkdb(fd, dbdir, nlistpath, nlistname, egid,
                    verbose)) != 0) {
                        if (fd == -1) 
                                warnx("can't open %s", _PATH_KSYMS);
@@ -119,7 +134,7 @@ main(int argc, char *argv[])
                                warnx("will try again using %s instead", 
_PATH_UNIX);
                        if ((fd = open((nlistpath = _PATH_UNIX), O_RDONLY, 0)) 
== -1)
                                err(1, "can't open %s", nlistpath);
-                       rval = kvm_mkdb(fd, dbdir, nlistpath, nlistname, 
+                       rval = kvm_mkdb(fd, dbdir, nlistpath, nlistname, egid,
                            verbose);
                }
        }
@@ -127,13 +142,12 @@ main(int argc, char *argv[])
 }
 
 int
-kvm_mkdb(int fd, const char *dbdir, char *nlistpath, char *nlistname, 
+kvm_mkdb(int fd, const char *dbdir, char *nlistpath, char *nlistname, gid_t 
gid, 
     int verbose)
 {
        DB *db;
        char dbtemp[PATH_MAX], dbname[PATH_MAX];
        int r;
-       struct group *gr;
 
        r = snprintf(dbtemp, sizeof(dbtemp), "%skvm_%s.tmp",
            dbdir, nlistname);
@@ -164,9 +178,7 @@ kvm_mkdb(int fd, const char *dbdir, char
                return(1);
        }
 
-       if ((gr = getgrnam("kmem")) == NULL) {
-               warn("can't find kmem group");
-       } else if (fchown(db->fd(db), -1, gr->gr_gid)) {
+       if (gid != -1 && fchown(db->fd(db), -1, gid) == -1) {
                warn("can't chown %s", dbtemp);
                (void)unlink(dbtemp);
                return(1);
@@ -192,7 +204,7 @@ kvm_mkdb(int fd, const char *dbdir, char
        return(0);
 }
 
-void
+__dead void
 usage(void)
 {
        (void)fprintf(stderr, "usage: kvm_mkdb [-v] [-o directory] [file]\n");

Reply via email to