]> granicus.if.org Git - shadow/blob - lib/subordinateio.c
Avoid dead branches.
[shadow] / lib / subordinateio.c
1 /*
2  * Copyright (c) 2012 - Eric Biederman
3  */
4
5 #include <config.h>
6
7 #ifdef ENABLE_SUBIDS
8
9 #include "prototypes.h"
10 #include "defines.h"
11 #include <stdio.h>
12 #include "commonio.h"
13 #include "subordinateio.h"
14
15 struct subordinate_range {
16         const char *owner;
17         unsigned long start;
18         unsigned long count;
19 };
20
21 #define NFIELDS 3
22
23 static /*@null@*/ /*@only@*/void *subordinate_dup (const void *ent)
24 {
25         const struct subordinate_range *rangeent = ent;
26         struct subordinate_range *range;
27
28         range = (struct subordinate_range *) malloc (sizeof *range);
29         if (NULL == range) {
30                 return NULL;
31         }
32         range->owner = strdup (rangeent->owner);
33         if (NULL == range->owner) {
34                 free(range);
35                 return NULL;
36         }
37         range->start = rangeent->start;
38         range->count = rangeent->count;
39
40         return range;
41 }
42
43 static void subordinate_free (/*@out@*/ /*@only@*/void *ent)
44 {
45         struct subordinate_range *rangeent = ent;
46         
47         free ((void *)(rangeent->owner));
48         free (rangeent);
49 }
50
51 static void *subordinate_parse (const char *line)
52 {
53         static struct subordinate_range range;
54         static char rangebuf[1024];
55         int i;
56         char *cp;
57         char *fields[NFIELDS];
58
59         /*
60          * Copy the string to a temporary buffer so the substrings can
61          * be modified to be NULL terminated.
62          */
63         if (strlen (line) >= sizeof rangebuf)
64                 return NULL;    /* fail if too long */
65         strcpy (rangebuf, line);
66
67         /*
68          * Save a pointer to the start of each colon separated
69          * field.  The fields are converted into NUL terminated strings.
70          */
71
72         for (cp = rangebuf, i = 0; (i < NFIELDS) && (NULL != cp); i++) {
73                 fields[i] = cp;
74                 while (('\0' != *cp) && (':' != *cp)) {
75                         cp++;
76                 }
77
78                 if ('\0' != *cp) {
79                         *cp = '\0';
80                         cp++;
81                 } else {
82                         cp = NULL;
83                 }
84         }
85
86         /*
87          * There must be exactly NFIELDS colon separated fields or
88          * the entry is invalid.  Also, fields must be non-blank.
89          */
90         if (i != NFIELDS || *fields[0] == '\0' || *fields[1] == '\0' || *fields[2] == '\0')
91                 return NULL;
92         range.owner = fields[0];
93         if (getulong (fields[1], &range.start) == 0)
94                 return NULL;
95         if (getulong (fields[2], &range.count) == 0)
96                 return NULL;
97
98         return &range;
99 }
100
101 static int subordinate_put (const void *ent, FILE * file)
102 {
103         const struct subordinate_range *range = ent;
104
105         return fprintf(file, "%s:%lu:%lu\n",
106                                range->owner,
107                                range->start,
108                                range->count) < 0 ? -1  : 0;
109 }
110
111 static struct commonio_ops subordinate_ops = {
112         subordinate_dup,        /* dup */
113         subordinate_free,       /* free */
114         NULL,                   /* getname */
115         subordinate_parse,      /* parse */
116         subordinate_put,        /* put */
117         fgets,                  /* fgets */
118         fputs,                  /* fputs */
119         NULL,                   /* open_hook */
120         NULL,                   /* close_hook */
121 };
122
123 static /*@observer@*/ /*@null*/const struct subordinate_range *subordinate_next(struct commonio_db *db)
124 {
125         commonio_next (db);
126 }
127
128 static bool is_range_free(struct commonio_db *db, unsigned long start,
129                           unsigned long count)
130 {
131         const struct subordinate_range *range;
132         unsigned long end = start + count - 1;
133
134         commonio_rewind(db);
135         while ((range = commonio_next(db)) != NULL) {
136                 unsigned long first = range->start;
137                 unsigned long last = first + range->count - 1;
138
139                 if ((end >= first) && (start <= last))
140                         return false;
141         }
142         return true;
143 }
144
145 static const bool range_exists(struct commonio_db *db, const char *owner)
146 {
147         const struct subordinate_range *range;
148         commonio_rewind(db);
149         while ((range = commonio_next(db)) != NULL) {
150                 if (0 == strcmp(range->owner, owner))
151                         return true;
152         }
153         return false;
154 }
155
156 static const struct subordinate_range *find_range(struct commonio_db *db,
157                                                   const char *owner, unsigned long val)
158 {
159         const struct subordinate_range *range;
160         commonio_rewind(db);
161         while ((range = commonio_next(db)) != NULL) {
162                 unsigned long first = range->start;
163                 unsigned long last = first + range->count - 1;
164
165                 if (0 != strcmp(range->owner, owner))
166                         continue;
167
168                 if ((val >= first) && (val <= last))
169                         return range;
170         }
171         return NULL;
172 }
173
174 static bool have_range(struct commonio_db *db,
175                        const char *owner, unsigned long start, unsigned long count)
176 {
177         const struct subordinate_range *range;
178         unsigned long end;
179
180         if (count == 0)
181                 return false;
182
183         end = start + count - 1;
184         range = find_range (db, owner, start);
185         while (range) {
186                 unsigned long last; 
187
188                 last = range->start + range->count - 1;
189                 if (last >= (start + count - 1))
190                         return true;
191
192                 count = end - last;
193                 start = last + 1;
194                 range = find_range(db, owner, start);
195         }
196         return false;
197 }
198
199 static int subordinate_range_cmp (const void *p1, const void *p2)
200 {
201         struct subordinate_range *range1, *range2;
202
203         if ((*(struct commonio_entry **) p1)->eptr == NULL)
204                 return 1;
205         if ((*(struct commonio_entry **) p2)->eptr == NULL)
206                 return -1;
207
208         range1 = ((struct subordinate_range *) (*(struct commonio_entry **) p1)->eptr);
209         range2 = ((struct subordinate_range *) (*(struct commonio_entry **) p2)->eptr);
210
211         if (range1->start < range2->start)
212                 return -1;
213         else if (range1->start > range2->start)
214                 return 1;
215         else if (range1->count < range2->count)
216                 return -1;
217         else if (range1->count > range2->count)
218                 return 1;
219         else
220                 return strcmp(range1->owner, range2->owner);
221 }
222
223 static unsigned long find_free_range(struct commonio_db *db,
224                                      unsigned long min, unsigned long max,
225                                      unsigned long count)
226 {
227         const struct subordinate_range *range;
228         unsigned long low, high;
229
230         /* When given invalid parameters fail */
231         if ((count == 0) || (max < min))
232                 goto fail;
233
234         /* Sort by range then by owner */
235         commonio_sort (db, subordinate_range_cmp);
236         commonio_rewind(db);
237
238         low = min;
239         while ((range = commonio_next(db)) != NULL) {
240                 unsigned long first = range->start;
241                 unsigned long last = first + range->count - 1;
242
243                 /* Find the top end of the hole before this range */
244                 high = first;
245                 if (high > max)
246                         high = max;
247
248                 /* Is the hole before this range large enough? */
249                 if ((high > low) && ((high - low) >= count))
250                         return low;
251
252                 /* Compute the low end of the next hole */
253                 if (low < (last + 1))
254                         low = last + 1;
255                 if (low > max)
256                         goto fail;
257         }
258
259         /* Is the remaining unclaimed area large enough? */
260         if (((max - low) + 1) >= count)
261                 return low;
262 fail:
263         return ULONG_MAX;
264 }
265
266 static int add_range(struct commonio_db *db,
267         const char *owner, unsigned long start, unsigned long count)
268 {
269         struct subordinate_range range;
270         range.owner = owner;
271         range.start = start;
272         range.count = count;
273
274         /* See if the range is already present */
275         if (have_range(db, owner, start, count))
276                 return 1;
277
278         /* Otherwise append the range */
279         return commonio_append(db, &range);
280 }
281
282 static int remove_range(struct commonio_db *db,
283         const char *owner, unsigned long start, unsigned long count)
284 {
285         struct commonio_entry *ent;
286         unsigned long end;
287
288         if (count == 0)
289                 return 1;
290
291         end = start + count - 1;
292         for (ent = db->head; ent; ent = ent->next) {
293                 struct subordinate_range *range = ent->eptr;
294                 unsigned long first;
295                 unsigned long last;
296
297                 /* Skip unparsed entries */
298                 if (!range)
299                         continue;
300
301                 first = range->start;
302                 last = first + range->count - 1;
303
304                 /* Skip entries with a different owner */
305                 if (0 != strcmp(range->owner, owner))
306                         continue;
307
308                 /* Skip entries outside of the range to remove */
309                 if ((end < first) || (start > last))
310                         continue;
311
312                 if (start <= first) {
313                         if (end >= last) {
314                                 /* entry completely contained in the
315                                  * range to remove */
316                                 commonio_del_entry (db, ent);
317                         } else {
318                                 /* Remove only the start of the entry */
319                                 range->start = end + 1;
320                                 range->count = (last - range->start) + 1;
321
322                                 ent->changed = true;
323                                 db->changed = true;
324                         }
325                 } else {
326                         if (end >= last) {
327                                 /* Remove only the end of the entry */
328                                 range->count = start - range->start;
329
330                                 ent->changed = true;
331                                 db->changed = true;
332                         } else {
333                                 /* Remove the middle of the range
334                                  * This requires to create a new range */
335                                 struct subordinate_range tail;
336                                 tail.owner = range->owner;
337                                 tail.start = end + 1;
338                                 tail.count = (last - tail.start) + 1;
339
340                                 if (commonio_append(db, &tail) == 0) {
341                                         return 0;
342                                 }
343
344                                 range->count = start - range->start;
345
346                                 ent->changed = true;
347                                 db->changed = true;
348                         }
349                 }
350         }
351
352         return 1;
353 }
354
355 static struct commonio_db subordinate_uid_db = {
356         "/etc/subuid",          /* filename */
357         &subordinate_ops,       /* ops */
358         NULL,                   /* fp */
359 #ifdef WITH_SELINUX
360         NULL,                   /* scontext */
361 #endif
362         NULL,                   /* head */
363         NULL,                   /* tail */
364         NULL,                   /* cursor */
365         false,                  /* changed */
366         false,                  /* isopen */
367         false,                  /* locked */
368         false                   /* readonly */
369 };
370
371 int sub_uid_setdbname (const char *filename)
372 {
373         return commonio_setname (&subordinate_uid_db, filename);
374 }
375
376 /*@observer@*/const char *sub_uid_dbname (void)
377 {
378         return subordinate_uid_db.filename;
379 }
380
381 bool sub_uid_file_present (void)
382 {
383         return commonio_present (&subordinate_uid_db);
384 }
385
386 int sub_uid_lock (void)
387 {
388         return commonio_lock (&subordinate_uid_db);
389 }
390
391 int sub_uid_open (int mode)
392 {
393         return commonio_open (&subordinate_uid_db, mode);
394 }
395
396 bool is_sub_uid_range_free(uid_t start, unsigned long count)
397 {
398         return is_range_free (&subordinate_uid_db, start, count);
399 }
400
401 bool sub_uid_assigned(const char *owner)
402 {
403         return range_exists (&subordinate_uid_db, owner);
404 }
405
406 bool have_sub_uids(const char *owner, uid_t start, unsigned long count)
407 {
408         return have_range (&subordinate_uid_db, owner, start, count);
409 }
410
411 int sub_uid_add (const char *owner, uid_t start, unsigned long count)
412 {
413         return add_range (&subordinate_uid_db, owner, start, count);
414 }
415
416 int sub_uid_remove (const char *owner, uid_t start, unsigned long count)
417 {
418         return remove_range (&subordinate_uid_db, owner, start, count);
419 }
420
421 int sub_uid_close (void)
422 {
423         return commonio_close (&subordinate_uid_db);
424 }
425
426 int sub_uid_unlock (void)
427 {
428         return commonio_unlock (&subordinate_uid_db);
429 }
430
431 uid_t sub_uid_find_free_range(uid_t min, uid_t max, unsigned long count)
432 {
433         unsigned long start;
434         start = find_free_range (&subordinate_uid_db, min, max, count);
435         return start == ULONG_MAX ? (uid_t) -1 : start;
436 }
437
438 static struct commonio_db subordinate_gid_db = {
439         "/etc/subgid",          /* filename */
440         &subordinate_ops,       /* ops */
441         NULL,                   /* fp */
442 #ifdef WITH_SELINUX
443         NULL,                   /* scontext */
444 #endif
445         NULL,                   /* head */
446         NULL,                   /* tail */
447         NULL,                   /* cursor */
448         false,                  /* changed */
449         false,                  /* isopen */
450         false,                  /* locked */
451         false                   /* readonly */
452 };
453
454 int sub_gid_setdbname (const char *filename)
455 {
456         return commonio_setname (&subordinate_gid_db, filename);
457 }
458
459 /*@observer@*/const char *sub_gid_dbname (void)
460 {
461         return subordinate_gid_db.filename;
462 }
463
464 bool sub_gid_file_present (void)
465 {
466         return commonio_present (&subordinate_gid_db);
467 }
468
469 int sub_gid_lock (void)
470 {
471         return commonio_lock (&subordinate_gid_db);
472 }
473
474 int sub_gid_open (int mode)
475 {
476         return commonio_open (&subordinate_gid_db, mode);
477 }
478
479 bool is_sub_gid_range_free(gid_t start, unsigned long count)
480 {
481         return is_range_free (&subordinate_gid_db, start, count);
482 }
483
484 bool have_sub_gids(const char *owner, gid_t start, unsigned long count)
485 {
486         return have_range(&subordinate_gid_db, owner, start, count);
487 }
488
489 bool sub_gid_assigned(const char *owner)
490 {
491         return range_exists (&subordinate_gid_db, owner);
492 }
493
494 int sub_gid_add (const char *owner, gid_t start, unsigned long count)
495 {
496         return add_range (&subordinate_gid_db, owner, start, count);
497 }
498
499 int sub_gid_remove (const char *owner, gid_t start, unsigned long count)
500 {
501         return remove_range (&subordinate_gid_db, owner, start, count);
502 }
503
504 int sub_gid_close (void)
505 {
506         return commonio_close (&subordinate_gid_db);
507 }
508
509 int sub_gid_unlock (void)
510 {
511         return commonio_unlock (&subordinate_gid_db);
512 }
513
514 gid_t sub_gid_find_free_range(gid_t min, gid_t max, unsigned long count)
515 {
516         unsigned long start;
517         start = find_free_range (&subordinate_gid_db, min, max, count);
518         return start == ULONG_MAX ? (gid_t) -1 : start;
519 }
520 #else                           /* !ENABLE_SUBIDS */
521 extern int errno;               /* warning: ANSI C forbids an empty source file */
522 #endif                          /* !ENABLE_SUBIDS */
523