1 /* $NetBSD: grutil.c,v 1.1 2007/10/27 15:36:21 christos Exp $ */
4 * Copyright (c) 2007 The NetBSD Foundation, Inc.
7 * This code is derived from software contributed to The NetBSD Foundation
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
31 #include <sys/cdefs.h>
32 __RCSID("$NetBSD: grutil.c,v 1.1 2007/10/27 15:36:21 christos Exp $");
34 #include <sys/param.h>
46 #include <login_cap.h>
52 ADDGRP_NOERROR
= 0, /* must be zero */
54 ADDGRP_EGETGROUPS
= 2,
59 free_groups(void *groups
)
69 alloc_groups(int *ngroups
, gid_t
**groups
, int *ngroupsmax
)
71 *ngroupsmax
= (int)sysconf(_SC_NGROUPS_MAX
);
73 *ngroupsmax
= NGROUPS_MAX
;
75 *groups
= malloc(*ngroupsmax
* sizeof(**groups
));
77 return ADDGRP_EMALLOC
;
79 *ngroups
= getgroups(*ngroupsmax
, *groups
);
82 return ADDGRP_ESETGROUPS
;
84 return ADDGRP_NOERROR
;
88 addgid(gid_t
*groups
, int ngroups
, int ngroupsmax
, gid_t gid
, int makespace
)
92 /* search for gid in supplemental group list */
93 for (i
= 0; i
< ngroups
&& groups
[i
] != gid
; i
++)
96 /* add the gid to the supplemental group list */
98 if (ngroups
< ngroupsmax
)
99 groups
[ngroups
++] = gid
;
101 * setgroups(2) will fail with errno = EINVAL
102 * if ngroups > nmaxgroups. If makespace is
103 * set, replace the last group with the new
104 * one. Otherwise, fail the way setgroups(2)
105 * would if we passed the larger groups array.
109 * Find a slot that doesn't contain
114 pwd
= getpwuid(getuid());
118 for (i
= ngroupsmax
- 1; i
>= 0; i
--)
119 if (groups
[i
] != pgid
)
128 return ADDGRP_ESETGROUPS
;
131 if (setgroups(ngroups
, groups
) < 0)
132 return ADDGRP_ESETGROUPS
;
134 return ADDGRP_NOERROR
;
138 addgrp(gid_t newgid
, int makespace
)
140 int ngroups
, ngroupsmax
, rval
;
145 if (oldgid
== newgid
) /* nothing to do */
146 return ADDGRP_NOERROR
;
148 rval
= alloc_groups(&ngroups
, &groups
, &ngroupsmax
);
153 * BSD based systems normally have the egid in the supplemental
156 #if (defined(BSD) && BSD >= 199306)
158 * According to POSIX/XPG6:
159 * On system where the egid is normally in the supplemental group list
160 * (or whenever the old egid actually is in the supplemental group
162 * o If the new egid is in the supplemental group list,
163 * just change the egid.
164 * o If the new egid is not in the supplemental group list,
165 * add the new egid to the list if there is room.
168 rval
= addgid(groups
, ngroups
, ngroupsmax
, newgid
, makespace
);
171 * According to POSIX/XPG6:
172 * On systems where the egid is not normally in the supplemental group
173 * list (or whenever the old egid is not in the supplemental group
175 * o If the new egid is in the supplemental group list, delete
177 * o If the old egid is not in the supplemental group list,
178 * add the old egid to the list if there is room.
183 /* search for new egid in supplemental group list */
184 for (i
= 0; i
< ngroups
&& groups
[i
] != newgid
; i
++)
187 /* remove new egid from supplemental group list */
189 for (--ngroups
; i
< ngroups
; i
++)
190 groups
[i
] = groups
[i
+ 1];
192 rval
= addgid(groups
, ngroups
, ngroupsmax
, oldgid
, makespace
);
200 * If newgrp fails, it returns (gid_t)-1 and the errno variable is
202 * [EINVAL] Unknown group.
203 * [EPERM] Bad password.
206 newgrp(const char *gname
, struct passwd
*pwd
, uid_t ruid
, const char *prompt
)
212 int ngroups
, ngroupsmax
;
217 grp
= getgrnam(gname
);
219 #ifdef GRUTIL_ACCEPT_GROUP_NUMBERS
223 gid
= (gid_t
)strtol(gname
, &p
, 10);
234 if (ruid
== 0 || pwd
->pw_gid
== grp
->gr_gid
)
237 if (alloc_groups(&ngroups
, &groups
, &ngroupsmax
) == 0) {
239 for (i
= 0; i
< ngroups
; i
++)
240 if (groups
[i
] == grp
->gr_gid
) {
248 * Check the group membership list in case the groups[] array
249 * was maxed out or the user has been added to it since login.
251 for (ap
= grp
->gr_mem
; *ap
!= NULL
; ap
++)
252 if (strcmp(*ap
, pwd
->pw_name
) == 0)
255 if (*grp
->gr_passwd
!= '\0') {
257 if (strcmp(grp
->gr_passwd
, crypt(p
, grp
->gr_passwd
)) == 0) {
258 (void)memset(p
, '\0', _PASSWORD_LEN
);
261 (void)memset(p
, '\0', _PASSWORD_LEN
);
268 #ifdef GRUTIL_SETGROUPS_MAKESPACE
269 # define ADDGRP_MAKESPACE 1
271 # define ADDGRP_MAKESPACE 0
274 #ifdef GRUTIL_ALLOW_GROUP_ERRORS
275 # define maybe_exit(e)
277 # define maybe_exit(e) exit(e);
285 const char *gname
, struct passwd
*pwd
, uid_t ruid
, const char *prompt
)
287 pwd
->pw_gid
= newgrp(gname
, pwd
, ruid
, prompt
);
288 if (pwd
->pw_gid
== (gid_t
)-1) {
291 warnx("Unknown group `%s'", gname
);
292 maybe_exit(EXIT_FAILURE
);
294 case EPERM
: /* password failure */
296 maybe_exit(EXIT_FAILURE
);
298 default: /* XXX - should never happen */
299 err(EXIT_FAILURE
, "unknown error");
302 pwd
->pw_gid
= getgid();
305 switch (addgrp(pwd
->pw_gid
, ADDGRP_MAKESPACE
)) {
309 err(EXIT_FAILURE
, "malloc");
311 case ADDGRP_EGETGROUPS
:
312 err(EXIT_FAILURE
, "getgroups");
314 case ADDGRP_ESETGROUPS
:
317 warnx("setgroups: ngroups > ngroupsmax");
318 maybe_exit(EXIT_FAILURE
);
324 maybe_exit(EXIT_FAILURE
);
331 if (setusercontext(lc
, pwd
, pwd
->pw_uid
, LOGIN_SETGID
) == -1)
332 err(EXIT_FAILURE
, "setting user context");
334 if (setgid(pwd
->pw_gid
) == -1)
335 err(EXIT_FAILURE
, "setgid");