2 * Copyright (c) 2012 - Eric Biederman
9 #include "prototypes.h"
13 #include "subordinateio.h"
15 struct subordinate_range {
23 static /*@null@*/ /*@only@*/void *subordinate_dup (const void *ent)
25 const struct subordinate_range *rangeent = ent;
26 struct subordinate_range *range;
28 range = (struct subordinate_range *) malloc (sizeof *range);
32 range->owner = strdup (rangeent->owner);
33 if (NULL == range->owner) {
37 range->start = rangeent->start;
38 range->count = rangeent->count;
43 static void subordinate_free (/*@out@*/ /*@only@*/void *ent)
45 struct subordinate_range *rangeent = ent;
47 free ((void *)(rangeent->owner));
51 static void *subordinate_parse (const char *line)
53 static struct subordinate_range range;
54 static char rangebuf[1024];
57 char *fields[NFIELDS];
60 * Copy the string to a temporary buffer so the substrings can
61 * be modified to be NULL terminated.
63 if (strlen (line) >= sizeof rangebuf)
64 return NULL; /* fail if too long */
65 strcpy (rangebuf, line);
68 * Save a pointer to the start of each colon separated
69 * field. The fields are converted into NUL terminated strings.
72 for (cp = rangebuf, i = 0; (i < NFIELDS) && (NULL != cp); i++) {
74 while (('\0' != *cp) && (':' != *cp)) {
87 * There must be exactly NFIELDS colon separated fields or
88 * the entry is invalid. Also, fields must be non-blank.
90 if (i != NFIELDS || *fields[0] == '\0' || *fields[1] == '\0' || *fields[2] == '\0')
92 range.owner = fields[0];
93 if (getulong (fields[1], &range.start) == 0)
95 if (getulong (fields[2], &range.count) == 0)
101 static int subordinate_put (const void *ent, FILE * file)
103 const struct subordinate_range *range = ent;
105 return fprintf(file, "%s:%lu:%lu\n",
108 range->count) < 0 ? -1 : 0;
111 static struct commonio_ops subordinate_ops = {
112 subordinate_dup, /* dup */
113 subordinate_free, /* free */
115 subordinate_parse, /* parse */
116 subordinate_put, /* put */
119 NULL, /* open_hook */
120 NULL, /* close_hook */
123 static /*@observer@*/ /*@null*/const struct subordinate_range *subordinate_next(struct commonio_db *db)
128 static const bool range_exists(struct commonio_db *db, const char *owner)
130 const struct subordinate_range *range;
132 while ((range = commonio_next(db)) != NULL) {
133 if (0 == strcmp(range->owner, owner))
139 static const struct subordinate_range *find_range(struct commonio_db *db,
140 const char *owner, unsigned long val)
142 const struct subordinate_range *range;
144 while ((range = commonio_next(db)) != NULL) {
145 unsigned long first = range->start;
146 unsigned long last = first + range->count - 1;
148 if (0 != strcmp(range->owner, owner))
151 if ((val >= first) && (val <= last))
157 static bool have_range(struct commonio_db *db,
158 const char *owner, unsigned long start, unsigned long count)
160 const struct subordinate_range *range;
166 end = start + count - 1;
167 range = find_range (db, owner, start);
171 last = range->start + range->count - 1;
172 if (last >= (start + count - 1))
177 range = find_range(db, owner, start);
182 static int subordinate_range_cmp (const void *p1, const void *p2)
184 struct subordinate_range *range1, *range2;
186 if ((*(struct commonio_entry **) p1)->eptr == NULL)
188 if ((*(struct commonio_entry **) p2)->eptr == NULL)
191 range1 = ((struct subordinate_range *) (*(struct commonio_entry **) p1)->eptr);
192 range2 = ((struct subordinate_range *) (*(struct commonio_entry **) p2)->eptr);
194 if (range1->start < range2->start)
196 else if (range1->start > range2->start)
198 else if (range1->count < range2->count)
200 else if (range1->count > range2->count)
203 return strcmp(range1->owner, range2->owner);
206 static unsigned long find_free_range(struct commonio_db *db,
207 unsigned long min, unsigned long max,
210 const struct subordinate_range *range;
211 unsigned long low, high;
213 /* When given invalid parameters fail */
214 if ((count == 0) || (max < min))
217 /* Sort by range then by owner */
218 commonio_sort (db, subordinate_range_cmp);
222 while ((range = commonio_next(db)) != NULL) {
223 unsigned long first = range->start;
224 unsigned long last = first + range->count - 1;
226 /* Find the top end of the hole before this range */
229 /* Don't allocate IDs after max (included) */
230 if (high > max + 1) {
234 /* Is the hole before this range large enough? */
235 if ((high > low) && ((high - low) >= count))
238 /* Compute the low end of the next hole */
239 if (low < (last + 1))
245 /* Is the remaining unclaimed area large enough? */
246 if (((max - low) + 1) >= count)
252 static int add_range(struct commonio_db *db,
253 const char *owner, unsigned long start, unsigned long count)
255 struct subordinate_range range;
260 /* See if the range is already present */
261 if (have_range(db, owner, start, count))
264 /* Otherwise append the range */
265 return commonio_append(db, &range);
268 static int remove_range (struct commonio_db *db,
270 unsigned long start, unsigned long count)
272 struct commonio_entry *ent;
279 end = start + count - 1;
280 for (ent = db->head; NULL != ent; ent = ent->next) {
281 struct subordinate_range *range = ent->eptr;
285 /* Skip unparsed entries */
290 first = range->start;
291 last = first + range->count - 1;
293 /* Skip entries with a different owner */
294 if (0 != strcmp (range->owner, owner)) {
298 /* Skip entries outside of the range to remove */
299 if ((end < first) || (start > last)) {
303 if (start <= first) {
305 /* to be removed: [start, end]
306 * range: [first, last] */
307 /* entry completely contained in the
309 commonio_del_entry (db, ent);
311 /* to be removed: [start, end]
312 * range: [first, last] */
313 /* Remove only the start of the entry */
314 range->start = end + 1;
315 range->count = (last - range->start) + 1;
322 /* to be removed: [start, end]
323 * range: [first, last] */
324 /* Remove only the end of the entry */
325 range->count = start - range->start;
330 /* to be removed: [start, end]
331 * range: [first, last] */
332 /* Remove the middle of the range
333 * This requires to create a new range */
334 struct subordinate_range tail;
335 tail.owner = range->owner;
336 tail.start = end + 1;
337 tail.count = (last - tail.start) + 1;
339 if (commonio_append (db, &tail) == 0) {
343 range->count = start - range->start;
354 static struct commonio_db subordinate_uid_db = {
355 "/etc/subuid", /* filename */
356 &subordinate_ops, /* ops */
370 int sub_uid_setdbname (const char *filename)
372 return commonio_setname (&subordinate_uid_db, filename);
375 /*@observer@*/const char *sub_uid_dbname (void)
377 return subordinate_uid_db.filename;
380 bool sub_uid_file_present (void)
382 return commonio_present (&subordinate_uid_db);
385 int sub_uid_lock (void)
387 return commonio_lock (&subordinate_uid_db);
390 int sub_uid_open (int mode)
392 return commonio_open (&subordinate_uid_db, mode);
395 bool sub_uid_assigned(const char *owner)
397 return range_exists (&subordinate_uid_db, owner);
400 bool have_sub_uids(const char *owner, uid_t start, unsigned long count)
402 return have_range (&subordinate_uid_db, owner, start, count);
405 int sub_uid_add (const char *owner, uid_t start, unsigned long count)
407 return add_range (&subordinate_uid_db, owner, start, count);
410 int sub_uid_remove (const char *owner, uid_t start, unsigned long count)
412 return remove_range (&subordinate_uid_db, owner, start, count);
415 int sub_uid_close (void)
417 return commonio_close (&subordinate_uid_db);
420 int sub_uid_unlock (void)
422 return commonio_unlock (&subordinate_uid_db);
425 uid_t sub_uid_find_free_range(uid_t min, uid_t max, unsigned long count)
428 start = find_free_range (&subordinate_uid_db, min, max, count);
429 return start == ULONG_MAX ? (uid_t) -1 : start;
432 static struct commonio_db subordinate_gid_db = {
433 "/etc/subgid", /* filename */
434 &subordinate_ops, /* ops */
448 int sub_gid_setdbname (const char *filename)
450 return commonio_setname (&subordinate_gid_db, filename);
453 /*@observer@*/const char *sub_gid_dbname (void)
455 return subordinate_gid_db.filename;
458 bool sub_gid_file_present (void)
460 return commonio_present (&subordinate_gid_db);
463 int sub_gid_lock (void)
465 return commonio_lock (&subordinate_gid_db);
468 int sub_gid_open (int mode)
470 return commonio_open (&subordinate_gid_db, mode);
473 bool have_sub_gids(const char *owner, gid_t start, unsigned long count)
475 return have_range(&subordinate_gid_db, owner, start, count);
478 bool sub_gid_assigned(const char *owner)
480 return range_exists (&subordinate_gid_db, owner);
483 int sub_gid_add (const char *owner, gid_t start, unsigned long count)
485 return add_range (&subordinate_gid_db, owner, start, count);
488 int sub_gid_remove (const char *owner, gid_t start, unsigned long count)
490 return remove_range (&subordinate_gid_db, owner, start, count);
493 int sub_gid_close (void)
495 return commonio_close (&subordinate_gid_db);
498 int sub_gid_unlock (void)
500 return commonio_unlock (&subordinate_gid_db);
503 gid_t sub_gid_find_free_range(gid_t min, gid_t max, unsigned long count)
506 start = find_free_range (&subordinate_gid_db, min, max, count);
507 return start == ULONG_MAX ? (gid_t) -1 : start;
509 #else /* !ENABLE_SUBIDS */
510 extern int errno; /* warning: ANSI C forbids an empty source file */
511 #endif /* !ENABLE_SUBIDS */