121288Sdavidn/*-
221288Sdavidn * Copyright (c) 1996 by
321288Sdavidn * Sean Eric Fagan <sef@kithrup.com>
421288Sdavidn * David Nugent <davidn@blaze.net.au>
521288Sdavidn * All rights reserved.
621288Sdavidn *
721288Sdavidn * Redistribution and use in source and binary forms, with or without
821288Sdavidn * modification, is permitted provided that the following conditions
921288Sdavidn * are met:
1021288Sdavidn * 1. Redistributions of source code must retain the above copyright
1121288Sdavidn *    notice immediately at the beginning of the file, without modification,
1221288Sdavidn *    this list of conditions, and the following disclaimer.
1321288Sdavidn * 2. Redistributions in binary form must reproduce the above copyright
1421288Sdavidn *    notice, this list of conditions and the following disclaimer in the
1521288Sdavidn *    documentation and/or other materials provided with the distribution.
1621288Sdavidn * 3. This work was done expressly for inclusion into FreeBSD.  Other use
1721288Sdavidn *    is permitted provided this notation is included.
1821288Sdavidn * 4. Absolutely no warranty of function or purpose is made by the authors.
1921288Sdavidn * 5. Modifications may be freely made to this file providing the above
2021288Sdavidn *    conditions are met.
2121288Sdavidn *
2221288Sdavidn * High-level routines relating to use of the user capabilities database
2321288Sdavidn */
2421288Sdavidn
2584225Sdillon#include <sys/cdefs.h>
2684225Sdillon__FBSDID("$FreeBSD: releng/10.2/lib/libutil/login_class.c 273931 2014-11-01 13:45:01Z dim $");
2784225Sdillon
28180815Sbrooks#include <sys/param.h>
29180815Sbrooks#include <sys/cpuset.h>
30116344Smarkm#include <sys/mac.h>
31184081Sdes#include <sys/resource.h>
32116344Smarkm#include <sys/rtprio.h>
33184081Sdes#include <sys/stat.h>
34184081Sdes#include <sys/time.h>
35184081Sdes
36184081Sdes#include <ctype.h>
37184081Sdes#include <err.h>
38116344Smarkm#include <errno.h>
3921288Sdavidn#include <fcntl.h>
40116344Smarkm#include <login_cap.h>
41116344Smarkm#include <paths.h>
4221288Sdavidn#include <pwd.h>
43219304Strasz#include <signal.h>
44116344Smarkm#include <stdio.h>
45116344Smarkm#include <stdlib.h>
46116344Smarkm#include <string.h>
4721288Sdavidn#include <syslog.h>
48116344Smarkm#include <unistd.h>
4921288Sdavidn
5021288Sdavidn
5121288Sdavidnstatic struct login_res {
5225670Sdavidn    const char *what;
5325670Sdavidn    rlim_t (*who)(login_cap_t *, const char *, rlim_t, rlim_t);
5425670Sdavidn    int why;
5521288Sdavidn} resources[] = {
56181905Sed    { "cputime",         login_getcaptime, RLIMIT_CPU     },
57181905Sed    { "filesize",        login_getcapsize, RLIMIT_FSIZE   },
58181905Sed    { "datasize",        login_getcapsize, RLIMIT_DATA    },
59181905Sed    { "stacksize",       login_getcapsize, RLIMIT_STACK   },
60181905Sed    { "memoryuse",       login_getcapsize, RLIMIT_RSS     },
61181905Sed    { "memorylocked",    login_getcapsize, RLIMIT_MEMLOCK },
62181905Sed    { "maxproc",         login_getcapnum,  RLIMIT_NPROC   },
63181905Sed    { "openfiles",       login_getcapnum,  RLIMIT_NOFILE  },
64181905Sed    { "coredumpsize",    login_getcapsize, RLIMIT_CORE    },
65181905Sed    { "sbsize",          login_getcapsize, RLIMIT_SBSIZE  },
66181905Sed    { "vmemoryuse",      login_getcapsize, RLIMIT_VMEM    },
67181905Sed    { "pseudoterminals", login_getcapnum,  RLIMIT_NPTS    },
68194767Skib    { "swapuse",         login_getcapsize, RLIMIT_SWAP    },
69181905Sed    { NULL,              0,                0              }
7021288Sdavidn};
7121288Sdavidn
7221288Sdavidn
7321288Sdavidnvoid
7421288Sdavidnsetclassresources(login_cap_t *lc)
7521288Sdavidn{
7625670Sdavidn    struct login_res *lr;
7721288Sdavidn
7825670Sdavidn    if (lc == NULL)
7925670Sdavidn	return;
8021402Sdavidn
8125670Sdavidn    for (lr = resources; lr->what != NULL; ++lr) {
8225670Sdavidn	struct rlimit	rlim;
8321288Sdavidn
8425670Sdavidn	/*
8525670Sdavidn	 * The login.conf file can have <limit>, <limit>-max, and
8625670Sdavidn	 * <limit>-cur entries.
8725670Sdavidn	 * What we do is get the current current- and maximum- limits.
8825670Sdavidn	 * Then, we try to get an entry for <limit> from the capability,
8925670Sdavidn	 * using the current and max limits we just got as the
9025670Sdavidn	 * default/error values.
9125670Sdavidn	 * *Then*, we try looking for <limit>-cur and <limit>-max,
9225670Sdavidn	 * again using the appropriate values as the default/error
9325670Sdavidn	 * conditions.
9425670Sdavidn	 */
9521288Sdavidn
9625670Sdavidn	if (getrlimit(lr->why, &rlim) != 0)
9725670Sdavidn	    syslog(LOG_ERR, "getting %s resource limit: %m", lr->what);
9825670Sdavidn	else {
99184081Sdes	    char	name_cur[40];
10025670Sdavidn	    char	name_max[40];
10125670Sdavidn	    rlim_t	rcur = rlim.rlim_cur;
10225670Sdavidn	    rlim_t	rmax = rlim.rlim_max;
10321288Sdavidn
10425670Sdavidn	    sprintf(name_cur, "%s-cur", lr->what);
10525670Sdavidn	    sprintf(name_max, "%s-max", lr->what);
10621288Sdavidn
10725670Sdavidn	    rcur = (*lr->who)(lc, lr->what, rcur, rcur);
10825670Sdavidn	    rmax = (*lr->who)(lc, lr->what, rmax, rmax);
10925670Sdavidn	    rlim.rlim_cur = (*lr->who)(lc, name_cur, rcur, rcur);
11025670Sdavidn	    rlim.rlim_max = (*lr->who)(lc, name_max, rmax, rmax);
111184081Sdes
11225670Sdavidn	    if (setrlimit(lr->why, &rlim) == -1)
11325670Sdavidn		syslog(LOG_WARNING, "set class '%s' resource limit %s: %m", lc->lc_class, lr->what);
11425670Sdavidn	}
11525670Sdavidn    }
11621288Sdavidn}
11721288Sdavidn
11825670Sdavidn
11925670Sdavidn
12021288Sdavidnstatic struct login_vars {
12125670Sdavidn    const char *tag;
12225670Sdavidn    const char *var;
12325670Sdavidn    const char *def;
12498977Sache    int overwrite;
12521288Sdavidn} pathvars[] = {
12698977Sache    { "path",           "PATH",       NULL, 1},
12798977Sache    { "cdpath",         "CDPATH",     NULL, 1},
12898977Sache    { "manpath",        "MANPATH",    NULL, 1},
12998977Sache    { NULL,             NULL,         NULL, 0}
13021288Sdavidn}, envars[] = {
13198977Sache    { "lang",           "LANG",       NULL, 1},
13298977Sache    { "charset",        "MM_CHARSET", NULL, 1},
13398977Sache    { "timezone",       "TZ",         NULL, 1},
13498977Sache    { "term",           "TERM",       NULL, 0},
13598977Sache    { NULL,             NULL,         NULL, 0}
13621288Sdavidn};
13721288Sdavidn
13821288Sdavidnstatic char *
13994202Srusubstvar(const char * var, const struct passwd * pwd, int hlen, int pch, int nlen)
14021288Sdavidn{
14125670Sdavidn    char    *np = NULL;
14221288Sdavidn
14325670Sdavidn    if (var != NULL) {
14425670Sdavidn	int	tildes = 0;
14525670Sdavidn	int	dollas = 0;
14625670Sdavidn	char	*p;
147184633Sdes	const char *q;
14821288Sdavidn
14925670Sdavidn	if (pwd != NULL) {
150184633Sdes	    for (q = var; *q != '\0'; ++q) {
151184633Sdes		tildes += (*q == '~');
152184633Sdes		dollas += (*q == '$');
153184633Sdes	    }
15425670Sdavidn	}
15521288Sdavidn
15625670Sdavidn	np = malloc(strlen(var) + (dollas * nlen)
15725670Sdavidn		    - dollas + (tildes * (pch+hlen))
15825670Sdavidn		    - tildes + 1);
15921288Sdavidn
16025670Sdavidn	if (np != NULL) {
16125670Sdavidn	    p = strcpy(np, var);
16221288Sdavidn
16325670Sdavidn	    if (pwd != NULL) {
16425670Sdavidn		/*
16525670Sdavidn		 * This loop does user username and homedir substitutions
16625670Sdavidn		 * for unescaped $ (username) and ~ (homedir)
16725670Sdavidn		 */
16825670Sdavidn		while (*(p += strcspn(p, "~$")) != '\0') {
16925670Sdavidn		    int	l = strlen(p);
17021288Sdavidn
17147118Sache		    if (p > np && *(p-1) == '\\')  /* Escaped: */
17225670Sdavidn			memmove(p - 1, p, l + 1); /* Slide-out the backslash */
17325670Sdavidn		    else if (*p == '~') {
17425670Sdavidn			int	v = pch && *(p+1) != '/'; /* Avoid double // */
17525670Sdavidn			memmove(p + hlen + v, p + 1, l);  /* Subst homedir */
17625670Sdavidn			memmove(p, pwd->pw_dir, hlen);
17725670Sdavidn			if (v)
17825670Sdavidn			    p[hlen] = '/';
17925670Sdavidn			p += hlen + v;
18025670Sdavidn		    }
18125670Sdavidn		    else /* if (*p == '$') */ {
18225670Sdavidn			memmove(p + nlen, p + 1, l);	/* Subst username */
18325670Sdavidn			memmove(p, pwd->pw_name, nlen);
18425670Sdavidn			p += nlen;
18525670Sdavidn		    }
18625670Sdavidn		}
18725670Sdavidn	    }
18821288Sdavidn	}
18921288Sdavidn    }
19025670Sdavidn
191184082Sdes    return (np);
19221288Sdavidn}
19321288Sdavidn
19421288Sdavidn
19521288Sdavidnvoid
19621288Sdavidnsetclassenvironment(login_cap_t *lc, const struct passwd * pwd, int paths)
19721288Sdavidn{
19825670Sdavidn    struct login_vars	*vars = paths ? pathvars : envars;
19925670Sdavidn    int			hlen = pwd ? strlen(pwd->pw_dir) : 0;
20025670Sdavidn    int			nlen = pwd ? strlen(pwd->pw_name) : 0;
20125670Sdavidn    char pch = 0;
20221288Sdavidn
20325670Sdavidn    if (hlen && pwd->pw_dir[hlen-1] != '/')
20425670Sdavidn	++pch;
20521288Sdavidn
20625670Sdavidn    while (vars->tag != NULL) {
20794202Sru	const char * var = paths ? login_getpath(lc, vars->tag, NULL)
20894202Sru				 : login_getcapstr(lc, vars->tag, NULL, NULL);
20921288Sdavidn
21025670Sdavidn	char * np  = substvar(var, pwd, hlen, pch, nlen);
21121288Sdavidn
21225670Sdavidn	if (np != NULL) {
21398977Sache	    setenv(vars->var, np, vars->overwrite);
21425670Sdavidn	    free(np);
21525670Sdavidn	} else if (vars->def != NULL) {
21625670Sdavidn	    setenv(vars->var, vars->def, 0);
21725670Sdavidn	}
21825670Sdavidn	++vars;
21921288Sdavidn    }
22021288Sdavidn
22125670Sdavidn    /*
22225670Sdavidn     * If we're not processing paths, then see if there is a setenv list by
22325670Sdavidn     * which the admin and/or user may set an arbitrary set of env vars.
22425670Sdavidn     */
22525670Sdavidn    if (!paths) {
226121193Smarkm	const char	**set_env = login_getcaplist(lc, "setenv", ",");
22721402Sdavidn
22825670Sdavidn	if (set_env != NULL) {
22925670Sdavidn	    while (*set_env != NULL) {
23025670Sdavidn		char	*p = strchr(*set_env, '=');
23121402Sdavidn
23225670Sdavidn		if (p != NULL) {  /* Discard invalid entries */
23325670Sdavidn		    char	*np;
23425670Sdavidn
23525670Sdavidn		    *p++ = '\0';
23625670Sdavidn		    if ((np = substvar(p, pwd, hlen, pch, nlen)) != NULL) {
23725670Sdavidn			setenv(*set_env, np, 1);
23825670Sdavidn			free(np);
23925670Sdavidn		    }
24025670Sdavidn		}
24125670Sdavidn		++set_env;
24225670Sdavidn	    }
24321288Sdavidn	}
24421288Sdavidn    }
24521288Sdavidn}
24621288Sdavidn
24721288Sdavidn
248180815Sbrooksstatic int
249180815Sbrookslist2cpuset(const char *list, cpuset_t *mask)
250180815Sbrooks{
251180815Sbrooks	enum { NONE, NUM, DASH } state;
252180815Sbrooks	int lastnum;
253180815Sbrooks	int curnum;
254180815Sbrooks	const char *l;
255180815Sbrooks
256180815Sbrooks	state = NONE;
257180815Sbrooks	curnum = lastnum = 0;
258180815Sbrooks	for (l = list; *l != '\0';) {
259180815Sbrooks		if (isdigit(*l)) {
260180815Sbrooks			curnum = atoi(l);
261180815Sbrooks			if (curnum > CPU_SETSIZE)
262180815Sbrooks				errx(EXIT_FAILURE,
263180815Sbrooks				    "Only %d cpus supported", CPU_SETSIZE);
264180815Sbrooks			while (isdigit(*l))
265180815Sbrooks				l++;
266180815Sbrooks			switch (state) {
267180815Sbrooks			case NONE:
268180815Sbrooks				lastnum = curnum;
269180815Sbrooks				state = NUM;
270180815Sbrooks				break;
271180815Sbrooks			case DASH:
272180815Sbrooks				for (; lastnum <= curnum; lastnum++)
273180815Sbrooks					CPU_SET(lastnum, mask);
274180815Sbrooks				state = NONE;
275180815Sbrooks				break;
276180815Sbrooks			case NUM:
277180815Sbrooks			default:
278180815Sbrooks				return (0);
279180815Sbrooks			}
280180815Sbrooks			continue;
281180815Sbrooks		}
282180815Sbrooks		switch (*l) {
283180815Sbrooks		case ',':
284180815Sbrooks			switch (state) {
285180815Sbrooks			case NONE:
286180815Sbrooks				break;
287180815Sbrooks			case NUM:
288180815Sbrooks				CPU_SET(curnum, mask);
289180815Sbrooks				state = NONE;
290180815Sbrooks				break;
291180815Sbrooks			case DASH:
292180815Sbrooks				return (0);
293180815Sbrooks				break;
294180815Sbrooks			}
295180815Sbrooks			break;
296180815Sbrooks		case '-':
297180815Sbrooks			if (state != NUM)
298180815Sbrooks				return (0);
299180815Sbrooks			state = DASH;
300180815Sbrooks			break;
301180815Sbrooks		default:
302180815Sbrooks			return (0);
303180815Sbrooks		}
304180815Sbrooks		l++;
305180815Sbrooks	}
306180815Sbrooks	switch (state) {
307180815Sbrooks		case NONE:
308180815Sbrooks			break;
309180815Sbrooks		case NUM:
310180815Sbrooks			CPU_SET(curnum, mask);
311180815Sbrooks			break;
312180815Sbrooks		case DASH:
313180815Sbrooks			return (0);
314180815Sbrooks	}
315184082Sdes	return (1);
316180815Sbrooks}
317180815Sbrooks
318180815Sbrooks
319180815Sbrooksvoid
320180815Sbrookssetclasscpumask(login_cap_t *lc)
321180815Sbrooks{
322180815Sbrooks	const char *maskstr;
323180815Sbrooks	cpuset_t maskset;
324180815Sbrooks	cpusetid_t setid;
325180815Sbrooks
326180815Sbrooks	maskstr = login_getcapstr(lc, "cpumask", NULL, NULL);
327180815Sbrooks	CPU_ZERO(&maskset);
328180815Sbrooks	if (maskstr == NULL)
329180815Sbrooks		return;
330180815Sbrooks	if (strcasecmp("default", maskstr) == 0)
331180815Sbrooks		return;
332180815Sbrooks	if (!list2cpuset(maskstr, &maskset)) {
333180815Sbrooks		syslog(LOG_WARNING,
334180815Sbrooks		    "list2cpuset(%s) invalid mask specification", maskstr);
335180815Sbrooks		return;
336180815Sbrooks	}
337180815Sbrooks
338180815Sbrooks	if (cpuset(&setid) != 0) {
339180815Sbrooks		syslog(LOG_ERR, "cpuset(): %s", strerror(errno));
340180815Sbrooks		return;
341180815Sbrooks	}
342180815Sbrooks
343180815Sbrooks	if (cpuset_setaffinity(CPU_LEVEL_CPUSET, CPU_WHICH_PID, -1,
344180815Sbrooks	    sizeof(maskset), &maskset) != 0)
345180815Sbrooks		syslog(LOG_ERR, "cpuset_setaffinity(%s): %s", maskstr,
346180815Sbrooks		    strerror(errno));
347180815Sbrooks}
348180815Sbrooks
349180815Sbrooks
35021288Sdavidn/*
35121288Sdavidn * setclasscontext()
35221288Sdavidn *
35321288Sdavidn * For the login class <class>, set various class context values
35421288Sdavidn * (limits, mainly) to the values for that class.  Which values are
35521288Sdavidn * set are controlled by <flags> -- see <login_class.h> for the
35621288Sdavidn * possible values.
35721288Sdavidn *
35821288Sdavidn * setclasscontext() can only set resources, priority, and umask.
35921288Sdavidn */
36021288Sdavidn
36121288Sdavidnint
36221288Sdavidnsetclasscontext(const char *classname, unsigned int flags)
36321288Sdavidn{
36425670Sdavidn    int		rc;
36525670Sdavidn    login_cap_t *lc;
36625670Sdavidn
36725670Sdavidn    lc = login_getclassbyname(classname, NULL);
36825670Sdavidn
36925670Sdavidn    flags &= LOGIN_SETRESOURCES | LOGIN_SETPRIORITY |
37025670Sdavidn	    LOGIN_SETUMASK | LOGIN_SETPATH;
37125670Sdavidn
37225670Sdavidn    rc = lc ? setusercontext(lc, NULL, 0, flags) : -1;
37325670Sdavidn    login_close(lc);
374184082Sdes    return (rc);
37521288Sdavidn}
37621288Sdavidn
37721288Sdavidn
37825670Sdavidn
37921288Sdavidn/*
380169189Syar * Private function which takes care of processing
38125670Sdavidn */
38225670Sdavidn
38325670Sdavidnstatic mode_t
38425670Sdavidnsetlogincontext(login_cap_t *lc, const struct passwd *pwd,
38525670Sdavidn		mode_t mymask, unsigned long flags)
38625670Sdavidn{
38725670Sdavidn    if (lc) {
38825670Sdavidn	/* Set resources */
38925670Sdavidn	if (flags & LOGIN_SETRESOURCES)
39025670Sdavidn	    setclassresources(lc);
39125670Sdavidn	/* See if there's a umask override */
39225670Sdavidn	if (flags & LOGIN_SETUMASK)
39325670Sdavidn	    mymask = (mode_t)login_getcapnum(lc, "umask", mymask, mymask);
39425670Sdavidn	/* Set paths */
39525670Sdavidn	if (flags & LOGIN_SETPATH)
39625670Sdavidn	    setclassenvironment(lc, pwd, 1);
39725670Sdavidn	/* Set environment */
39825670Sdavidn	if (flags & LOGIN_SETENV)
39925670Sdavidn	    setclassenvironment(lc, pwd, 0);
400180815Sbrooks	/* Set cpu affinity */
401180815Sbrooks	if (flags & LOGIN_SETCPUMASK)
402180815Sbrooks	    setclasscpumask(lc);
40325670Sdavidn    }
404184082Sdes    return (mymask);
40525670Sdavidn}
40625670Sdavidn
40725670Sdavidn
40825670Sdavidn
40925670Sdavidn/*
41021288Sdavidn * setusercontext()
41121288Sdavidn *
41221288Sdavidn * Given a login class <lc> and a user in <pwd>, with a uid <uid>,
41321288Sdavidn * set the context as in setclasscontext().  <flags> controls which
41421288Sdavidn * values are set.
41521288Sdavidn *
41621288Sdavidn * The difference between setclasscontext() and setusercontext() is
41721288Sdavidn * that the former sets things up for an already-existing process,
41821288Sdavidn * while the latter sets things up from a root context.  Such as might
41921288Sdavidn * be called from login(1).
42021288Sdavidn *
42121288Sdavidn */
42221288Sdavidn
42321288Sdavidnint
42421288Sdavidnsetusercontext(login_cap_t *lc, const struct passwd *pwd, uid_t uid, unsigned int flags)
42521288Sdavidn{
426273931Sdim    rlim_t	p;
42725670Sdavidn    mode_t	mymask;
42825670Sdavidn    login_cap_t *llc = NULL;
429219304Strasz    struct sigaction sa, prevsa;
43036351Ssteve    struct rtprio rtp;
431105757Srwatson    int error;
43221288Sdavidn
43325670Sdavidn    if (lc == NULL) {
43425670Sdavidn	if (pwd != NULL && (lc = login_getpwclass(pwd)) != NULL)
43525670Sdavidn	    llc = lc; /* free this when we're done */
43625670Sdavidn    }
43721288Sdavidn
43825670Sdavidn    if (flags & LOGIN_SETPATH)
43925670Sdavidn	pathvars[0].def = uid ? _PATH_DEFPATH : _PATH_STDPATH;
44021288Sdavidn
44125670Sdavidn    /* we need a passwd entry to set these */
44225670Sdavidn    if (pwd == NULL)
443106831Srwatson	flags &= ~(LOGIN_SETGROUP | LOGIN_SETLOGIN | LOGIN_SETMAC);
44421288Sdavidn
44525670Sdavidn    /* Set the process priority */
44625670Sdavidn    if (flags & LOGIN_SETPRIORITY) {
44725670Sdavidn	p = login_getcapnum(lc, "priority", LOGIN_DEFPRI, LOGIN_DEFPRI);
44821288Sdavidn
449169189Syar	if (p > PRIO_MAX) {
45036351Ssteve	    rtp.type = RTP_PRIO_IDLE;
451273931Sdim	    p -= PRIO_MAX + 1;
452273931Sdim	    rtp.prio = p > RTP_PRIO_MAX ? RTP_PRIO_MAX : p;
453169189Syar	    if (rtprio(RTP_SET, 0, &rtp))
45436351Ssteve		syslog(LOG_WARNING, "rtprio '%s' (%s): %m",
455231306Seadler		    pwd ? pwd->pw_name : "-",
456231306Seadler		    lc ? lc->lc_class : LOGIN_DEFCLASS);
457169189Syar	} else if (p < PRIO_MIN) {
45836351Ssteve	    rtp.type = RTP_PRIO_REALTIME;
459273931Sdim	    p -= PRIO_MIN - RTP_PRIO_MAX;
460273931Sdim	    rtp.prio = p < RTP_PRIO_MIN ? RTP_PRIO_MIN : p;
461169189Syar	    if (rtprio(RTP_SET, 0, &rtp))
46236351Ssteve		syslog(LOG_WARNING, "rtprio '%s' (%s): %m",
463231306Seadler		    pwd ? pwd->pw_name : "-",
464231306Seadler		    lc ? lc->lc_class : LOGIN_DEFCLASS);
46536351Ssteve	} else {
46636351Ssteve	    if (setpriority(PRIO_PROCESS, 0, (int)p) != 0)
46736351Ssteve		syslog(LOG_WARNING, "setpriority '%s' (%s): %m",
468231306Seadler		    pwd ? pwd->pw_name : "-",
469231306Seadler		    lc ? lc->lc_class : LOGIN_DEFCLASS);
47036351Ssteve	}
47125670Sdavidn    }
47221288Sdavidn
47325670Sdavidn    /* Setup the user's group permissions */
47425670Sdavidn    if (flags & LOGIN_SETGROUP) {
47525670Sdavidn	if (setgid(pwd->pw_gid) != 0) {
47637947Sache	    syslog(LOG_ERR, "setgid(%lu): %m", (u_long)pwd->pw_gid);
47725670Sdavidn	    login_close(llc);
478184082Sdes	    return (-1);
47925670Sdavidn	}
48025670Sdavidn	if (initgroups(pwd->pw_name, pwd->pw_gid) == -1) {
48137947Sache	    syslog(LOG_ERR, "initgroups(%s,%lu): %m", pwd->pw_name,
48237947Sache		   (u_long)pwd->pw_gid);
48325670Sdavidn	    login_close(llc);
484184082Sdes	    return (-1);
48525670Sdavidn	}
48625670Sdavidn    }
48721288Sdavidn
488105757Srwatson    /* Set up the user's MAC label. */
489105757Srwatson    if ((flags & LOGIN_SETMAC) && mac_is_present(NULL) == 1) {
490105757Srwatson	const char *label_string;
491105757Srwatson	mac_t label;
492105757Srwatson
493105757Srwatson	label_string = login_getcapstr(lc, "label", NULL, NULL);
494105757Srwatson	if (label_string != NULL) {
495105757Srwatson	    if (mac_from_text(&label, label_string) == -1) {
496105757Srwatson		syslog(LOG_ERR, "mac_from_text('%s') for %s: %m",
497105757Srwatson		    pwd->pw_name, label_string);
498184082Sdes		return (-1);
499105757Srwatson	    }
500105757Srwatson	    if (mac_set_proc(label) == -1)
501105757Srwatson		error = errno;
502105757Srwatson	    else
503105757Srwatson		error = 0;
504105757Srwatson	    mac_free(label);
505105757Srwatson	    if (error != 0) {
506105757Srwatson		syslog(LOG_ERR, "mac_set_proc('%s') for %s: %s",
507105757Srwatson		    label_string, pwd->pw_name, strerror(error));
508184082Sdes		return (-1);
509105757Srwatson	    }
510105757Srwatson	}
511105757Srwatson    }
512105757Srwatson
51325670Sdavidn    /* Set the sessions login */
51425670Sdavidn    if ((flags & LOGIN_SETLOGIN) && setlogin(pwd->pw_name) != 0) {
51525670Sdavidn	syslog(LOG_ERR, "setlogin(%s): %m", pwd->pw_name);
51625670Sdavidn	login_close(llc);
517184082Sdes	return (-1);
51825670Sdavidn    }
51921288Sdavidn
520219304Strasz    /* Inform the kernel about current login class */
521219304Strasz    if (lc != NULL && lc->lc_class != NULL && (flags & LOGIN_SETLOGINCLASS)) {
522219304Strasz	/*
523219304Strasz	 * XXX: This is a workaround to fail gracefully in case the kernel
524219304Strasz	 *      does not support setloginclass(2).
525219304Strasz	 */
526219304Strasz	bzero(&sa, sizeof(sa));
527219304Strasz	sa.sa_handler = SIG_IGN;
528219304Strasz	sigfillset(&sa.sa_mask);
529219304Strasz	sigaction(SIGSYS, &sa, &prevsa);
530219304Strasz	error = setloginclass(lc->lc_class);
531219304Strasz	sigaction(SIGSYS, &prevsa, NULL);
532219304Strasz	if (error != 0) {
533219304Strasz	    syslog(LOG_ERR, "setloginclass(%s): %m", lc->lc_class);
534219304Strasz#ifdef notyet
535219304Strasz	    login_close(llc);
536219304Strasz	    return (-1);
537219304Strasz#endif
538219304Strasz	}
539219304Strasz    }
540219304Strasz
54125670Sdavidn    mymask = (flags & LOGIN_SETUMASK) ? umask(LOGIN_DEFUMASK) : 0;
54225670Sdavidn    mymask = setlogincontext(lc, pwd, mymask, flags);
54325670Sdavidn    login_close(llc);
54421288Sdavidn
54525670Sdavidn    /* This needs to be done after anything that needs root privs */
54625670Sdavidn    if ((flags & LOGIN_SETUSER) && setuid(uid) != 0) {
54737947Sache	syslog(LOG_ERR, "setuid(%lu): %m", (u_long)uid);
548184082Sdes	return (-1);	/* Paranoia again */
54921288Sdavidn    }
55021288Sdavidn
55125670Sdavidn    /*
55225670Sdavidn     * Now, we repeat some of the above for the user's private entries
55325670Sdavidn     */
554211393Sdes    if (getuid() == uid && (lc = login_getuserclass(pwd)) != NULL) {
55525670Sdavidn	mymask = setlogincontext(lc, pwd, mymask, flags);
55625670Sdavidn	login_close(lc);
55725670Sdavidn    }
55821288Sdavidn
55925670Sdavidn    /* Finally, set any umask we've found */
56025670Sdavidn    if (flags & LOGIN_SETUMASK)
56125670Sdavidn	umask(mymask);
56221288Sdavidn
563184082Sdes    return (0);
56421288Sdavidn}
565