Deleted Added
full compact
login_class.c (22993) login_class.c (25670)
1/*-
2 * Copyright (c) 1996 by
3 * Sean Eric Fagan <sef@kithrup.com>
4 * David Nugent <davidn@blaze.net.au>
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, is permitted provided that the following conditions

--- 7 unchanged lines hidden (view full) ---

16 * 3. This work was done expressly for inclusion into FreeBSD. Other use
17 * is permitted provided this notation is included.
18 * 4. Absolutely no warranty of function or purpose is made by the authors.
19 * 5. Modifications may be freely made to this file providing the above
20 * conditions are met.
21 *
22 * High-level routines relating to use of the user capabilities database
23 *
1/*-
2 * Copyright (c) 1996 by
3 * Sean Eric Fagan <sef@kithrup.com>
4 * David Nugent <davidn@blaze.net.au>
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, is permitted provided that the following conditions

--- 7 unchanged lines hidden (view full) ---

16 * 3. This work was done expressly for inclusion into FreeBSD. Other use
17 * is permitted provided this notation is included.
18 * 4. Absolutely no warranty of function or purpose is made by the authors.
19 * 5. Modifications may be freely made to this file providing the above
20 * conditions are met.
21 *
22 * High-level routines relating to use of the user capabilities database
23 *
24 * $Id$
24 * $Id: login_class.c,v 1.5 1997/02/22 15:08:22 peter Exp $
25 */
26
27#include <stdio.h>
28#include <stdlib.h>
29#include <string.h>
30#include <unistd.h>
31#include <errno.h>
32#include <sys/types.h>

--- 7 unchanged lines hidden (view full) ---

