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