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