40#include <paths.h>
41
42
43#undef UNKNOWN
44#define UNKNOWN "su"
45
46
47static struct login_res {
25 */
26
27#include <stdio.h>
28#include <stdlib.h>
29#include <string.h>
30#include <unistd.h>
31#include <errno.h>
32#include <sys/types.h>

--- 7 unchanged lines hidden (view full) ---

40#include <paths.h>
41
42
43#undef UNKNOWN
44#define UNKNOWN "su"
45
46
47static struct login_res {
48 const char * what;
49 rlim_t (*who)(login_cap_t *, const char *, rlim_t, rlim_t);
50 int why;
48 const char *what;
49 rlim_t (*who)(login_cap_t *, const char *, rlim_t, rlim_t);
50 int why;
51} resources[] = {
51} resources[] = {
52 { "cputime", login_getcaptime, RLIMIT_CPU },
53 { "filesize", login_getcapsize, RLIMIT_FSIZE },
54 { "datasize", login_getcapsize, RLIMIT_DATA },
55 { "stacksize", login_getcapsize, RLIMIT_STACK },
56 { "coredumpsize", login_getcapsize, RLIMIT_CORE },
57 { "memoryuse", login_getcapsize, RLIMIT_RSS },
58 { "memorylocked", login_getcapsize, RLIMIT_MEMLOCK },
59 { "maxproc", login_getcapnum, RLIMIT_NPROC },
60 { "openfiles", login_getcapnum, RLIMIT_NOFILE },
61 { NULL, 0, 0 }
52 { "cputime", login_getcaptime, RLIMIT_CPU },
53 { "filesize", login_getcapsize, RLIMIT_FSIZE },
54 { "datasize", login_getcapsize, RLIMIT_DATA },
55 { "stacksize", login_getcapsize, RLIMIT_STACK },
56 { "memoryuse", login_getcapsize, RLIMIT_RSS },
57 { "memorylocked", login_getcapsize, RLIMIT_MEMLOCK },
58 { "maxproc", login_getcapnum, RLIMIT_NPROC },
59 { "openfiles", login_getcapnum, RLIMIT_NOFILE },
60 { "coredumpsize", login_getcapsize, RLIMIT_CORE },
61 { NULL, 0, 0 }
62};
63
64
62};
63
64
65
66void
67setclassresources(login_cap_t *lc)
68{
65void
66setclassresources(login_cap_t *lc)
67{
69 struct login_res *lr = resources;
68 struct login_res *lr;
70
69
71 if (lc == NULL)
72 return;
70 if (lc == NULL)
71 return;
73
72
74 while (lr->what != NULL) {
75 struct rlimit rlim,
76 newlim;
77 char cur[40],
78 max[40];
79 rlim_t rcur,
80 rmax;
73 for (lr = resources; lr->what != NULL; ++lr) {
74 struct rlimit rlim;
81
75
82 sprintf(cur, "%s-cur", lr->what);
83 sprintf(max, "%s-max", lr->what);
76 /*
77 * The login.conf file can have <limit>, <limit>-max, and
78 * <limit>-cur entries.
79 * What we do is get the current current- and maximum- limits.
80 * Then, we try to get an entry for <limit> from the capability,
81 * using the current and max limits we just got as the
82 * default/error values.
83 * *Then*, we try looking for <limit>-cur and <limit>-max,
84 * again using the appropriate values as the default/error
85 * conditions.
86 */
84
87
85 /*
86 * The login.conf file can have <limit>, <limit>-max, and
87 * <limit>-cur entries.
88 * What we do is get the current current- and maximum- limits.
89 * Then, we try to get an entry for <limit> from the capability,
90 * using the current and max limits we just got as the
91 * default/error values.
92 * *Then*, we try looking for <limit>-cur and <limit>-max,
93 * again using the appropriate values as the default/error
94 * conditions.
95 */
88 if (getrlimit(lr->why, &rlim) != 0)
89 syslog(LOG_ERR, "getting %s resource limit: %m", lr->what);
90 else {
91 char name_cur[40];
92 char name_max[40];
93 rlim_t rcur = rlim.rlim_cur;
94 rlim_t rmax = rlim.rlim_max;
96
95
97 getrlimit(lr->why, &rlim);
98 rcur = rlim.rlim_cur;
99 rmax = rlim.rlim_max;
96 sprintf(name_cur, "%s-cur", lr->what);
97 sprintf(name_max, "%s-max", lr->what);
100
98
101 rcur = (*lr->who)(lc, lr->what, rcur, rcur);
102 rmax = (*lr->who)(lc, lr->what, rmax, rmax);
103 newlim.rlim_cur = (*lr->who)(lc, cur, rcur, rcur);
104 newlim.rlim_max = (*lr->who)(lc, max, rmax, rmax);
99 rcur = (*lr->who)(lc, lr->what, rcur, rcur);
100 rmax = (*lr->who)(lc, lr->what, rmax, rmax);
101 rlim.rlim_cur = (*lr->who)(lc, name_cur, rcur, rcur);
102 rlim.rlim_max = (*lr->who)(lc, name_max, rmax, rmax);
105
103
106 if (setrlimit(lr->why, &newlim) == -1)
107 syslog(LOG_WARNING, "set class '%s' resource limit %s: %m", lc->lc_class, lr->what);
108
109 ++lr;
110 }
104 if (setrlimit(lr->why, &rlim) == -1)
105 syslog(LOG_WARNING, "set class '%s' resource limit %s: %m", lc->lc_class, lr->what);
106 }
107 }
111}
112
108}
109
110
111
113static struct login_vars {
112static struct login_vars {
114 const char * tag;
115 const char * var;
116 const char * def;
113 const char *tag;
114 const char *var;
115 const char *def;
117} pathvars[] = {
116} pathvars[] = {
118 { "path", "PATH", NULL },
119 { "manpath", "MANPATH", NULL },
120 { NULL, NULL, NULL }
117 { "path", "PATH", NULL },
118 { "cdpath", "CDPATH", NULL },
119 { "manpath", "MANPATH", NULL },
120 { NULL, NULL, NULL }
121}, envars[] = {
121}, envars[] = {
122 { "lang", "LANG", NULL },
123 { "charset", "MM_CHARSET", NULL },
124 { "timezone", "TZ", NULL },
125 { "term", "TERM", UNKNOWN },
126 { NULL, NULL, NULL }
122 { "lang", "LANG", NULL },
123 { "charset", "MM_CHARSET", NULL },
124 { "timezone", "TZ", NULL },
125 { "term", "TERM", UNKNOWN },
126 { NULL, NULL, NULL }
127};
128
129static char *
130substvar(char * var, const struct passwd * pwd, int hlen, int pch, int nlen)
131{
127};
128
129static char *
130substvar(char * var, const struct passwd * pwd, int hlen, int pch, int nlen)
131{
132 char * np = NULL;
132 char *np = NULL;
133
133
134 if (var != NULL) {
135 int tildes = 0;
136 int dollas = 0;
137 char * p;
134 if (var != NULL) {
135 int tildes = 0;
136 int dollas = 0;
137 char *p;
138
138
139 if (pwd != NULL) {
140 /*
141 * Count the number of ~'s in var to substitute
142 */
143 p = var;
144 while ((p = strchr(p, '~')) != NULL) {
145 ++tildes;
146 ++p;
147 }
139 if (pwd != NULL) {
140 /* Count the number of ~'s in var to substitute */
141 p = var;
142 for (p = var; (p = strchr(p, '~')) != NULL; p++)
143 ++tildes;
144 /* Count the number of $'s in var to substitute */
145 p = var;
146 for (p = var; (p = strchr(p, '$')) != NULL; p++)
147 ++dollas;
148 }
148
149
149 /*
150 * Count the number of $'s in var to substitute
151 */
152 p = var;
153 while ((p = strchr(p, '$')) != NULL) {
154 ++dollas;
155 ++p;
156 }
157 }
150 np = malloc(strlen(var) + (dollas * nlen)
151 - dollas + (tildes * (pch+hlen))
152 - tildes + 1);
158
153
159 np = malloc(strlen(var) + (dollas * nlen) - dollas + (tildes * (pch+hlen)) - tildes + 1);
154 if (np != NULL) {
155 p = strcpy(np, var);
160
156
161 if (np != NULL) {
162 p = strcpy(np, var);
157 if (pwd != NULL) {
158 /*
159 * This loop does user username and homedir substitutions
160 * for unescaped $ (username) and ~ (homedir)
161 */
162 while (*(p += strcspn(p, "~$")) != '\0') {
163 int l = strlen(p);
163
164
164 if (pwd != NULL) {
165 /*
166 * This loop does user username and homedir substitutions
167 * for unescaped $ (username) and ~ (homedir)
168 */
169 while (*(p += strcspn(p, "~$")) != '\0') {
170 int l = strlen(p);
171
172 if (p > var && *(p-1) == '\\') /* Escaped: */
173 memmove(p - 1, p, l + 1); /* Slide-out the backslash */
174 else if (*p == '~') {
175 int v = pch && *(p+1) != '/'; /* Avoid double // */
176 memmove(p + hlen + v, p + 1, l); /* Subst homedir */
177 memmove(p, pwd->pw_dir, hlen);
178 if (v)
179 p[hlen] = '/';
180 p += hlen + v;
181 }
182 else /* if (*p == '$') */ {
183 memmove(p + nlen, p + 1, l); /* Subst username */
184 memmove(p, pwd->pw_name, nlen);
185 p += nlen;
186 }
165 if (p > var && *(p-1) == '\\') /* Escaped: */
166 memmove(p - 1, p, l + 1); /* Slide-out the backslash */
167 else if (*p == '~') {
168 int v = pch && *(p+1) != '/'; /* Avoid double // */
169 memmove(p + hlen + v, p + 1, l); /* Subst homedir */
170 memmove(p, pwd->pw_dir, hlen);
171 if (v)
172 p[hlen] = '/';
173 p += hlen + v;
174 }
175 else /* if (*p == '$') */ {
176 memmove(p + nlen, p + 1, l); /* Subst username */
177 memmove(p, pwd->pw_name, nlen);
178 p += nlen;
179 }
180 }
181 }
187 }
182 }
188 }
189 }
183 }
190 }
191 return np;
184
185 return np;
192}
193
194
195void
196setclassenvironment(login_cap_t *lc, const struct passwd * pwd, int paths)
197{
186}
187
188
189void
190setclassenvironment(login_cap_t *lc, const struct passwd * pwd, int paths)
191{
198 struct login_vars * vars = paths ? pathvars : envars;
199 int hlen = pwd ? strlen(pwd->pw_dir) : 0;
200 int nlen = pwd ? strlen(pwd->pw_name) : 0;
201 char pch = 0;
192 struct login_vars *vars = paths ? pathvars : envars;
193 int hlen = pwd ? strlen(pwd->pw_dir) : 0;
194 int nlen = pwd ? strlen(pwd->pw_name) : 0;
195 char pch = 0;
202
196
203 if (hlen && pwd->pw_dir[hlen-1] != '/')
204 ++pch;
197 if (hlen && pwd->pw_dir[hlen-1] != '/')
198 ++pch;
205
199
206 while (vars->tag != NULL) {
207 char * var = paths ? login_getpath(lc, vars->tag, NULL)
208 : login_getcapstr(lc, vars->tag, NULL, NULL);
200 while (vars->tag != NULL) {
201 char * var = paths ? login_getpath(lc, vars->tag, NULL)
202 : login_getcapstr(lc, vars->tag, NULL, NULL);
209
203
210 char * np = substvar(var, pwd, hlen, pch, nlen);
204 char * np = substvar(var, pwd, hlen, pch, nlen);
211
205
212 if (np != NULL) {
213 setenv(vars->var, np, 1);
214 free(np);
215 } else if (vars->def != NULL) {
216 setenv(vars->var, vars->def, 0);
206 if (np != NULL) {
207 setenv(vars->var, np, 1);
208 free(np);
209 } else if (vars->def != NULL) {
210 setenv(vars->var, vars->def, 0);
211 }
212 ++vars;
217 }
213 }
218 ++vars;
219 }
220
214
221 /*
222 * If we're not processing paths, then see if there is a setenv list by
223 * which the admin and/or user may set an arbitrary set of env vars.
224 */
225 if (!paths) {
226 char ** set_env = login_getcaplist(lc, "setenv", ",");
215 /*
216 * If we're not processing paths, then see if there is a setenv list by
217 * which the admin and/or user may set an arbitrary set of env vars.
218 */
219 if (!paths) {
220 char **set_env = login_getcaplist(lc, "setenv", ",");
227
221
228 if (set_env != NULL) {
229 while (*set_env != NULL) {
230 char *p = strchr(*set_env, '=');
231 if (p != NULL) { /* Discard invalid entries */
232 char * np;
222 if (set_env != NULL) {
223 while (*set_env != NULL) {
224 char *p = strchr(*set_env, '=');
233
225
234 *p++ = '\0';
235 if ((np = substvar(p, pwd, hlen, pch, nlen)) != NULL) {
236 setenv(*set_env, np, 1);
237 free(np);
238 }
226 if (p != NULL) { /* Discard invalid entries */
227 char *np;
228
229 *p++ = '\0';
230 if ((np = substvar(p, pwd, hlen, pch, nlen)) != NULL) {
231 setenv(*set_env, np, 1);
232 free(np);
233 }
234 }
235 ++set_env;
236 }
239 }
237 }
240 ++set_env;
241 }
242 }
238 }
243 }
244}
245
246
247/*
248 * setclasscontext()
249 *
250 * For the login class <class>, set various class context values
251 * (limits, mainly) to the values for that class. Which values are
252 * set are controlled by <flags> -- see <login_class.h> for the
253 * possible values.
254 *
255 * setclasscontext() can only set resources, priority, and umask.
256 */
257
258int
259setclasscontext(const char *classname, unsigned int flags)
260{
239}
240
241
242/*
243 * setclasscontext()
244 *
245 * For the login class <class>, set various class context values
246 * (limits, mainly) to the values for that class. Which values are
247 * set are controlled by <flags> -- see <login_class.h> for the
248 * possible values.
249 *
250 * setclasscontext() can only set resources, priority, and umask.
251 */
252
253int
254setclasscontext(const char *classname, unsigned int flags)
255{
261 int rc;
262 login_cap_t * lc = login_getclassbyname(classname, NULL);
263 flags &= (LOGIN_SETRESOURCES| LOGIN_SETPRIORITY|LOGIN_SETUMASK);
264 rc = setusercontext(lc, NULL, 0, flags);
265 login_close(lc);
266 return rc;
256 int rc;
257 login_cap_t *lc;
258
259 lc = login_getclassbyname(classname, NULL);
260
261 flags &= LOGIN_SETRESOURCES | LOGIN_SETPRIORITY |
262 LOGIN_SETUMASK | LOGIN_SETPATH;
263
264 rc = lc ? setusercontext(lc, NULL, 0, flags) : -1;
265 login_close(lc);
266 return rc;
267}
268
269
267}
268
269
270
270/*
271/*
272 * Private functionw which takes care of processing
273 */
274
275static mode_t
276setlogincontext(login_cap_t *lc, const struct passwd *pwd,
277 mode_t mymask, unsigned long flags)
278{
279 if (lc) {
280 /* Set resources */
281 if (flags & LOGIN_SETRESOURCES)
282 setclassresources(lc);
283 /* See if there's a umask override */
284 if (flags & LOGIN_SETUMASK)
285 mymask = (mode_t)login_getcapnum(lc, "umask", mymask, mymask);
286 /* Set paths */
287 if (flags & LOGIN_SETPATH)
288 setclassenvironment(lc, pwd, 1);
289 /* Set environment */
290 if (flags & LOGIN_SETENV)
291 setclassenvironment(lc, pwd, 0);
292 }
293 return mymask;
294}
295
296
297
298/*
271 * setusercontext()
272 *
273 * Given a login class <lc> and a user in <pwd>, with a uid <uid>,
274 * set the context as in setclasscontext(). <flags> controls which
275 * values are set.
276 *
277 * The difference between setclasscontext() and setusercontext() is
278 * that the former sets things up for an already-existing process,
279 * while the latter sets things up from a root context. Such as might
280 * be called from login(1).
281 *
282 */
283
284int
285setusercontext(login_cap_t *lc, const struct passwd *pwd, uid_t uid, unsigned int flags)
286{
299 * setusercontext()
300 *
301 * Given a login class <lc> and a user in <pwd>, with a uid <uid>,
302 * set the context as in setclasscontext(). <flags> controls which
303 * values are set.
304 *
305 * The difference between setclasscontext() and setusercontext() is
306 * that the former sets things up for an already-existing process,
307 * while the latter sets things up from a root context. Such as might
308 * be called from login(1).
309 *
310 */
311
312int
313setusercontext(login_cap_t *lc, const struct passwd *pwd, uid_t uid, unsigned int flags)
314{
287 int i;
288 login_cap_t * llc = NULL;
315 quad_t p;
316 mode_t mymask;
317 login_cap_t *llc = NULL;
289
318
290 if (lc == NULL)
291 {
292 if (pwd != NULL && (lc = login_getclass(pwd)) != NULL)
293 llc = lc; /* free this when we're done */
294 }
319 if (lc == NULL) {
320 if (pwd != NULL && (lc = login_getpwclass(pwd)) != NULL)
321 llc = lc; /* free this when we're done */
322 }
295
323
296 if (flags & LOGIN_SETPATH)
297 pathvars[0].def = uid ? _PATH_DEFPATH : _PATH_STDPATH;
324 if (flags & LOGIN_SETPATH)
325 pathvars[0].def = uid ? _PATH_DEFPATH : _PATH_STDPATH;
298
326
299 /*
300 * Set the process priority
301 */
302 if (flags & LOGIN_SETPRIORITY) {
303 int pri = (int)login_getcapnum(lc, "priority", LOGIN_DEFPRI, LOGIN_DEFPRI);
304 pri = (pri < PRIO_MIN) ? PRIO_MIN : (pri > PRIO_MAX) ? PRIO_MAX : pri;
305 if (setpriority(PRIO_PROCESS, 0, pri) != 0)
306 syslog(LOG_WARNING, "setpriority '%s': %m", pwd->pw_name);
307 }
327 /* we need a passwd entry to set these */
328 if (pwd == NULL)
329 flags &= ~(LOGIN_SETGROUP | LOGIN_SETLOGIN);
308
330
309 /*
310 * Set resources
311 */
312 if (flags & LOGIN_SETRESOURCES)
313 setclassresources(lc);
331 /* Set the process priority */
332 if (flags & LOGIN_SETPRIORITY) {
333 p = login_getcapnum(lc, "priority", LOGIN_DEFPRI, LOGIN_DEFPRI);
314
334
315 /*
316 * Set the sessions login
317 */
318 if ((flags & LOGIN_SETLOGIN) && setlogin(pwd->pw_name) != 0) {
319 syslog(LOG_ERR, "setlogin '%s': %m", pwd->pw_name);
320 login_close(llc);
321 return -1;
322 }
335 p = (p < PRIO_MIN || p > PRIO_MAX) ? LOGIN_DEFPRI : p;
336 if (setpriority(PRIO_PROCESS, 0, (int)p) != 0)
337 syslog(LOG_WARNING, "setpriority '%s' (%s): %m",
338 pwd->pw_name, lc ? lc->lc_class : LOGIN_DEFCLASS);
339 }
323
340
324 /*
325 * Setup the user's group permissions
326 */
327 if (flags & LOGIN_SETGROUP) {
328 if (setgid(pwd->pw_gid) != 0)
329 syslog(LOG_WARNING, "setgid %ld: %m", (long)pwd->pw_gid);
330 if (initgroups(pwd->pw_name, pwd->pw_gid) == -1)
331 syslog(LOG_WARNING, "initgroups '%s': %m", pwd->pw_name);
332 }
341 /* Setup the user's group permissions */
342 if (flags & LOGIN_SETGROUP) {
343 if (setgid(pwd->pw_gid) != 0) {
344 syslog(LOG_ERR, "setgid(%ld): %m", (long)pwd->pw_gid);
345 login_close(llc);
346 return -1;
347 }
348 if (initgroups(pwd->pw_name, pwd->pw_gid) == -1) {
349 syslog(LOG_ERR, "initgroups(%s,%ld): %m", pwd->pw_name,
350 pwd->pw_gid);
351 login_close(llc);
352 return -1;
353 }
354 }
333
355
334 /*
335 * FreeBSD extension: here we (might) loop and do this twice.
336 * First, for the class we have been given, and next for
337 * any user overrides in ~/.login.conf the user's home directory.
338 */
339 if (flags & LOGIN_SETUMASK)
340 umask(LOGIN_DEFUMASK); /* Set default umask up front */
356 /* Set the sessions login */
357 if ((flags & LOGIN_SETLOGIN) && setlogin(pwd->pw_name) != 0) {
358 syslog(LOG_ERR, "setlogin(%s): %m", pwd->pw_name);
359 login_close(llc);
360 return -1;
361 }
341
362
342 i = 0;
343 while (i < 2 && lc != NULL) {
363 mymask = (flags & LOGIN_SETUMASK) ? umask(LOGIN_DEFUMASK) : 0;
364 mymask = setlogincontext(lc, pwd, mymask, flags);
365 login_close(llc);
344
366
345 if (flags & LOGIN_SETUMASK) {
346 rlim_t tmp = login_getcapnum(lc, "umask", RLIM_INFINITY, RLIM_INFINITY);
347 if (tmp != RLIM_INFINITY)
348 umask((mode_t)tmp);
367 /* This needs to be done after anything that needs root privs */
368 if ((flags & LOGIN_SETUSER) && setuid(uid) != 0) {
369 syslog(LOG_ERR, "setuid(%ld): %m", uid);
370 return -1; /* Paranoia again */
349 }
350
371 }
372
351 if (flags & LOGIN_SETPATH)
352 setclassenvironment(lc, pwd, 1);
373 /*
374 * Now, we repeat some of the above for the user's private entries
375 */
376 if ((lc = login_getuserclass(pwd)) != NULL) {
377 mymask = setlogincontext(lc, pwd, mymask, flags);
378 login_close(lc);
379 }
353
380
354 if (flags & LOGIN_SETENV)
355 setclassenvironment(lc, pwd, 0);
381 /* Finally, set any umask we've found */
382 if (flags & LOGIN_SETUMASK)
383 umask(mymask);
356
384
357 if (i++ == 0) /* Play it again, Sam */
358 lc = (pwd == NULL) ? NULL : login_getuserclass(pwd);
359 }
360
361 login_close(lc); /* User's private 'me' class */
362 login_close(llc); /* Class we opened */
363
364 /*
365 * This needs to be done after all of the above.
366 * Do it last so we avoid getting killed and dropping core
367 */
368 if ((flags & LOGIN_SETUSER) && setuid(uid) != 0) {
369 syslog(LOG_ERR, "setuid %ld: %m", uid);
370 login_close(llc);
371 return -1; /* Paranoia again */
372 }
373
374 return 0;
385 return 0;
375}
376
386}
387