login_class.c revision 36607
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 *
2436607Sjb *	$Id: login_class.c,v 1.7 1998/05/25 03:55:23 steve Exp $
2521288Sdavidn */
2621288Sdavidn
2721288Sdavidn#include <stdio.h>
2821288Sdavidn#include <stdlib.h>
2921288Sdavidn#include <string.h>
3021288Sdavidn#include <unistd.h>
3121288Sdavidn#include <errno.h>
3221288Sdavidn#include <sys/types.h>
3322084Sdavidn#include <sys/stat.h>
3421288Sdavidn#include <sys/time.h>
3521288Sdavidn#include <sys/resource.h>
3621288Sdavidn#include <fcntl.h>
3721288Sdavidn#include <pwd.h>
3821288Sdavidn#include <syslog.h>
3921288Sdavidn#include <login_cap.h>
4021288Sdavidn#include <paths.h>
4136351Ssteve#include <sys/rtprio.h>
4221288Sdavidn
4321288Sdavidn
4421288Sdavidn#undef	UNKNOWN
4521288Sdavidn#define	UNKNOWN	"su"
4621288Sdavidn
4721288Sdavidn
4821288Sdavidnstatic struct login_res {
4925670Sdavidn    const char *what;
5025670Sdavidn    rlim_t (*who)(login_cap_t *, const char *, rlim_t, rlim_t);
5125670Sdavidn    int why;
5221288Sdavidn} resources[] = {
5325670Sdavidn    { "cputime",      login_getcaptime, RLIMIT_CPU      },
5425670Sdavidn    { "filesize",     login_getcapsize, RLIMIT_FSIZE    },
5525670Sdavidn    { "datasize",     login_getcapsize, RLIMIT_DATA     },
5625670Sdavidn    { "stacksize",    login_getcapsize, RLIMIT_STACK    },
5725670Sdavidn    { "memoryuse",    login_getcapsize, RLIMIT_RSS      },
5825670Sdavidn    { "memorylocked", login_getcapsize, RLIMIT_MEMLOCK  },
5925670Sdavidn    { "maxproc",      login_getcapnum,  RLIMIT_NPROC    },
6025670Sdavidn    { "openfiles",    login_getcapnum,  RLIMIT_NOFILE   },
6125670Sdavidn    { "coredumpsize", login_getcapsize, RLIMIT_CORE     },
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;
11721288Sdavidn} pathvars[] = {
11825670Sdavidn    { "path",		"PATH",	      NULL    },
11925670Sdavidn    { "cdpath",		"CDPATH",     NULL    },
12025670Sdavidn    { "manpath",	"MANPATH",    NULL    },
12125670Sdavidn    { NULL,		NULL,	      NULL    }
12221288Sdavidn}, envars[] = {
12325670Sdavidn    { "lang",		"LANG",	      NULL    },
12425670Sdavidn    { "charset",	"MM_CHARSET", NULL    },
12525670Sdavidn    { "timezone",	"TZ",	      NULL    },
12625670Sdavidn    { "term",		"TERM",       UNKNOWN },
12725670Sdavidn    { NULL,		NULL,	      NULL    }
12821288Sdavidn};
12921288Sdavidn
13021288Sdavidnstatic char *
13121288Sdavidnsubstvar(char * var, const struct passwd * pwd, int hlen, int pch, int nlen)
13221288Sdavidn{
13325670Sdavidn    char    *np = NULL;
13421288Sdavidn
13525670Sdavidn    if (var != NULL) {
13625670Sdavidn	int	tildes = 0;
13725670Sdavidn	int	dollas = 0;
13825670Sdavidn	char	*p;
13921288Sdavidn
14025670Sdavidn	if (pwd != NULL) {
14125670Sdavidn	    /* Count the number of ~'s in var to substitute */
14225670Sdavidn	    p = var;
14325670Sdavidn	    for (p = var; (p = strchr(p, '~')) != NULL; p++)
14425670Sdavidn		++tildes;
14525670Sdavidn	    /* Count the number of $'s in var to substitute */
14625670Sdavidn	    p = var;
14725670Sdavidn	    for (p = var; (p = strchr(p, '$')) != NULL; p++)
14825670Sdavidn		++dollas;
14925670Sdavidn	}
15021288Sdavidn
15125670Sdavidn	np = malloc(strlen(var) + (dollas * nlen)
15225670Sdavidn		    - dollas + (tildes * (pch+hlen))
15325670Sdavidn		    - tildes + 1);
15421288Sdavidn
15525670Sdavidn	if (np != NULL) {
15625670Sdavidn	    p = strcpy(np, var);
15721288Sdavidn
15825670Sdavidn	    if (pwd != NULL) {
15925670Sdavidn		/*
16025670Sdavidn		 * This loop does user username and homedir substitutions
16125670Sdavidn		 * for unescaped $ (username) and ~ (homedir)
16225670Sdavidn		 */
16325670Sdavidn		while (*(p += strcspn(p, "~$")) != '\0') {
16425670Sdavidn		    int	l = strlen(p);
16521288Sdavidn
16625670Sdavidn		    if (p > var && *(p-1) == '\\')  /* Escaped: */
16725670Sdavidn			memmove(p - 1, p, l + 1); /* Slide-out the backslash */
16825670Sdavidn		    else if (*p == '~') {
16925670Sdavidn			int	v = pch && *(p+1) != '/'; /* Avoid double // */
17025670Sdavidn			memmove(p + hlen + v, p + 1, l);  /* Subst homedir */
17125670Sdavidn			memmove(p, pwd->pw_dir, hlen);
17225670Sdavidn			if (v)
17325670Sdavidn			    p[hlen] = '/';
17425670Sdavidn			p += hlen + v;
17525670Sdavidn		    }
17625670Sdavidn		    else /* if (*p == '$') */ {
17725670Sdavidn			memmove(p + nlen, p + 1, l);	/* Subst username */
17825670Sdavidn			memmove(p, pwd->pw_name, nlen);
17925670Sdavidn			p += nlen;
18025670Sdavidn		    }
18125670Sdavidn		}
18225670Sdavidn	    }
18321288Sdavidn	}
18421288Sdavidn    }
18525670Sdavidn
18625670Sdavidn    return np;
18721288Sdavidn}
18821288Sdavidn
18921288Sdavidn
19021288Sdavidnvoid
19121288Sdavidnsetclassenvironment(login_cap_t *lc, const struct passwd * pwd, int paths)
19221288Sdavidn{
19325670Sdavidn    struct login_vars	*vars = paths ? pathvars : envars;
19425670Sdavidn    int			hlen = pwd ? strlen(pwd->pw_dir) : 0;
19525670Sdavidn    int			nlen = pwd ? strlen(pwd->pw_name) : 0;
19625670Sdavidn    char pch = 0;
19721288Sdavidn
19825670Sdavidn    if (hlen && pwd->pw_dir[hlen-1] != '/')
19925670Sdavidn	++pch;
20021288Sdavidn
20125670Sdavidn    while (vars->tag != NULL) {
20225670Sdavidn	char * var = paths ? login_getpath(lc, vars->tag, NULL)
20325670Sdavidn	    		   : login_getcapstr(lc, vars->tag, NULL, NULL);
20421288Sdavidn
20525670Sdavidn	char * np  = substvar(var, pwd, hlen, pch, nlen);
20621288Sdavidn
20725670Sdavidn	if (np != NULL) {
20825670Sdavidn	    setenv(vars->var, np, 1);
20925670Sdavidn	    free(np);
21025670Sdavidn	} else if (vars->def != NULL) {
21125670Sdavidn	    setenv(vars->var, vars->def, 0);
21225670Sdavidn	}
21325670Sdavidn	++vars;
21421288Sdavidn    }
21521288Sdavidn
21625670Sdavidn    /*
21725670Sdavidn     * If we're not processing paths, then see if there is a setenv list by
21825670Sdavidn     * which the admin and/or user may set an arbitrary set of env vars.
21925670Sdavidn     */
22025670Sdavidn    if (!paths) {
22125670Sdavidn	char	**set_env = login_getcaplist(lc, "setenv", ",");
22221402Sdavidn
22325670Sdavidn	if (set_env != NULL) {
22425670Sdavidn	    while (*set_env != NULL) {
22525670Sdavidn		char	*p = strchr(*set_env, '=');
22621402Sdavidn
22725670Sdavidn		if (p != NULL) {  /* Discard invalid entries */
22825670Sdavidn		    char	*np;
22925670Sdavidn
23025670Sdavidn		    *p++ = '\0';
23125670Sdavidn		    if ((np = substvar(p, pwd, hlen, pch, nlen)) != NULL) {
23225670Sdavidn			setenv(*set_env, np, 1);
23325670Sdavidn			free(np);
23425670Sdavidn		    }
23525670Sdavidn		}
23625670Sdavidn		++set_env;
23725670Sdavidn	    }
23821288Sdavidn	}
23921288Sdavidn    }
24021288Sdavidn}
24121288Sdavidn
24221288Sdavidn
24321288Sdavidn/*
24421288Sdavidn * setclasscontext()
24521288Sdavidn *
24621288Sdavidn * For the login class <class>, set various class context values
24721288Sdavidn * (limits, mainly) to the values for that class.  Which values are
24821288Sdavidn * set are controlled by <flags> -- see <login_class.h> for the
24921288Sdavidn * possible values.
25021288Sdavidn *
25121288Sdavidn * setclasscontext() can only set resources, priority, and umask.
25221288Sdavidn */
25321288Sdavidn
25421288Sdavidnint
25521288Sdavidnsetclasscontext(const char *classname, unsigned int flags)
25621288Sdavidn{
25725670Sdavidn    int		rc;
25825670Sdavidn    login_cap_t *lc;
25925670Sdavidn
26025670Sdavidn    lc = login_getclassbyname(classname, NULL);
26125670Sdavidn
26225670Sdavidn    flags &= LOGIN_SETRESOURCES | LOGIN_SETPRIORITY |
26325670Sdavidn	    LOGIN_SETUMASK | LOGIN_SETPATH;
26425670Sdavidn
26525670Sdavidn    rc = lc ? setusercontext(lc, NULL, 0, flags) : -1;
26625670Sdavidn    login_close(lc);
26725670Sdavidn    return rc;
26821288Sdavidn}
26921288Sdavidn
27021288Sdavidn
27125670Sdavidn
27221288Sdavidn/*
27325670Sdavidn * Private functionw which takes care of processing
27425670Sdavidn */
27525670Sdavidn
27625670Sdavidnstatic mode_t
27725670Sdavidnsetlogincontext(login_cap_t *lc, const struct passwd *pwd,
27825670Sdavidn		mode_t mymask, unsigned long flags)
27925670Sdavidn{
28025670Sdavidn    if (lc) {
28125670Sdavidn	/* Set resources */
28225670Sdavidn	if (flags & LOGIN_SETRESOURCES)
28325670Sdavidn	    setclassresources(lc);
28425670Sdavidn	/* See if there's a umask override */
28525670Sdavidn	if (flags & LOGIN_SETUMASK)
28625670Sdavidn	    mymask = (mode_t)login_getcapnum(lc, "umask", mymask, mymask);
28725670Sdavidn	/* Set paths */
28825670Sdavidn	if (flags & LOGIN_SETPATH)
28925670Sdavidn	    setclassenvironment(lc, pwd, 1);
29025670Sdavidn	/* Set environment */
29125670Sdavidn	if (flags & LOGIN_SETENV)
29225670Sdavidn	    setclassenvironment(lc, pwd, 0);
29325670Sdavidn    }
29425670Sdavidn    return mymask;
29525670Sdavidn}
29625670Sdavidn
29725670Sdavidn
29825670Sdavidn
29925670Sdavidn/*
30021288Sdavidn * setusercontext()
30121288Sdavidn *
30221288Sdavidn * Given a login class <lc> and a user in <pwd>, with a uid <uid>,
30321288Sdavidn * set the context as in setclasscontext().  <flags> controls which
30421288Sdavidn * values are set.
30521288Sdavidn *
30621288Sdavidn * The difference between setclasscontext() and setusercontext() is
30721288Sdavidn * that the former sets things up for an already-existing process,
30821288Sdavidn * while the latter sets things up from a root context.  Such as might
30921288Sdavidn * be called from login(1).
31021288Sdavidn *
31121288Sdavidn */
31221288Sdavidn
31321288Sdavidnint
31421288Sdavidnsetusercontext(login_cap_t *lc, const struct passwd *pwd, uid_t uid, unsigned int flags)
31521288Sdavidn{
31625670Sdavidn    quad_t	p;
31725670Sdavidn    mode_t	mymask;
31825670Sdavidn    login_cap_t *llc = NULL;
31936607Sjb#ifndef __NETBSD_SYSCALLS
32036351Ssteve    struct rtprio rtp;
32136607Sjb#endif
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) {
36725670Sdavidn	    syslog(LOG_ERR, "setgid(%ld): %m", (long)pwd->pw_gid);
36825670Sdavidn	    login_close(llc);
36925670Sdavidn	    return -1;
37025670Sdavidn	}
37125670Sdavidn	if (initgroups(pwd->pw_name, pwd->pw_gid) == -1) {
37225670Sdavidn	    syslog(LOG_ERR, "initgroups(%s,%ld): %m", pwd->pw_name,
37325670Sdavidn		   pwd->pw_gid);
37425670Sdavidn	    login_close(llc);
37525670Sdavidn	    return -1;
37625670Sdavidn	}
37725670Sdavidn    }
37821288Sdavidn
37925670Sdavidn    /* Set the sessions login */
38025670Sdavidn    if ((flags & LOGIN_SETLOGIN) && setlogin(pwd->pw_name) != 0) {
38125670Sdavidn	syslog(LOG_ERR, "setlogin(%s): %m", pwd->pw_name);
38225670Sdavidn	login_close(llc);
38325670Sdavidn	return -1;
38425670Sdavidn    }
38521288Sdavidn
38625670Sdavidn    mymask = (flags & LOGIN_SETUMASK) ? umask(LOGIN_DEFUMASK) : 0;
38725670Sdavidn    mymask = setlogincontext(lc, pwd, mymask, flags);
38825670Sdavidn    login_close(llc);
38921288Sdavidn
39025670Sdavidn    /* This needs to be done after anything that needs root privs */
39125670Sdavidn    if ((flags & LOGIN_SETUSER) && setuid(uid) != 0) {
39225670Sdavidn	syslog(LOG_ERR, "setuid(%ld): %m", uid);
39325670Sdavidn	return -1;	/* Paranoia again */
39421288Sdavidn    }
39521288Sdavidn
39625670Sdavidn    /*
39725670Sdavidn     * Now, we repeat some of the above for the user's private entries
39825670Sdavidn     */
39925670Sdavidn    if ((lc = login_getuserclass(pwd)) != NULL) {
40025670Sdavidn	mymask = setlogincontext(lc, pwd, mymask, flags);
40125670Sdavidn	login_close(lc);
40225670Sdavidn    }
40321288Sdavidn
40425670Sdavidn    /* Finally, set any umask we've found */
40525670Sdavidn    if (flags & LOGIN_SETUMASK)
40625670Sdavidn	umask(mymask);
40721288Sdavidn
40825670Sdavidn    return 0;
40921288Sdavidn}
41021288Sdavidn
411