1/*- 2 * Copyright (c) 2011 The FreeBSD Foundation 3 * All rights reserved. 4 * 5 * This software was developed by Edward Tomasz Napierala under sponsorship 6 * from the FreeBSD Foundation. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27 * SUCH DAMAGE. 28 * 29 * $FreeBSD$ 30 */ 31 32/* 33 * Processes may set login class name using setloginclass(2). This 34 * is usually done through call to setusercontext(3), by programs 35 * such as login(1), based on information from master.passwd(5). Kernel 36 * uses this information to enforce per-class resource limits. Current 37 * login class can be determined using id(1). Login class is inherited 38 * from the parent process during fork(2). If not set, it defaults 39 * to "default". 40 * 41 * Code in this file implements setloginclass(2) and getloginclass(2) 42 * system calls, and maintains class name storage and retrieval. 43 */ 44 45#include <sys/cdefs.h> 46__FBSDID("$FreeBSD$"); 47 48#include <sys/param.h> 49#include <sys/eventhandler.h> 50#include <sys/kernel.h> 51#include <sys/lock.h> 52#include <sys/loginclass.h> 53#include <sys/malloc.h> 54#include <sys/mutex.h> 55#include <sys/types.h> 56#include <sys/priv.h> 57#include <sys/proc.h> 58#include <sys/queue.h> 59#include <sys/racct.h> 60#include <sys/refcount.h> 61#include <sys/sysproto.h> 62#include <sys/systm.h> 63 64static MALLOC_DEFINE(M_LOGINCLASS, "loginclass", "loginclass structures"); 65 66LIST_HEAD(, loginclass) loginclasses; 67 68/* 69 * Lock protecting loginclasses list. 70 */ 71static struct mtx loginclasses_lock; 72 73static void lc_init(void); 74SYSINIT(loginclass, SI_SUB_CPU, SI_ORDER_FIRST, lc_init, NULL); 75 76void 77loginclass_hold(struct loginclass *lc) 78{ 79 80 refcount_acquire(&lc->lc_refcount); 81} 82 83void 84loginclass_free(struct loginclass *lc) 85{ 86 int old; 87 88 old = lc->lc_refcount; 89 if (old > 1 && atomic_cmpset_int(&lc->lc_refcount, old, old - 1)) 90 return; 91 92 mtx_lock(&loginclasses_lock); 93 if (refcount_release(&lc->lc_refcount)) { 94 racct_destroy(&lc->lc_racct); 95 LIST_REMOVE(lc, lc_next); 96 mtx_unlock(&loginclasses_lock); 97 free(lc, M_LOGINCLASS); 98 99 return; 100 } 101 mtx_unlock(&loginclasses_lock); 102} 103 104/* 105 * Return loginclass structure with a corresponding name. Not 106 * performance critical, as it's used mainly by setloginclass(2), 107 * which happens once per login session. Caller has to use 108 * loginclass_free() on the returned value when it's no longer 109 * needed. 110 */ 111struct loginclass * 112loginclass_find(const char *name) 113{ 114 struct loginclass *lc, *newlc; 115 116 if (name[0] == '\0' || strlen(name) >= MAXLOGNAME) 117 return (NULL); 118 119 newlc = malloc(sizeof(*newlc), M_LOGINCLASS, M_ZERO | M_WAITOK); 120 racct_create(&newlc->lc_racct); 121 122 mtx_lock(&loginclasses_lock); 123 LIST_FOREACH(lc, &loginclasses, lc_next) { 124 if (strcmp(name, lc->lc_name) != 0) 125 continue; 126 127 /* Found loginclass with a matching name? */ 128 loginclass_hold(lc); 129 mtx_unlock(&loginclasses_lock); 130 racct_destroy(&newlc->lc_racct); 131 free(newlc, M_LOGINCLASS); 132 return (lc); 133 } 134 135 /* Add new loginclass. */ 136 strcpy(newlc->lc_name, name); 137 refcount_init(&newlc->lc_refcount, 1); 138 LIST_INSERT_HEAD(&loginclasses, newlc, lc_next); 139 mtx_unlock(&loginclasses_lock); 140 141 return (newlc); 142} 143 144/* 145 * Get login class name. 146 */ 147#ifndef _SYS_SYSPROTO_H_ 148struct getloginclass_args { 149 char *namebuf; 150 size_t namelen; 151}; 152#endif 153/* ARGSUSED */ 154int 155sys_getloginclass(struct thread *td, struct getloginclass_args *uap) 156{ 157 int error = 0; 158 size_t lcnamelen; 159 struct proc *p; 160 struct loginclass *lc; 161 162 p = td->td_proc; 163 PROC_LOCK(p); 164 lc = p->p_ucred->cr_loginclass; 165 loginclass_hold(lc); 166 PROC_UNLOCK(p); 167 168 lcnamelen = strlen(lc->lc_name) + 1; 169 if (lcnamelen > uap->namelen) 170 error = ERANGE; 171 if (error == 0) 172 error = copyout(lc->lc_name, uap->namebuf, lcnamelen); 173 loginclass_free(lc); 174 return (error); 175} 176 177/* 178 * Set login class name. 179 */ 180#ifndef _SYS_SYSPROTO_H_ 181struct setloginclass_args { 182 const char *namebuf; 183}; 184#endif 185/* ARGSUSED */ 186int 187sys_setloginclass(struct thread *td, struct setloginclass_args *uap) 188{ 189 struct proc *p = td->td_proc; 190 int error; 191 char lcname[MAXLOGNAME]; 192 struct loginclass *newlc; 193 struct ucred *newcred, *oldcred; 194 195 error = priv_check(td, PRIV_PROC_SETLOGINCLASS); 196 if (error != 0) 197 return (error); 198 error = copyinstr(uap->namebuf, lcname, sizeof(lcname), NULL); 199 if (error != 0) 200 return (error); 201 202 newlc = loginclass_find(lcname); 203 if (newlc == NULL) 204 return (EINVAL); 205 newcred = crget(); 206 207 PROC_LOCK(p); 208 oldcred = crcopysafe(p, newcred); 209 newcred->cr_loginclass = newlc; 210 p->p_ucred = newcred; 211 PROC_UNLOCK(p); 212#ifdef RACCT 213 racct_proc_ucred_changed(p, oldcred, newcred); 214#endif 215 loginclass_free(oldcred->cr_loginclass); 216 crfree(oldcred); 217 218 return (0); 219} 220 221void 222loginclass_racct_foreach(void (*callback)(struct racct *racct, 223 void *arg2, void *arg3), void *arg2, void *arg3) 224{ 225 struct loginclass *lc; 226 227 mtx_lock(&loginclasses_lock); 228 LIST_FOREACH(lc, &loginclasses, lc_next) 229 (callback)(lc->lc_racct, arg2, arg3); 230 mtx_unlock(&loginclasses_lock); 231} 232 233static void 234lc_init(void) 235{ 236 237 mtx_init(&loginclasses_lock, "loginclasses lock", NULL, MTX_DEF); 238} 239