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/11/sys/kern/kern_loginclass.c 335536 2018-06-22 09:18:38Z avg $ 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/11/sys/kern/kern_loginclass.c 335536 2018-06-22 09:18:38Z avg $"); 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/types.h> 55#include <sys/priv.h> 56#include <sys/proc.h> 57#include <sys/queue.h> 58#include <sys/racct.h> 59#include <sys/rctl.h> 60#include <sys/refcount.h> 61#include <sys/rwlock.h> 62#include <sys/sysproto.h> 63#include <sys/systm.h> 64 65static MALLOC_DEFINE(M_LOGINCLASS, "loginclass", "loginclass structures"); 66 67LIST_HEAD(, loginclass) loginclasses; 68 69/* 70 * Lock protecting loginclasses list. 71 */ 72static struct rwlock loginclasses_lock; 73RW_SYSINIT(loginclasses_init, &loginclasses_lock, "loginclasses lock"); 74 75void 76loginclass_hold(struct loginclass *lc) 77{ 78 79 refcount_acquire(&lc->lc_refcount); 80} 81 82void 83loginclass_free(struct loginclass *lc) 84{ 85 int old; 86 87 old = lc->lc_refcount; 88 if (old > 1 && atomic_cmpset_int(&lc->lc_refcount, old, old - 1)) 89 return; 90 91 rw_wlock(&loginclasses_lock); 92 if (!refcount_release(&lc->lc_refcount)) { 93 rw_wunlock(&loginclasses_lock); 94 return; 95 } 96 97 racct_destroy(&lc->lc_racct); 98 LIST_REMOVE(lc, lc_next); 99 rw_wunlock(&loginclasses_lock); 100 101 free(lc, M_LOGINCLASS); 102} 103 104/* 105 * Look up a loginclass struct for the parameter name. 106 * loginclasses_lock must be locked. 107 * Increase refcount on loginclass struct returned. 108 */ 109static struct loginclass * 110loginclass_lookup(const char *name) 111{ 112 struct loginclass *lc; 113 114 rw_assert(&loginclasses_lock, RA_LOCKED); 115 LIST_FOREACH(lc, &loginclasses, lc_next) 116 if (strcmp(name, lc->lc_name) == 0) { 117 loginclass_hold(lc); 118 break; 119 } 120 121 return (lc); 122} 123 124/* 125 * Return loginclass structure with a corresponding name. Not 126 * performance critical, as it's used mainly by setloginclass(2), 127 * which happens once per login session. Caller has to use 128 * loginclass_free() on the returned value when it's no longer 129 * needed. 130 */ 131struct loginclass * 132loginclass_find(const char *name) 133{ 134 struct loginclass *lc, *new_lc; 135 136 if (name[0] == '\0' || strlen(name) >= MAXLOGNAME) 137 return (NULL); 138 139 rw_rlock(&loginclasses_lock); 140 lc = loginclass_lookup(name); 141 rw_runlock(&loginclasses_lock); 142 if (lc != NULL) 143 return (lc); 144 145 new_lc = malloc(sizeof(*new_lc), M_LOGINCLASS, M_ZERO | M_WAITOK); 146 racct_create(&new_lc->lc_racct); 147 refcount_init(&new_lc->lc_refcount, 1); 148 strcpy(new_lc->lc_name, name); 149 150 rw_wlock(&loginclasses_lock); 151 /* 152 * There's a chance someone created our loginclass while we 153 * were in malloc and not holding the lock, so we have to 154 * make sure we don't insert a duplicate loginclass. 155 */ 156 if ((lc = loginclass_lookup(name)) == NULL) { 157 LIST_INSERT_HEAD(&loginclasses, new_lc, lc_next); 158 rw_wunlock(&loginclasses_lock); 159 lc = new_lc; 160 } else { 161 rw_wunlock(&loginclasses_lock); 162 racct_destroy(&new_lc->lc_racct); 163 free(new_lc, M_LOGINCLASS); 164 } 165 166 return (lc); 167} 168 169/* 170 * Get login class name. 171 */ 172#ifndef _SYS_SYSPROTO_H_ 173struct getloginclass_args { 174 char *namebuf; 175 size_t namelen; 176}; 177#endif 178/* ARGSUSED */ 179int 180sys_getloginclass(struct thread *td, struct getloginclass_args *uap) 181{ 182 struct loginclass *lc; 183 size_t lcnamelen; 184 185 lc = td->td_ucred->cr_loginclass; 186 lcnamelen = strlen(lc->lc_name) + 1; 187 if (lcnamelen > uap->namelen) 188 return (ERANGE); 189 return (copyout(lc->lc_name, uap->namebuf, lcnamelen)); 190} 191 192/* 193 * Set login class name. 194 */ 195#ifndef _SYS_SYSPROTO_H_ 196struct setloginclass_args { 197 const char *namebuf; 198}; 199#endif 200/* ARGSUSED */ 201int 202sys_setloginclass(struct thread *td, struct setloginclass_args *uap) 203{ 204 struct proc *p = td->td_proc; 205 int error; 206 char lcname[MAXLOGNAME]; 207 struct loginclass *newlc; 208 struct ucred *newcred, *oldcred; 209 210 error = priv_check(td, PRIV_PROC_SETLOGINCLASS); 211 if (error != 0) 212 return (error); 213 error = copyinstr(uap->namebuf, lcname, sizeof(lcname), NULL); 214 if (error != 0) 215 return (error); 216 217 newlc = loginclass_find(lcname); 218 if (newlc == NULL) 219 return (EINVAL); 220 newcred = crget(); 221 222 PROC_LOCK(p); 223 oldcred = crcopysafe(p, newcred); 224 newcred->cr_loginclass = newlc; 225 proc_set_cred(p, newcred); 226#ifdef RACCT 227 racct_proc_ucred_changed(p, oldcred, newcred); 228 crhold(newcred); 229#endif 230 PROC_UNLOCK(p); 231#ifdef RCTL 232 rctl_proc_ucred_changed(p, newcred); 233 crfree(newcred); 234#endif 235 loginclass_free(oldcred->cr_loginclass); 236 crfree(oldcred); 237 238 return (0); 239} 240 241void 242loginclass_racct_foreach(void (*callback)(struct racct *racct, 243 void *arg2, void *arg3), void (*pre)(void), void (*post)(void), 244 void *arg2, void *arg3) 245{ 246 struct loginclass *lc; 247 248 rw_rlock(&loginclasses_lock); 249 if (pre != NULL) 250 (pre)(); 251 LIST_FOREACH(lc, &loginclasses, lc_next) 252 (callback)(lc->lc_racct, arg2, arg3); 253 if (post != NULL) 254 (post)(); 255 rw_runlock(&loginclasses_lock); 256} 257