]> granicus.if.org Git - shadow/blob - lib/subordinateio.c
Fix typos.
[shadow] / lib / subordinateio.c
1 /*
2  * Copyright (c) 2012 - Eric Biederman
3  */
4
5 #include <config.h>
6 #include "prototypes.h"
7 #include "defines.h"
8 #include <stdio.h>
9 #include "commonio.h"
10 #include "subordinateio.h"
11
12 struct subordinate_range {
13         const char *owner;
14         unsigned long start;
15         unsigned long count;
16 };
17
18 #define NFIELDS 3
19
20 static /*@null@*/ /*@only@*/void *subordinate_dup (const void *ent)
21 {
22         const struct subordinate_range *rangeent = ent;
23         struct subordinate_range *range;
24
25         range = (struct subordinate_range *) malloc (sizeof *range);
26         if (NULL == range) {
27                 return NULL;
28         }
29         range->owner = strdup (rangeent->owner);
30         if (NULL == range->owner) {
31                 free(range);
32                 return NULL;
33         }
34         range->start = rangeent->start;
35         range->count = rangeent->count;
36
37         return range;
38 }
39
40 static void subordinate_free (/*@out@*/ /*@only@*/void *ent)
41 {
42         struct subordinate_range *rangeent = ent;
43         
44         free ((void *)(rangeent->owner));
45         free (rangeent);
46 }
47
48 static void *subordinate_parse (const char *line)
49 {
50         static struct subordinate_range range;
51         static char rangebuf[1024];
52         int i;
53         char *cp;
54         char *fields[NFIELDS];
55
56         /*
57          * Copy the string to a temporary buffer so the substrings can
58          * be modified to be NULL terminated.
59          */
60         if (strlen (line) >= sizeof rangebuf)
61                 return NULL;    /* fail if too long */
62         strcpy (rangebuf, line);
63
64         /*
65          * Save a pointer to the start of each colon separated
66          * field.  The fields are converted into NUL terminated strings.
67          */
68
69         for (cp = rangebuf, i = 0; (i < NFIELDS) && (NULL != cp); i++) {
70                 fields[i] = cp;
71                 while (('\0' != *cp) && (':' != *cp)) {
72                         cp++;
73                 }
74
75                 if ('\0' != *cp) {
76                         *cp = '\0';
77                         cp++;
78                 } else {
79                         cp = NULL;
80                 }
81         }
82
83         /*
84          * There must be exactly NFIELDS colon separated fields or
85          * the entry is invalid.  Also, fields must be non-blank.
86          */
87         if (i != NFIELDS || *fields[0] == '\0' || *fields[1] == '\0' || *fields[2] == '\0')
88                 return NULL;
89         range.owner = fields[0];
90         if (getulong (fields[1], &range.start) == 0)
91                 return NULL;
92         if (getulong (fields[2], &range.count) == 0)
93                 return NULL;
94
95         return &range;
96 }
97
98 static int subordinate_put (const void *ent, FILE * file)
99 {
100         const struct subordinate_range *range = ent;
101
102         return fprintf(file, "%s:%lu:%lu\n",
103                                range->owner,
104                                range->start,
105                                range->count) < 0 ? -1  : 0;
106 }
107
108 static struct commonio_ops subordinate_ops = {
109         subordinate_dup,        /* dup */
110         subordinate_free,       /* free */
111         NULL,                   /* getname */
112         subordinate_parse,      /* parse */
113         subordinate_put,        /* put */
114         fgets,                  /* fgets */
115         fputs,                  /* fputs */
116         NULL,                   /* open_hook */
117         NULL,                   /* close_hook */
118 };
119
120 static /*@observer@*/ /*@null*/const struct subordinate_range *subordinate_next(struct commonio_db *db)
121 {
122         commonio_next (db);
123 }
124
125 static bool is_range_free(struct commonio_db *db, unsigned long start,
126                           unsigned long count)
127 {
128         const struct subordinate_range *range;
129         unsigned long end = start + count - 1;
130
131         commonio_rewind(db);
132         while ((range = commonio_next(db)) != NULL) {
133                 unsigned long first = range->start;
134                 unsigned long last = first + range->count - 1;
135
136                 if ((end >= first) && (start <= last))
137                         return false;
138         }
139         return true;
140 }
141
142 static const bool range_exists(struct commonio_db *db, const char *owner)
143 {
144         const struct subordinate_range *range;
145         commonio_rewind(db);
146         while ((range = commonio_next(db)) != NULL) {
147                 unsigned long first = range->start;
148                 unsigned long last = first + range->count - 1;
149
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) + 1) >= 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                 /* Is entry completely contained in the range to remove? */
313                 if ((start <= first) && (end >= last)) {
314                         commonio_del_entry (db, ent);
315                 } 
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;
320
321                         ent->changed = true;
322                 }
323                 /* Is just the end of the entry removed? */
324                 else if ((start > first) && (end >= last)) {
325                         range->count = (start - range->start) + 1;
326
327                         ent->changed = true;
328                 }
329                 /* The middle of the range is removed */
330                 else {
331                         struct subordinate_range tail;
332                         tail.owner = range->owner;
333                         tail.start = end + 1;
334                         tail.count = (last - tail.start) + 1;
335
336                         if (!commonio_append(db, &tail))
337                                 return 0;
338
339                         range->count = (start - range->start) + 1;
340
341                         ent->changed = true;
342                 }
343         }
344
345         return 1;
346 }
347
348 static struct commonio_db subordinate_uid_db = {
349         "/etc/subuid",          /* filename */
350         &subordinate_ops,       /* ops */
351         NULL,                   /* fp */
352 #ifdef WITH_SELINUX
353         NULL,                   /* scontext */
354 #endif
355         NULL,                   /* head */
356         NULL,                   /* tail */
357         NULL,                   /* cursor */
358         false,                  /* changed */
359         false,                  /* isopen */
360         false,                  /* locked */
361         false                   /* readonly */
362 };
363
364 int sub_uid_setdbname (const char *filename)
365 {
366         return commonio_setname (&subordinate_uid_db, filename);
367 }
368
369 /*@observer@*/const char *sub_uid_dbname (void)
370 {
371         return subordinate_uid_db.filename;
372 }
373
374 bool sub_uid_file_present (void)
375 {
376         return commonio_present (&subordinate_uid_db);
377 }
378
379 int sub_uid_lock (void)
380 {
381         return commonio_lock (&subordinate_uid_db);
382 }
383
384 int sub_uid_open (int mode)
385 {
386         return commonio_open (&subordinate_uid_db, mode);
387 }
388
389 bool is_sub_uid_range_free(uid_t start, unsigned long count)
390 {
391         return is_range_free (&subordinate_uid_db, start, count);
392 }
393
394 bool sub_uid_assigned(const char *owner)
395 {
396         return range_exists (&subordinate_uid_db, owner);
397 }
398
399 bool have_sub_uids(const char *owner, uid_t start, unsigned long count)
400 {
401         return have_range (&subordinate_uid_db, owner, start, count);
402 }
403
404 int sub_uid_add (const char *owner, uid_t start, unsigned long count)
405 {
406         return add_range (&subordinate_uid_db, owner, start, count);
407 }
408
409 int sub_uid_remove (const char *owner, uid_t start, unsigned long count)
410 {
411         return remove_range (&subordinate_uid_db, owner, start, count);
412 }
413
414 int sub_uid_close (void)
415 {
416         return commonio_close (&subordinate_uid_db);
417 }
418
419 int sub_uid_unlock (void)
420 {
421         return commonio_unlock (&subordinate_uid_db);
422 }
423
424 uid_t sub_uid_find_free_range(uid_t min, uid_t max, unsigned long count)
425 {
426         unsigned long start;
427         start = find_free_range (&subordinate_uid_db, min, max, count);
428         return start == ULONG_MAX ? (uid_t) -1 : start;
429 }
430
431 static struct commonio_db subordinate_gid_db = {
432         "/etc/subgid",          /* filename */
433         &subordinate_ops,       /* ops */
434         NULL,                   /* fp */
435 #ifdef WITH_SELINUX
436         NULL,                   /* scontext */
437 #endif
438         NULL,                   /* head */
439         NULL,                   /* tail */
440         NULL,                   /* cursor */
441         false,                  /* changed */
442         false,                  /* isopen */
443         false,                  /* locked */
444         false                   /* readonly */
445 };
446
447 int sub_gid_setdbname (const char *filename)
448 {
449         return commonio_setname (&subordinate_gid_db, filename);
450 }
451
452 /*@observer@*/const char *sub_gid_dbname (void)
453 {
454         return subordinate_gid_db.filename;
455 }
456
457 bool sub_gid_file_present (void)
458 {
459         return commonio_present (&subordinate_gid_db);
460 }
461
462 int sub_gid_lock (void)
463 {
464         return commonio_lock (&subordinate_gid_db);
465 }
466
467 int sub_gid_open (int mode)
468 {
469         return commonio_open (&subordinate_gid_db, mode);
470 }
471
472 bool is_sub_gid_range_free(gid_t start, unsigned long count)
473 {
474         return is_range_free (&subordinate_gid_db, start, count);
475 }
476
477 bool have_sub_gids(const char *owner, gid_t start, unsigned long count)
478 {
479         return have_range(&subordinate_gid_db, owner, start, count);
480 }
481
482 bool sub_gid_assigned(const char *owner)
483 {
484         return range_exists (&subordinate_gid_db, owner);
485 }
486
487 int sub_gid_add (const char *owner, gid_t start, unsigned long count)
488 {
489         return add_range (&subordinate_gid_db, owner, start, count);
490 }
491
492 int sub_gid_remove (const char *owner, gid_t start, unsigned long count)
493 {
494         return remove_range (&subordinate_gid_db, owner, start, count);
495 }
496
497 int sub_gid_close (void)
498 {
499         return commonio_close (&subordinate_gid_db);
500 }
501
502 int sub_gid_unlock (void)
503 {
504         return commonio_unlock (&subordinate_gid_db);
505 }
506
507 gid_t sub_gid_find_free_range(gid_t min, gid_t max, unsigned long count)
508 {
509         unsigned long start;
510         start = find_free_range (&subordinate_gid_db, min, max, count);
511         return start == ULONG_MAX ? (gid_t) -1 : start;
512 }