login_class.c revision 184081
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: head/lib/libutil/login_class.c 184081 2008-10-20 16:48:18Z des $");
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>
43116344Smarkm#include <stdio.h>
44116344Smarkm#include <stdlib.h>
45116344Smarkm#include <string.h>
4621288Sdavidn#include <syslog.h>
47116344Smarkm#include <unistd.h>
4821288Sdavidn
4921288Sdavidn
5021288Sdavidnstatic struct login_res {
5125670Sdavidn    const char *what;
5225670Sdavidn    rlim_t (*who)(login_cap_t *, const char *, rlim_t, rlim_t);
5325670Sdavidn    int why;
5421288Sdavidn} resources[] = {
55181905Sed    { "cputime",         login_getcaptime, RLIMIT_CPU     },
56181905Sed    { "filesize",        login_getcapsize, RLIMIT_FSIZE   },
57181905Sed    { "datasize",        login_getcapsize, RLIMIT_DATA    },
58181905Sed    { "stacksize",       login_getcapsize, RLIMIT_STACK   },
59181905Sed    { "memoryuse",       login_getcapsize, RLIMIT_RSS     },
60181905Sed    { "memorylocked",    login_getcapsize, RLIMIT_MEMLOCK },
61181905Sed    { "maxproc",         login_getcapnum,  RLIMIT_NPROC   },
62181905Sed    { "openfiles",       login_getcapnum,  RLIMIT_NOFILE  },
63181905Sed    { "coredumpsize",    login_getcapsize, RLIMIT_CORE    },
64181905Sed    { "sbsize",          login_getcapsize, RLIMIT_SBSIZE  },
65181905Sed    { "vmemoryuse",      login_getcapsize, RLIMIT_VMEM    },
66181905Sed    { "pseudoterminals", login_getcapnum,  RLIMIT_NPTS    },
67181905Sed    { NULL,              0,                0              }
6821288Sdavidn};
6921288Sdavidn
7021288Sdavidn
7121288Sdavidnvoid
7221288Sdavidnsetclassresources(login_cap_t *lc)
7321288Sdavidn{
7425670Sdavidn    struct login_res *lr;
7521288Sdavidn
7625670Sdavidn    if (lc == NULL)
7725670Sdavidn	return;
7821402Sdavidn
7925670Sdavidn    for (lr = resources; lr->what != NULL; ++lr) {
8025670Sdavidn	struct rlimit	rlim;
8121288Sdavidn
8225670Sdavidn	/*
8325670Sdavidn	 * The login.conf file can have <limit>, <limit>-max, and
8425670Sdavidn	 * <limit>-cur entries.
8525670Sdavidn	 * What we do is get the current current- and maximum- limits.
8625670Sdavidn	 * Then, we try to get an entry for <limit> from the capability,
8725670Sdavidn	 * using the current and max limits we just got as the
8825670Sdavidn	 * default/error values.
8925670Sdavidn	 * *Then*, we try looking for <limit>-cur and <limit>-max,
9025670Sdavidn	 * again using the appropriate values as the default/error
9125670Sdavidn	 * conditions.
9225670Sdavidn	 */
9321288Sdavidn
9425670Sdavidn	if (getrlimit(lr->why, &rlim) != 0)
9525670Sdavidn	    syslog(LOG_ERR, "getting %s resource limit: %m", lr->what);
9625670Sdavidn	else {
97184081Sdes	    char	name_cur[40];
9825670Sdavidn	    char	name_max[40];
9925670Sdavidn	    rlim_t	rcur = rlim.rlim_cur;
10025670Sdavidn	    rlim_t	rmax = rlim.rlim_max;
10121288Sdavidn
10225670Sdavidn	    sprintf(name_cur, "%s-cur", lr->what);
10325670Sdavidn	    sprintf(name_max, "%s-max", lr->what);
10421288Sdavidn
10525670Sdavidn	    rcur = (*lr->who)(lc, lr->what, rcur, rcur);
10625670Sdavidn	    rmax = (*lr->who)(lc, lr->what, rmax, rmax);
10725670Sdavidn	    rlim.rlim_cur = (*lr->who)(lc, name_cur, rcur, rcur);
10825670Sdavidn	    rlim.rlim_max = (*lr->who)(lc, name_max, rmax, rmax);
109184081Sdes
11025670Sdavidn	    if (setrlimit(lr->why, &rlim) == -1)
11125670Sdavidn		syslog(LOG_WARNING, "set class '%s' resource limit %s: %m", lc->lc_class, lr->what);
11225670Sdavidn	}
11325670Sdavidn    }
11421288Sdavidn}
11521288Sdavidn
11625670Sdavidn
11725670Sdavidn
11821288Sdavidnstatic struct login_vars {
11925670Sdavidn    const char *tag;
12025670Sdavidn    const char *var;
12125670Sdavidn    const char *def;
12298977Sache    int overwrite;
12321288Sdavidn} pathvars[] = {
12498977Sache    { "path",           "PATH",       NULL, 1},
12598977Sache    { "cdpath",         "CDPATH",     NULL, 1},
12698977Sache    { "manpath",        "MANPATH",    NULL, 1},
12798977Sache    { NULL,             NULL,         NULL, 0}
12821288Sdavidn}, envars[] = {
12998977Sache    { "lang",           "LANG",       NULL, 1},
13098977Sache    { "charset",        "MM_CHARSET", NULL, 1},
13198977Sache    { "timezone",       "TZ",         NULL, 1},
13298977Sache    { "term",           "TERM",       NULL, 0},
13398977Sache    { NULL,             NULL,         NULL, 0}
13421288Sdavidn};
13521288Sdavidn
13621288Sdavidnstatic char *
13794202Srusubstvar(const char * var, const struct passwd * pwd, int hlen, int pch, int nlen)
13821288Sdavidn{
13925670Sdavidn    char    *np = NULL;
14021288Sdavidn
14125670Sdavidn    if (var != NULL) {
14225670Sdavidn	int	tildes = 0;
14325670Sdavidn	int	dollas = 0;
14425670Sdavidn	char	*p;
14521288Sdavidn
14625670Sdavidn	if (pwd != NULL) {
14725670Sdavidn	    /* Count the number of ~'s in var to substitute */
14894202Sru	    for (p = (char *)var; (p = strchr(p, '~')) != NULL; p++)
14925670Sdavidn		++tildes;
15025670Sdavidn	    /* Count the number of $'s in var to substitute */
15194202Sru	    for (p = (char *)var; (p = strchr(p, '$')) != NULL; p++)
15225670Sdavidn		++dollas;
15325670Sdavidn	}
15421288Sdavidn
15525670Sdavidn	np = malloc(strlen(var) + (dollas * nlen)
15625670Sdavidn		    - dollas + (tildes * (pch+hlen))
15725670Sdavidn		    - tildes + 1);
15821288Sdavidn
15925670Sdavidn	if (np != NULL) {
16025670Sdavidn	    p = strcpy(np, var);
16121288Sdavidn
16225670Sdavidn	    if (pwd != NULL) {
16325670Sdavidn		/*
16425670Sdavidn		 * This loop does user username and homedir substitutions
16525670Sdavidn		 * for unescaped $ (username) and ~ (homedir)
16625670Sdavidn		 */
16725670Sdavidn		while (*(p += strcspn(p, "~$")) != '\0') {
16825670Sdavidn		    int	l = strlen(p);
16921288Sdavidn
17047118Sache		    if (p > np && *(p-1) == '\\')  /* Escaped: */
17125670Sdavidn			memmove(p - 1, p, l + 1); /* Slide-out the backslash */
17225670Sdavidn		    else if (*p == '~') {
17325670Sdavidn			int	v = pch && *(p+1) != '/'; /* Avoid double // */
17425670Sdavidn			memmove(p + hlen + v, p + 1, l);  /* Subst homedir */
17525670Sdavidn			memmove(p, pwd->pw_dir, hlen);
17625670Sdavidn			if (v)
17725670Sdavidn			    p[hlen] = '/';
17825670Sdavidn			p += hlen + v;
17925670Sdavidn		    }
18025670Sdavidn		    else /* if (*p == '$') */ {
18125670Sdavidn			memmove(p + nlen, p + 1, l);	/* Subst username */
18225670Sdavidn			memmove(p, pwd->pw_name, nlen);
18325670Sdavidn			p += nlen;
18425670Sdavidn		    }
18525670Sdavidn		}
18625670Sdavidn	    }
18721288Sdavidn	}
18821288Sdavidn    }
18925670Sdavidn
19025670Sdavidn    return np;
19121288Sdavidn}
19221288Sdavidn
19321288Sdavidn
19421288Sdavidnvoid
19521288Sdavidnsetclassenvironment(login_cap_t *lc, const struct passwd * pwd, int paths)
19621288Sdavidn{
19725670Sdavidn    struct login_vars	*vars = paths ? pathvars : envars;
19825670Sdavidn    int			hlen = pwd ? strlen(pwd->pw_dir) : 0;
19925670Sdavidn    int			nlen = pwd ? strlen(pwd->pw_name) : 0;
20025670Sdavidn    char pch = 0;
20121288Sdavidn
20225670Sdavidn    if (hlen && pwd->pw_dir[hlen-1] != '/')
20325670Sdavidn	++pch;
20421288Sdavidn
20525670Sdavidn    while (vars->tag != NULL) {
20694202Sru	const char * var = paths ? login_getpath(lc, vars->tag, NULL)
20794202Sru				 : login_getcapstr(lc, vars->tag, NULL, NULL);
20821288Sdavidn
20925670Sdavidn	char * np  = substvar(var, pwd, hlen, pch, nlen);
21021288Sdavidn
21125670Sdavidn	if (np != NULL) {
21298977Sache	    setenv(vars->var, np, vars->overwrite);
21325670Sdavidn	    free(np);
21425670Sdavidn	} else if (vars->def != NULL) {
21525670Sdavidn	    setenv(vars->var, vars->def, 0);
21625670Sdavidn	}
21725670Sdavidn	++vars;
21821288Sdavidn    }
21921288Sdavidn
22025670Sdavidn    /*
22125670Sdavidn     * If we're not processing paths, then see if there is a setenv list by
22225670Sdavidn     * which the admin and/or user may set an arbitrary set of env vars.
22325670Sdavidn     */
22425670Sdavidn    if (!paths) {
225121193Smarkm	const char	**set_env = login_getcaplist(lc, "setenv", ",");
22621402Sdavidn
22725670Sdavidn	if (set_env != NULL) {
22825670Sdavidn	    while (*set_env != NULL) {
22925670Sdavidn		char	*p = strchr(*set_env, '=');
23021402Sdavidn
23125670Sdavidn		if (p != NULL) {  /* Discard invalid entries */
23225670Sdavidn		    char	*np;
23325670Sdavidn
23425670Sdavidn		    *p++ = '\0';
23525670Sdavidn		    if ((np = substvar(p, pwd, hlen, pch, nlen)) != NULL) {
23625670Sdavidn			setenv(*set_env, np, 1);
23725670Sdavidn			free(np);
23825670Sdavidn		    }
23925670Sdavidn		}
24025670Sdavidn		++set_env;
24125670Sdavidn	    }
24221288Sdavidn	}
24321288Sdavidn    }
24421288Sdavidn}
24521288Sdavidn
24621288Sdavidn
247180815Sbrooksstatic int
248180815Sbrookslist2cpuset(const char *list, cpuset_t *mask)
249180815Sbrooks{
250180815Sbrooks	enum { NONE, NUM, DASH } state;
251180815Sbrooks	int lastnum;
252180815Sbrooks	int curnum;
253180815Sbrooks	const char *l;
254180815Sbrooks
255180815Sbrooks	state = NONE;
256180815Sbrooks	curnum = lastnum = 0;
257180815Sbrooks	for (l = list; *l != '\0';) {
258180815Sbrooks		if (isdigit(*l)) {
259180815Sbrooks			curnum = atoi(l);
260180815Sbrooks			if (curnum > CPU_SETSIZE)
261180815Sbrooks				errx(EXIT_FAILURE,
262180815Sbrooks				    "Only %d cpus supported", CPU_SETSIZE);
263180815Sbrooks			while (isdigit(*l))
264180815Sbrooks				l++;
265180815Sbrooks			switch (state) {
266180815Sbrooks			case NONE:
267180815Sbrooks				lastnum = curnum;
268180815Sbrooks				state = NUM;
269180815Sbrooks				break;
270180815Sbrooks			case DASH:
271180815Sbrooks				for (; lastnum <= curnum; lastnum++)
272180815Sbrooks					CPU_SET(lastnum, mask);
273180815Sbrooks				state = NONE;
274180815Sbrooks				break;
275180815Sbrooks			case NUM:
276180815Sbrooks			default:
277180815Sbrooks				return (0);
278180815Sbrooks			}
279180815Sbrooks			continue;
280180815Sbrooks		}
281180815Sbrooks		switch (*l) {
282180815Sbrooks		case ',':
283180815Sbrooks			switch (state) {
284180815Sbrooks			case NONE:
285180815Sbrooks				break;
286180815Sbrooks			case NUM:
287180815Sbrooks				CPU_SET(curnum, mask);
288180815Sbrooks				state = NONE;
289180815Sbrooks				break;
290180815Sbrooks			case DASH:
291180815Sbrooks				return (0);
292180815Sbrooks				break;
293180815Sbrooks			}
294180815Sbrooks			break;
295180815Sbrooks		case '-':
296180815Sbrooks			if (state != NUM)
297180815Sbrooks				return (0);
298180815Sbrooks			state = DASH;
299180815Sbrooks			break;
300180815Sbrooks		default:
301180815Sbrooks			return (0);
302180815Sbrooks		}
303180815Sbrooks		l++;
304180815Sbrooks	}
305180815Sbrooks	switch (state) {
306180815Sbrooks		case NONE:
307180815Sbrooks			break;
308180815Sbrooks		case NUM:
309180815Sbrooks			CPU_SET(curnum, mask);
310180815Sbrooks			break;
311180815Sbrooks		case DASH:
312180815Sbrooks			return (0);
313180815Sbrooks	}
314180815Sbrooks	return 1;
315180815Sbrooks}
316180815Sbrooks
317180815Sbrooks
318180815Sbrooksvoid
319180815Sbrookssetclasscpumask(login_cap_t *lc)
320180815Sbrooks{
321180815Sbrooks	const char *maskstr;
322180815Sbrooks	cpuset_t maskset;
323180815Sbrooks	cpusetid_t setid;
324180815Sbrooks
325180815Sbrooks	maskstr = login_getcapstr(lc, "cpumask", NULL, NULL);
326180815Sbrooks	CPU_ZERO(&maskset);
327180815Sbrooks	if (maskstr == NULL)
328180815Sbrooks		return;
329180815Sbrooks	if (strcasecmp("default", maskstr) == 0)
330180815Sbrooks		return;
331180815Sbrooks	if (!list2cpuset(maskstr, &maskset)) {
332180815Sbrooks		syslog(LOG_WARNING,
333180815Sbrooks		    "list2cpuset(%s) invalid mask specification", maskstr);
334180815Sbrooks		return;
335180815Sbrooks	}
336180815Sbrooks
337180815Sbrooks	if (cpuset(&setid) != 0) {
338180815Sbrooks		syslog(LOG_ERR, "cpuset(): %s", strerror(errno));
339180815Sbrooks		return;
340180815Sbrooks	}
341180815Sbrooks
342180815Sbrooks	if (cpuset_setaffinity(CPU_LEVEL_CPUSET, CPU_WHICH_PID, -1,
343180815Sbrooks	    sizeof(maskset), &maskset) != 0)
344180815Sbrooks		syslog(LOG_ERR, "cpuset_setaffinity(%s): %s", maskstr,
345180815Sbrooks		    strerror(errno));
346180815Sbrooks}
347180815Sbrooks
348180815Sbrooks
34921288Sdavidn/*
35021288Sdavidn * setclasscontext()
35121288Sdavidn *
35221288Sdavidn * For the login class <class>, set various class context values
35321288Sdavidn * (limits, mainly) to the values for that class.  Which values are
35421288Sdavidn * set are controlled by <flags> -- see <login_class.h> for the
35521288Sdavidn * possible values.
35621288Sdavidn *
35721288Sdavidn * setclasscontext() can only set resources, priority, and umask.
35821288Sdavidn */
35921288Sdavidn
36021288Sdavidnint
36121288Sdavidnsetclasscontext(const char *classname, unsigned int flags)
36221288Sdavidn{
36325670Sdavidn    int		rc;
36425670Sdavidn    login_cap_t *lc;
36525670Sdavidn
36625670Sdavidn    lc = login_getclassbyname(classname, NULL);
36725670Sdavidn
36825670Sdavidn    flags &= LOGIN_SETRESOURCES | LOGIN_SETPRIORITY |
36925670Sdavidn	    LOGIN_SETUMASK | LOGIN_SETPATH;
37025670Sdavidn
37125670Sdavidn    rc = lc ? setusercontext(lc, NULL, 0, flags) : -1;
37225670Sdavidn    login_close(lc);
37325670Sdavidn    return rc;
37421288Sdavidn}
37521288Sdavidn
37621288Sdavidn
37725670Sdavidn
37821288Sdavidn/*
379169189Syar * Private function which takes care of processing
38025670Sdavidn */
38125670Sdavidn
38225670Sdavidnstatic mode_t
38325670Sdavidnsetlogincontext(login_cap_t *lc, const struct passwd *pwd,
38425670Sdavidn		mode_t mymask, unsigned long flags)
38525670Sdavidn{
38625670Sdavidn    if (lc) {
38725670Sdavidn	/* Set resources */
38825670Sdavidn	if (flags & LOGIN_SETRESOURCES)
38925670Sdavidn	    setclassresources(lc);
39025670Sdavidn	/* See if there's a umask override */
39125670Sdavidn	if (flags & LOGIN_SETUMASK)
39225670Sdavidn	    mymask = (mode_t)login_getcapnum(lc, "umask", mymask, mymask);
39325670Sdavidn	/* Set paths */
39425670Sdavidn	if (flags & LOGIN_SETPATH)
39525670Sdavidn	    setclassenvironment(lc, pwd, 1);
39625670Sdavidn	/* Set environment */
39725670Sdavidn	if (flags & LOGIN_SETENV)
39825670Sdavidn	    setclassenvironment(lc, pwd, 0);
399180815Sbrooks	/* Set cpu affinity */
400180815Sbrooks	if (flags & LOGIN_SETCPUMASK)
401180815Sbrooks	    setclasscpumask(lc);
40225670Sdavidn    }
40325670Sdavidn    return mymask;
40425670Sdavidn}
40525670Sdavidn
40625670Sdavidn
40725670Sdavidn
40825670Sdavidn/*
40921288Sdavidn * setusercontext()
41021288Sdavidn *
41121288Sdavidn * Given a login class <lc> and a user in <pwd>, with a uid <uid>,
41221288Sdavidn * set the context as in setclasscontext().  <flags> controls which
41321288Sdavidn * values are set.
41421288Sdavidn *
41521288Sdavidn * The difference between setclasscontext() and setusercontext() is
41621288Sdavidn * that the former sets things up for an already-existing process,
41721288Sdavidn * while the latter sets things up from a root context.  Such as might
41821288Sdavidn * be called from login(1).
41921288Sdavidn *
42021288Sdavidn */
42121288Sdavidn
42221288Sdavidnint
42321288Sdavidnsetusercontext(login_cap_t *lc, const struct passwd *pwd, uid_t uid, unsigned int flags)
42421288Sdavidn{
42525670Sdavidn    quad_t	p;
42625670Sdavidn    mode_t	mymask;
42725670Sdavidn    login_cap_t *llc = NULL;
42836351Ssteve    struct rtprio rtp;
429105757Srwatson    int error;
43021288Sdavidn
43125670Sdavidn    if (lc == NULL) {
43225670Sdavidn	if (pwd != NULL && (lc = login_getpwclass(pwd)) != NULL)
43325670Sdavidn	    llc = lc; /* free this when we're done */
43425670Sdavidn    }
43521288Sdavidn
43625670Sdavidn    if (flags & LOGIN_SETPATH)
43725670Sdavidn	pathvars[0].def = uid ? _PATH_DEFPATH : _PATH_STDPATH;
43821288Sdavidn
43925670Sdavidn    /* we need a passwd entry to set these */
44025670Sdavidn    if (pwd == NULL)
441106831Srwatson	flags &= ~(LOGIN_SETGROUP | LOGIN_SETLOGIN | LOGIN_SETMAC);
44221288Sdavidn
44325670Sdavidn    /* Set the process priority */
44425670Sdavidn    if (flags & LOGIN_SETPRIORITY) {
44525670Sdavidn	p = login_getcapnum(lc, "priority", LOGIN_DEFPRI, LOGIN_DEFPRI);
44621288Sdavidn
447169189Syar	if (p > PRIO_MAX) {
44836351Ssteve	    rtp.type = RTP_PRIO_IDLE;
44936351Ssteve	    rtp.prio = p - PRIO_MAX - 1;
45036351Ssteve	    p = (rtp.prio > RTP_PRIO_MAX) ? 31 : p;
451169189Syar	    if (rtprio(RTP_SET, 0, &rtp))
45236351Ssteve		syslog(LOG_WARNING, "rtprio '%s' (%s): %m",
45336351Ssteve		    pwd->pw_name, lc ? lc->lc_class : LOGIN_DEFCLASS);
454169189Syar	} else if (p < PRIO_MIN) {
45536351Ssteve	    rtp.type = RTP_PRIO_REALTIME;
45636351Ssteve	    rtp.prio = abs(p - PRIO_MIN + RTP_PRIO_MAX);
45736351Ssteve	    p = (rtp.prio > RTP_PRIO_MAX) ? 1 : p;
458169189Syar	    if (rtprio(RTP_SET, 0, &rtp))
45936351Ssteve		syslog(LOG_WARNING, "rtprio '%s' (%s): %m",
46036351Ssteve		    pwd->pw_name, lc ? lc->lc_class : LOGIN_DEFCLASS);
46136351Ssteve	} else {
46236351Ssteve	    if (setpriority(PRIO_PROCESS, 0, (int)p) != 0)
46336351Ssteve		syslog(LOG_WARNING, "setpriority '%s' (%s): %m",
46436351Ssteve		    pwd->pw_name, lc ? lc->lc_class : LOGIN_DEFCLASS);
46536351Ssteve	}
46625670Sdavidn    }
46721288Sdavidn
46825670Sdavidn    /* Setup the user's group permissions */
46925670Sdavidn    if (flags & LOGIN_SETGROUP) {
47025670Sdavidn	if (setgid(pwd->pw_gid) != 0) {
47137947Sache	    syslog(LOG_ERR, "setgid(%lu): %m", (u_long)pwd->pw_gid);
47225670Sdavidn	    login_close(llc);
47325670Sdavidn	    return -1;
47425670Sdavidn	}
47525670Sdavidn	if (initgroups(pwd->pw_name, pwd->pw_gid) == -1) {
47637947Sache	    syslog(LOG_ERR, "initgroups(%s,%lu): %m", pwd->pw_name,
47737947Sache		   (u_long)pwd->pw_gid);
47825670Sdavidn	    login_close(llc);
47925670Sdavidn	    return -1;
48025670Sdavidn	}
48125670Sdavidn    }
48221288Sdavidn
483105757Srwatson    /* Set up the user's MAC label. */
484105757Srwatson    if ((flags & LOGIN_SETMAC) && mac_is_present(NULL) == 1) {
485105757Srwatson	const char *label_string;
486105757Srwatson	mac_t label;
487105757Srwatson
488105757Srwatson	label_string = login_getcapstr(lc, "label", NULL, NULL);
489105757Srwatson	if (label_string != NULL) {
490105757Srwatson	    if (mac_from_text(&label, label_string) == -1) {
491105757Srwatson		syslog(LOG_ERR, "mac_from_text('%s') for %s: %m",
492105757Srwatson		    pwd->pw_name, label_string);
493105757Srwatson		    return -1;
494105757Srwatson	    }
495105757Srwatson	    if (mac_set_proc(label) == -1)
496105757Srwatson		error = errno;
497105757Srwatson	    else
498105757Srwatson		error = 0;
499105757Srwatson	    mac_free(label);
500105757Srwatson	    if (error != 0) {
501105757Srwatson		syslog(LOG_ERR, "mac_set_proc('%s') for %s: %s",
502105757Srwatson		    label_string, pwd->pw_name, strerror(error));
503105757Srwatson		return -1;
504105757Srwatson	    }
505105757Srwatson	}
506105757Srwatson    }
507105757Srwatson
50825670Sdavidn    /* Set the sessions login */
50925670Sdavidn    if ((flags & LOGIN_SETLOGIN) && setlogin(pwd->pw_name) != 0) {
51025670Sdavidn	syslog(LOG_ERR, "setlogin(%s): %m", pwd->pw_name);
51125670Sdavidn	login_close(llc);
51225670Sdavidn	return -1;
51325670Sdavidn    }
51421288Sdavidn
51525670Sdavidn    mymask = (flags & LOGIN_SETUMASK) ? umask(LOGIN_DEFUMASK) : 0;
51625670Sdavidn    mymask = setlogincontext(lc, pwd, mymask, flags);
51725670Sdavidn    login_close(llc);
51821288Sdavidn
51925670Sdavidn    /* This needs to be done after anything that needs root privs */
52025670Sdavidn    if ((flags & LOGIN_SETUSER) && setuid(uid) != 0) {
52137947Sache	syslog(LOG_ERR, "setuid(%lu): %m", (u_long)uid);
52225670Sdavidn	return -1;	/* Paranoia again */
52321288Sdavidn    }
52421288Sdavidn
52525670Sdavidn    /*
52625670Sdavidn     * Now, we repeat some of the above for the user's private entries
52725670Sdavidn     */
52825670Sdavidn    if ((lc = login_getuserclass(pwd)) != NULL) {
52925670Sdavidn	mymask = setlogincontext(lc, pwd, mymask, flags);
53025670Sdavidn	login_close(lc);
53125670Sdavidn    }
53221288Sdavidn
53325670Sdavidn    /* Finally, set any umask we've found */
53425670Sdavidn    if (flags & LOGIN_SETUMASK)
53525670Sdavidn	umask(mymask);
53621288Sdavidn
53725670Sdavidn    return 0;
53821288Sdavidn}
539