Remove building with NOCRYPTO option
[minix.git] / usr.bin / newgrp / grutil.c
blobee81590a2ee83bc962849b29c46643f4c186f4c2
1 /* $NetBSD: grutil.c,v 1.4 2014/06/23 06:57:31 shm Exp $ */
3 /*-
4 * Copyright (c) 2007 The NetBSD Foundation, Inc.
5 * All rights reserved.
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Brian Ginsbach.
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
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.4 2014/06/23 06:57:31 shm Exp $");
34 #include <sys/param.h>
35 #include <err.h>
36 #include <errno.h>
37 #include <grp.h>
38 #include <pwd.h>
39 #include <stdio.h>
40 #include <stdlib.h>
41 #include <string.h>
42 #include <unistd.h>
43 #include <util.h>
45 #ifdef LOGIN_CAP
46 #include <login_cap.h>
47 #endif
49 #include "grutil.h"
51 typedef enum {
52 ADDGRP_NOERROR = 0, /* must be zero */
53 ADDGRP_EMALLOC = 1,
54 ADDGRP_EGETGROUPS = 2,
55 ADDGRP_ESETGROUPS = 3
56 } addgrp_ret_t;
58 static void
59 free_groups(void *groups)
61 int oerrno;
63 oerrno = errno;
64 free(groups);
65 errno = oerrno;
68 static addgrp_ret_t
69 alloc_groups(int *ngroups, gid_t **groups, int *ngroupsmax)
71 *ngroupsmax = (int)sysconf(_SC_NGROUPS_MAX);
72 if (*ngroupsmax < 0)
73 *ngroupsmax = NGROUPS_MAX;
75 *groups = malloc(*ngroupsmax * sizeof(**groups));
76 if (*groups == NULL)
77 return ADDGRP_EMALLOC;
79 *ngroups = getgroups(*ngroupsmax, *groups);
80 if (*ngroups == -1) {
81 free_groups(*groups);
82 return ADDGRP_ESETGROUPS;
84 return ADDGRP_NOERROR;
87 static addgrp_ret_t
88 addgid(gid_t *groups, int ngroups, int ngroupsmax, gid_t gid, int makespace)
90 int i;
92 /* search for gid in supplemental group list */
93 for (i = 0; i < ngroups && groups[i] != gid; i++)
94 continue;
96 /* add the gid to the supplemental group list */
97 if (i == ngroups) {
98 if (ngroups < ngroupsmax)
99 groups[ngroups++] = gid;
100 else { /*
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.
107 if (makespace) {
109 * Find a slot that doesn't contain
110 * the primary group.
112 struct passwd *pwd;
113 gid_t pgid;
114 pwd = getpwuid(getuid());
115 if (pwd == NULL)
116 goto error;
117 pgid = pwd->pw_gid;
118 for (i = ngroupsmax - 1; i >= 0; i--)
119 if (groups[i] != pgid)
120 break;
121 if (i < 0)
122 goto error;
123 groups[i] = gid;
125 else {
126 error:
127 errno = EINVAL;
128 return ADDGRP_ESETGROUPS;
131 if (setgroups(ngroups, groups) < 0)
132 return ADDGRP_ESETGROUPS;
134 return ADDGRP_NOERROR;
137 static addgrp_ret_t
138 addgrp(gid_t newgid, int makespace)
140 int ngroups, ngroupsmax;
141 addgrp_ret_t rval;
142 gid_t *groups;
143 gid_t oldgid;
145 oldgid = getgid();
146 if (oldgid == newgid) /* nothing to do */
147 return ADDGRP_NOERROR;
149 rval = alloc_groups(&ngroups, &groups, &ngroupsmax);
150 if (rval != ADDGRP_NOERROR)
151 return rval;
154 * BSD based systems normally have the egid in the supplemental
155 * group list.
157 #if (defined(BSD) && BSD >= 199306)
159 * According to POSIX/XPG6:
160 * On system where the egid is normally in the supplemental group list
161 * (or whenever the old egid actually is in the supplemental group
162 * list):
163 * o If the new egid is in the supplemental group list,
164 * just change the egid.
165 * o If the new egid is not in the supplemental group list,
166 * add the new egid to the list if there is room.
169 rval = addgid(groups, ngroups, ngroupsmax, newgid, makespace);
170 #else
172 * According to POSIX/XPG6:
173 * On systems where the egid is not normally in the supplemental group
174 * list (or whenever the old egid is not in the supplemental group
175 * list):
176 * o If the new egid is in the supplemental group list, delete
177 * it from the list.
178 * o If the old egid is not in the supplemental group list,
179 * add the old egid to the list if there is room.
182 int i;
184 /* search for new egid in supplemental group list */
185 for (i = 0; i < ngroups && groups[i] != newgid; i++)
186 continue;
188 /* remove new egid from supplemental group list */
189 if (i != ngroups)
190 for (--ngroups; i < ngroups; i++)
191 groups[i] = groups[i + 1];
193 rval = addgid(groups, ngroups, ngroupsmax, oldgid, makespace);
195 #endif
196 free_groups(groups);
197 return rval;
201 * If newgrp fails, it returns (gid_t)-1 and the errno variable is
202 * set to:
203 * [EINVAL] Unknown group.
204 * [EPERM] Bad password.
206 static gid_t
207 newgrp(const char *gname, struct passwd *pwd, uid_t ruid, const char *prompt)
209 struct group *grp;
210 char **ap;
211 char *p;
212 gid_t *groups;
213 int ngroups, ngroupsmax;
215 if (gname == NULL)
216 return pwd->pw_gid;
218 grp = getgrnam(gname);
220 #ifdef GRUTIL_ACCEPT_GROUP_NUMBERS
221 if (grp == NULL) {
222 gid_t gid;
223 if (*gname != '-') {
224 gid = (gid_t)strtol(gname, &p, 10);
225 if (*p == '\0')
226 grp = getgrgid(gid);
229 #endif
230 if (grp == NULL) {
231 errno = EINVAL;
232 return (gid_t)-1;
235 if (ruid == 0 || pwd->pw_gid == grp->gr_gid)
236 return grp->gr_gid;
238 if (alloc_groups(&ngroups, &groups, &ngroupsmax) == ADDGRP_NOERROR) {
239 int i;
240 for (i = 0; i < ngroups; i++)
241 if (groups[i] == grp->gr_gid) {
242 free_groups(groups);
243 return grp->gr_gid;
245 free_groups(groups);
249 * Check the group membership list in case the groups[] array
250 * was maxed out or the user has been added to it since login.
252 for (ap = grp->gr_mem; *ap != NULL; ap++)
253 if (strcmp(*ap, pwd->pw_name) == 0)
254 return grp->gr_gid;
256 if (*grp->gr_passwd != '\0') {
257 p = getpass(prompt);
258 if (strcmp(grp->gr_passwd, crypt(p, grp->gr_passwd)) == 0) {
259 (void)memset(p, '\0', _PASSWORD_LEN);
260 return grp->gr_gid;
262 (void)memset(p, '\0', _PASSWORD_LEN);
265 errno = EPERM;
266 return (gid_t)-1;
269 #ifdef GRUTIL_SETGROUPS_MAKESPACE
270 # define ADDGRP_MAKESPACE 1
271 #else
272 # define ADDGRP_MAKESPACE 0
273 #endif
275 #ifdef GRUTIL_ALLOW_GROUP_ERRORS
276 # define maybe_exit(e)
277 #else
278 # define maybe_exit(e) exit(e);
279 #endif
281 void
282 addgroup(
283 #ifdef LOGIN_CAP
284 login_cap_t *lc,
285 #endif
286 const char *gname, struct passwd *pwd, uid_t ruid, const char *prompt)
288 pwd->pw_gid = newgrp(gname, pwd, ruid, prompt);
289 if (pwd->pw_gid == (gid_t)-1) {
290 switch (errno) {
291 case EINVAL:
292 warnx("Unknown group `%s'", gname);
293 maybe_exit(EXIT_FAILURE);
294 break;
295 case EPERM: /* password failure */
296 warnx("Sorry");
297 maybe_exit(EXIT_FAILURE);
298 break;
299 default: /* XXX - should never happen */
300 err(EXIT_FAILURE, "unknown error");
301 break;
303 pwd->pw_gid = getgid();
306 switch (addgrp(pwd->pw_gid, ADDGRP_MAKESPACE)) {
307 case ADDGRP_NOERROR:
308 break;
309 case ADDGRP_EMALLOC:
310 err(EXIT_FAILURE, "malloc");
311 break;
312 case ADDGRP_EGETGROUPS:
313 err(EXIT_FAILURE, "getgroups");
314 break;
315 case ADDGRP_ESETGROUPS:
316 switch(errno) {
317 case EINVAL:
318 warnx("setgroups: ngroups > ngroupsmax");
319 maybe_exit(EXIT_FAILURE);
320 break;
321 case EPERM:
322 case EFAULT:
323 default:
324 warn("setgroups");
325 maybe_exit(EXIT_FAILURE);
326 break;
328 break;
331 #ifdef LOGIN_CAP
332 if (setusercontext(lc, pwd, pwd->pw_uid, LOGIN_SETGID) == -1)
333 err(EXIT_FAILURE, "setting user context");
334 #else
335 if (setgid(pwd->pw_gid) == -1)
336 err(EXIT_FAILURE, "setgid");
337 #endif