login_class.c revision 105757
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 105757 2002-10-23 03:17:22Z rwatson $");
2784225Sdillon
2821288Sdavidn#include <stdio.h>
2921288Sdavidn#include <stdlib.h>
3021288Sdavidn#include <string.h>
3121288Sdavidn#include <unistd.h>
3221288Sdavidn#include <errno.h>
3321288Sdavidn#include <sys/types.h>
3422084Sdavidn#include <sys/stat.h>
3521288Sdavidn#include <sys/time.h>
3621288Sdavidn#include <sys/resource.h>
3721288Sdavidn#include <fcntl.h>
3821288Sdavidn#include <pwd.h>
3921288Sdavidn#include <syslog.h>
4021288Sdavidn#include <login_cap.h>
4121288Sdavidn#include <paths.h>
4236351Ssteve#include <sys/rtprio.h>
43105757Srwatson#include <sys/mac.h>
4421288Sdavidn
4521288Sdavidn
4621288Sdavidnstatic struct login_res {
4725670Sdavidn    const char *what;
4825670Sdavidn    rlim_t (*who)(login_cap_t *, const char *, rlim_t, rlim_t);
4925670Sdavidn    int why;
5021288Sdavidn} resources[] = {
5125670Sdavidn    { "cputime",      login_getcaptime, RLIMIT_CPU      },
5225670Sdavidn    { "filesize",     login_getcapsize, RLIMIT_FSIZE    },
5325670Sdavidn    { "datasize",     login_getcapsize, RLIMIT_DATA     },
5425670Sdavidn    { "stacksize",    login_getcapsize, RLIMIT_STACK    },
5525670Sdavidn    { "memoryuse",    login_getcapsize, RLIMIT_RSS      },
5625670Sdavidn    { "memorylocked", login_getcapsize, RLIMIT_MEMLOCK  },
5725670Sdavidn    { "maxproc",      login_getcapnum,  RLIMIT_NPROC    },
5825670Sdavidn    { "openfiles",    login_getcapnum,  RLIMIT_NOFILE   },
5925670Sdavidn    { "coredumpsize", login_getcapsize, RLIMIT_CORE     },
6063149Ssheldonh    { "sbsize",       login_getcapsize,	RLIMIT_SBSIZE	},
6198851Sdillon    { "vmemoryuse",   login_getcapsize,	RLIMIT_VMEM	},
6225670Sdavidn    { NULL,	      0,		0 	        }
6321288Sdavidn};
6421288Sdavidn
6521288Sdavidn
6621288Sdavidnvoid
6721288Sdavidnsetclassresources(login_cap_t *lc)
6821288Sdavidn{
6925670Sdavidn    struct login_res *lr;
7021288Sdavidn
7125670Sdavidn    if (lc == NULL)
7225670Sdavidn	return;
7321402Sdavidn
7425670Sdavidn    for (lr = resources; lr->what != NULL; ++lr) {
7525670Sdavidn	struct rlimit	rlim;
7621288Sdavidn
7725670Sdavidn	/*
7825670Sdavidn	 * The login.conf file can have <limit>, <limit>-max, and
7925670Sdavidn	 * <limit>-cur entries.
8025670Sdavidn	 * What we do is get the current current- and maximum- limits.
8125670Sdavidn	 * Then, we try to get an entry for <limit> from the capability,
8225670Sdavidn	 * using the current and max limits we just got as the
8325670Sdavidn	 * default/error values.
8425670Sdavidn	 * *Then*, we try looking for <limit>-cur and <limit>-max,
8525670Sdavidn	 * again using the appropriate values as the default/error
8625670Sdavidn	 * conditions.
8725670Sdavidn	 */
8821288Sdavidn
8925670Sdavidn	if (getrlimit(lr->why, &rlim) != 0)
9025670Sdavidn	    syslog(LOG_ERR, "getting %s resource limit: %m", lr->what);
9125670Sdavidn	else {
9225670Sdavidn	    char  	name_cur[40];
9325670Sdavidn	    char	name_max[40];
9425670Sdavidn	    rlim_t	rcur = rlim.rlim_cur;
9525670Sdavidn	    rlim_t	rmax = rlim.rlim_max;
9621288Sdavidn
9725670Sdavidn	    sprintf(name_cur, "%s-cur", lr->what);
9825670Sdavidn	    sprintf(name_max, "%s-max", lr->what);
9921288Sdavidn
10025670Sdavidn	    rcur = (*lr->who)(lc, lr->what, rcur, rcur);
10125670Sdavidn	    rmax = (*lr->who)(lc, lr->what, rmax, rmax);
10225670Sdavidn	    rlim.rlim_cur = (*lr->who)(lc, name_cur, rcur, rcur);
10325670Sdavidn	    rlim.rlim_max = (*lr->who)(lc, name_max, rmax, rmax);
10421288Sdavidn
10525670Sdavidn	    if (setrlimit(lr->why, &rlim) == -1)
10625670Sdavidn		syslog(LOG_WARNING, "set class '%s' resource limit %s: %m", lc->lc_class, lr->what);
10725670Sdavidn	}
10825670Sdavidn    }
10921288Sdavidn}
11021288Sdavidn
11125670Sdavidn
11225670Sdavidn
11321288Sdavidnstatic struct login_vars {
11425670Sdavidn    const char *tag;
11525670Sdavidn    const char *var;
11625670Sdavidn    const char *def;
11798977Sache    int overwrite;
11821288Sdavidn} pathvars[] = {
11998977Sache    { "path",           "PATH",       NULL, 1},
12098977Sache    { "cdpath",         "CDPATH",     NULL, 1},
12198977Sache    { "manpath",        "MANPATH",    NULL, 1},
12298977Sache    { NULL,             NULL,         NULL, 0}
12321288Sdavidn}, envars[] = {
12498977Sache    { "lang",           "LANG",       NULL, 1},
12598977Sache    { "charset",        "MM_CHARSET", NULL, 1},
12698977Sache    { "timezone",       "TZ",         NULL, 1},
12798977Sache    { "term",           "TERM",       NULL, 0},
12898977Sache    { NULL,             NULL,         NULL, 0}
12921288Sdavidn};
13021288Sdavidn
13121288Sdavidnstatic char *
13294202Srusubstvar(const char * var, const struct passwd * pwd, int hlen, int pch, int nlen)
13321288Sdavidn{
13425670Sdavidn    char    *np = NULL;
13521288Sdavidn
13625670Sdavidn    if (var != NULL) {
13725670Sdavidn	int	tildes = 0;
13825670Sdavidn	int	dollas = 0;
13925670Sdavidn	char	*p;
14021288Sdavidn
14125670Sdavidn	if (pwd != NULL) {
14225670Sdavidn	    /* Count the number of ~'s in var to substitute */
14394202Sru	    for (p = (char *)var; (p = strchr(p, '~')) != NULL; p++)
14425670Sdavidn		++tildes;
14525670Sdavidn	    /* Count the number of $'s in var to substitute */
14694202Sru	    for (p = (char *)var; (p = strchr(p, '$')) != NULL; p++)
14725670Sdavidn		++dollas;
14825670Sdavidn	}
14921288Sdavidn
15025670Sdavidn	np = malloc(strlen(var) + (dollas * nlen)
15125670Sdavidn		    - dollas + (tildes * (pch+hlen))
15225670Sdavidn		    - tildes + 1);
15321288Sdavidn
15425670Sdavidn	if (np != NULL) {
15525670Sdavidn	    p = strcpy(np, var);
15621288Sdavidn
15725670Sdavidn	    if (pwd != NULL) {
15825670Sdavidn		/*
15925670Sdavidn		 * This loop does user username and homedir substitutions
16025670Sdavidn		 * for unescaped $ (username) and ~ (homedir)
16125670Sdavidn		 */
16225670Sdavidn		while (*(p += strcspn(p, "~$")) != '\0') {
16325670Sdavidn		    int	l = strlen(p);
16421288Sdavidn
16547118Sache		    if (p > np && *(p-1) == '\\')  /* Escaped: */
16625670Sdavidn			memmove(p - 1, p, l + 1); /* Slide-out the backslash */
16725670Sdavidn		    else if (*p == '~') {
16825670Sdavidn			int	v = pch && *(p+1) != '/'; /* Avoid double // */
16925670Sdavidn			memmove(p + hlen + v, p + 1, l);  /* Subst homedir */
17025670Sdavidn			memmove(p, pwd->pw_dir, hlen);
17125670Sdavidn			if (v)
17225670Sdavidn			    p[hlen] = '/';
17325670Sdavidn			p += hlen + v;
17425670Sdavidn		    }
17525670Sdavidn		    else /* if (*p == '$') */ {
17625670Sdavidn			memmove(p + nlen, p + 1, l);	/* Subst username */
17725670Sdavidn			memmove(p, pwd->pw_name, nlen);
17825670Sdavidn			p += nlen;
17925670Sdavidn		    }
18025670Sdavidn		}
18125670Sdavidn	    }
18221288Sdavidn	}
18321288Sdavidn    }
18425670Sdavidn
18525670Sdavidn    return np;
18621288Sdavidn}
18721288Sdavidn
18821288Sdavidn
18921288Sdavidnvoid
19021288Sdavidnsetclassenvironment(login_cap_t *lc, const struct passwd * pwd, int paths)
19121288Sdavidn{
19225670Sdavidn    struct login_vars	*vars = paths ? pathvars : envars;
19325670Sdavidn    int			hlen = pwd ? strlen(pwd->pw_dir) : 0;
19425670Sdavidn    int			nlen = pwd ? strlen(pwd->pw_name) : 0;
19525670Sdavidn    char pch = 0;
19621288Sdavidn
19725670Sdavidn    if (hlen && pwd->pw_dir[hlen-1] != '/')
19825670Sdavidn	++pch;
19921288Sdavidn
20025670Sdavidn    while (vars->tag != NULL) {
20194202Sru	const char * var = paths ? login_getpath(lc, vars->tag, NULL)
20294202Sru				 : login_getcapstr(lc, vars->tag, NULL, NULL);
20321288Sdavidn
20425670Sdavidn	char * np  = substvar(var, pwd, hlen, pch, nlen);
20521288Sdavidn
20625670Sdavidn	if (np != NULL) {
20798977Sache	    setenv(vars->var, np, vars->overwrite);
20825670Sdavidn	    free(np);
20925670Sdavidn	} else if (vars->def != NULL) {
21025670Sdavidn	    setenv(vars->var, vars->def, 0);
21125670Sdavidn	}
21225670Sdavidn	++vars;
21321288Sdavidn    }
21421288Sdavidn
21525670Sdavidn    /*
21625670Sdavidn     * If we're not processing paths, then see if there is a setenv list by
21725670Sdavidn     * which the admin and/or user may set an arbitrary set of env vars.
21825670Sdavidn     */
21925670Sdavidn    if (!paths) {
22025670Sdavidn	char	**set_env = login_getcaplist(lc, "setenv", ",");
22121402Sdavidn
22225670Sdavidn	if (set_env != NULL) {
22325670Sdavidn	    while (*set_env != NULL) {
22425670Sdavidn		char	*p = strchr(*set_env, '=');
22521402Sdavidn
22625670Sdavidn		if (p != NULL) {  /* Discard invalid entries */
22725670Sdavidn		    char	*np;
22825670Sdavidn
22925670Sdavidn		    *p++ = '\0';
23025670Sdavidn		    if ((np = substvar(p, pwd, hlen, pch, nlen)) != NULL) {
23125670Sdavidn			setenv(*set_env, np, 1);
23225670Sdavidn			free(np);
23325670Sdavidn		    }
23425670Sdavidn		}
23525670Sdavidn		++set_env;
23625670Sdavidn	    }
23721288Sdavidn	}
23821288Sdavidn    }
23921288Sdavidn}
24021288Sdavidn
24121288Sdavidn
24221288Sdavidn/*
24321288Sdavidn * setclasscontext()
24421288Sdavidn *
24521288Sdavidn * For the login class <class>, set various class context values
24621288Sdavidn * (limits, mainly) to the values for that class.  Which values are
24721288Sdavidn * set are controlled by <flags> -- see <login_class.h> for the
24821288Sdavidn * possible values.
24921288Sdavidn *
25021288Sdavidn * setclasscontext() can only set resources, priority, and umask.
25121288Sdavidn */
25221288Sdavidn
25321288Sdavidnint
25421288Sdavidnsetclasscontext(const char *classname, unsigned int flags)
25521288Sdavidn{
25625670Sdavidn    int		rc;
25725670Sdavidn    login_cap_t *lc;
25825670Sdavidn
25925670Sdavidn    lc = login_getclassbyname(classname, NULL);
26025670Sdavidn
26125670Sdavidn    flags &= LOGIN_SETRESOURCES | LOGIN_SETPRIORITY |
26225670Sdavidn	    LOGIN_SETUMASK | LOGIN_SETPATH;
26325670Sdavidn
26425670Sdavidn    rc = lc ? setusercontext(lc, NULL, 0, flags) : -1;
26525670Sdavidn    login_close(lc);
26625670Sdavidn    return rc;
26721288Sdavidn}
26821288Sdavidn
26921288Sdavidn
27025670Sdavidn
27121288Sdavidn/*
27225670Sdavidn * Private functionw which takes care of processing
27325670Sdavidn */
27425670Sdavidn
27525670Sdavidnstatic mode_t
27625670Sdavidnsetlogincontext(login_cap_t *lc, const struct passwd *pwd,
27725670Sdavidn		mode_t mymask, unsigned long flags)
27825670Sdavidn{
27925670Sdavidn    if (lc) {
28025670Sdavidn	/* Set resources */
28125670Sdavidn	if (flags & LOGIN_SETRESOURCES)
28225670Sdavidn	    setclassresources(lc);
28325670Sdavidn	/* See if there's a umask override */
28425670Sdavidn	if (flags & LOGIN_SETUMASK)
28525670Sdavidn	    mymask = (mode_t)login_getcapnum(lc, "umask", mymask, mymask);
28625670Sdavidn	/* Set paths */
28725670Sdavidn	if (flags & LOGIN_SETPATH)
28825670Sdavidn	    setclassenvironment(lc, pwd, 1);
28925670Sdavidn	/* Set environment */
29025670Sdavidn	if (flags & LOGIN_SETENV)
29125670Sdavidn	    setclassenvironment(lc, pwd, 0);
29225670Sdavidn    }
29325670Sdavidn    return mymask;
29425670Sdavidn}
29525670Sdavidn
29625670Sdavidn
29725670Sdavidn
29825670Sdavidn/*
29921288Sdavidn * setusercontext()
30021288Sdavidn *
30121288Sdavidn * Given a login class <lc> and a user in <pwd>, with a uid <uid>,
30221288Sdavidn * set the context as in setclasscontext().  <flags> controls which
30321288Sdavidn * values are set.
30421288Sdavidn *
30521288Sdavidn * The difference between setclasscontext() and setusercontext() is
30621288Sdavidn * that the former sets things up for an already-existing process,
30721288Sdavidn * while the latter sets things up from a root context.  Such as might
30821288Sdavidn * be called from login(1).
30921288Sdavidn *
31021288Sdavidn */
31121288Sdavidn
31221288Sdavidnint
31321288Sdavidnsetusercontext(login_cap_t *lc, const struct passwd *pwd, uid_t uid, unsigned int flags)
31421288Sdavidn{
31525670Sdavidn    quad_t	p;
31625670Sdavidn    mode_t	mymask;
31725670Sdavidn    login_cap_t *llc = NULL;
31836607Sjb#ifndef __NETBSD_SYSCALLS
31936351Ssteve    struct rtprio rtp;
32036607Sjb#endif
321105757Srwatson    int error;
32221288Sdavidn
32325670Sdavidn    if (lc == NULL) {
32425670Sdavidn	if (pwd != NULL && (lc = login_getpwclass(pwd)) != NULL)
32525670Sdavidn	    llc = lc; /* free this when we're done */
32625670Sdavidn    }
32721288Sdavidn
32825670Sdavidn    if (flags & LOGIN_SETPATH)
32925670Sdavidn	pathvars[0].def = uid ? _PATH_DEFPATH : _PATH_STDPATH;
33021288Sdavidn
33125670Sdavidn    /* we need a passwd entry to set these */
33225670Sdavidn    if (pwd == NULL)
33325670Sdavidn	flags &= ~(LOGIN_SETGROUP | LOGIN_SETLOGIN);
33421288Sdavidn
33525670Sdavidn    /* Set the process priority */
33625670Sdavidn    if (flags & LOGIN_SETPRIORITY) {
33725670Sdavidn	p = login_getcapnum(lc, "priority", LOGIN_DEFPRI, LOGIN_DEFPRI);
33821288Sdavidn
33936351Ssteve	if(p > PRIO_MAX) {
34036607Sjb#ifndef __NETBSD_SYSCALLS
34136351Ssteve	    rtp.type = RTP_PRIO_IDLE;
34236351Ssteve	    rtp.prio = p - PRIO_MAX - 1;
34336351Ssteve	    p = (rtp.prio > RTP_PRIO_MAX) ? 31 : p;
34436351Ssteve	    if(rtprio(RTP_SET, 0, &rtp))
34536351Ssteve		syslog(LOG_WARNING, "rtprio '%s' (%s): %m",
34636351Ssteve		    pwd->pw_name, lc ? lc->lc_class : LOGIN_DEFCLASS);
34736607Sjb#endif
34836351Ssteve	} else if(p < PRIO_MIN) {
34936607Sjb#ifndef __NETBSD_SYSCALLS
35036351Ssteve	    rtp.type = RTP_PRIO_REALTIME;
35136351Ssteve	    rtp.prio = abs(p - PRIO_MIN + RTP_PRIO_MAX);
35236351Ssteve	    p = (rtp.prio > RTP_PRIO_MAX) ? 1 : p;
35336351Ssteve	    if(rtprio(RTP_SET, 0, &rtp))
35436351Ssteve		syslog(LOG_WARNING, "rtprio '%s' (%s): %m",
35536351Ssteve		    pwd->pw_name, lc ? lc->lc_class : LOGIN_DEFCLASS);
35636607Sjb#endif
35736351Ssteve	} else {
35836351Ssteve	    if (setpriority(PRIO_PROCESS, 0, (int)p) != 0)
35936351Ssteve		syslog(LOG_WARNING, "setpriority '%s' (%s): %m",
36036351Ssteve		    pwd->pw_name, lc ? lc->lc_class : LOGIN_DEFCLASS);
36136351Ssteve	}
36225670Sdavidn    }
36321288Sdavidn
36425670Sdavidn    /* Setup the user's group permissions */
36525670Sdavidn    if (flags & LOGIN_SETGROUP) {
36625670Sdavidn	if (setgid(pwd->pw_gid) != 0) {
36737947Sache	    syslog(LOG_ERR, "setgid(%lu): %m", (u_long)pwd->pw_gid);
36825670Sdavidn	    login_close(llc);
36925670Sdavidn	    return -1;
37025670Sdavidn	}
37125670Sdavidn	if (initgroups(pwd->pw_name, pwd->pw_gid) == -1) {
37237947Sache	    syslog(LOG_ERR, "initgroups(%s,%lu): %m", pwd->pw_name,
37337947Sache		   (u_long)pwd->pw_gid);
37425670Sdavidn	    login_close(llc);
37525670Sdavidn	    return -1;
37625670Sdavidn	}
37725670Sdavidn    }
37821288Sdavidn
379105757Srwatson    /* Set up the user's MAC label. */
380105757Srwatson    if ((flags & LOGIN_SETMAC) && mac_is_present(NULL) == 1) {
381105757Srwatson	const char *label_string;
382105757Srwatson	mac_t label;
383105757Srwatson
384105757Srwatson	label_string = login_getcapstr(lc, "label", NULL, NULL);
385105757Srwatson	if (label_string != NULL) {
386105757Srwatson	    if (mac_from_text(&label, label_string) == -1) {
387105757Srwatson		syslog(LOG_ERR, "mac_from_text('%s') for %s: %m",
388105757Srwatson		    pwd->pw_name, label_string);
389105757Srwatson		    return -1;
390105757Srwatson	    }
391105757Srwatson	    if (mac_set_proc(label) == -1)
392105757Srwatson		error = errno;
393105757Srwatson	    else
394105757Srwatson		error = 0;
395105757Srwatson	    mac_free(label);
396105757Srwatson	    if (error != 0) {
397105757Srwatson		syslog(LOG_ERR, "mac_set_proc('%s') for %s: %s",
398105757Srwatson		    label_string, pwd->pw_name, strerror(error));
399105757Srwatson		return -1;
400105757Srwatson	    }
401105757Srwatson	}
402105757Srwatson    }
403105757Srwatson
40425670Sdavidn    /* Set the sessions login */
40525670Sdavidn    if ((flags & LOGIN_SETLOGIN) && setlogin(pwd->pw_name) != 0) {
40625670Sdavidn	syslog(LOG_ERR, "setlogin(%s): %m", pwd->pw_name);
40725670Sdavidn	login_close(llc);
40825670Sdavidn	return -1;
40925670Sdavidn    }
41021288Sdavidn
41125670Sdavidn    mymask = (flags & LOGIN_SETUMASK) ? umask(LOGIN_DEFUMASK) : 0;
41225670Sdavidn    mymask = setlogincontext(lc, pwd, mymask, flags);
41325670Sdavidn    login_close(llc);
41421288Sdavidn
41525670Sdavidn    /* This needs to be done after anything that needs root privs */
41625670Sdavidn    if ((flags & LOGIN_SETUSER) && setuid(uid) != 0) {
41737947Sache	syslog(LOG_ERR, "setuid(%lu): %m", (u_long)uid);
41825670Sdavidn	return -1;	/* Paranoia again */
41921288Sdavidn    }
42021288Sdavidn
42125670Sdavidn    /*
42225670Sdavidn     * Now, we repeat some of the above for the user's private entries
42325670Sdavidn     */
42425670Sdavidn    if ((lc = login_getuserclass(pwd)) != NULL) {
42525670Sdavidn	mymask = setlogincontext(lc, pwd, mymask, flags);
42625670Sdavidn	login_close(lc);
42725670Sdavidn    }
42821288Sdavidn
42925670Sdavidn    /* Finally, set any umask we've found */
43025670Sdavidn    if (flags & LOGIN_SETUMASK)
43125670Sdavidn	umask(mymask);
43221288Sdavidn
43325670Sdavidn    return 0;
43421288Sdavidn}
43521288Sdavidn
436