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
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice immediately at the beginning of the file, without modification,
12 *    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. 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
25#include <sys/cdefs.h>
26__FBSDID("$FreeBSD$");
27
28#include <sys/param.h>
29#include <sys/cpuset.h>
30#include <sys/mac.h>
31#include <sys/resource.h>
32#include <sys/rtprio.h>
33#include <sys/stat.h>
34#include <sys/time.h>
35
36#include <ctype.h>
37#include <err.h>
38#include <errno.h>
39#include <fcntl.h>
40#include <login_cap.h>
41#include <paths.h>
42#include <pwd.h>
43#include <signal.h>
44#include <stdio.h>
45#include <stdlib.h>
46#include <string.h>
47#include <syslog.h>
48#include <unistd.h>
49
50
51static struct login_res {
52    const char *what;
53    rlim_t (*who)(login_cap_t *, const char *, rlim_t, rlim_t);
54    int why;
55} resources[] = {
56    { "cputime",         login_getcaptime, RLIMIT_CPU     },
57    { "filesize",        login_getcapsize, RLIMIT_FSIZE   },
58    { "datasize",        login_getcapsize, RLIMIT_DATA    },
59    { "stacksize",       login_getcapsize, RLIMIT_STACK   },
60    { "memoryuse",       login_getcapsize, RLIMIT_RSS     },
61    { "memorylocked",    login_getcapsize, RLIMIT_MEMLOCK },
62    { "maxproc",         login_getcapnum,  RLIMIT_NPROC   },
63    { "openfiles",       login_getcapnum,  RLIMIT_NOFILE  },
64    { "coredumpsize",    login_getcapsize, RLIMIT_CORE    },
65    { "sbsize",          login_getcapsize, RLIMIT_SBSIZE  },
66    { "vmemoryuse",      login_getcapsize, RLIMIT_VMEM    },
67    { "pseudoterminals", login_getcapnum,  RLIMIT_NPTS    },
68    { "swapuse",         login_getcapsize, RLIMIT_SWAP    },
69    { NULL,              0,                0              }
70};
71
72
73void
74setclassresources(login_cap_t *lc)
75{
76    struct login_res *lr;
77
78    if (lc == NULL)
79	return;
80
81    for (lr = resources; lr->what != NULL; ++lr) {
82	struct rlimit	rlim;
83
84	/*
85	 * The login.conf file can have <limit>, <limit>-max, and
86	 * <limit>-cur entries.
87	 * What we do is get the current current- and maximum- limits.
88	 * Then, we try to get an entry for <limit> from the capability,
89	 * using the current and max limits we just got as the
90	 * default/error values.
91	 * *Then*, we try looking for <limit>-cur and <limit>-max,
92	 * again using the appropriate values as the default/error
93	 * conditions.
94	 */
95
96	if (getrlimit(lr->why, &rlim) != 0)
97	    syslog(LOG_ERR, "getting %s resource limit: %m", lr->what);
98	else {
99	    char	name_cur[40];
100	    char	name_max[40];
101	    rlim_t	rcur = rlim.rlim_cur;
102	    rlim_t	rmax = rlim.rlim_max;
103
104	    sprintf(name_cur, "%s-cur", lr->what);
105	    sprintf(name_max, "%s-max", lr->what);
106
107	    rcur = (*lr->who)(lc, lr->what, rcur, rcur);
108	    rmax = (*lr->who)(lc, lr->what, rmax, rmax);
109	    rlim.rlim_cur = (*lr->who)(lc, name_cur, rcur, rcur);
110	    rlim.rlim_max = (*lr->who)(lc, name_max, rmax, rmax);
111
112	    if (setrlimit(lr->why, &rlim) == -1)
113		syslog(LOG_WARNING, "set class '%s' resource limit %s: %m", lc->lc_class, lr->what);
114	}
115    }
116}
117
118
119
120static struct login_vars {
121    const char *tag;
122    const char *var;
123    const char *def;
124    int overwrite;
125} pathvars[] = {
126    { "path",           "PATH",       NULL, 1},
127    { "cdpath",         "CDPATH",     NULL, 1},
128    { "manpath",        "MANPATH",    NULL, 1},
129    { NULL,             NULL,         NULL, 0}
130}, envars[] = {
131    { "lang",           "LANG",       NULL, 1},
132    { "charset",        "MM_CHARSET", NULL, 1},
133    { "timezone",       "TZ",         NULL, 1},
134    { "term",           "TERM",       NULL, 0},
135    { NULL,             NULL,         NULL, 0}
136};
137
138static char *
139substvar(const char * var, const struct passwd * pwd, int hlen, int pch, int nlen)
140{
141    char    *np = NULL;
142
143    if (var != NULL) {
144	int	tildes = 0;
145	int	dollas = 0;
146	char	*p;
147	const char *q;
148
149	if (pwd != NULL) {
150	    for (q = var; *q != '\0'; ++q) {
151		tildes += (*q == '~');
152		dollas += (*q == '$');
153	    }
154	}
155
156	np = malloc(strlen(var) + (dollas * nlen)
157		    - dollas + (tildes * (pch+hlen))
158		    - tildes + 1);
159
160	if (np != NULL) {
161	    p = strcpy(np, var);
162
163	    if (pwd != NULL) {
164		/*
165		 * This loop does user username and homedir substitutions
166		 * for unescaped $ (username) and ~ (homedir)
167		 */
168		while (*(p += strcspn(p, "~$")) != '\0') {
169		    int	l = strlen(p);
170
171		    if (p > np && *(p-1) == '\\')  /* Escaped: */
172			memmove(p - 1, p, l + 1); /* Slide-out the backslash */
173		    else if (*p == '~') {
174			int	v = pch && *(p+1) != '/'; /* Avoid double // */
175			memmove(p + hlen + v, p + 1, l);  /* Subst homedir */
176			memmove(p, pwd->pw_dir, hlen);
177			if (v)
178			    p[hlen] = '/';
179			p += hlen + v;
180		    }
181		    else /* if (*p == '$') */ {
182			memmove(p + nlen, p + 1, l);	/* Subst username */
183			memmove(p, pwd->pw_name, nlen);
184			p += nlen;
185		    }
186		}
187	    }
188	}
189    }
190
191    return (np);
192}
193
194
195void
196setclassenvironment(login_cap_t *lc, const struct passwd * pwd, int paths)
197{
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;
202
203    if (hlen && pwd->pw_dir[hlen-1] != '/')
204	++pch;
205
206    while (vars->tag != NULL) {
207	const char * var = paths ? login_getpath(lc, vars->tag, NULL)
208				 : login_getcapstr(lc, vars->tag, NULL, NULL);
209
210	char * np  = substvar(var, pwd, hlen, pch, nlen);
211
212	if (np != NULL) {
213	    setenv(vars->var, np, vars->overwrite);
214	    free(np);
215	} else if (vars->def != NULL) {
216	    setenv(vars->var, vars->def, 0);
217	}
218	++vars;
219    }
220
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	const char	**set_env = login_getcaplist(lc, "setenv", ",");
227
228	if (set_env != NULL) {
229	    while (*set_env != NULL) {
230		char	*p = strchr(*set_env, '=');
231
232		if (p != NULL) {  /* Discard invalid entries */
233		    char	*np;
234
235		    *p++ = '\0';
236		    if ((np = substvar(p, pwd, hlen, pch, nlen)) != NULL) {
237			setenv(*set_env, np, 1);
238			free(np);
239		    }
240		}
241		++set_env;
242	    }
243	}
244    }
245}
246
247
248static int
249list2cpuset(const char *list, cpuset_t *mask)
250{
251	enum { NONE, NUM, DASH } state;
252	int lastnum;
253	int curnum;
254	const char *l;
255
256	state = NONE;
257	curnum = lastnum = 0;
258	for (l = list; *l != '\0';) {
259		if (isdigit(*l)) {
260			curnum = atoi(l);
261			if (curnum > CPU_SETSIZE)
262				errx(EXIT_FAILURE,
263				    "Only %d cpus supported", CPU_SETSIZE);
264			while (isdigit(*l))
265				l++;
266			switch (state) {
267			case NONE:
268				lastnum = curnum;
269				state = NUM;
270				break;
271			case DASH:
272				for (; lastnum <= curnum; lastnum++)
273					CPU_SET(lastnum, mask);
274				state = NONE;
275				break;
276			case NUM:
277			default:
278				return (0);
279			}
280			continue;
281		}
282		switch (*l) {
283		case ',':
284			switch (state) {
285			case NONE:
286				break;
287			case NUM:
288				CPU_SET(curnum, mask);
289				state = NONE;
290				break;
291			case DASH:
292				return (0);
293				break;
294			}
295			break;
296		case '-':
297			if (state != NUM)
298				return (0);
299			state = DASH;
300			break;
301		default:
302			return (0);
303		}
304		l++;
305	}
306	switch (state) {
307		case NONE:
308			break;
309		case NUM:
310			CPU_SET(curnum, mask);
311			break;
312		case DASH:
313			return (0);
314	}
315	return (1);
316}
317
318
319void
320setclasscpumask(login_cap_t *lc)
321{
322	const char *maskstr;
323	cpuset_t maskset;
324	cpusetid_t setid;
325
326	maskstr = login_getcapstr(lc, "cpumask", NULL, NULL);
327	CPU_ZERO(&maskset);
328	if (maskstr == NULL)
329		return;
330	if (strcasecmp("default", maskstr) == 0)
331		return;
332	if (!list2cpuset(maskstr, &maskset)) {
333		syslog(LOG_WARNING,
334		    "list2cpuset(%s) invalid mask specification", maskstr);
335		return;
336	}
337
338	if (cpuset(&setid) != 0) {
339		syslog(LOG_ERR, "cpuset(): %s", strerror(errno));
340		return;
341	}
342
343	if (cpuset_setaffinity(CPU_LEVEL_CPUSET, CPU_WHICH_PID, -1,
344	    sizeof(maskset), &maskset) != 0)
345		syslog(LOG_ERR, "cpuset_setaffinity(%s): %s", maskstr,
346		    strerror(errno));
347}
348
349
350/*
351 * setclasscontext()
352 *
353 * For the login class <class>, set various class context values
354 * (limits, mainly) to the values for that class.  Which values are
355 * set are controlled by <flags> -- see <login_class.h> for the
356 * possible values.
357 *
358 * setclasscontext() can only set resources, priority, and umask.
359 */
360
361int
362setclasscontext(const char *classname, unsigned int flags)
363{
364    int		rc;
365    login_cap_t *lc;
366
367    lc = login_getclassbyname(classname, NULL);
368
369    flags &= LOGIN_SETRESOURCES | LOGIN_SETPRIORITY |
370	    LOGIN_SETUMASK | LOGIN_SETPATH;
371
372    rc = lc ? setusercontext(lc, NULL, 0, flags) : -1;
373    login_close(lc);
374    return (rc);
375}
376
377
378
379/*
380 * Private function which takes care of processing
381 */
382
383static mode_t
384setlogincontext(login_cap_t *lc, const struct passwd *pwd,
385		mode_t mymask, unsigned long flags)
386{
387    if (lc) {
388	/* Set resources */
389	if (flags & LOGIN_SETRESOURCES)
390	    setclassresources(lc);
391	/* See if there's a umask override */
392	if (flags & LOGIN_SETUMASK)
393	    mymask = (mode_t)login_getcapnum(lc, "umask", mymask, mymask);
394	/* Set paths */
395	if (flags & LOGIN_SETPATH)
396	    setclassenvironment(lc, pwd, 1);
397	/* Set environment */
398	if (flags & LOGIN_SETENV)
399	    setclassenvironment(lc, pwd, 0);
400	/* Set cpu affinity */
401	if (flags & LOGIN_SETCPUMASK)
402	    setclasscpumask(lc);
403    }
404    return (mymask);
405}
406
407
408
409/*
410 * setusercontext()
411 *
412 * Given a login class <lc> and a user in <pwd>, with a uid <uid>,
413 * set the context as in setclasscontext().  <flags> controls which
414 * values are set.
415 *
416 * The difference between setclasscontext() and setusercontext() is
417 * that the former sets things up for an already-existing process,
418 * while the latter sets things up from a root context.  Such as might
419 * be called from login(1).
420 *
421 */
422
423int
424setusercontext(login_cap_t *lc, const struct passwd *pwd, uid_t uid, unsigned int flags)
425{
426    quad_t	p;
427    mode_t	mymask;
428    login_cap_t *llc = NULL;
429    struct sigaction sa, prevsa;
430    struct rtprio rtp;
431    int error;
432
433    if (lc == NULL) {
434	if (pwd != NULL && (lc = login_getpwclass(pwd)) != NULL)
435	    llc = lc; /* free this when we're done */
436    }
437
438    if (flags & LOGIN_SETPATH)
439	pathvars[0].def = uid ? _PATH_DEFPATH : _PATH_STDPATH;
440
441    /* we need a passwd entry to set these */
442    if (pwd == NULL)
443	flags &= ~(LOGIN_SETGROUP | LOGIN_SETLOGIN | LOGIN_SETMAC);
444
445    /* Set the process priority */
446    if (flags & LOGIN_SETPRIORITY) {
447	p = login_getcapnum(lc, "priority", LOGIN_DEFPRI, LOGIN_DEFPRI);
448
449	if (p > PRIO_MAX) {
450	    rtp.type = RTP_PRIO_IDLE;
451	    rtp.prio = p - PRIO_MAX - 1;
452	    p = (rtp.prio > RTP_PRIO_MAX) ? 31 : p;
453	    if (rtprio(RTP_SET, 0, &rtp))
454		syslog(LOG_WARNING, "rtprio '%s' (%s): %m",
455		    pwd ? pwd->pw_name : "-",
456		    lc ? lc->lc_class : LOGIN_DEFCLASS);
457	} else if (p < PRIO_MIN) {
458	    rtp.type = RTP_PRIO_REALTIME;
459	    rtp.prio = abs(p - PRIO_MIN + RTP_PRIO_MAX);
460	    p = (rtp.prio > RTP_PRIO_MAX) ? 1 : p;
461	    if (rtprio(RTP_SET, 0, &rtp))
462		syslog(LOG_WARNING, "rtprio '%s' (%s): %m",
463		    pwd ? pwd->pw_name : "-",
464		    lc ? lc->lc_class : LOGIN_DEFCLASS);
465	} else {
466	    if (setpriority(PRIO_PROCESS, 0, (int)p) != 0)
467		syslog(LOG_WARNING, "setpriority '%s' (%s): %m",
468		    pwd ? pwd->pw_name : "-",
469		    lc ? lc->lc_class : LOGIN_DEFCLASS);
470	}
471    }
472
473    /* Setup the user's group permissions */
474    if (flags & LOGIN_SETGROUP) {
475	if (setgid(pwd->pw_gid) != 0) {
476	    syslog(LOG_ERR, "setgid(%lu): %m", (u_long)pwd->pw_gid);
477	    login_close(llc);
478	    return (-1);
479	}
480	if (initgroups(pwd->pw_name, pwd->pw_gid) == -1) {
481	    syslog(LOG_ERR, "initgroups(%s,%lu): %m", pwd->pw_name,
482		   (u_long)pwd->pw_gid);
483	    login_close(llc);
484	    return (-1);
485	}
486    }
487
488    /* Set up the user's MAC label. */
489    if ((flags & LOGIN_SETMAC) && mac_is_present(NULL) == 1) {
490	const char *label_string;
491	mac_t label;
492
493	label_string = login_getcapstr(lc, "label", NULL, NULL);
494	if (label_string != NULL) {
495	    if (mac_from_text(&label, label_string) == -1) {
496		syslog(LOG_ERR, "mac_from_text('%s') for %s: %m",
497		    pwd->pw_name, label_string);
498		return (-1);
499	    }
500	    if (mac_set_proc(label) == -1)
501		error = errno;
502	    else
503		error = 0;
504	    mac_free(label);
505	    if (error != 0) {
506		syslog(LOG_ERR, "mac_set_proc('%s') for %s: %s",
507		    label_string, pwd->pw_name, strerror(error));
508		return (-1);
509	    }
510	}
511    }
512
513    /* Set the sessions login */
514    if ((flags & LOGIN_SETLOGIN) && setlogin(pwd->pw_name) != 0) {
515	syslog(LOG_ERR, "setlogin(%s): %m", pwd->pw_name);
516	login_close(llc);
517	return (-1);
518    }
519
520    /* Inform the kernel about current login class */
521    if (lc != NULL && lc->lc_class != NULL && (flags & LOGIN_SETLOGINCLASS)) {
522	/*
523	 * XXX: This is a workaround to fail gracefully in case the kernel
524	 *      does not support setloginclass(2).
525	 */
526	bzero(&sa, sizeof(sa));
527	sa.sa_handler = SIG_IGN;
528	sigfillset(&sa.sa_mask);
529	sigaction(SIGSYS, &sa, &prevsa);
530	error = setloginclass(lc->lc_class);
531	sigaction(SIGSYS, &prevsa, NULL);
532	if (error != 0) {
533	    syslog(LOG_ERR, "setloginclass(%s): %m", lc->lc_class);
534#ifdef notyet
535	    login_close(llc);
536	    return (-1);
537#endif
538	}
539    }
540
541    mymask = (flags & LOGIN_SETUMASK) ? umask(LOGIN_DEFUMASK) : 0;
542    mymask = setlogincontext(lc, pwd, mymask, flags);
543    login_close(llc);
544
545    /* This needs to be done after anything that needs root privs */
546    if ((flags & LOGIN_SETUSER) && setuid(uid) != 0) {
547	syslog(LOG_ERR, "setuid(%lu): %m", (u_long)uid);
548	return (-1);	/* Paranoia again */
549    }
550
551    /*
552     * Now, we repeat some of the above for the user's private entries
553     */
554    if (getuid() == uid && (lc = login_getuserclass(pwd)) != NULL) {
555	mymask = setlogincontext(lc, pwd, mymask, flags);
556	login_close(lc);
557    }
558
559    /* Finally, set any umask we've found */
560    if (flags & LOGIN_SETUMASK)
561	umask(mymask);
562
563    return (0);
564}
565