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