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/refcount.h> 60 #include <sys/sysproto.h> 61 #include <sys/systm.h> 62 63 static MALLOC_DEFINE(M_LOGINCLASS, "loginclass", "loginclass structures"); 64 65 LIST_HEAD(, loginclass) loginclasses; 66 67 /* 68 * Lock protecting loginclasses list. 69 */ 70 static struct mtx loginclasses_lock; 71 72 static void lc_init(void); 73 SYSINIT(loginclass, SI_SUB_CPU, SI_ORDER_FIRST, lc_init, NULL); 74 75 void 76 loginclass_hold(struct loginclass *lc) 77 { 78 79 refcount_acquire(&lc->lc_refcount); 80 } 81 82 void 83 loginclass_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 mtx_lock(&loginclasses_lock); 92 if (refcount_release(&lc->lc_refcount)) { 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 */ 109 struct loginclass * 110 loginclass_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 119 mtx_lock(&loginclasses_lock); 120 LIST_FOREACH(lc, &loginclasses, lc_next) { 121 if (strcmp(name, lc->lc_name) != 0) 122 continue; 123 124 /* Found loginclass with a matching name? */ 125 loginclass_hold(lc); 126 mtx_unlock(&loginclasses_lock); 127 free(newlc, M_LOGINCLASS); 128 return (lc); 129 } 130 131 /* Add new loginclass. */ 132 strcpy(newlc->lc_name, name); 133 refcount_init(&newlc->lc_refcount, 1); 134 LIST_INSERT_HEAD(&loginclasses, newlc, lc_next); 135 mtx_unlock(&loginclasses_lock); 136 137 return (newlc); 138 } 139 140 /* 141 * Get login class name. 142 */ 143 #ifndef _SYS_SYSPROTO_H_ 144 struct getloginclass_args { 145 char *namebuf; 146 size_t namelen; 147 }; 148 #endif 149 /* ARGSUSED */ 150 int 151 getloginclass(struct thread *td, struct getloginclass_args *uap) 152 { 153 int error = 0; 154 size_t lcnamelen; 155 struct proc *p; 156 struct loginclass *lc; 157 158 p = td->td_proc; 159 PROC_LOCK(p); 160 lc = p->p_ucred->cr_loginclass; 161 loginclass_hold(lc); 162 PROC_UNLOCK(p); 163 164 lcnamelen = strlen(lc->lc_name) + 1; 165 if (lcnamelen > uap->namelen) 166 error = ERANGE; 167 if (error == 0) 168 error = copyout(lc->lc_name, uap->namebuf, lcnamelen); 169 loginclass_free(lc); 170 return (error); 171 } 172 173 /* 174 * Set login class name. 175 */ 176 #ifndef _SYS_SYSPROTO_H_ 177 struct setloginclass_args { 178 const char *namebuf; 179 }; 180 #endif 181 /* ARGSUSED */ 182 int 183 setloginclass(struct thread *td, struct setloginclass_args *uap) 184 { 185 struct proc *p = td->td_proc; 186 int error; 187 char lcname[MAXLOGNAME]; 188 struct loginclass *newlc; 189 struct ucred *newcred, *oldcred; 190 191 error = priv_check(td, PRIV_PROC_SETLOGINCLASS); 192 if (error != 0) 193 return (error); 194 error = copyinstr(uap->namebuf, lcname, sizeof(lcname), NULL); 195 if (error != 0) 196 return (error); 197 198 newlc = loginclass_find(lcname); 199 if (newlc == NULL) 200 return (EINVAL); 201 newcred = crget(); 202 203 PROC_LOCK(p); 204 oldcred = crcopysafe(p, newcred); 205 newcred->cr_loginclass = newlc; 206 p->p_ucred = newcred; 207 PROC_UNLOCK(p); 208 209 loginclass_free(oldcred->cr_loginclass); 210 crfree(oldcred); 211 212 return (0); 213 } 214 215 static void 216 lc_init(void) 217 { 218 219 mtx_init(&loginclasses_lock, "loginclasses lock", NULL, MTX_DEF); 220 } 221