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 64 static MALLOC_DEFINE(M_LOGINCLASS, "loginclass", "loginclass structures"); 65 66 LIST_HEAD(, loginclass) loginclasses; 67 68 /* 69 * Lock protecting loginclasses list. 70 */ 71 static struct mtx loginclasses_lock; 72 73 static void lc_init(void); 74 SYSINIT(loginclass, SI_SUB_CPU, SI_ORDER_FIRST, lc_init, NULL); 75 76 void 77 loginclass_hold(struct loginclass *lc) 78 { 79 80 refcount_acquire(&lc->lc_refcount); 81 } 82 83 void 84 loginclass_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 */ 111 struct loginclass * 112 loginclass_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_ 148 struct getloginclass_args { 149 char *namebuf; 150 size_t namelen; 151 }; 152 #endif 153 /* ARGSUSED */ 154 int 155 sys_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_ 181 struct setloginclass_args { 182 const char *namebuf; 183 }; 184 #endif 185 /* ARGSUSED */ 186 int 187 sys_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 221 void 222 loginclass_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 233 static void 234 lc_init(void) 235 { 236 237 mtx_init(&loginclasses_lock, "loginclasses lock", NULL, MTX_DEF); 238 } 239