1
From ebiederm@xmission.com Tue Jan 22 09:20:27 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 8625BC80F4; Tue, 22 Jan 2013 09:20:27 +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 69CACC80D1
15
for <serge@hallyn.com>; Tue, 22 Jan 2013 09:20:23 +0000 (UTC)
16
Received: from in02.mta.xmission.com ([166.70.13.52])
17
by out02.mta.xmission.com with esmtps (TLS1.0:RSA_AES_256_CBC_SHA1:32)
19
(envelope-from <ebiederm@xmission.com>)
20
id 1Txa08-0000JL-Uo; Tue, 22 Jan 2013 02:18:41 -0700
21
Received: from c-98-207-153-68.hsd1.ca.comcast.net ([98.207.153.68] helo=eric-ThinkPad-X220.xmission.com)
22
by in02.mta.xmission.com with esmtpsa (TLS1.0:DHE_RSA_AES_128_CBC_SHA1:16)
24
(envelope-from <ebiederm@xmission.com>)
25
id 1TxZzw-0004wm-8g; Tue, 22 Jan 2013 02:18:40 -0700
26
From: ebiederm@xmission.com (Eric W. Biederman)
27
To: Nicolas =?utf-8?Q?Fran=C3=A7ois?= <nicolas.francois@centraliens.net>
28
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>
29
References: <87d2wxshu0.fsf@xmission.com>
30
Date: Tue, 22 Jan 2013 01:18:24 -0800
31
In-Reply-To: <87d2wxshu0.fsf@xmission.com> (Eric W. Biederman's message of
32
"Tue, 22 Jan 2013 01:11:19 -0800")
33
Message-ID: <87sj5tpodb.fsf@xmission.com>
34
User-Agent: Gnus/5.13 (Gnus v5.13) Emacs/24.1 (gnu/linux)
36
Content-Type: text/plain
37
X-XM-AID: U2FsdGVkX1/EkNiL4owL54HOscHbdbK8RucFTofOBo8=
38
X-SA-Exim-Connect-IP: 98.207.153.68
39
X-SA-Exim-Mail-From: ebiederm@xmission.com
40
Subject: [PATCH 09/11] usermod: Add support for subordinate uids and gids.
41
X-SA-Exim-Version: 4.2.1 (built Wed, 14 Nov 2012 14:26:46 -0700)
42
X-SA-Exim-Scanned: Yes (on in02.mta.xmission.com)
49
Signed-off-by: "Eric W. Biederman" <ebiederm@xmission.com>
51
man/usermod.8.xml | 80 +++++++++++++++++
52
src/usermod.c | 255 ++++++++++++++++++++++++++++++++++++++++++++++++++++-
53
2 files changed, 332 insertions(+), 3 deletions(-)
55
Index: shadow/man/usermod.8.xml
56
===================================================================
57
--- shadow.orig/man/usermod.8.xml 2013-02-01 15:27:53.240080352 -0600
58
+++ shadow/man/usermod.8.xml 2013-02-01 15:27:53.232080353 -0600
63
+ <option>-v</option>, <option>--add-sub-uids</option>
64
+ <replaceable>FIRST</replaceable>-<replaceable>LAST</replaceable>
68
+ Add a range of subordinate uids to the users account.
71
+ This option may be specified multiple times to add multiple ranges to a users account.
74
+ No checks will be performed with regard to
75
+ <option>SUB_UID_MIN</option>, <option>SUB_UID_MAX</option>, or
76
+ <option>SUB_UID_COUNT</option> from /etc/login.defs.
82
+ <option>-V</option>, <option>--del-sub-uids</option>
83
+ <replaceable>FIRST</replaceable>-<replaceable>LAST</replaceable>
87
+ Remove a range of subordinate uids from the users account.
90
+ This option may be specified multiple times to remove multiple ranges to a users account.
91
+ When both <option>--del-sub-uids</option> and <option>--add-sub-uids</option> are specified
92
+ remove of all subordinate uid ranges happens before any subordinate uid ranges are added.
95
+ No checks will be performed with regard to
96
+ <option>SUB_UID_MIN</option>, <option>SUB_UID_MAX</option>, or
97
+ <option>SUB_UID_COUNT</option> from /etc/login.defs.
103
+ <option>-w</option>, <option>--add-sub-gids</option>
104
+ <replaceable>FIRST</replaceable>-<replaceable>LAST</replaceable>
108
+ Add a range of subordinate gids to the users account.
111
+ This option may be specified multiple times to add multiple ranges to a users account.
114
+ No checks will be performed with regard to
115
+ <option>SUB_GID_MIN</option>, <option>SUB_GID_MAX</option>, or
116
+ <option>SUB_GID_COUNT</option> from /etc/login.defs.
122
+ <option>-W</option>, <option>--del-sub-gids</option>
123
+ <replaceable>FIRST</replaceable>-<replaceable>LAST</replaceable>
127
+ Remove a range of subordinate gids from the users account.
130
+ This option may be specified multiple times to remove multiple ranges to a users account.
131
+ When both <option>--del-sub-gids</option> and <option>--add-sub-gids</option> are specified
132
+ remove of all subordinate gid ranges happens before any subordinate gid ranges are added.
135
+ No checks will be performed with regard to
136
+ <option>SUB_GID_MIN</option>, <option>SUB_GID_MAX</option>, or
137
+ <option>SUB_GID_COUNT</option> from /etc/login.defs.
143
<option>-Z</option>, <option>--selinux-user</option>
144
<replaceable>SEUSER</replaceable>
146
Index: shadow/src/usermod.c
147
===================================================================
148
--- shadow.orig/src/usermod.c 2013-02-01 15:27:53.240080352 -0600
149
+++ shadow/src/usermod.c 2013-02-01 15:27:53.236080353 -0600
151
#include "sgroupio.h"
153
#include "shadowio.h"
154
+#include "subordinateio.h"
156
#include "tcbfuncs.h"
159
/* #define E_NOSPACE 11 insufficient space to move home dir */
160
#define E_HOMEDIR 12 /* unable to complete home dir move */
161
#define E_SE_UPDATE 13 /* can't update SELinux user mapping */
162
+#define E_SUB_UID_UPDATE 16 /* can't update the subordinate uid file */
163
+#define E_SUB_GID_UPDATE 18 /* can't update the subordinate gid file */
164
#define VALID(s) (strcspn (s, ":\n") == strlen (s))
168
Zflg = false, /* new selinux user */
170
uflg = false, /* specify new user ID */
171
- Uflg = false; /* unlock the password */
172
+ Uflg = false, /* unlock the password */
173
+ vflg = false, /* add subordinate uids */
174
+ Vflg = false, /* delete subordinate uids */
175
+ wflg = false, /* add subordinate gids */
176
+ Wflg = false; /* delete subordinate gids */
178
static bool is_shadow_pwd;
180
@@ -141,12 +148,17 @@
181
static bool is_shadow_grp;
184
+static bool is_sub_uid = false;
185
+static bool is_sub_gid = false;
187
static bool pw_locked = false;
188
static bool spw_locked = false;
189
static bool gr_locked = false;
191
static bool sgr_locked = false;
193
+static bool sub_uid_locked = false;
194
+static bool sub_gid_locked = false;
197
/* local function prototypes */
204
+ unsigned long first;
205
+ unsigned long last;
208
+static struct ulong_range getulong_range(const char *str)
210
+ struct ulong_range result = { .first = ULONG_MAX, .last = 0 };
211
+ unsigned long long first, last;
215
+ first = strtoll(str, &pos, 10);
216
+ if (('\0' == *str) || ('-' != *pos ) || (ERANGE == errno) ||
217
+ (first != (unsigned long int)first))
221
+ last = strtoul(pos + 1, &pos, 10);
222
+ if (('\0' != *pos ) || (ERANGE == errno) ||
223
+ (last != (unsigned long int)last))
229
+ result.first = (unsigned long int)first;
230
+ result.last = (unsigned long int)last;
236
+struct ulong_range_list_entry {
237
+ struct ulong_range_list_entry *next;
238
+ struct ulong_range range;
241
+static struct ulong_range_list_entry *add_sub_uids = NULL, *del_sub_uids = NULL;
242
+static struct ulong_range_list_entry *add_sub_gids = NULL, *del_sub_gids = NULL;
244
+static int prepend_range(const char *str, struct ulong_range_list_entry **head)
246
+ struct ulong_range range;
247
+ struct ulong_range_list_entry *entry;
248
+ range = getulong_range(str);
249
+ if (range.first > range.last)
252
+ entry = malloc(sizeof(*entry));
255
+ _("%s: failed to allocate memory: %s\n"),
256
+ Prog, strerror (errno));
259
+ entry->next = *head;
260
+ entry->range = range;
266
* usage - display usage message and exit
269
(void) fputs (_(" -s, --shell SHELL new login shell for the user account\n"), usageout);
270
(void) fputs (_(" -u, --uid UID new UID for the user account\n"), usageout);
271
(void) fputs (_(" -U, --unlock unlock the user account\n"), usageout);
272
+ (void) fputs (_(" -v, --add-subuids FIRST-LAST add range of subordinate uids\n"), usageout);
273
+ (void) fputs (_(" -V, --del-subuids FIRST-LAST remvoe range of subordinate uids\n"), usageout);
274
+ (void) fputs (_(" -w, --add-subgids FIRST-LAST add range of subordinate gids\n"), usageout);
275
+ (void) fputs (_(" -W, --del-subgids FIRST-LAST remvoe range of subordinate gids\n"), usageout);
277
(void) fputs (_(" -Z, --selinux-user SEUSER new SELinux user mapping for the user account\n"), usageout);
278
#endif /* WITH_SELINUX */
283
+ if (sub_uid_locked) {
284
+ if (sub_uid_unlock () == 0) {
285
+ fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, sub_uid_dbname ());
286
+ SYSLOG ((LOG_ERR, "failed to unlock %s", sub_uid_dbname ()));
290
+ if (sub_gid_locked) {
291
+ if (sub_gid_unlock () == 0) {
292
+ fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, sub_gid_dbname ());
293
+ SYSLOG ((LOG_ERR, "failed to unlock %s", sub_gid_dbname ()));
299
audit_logger (AUDIT_USER_CHAUTHTOK, Prog,
301
{"shell", required_argument, NULL, 's'},
302
{"uid", required_argument, NULL, 'u'},
303
{"unlock", no_argument, NULL, 'U'},
304
+ {"add-subuids", required_argument, NULL, 'v'},
305
+ {"del-subuids", required_argument, NULL, 'V'},
306
+ {"add-subgids", required_argument, NULL, 'w'},
307
+ {"del-subgids", required_argument, NULL, 'W'},
309
{"selinux-user", required_argument, NULL, 'Z'},
310
#endif /* WITH_SELINUX */
311
@@ -1018,6 +1115,41 @@
316
+ if (prepend_range (optarg, &add_sub_uids) == 0) {
318
+ _("%s: invalid subordinate uid range '%s'\n"),
325
+ if (prepend_range (optarg, &del_sub_uids) == 0) {
327
+ _("%s: invalid subordinate uid range '%s'\n"),
334
+ if (prepend_range (optarg, &add_sub_gids) == 0) {
336
+ _("%s: invalid subordinate gid range '%s'\n"),
342
+ if (prepend_range (optarg, &del_sub_gids) == 0) {
344
+ _("%s: invalid subordinate gid range '%s'\n"),
352
if (is_selinux_enabled () > 0) {
353
@@ -1170,6 +1302,7 @@
355
if (!(Uflg || uflg || sflg || pflg || mflg || Lflg ||
356
lflg || Gflg || gflg || fflg || eflg || dflg || cflg
357
+ || vflg || Vflg || wflg || Wflg
360
#endif /* WITH_SELINUX */
361
@@ -1200,6 +1333,7 @@
362
Prog, (unsigned long) user_newid);
369
@@ -1248,6 +1382,10 @@
371
fail_exit (E_GRP_UPDATE);
376
+ if (is_shadow_grp) {
377
if (sgr_unlock () == 0) {
379
_("%s: failed to unlock %s\n"),
380
@@ -1296,6 +1434,33 @@
384
+ if (vflg || Vflg) {
385
+ if (!is_sub_uid || (sub_uid_close () == 0)) {
386
+ fprintf (stderr, _("%s: failure while writing changes to %s\n"), Prog, sub_uid_dbname ());
387
+ SYSLOG ((LOG_ERR, "failure while writing changes to %s", sub_uid_dbname ()));
388
+ fail_exit (E_SUB_UID_UPDATE);
390
+ if (!is_sub_uid || (sub_uid_unlock () == 0)) {
391
+ fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, sub_uid_dbname ());
392
+ SYSLOG ((LOG_ERR, "failed to unlock %s", sub_uid_dbname ()));
395
+ sub_uid_locked = false;
397
+ if (wflg || Wflg) {
398
+ if (!is_sub_gid || (sub_gid_close () == 0)) {
399
+ fprintf (stderr, _("%s: failure while writing changes to %s\n"), Prog, sub_gid_dbname ());
400
+ SYSLOG ((LOG_ERR, "failure while writing changes to %s", sub_gid_dbname ()));
401
+ fail_exit (E_SUB_GID_UPDATE);
403
+ if (!is_sub_gid || (sub_gid_unlock () == 0)) {
404
+ fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, sub_gid_dbname ());
405
+ SYSLOG ((LOG_ERR, "failed to unlock %s", sub_gid_dbname ()));
408
+ sub_gid_locked = false;
412
* Close the DBM and/or flat files
414
@@ -1375,6 +1540,36 @@
418
+ if (vflg || Vflg) {
419
+ if (!is_sub_uid || (sub_uid_lock () == 0)) {
421
+ _("%s: cannot lock %s; try again later.\n"),
422
+ Prog, sub_uid_dbname ());
423
+ fail_exit (E_SUB_UID_UPDATE);
425
+ sub_uid_locked = true;
426
+ if (!is_sub_uid || (sub_uid_open (O_RDWR) == 0)) {
428
+ _("%s: cannot open %s\n"),
429
+ Prog, sub_uid_dbname ());
430
+ fail_exit (E_SUB_UID_UPDATE);
433
+ if (wflg || Wflg) {
434
+ if (!is_sub_gid || (sub_gid_lock () == 0)) {
436
+ _("%s: cannot lock %s; try again later.\n"),
437
+ Prog, sub_gid_dbname ());
438
+ fail_exit (E_SUB_GID_UPDATE);
440
+ sub_gid_locked = true;
441
+ if (!is_sub_gid || (sub_gid_open (O_RDWR) == 0)) {
443
+ _("%s: cannot open %s\n"),
444
+ Prog, sub_gid_dbname ());
445
+ fail_exit (E_SUB_GID_UPDATE);
451
@@ -1476,6 +1671,58 @@
452
fail_exit (E_PW_UPDATE);
456
+ struct ulong_range_list_entry *ptr;
457
+ for (ptr = del_sub_uids; ptr != NULL; ptr = ptr->next) {
458
+ unsigned long count = ptr->range.last - ptr->range.first + 1;
459
+ if (sub_uid_remove(user_name, ptr->range.first, count) == 0) {
461
+ _("%s: failed to remove uid range %lu-%lu from '%s'\n"),
462
+ Prog, ptr->range.first, ptr->range.last,
463
+ sub_uid_dbname ());
464
+ fail_exit (E_SUB_UID_UPDATE);
469
+ struct ulong_range_list_entry *ptr;
470
+ for (ptr = add_sub_uids; ptr != NULL; ptr = ptr->next) {
471
+ unsigned long count = ptr->range.last - ptr->range.first + 1;
472
+ if (sub_uid_add(user_name, ptr->range.first, count) == 0) {
474
+ _("%s: failed to add uid range %lu-%lu from '%s'\n"),
475
+ Prog, ptr->range.first, ptr->range.last,
476
+ sub_uid_dbname ());
477
+ fail_exit (E_SUB_UID_UPDATE);
482
+ struct ulong_range_list_entry *ptr;
483
+ for (ptr = del_sub_gids; ptr != NULL; ptr = ptr->next) {
484
+ unsigned long count = ptr->range.last - ptr->range.first + 1;
485
+ if (sub_gid_remove(user_name, ptr->range.first, count) == 0) {
487
+ _("%s: failed to remove gid range %lu-%lu from '%s'\n"),
488
+ Prog, ptr->range.first, ptr->range.last,
489
+ sub_gid_dbname ());
490
+ fail_exit (E_SUB_GID_UPDATE);
495
+ struct ulong_range_list_entry *ptr;
496
+ for (ptr = add_sub_gids; ptr != NULL; ptr = ptr->next) {
497
+ unsigned long count = ptr->range.last - ptr->range.first + 1;
498
+ if (sub_gid_add(user_name, ptr->range.first, count) == 0) {
500
+ _("%s: failed to add gid range %lu-%lu from '%s'\n"),
501
+ Prog, ptr->range.first, ptr->range.last,
502
+ sub_gid_dbname ());
503
+ fail_exit (E_SUB_GID_UPDATE);
510
@@ -1811,6 +2058,8 @@
512
is_shadow_grp = sgr_file_present ();
514
+ is_sub_uid = sub_uid_file_present ();
515
+ is_sub_gid = sub_gid_file_present ();
517
process_flags (argc, argv);
519
@@ -1818,7 +2067,7 @@
520
* The home directory, the username and the user's UID should not
521
* be changed while the user is logged in.
523
- if ( (uflg || lflg || dflg)
524
+ if ( (uflg || lflg || dflg || Vflg || Wflg)
525
&& (user_busy (user_name, user_id) != 0)) {
528
@@ -1871,7 +2120,7 @@
531
if ( cflg || dflg || eflg || fflg || gflg || Lflg || lflg || pflg
532
- || sflg || uflg || Uflg) {
533
+ || sflg || uflg || Uflg || vflg || Vflg || wflg || Wflg) {