]> granicus.if.org Git - shadow/commitdiff
newuidmap/newgidmap: added support for user matching by UID in /etc/sub[ug]id
authorBostjan Skufca <bostjan@a2o.si>
Fri, 5 Sep 2014 21:21:03 +0000 (21:21 +0000)
committerSerge Hallyn <serge.hallyn@ubuntu.com>
Wed, 17 Sep 2014 20:48:10 +0000 (15:48 -0500)
Until now only exact username specification in /etc/sub[ug]id file allowed the
mapping. This prevented normal use for those users who use multiple usernames
with the same UID, as it rejected mapping even though it was allowed for
another username with the same UID.

This patch initially retains the old behaviour, for performance's sake. In the
first pass, new[ug]idmap only searches for exact username match.
If that yields no valid results, it continues into another loop, which does UID
resolution and comparison. If either definition (numeric UID mapping
specification or mapping specification for another username with the same UID as
current username) is found, it is used.

Signed-off-by: Serge Hallyn <serge.hallyn@ubuntu.com>
lib/subordinateio.c
man/subgid.5.xml
man/subuid.5.xml

index 0ba117b0714bf6ed84f6a49a655f9378ee003d4b..25353368238fd620c7e1425cd716e752be08b56c 100644 (file)
@@ -11,6 +11,8 @@
 #include <stdio.h>
 #include "commonio.h"
 #include "subordinateio.h"
+#include <sys/types.h>
+#include <pwd.h>
 
 struct subordinate_range {
        const char *owner;
@@ -189,6 +191,15 @@ static const struct subordinate_range *find_range(struct commonio_db *db,
                                                  const char *owner, unsigned long val)
 {
        const struct subordinate_range *range;
+
+       /*
+        * Search for exact username/group specification
+        *
+        * This is the original method - go fast through the db, doing only
+        * exact username/group string comparison. Therefore we leave it as-is
+        * for the time being, in order to keep it equally fast as it was
+        * before.
+        */
        commonio_rewind(db);
        while ((range = commonio_next(db)) != NULL) {
                unsigned long first = range->start;
@@ -200,6 +211,74 @@ static const struct subordinate_range *find_range(struct commonio_db *db,
                if ((val >= first) && (val <= last))
                        return range;
        }
+
+
+        /*
+         * We only do special handling for these two files
+         */
+        if ((0 != strcmp(db->filename, "/etc/subuid")) && (0 != strcmp(db->filename, "/etc/subgid")))
+                return NULL;
+
+        /*
+         * Search loop above did not produce any result. Let's rerun it,
+         * but this time try to matcha actual UIDs. The first entry that
+         * matches is considered a success.
+         * (It may be specified as literal UID or as another username which
+         * has the same UID as the username we are looking for.)
+         */
+        struct passwd *pwd;
+        uid_t          owner_uid;
+        char           owner_uid_string[33] = "";
+
+
+        /* Get UID of the username we are looking for */
+        pwd = getpwnam(owner);
+        if (NULL == pwd) {
+                /* Username not defined in /etc/passwd, or error occured during lookup */
+                return NULL;
+        }
+        owner_uid = pwd->pw_uid;
+        sprintf(owner_uid_string, "%lu", (unsigned long int)owner_uid);
+
+        commonio_rewind(db);
+        while ((range = commonio_next(db)) != NULL) {
+                unsigned long first = range->start;
+                unsigned long last = first + range->count - 1;
+
+                /*
+                 * First check if range owner is specified as numeric UID
+                 * and if it matches.
+                 */
+                if (0 != strcmp(range->owner, owner_uid_string)) {
+                        /*
+                         * Ok, this range owner is not specified as numeric UID
+                         * we are looking for. It may be specified as another
+                         * UID or as a literal username.
+                         *
+                         * If specified as another UID, the call to getpwnam()
+                         * will return NULL.
+                         *
+                         * If specified as literal username, we will get its
+                         * UID and compare that to UID we are looking for.
+                         */
+                        const struct passwd *range_owner_pwd;
+
+                        range_owner_pwd = getpwnam(range->owner);
+                        if (NULL == range_owner_pwd) {
+                                continue;
+                        }
+
+                        if (owner_uid != range_owner_pwd->pw_uid) {
+                                continue;
+                        }
+                }
+
+                /* Owner matches, now let us check this UID/GID range */
+                if ((val >= first) && (val <= last)) {
+                        return range;
+                }
+        }
+
        return NULL;
 }
 
index f7b4b50b7f91d48404ebbe621e70eb3424292728..27799ba9338748e994ce3cce12a1555d3f1344c7 100644 (file)
@@ -57,7 +57,7 @@
     </para>
     <itemizedlist mark='bullet'>
       <listitem>
-       <para>login name</para>
+       <para>login name or UID</para>
       </listitem>
       <listitem>
        <para>numerical subordinate group ID</para>
index a833f57f279467186b9ebdb360a4546360d0e43c..939ea166ef40571fa4bcdb2b6eceb7f358d2f966 100644 (file)
@@ -57,7 +57,7 @@
     </para>
     <itemizedlist mark='bullet'>
       <listitem>
-       <para>login name</para>
+       <para>login name or UID</para>
       </listitem>
       <listitem>
        <para>numerical subordinate user ID</para>