kern_loginclass.c revision 267579
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: stable/10/sys/kern/kern_loginclass.c 267579 2014-06-17 13:14:31Z trasz $ 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: stable/10/sys/kern/kern_loginclass.c 267579 2014-06-17 13:14:31Z trasz $"); 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; 72MTX_SYSINIT(loginclasses_init, &loginclasses_lock, "loginclasses lock", MTX_DEF); 73 74void 75loginclass_hold(struct loginclass *lc) 76{ 77 78 refcount_acquire(&lc->lc_refcount); 79} 80 81void 82loginclass_free(struct loginclass *lc) 83{ 84 int old; 85 86 old = lc->lc_refcount; 87 if (old > 1 && atomic_cmpset_int(&lc->lc_refcount, old, old - 1)) 88 return; 89 90 mtx_lock(&loginclasses_lock); 91 if (refcount_release(&lc->lc_refcount)) { 92 racct_destroy(&lc->lc_racct); 93 LIST_REMOVE(lc, lc_next); 94 mtx_unlock(&loginclasses_lock); 95 free(lc, M_LOGINCLASS); 96 97 return; 98 } 99 mtx_unlock(&loginclasses_lock); 100} 101 102/* 103 * Return loginclass structure with a corresponding name. Not 104 * performance critical, as it's used mainly by setloginclass(2), 105 * which happens once per login session. Caller has to use 106 * loginclass_free() on the returned value when it's no longer 107 * needed. 108 */ 109struct loginclass * 110loginclass_find(const char *name) 111{ 112 struct loginclass *lc, *newlc; 113 114 if (name[0] == '\0' || strlen(name) >= MAXLOGNAME) 115 return (NULL); 116 117 newlc = malloc(sizeof(*newlc), M_LOGINCLASS, M_ZERO | M_WAITOK); 118 racct_create(&newlc->lc_racct); 119 120 mtx_lock(&loginclasses_lock); 121 LIST_FOREACH(lc, &loginclasses, lc_next) { 122 if (strcmp(name, lc->lc_name) != 0) 123 continue; 124 125 /* Found loginclass with a matching name? */ 126 loginclass_hold(lc); 127 mtx_unlock(&loginclasses_lock); 128 racct_destroy(&newlc->lc_racct); 129 free(newlc, M_LOGINCLASS); 130 return (lc); 131 } 132 133 /* Add new loginclass. */ 134 strcpy(newlc->lc_name, name); 135 refcount_init(&newlc->lc_refcount, 1); 136 LIST_INSERT_HEAD(&loginclasses, newlc, lc_next); 137 mtx_unlock(&loginclasses_lock); 138 139 return (newlc); 140} 141 142/* 143 * Get login class name. 144 */ 145#ifndef _SYS_SYSPROTO_H_ 146struct getloginclass_args { 147 char *namebuf; 148 size_t namelen; 149}; 150#endif 151/* ARGSUSED */ 152int 153sys_getloginclass(struct thread *td, struct getloginclass_args *uap) 154{ 155 int error = 0; 156 size_t lcnamelen; 157 struct proc *p; 158 struct loginclass *lc; 159 160 p = td->td_proc; 161 PROC_LOCK(p); 162 lc = p->p_ucred->cr_loginclass; 163 loginclass_hold(lc); 164 PROC_UNLOCK(p); 165 166 lcnamelen = strlen(lc->lc_name) + 1; 167 if (lcnamelen > uap->namelen) 168 error = ERANGE; 169 if (error == 0) 170 error = copyout(lc->lc_name, uap->namebuf, lcnamelen); 171 loginclass_free(lc); 172 return (error); 173} 174 175/* 176 * Set login class name. 177 */ 178#ifndef _SYS_SYSPROTO_H_ 179struct setloginclass_args { 180 const char *namebuf; 181}; 182#endif 183/* ARGSUSED */ 184int 185sys_setloginclass(struct thread *td, struct setloginclass_args *uap) 186{ 187 struct proc *p = td->td_proc; 188 int error; 189 char lcname[MAXLOGNAME]; 190 struct loginclass *newlc; 191 struct ucred *newcred, *oldcred; 192 193 error = priv_check(td, PRIV_PROC_SETLOGINCLASS); 194 if (error != 0) 195 return (error); 196 error = copyinstr(uap->namebuf, lcname, sizeof(lcname), NULL); 197 if (error != 0) 198 return (error); 199 200 newlc = loginclass_find(lcname); 201 if (newlc == NULL) 202 return (EINVAL); 203 newcred = crget(); 204 205 PROC_LOCK(p); 206 oldcred = crcopysafe(p, newcred); 207 newcred->cr_loginclass = newlc; 208 p->p_ucred = newcred; 209 PROC_UNLOCK(p); 210#ifdef RACCT 211 racct_proc_ucred_changed(p, oldcred, newcred); 212#endif 213 loginclass_free(oldcred->cr_loginclass); 214 crfree(oldcred); 215 216 return (0); 217} 218 219void 220loginclass_racct_foreach(void (*callback)(struct racct *racct, 221 void *arg2, void *arg3), void *arg2, void *arg3) 222{ 223 struct loginclass *lc; 224 225 mtx_lock(&loginclasses_lock); 226 LIST_FOREACH(lc, &loginclasses, lc_next) 227 (callback)(lc->lc_racct, arg2, arg3); 228 mtx_unlock(&loginclasses_lock); 229} 230