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