newgrp.c revision 1.1 1 /*-
2 * Copyright (c) 2007 The NetBSD Foundation, Inc.
3 * All rights reserved.
4 *
5 * This code is derived from software contributed to The NetBSD Foundation
6 * by Brian Ginsbach.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 * 3. All advertising materials mentioning features or use of this software
17 * must display the following acknowledgement:
18 * This product includes software developed by the NetBSD
19 * Foundation, Inc. and its contributors.
20 * 4. Neither the name of The NetBSD Foundation nor the names of its
21 * contributors may be used to endorse or promote products derived
22 * from this software without specific prior written permission.
23 *
24 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
25 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
26 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
27 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
28 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
29 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
30 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
31 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
32 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
33 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
34 * POSSIBILITY OF SUCH DAMAGE.
35 */
36
37 #include <sys/cdefs.h>
38
39 #ifndef lint
40 __RCSID("$NetBSD: newgrp.c,v 1.1 2007/06/21 14:09:24 ginsbach Exp $");
41 #endif /* not lint */
42
43 #include <sys/param.h>
44 #include <sys/types.h>
45
46 #include <err.h>
47 #include <grp.h>
48 #include <libgen.h>
49 #include <paths.h>
50 #include <pwd.h>
51 #include <stdio.h>
52 #include <stdlib.h>
53 #include <string.h>
54 #include <unistd.h>
55
56 #ifdef LOGIN_CAP
57 #include <login_cap.h>
58 #endif
59
60 int addgrp(gid_t);
61 gid_t newgrp(const char *, struct passwd *);
62 void usage(void);
63
64 int
65 main(int argc, char *argv[])
66 {
67 extern char **environ;
68 struct passwd *pwd;
69 int c, lflag;
70 char *shell, sbuf[MAXPATHLEN + 2];
71 uid_t uid;
72 #ifdef LOGIN_CAP
73 login_cap_t *lc;
74 u_int flags = LOGIN_SETUSER;
75 #endif
76
77 uid = getuid();
78 pwd = getpwuid(uid);
79 if (pwd == NULL)
80 errx(1, "who are you?");
81
82 #ifdef LOGIN_CAP
83 if ((lc = login_getclass(pwd->pw_class)) == NULL)
84 errx(1, "%s: unknown login class", pwd->pw_class);
85 #endif
86
87 lflag = 0;
88 while ((c = getopt(argc, argv, "-l")) != -1) {
89 switch (c) {
90 case '-':
91 case 'l':
92 if (lflag)
93 usage();
94 lflag = 1;
95 break;
96 default:
97 usage();
98 break;
99 }
100 }
101
102 argc -= optind;
103 argv += optind;
104
105 if (argc > 0) {
106 pwd->pw_gid = newgrp(*argv, pwd);
107 addgrp(pwd->pw_gid);
108 if (setgid(pwd->pw_gid) < 0)
109 err(1, "setgid");
110 } else {
111 #ifdef LOGIN_CAP
112 flags |= LOGIN_SETGROUP;
113 #else
114 if (initgroups(pwd->pw_name, pwd->pw_gid) < 0)
115 err(1, "initgroups");
116 if (setgid(pwd->pw_gid) < 0)
117 err(1, "setgid");
118 #endif
119 }
120
121 #ifdef LOGIN_CAP
122 if (setusercontext(lc, pwd, uid, flags))
123 err(1, "setusercontext");
124 if (!lflag)
125 login_close(lc);
126 #else
127 if (setuid(pwd->pw_uid) < 0)
128 err(1, "setuid");
129 #endif
130
131 if (*pwd->pw_shell == '\0') {
132 #ifdef TRUST_ENV_SHELL
133 shell = getenv("SHELL");
134 if (shell != NULL)
135 pwd->pw_shell = shell;
136 else
137 #endif
138 pwd->pw_shell = _PATH_BSHELL;
139 }
140
141 shell = pwd->pw_shell;
142
143 if (lflag) {
144 char *term;
145 #ifdef KERBEROS
146 char *krbtkfile;
147 #endif
148
149 if (chdir(pwd->pw_dir) < 0)
150 warn("%s", pwd->pw_dir);
151
152 term = getenv("TERM");
153 #ifdef KERBEROS
154 krbtkfile = getenv("KRBTKFILE");
155 #endif
156
157 /* create an empty environment */
158 if ((environ = malloc(sizeof(char *))) == NULL)
159 err(1, NULL);
160 environ[0] = NULL;
161 #ifdef LOGIN_CAP
162 if (setusercontext(lc, pwd, uid, LOGIN_SETENV|LOGIN_SETPATH))
163 err(1, "setusercontext");
164 login_close(lc);
165 #else
166 (void)setenv("PATH", _PATH_DEFPATH, 1);
167 #endif
168 if (term != NULL)
169 (void)setenv("TERM", term, 1);
170 #ifdef KERBEROS
171 if (krbtkfile != NULL)
172 (void)setenv("KRBTKFILE", krbtkfile, 1);
173 #endif
174
175 (void)setenv("LOGNAME", pwd->pw_name, 1);
176 (void)setenv("USER", pwd->pw_name, 1);
177 (void)setenv("HOME", pwd->pw_dir, 1);
178 (void)setenv("SHELL", pwd->pw_shell, 1);
179
180 sbuf[0] = '-';
181 (void)strlcpy(sbuf + 1, basename(pwd->pw_shell),
182 sizeof(sbuf) - 1);
183 shell = sbuf;
184 }
185
186 execl(pwd->pw_shell, shell, NULL);
187 err(1, "%s", pwd->pw_shell);
188 }
189
190 gid_t
191 newgrp(const char *group, struct passwd *pwd)
192 {
193 struct group *grp;
194 char *ep, **p;
195 gid_t gid;
196
197 grp = getgrnam(group);
198 if (grp == NULL) {
199 if (*group != '-') {
200 gid = (gid_t)strtol(group, &ep, 10);
201 if (*ep == '\0')
202 grp = getgrgid(gid);
203 }
204 }
205
206 if (grp == NULL) {
207 warnx("%s: unknown group", group);
208 return getgid();
209 }
210
211 if (pwd->pw_gid == grp->gr_gid || getuid() == 0)
212 return grp->gr_gid;
213
214 for (p = grp->gr_mem; *p == NULL; p++)
215 if (strcmp(*p, pwd->pw_name) == 0)
216 return grp->gr_gid;
217
218 if (*grp->gr_passwd != '\0') {
219 ep = getpass("Password:");
220 if (strcmp(grp->gr_passwd, crypt(ep, grp->gr_passwd)) == 0) {
221 memset(p, '\0', _PASSWORD_LEN);
222 return grp->gr_gid;
223 }
224 memset(ep, '\0', _PASSWORD_LEN);
225 }
226
227 warnx("Sorry");
228 return getgid();
229 }
230
231 int
232 addgrp(gid_t group)
233 {
234 int i, ngroups, ngroupsmax, rval;
235 gid_t *groups;
236
237 rval = 0;
238
239 ngroupsmax = (int)sysconf(_SC_NGROUPS_MAX);
240 if (ngroupsmax < 0)
241 ngroupsmax = NGROUPS_MAX;
242
243 groups = malloc(ngroupsmax * sizeof(*groups));
244 if (groups == NULL)
245 return -1;
246
247 ngroups = getgroups(ngroupsmax, groups);
248 if (ngroups < 0) {
249 free(groups);
250 err(1, "getgroups");
251 return -1;
252 }
253
254 /*
255 * BSD based systems normally have the egid in the supplemental
256 * group list.
257 */
258 #if (defined(BSD) && BSD >= 199306)
259 /*
260 * According to POSIX/XPG6:
261 * On system where the egid is normally in the supplemental group list
262 * (or whenever the old egid actually is in the supplemental group
263 * list):
264 * o If the new egid is in the supplemental group list,
265 * just change the egid.
266 * o If the new egid is not in the supplemental group list,
267 * add the new egid to the list if there is room.
268 */
269
270 /* search for new egid in supplemental group list */
271 for (i = 0; i < ngroups && groups[i] != group; i++)
272 continue;
273
274 /* add the new egid to the supplemental group list */
275 if (i == ngroups && ngroups < ngroupsmax) {
276 groups[ngroups++] = group;
277 if (setgroups(ngroups, groups) < 0) {
278 warn("setgroups");
279 rval = -1;
280 }
281 }
282 #else
283 /*
284 * According to POSIX/XPG6:
285 * On systems where the egid is not normally in the supplemental group
286 * list (or whenever the old egid is not in the supplemental group
287 * list):
288 * o If the new egid is in the supplemental group list, delete
289 * it from the list.
290 * o If the old egid is not in the supplemental group list,
291 * add the old egid to the list if there is room.
292 */
293
294 /* search for new egid in supplemental group list */
295 for (i = 0; i < ngroups && group[i] != group; i++)
296 continue;
297
298 /* remove new egid from supplemental group list */
299 if (i != ngroup) {
300 for (--ngroups; i < ngroups; i++)
301 groups[i] = groups[i + 1];
302 }
303
304 /* search for old egid in supplemental group list */
305 for (i = 0; i < ngroups && groups[i] != egid; i++)
306 continue;
307
308 /* add old egid from supplemental group list */
309 if (i == ngroups && ngroups < maxngroups) {
310 groups[ngroups++] = egid;
311 if (setgroups(ngroups, groups) < 0) {
312 warn("setgroups");
313 rval = -1;
314 }
315 }
316 #endif
317
318 free(groups);
319 return rval;
320 }
321
322 void
323 usage()
324 {
325
326 (void)fprintf(stderr, "usage: %s [-l] [group]\n", getprogname());
327 exit(1);
328 }
329