]> granicus.if.org Git - shadow/blob - lib/subordinateio.c
remove unused fn commonio_next
[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 #include <sys/types.h>
15 #include <pwd.h>
16
17 struct subordinate_range {
18         const char *owner;
19         unsigned long start;
20         unsigned long count;
21 };
22
23 #define NFIELDS 3
24
25 /*
26  * subordinate_dup: create a duplicate range
27  *
28  * @ent: a pointer to a subordinate_range struct
29  *
30  * Returns a pointer to a newly allocated duplicate subordinate_range struct
31  * or NULL on failure
32  */
33 static /*@null@*/ /*@only@*/void *subordinate_dup (const void *ent)
34 {
35         const struct subordinate_range *rangeent = ent;
36         struct subordinate_range *range;
37
38         range = (struct subordinate_range *) malloc (sizeof *range);
39         if (NULL == range) {
40                 return NULL;
41         }
42         range->owner = strdup (rangeent->owner);
43         if (NULL == range->owner) {
44                 free(range);
45                 return NULL;
46         }
47         range->start = rangeent->start;
48         range->count = rangeent->count;
49
50         return range;
51 }
52
53 /*
54  * subordinate_free: free a subordinate_range struct
55  *
56  * @ent: pointer to a subordinate_range struct to free.
57  */
58 static void subordinate_free (/*@out@*/ /*@only@*/void *ent)
59 {
60         struct subordinate_range *rangeent = ent;
61         
62         free ((void *)(rangeent->owner));
63         free (rangeent);
64 }
65
66 /*
67  * subordinate_parse:
68  *
69  * @line: a line to parse
70  *
71  * Returns a pointer to a subordinate_range struct representing the values
72  * in @line, or NULL on failure.  Note that the returned value should not
73  * be freed by the caller.
74  */
75 static void *subordinate_parse (const char *line)
76 {
77         static struct subordinate_range range;
78         static char rangebuf[1024];
79         int i;
80         char *cp;
81         char *fields[NFIELDS];
82
83         /*
84          * Copy the string to a temporary buffer so the substrings can
85          * be modified to be NULL terminated.
86          */
87         if (strlen (line) >= sizeof rangebuf)
88                 return NULL;    /* fail if too long */
89         strcpy (rangebuf, line);
90
91         /*
92          * Save a pointer to the start of each colon separated
93          * field.  The fields are converted into NUL terminated strings.
94          */
95
96         for (cp = rangebuf, i = 0; (i < NFIELDS) && (NULL != cp); i++) {
97                 fields[i] = cp;
98                 while (('\0' != *cp) && (':' != *cp)) {
99                         cp++;
100                 }
101
102                 if ('\0' != *cp) {
103                         *cp = '\0';
104                         cp++;
105                 } else {
106                         cp = NULL;
107                 }
108         }
109
110         /*
111          * There must be exactly NFIELDS colon separated fields or
112          * the entry is invalid.  Also, fields must be non-blank.
113          */
114         if (i != NFIELDS || *fields[0] == '\0' || *fields[1] == '\0' || *fields[2] == '\0')
115                 return NULL;
116         range.owner = fields[0];
117         if (getulong (fields[1], &range.start) == 0)
118                 return NULL;
119         if (getulong (fields[2], &range.count) == 0)
120                 return NULL;
121
122         return &range;
123 }
124
125 /*
126  * subordinate_put: print a subordinate_range value to a file
127  *
128  * @ent: a pointer to a subordinate_range struct to print out.
129  * @file: file to which to print.
130  *
131  * Returns 0 on success, -1 on error.
132  */
133 static int subordinate_put (const void *ent, FILE * file)
134 {
135         const struct subordinate_range *range = ent;
136
137         return fprintf(file, "%s:%lu:%lu\n",
138                                range->owner,
139                                range->start,
140                                range->count) < 0 ? -1  : 0;
141 }
142
143 static struct commonio_ops subordinate_ops = {
144         subordinate_dup,        /* dup */
145         subordinate_free,       /* free */
146         NULL,                   /* getname */
147         subordinate_parse,      /* parse */
148         subordinate_put,        /* put */
149         fgets,                  /* fgets */
150         fputs,                  /* fputs */
151         NULL,                   /* open_hook */
152         NULL,                   /* close_hook */
153 };
154
155 /*
156  * range_exists: Check whether @owner owns any ranges
157  *
158  * @db: database to query
159  * @owner: owner being queried
160  *
161  * Returns true if @owner owns any subuid ranges, false otherwise.
162  */
163 static const bool range_exists(struct commonio_db *db, const char *owner)
164 {
165         const struct subordinate_range *range;
166         commonio_rewind(db);
167         while ((range = commonio_next(db)) != NULL) {
168                 if (0 == strcmp(range->owner, owner))
169                         return true;
170         }
171         return false;
172 }
173
174 /*
175  * find_range: find a range which @owner is authorized to use which includes
176  *             subuid @val.
177  *
178  * @db: database to query
179  * @owner: owning uid being queried
180  * @val: subuid being searched for.
181  *
182  * Returns a range of subuids belonging to @owner and including the subuid
183  * @val, or NULL if no such range exists.
184  */
185 static const struct subordinate_range *find_range(struct commonio_db *db,
186                                                   const char *owner, unsigned long val)
187 {
188         const struct subordinate_range *range;
189
190         /*
191          * Search for exact username/group specification
192          *
193          * This is the original method - go fast through the db, doing only
194          * exact username/group string comparison. Therefore we leave it as-is
195          * for the time being, in order to keep it equally fast as it was
196          * before.
197          */
198         commonio_rewind(db);
199         while ((range = commonio_next(db)) != NULL) {
200                 unsigned long first = range->start;
201                 unsigned long last = first + range->count - 1;
202
203                 if (0 != strcmp(range->owner, owner))
204                         continue;
205
206                 if ((val >= first) && (val <= last))
207                         return range;
208         }
209
210
211         /*
212          * We only do special handling for these two files
213          */
214         if ((0 != strcmp(db->filename, "/etc/subuid")) && (0 != strcmp(db->filename, "/etc/subgid")))
215                 return NULL;
216
217         /*
218          * Search loop above did not produce any result. Let's rerun it,
219          * but this time try to match actual UIDs. The first entry that
220          * matches is considered a success.
221          * (It may be specified as literal UID or as another username which
222          * has the same UID as the username we are looking for.)
223          */
224         struct passwd *pwd;
225         uid_t          owner_uid;
226         char           owner_uid_string[33] = "";
227
228
229         /* Get UID of the username we are looking for */
230         pwd = getpwnam(owner);
231         if (NULL == pwd) {
232                 /* Username not defined in /etc/passwd, or error occured during lookup */
233                 return NULL;
234         }
235         owner_uid = pwd->pw_uid;
236         sprintf(owner_uid_string, "%lu", (unsigned long int)owner_uid);
237
238         commonio_rewind(db);
239         while ((range = commonio_next(db)) != NULL) {
240                 unsigned long first = range->start;
241                 unsigned long last = first + range->count - 1;
242
243                 /* For performance reasons check range before using getpwnam() */
244                 if ((val < first) || (val > last)) {
245                         continue;
246                 }
247
248                 /*
249                  * Range matches. Check if range owner is specified
250                  * as numeric UID and if it matches.
251                  */
252                 if (0 == strcmp(range->owner, owner_uid_string)) {
253                         return range;
254                 }
255
256                 /*
257                  * Ok, this range owner is not specified as numeric UID
258                  * we are looking for. It may be specified as another
259                  * UID or as a literal username.
260                  *
261                  * If specified as another UID, the call to getpwnam()
262                  * will return NULL.
263                  *
264                  * If specified as literal username, we will get its
265                  * UID and compare that to UID we are looking for.
266                  */
267                 const struct passwd *range_owner_pwd;
268
269                 range_owner_pwd = getpwnam(range->owner);
270                 if (NULL == range_owner_pwd) {
271                         continue;
272                 }
273
274                 if (owner_uid == range_owner_pwd->pw_uid) {
275                         return range;
276                 }
277         }
278
279         return NULL;
280 }
281
282 /*
283  * have_range: check whether @owner is authorized to use the range
284  *             (@start .. @start+@count-1).
285  * @db: database to check
286  * @owner: owning uid being queried
287  * @start: start of range
288  * @count: number of uids in range
289  *
290  * Returns true if @owner is authorized to use the range, false otherwise.
291  */
292 static bool have_range(struct commonio_db *db,
293                        const char *owner, unsigned long start, unsigned long count)
294 {
295         const struct subordinate_range *range;
296         unsigned long end;
297
298         if (count == 0)
299                 return false;
300
301         end = start + count - 1;
302         range = find_range (db, owner, start);
303         while (range) {
304                 unsigned long last; 
305
306                 last = range->start + range->count - 1;
307                 if (last >= (start + count - 1))
308                         return true;
309
310                 count = end - last;
311                 start = last + 1;
312                 range = find_range(db, owner, start);
313         }
314         return false;
315 }
316
317 /*
318  * subordinate_range_cmp: compare uid ranges
319  *
320  * @p1: pointer to a commonio_entry struct to compare
321  * @p2: pointer to second commonio_entry struct to compare
322  *
323  * Returns 0 if the entries are the same.  Otherwise return -1
324  * if the range in p1 is lower than that in p2, or (if the ranges are
325  * equal) if the owning uid in p1 is lower than p2's.  Return 1 if p1's
326  * range or owning uid is great than p2's.
327  */
328 static int subordinate_range_cmp (const void *p1, const void *p2)
329 {
330         struct subordinate_range *range1, *range2;
331
332         if ((*(struct commonio_entry **) p1)->eptr == NULL)
333                 return 1;
334         if ((*(struct commonio_entry **) p2)->eptr == NULL)
335                 return -1;
336
337         range1 = ((struct subordinate_range *) (*(struct commonio_entry **) p1)->eptr);
338         range2 = ((struct subordinate_range *) (*(struct commonio_entry **) p2)->eptr);
339
340         if (range1->start < range2->start)
341                 return -1;
342         else if (range1->start > range2->start)
343                 return 1;
344         else if (range1->count < range2->count)
345                 return -1;
346         else if (range1->count > range2->count)
347                 return 1;
348         else
349                 return strcmp(range1->owner, range2->owner);
350 }
351
352 /*
353  * find_free_range: find an unused consecutive sequence of ids to allocate
354  *                  to a user.
355  * @db: database to search
356  * @min: the first uid in the range to find
357  * @max: the highest uid to find
358  * @count: the number of uids needed
359  *
360  * Return the lowest new uid, or ULONG_MAX on failure.
361  */
362 static unsigned long find_free_range(struct commonio_db *db,
363                                      unsigned long min, unsigned long max,
364                                      unsigned long count)
365 {
366         const struct subordinate_range *range;
367         unsigned long low, high;
368
369         /* When given invalid parameters fail */
370         if ((count == 0) || (max < min))
371                 goto fail;
372
373         /* Sort by range then by owner */
374         commonio_sort (db, subordinate_range_cmp);
375         commonio_rewind(db);
376
377         low = min;
378         while ((range = commonio_next(db)) != NULL) {
379                 unsigned long first = range->start;
380                 unsigned long last = first + range->count - 1;
381
382                 /* Find the top end of the hole before this range */
383                 high = first;
384
385                 /* Don't allocate IDs after max (included) */
386                 if (high > max + 1) {
387                         high = max + 1;
388                 }
389
390                 /* Is the hole before this range large enough? */
391                 if ((high > low) && ((high - low) >= count))
392                         return low;
393
394                 /* Compute the low end of the next hole */
395                 if (low < (last + 1))
396                         low = last + 1;
397                 if (low > max)
398                         goto fail;
399         }
400
401         /* Is the remaining unclaimed area large enough? */
402         if (((max - low) + 1) >= count)
403                 return low;
404 fail:
405         return ULONG_MAX;
406 }
407
408 /*
409  * add_range: add a subuid range to an owning uid's list of authorized
410  *            subuids.
411  * @db: database to which to add
412  * @owner: uid which owns the subuid
413  * @start: the first uid in the owned range
414  * @count: the number of uids in the range
415  *
416  * Return 1 if the range is already present or on success.  On error
417  * return 0 and set errno appropriately.
418  */
419 static int add_range(struct commonio_db *db,
420         const char *owner, unsigned long start, unsigned long count)
421 {
422         struct subordinate_range range;
423         range.owner = owner;
424         range.start = start;
425         range.count = count;
426
427         /* See if the range is already present */
428         if (have_range(db, owner, start, count))
429                 return 1;
430
431         /* Otherwise append the range */
432         return commonio_append(db, &range);
433 }
434
435 /*
436  * remove_range:  remove a range of subuids from an owning uid's list
437  *                of authorized subuids.
438  * @db: database to work on
439  * @owner: owning uid whose range is being removed
440  * @start: start of the range to be removed
441  * @count: number of uids in the range.
442  *
443  * Returns 0 on failure, 1 on success.  Failure means that we needed to
444  * create a new range to represent the new limits, and failed doing so.
445  */
446 static int remove_range (struct commonio_db *db,
447                          const char *owner,
448                          unsigned long start, unsigned long count)
449 {
450         struct commonio_entry *ent;
451         unsigned long end;
452
453         if (count == 0) {
454                 return 1;
455         }
456
457         end = start + count - 1;
458         for (ent = db->head; NULL != ent; ent = ent->next) {
459                 struct subordinate_range *range = ent->eptr;
460                 unsigned long first;
461                 unsigned long last;
462
463                 /* Skip unparsed entries */
464                 if (NULL == range) {
465                         continue;
466                 }
467
468                 first = range->start;
469                 last = first + range->count - 1;
470
471                 /* Skip entries with a different owner */
472                 if (0 != strcmp (range->owner, owner)) {
473                         continue;
474                 }
475
476                 /* Skip entries outside of the range to remove */
477                 if ((end < first) || (start > last)) {
478                         continue;
479                 }
480
481                 if (start <= first) {
482                         if (end >= last) {
483                                 /* to be removed: [start,      end]
484                                  * range:           [first, last] */
485                                 /* entry completely contained in the
486                                  * range to remove */
487                                 commonio_del_entry (db, ent);
488                         } else {
489                                 /* to be removed: [start,  end]
490                                  * range:           [first, last] */
491                                 /* Remove only the start of the entry */
492                                 range->start = end + 1;
493                                 range->count = (last - range->start) + 1;
494
495                                 ent->changed = true;
496                                 db->changed = true;
497                         }
498                 } else {
499                         if (end >= last) {
500                                 /* to be removed:   [start,  end]
501                                  * range:         [first, last] */
502                                 /* Remove only the end of the entry */
503                                 range->count = start - range->start;
504
505                                 ent->changed = true;
506                                 db->changed = true;
507                         } else {
508                                 /* to be removed:   [start, end]
509                                  * range:         [first,    last] */
510                                 /* Remove the middle of the range
511                                  * This requires to create a new range */
512                                 struct subordinate_range tail;
513                                 tail.owner = range->owner;
514                                 tail.start = end + 1;
515                                 tail.count = (last - tail.start) + 1;
516
517                                 if (commonio_append (db, &tail) == 0) {
518                                         return 0;
519                                 }
520
521                                 range->count = start - range->start;
522
523                                 ent->changed = true;
524                                 db->changed = true;
525                         }
526                 }
527         }
528
529         return 1;
530 }
531
532 static struct commonio_db subordinate_uid_db = {
533         "/etc/subuid",          /* filename */
534         &subordinate_ops,       /* ops */
535         NULL,                   /* fp */
536 #ifdef WITH_SELINUX
537         NULL,                   /* scontext */
538 #endif
539         0644,                   /* st_mode */
540         0,                      /* st_uid */
541         0,                      /* st_gid */
542         NULL,                   /* head */
543         NULL,                   /* tail */
544         NULL,                   /* cursor */
545         false,                  /* changed */
546         false,                  /* isopen */
547         false,                  /* locked */
548         false,                  /* readonly */
549         false                   /* setname */
550 };
551
552 int sub_uid_setdbname (const char *filename)
553 {
554         return commonio_setname (&subordinate_uid_db, filename);
555 }
556
557 /*@observer@*/const char *sub_uid_dbname (void)
558 {
559         return subordinate_uid_db.filename;
560 }
561
562 bool sub_uid_file_present (void)
563 {
564         return commonio_present (&subordinate_uid_db);
565 }
566
567 int sub_uid_lock (void)
568 {
569         return commonio_lock (&subordinate_uid_db);
570 }
571
572 int sub_uid_open (int mode)
573 {
574         return commonio_open (&subordinate_uid_db, mode);
575 }
576
577 bool sub_uid_assigned(const char *owner)
578 {
579         return range_exists (&subordinate_uid_db, owner);
580 }
581
582 bool have_sub_uids(const char *owner, uid_t start, unsigned long count)
583 {
584         return have_range (&subordinate_uid_db, owner, start, count);
585 }
586
587 int sub_uid_add (const char *owner, uid_t start, unsigned long count)
588 {
589         return add_range (&subordinate_uid_db, owner, start, count);
590 }
591
592 int sub_uid_remove (const char *owner, uid_t start, unsigned long count)
593 {
594         return remove_range (&subordinate_uid_db, owner, start, count);
595 }
596
597 int sub_uid_close (void)
598 {
599         return commonio_close (&subordinate_uid_db);
600 }
601
602 int sub_uid_unlock (void)
603 {
604         return commonio_unlock (&subordinate_uid_db);
605 }
606
607 uid_t sub_uid_find_free_range(uid_t min, uid_t max, unsigned long count)
608 {
609         unsigned long start;
610         start = find_free_range (&subordinate_uid_db, min, max, count);
611         return start == ULONG_MAX ? (uid_t) -1 : start;
612 }
613
614 static struct commonio_db subordinate_gid_db = {
615         "/etc/subgid",          /* filename */
616         &subordinate_ops,       /* ops */
617         NULL,                   /* fp */
618 #ifdef WITH_SELINUX
619         NULL,                   /* scontext */
620 #endif
621         0644,                   /* st_mode */
622         0,                      /* st_uid */
623         0,                      /* st_gid */
624         NULL,                   /* head */
625         NULL,                   /* tail */
626         NULL,                   /* cursor */
627         false,                  /* changed */
628         false,                  /* isopen */
629         false,                  /* locked */
630         false,                  /* readonly */
631         false                   /* setname */
632 };
633
634 int sub_gid_setdbname (const char *filename)
635 {
636         return commonio_setname (&subordinate_gid_db, filename);
637 }
638
639 /*@observer@*/const char *sub_gid_dbname (void)
640 {
641         return subordinate_gid_db.filename;
642 }
643
644 bool sub_gid_file_present (void)
645 {
646         return commonio_present (&subordinate_gid_db);
647 }
648
649 int sub_gid_lock (void)
650 {
651         return commonio_lock (&subordinate_gid_db);
652 }
653
654 int sub_gid_open (int mode)
655 {
656         return commonio_open (&subordinate_gid_db, mode);
657 }
658
659 bool have_sub_gids(const char *owner, gid_t start, unsigned long count)
660 {
661         return have_range(&subordinate_gid_db, owner, start, count);
662 }
663
664 bool sub_gid_assigned(const char *owner)
665 {
666         return range_exists (&subordinate_gid_db, owner);
667 }
668
669 int sub_gid_add (const char *owner, gid_t start, unsigned long count)
670 {
671         return add_range (&subordinate_gid_db, owner, start, count);
672 }
673
674 int sub_gid_remove (const char *owner, gid_t start, unsigned long count)
675 {
676         return remove_range (&subordinate_gid_db, owner, start, count);
677 }
678
679 int sub_gid_close (void)
680 {
681         return commonio_close (&subordinate_gid_db);
682 }
683
684 int sub_gid_unlock (void)
685 {
686         return commonio_unlock (&subordinate_gid_db);
687 }
688
689 gid_t sub_gid_find_free_range(gid_t min, gid_t max, unsigned long count)
690 {
691         unsigned long start;
692         start = find_free_range (&subordinate_gid_db, min, max, count);
693         return start == ULONG_MAX ? (gid_t) -1 : start;
694 }
695 #else                           /* !ENABLE_SUBIDS */
696 extern int errno;               /* warning: ANSI C forbids an empty source file */
697 #endif                          /* !ENABLE_SUBIDS */
698