1
From ebiederm@xmission.com Tue Jan 22 09:16:29 2013
2
Return-Path: <ebiederm@xmission.com>
3
X-Original-To: serge@hallyn.com
4
Delivered-To: serge@hallyn.com
5
Received: by mail.hallyn.com (Postfix, from userid 5001)
6
id AF9A9C80F4; Tue, 22 Jan 2013 09:16:29 +0000 (UTC)
7
X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on mail
9
X-Spam-Status: No, score=0.1 required=8.0 tests=BAD_ENC_HEADER,BAYES_00
10
autolearn=no version=3.3.1
11
Received: from out02.mta.xmission.com (out02.mta.xmission.com [166.70.13.232])
12
(using TLSv1 with cipher AES256-SHA (256/256 bits))
13
(No client certificate requested)
14
by mail.hallyn.com (Postfix) with ESMTPS id EDF70C80D1
15
for <serge@hallyn.com>; Tue, 22 Jan 2013 09:16:24 +0000 (UTC)
16
Received: from out01.mta.xmission.com ([166.70.13.231])
17
by out02.mta.xmission.com with esmtps (TLS1.0:RSA_AES_256_CBC_SHA1:32)
19
(envelope-from <ebiederm@xmission.com>)
20
id 1TxZwI-0007HS-Mn; Tue, 22 Jan 2013 02:14:42 -0700
21
Received: from in02.mta.xmission.com ([166.70.13.52])
22
by out01.mta.xmission.com with esmtps (TLS1.0:RSA_AES_256_CBC_SHA1:32)
24
(envelope-from <ebiederm@xmission.com>)
25
id 1TxZwI-0005wP-8E; Tue, 22 Jan 2013 02:14:42 -0700
26
Received: from c-98-207-153-68.hsd1.ca.comcast.net ([98.207.153.68] helo=eric-ThinkPad-X220.xmission.com)
27
by in02.mta.xmission.com with esmtpsa (TLS1.0:DHE_RSA_AES_128_CBC_SHA1:16)
29
(envelope-from <ebiederm@xmission.com>)
30
id 1TxZwE-0004bA-Mv; Tue, 22 Jan 2013 02:14:42 -0700
31
From: ebiederm@xmission.com (Eric W. Biederman)
32
To: Nicolas =?utf-8?Q?Fran=C3=A7ois?= <nicolas.francois@centraliens.net>
33
Cc: <Pkg-shadow-devel@lists.alioth.debian.org>, Linux Containers <containers@lists.linux-foundation.org>, "Michael Kerrisk \(man-pages\)" <mtk.manpages@gmail.com>, "Serge E. Hallyn" <serge@hallyn.com>
34
References: <87d2wxshu0.fsf@xmission.com>
35
Date: Tue, 22 Jan 2013 01:14:35 -0800
36
In-Reply-To: <87d2wxshu0.fsf@xmission.com> (Eric W. Biederman's message of
37
"Tue, 22 Jan 2013 01:11:19 -0800")
38
Message-ID: <87liblr344.fsf@xmission.com>
39
User-Agent: Gnus/5.13 (Gnus v5.13) Emacs/24.1 (gnu/linux)
41
Content-Type: text/plain
42
X-XM-AID: U2FsdGVkX1/3QOlmT6VsAuzQbs/RJ/nb1IrpO++QYVA=
43
X-SA-Exim-Connect-IP: 98.207.153.68
44
X-SA-Exim-Mail-From: ebiederm@xmission.com
45
Subject: [PATCH 04/11] Add backend support for suboridnate uids and gids
46
X-SA-Exim-Version: 4.2.1 (built Wed, 14 Nov 2012 14:26:46 -0700)
47
X-SA-Exim-Scanned: Yes (on in02.mta.xmission.com)
55
These files list the set of subordinate uids and gids that users are allowed
56
to use. The expect use case is with the user namespace but other uses are
59
Signed-off-by: "Eric W. Biederman" <ebiederm@xmission.com>
64
lib/subordinateio.c | 512 +++++++++++++++++++++++++++++++++++++++++++++++++++
65
lib/subordinateio.h | 38 ++++
66
5 files changed, 566 insertions(+), 0 deletions(-)
67
create mode 100644 lib/subordinateio.c
68
create mode 100644 lib/subordinateio.h
70
Index: shadow/etc/login.defs
71
===================================================================
72
--- shadow.orig/etc/login.defs 2013-02-01 15:27:51.684080379 -0600
73
+++ shadow/etc/login.defs 2013-02-01 15:27:51.676080379 -0600
78
+# Extra per user uids
80
+SUB_UID_MAX 600100000
84
# Min/max values for automatic gid selection in groupadd
89
+# Extra per user group ids
91
+SUB_GID_MAX 600100000
95
# Max number of login retries if password is bad
96
Index: shadow/lib/Makefile.am
97
===================================================================
98
--- shadow.orig/lib/Makefile.am 2013-02-01 15:27:51.684080379 -0600
99
+++ shadow/lib/Makefile.am 2013-02-01 15:27:51.676080379 -0600
109
Index: shadow/lib/getdef.c
110
===================================================================
111
--- shadow.orig/lib/getdef.c 2013-02-01 15:27:51.684080379 -0600
112
+++ shadow/lib/getdef.c 2013-02-01 15:27:51.680080379 -0600
114
{"SHA_CRYPT_MAX_ROUNDS", NULL},
115
{"SHA_CRYPT_MIN_ROUNDS", NULL},
117
+ {"SUB_GID_COUNT", NULL},
118
+ {"SUB_GID_MAX", NULL},
119
+ {"SUB_GID_MIN", NULL},
120
+ {"SUB_UID_COUNT", NULL},
121
+ {"SUB_UID_MAX", NULL},
122
+ {"SUB_UID_MIN", NULL},
123
{"SULOG_FILE", NULL},
125
{"SYS_GID_MAX", NULL},
126
Index: shadow/lib/subordinateio.c
127
===================================================================
128
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
129
+++ shadow/lib/subordinateio.c 2013-02-01 15:27:51.680080379 -0600
132
+ * Copyright (c) 2012 - Eric Biederman
136
+#include "prototypes.h"
137
+#include "defines.h"
139
+#include "commonio.h"
140
+#include "subordinateio.h"
142
+struct subordinate_range {
144
+ unsigned long start;
145
+ unsigned long count;
150
+static /*@null@*/ /*@only@*/void *subordinate_dup (const void *ent)
152
+ const struct subordinate_range *rangeent = ent;
153
+ struct subordinate_range *range;
155
+ range = (struct subordinate_range *) malloc (sizeof *range);
156
+ if (NULL == range) {
159
+ range->owner = strdup (rangeent->owner);
160
+ if (NULL == range->owner) {
164
+ range->start = rangeent->start;
165
+ range->count = rangeent->count;
170
+static void subordinate_free (/*@out@*/ /*@only@*/void *ent)
172
+ struct subordinate_range *rangeent = ent;
174
+ free ((void *)(rangeent->owner));
178
+static void *subordinate_parse (const char *line)
180
+ static struct subordinate_range range;
181
+ char rangebuf[1024];
184
+ char *fields[NFIELDS];
187
+ * Copy the string to a temporary buffer so the substrings can
188
+ * be modified to be NULL terminated.
190
+ if (strlen (line) >= sizeof rangebuf)
191
+ return NULL; /* fail if too long */
192
+ strcpy (rangebuf, line);
195
+ * Save a pointer to the start of each colon separated
196
+ * field. The fields are converted into NUL terminated strings.
199
+ for (cp = rangebuf, i = 0; (i < NFIELDS) && (NULL != cp); i++) {
201
+ while (('\0' != *cp) && (':' != *cp)) {
214
+ * There must be exactly NFIELDS colon separated fields or
215
+ * the entry is invalid. Also, fields must be non-blank.
217
+ if (i != NFIELDS || *fields[0] == '\0' || *fields[1] == '\0' || *fields[2] == '\0')
219
+ range.owner = fields[0];
220
+ if (getulong (fields[1], &range.start) == 0)
222
+ if (getulong (fields[2], &range.count) == 0)
228
+static int subordinate_put (const void *ent, FILE * file)
230
+ const struct subordinate_range *range = ent;
232
+ return fprintf(file, "%s:%lu:%lu\n",
235
+ range->count) < 0 ? -1 : 0;
238
+static struct commonio_ops subordinate_ops = {
239
+ subordinate_dup, /* dup */
240
+ subordinate_free, /* free */
241
+ NULL, /* getname */
242
+ subordinate_parse, /* parse */
243
+ subordinate_put, /* put */
246
+ NULL, /* open_hook */
247
+ NULL, /* close_hook */
250
+static /*@observer@*/ /*@null*/const struct subordinate_range *subordinate_next(struct commonio_db *db)
252
+ commonio_next (db);
255
+static bool is_range_free(struct commonio_db *db, unsigned long start,
256
+ unsigned long count)
258
+ const struct subordinate_range *range;
259
+ unsigned long end = start + count - 1;
261
+ commonio_rewind(db);
262
+ while ((range = commonio_next(db)) != NULL) {
263
+ unsigned long first = range->start;
264
+ unsigned long last = first + range->count - 1;
266
+ if ((end >= first) && (start <= last))
272
+static const bool range_exists(struct commonio_db *db, const char *owner)
274
+ const struct subordinate_range *range;
275
+ commonio_rewind(db);
276
+ while ((range = commonio_next(db)) != NULL) {
277
+ unsigned long first = range->start;
278
+ unsigned long last = first + range->count - 1;
280
+ if (0 == strcmp(range->owner, owner))
286
+static const struct subordinate_range *find_range(struct commonio_db *db,
287
+ const char *owner, unsigned long val)
289
+ const struct subordinate_range *range;
290
+ commonio_rewind(db);
291
+ while ((range = commonio_next(db)) != NULL) {
292
+ unsigned long first = range->start;
293
+ unsigned long last = first + range->count - 1;
295
+ if (0 != strcmp(range->owner, owner))
298
+ if ((val >= first) && (val <= last))
304
+static bool have_range(struct commonio_db *db,
305
+ const char *owner, unsigned long start, unsigned long count)
307
+ const struct subordinate_range *range;
313
+ end = start + count - 1;
314
+ range = find_range (db, owner, start);
316
+ unsigned long last;
318
+ last = range->start + range->count - 1;
319
+ if (last >= (start + count - 1))
322
+ count = end - last;
324
+ range = find_range(db, owner, start);
329
+static int subordinate_range_cmp (const void *p1, const void *p2)
331
+ struct subordinate_range *range1, *range2;
333
+ if ((*(struct commonio_entry **) p1)->eptr == NULL)
335
+ if ((*(struct commonio_entry **) p2)->eptr == NULL)
338
+ range1 = ((struct subordinate_range *) (*(struct commonio_entry **) p1)->eptr);
339
+ range2 = ((struct subordinate_range *) (*(struct commonio_entry **) p2)->eptr);
341
+ if (range1->start < range2->start)
343
+ else if (range1->start > range2->start)
345
+ else if (range1->count < range2->count)
347
+ else if (range1->count > range2->count)
350
+ return strcmp(range1->owner, range2->owner);
353
+static unsigned long find_free_range(struct commonio_db *db,
354
+ unsigned long min, unsigned long max,
355
+ unsigned long count)
357
+ const struct subordinate_range *range;
358
+ unsigned long low, high;
360
+ /* When given invalid parameters fail */
361
+ if ((count == 0) || (max <= min))
364
+ /* Sort by range than by owner */
365
+ commonio_sort (db, subordinate_range_cmp);
366
+ commonio_rewind(db);
369
+ while ((range = commonio_next(db)) != NULL) {
370
+ unsigned long first = range->start;
371
+ unsigned long last = first + range->count - 1;
373
+ /* Find the top end of the hole before this range */
378
+ /* Is the hole before this range large enough? */
379
+ if ((high > low) && (((high - low) + 1) >= count))
382
+ /* Compute the low end of the next hole */
383
+ if (low < (last + 1))
389
+ /* Is the remaining unclaimed area large enough? */
390
+ if (((max - low) + 1) >= count)
396
+static int add_range(struct commonio_db *db,
397
+ const char *owner, unsigned long start, unsigned long count)
399
+ struct subordinate_range range;
400
+ range.owner = owner;
401
+ range.start = start;
402
+ range.count = count;
404
+ /* See if the range is already present */
405
+ if (have_range(db, owner, start, count))
408
+ /* Oterwise append the range */
409
+ return commonio_append(db, &range);
412
+static int remove_range(struct commonio_db *db,
413
+ const char *owner, unsigned long start, unsigned long count)
415
+ struct commonio_entry *ent;
421
+ end = start + count - 1;
422
+ for (ent = db->head; ent; ent = ent->next) {
423
+ struct subordinate_range *range = ent->eptr;
424
+ unsigned long first;
425
+ unsigned long last;
427
+ /* Skip unparsed entries */
431
+ first = range->start;
432
+ last = first + range->count - 1;
434
+ /* Skip entries with a different owner */
435
+ if (0 != strcmp(range->owner, owner))
438
+ /* Skip entries outside of the range to remove */
439
+ if ((end < first) || (start > last))
442
+ /* Is entry completely contained in the range to remove? */
443
+ if ((start <= first) && (end >= last)) {
444
+ commonio_del_entry (db, ent);
446
+ /* Is just the start of the entry removed? */
447
+ else if ((start <= first) && (end < last)) {
448
+ range->start = end + 1;
449
+ range->count = (last - range->start) + 1;
451
+ ent->changed = true;
453
+ /* Is just the end of the entry removed? */
454
+ else if ((start > first) && (end >= last)) {
455
+ range->count = (start - range->start) + 1;
457
+ ent->changed = true;
459
+ /* The middle of the range is removed */
461
+ struct subordinate_range tail;
462
+ tail.owner = range->owner;
463
+ tail.start = end + 1;
464
+ tail.count = (last - tail.start) + 1;
466
+ if (!commonio_append(db, &tail))
469
+ range->count = (start - range->start) + 1;
471
+ ent->changed = true;
478
+static struct commonio_db subordinate_uid_db = {
479
+ "/etc/subuid", /* filename */
480
+ &subordinate_ops, /* ops */
483
+ NULL, /* scontext */
488
+ false, /* changed */
489
+ false, /* isopen */
490
+ false, /* locked */
491
+ false /* readonly */
494
+int sub_uid_setdbname (const char *filename)
496
+ return commonio_setname (&subordinate_uid_db, filename);
499
+/*@observer@*/const char *sub_uid_dbname (void)
501
+ return subordinate_uid_db.filename;
504
+bool sub_uid_file_present (void)
506
+ return commonio_present (&subordinate_uid_db);
509
+int sub_uid_lock (void)
511
+ return commonio_lock (&subordinate_uid_db);
514
+int sub_uid_open (int mode)
516
+ return commonio_open (&subordinate_uid_db, mode);
519
+bool is_sub_uid_range_free(uid_t start, unsigned long count)
521
+ return is_range_free (&subordinate_uid_db, start, count);
524
+bool sub_uid_assigned(const char *owner)
526
+ return range_exists (&subordinate_uid_db, owner);
529
+bool have_sub_uids(const char *owner, uid_t start, unsigned long count)
531
+ return have_range (&subordinate_uid_db, owner, start, count);
534
+int sub_uid_add (const char *owner, uid_t start, unsigned long count)
536
+ return add_range (&subordinate_uid_db, owner, start, count);
539
+int sub_uid_remove (const char *owner, uid_t start, unsigned long count)
541
+ return remove_range (&subordinate_uid_db, owner, start, count);
544
+int sub_uid_close (void)
546
+ return commonio_close (&subordinate_uid_db);
549
+int sub_uid_unlock (void)
551
+ return commonio_unlock (&subordinate_uid_db);
554
+uid_t sub_uid_find_free_range(uid_t min, uid_t max, unsigned long count)
556
+ unsigned long start;
557
+ start = find_free_range (&subordinate_uid_db, min, max, count);
558
+ return start == ULONG_MAX ? (uid_t) -1 : start;
561
+static struct commonio_db subordinate_gid_db = {
562
+ "/etc/subgid", /* filename */
563
+ &subordinate_ops, /* ops */
566
+ NULL, /* scontext */
571
+ false, /* changed */
572
+ false, /* isopen */
573
+ false, /* locked */
574
+ false /* readonly */
577
+int sub_gid_setdbname (const char *filename)
579
+ return commonio_setname (&subordinate_gid_db, filename);
582
+/*@observer@*/const char *sub_gid_dbname (void)
584
+ return subordinate_gid_db.filename;
587
+bool sub_gid_file_present (void)
589
+ return commonio_present (&subordinate_gid_db);
592
+int sub_gid_lock (void)
594
+ return commonio_lock (&subordinate_gid_db);
597
+int sub_gid_open (int mode)
599
+ return commonio_open (&subordinate_gid_db, mode);
602
+bool is_sub_gid_range_free(gid_t start, unsigned long count)
604
+ return is_range_free (&subordinate_gid_db, start, count);
607
+bool have_sub_gids(const char *owner, gid_t start, unsigned long count)
609
+ return have_range(&subordinate_gid_db, owner, start, count);
612
+bool sub_gid_assigned(const char *owner)
614
+ return range_exists (&subordinate_gid_db, owner);
617
+int sub_gid_add (const char *owner, gid_t start, unsigned long count)
619
+ return add_range (&subordinate_gid_db, owner, start, count);
622
+int sub_gid_remove (const char *owner, gid_t start, unsigned long count)
624
+ return remove_range (&subordinate_gid_db, owner, start, count);
627
+int sub_gid_close (void)
629
+ return commonio_close (&subordinate_gid_db);
632
+int sub_gid_unlock (void)
634
+ return commonio_unlock (&subordinate_gid_db);
637
+gid_t sub_gid_find_free_range(gid_t min, gid_t max, unsigned long count)
639
+ unsigned long start;
640
+ start = find_free_range (&subordinate_gid_db, min, max, count);
641
+ return start == ULONG_MAX ? (gid_t) -1 : start;
643
Index: shadow/lib/subordinateio.h
644
===================================================================
645
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
646
+++ shadow/lib/subordinateio.h 2013-02-01 15:27:51.680080379 -0600
649
+ * Copyright (c) 2012- Eric W. Biederman
652
+#ifndef _SUBORDINATEIO_H
653
+#define _SUBORDINATEIO_H
655
+#include <sys/types.h>
657
+extern int sub_uid_close(void);
658
+extern bool is_sub_uid_range_free(uid_t start, unsigned long count);
659
+extern bool have_sub_uids(const char *owner, uid_t start, unsigned long count);
660
+extern bool sub_uid_file_present (void);
661
+extern bool sub_uid_assigned(const char *owner);
662
+extern int sub_uid_lock (void);
663
+extern int sub_uid_setdbname (const char *filename);
664
+extern /*@observer@*/const char *sub_uid_dbname (void);
665
+extern int sub_uid_open (int mode);
666
+extern int sub_uid_unlock (void);
667
+extern int sub_uid_add (const char *owner, uid_t start, unsigned long count);
668
+extern int sub_uid_remove (const char *owner, uid_t start, unsigned long count);
669
+extern uid_t sub_uid_find_free_range(uid_t min, uid_t max, unsigned long count);
671
+extern int sub_gid_close(void);
672
+extern bool is_sub_gid_range_free(gid_t start, unsigned long count);
673
+extern bool have_sub_gids(const char *owner, gid_t start, unsigned long count);
674
+extern bool sub_gid_file_present (void);
675
+extern bool sub_gid_assigned(const char *owner);
676
+extern int sub_gid_lock (void);
677
+extern int sub_gid_setdbname (const char *filename);
678
+extern /*@observer@*/const char *sub_gid_dbname (void);
679
+extern int sub_gid_open (int mode);
680
+extern int sub_gid_unlock (void);
681
+extern int sub_gid_add (const char *owner, gid_t start, unsigned long count);
682
+extern int sub_gid_remove (const char *owner, gid_t start, unsigned long count);
683
+extern uid_t sub_gid_find_free_range(gid_t min, gid_t max, unsigned long count);