2 * Copyright (c) 2012 - Eric Biederman
6 #include "prototypes.h"
10 #include "subordinateio.h"
12 struct subordinate_range {
20 static /*@null@*/ /*@only@*/void *subordinate_dup (const void *ent)
22 const struct subordinate_range *rangeent = ent;
23 struct subordinate_range *range;
25 range = (struct subordinate_range *) malloc (sizeof *range);
29 range->owner = strdup (rangeent->owner);
30 if (NULL == range->owner) {
34 range->start = rangeent->start;
35 range->count = rangeent->count;
40 static void subordinate_free (/*@out@*/ /*@only@*/void *ent)
42 struct subordinate_range *rangeent = ent;
44 free ((void *)(rangeent->owner));
48 static void *subordinate_parse (const char *line)
50 static struct subordinate_range range;
51 static char rangebuf[1024];
54 char *fields[NFIELDS];
57 * Copy the string to a temporary buffer so the substrings can
58 * be modified to be NULL terminated.
60 if (strlen (line) >= sizeof rangebuf)
61 return NULL; /* fail if too long */
62 strcpy (rangebuf, line);
65 * Save a pointer to the start of each colon separated
66 * field. The fields are converted into NUL terminated strings.
69 for (cp = rangebuf, i = 0; (i < NFIELDS) && (NULL != cp); i++) {
71 while (('\0' != *cp) && (':' != *cp)) {
84 * There must be exactly NFIELDS colon separated fields or
85 * the entry is invalid. Also, fields must be non-blank.
87 if (i != NFIELDS || *fields[0] == '\0' || *fields[1] == '\0' || *fields[2] == '\0')
89 range.owner = fields[0];
90 if (getulong (fields[1], &range.start) == 0)
92 if (getulong (fields[2], &range.count) == 0)
98 static int subordinate_put (const void *ent, FILE * file)
100 const struct subordinate_range *range = ent;
102 return fprintf(file, "%s:%lu:%lu\n",
105 range->count) < 0 ? -1 : 0;
108 static struct commonio_ops subordinate_ops = {
109 subordinate_dup, /* dup */
110 subordinate_free, /* free */
112 subordinate_parse, /* parse */
113 subordinate_put, /* put */
116 NULL, /* open_hook */
117 NULL, /* close_hook */
120 static /*@observer@*/ /*@null*/const struct subordinate_range *subordinate_next(struct commonio_db *db)
125 static bool is_range_free(struct commonio_db *db, unsigned long start,
128 const struct subordinate_range *range;
129 unsigned long end = start + count - 1;
132 while ((range = commonio_next(db)) != NULL) {
133 unsigned long first = range->start;
134 unsigned long last = first + range->count - 1;
136 if ((end >= first) && (start <= last))
142 static const bool range_exists(struct commonio_db *db, const char *owner)
144 const struct subordinate_range *range;
146 while ((range = commonio_next(db)) != NULL) {
147 unsigned long first = range->start;
148 unsigned long last = first + range->count - 1;
150 if (0 == strcmp(range->owner, owner))
156 static const struct subordinate_range *find_range(struct commonio_db *db,
157 const char *owner, unsigned long val)
159 const struct subordinate_range *range;
161 while ((range = commonio_next(db)) != NULL) {
162 unsigned long first = range->start;
163 unsigned long last = first + range->count - 1;
165 if (0 != strcmp(range->owner, owner))
168 if ((val >= first) && (val <= last))
174 static bool have_range(struct commonio_db *db,
175 const char *owner, unsigned long start, unsigned long count)
177 const struct subordinate_range *range;
183 end = start + count - 1;
184 range = find_range (db, owner, start);
188 last = range->start + range->count - 1;
189 if (last >= (start + count - 1))
194 range = find_range(db, owner, start);
199 static int subordinate_range_cmp (const void *p1, const void *p2)
201 struct subordinate_range *range1, *range2;
203 if ((*(struct commonio_entry **) p1)->eptr == NULL)
205 if ((*(struct commonio_entry **) p2)->eptr == NULL)
208 range1 = ((struct subordinate_range *) (*(struct commonio_entry **) p1)->eptr);
209 range2 = ((struct subordinate_range *) (*(struct commonio_entry **) p2)->eptr);
211 if (range1->start < range2->start)
213 else if (range1->start > range2->start)
215 else if (range1->count < range2->count)
217 else if (range1->count > range2->count)
220 return strcmp(range1->owner, range2->owner);
223 static unsigned long find_free_range(struct commonio_db *db,
224 unsigned long min, unsigned long max,
227 const struct subordinate_range *range;
228 unsigned long low, high;
230 /* When given invalid parameters fail */
231 if ((count == 0) || (max <= min))
234 /* Sort by range then by owner */
235 commonio_sort (db, subordinate_range_cmp);
239 while ((range = commonio_next(db)) != NULL) {
240 unsigned long first = range->start;
241 unsigned long last = first + range->count - 1;
243 /* Find the top end of the hole before this range */
248 /* Is the hole before this range large enough? */
249 if ((high > low) && (((high - low) + 1) >= count))
252 /* Compute the low end of the next hole */
253 if (low < (last + 1))
259 /* Is the remaining unclaimed area large enough? */
260 if (((max - low) + 1) >= count)
266 static int add_range(struct commonio_db *db,
267 const char *owner, unsigned long start, unsigned long count)
269 struct subordinate_range range;
274 /* See if the range is already present */
275 if (have_range(db, owner, start, count))
278 /* Otherwise append the range */
279 return commonio_append(db, &range);
282 static int remove_range(struct commonio_db *db,
283 const char *owner, unsigned long start, unsigned long count)
285 struct commonio_entry *ent;
291 end = start + count - 1;
292 for (ent = db->head; ent; ent = ent->next) {
293 struct subordinate_range *range = ent->eptr;
297 /* Skip unparsed entries */
301 first = range->start;
302 last = first + range->count - 1;
304 /* Skip entries with a different owner */
305 if (0 != strcmp(range->owner, owner))
308 /* Skip entries outside of the range to remove */
309 if ((end < first) || (start > last))
312 /* Is entry completely contained in the range to remove? */
313 if ((start <= first) && (end >= last)) {
314 commonio_del_entry (db, ent);
316 /* Is just the start of the entry removed? */
317 else if ((start <= first) && (end < last)) {
318 range->start = end + 1;
319 range->count = (last - range->start) + 1;
323 /* Is just the end of the entry removed? */
324 else if ((start > first) && (end >= last)) {
325 range->count = (start - range->start) + 1;
329 /* The middle of the range is removed */
331 struct subordinate_range tail;
332 tail.owner = range->owner;
333 tail.start = end + 1;
334 tail.count = (last - tail.start) + 1;
336 if (!commonio_append(db, &tail))
339 range->count = (start - range->start) + 1;
348 static struct commonio_db subordinate_uid_db = {
349 "/etc/subuid", /* filename */
350 &subordinate_ops, /* ops */
364 int sub_uid_setdbname (const char *filename)
366 return commonio_setname (&subordinate_uid_db, filename);
369 /*@observer@*/const char *sub_uid_dbname (void)
371 return subordinate_uid_db.filename;
374 bool sub_uid_file_present (void)
376 return commonio_present (&subordinate_uid_db);
379 int sub_uid_lock (void)
381 return commonio_lock (&subordinate_uid_db);
384 int sub_uid_open (int mode)
386 return commonio_open (&subordinate_uid_db, mode);
389 bool is_sub_uid_range_free(uid_t start, unsigned long count)
391 return is_range_free (&subordinate_uid_db, start, count);
394 bool sub_uid_assigned(const char *owner)
396 return range_exists (&subordinate_uid_db, owner);
399 bool have_sub_uids(const char *owner, uid_t start, unsigned long count)
401 return have_range (&subordinate_uid_db, owner, start, count);
404 int sub_uid_add (const char *owner, uid_t start, unsigned long count)
406 return add_range (&subordinate_uid_db, owner, start, count);
409 int sub_uid_remove (const char *owner, uid_t start, unsigned long count)
411 return remove_range (&subordinate_uid_db, owner, start, count);
414 int sub_uid_close (void)
416 return commonio_close (&subordinate_uid_db);
419 int sub_uid_unlock (void)
421 return commonio_unlock (&subordinate_uid_db);
424 uid_t sub_uid_find_free_range(uid_t min, uid_t max, unsigned long count)
427 start = find_free_range (&subordinate_uid_db, min, max, count);
428 return start == ULONG_MAX ? (uid_t) -1 : start;
431 static struct commonio_db subordinate_gid_db = {
432 "/etc/subgid", /* filename */
433 &subordinate_ops, /* ops */
447 int sub_gid_setdbname (const char *filename)
449 return commonio_setname (&subordinate_gid_db, filename);
452 /*@observer@*/const char *sub_gid_dbname (void)
454 return subordinate_gid_db.filename;
457 bool sub_gid_file_present (void)
459 return commonio_present (&subordinate_gid_db);
462 int sub_gid_lock (void)
464 return commonio_lock (&subordinate_gid_db);
467 int sub_gid_open (int mode)
469 return commonio_open (&subordinate_gid_db, mode);
472 bool is_sub_gid_range_free(gid_t start, unsigned long count)
474 return is_range_free (&subordinate_gid_db, start, count);
477 bool have_sub_gids(const char *owner, gid_t start, unsigned long count)
479 return have_range(&subordinate_gid_db, owner, start, count);
482 bool sub_gid_assigned(const char *owner)
484 return range_exists (&subordinate_gid_db, owner);
487 int sub_gid_add (const char *owner, gid_t start, unsigned long count)
489 return add_range (&subordinate_gid_db, owner, start, count);
492 int sub_gid_remove (const char *owner, gid_t start, unsigned long count)
494 return remove_range (&subordinate_gid_db, owner, start, count);
497 int sub_gid_close (void)
499 return commonio_close (&subordinate_gid_db);
502 int sub_gid_unlock (void)
504 return commonio_unlock (&subordinate_gid_db);
507 gid_t sub_gid_find_free_range(gid_t min, gid_t max, unsigned long count)
510 start = find_free_range (&subordinate_gid_db, min, max, count);
511 return start == ULONG_MAX ? (gid_t) -1 : start;