]> granicus.if.org Git - shadow/blobdiff - lib/subordinateio.c
remove unused fn commonio_next
[shadow] / lib / subordinateio.c
index 34630f2c608e43bf522ee0cf9f6e0cc94a92a3c6..0d89a64ef7bb0e6dcc954198ec71c39022fed395 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;
@@ -20,6 +22,14 @@ struct subordinate_range {
 
 #define NFIELDS 3
 
+/*
+ * subordinate_dup: create a duplicate range
+ *
+ * @ent: a pointer to a subordinate_range struct
+ *
+ * Returns a pointer to a newly allocated duplicate subordinate_range struct
+ * or NULL on failure
+ */
 static /*@null@*/ /*@only@*/void *subordinate_dup (const void *ent)
 {
        const struct subordinate_range *rangeent = ent;
@@ -40,6 +50,11 @@ static /*@null@*/ /*@only@*/void *subordinate_dup (const void *ent)
        return range;
 }
 
+/*
+ * subordinate_free: free a subordinate_range struct
+ *
+ * @ent: pointer to a subordinate_range struct to free.
+ */
 static void subordinate_free (/*@out@*/ /*@only@*/void *ent)
 {
        struct subordinate_range *rangeent = ent;
@@ -48,6 +63,15 @@ static void subordinate_free (/*@out@*/ /*@only@*/void *ent)
        free (rangeent);
 }
 
+/*
+ * subordinate_parse:
+ *
+ * @line: a line to parse
+ *
+ * Returns a pointer to a subordinate_range struct representing the values
+ * in @line, or NULL on failure.  Note that the returned value should not
+ * be freed by the caller.
+ */
 static void *subordinate_parse (const char *line)
 {
        static struct subordinate_range range;
@@ -98,6 +122,14 @@ static void *subordinate_parse (const char *line)
        return &range;
 }
 
+/*
+ * subordinate_put: print a subordinate_range value to a file
+ *
+ * @ent: a pointer to a subordinate_range struct to print out.
+ * @file: file to which to print.
+ *
+ * Returns 0 on success, -1 on error.
+ */
 static int subordinate_put (const void *ent, FILE * file)
 {
        const struct subordinate_range *range = ent;
@@ -120,28 +152,14 @@ static struct commonio_ops subordinate_ops = {
        NULL,                   /* close_hook */
 };
 
-static /*@observer@*/ /*@null*/const struct subordinate_range *subordinate_next(struct commonio_db *db)
-{
-       commonio_next (db);
-}
-
-static bool is_range_free(struct commonio_db *db, unsigned long start,
-                         unsigned long count)
-{
-       const struct subordinate_range *range;
-       unsigned long end = start + count - 1;
-
-       commonio_rewind(db);
-       while ((range = commonio_next(db)) != NULL) {
-               unsigned long first = range->start;
-               unsigned long last = first + range->count - 1;
-
-               if ((end >= first) && (start <= last))
-                       return false;
-       }
-       return true;
-}
-
+/*
+ * range_exists: Check whether @owner owns any ranges
+ *
+ * @db: database to query
+ * @owner: owner being queried
+ *
+ * Returns true if @owner owns any subuid ranges, false otherwise.
+ */
 static const bool range_exists(struct commonio_db *db, const char *owner)
 {
        const struct subordinate_range *range;
@@ -153,10 +171,30 @@ static const bool range_exists(struct commonio_db *db, const char *owner)
        return false;
 }
 
+/*
+ * find_range: find a range which @owner is authorized to use which includes
+ *             subuid @val.
+ *
+ * @db: database to query
+ * @owner: owning uid being queried
+ * @val: subuid being searched for.
+ *
+ * Returns a range of subuids belonging to @owner and including the subuid
+ * @val, or NULL if no such range exists.
+ */
 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;
@@ -168,9 +206,89 @@ 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 match 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;
+
+                /* For performance reasons check range before using getpwnam() */
+                if ((val < first) || (val > last)) {
+                        continue;
+                }
+
+                /*
+                 * Range matches. Check if range owner is specified
+                 * as numeric UID and if it matches.
+                 */
+                if (0 == strcmp(range->owner, owner_uid_string)) {
+                        return range;
+                }
+
+                /*
+                 * 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) {
+                        return range;
+                }
+        }
+
        return NULL;
 }
 
+/*
+ * have_range: check whether @owner is authorized to use the range
+ *             (@start .. @start+@count-1).
+ * @db: database to check
+ * @owner: owning uid being queried
+ * @start: start of range
+ * @count: number of uids in range
+ *
+ * Returns true if @owner is authorized to use the range, false otherwise.
+ */
 static bool have_range(struct commonio_db *db,
                       const char *owner, unsigned long start, unsigned long count)
 {
@@ -196,6 +314,17 @@ static bool have_range(struct commonio_db *db,
        return false;
 }
 
+/*
+ * subordinate_range_cmp: compare uid ranges
+ *
+ * @p1: pointer to a commonio_entry struct to compare
+ * @p2: pointer to second commonio_entry struct to compare
+ *
+ * Returns 0 if the entries are the same.  Otherwise return -1
+ * if the range in p1 is lower than that in p2, or (if the ranges are
+ * equal) if the owning uid in p1 is lower than p2's.  Return 1 if p1's
+ * range or owning uid is great than p2's.
+ */
 static int subordinate_range_cmp (const void *p1, const void *p2)
 {
        struct subordinate_range *range1, *range2;
@@ -220,6 +349,16 @@ static int subordinate_range_cmp (const void *p1, const void *p2)
                return strcmp(range1->owner, range2->owner);
 }
 
+/*
+ * find_free_range: find an unused consecutive sequence of ids to allocate
+ *                  to a user.
+ * @db: database to search
+ * @min: the first uid in the range to find
+ * @max: the highest uid to find
+ * @count: the number of uids needed
+ *
+ * Return the lowest new uid, or ULONG_MAX on failure.
+ */
 static unsigned long find_free_range(struct commonio_db *db,
                                     unsigned long min, unsigned long max,
                                     unsigned long count)
@@ -228,7 +367,7 @@ static unsigned long find_free_range(struct commonio_db *db,
        unsigned long low, high;
 
        /* When given invalid parameters fail */
-       if ((count == 0) || (max <= min))
+       if ((count == 0) || (max < min))
                goto fail;
 
        /* Sort by range then by owner */
@@ -242,11 +381,14 @@ static unsigned long find_free_range(struct commonio_db *db,
 
                /* Find the top end of the hole before this range */
                high = first;
-               if (high > max)
-                       high = max;
+
+               /* Don't allocate IDs after max (included) */
+               if (high > max + 1) {
+                       high = max + 1;
+               }
 
                /* Is the hole before this range large enough? */
-               if ((high > low) && (((high - low) + 1) >= count))
+               if ((high > low) && ((high - low) >= count))
                        return low;
 
                /* Compute the low end of the next hole */
@@ -263,6 +405,17 @@ fail:
        return ULONG_MAX;
 }
 
+/*
+ * add_range: add a subuid range to an owning uid's list of authorized
+ *            subuids.
+ * @db: database to which to add
+ * @owner: uid which owns the subuid
+ * @start: the first uid in the owned range
+ * @count: the number of uids in the range
+ *
+ * Return 1 if the range is already present or on success.  On error
+ * return 0 and set errno appropriately.
+ */
 static int add_range(struct commonio_db *db,
        const char *owner, unsigned long start, unsigned long count)
 {
@@ -279,69 +432,97 @@ static int add_range(struct commonio_db *db,
        return commonio_append(db, &range);
 }
 
-static int remove_range(struct commonio_db *db,
-       const char *owner, unsigned long start, unsigned long count)
+/*
+ * remove_range:  remove a range of subuids from an owning uid's list
+ *                of authorized subuids.
+ * @db: database to work on
+ * @owner: owning uid whose range is being removed
+ * @start: start of the range to be removed
+ * @count: number of uids in the range.
+ *
+ * Returns 0 on failure, 1 on success.  Failure means that we needed to
+ * create a new range to represent the new limits, and failed doing so.
+ */
+static int remove_range (struct commonio_db *db,
+                         const char *owner,
+                         unsigned long start, unsigned long count)
 {
        struct commonio_entry *ent;
        unsigned long end;
 
-       if (count == 0)
+       if (count == 0) {
                return 1;
+       }
 
        end = start + count - 1;
-       for (ent = db->head; ent; ent = ent->next) {
+       for (ent = db->head; NULL != ent; ent = ent->next) {
                struct subordinate_range *range = ent->eptr;
                unsigned long first;
                unsigned long last;
 
                /* Skip unparsed entries */
-               if (!range)
+               if (NULL == range) {
                        continue;
+               }
 
                first = range->start;
                last = first + range->count - 1;
 
                /* Skip entries with a different owner */
-               if (0 != strcmp(range->owner, owner))
+               if (0 != strcmp (range->owner, owner)) {
                        continue;
+               }
 
                /* Skip entries outside of the range to remove */
-               if ((end < first) || (start > last))
+               if ((end < first) || (start > last)) {
                        continue;
-
-               /* Is entry completely contained in the range to remove? */
-               if ((start <= first) && (end >= last)) {
-                       commonio_del_entry (db, ent);
-               } 
-               /* Is just the start of the entry removed? */
-               else if ((start <= first) && (end < last)) {
-                       range->start = end + 1;
-                       range->count = (last - range->start) + 1;
-
-                       ent->changed = true;
-                       db->changed = true;
                }
-               /* Is just the end of the entry removed? */
-               else if ((start > first) && (end >= last)) {
-                       range->count = (start - range->start) + 1;
-
-                       ent->changed = true;
-                       db->changed = true;
-               }
-               /* The middle of the range is removed */
-               else {
-                       struct subordinate_range tail;
-                       tail.owner = range->owner;
-                       tail.start = end + 1;
-                       tail.count = (last - tail.start) + 1;
-
-                       if (!commonio_append(db, &tail))
-                               return 0;
-
-                       range->count = (start - range->start) + 1;
 
-                       ent->changed = true;
-                       db->changed = true;
+               if (start <= first) {
+                       if (end >= last) {
+                               /* to be removed: [start,      end]
+                                * range:           [first, last] */
+                               /* entry completely contained in the
+                                * range to remove */
+                               commonio_del_entry (db, ent);
+                       } else {
+                               /* to be removed: [start,  end]
+                                * range:           [first, last] */
+                               /* Remove only the start of the entry */
+                               range->start = end + 1;
+                               range->count = (last - range->start) + 1;
+
+                               ent->changed = true;
+                               db->changed = true;
+                       }
+               } else {
+                       if (end >= last) {
+                               /* to be removed:   [start,  end]
+                                * range:         [first, last] */
+                               /* Remove only the end of the entry */
+                               range->count = start - range->start;
+
+                               ent->changed = true;
+                               db->changed = true;
+                       } else {
+                               /* to be removed:   [start, end]
+                                * range:         [first,    last] */
+                               /* Remove the middle of the range
+                                * This requires to create a new range */
+                               struct subordinate_range tail;
+                               tail.owner = range->owner;
+                               tail.start = end + 1;
+                               tail.count = (last - tail.start) + 1;
+
+                               if (commonio_append (db, &tail) == 0) {
+                                       return 0;
+                               }
+
+                               range->count = start - range->start;
+
+                               ent->changed = true;
+                               db->changed = true;
+                       }
                }
        }
 
@@ -355,13 +536,17 @@ static struct commonio_db subordinate_uid_db = {
 #ifdef WITH_SELINUX
        NULL,                   /* scontext */
 #endif
+       0644,                   /* st_mode */
+       0,                      /* st_uid */
+       0,                      /* st_gid */
        NULL,                   /* head */
        NULL,                   /* tail */
        NULL,                   /* cursor */
        false,                  /* changed */
        false,                  /* isopen */
        false,                  /* locked */
-       false                   /* readonly */
+       false,                  /* readonly */
+       false                   /* setname */
 };
 
 int sub_uid_setdbname (const char *filename)
@@ -389,11 +574,6 @@ int sub_uid_open (int mode)
        return commonio_open (&subordinate_uid_db, mode);
 }
 
-bool is_sub_uid_range_free(uid_t start, unsigned long count)
-{
-       return is_range_free (&subordinate_uid_db, start, count);
-}
-
 bool sub_uid_assigned(const char *owner)
 {
        return range_exists (&subordinate_uid_db, owner);
@@ -438,13 +618,17 @@ static struct commonio_db subordinate_gid_db = {
 #ifdef WITH_SELINUX
        NULL,                   /* scontext */
 #endif
+       0644,                   /* st_mode */
+       0,                      /* st_uid */
+       0,                      /* st_gid */
        NULL,                   /* head */
        NULL,                   /* tail */
        NULL,                   /* cursor */
        false,                  /* changed */
        false,                  /* isopen */
        false,                  /* locked */
-       false                   /* readonly */
+       false,                  /* readonly */
+       false                   /* setname */
 };
 
 int sub_gid_setdbname (const char *filename)
@@ -472,11 +656,6 @@ int sub_gid_open (int mode)
        return commonio_open (&subordinate_gid_db, mode);
 }
 
-bool is_sub_gid_range_free(gid_t start, unsigned long count)
-{
-       return is_range_free (&subordinate_gid_db, start, count);
-}
-
 bool have_sub_gids(const char *owner, gid_t start, unsigned long count)
 {
        return have_range(&subordinate_gid_db, owner, start, count);