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 * $DragonFly: src/sys/kern/kern_varsym.c,v 1.9 2007/04/30 07:18:54 dillon Exp $ 35 */ 36 37 /* 38 * This module implements variable storage and management for variant 39 * symlinks. These variables may also be used for general purposes. 40 */ 41 42 #include <sys/param.h> 43 #include <sys/systm.h> 44 #include <sys/kernel.h> 45 #include <sys/ucred.h> 46 #include <sys/resourcevar.h> 47 #include <sys/proc.h> 48 #include <sys/priv.h> 49 #include <sys/jail.h> 50 #include <sys/queue.h> 51 #include <sys/sysctl.h> 52 #include <sys/malloc.h> 53 #include <sys/varsym.h> 54 #include <sys/sysproto.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 proc *p; 140 int error; 141 142 p = curproc; 143 144 if ((error = copyinstr(uap->name, name, sizeof(name), NULL)) != 0) 145 goto done2; 146 buf = kmalloc(MAXVARSYM_DATA, M_TEMP, M_WAITOK); 147 if (uap->data && 148 (error = copyinstr(uap->data, buf, MAXVARSYM_DATA, NULL)) != 0) 149 { 150 goto done1; 151 } 152 153 get_mplock(); 154 155 switch(uap->level) { 156 case VARSYM_SYS: 157 if (p != NULL && p->p_ucred->cr_prison != NULL) 158 uap->level = VARSYM_PRISON; 159 case VARSYM_PRISON: 160 if (p != NULL && 161 (error = priv_check_cred(p->p_ucred, PRIV_VARSYM_SYS, 0)) != 0) 162 break; 163 /* fall through */ 164 case VARSYM_USER: 165 /* XXX check jail / implement per-jail user */ 166 /* fall through */ 167 case VARSYM_PROC: 168 if (uap->data) { 169 (void)varsymmake(uap->level, name, NULL); 170 error = varsymmake(uap->level, name, buf); 171 } else { 172 error = varsymmake(uap->level, name, NULL); 173 } 174 break; 175 } 176 rel_mplock(); 177 done1: 178 kfree(buf, M_TEMP); 179 done2: 180 return(error); 181 } 182 183 /* 184 * varsym_get() system call 185 * 186 * (int mask, const char *wild, char *buf, int bufsize) 187 * 188 * MPALMOSTSAFE 189 */ 190 int 191 sys_varsym_get(struct varsym_get_args *uap) 192 { 193 char wild[MAXVARSYM_NAME]; 194 varsym_t sym; 195 int error; 196 int dlen; 197 198 get_mplock(); 199 if ((error = copyinstr(uap->wild, wild, sizeof(wild), NULL)) != 0) 200 goto done; 201 sym = varsymfind(uap->mask, wild, strlen(wild)); 202 if (sym == NULL) { 203 error = ENOENT; 204 goto done; 205 } 206 dlen = strlen(sym->vs_data); 207 if (dlen < uap->bufsize) { 208 copyout(sym->vs_data, uap->buf, dlen + 1); 209 } else if (uap->bufsize) { 210 copyout("", uap->buf, 1); 211 } 212 uap->sysmsg_result = dlen + 1; 213 varsymdrop(sym); 214 done: 215 rel_mplock(); 216 return(error); 217 } 218 219 /* 220 * varsym_list() system call 221 * 222 * (int level, char *buf, int maxsize, int *marker) 223 * 224 * MPALMOSTSAFE 225 */ 226 int 227 sys_varsym_list(struct varsym_list_args *uap) 228 { 229 struct varsymset *vss; 230 struct varsyment *ve; 231 struct proc *p; 232 int i; 233 int error; 234 int bytes; 235 int earlyterm; 236 int marker; 237 238 /* 239 * Get the marker from userspace. 240 */ 241 get_mplock(); 242 if ((error = copyin(uap->marker, &marker, sizeof(marker))) != 0) 243 goto done; 244 245 /* 246 * Figure out the varsym set. 247 */ 248 p = curproc; 249 vss = NULL; 250 251 switch (uap->level) { 252 case VARSYM_PROC: 253 if (p) 254 vss = &p->p_varsymset; 255 break; 256 case VARSYM_USER: 257 if (p) 258 vss = &p->p_ucred->cr_uidinfo->ui_varsymset; 259 break; 260 case VARSYM_SYS: 261 vss = &varsymset_sys; 262 break; 263 case VARSYM_PRISON: 264 if (p != NULL && p->p_ucred->cr_prison != NULL) 265 vss = &p->p_ucred->cr_prison->pr_varsymset; 266 break; 267 } 268 if (vss == NULL) { 269 error = EINVAL; 270 goto done; 271 } 272 273 /* 274 * Loop through the variables and dump them to uap->buf 275 */ 276 i = 0; 277 bytes = 0; 278 earlyterm = 0; 279 280 lockmgr(&vss->vx_lock, LK_SHARED); 281 TAILQ_FOREACH(ve, &vss->vx_queue, ve_entry) { 282 varsym_t sym = ve->ve_sym; 283 int namelen = strlen(sym->vs_name); 284 int datalen = strlen(sym->vs_data); 285 int totlen = namelen + datalen + 2; 286 287 /* 288 * Skip to our index point 289 */ 290 if (i < marker) { 291 ++i; 292 continue; 293 } 294 295 /* 296 * Stop if there is insufficient space in the user buffer. 297 * If we haven't stored anything yet return EOVERFLOW. 298 * Note that the marker index (i) does not change. 299 */ 300 if (bytes + totlen > uap->maxsize) { 301 if (bytes == 0) 302 error = EOVERFLOW; 303 earlyterm = 1; 304 break; 305 } 306 307 error = copyout(sym->vs_name, uap->buf + bytes, namelen + 1); 308 if (error == 0) { 309 bytes += namelen + 1; 310 error = copyout(sym->vs_data, uap->buf + bytes, datalen + 1); 311 if (error == 0) 312 bytes += datalen + 1; 313 else 314 bytes -= namelen + 1; /* revert if error */ 315 } 316 if (error) { 317 earlyterm = 1; 318 break; 319 } 320 ++i; 321 } 322 lockmgr(&vss->vx_lock, LK_RELEASE); 323 324 /* 325 * Save the marker back. If no error occured and earlyterm is clear 326 * the marker is set to -1 indicating that the variable list has been 327 * exhausted. If no error occured the number of bytes loaded into 328 * the buffer will be returned, otherwise the syscall code returns -1. 329 */ 330 if (error == 0 && earlyterm == 0) 331 marker = -1; 332 else 333 marker = i; 334 if (error == 0) 335 error = copyout(&marker, uap->marker, sizeof(marker)); 336 uap->sysmsg_result = bytes; 337 done: 338 rel_mplock(); 339 return(error); 340 } 341 342 /* 343 * Lookup a variant symlink. XXX use a hash table. 344 */ 345 static 346 struct varsyment * 347 varsymlookup(struct varsymset *vss, const char *name, int namelen) 348 { 349 struct varsyment *ve; 350 351 KKASSERT(lockstatus(&vss->vx_lock, curthread) != 0); 352 TAILQ_FOREACH(ve, &vss->vx_queue, ve_entry) { 353 varsym_t var = ve->ve_sym; 354 if (var->vs_namelen == namelen && 355 bcmp(name, var->vs_name, namelen) == 0 356 ) { 357 return(ve); 358 } 359 } 360 return(NULL); 361 } 362 363 static 364 void 365 vsslock(struct varsymset **vss, struct varsymset *n) 366 { 367 if (*vss) { 368 lockmgr(&(*vss)->vx_lock, LK_RELEASE); 369 } 370 lockmgr(&n->vx_lock, LK_SHARED); 371 *vss = n; 372 } 373 374 varsym_t 375 varsymfind(int mask, const char *name, int namelen) 376 { 377 struct proc *p = curproc; 378 struct varsyment *ve = NULL; 379 struct varsymset *vss = NULL; 380 varsym_t sym; 381 382 if ((mask & (VARSYM_PROC_MASK|VARSYM_USER_MASK)) && p != NULL) { 383 if (mask & VARSYM_PROC_MASK) { 384 vsslock(&vss, &p->p_varsymset); 385 ve = varsymlookup(vss, name, namelen); 386 } 387 if (ve == NULL && (mask & VARSYM_USER_MASK)) { 388 vsslock(&vss, &p->p_ucred->cr_uidinfo->ui_varsymset); 389 ve = varsymlookup(vss, name, namelen); 390 } 391 } 392 if (ve == NULL && (mask & VARSYM_SYS_MASK)) { 393 if (p != NULL && p->p_ucred->cr_prison) { 394 vsslock(&vss, &p->p_ucred->cr_prison->pr_varsymset); 395 ve = varsymlookup(vss, name, namelen); 396 } else { 397 vsslock(&vss, &varsymset_sys); 398 ve = varsymlookup(vss, name, namelen); 399 } 400 } 401 if (ve) { 402 sym = ve->ve_sym; 403 atomic_add_int(&sym->vs_refs, 1); 404 } else { 405 sym = NULL; 406 } 407 lockmgr(&vss->vx_lock, LK_RELEASE); 408 return sym; 409 } 410 411 int 412 varsymmake(int level, const char *name, const char *data) 413 { 414 struct varsymset *vss = NULL; 415 struct varsyment *ve; 416 struct proc *p = curproc; 417 varsym_t sym; 418 int namelen = strlen(name); 419 int datalen; 420 int error; 421 422 switch(level) { 423 case VARSYM_PROC: 424 if (p) 425 vss = &p->p_varsymset; 426 break; 427 case VARSYM_USER: 428 if (p) 429 vss = &p->p_ucred->cr_uidinfo->ui_varsymset; 430 break; 431 case VARSYM_SYS: 432 vss = &varsymset_sys; 433 break; 434 case VARSYM_PRISON: 435 if (p != NULL && p->p_ucred->cr_prison != NULL) 436 vss = &p->p_ucred->cr_prison->pr_varsymset; 437 break; 438 } 439 if (vss == NULL) { 440 return EINVAL; 441 } 442 lockmgr(&vss->vx_lock, LK_EXCLUSIVE); 443 if (data && vss->vx_setsize >= MAXVARSYM_SET) { 444 error = E2BIG; 445 } else if (data) { 446 datalen = strlen(data); 447 ve = kmalloc(sizeof(struct varsyment), M_VARSYM, M_WAITOK|M_ZERO); 448 sym = kmalloc(sizeof(struct varsym) + namelen + datalen + 2, M_VARSYM, M_WAITOK); 449 ve->ve_sym = sym; 450 sym->vs_refs = 1; 451 sym->vs_namelen = namelen; 452 sym->vs_name = (char *)(sym + 1); 453 sym->vs_data = sym->vs_name + namelen + 1; 454 strcpy(sym->vs_name, name); 455 strcpy(sym->vs_data, data); 456 TAILQ_INSERT_TAIL(&vss->vx_queue, ve, ve_entry); 457 vss->vx_setsize += sizeof(struct varsyment) + sizeof(struct varsym) + namelen + datalen + 8; 458 error = 0; 459 } else { 460 if ((ve = varsymlookup(vss, name, namelen)) != NULL) { 461 TAILQ_REMOVE(&vss->vx_queue, ve, ve_entry); 462 vss->vx_setsize -= sizeof(struct varsyment) + sizeof(struct varsym) + namelen + strlen(ve->ve_sym->vs_data) + 8; 463 varsymdrop(ve->ve_sym); 464 kfree(ve, M_VARSYM); 465 error = 0; 466 } else { 467 error = ENOENT; 468 } 469 } 470 lockmgr(&vss->vx_lock, LK_RELEASE); 471 return(error); 472 } 473 474 void 475 varsymdrop(varsym_t sym) 476 { 477 KKASSERT(sym->vs_refs > 0); 478 if (atomic_fetchadd_int(&sym->vs_refs, -1) == 1) { 479 kfree(sym, M_VARSYM); 480 } 481 } 482 483 /* 484 * Insert a duplicate of ve in vss. Does not do any locking, 485 * so it is the callers responsibility to make sure nobody 486 * else can mess with the TAILQ in vss at the same time. 487 */ 488 static void 489 varsymdup(struct varsymset *vss, struct varsyment *ve) 490 { 491 struct varsyment *nve; 492 493 nve = kmalloc(sizeof(struct varsyment), M_VARSYM, M_WAITOK|M_ZERO); 494 nve->ve_sym = ve->ve_sym; 495 ++nve->ve_sym->vs_refs; /* can't be reached, no need for atomic add */ 496 /* 497 * We're only called through varsymset_init() so vss is not yet reachable, 498 * no need to lock. 499 */ 500 TAILQ_INSERT_TAIL(&vss->vx_queue, nve, ve_entry); 501 } 502 503 void 504 varsymset_init(struct varsymset *vss, struct varsymset *copy) 505 { 506 struct varsyment *ve; 507 508 TAILQ_INIT(&vss->vx_queue); 509 lockinit(&vss->vx_lock, "vx", 0, 0); 510 if (copy) { 511 TAILQ_FOREACH(ve, ©->vx_queue, ve_entry) { 512 varsymdup(vss, ve); 513 } 514 vss->vx_setsize = copy->vx_setsize; 515 } 516 } 517 518 void 519 varsymset_clean(struct varsymset *vss) 520 { 521 struct varsyment *ve; 522 523 lockmgr(&vss->vx_lock, LK_EXCLUSIVE); 524 while ((ve = TAILQ_FIRST(&vss->vx_queue)) != NULL) { 525 TAILQ_REMOVE(&vss->vx_queue, ve, ve_entry); 526 varsymdrop(ve->ve_sym); 527 kfree(ve, M_VARSYM); 528 } 529 vss->vx_setsize = 0; 530 lockmgr(&vss->vx_lock, LK_RELEASE); 531 } 532 533