]> granicus.if.org Git - shadow/blob - libmisc/find_new_uid.c
* NEWS, libmisc/chowntty.c: Fix a race condition that could lead to
[shadow] / libmisc / find_new_uid.c
1 /*
2  * Copyright (c) 1991 - 1994, Julianne Frances Haugh
3  * Copyright (c) 2008       , Nicolas François
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  * 3. The name of the copyright holders or contributors may not be used to
15  *    endorse or promote products derived from this software without
16  *    specific prior written permission.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
21  * PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT
22  * HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29  */
30
31 #include <config.h>
32
33 #include <assert.h>
34 #include <stdio.h>
35
36 #include "prototypes.h"
37 #include "pwio.h"
38 #include "getdef.h"
39
40 /*
41  * find_new_uid - Find a new unused UID.
42  *
43  * If successful, find_new_uid provides an unused user ID in the
44  * [UID_MIN:UID_MAX] range.
45  * This ID should be higher than all the used UID, but if not possible,
46  * the lowest unused ID in the range will be returned.
47  * 
48  * Return 0 on success, -1 if no unused UIDs are available.
49  */
50 int find_new_uid (bool sys_user, uid_t *uid, uid_t const *preferred_uid)
51 {
52         const struct passwd *pwd;
53         uid_t uid_min, uid_max, user_id;
54         char *used_uids;
55
56         assert (uid != NULL);
57
58         if (!sys_user) {
59                 uid_min = getdef_ulong ("UID_MIN", 1000L);
60                 uid_max = getdef_ulong ("UID_MAX", 60000L);
61         } else {
62                 uid_min = getdef_ulong ("SYS_UID_MIN", 1L);
63                 uid_max = getdef_ulong ("UID_MIN", 1000L) - 1;
64                 uid_max = getdef_ulong ("SYS_UID_MAX", (unsigned long) uid_max);
65         }
66         used_uids = alloca (sizeof (char) * uid_max +1);
67         memset (used_uids, 0, sizeof (char) * uid_max + 1);
68
69         if (   (NULL != preferred_uid)
70             && (*preferred_uid >= uid_min)
71             && (*preferred_uid <= uid_max)
72             /* Check if the user exists according to NSS */
73             && (getpwuid (*preferred_uid) == NULL)
74             /* Check also the local database in case of uncommitted
75              * changes */
76             && (pw_locate_uid (*preferred_uid) == NULL)) {
77                 *uid = *preferred_uid;
78                 return 0;
79         }
80
81
82         user_id = uid_min;
83
84         /*
85          * Search the entire password file,
86          * looking for the largest unused value.
87          *
88          * We check the list of users according to NSS (setpwent/getpwent),
89          * but we also check the local database (pw_rewind/pw_next) in case
90          * some users were created but the changes were not committed yet.
91          */
92         setpwent ();
93         pw_rewind ();
94         while (   ((pwd = getpwent ()) != NULL)
95                || ((pwd = pw_next ()) != NULL)) {
96                 if ((pwd->pw_uid >= user_id) && (pwd->pw_uid <= uid_max)) {
97                         user_id = pwd->pw_uid + 1;
98                 }
99                 /* create index of used UIDs */
100                 if (pwd->pw_uid <= uid_max) {
101                         used_uids[pwd->pw_uid] = 1;
102                 }
103         }
104         endpwent ();
105
106         /*
107          * If a user with UID equal to UID_MAX exists, the above algorithm
108          * will give us UID_MAX+1 even if not unique. Search for the first
109          * free UID starting with UID_MIN.
110          */
111         if (user_id == uid_max + 1) {
112                 for (user_id = uid_min; user_id < uid_max; user_id++) {
113                         if (0 == used_uids[user_id]) {
114                                 break;
115                         }
116                 }
117                 if (user_id == uid_max) {
118                         fprintf (stderr, _("%s: Can't get unique UID (no more available UIDs)\n"), Prog);
119                         SYSLOG ((LOG_WARN, "no more available UID on the system"));
120                         return -1;
121                 }
122         }
123
124         *uid = user_id;
125         return 0;
126 }
127