]> granicus.if.org Git - shadow/blob - lib/subordinateio.c
Fix boundary conditions.
[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 const bool range_exists(struct commonio_db *db, const char *owner)
129 {
130         const struct subordinate_range *range;
131         commonio_rewind(db);
132         while ((range = commonio_next(db)) != NULL) {
133                 if (0 == strcmp(range->owner, owner))
134                         return true;
135         }
136         return false;
137 }
138
139 static const struct subordinate_range *find_range(struct commonio_db *db,
140                                                   const char *owner, unsigned long val)
141 {
142         const struct subordinate_range *range;
143         commonio_rewind(db);
144         while ((range = commonio_next(db)) != NULL) {
145                 unsigned long first = range->start;
146                 unsigned long last = first + range->count - 1;
147
148                 if (0 != strcmp(range->owner, owner))
149                         continue;
150
151                 if ((val >= first) && (val <= last))
152                         return range;
153         }
154         return NULL;
155 }
156
157 static bool have_range(struct commonio_db *db,
158                        const char *owner, unsigned long start, unsigned long count)
159 {
160         const struct subordinate_range *range;
161         unsigned long end;
162
163         if (count == 0)
164                 return false;
165
166         end = start + count - 1;
167         range = find_range (db, owner, start);
168         while (range) {
169                 unsigned long last; 
170
171                 last = range->start + range->count - 1;
172                 if (last >= (start + count - 1))
173                         return true;
174
175                 count = end - last;
176                 start = last + 1;
177                 range = find_range(db, owner, start);
178         }
179         return false;
180 }
181
182 static int subordinate_range_cmp (const void *p1, const void *p2)
183 {
184         struct subordinate_range *range1, *range2;
185
186         if ((*(struct commonio_entry **) p1)->eptr == NULL)
187                 return 1;
188         if ((*(struct commonio_entry **) p2)->eptr == NULL)
189                 return -1;
190
191         range1 = ((struct subordinate_range *) (*(struct commonio_entry **) p1)->eptr);
192         range2 = ((struct subordinate_range *) (*(struct commonio_entry **) p2)->eptr);
193
194         if (range1->start < range2->start)
195                 return -1;
196         else if (range1->start > range2->start)
197                 return 1;
198         else if (range1->count < range2->count)
199                 return -1;
200         else if (range1->count > range2->count)
201                 return 1;
202         else
203                 return strcmp(range1->owner, range2->owner);
204 }
205
206 static unsigned long find_free_range(struct commonio_db *db,
207                                      unsigned long min, unsigned long max,
208                                      unsigned long count)
209 {
210         const struct subordinate_range *range;
211         unsigned long low, high;
212
213         /* When given invalid parameters fail */
214         if ((count == 0) || (max < min))
215                 goto fail;
216
217         /* Sort by range then by owner */
218         commonio_sort (db, subordinate_range_cmp);
219         commonio_rewind(db);
220
221         low = min;
222         while ((range = commonio_next(db)) != NULL) {
223                 unsigned long first = range->start;
224                 unsigned long last = first + range->count - 1;
225
226                 /* Find the top end of the hole before this range */
227                 high = first;
228
229                 /* Don't allocate IDs after max (included) */
230                 if (high > max + 1) {
231                         high = max + 1;
232                 }
233
234                 /* Is the hole before this range large enough? */
235                 if ((high > low) && ((high - low) >= count))
236                         return low;
237
238                 /* Compute the low end of the next hole */
239                 if (low < (last + 1))
240                         low = last + 1;
241                 if (low > max)
242                         goto fail;
243         }
244
245         /* Is the remaining unclaimed area large enough? */
246         if (((max - low) + 1) >= count)
247                 return low;
248 fail:
249         return ULONG_MAX;
250 }
251
252 static int add_range(struct commonio_db *db,
253         const char *owner, unsigned long start, unsigned long count)
254 {
255         struct subordinate_range range;
256         range.owner = owner;
257         range.start = start;
258         range.count = count;
259
260         /* See if the range is already present */
261         if (have_range(db, owner, start, count))
262                 return 1;
263
264         /* Otherwise append the range */
265         return commonio_append(db, &range);
266 }
267
268 static int remove_range (struct commonio_db *db,
269                          const char *owner,
270                          unsigned long start, unsigned long count)
271 {
272         struct commonio_entry *ent;
273         unsigned long end;
274
275         if (count == 0) {
276                 return 1;
277         }
278
279         end = start + count - 1;
280         for (ent = db->head; NULL != ent; ent = ent->next) {
281                 struct subordinate_range *range = ent->eptr;
282                 unsigned long first;
283                 unsigned long last;
284
285                 /* Skip unparsed entries */
286                 if (NULL == range) {
287                         continue;
288                 }
289
290                 first = range->start;
291                 last = first + range->count - 1;
292
293                 /* Skip entries with a different owner */
294                 if (0 != strcmp (range->owner, owner)) {
295                         continue;
296                 }
297
298                 /* Skip entries outside of the range to remove */
299                 if ((end < first) || (start > last)) {
300                         continue;
301                 }
302
303                 if (start <= first) {
304                         if (end >= last) {
305                                 /* to be removed: [start,      end]
306                                  * range:           [first, last] */
307                                 /* entry completely contained in the
308                                  * range to remove */
309                                 commonio_del_entry (db, ent);
310                         } else {
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;
316
317                                 ent->changed = true;
318                                 db->changed = true;
319                         }
320                 } else {
321                         if (end >= last) {
322                                 /* to be removed:   [start,  end]
323                                  * range:         [first, last] */
324                                 /* Remove only the end of the entry */
325                                 range->count = start - range->start;
326
327                                 ent->changed = true;
328                                 db->changed = true;
329                         } else {
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;
338
339                                 if (commonio_append (db, &tail) == 0) {
340                                         return 0;
341                                 }
342
343                                 range->count = start - range->start;
344
345                                 ent->changed = true;
346                                 db->changed = true;
347                         }
348                 }
349         }
350
351         return 1;
352 }
353
354 static struct commonio_db subordinate_uid_db = {
355         "/etc/subuid",          /* filename */
356         &subordinate_ops,       /* ops */
357         NULL,                   /* fp */
358 #ifdef WITH_SELINUX
359         NULL,                   /* scontext */
360 #endif
361         NULL,                   /* head */
362         NULL,                   /* tail */
363         NULL,                   /* cursor */
364         false,                  /* changed */
365         false,                  /* isopen */
366         false,                  /* locked */
367         false                   /* readonly */
368 };
369
370 int sub_uid_setdbname (const char *filename)
371 {
372         return commonio_setname (&subordinate_uid_db, filename);
373 }
374
375 /*@observer@*/const char *sub_uid_dbname (void)
376 {
377         return subordinate_uid_db.filename;
378 }
379
380 bool sub_uid_file_present (void)
381 {
382         return commonio_present (&subordinate_uid_db);
383 }
384
385 int sub_uid_lock (void)
386 {
387         return commonio_lock (&subordinate_uid_db);
388 }
389
390 int sub_uid_open (int mode)
391 {
392         return commonio_open (&subordinate_uid_db, mode);
393 }
394
395 bool sub_uid_assigned(const char *owner)
396 {
397         return range_exists (&subordinate_uid_db, owner);
398 }
399
400 bool have_sub_uids(const char *owner, uid_t start, unsigned long count)
401 {
402         return have_range (&subordinate_uid_db, owner, start, count);
403 }
404
405 int sub_uid_add (const char *owner, uid_t start, unsigned long count)
406 {
407         return add_range (&subordinate_uid_db, owner, start, count);
408 }
409
410 int sub_uid_remove (const char *owner, uid_t start, unsigned long count)
411 {
412         return remove_range (&subordinate_uid_db, owner, start, count);
413 }
414
415 int sub_uid_close (void)
416 {
417         return commonio_close (&subordinate_uid_db);
418 }
419
420 int sub_uid_unlock (void)
421 {
422         return commonio_unlock (&subordinate_uid_db);
423 }
424
425 uid_t sub_uid_find_free_range(uid_t min, uid_t max, unsigned long count)
426 {
427         unsigned long start;
428         start = find_free_range (&subordinate_uid_db, min, max, count);
429         return start == ULONG_MAX ? (uid_t) -1 : start;
430 }
431
432 static struct commonio_db subordinate_gid_db = {
433         "/etc/subgid",          /* filename */
434         &subordinate_ops,       /* ops */
435         NULL,                   /* fp */
436 #ifdef WITH_SELINUX
437         NULL,                   /* scontext */
438 #endif
439         NULL,                   /* head */
440         NULL,                   /* tail */
441         NULL,                   /* cursor */
442         false,                  /* changed */
443         false,                  /* isopen */
444         false,                  /* locked */
445         false                   /* readonly */
446 };
447
448 int sub_gid_setdbname (const char *filename)
449 {
450         return commonio_setname (&subordinate_gid_db, filename);
451 }
452
453 /*@observer@*/const char *sub_gid_dbname (void)
454 {
455         return subordinate_gid_db.filename;
456 }
457
458 bool sub_gid_file_present (void)
459 {
460         return commonio_present (&subordinate_gid_db);
461 }
462
463 int sub_gid_lock (void)
464 {
465         return commonio_lock (&subordinate_gid_db);
466 }
467
468 int sub_gid_open (int mode)
469 {
470         return commonio_open (&subordinate_gid_db, mode);
471 }
472
473 bool have_sub_gids(const char *owner, gid_t start, unsigned long count)
474 {
475         return have_range(&subordinate_gid_db, owner, start, count);
476 }
477
478 bool sub_gid_assigned(const char *owner)
479 {
480         return range_exists (&subordinate_gid_db, owner);
481 }
482
483 int sub_gid_add (const char *owner, gid_t start, unsigned long count)
484 {
485         return add_range (&subordinate_gid_db, owner, start, count);
486 }
487
488 int sub_gid_remove (const char *owner, gid_t start, unsigned long count)
489 {
490         return remove_range (&subordinate_gid_db, owner, start, count);
491 }
492
493 int sub_gid_close (void)
494 {
495         return commonio_close (&subordinate_gid_db);
496 }
497
498 int sub_gid_unlock (void)
499 {
500         return commonio_unlock (&subordinate_gid_db);
501 }
502
503 gid_t sub_gid_find_free_range(gid_t min, gid_t max, unsigned long count)
504 {
505         unsigned long start;
506         start = find_free_range (&subordinate_gid_db, min, max, count);
507         return start == ULONG_MAX ? (gid_t) -1 : start;
508 }
509 #else                           /* !ENABLE_SUBIDS */
510 extern int errno;               /* warning: ANSI C forbids an empty source file */
511 #endif                          /* !ENABLE_SUBIDS */
512