1 /* 2 * Copyright (c) 2003,2004 The DragonFly Project. All rights reserved. 3 * 4 * This code is derived from software contributed to The DragonFly Project 5 * by Matthew Dillon <dillon@backplane.com> 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 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 15 * the documentation and/or other materials provided with the 16 * distribution. 17 * 3. Neither the name of The DragonFly Project nor the names of its 18 * contributors may be used to endorse or promote products derived 19 * from this software without specific, prior written permission. 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 22 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 23 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 24 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 25 * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 26 * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, 27 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 28 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 29 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 30 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT 31 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 32 * SUCH DAMAGE. 33 */ 34 35 /* 36 * This module implements variable storage and management for variant 37 * symlinks. These variables may also be used for general purposes. 38 */ 39 40 #include <sys/param.h> 41 #include <sys/systm.h> 42 #include <sys/kernel.h> 43 #include <sys/ucred.h> 44 #include <sys/resourcevar.h> 45 #include <sys/proc.h> 46 #include <sys/priv.h> 47 #include <sys/jail.h> 48 #include <sys/queue.h> 49 #include <sys/sysctl.h> 50 #include <sys/malloc.h> 51 #include <sys/varsym.h> 52 #include <sys/sysproto.h> 53 54 #include <sys/mplock2.h> 55 56 MALLOC_DEFINE(M_VARSYM, "varsym", "variable sets for variant symlinks"); 57 58 struct varsymset varsymset_sys; 59 60 /* 61 * Initialize the variant symlink subsystem 62 */ 63 static void 64 varsym_sysinit(void *dummy) 65 { 66 varsymset_init(&varsymset_sys, NULL); 67 } 68 SYSINIT(announce, SI_BOOT2_MACHDEP, SI_ORDER_FIRST, varsym_sysinit, NULL); 69 70 /* 71 * varsymreplace() - called from namei 72 * 73 * Do variant symlink variable substitution 74 */ 75 int 76 varsymreplace(char *cp, int linklen, int maxlen) 77 { 78 int rlen; 79 int xlen; 80 int nlen; 81 int i; 82 varsym_t var; 83 84 rlen = linklen; 85 while (linklen > 1) { 86 if (cp[0] == '$' && cp[1] == '{') { 87 for (i = 2; i < linklen; ++i) { 88 if (cp[i] == '}') 89 break; 90 } 91 if (i < linklen && 92 (var = varsymfind(VARSYM_ALL_MASK, cp + 2, i - 2)) != NULL 93 ) { 94 xlen = i + 1; /* bytes to strike */ 95 nlen = strlen(var->vs_data); /* bytes to add */ 96 if (linklen + nlen - xlen >= maxlen) { 97 varsymdrop(var); 98 return(-1); 99 } 100 KKASSERT(linklen >= xlen); 101 if (linklen != xlen) 102 bcopy(cp + xlen, cp + nlen, linklen - xlen); 103 bcopy(var->vs_data, cp, nlen); 104 linklen += nlen - xlen; /* new relative length */ 105 rlen += nlen - xlen; /* returned total length */ 106 cp += nlen; /* adjust past replacement */ 107 linklen -= nlen; /* adjust past replacement */ 108 maxlen -= nlen; /* adjust past replacement */ 109 } else { 110 /* 111 * It's ok if i points to the '}', it will simply be 112 * skipped. i could also have hit linklen. 113 */ 114 cp += i; 115 linklen -= i; 116 maxlen -= i; 117 } 118 } else { 119 ++cp; 120 --linklen; 121 --maxlen; 122 } 123 } 124 return(rlen); 125 } 126 127 /* 128 * varsym_set() system call 129 * 130 * (int level, const char *name, const char *data) 131 * 132 * MPALMOSTSAFE 133 */ 134 int 135 sys_varsym_set(struct varsym_set_args *uap) 136 { 137 char name[MAXVARSYM_NAME]; 138 char *buf; 139 struct thread *td; 140 struct lwp *lp; 141 int error; 142 143 td = curthread; 144 lp = td->td_lwp; 145 146 if ((error = copyinstr(uap->name, name, sizeof(name), NULL)) != 0) 147 goto done2; 148 buf = kmalloc(MAXVARSYM_DATA, M_TEMP, M_WAITOK); 149 if (uap->data && 150 (error = copyinstr(uap->data, buf, MAXVARSYM_DATA, NULL)) != 0) 151 { 152 goto done1; 153 } 154 155 get_mplock(); 156 157 switch(uap->level) { 158 case VARSYM_SYS: 159 if (lp != NULL && td->td_ucred->cr_prison != NULL) 160 uap->level = VARSYM_PRISON; 161 case VARSYM_PRISON: 162 if (lp != NULL && 163 (error = priv_check_cred(td->td_ucred, PRIV_VARSYM_SYS, 0)) != 0) 164 break; 165 /* fall through */ 166 case VARSYM_USER: 167 /* XXX check jail / implement per-jail user */ 168 /* fall through */ 169 case VARSYM_PROC: 170 if (uap->data) { 171 (void)varsymmake(uap->level, name, NULL); 172 error = varsymmake(uap->level, name, buf); 173 } else { 174 error = varsymmake(uap->level, name, NULL); 175 } 176 break; 177 } 178 rel_mplock(); 179 done1: 180 kfree(buf, M_TEMP); 181 done2: 182 return(error); 183 } 184 185 /* 186 * varsym_get() system call 187 * 188 * (int mask, const char *wild, char *buf, int bufsize) 189 * 190 * MPALMOSTSAFE 191 */ 192 int 193 sys_varsym_get(struct varsym_get_args *uap) 194 { 195 char wild[MAXVARSYM_NAME]; 196 varsym_t sym; 197 int error; 198 int dlen; 199 200 get_mplock(); 201 if ((error = copyinstr(uap->wild, wild, sizeof(wild), NULL)) != 0) 202 goto done; 203 sym = varsymfind(uap->mask, wild, strlen(wild)); 204 if (sym == NULL) { 205 error = ENOENT; 206 goto done; 207 } 208 dlen = strlen(sym->vs_data); 209 if (dlen < uap->bufsize) { 210 copyout(sym->vs_data, uap->buf, dlen + 1); 211 } else if (uap->bufsize) { 212 copyout("", uap->buf, 1); 213 } 214 uap->sysmsg_result = dlen + 1; 215 varsymdrop(sym); 216 done: 217 rel_mplock(); 218 return(error); 219 } 220 221 /* 222 * varsym_list() system call 223 * 224 * (int level, char *buf, int maxsize, int *marker) 225 * 226 * MPALMOSTSAFE 227 */ 228 int 229 sys_varsym_list(struct varsym_list_args *uap) 230 { 231 struct varsymset *vss; 232 struct varsyment *ve; 233 struct thread *td; 234 struct proc *p; 235 struct lwp *lp; 236 int i; 237 int error; 238 int bytes; 239 int earlyterm; 240 int marker; 241 242 /* 243 * Get the marker from userspace. 244 */ 245 get_mplock(); 246 if ((error = copyin(uap->marker, &marker, sizeof(marker))) != 0) 247 goto done; 248 249 /* 250 * Figure out the varsym set. 251 */ 252 td = curthread; 253 lp = td->td_lwp; 254 p = lp ? lp->lwp_proc : NULL; 255 256 vss = NULL; 257 258 switch (uap->level) { 259 case VARSYM_PROC: 260 if (p) 261 vss = &p->p_varsymset; 262 break; 263 case VARSYM_USER: 264 if (lp) 265 vss = &td->td_ucred->cr_uidinfo->ui_varsymset; 266 break; 267 case VARSYM_SYS: 268 vss = &varsymset_sys; 269 break; 270 case VARSYM_PRISON: 271 if (lp && td->td_ucred->cr_prison) 272 vss = &td->td_ucred->cr_prison->pr_varsymset; 273 break; 274 } 275 if (vss == NULL) { 276 error = EINVAL; 277 goto done; 278 } 279 280 /* 281 * Loop through the variables and dump them to uap->buf 282 */ 283 i = 0; 284 bytes = 0; 285 earlyterm = 0; 286 287 lockmgr(&vss->vx_lock, LK_SHARED); 288 TAILQ_FOREACH(ve, &vss->vx_queue, ve_entry) { 289 varsym_t sym = ve->ve_sym; 290 int namelen = strlen(sym->vs_name); 291 int datalen = strlen(sym->vs_data); 292 int totlen = namelen + datalen + 2; 293 294 /* 295 * Skip to our index point 296 */ 297 if (i < marker) { 298 ++i; 299 continue; 300 } 301 302 /* 303 * Stop if there is insufficient space in the user buffer. 304 * If we haven't stored anything yet return EOVERFLOW. 305 * Note that the marker index (i) does not change. 306 */ 307 if (bytes + totlen > uap->maxsize) { 308 if (bytes == 0) 309 error = EOVERFLOW; 310 earlyterm = 1; 311 break; 312 } 313 314 error = copyout(sym->vs_name, uap->buf + bytes, namelen + 1); 315 if (error == 0) { 316 bytes += namelen + 1; 317 error = copyout(sym->vs_data, uap->buf + bytes, datalen + 1); 318 if (error == 0) 319 bytes += datalen + 1; 320 else 321 bytes -= namelen + 1; /* revert if error */ 322 } 323 if (error) { 324 earlyterm = 1; 325 break; 326 } 327 ++i; 328 } 329 lockmgr(&vss->vx_lock, LK_RELEASE); 330 331 /* 332 * Save the marker back. If no error occured and earlyterm is clear 333 * the marker is set to -1 indicating that the variable list has been 334 * exhausted. If no error occured the number of bytes loaded into 335 * the buffer will be returned, otherwise the syscall code returns -1. 336 */ 337 if (error == 0 && earlyterm == 0) 338 marker = -1; 339 else 340 marker = i; 341 if (error == 0) 342 error = copyout(&marker, uap->marker, sizeof(marker)); 343 uap->sysmsg_result = bytes; 344 done: 345 rel_mplock(); 346 return(error); 347 } 348 349 /* 350 * Lookup a variant symlink. XXX use a hash table. 351 */ 352 static 353 struct varsyment * 354 varsymlookup(struct varsymset *vss, const char *name, int namelen) 355 { 356 struct varsyment *ve; 357 358 KKASSERT(lockstatus(&vss->vx_lock, curthread) != 0); 359 TAILQ_FOREACH(ve, &vss->vx_queue, ve_entry) { 360 varsym_t var = ve->ve_sym; 361 if (var->vs_namelen == namelen && 362 bcmp(name, var->vs_name, namelen) == 0 363 ) { 364 return(ve); 365 } 366 } 367 return(NULL); 368 } 369 370 static 371 void 372 vsslock(struct varsymset **vss, struct varsymset *n) 373 { 374 if (*vss) { 375 lockmgr(&(*vss)->vx_lock, LK_RELEASE); 376 } 377 lockmgr(&n->vx_lock, LK_SHARED); 378 *vss = n; 379 } 380 381 varsym_t 382 varsymfind(int mask, const char *name, int namelen) 383 { 384 struct varsyment *ve = NULL; 385 struct varsymset *vss = NULL; 386 struct thread *td; 387 struct lwp *lp; 388 struct proc *p; 389 varsym_t sym; 390 391 td = curthread; 392 lp = td->td_lwp; 393 p = lp ? lp->lwp_proc : NULL; 394 395 if ((mask & (VARSYM_PROC_MASK|VARSYM_USER_MASK)) && lp != NULL) { 396 if (mask & VARSYM_PROC_MASK) { 397 vsslock(&vss, &p->p_varsymset); 398 ve = varsymlookup(vss, name, namelen); 399 } 400 if (ve == NULL && (mask & VARSYM_USER_MASK)) { 401 vsslock(&vss, &td->td_ucred->cr_uidinfo->ui_varsymset); 402 ve = varsymlookup(vss, name, namelen); 403 } 404 } 405 if (ve == NULL && (mask & VARSYM_SYS_MASK)) { 406 if (lp != NULL && td->td_ucred->cr_prison) { 407 vsslock(&vss, &td->td_ucred->cr_prison->pr_varsymset); 408 ve = varsymlookup(vss, name, namelen); 409 } else { 410 vsslock(&vss, &varsymset_sys); 411 ve = varsymlookup(vss, name, namelen); 412 } 413 } 414 if (ve) { 415 sym = ve->ve_sym; 416 atomic_add_int(&sym->vs_refs, 1); 417 } else { 418 sym = NULL; 419 } 420 if (vss) 421 lockmgr(&vss->vx_lock, LK_RELEASE); 422 return sym; 423 } 424 425 int 426 varsymmake(int level, const char *name, const char *data) 427 { 428 struct varsymset *vss = NULL; 429 struct varsyment *ve; 430 struct thread *td; 431 struct proc *p; 432 struct lwp *lp; 433 varsym_t sym; 434 int namelen = strlen(name); 435 int datalen; 436 int error; 437 438 td = curthread; 439 lp = td->td_lwp; 440 p = lp ? lp->lwp_proc : NULL; 441 442 switch(level) { 443 case VARSYM_PROC: 444 if (p) 445 vss = &p->p_varsymset; 446 break; 447 case VARSYM_USER: 448 if (lp) 449 vss = &td->td_ucred->cr_uidinfo->ui_varsymset; 450 break; 451 case VARSYM_SYS: 452 vss = &varsymset_sys; 453 break; 454 case VARSYM_PRISON: 455 if (lp && td->td_ucred->cr_prison) 456 vss = &td->td_ucred->cr_prison->pr_varsymset; 457 break; 458 } 459 if (vss == NULL) { 460 return EINVAL; 461 } 462 lockmgr(&vss->vx_lock, LK_EXCLUSIVE); 463 if (data && vss->vx_setsize >= MAXVARSYM_SET) { 464 error = E2BIG; 465 } else if (data) { 466 datalen = strlen(data); 467 ve = kmalloc(sizeof(struct varsyment), M_VARSYM, M_WAITOK|M_ZERO); 468 sym = kmalloc(sizeof(struct varsym) + namelen + datalen + 2, M_VARSYM, M_WAITOK); 469 ve->ve_sym = sym; 470 sym->vs_refs = 1; 471 sym->vs_namelen = namelen; 472 sym->vs_name = (char *)(sym + 1); 473 sym->vs_data = sym->vs_name + namelen + 1; 474 strcpy(sym->vs_name, name); 475 strcpy(sym->vs_data, data); 476 TAILQ_INSERT_TAIL(&vss->vx_queue, ve, ve_entry); 477 vss->vx_setsize += sizeof(struct varsyment) + sizeof(struct varsym) + namelen + datalen + 8; 478 error = 0; 479 } else { 480 if ((ve = varsymlookup(vss, name, namelen)) != NULL) { 481 TAILQ_REMOVE(&vss->vx_queue, ve, ve_entry); 482 vss->vx_setsize -= sizeof(struct varsyment) + sizeof(struct varsym) + namelen + strlen(ve->ve_sym->vs_data) + 8; 483 varsymdrop(ve->ve_sym); 484 kfree(ve, M_VARSYM); 485 error = 0; 486 } else { 487 error = ENOENT; 488 } 489 } 490 lockmgr(&vss->vx_lock, LK_RELEASE); 491 return(error); 492 } 493 494 void 495 varsymdrop(varsym_t sym) 496 { 497 KKASSERT(sym->vs_refs > 0); 498 if (atomic_fetchadd_int(&sym->vs_refs, -1) == 1) { 499 kfree(sym, M_VARSYM); 500 } 501 } 502 503 /* 504 * Insert a duplicate of ve in vss. Does not do any locking, 505 * so it is the callers responsibility to make sure nobody 506 * else can mess with the TAILQ in vss at the same time. 507 */ 508 static void 509 varsymdup(struct varsymset *vss, struct varsyment *ve) 510 { 511 struct varsyment *nve; 512 513 nve = kmalloc(sizeof(struct varsyment), M_VARSYM, M_WAITOK|M_ZERO); 514 nve->ve_sym = ve->ve_sym; 515 ++nve->ve_sym->vs_refs; /* can't be reached, no need for atomic add */ 516 /* 517 * We're only called through varsymset_init() so vss is not yet reachable, 518 * no need to lock. 519 */ 520 TAILQ_INSERT_TAIL(&vss->vx_queue, nve, ve_entry); 521 } 522 523 void 524 varsymset_init(struct varsymset *vss, struct varsymset *copy) 525 { 526 struct varsyment *ve; 527 528 TAILQ_INIT(&vss->vx_queue); 529 lockinit(&vss->vx_lock, "vx", 0, 0); 530 if (copy) { 531 TAILQ_FOREACH(ve, ©->vx_queue, ve_entry) { 532 varsymdup(vss, ve); 533 } 534 vss->vx_setsize = copy->vx_setsize; 535 } 536 } 537 538 void 539 varsymset_clean(struct varsymset *vss) 540 { 541 struct varsyment *ve; 542 543 lockmgr(&vss->vx_lock, LK_EXCLUSIVE); 544 while ((ve = TAILQ_FIRST(&vss->vx_queue)) != NULL) { 545 TAILQ_REMOVE(&vss->vx_queue, ve, ve_entry); 546 varsymdrop(ve->ve_sym); 547 kfree(ve, M_VARSYM); 548 } 549 vss->vx_setsize = 0; 550 lockmgr(&vss->vx_lock, LK_RELEASE); 551 } 552 553