1/*- 2 * Copyright (c) 1996 by 3 * Sean Eric Fagan <sef@kithrup.com> 4 * David Nugent <davidn@blaze.net.au> 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, is permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice immediately at the beginning of the file, without modification, 12 * 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 * 3. This work was done expressly for inclusion into FreeBSD. Other use 17 * is permitted provided this notation is included. 18 * 4. Absolutely no warranty of function or purpose is made by the authors. 19 * 5. Modifications may be freely made to this file providing the above 20 * conditions are met. 21 * 22 * High-level routines relating to use of the user capabilities database 23 */ 24 25#include <sys/cdefs.h> 26__FBSDID("$FreeBSD$"); 27 28#include <sys/param.h> 29#include <sys/cpuset.h> 30#include <sys/mac.h> 31#include <sys/resource.h> 32#include <sys/rtprio.h> 33#include <sys/stat.h> 34#include <sys/time.h> 35 36#include <ctype.h> 37#include <err.h> 38#include <errno.h> 39#include <fcntl.h> 40#include <login_cap.h> 41#include <paths.h> 42#include <pwd.h> 43#include <signal.h> 44#include <stdio.h> 45#include <stdlib.h> 46#include <string.h> 47#include <syslog.h> 48#include <unistd.h> 49 50 51static struct login_res { 52 const char *what; 53 rlim_t (*who)(login_cap_t *, const char *, rlim_t, rlim_t); 54 int why; 55} resources[] = { 56 { "cputime", login_getcaptime, RLIMIT_CPU }, 57 { "filesize", login_getcapsize, RLIMIT_FSIZE }, 58 { "datasize", login_getcapsize, RLIMIT_DATA }, 59 { "stacksize", login_getcapsize, RLIMIT_STACK }, 60 { "memoryuse", login_getcapsize, RLIMIT_RSS }, 61 { "memorylocked", login_getcapsize, RLIMIT_MEMLOCK }, 62 { "maxproc", login_getcapnum, RLIMIT_NPROC }, 63 { "openfiles", login_getcapnum, RLIMIT_NOFILE }, 64 { "coredumpsize", login_getcapsize, RLIMIT_CORE }, 65 { "sbsize", login_getcapsize, RLIMIT_SBSIZE }, 66 { "vmemoryuse", login_getcapsize, RLIMIT_VMEM }, 67 { "pseudoterminals", login_getcapnum, RLIMIT_NPTS }, 68 { "swapuse", login_getcapsize, RLIMIT_SWAP }, 69 { NULL, 0, 0 } 70}; 71 72 73void 74setclassresources(login_cap_t *lc) 75{ 76 struct login_res *lr; 77 78 if (lc == NULL) 79 return; 80 81 for (lr = resources; lr->what != NULL; ++lr) { 82 struct rlimit rlim; 83 84 /* 85 * The login.conf file can have <limit>, <limit>-max, and 86 * <limit>-cur entries. 87 * What we do is get the current current- and maximum- limits. 88 * Then, we try to get an entry for <limit> from the capability, 89 * using the current and max limits we just got as the 90 * default/error values. 91 * *Then*, we try looking for <limit>-cur and <limit>-max, 92 * again using the appropriate values as the default/error 93 * conditions. 94 */ 95 96 if (getrlimit(lr->why, &rlim) != 0) 97 syslog(LOG_ERR, "getting %s resource limit: %m", lr->what); 98 else { 99 char name_cur[40]; 100 char name_max[40]; 101 rlim_t rcur = rlim.rlim_cur; 102 rlim_t rmax = rlim.rlim_max; 103 104 sprintf(name_cur, "%s-cur", lr->what); 105 sprintf(name_max, "%s-max", lr->what); 106 107 rcur = (*lr->who)(lc, lr->what, rcur, rcur); 108 rmax = (*lr->who)(lc, lr->what, rmax, rmax); 109 rlim.rlim_cur = (*lr->who)(lc, name_cur, rcur, rcur); 110 rlim.rlim_max = (*lr->who)(lc, name_max, rmax, rmax); 111 112 if (setrlimit(lr->why, &rlim) == -1) 113 syslog(LOG_WARNING, "set class '%s' resource limit %s: %m", lc->lc_class, lr->what); 114 } 115 } 116} 117 118 119 120static struct login_vars { 121 const char *tag; 122 const char *var; 123 const char *def; 124 int overwrite; 125} pathvars[] = { 126 { "path", "PATH", NULL, 1}, 127 { "cdpath", "CDPATH", NULL, 1}, 128 { "manpath", "MANPATH", NULL, 1}, 129 { NULL, NULL, NULL, 0} 130}, envars[] = { 131 { "lang", "LANG", NULL, 1}, 132 { "charset", "MM_CHARSET", NULL, 1}, 133 { "timezone", "TZ", NULL, 1}, 134 { "term", "TERM", NULL, 0}, 135 { NULL, NULL, NULL, 0} 136}; 137 138static char * 139substvar(const char * var, const struct passwd * pwd, int hlen, int pch, int nlen) 140{ 141 char *np = NULL; 142 143 if (var != NULL) { 144 int tildes = 0; 145 int dollas = 0; 146 char *p; 147 const char *q; 148 149 if (pwd != NULL) { 150 for (q = var; *q != '\0'; ++q) { 151 tildes += (*q == '~'); 152 dollas += (*q == '$'); 153 } 154 } 155 156 np = malloc(strlen(var) + (dollas * nlen) 157 - dollas + (tildes * (pch+hlen)) 158 - tildes + 1); 159 160 if (np != NULL) { 161 p = strcpy(np, var); 162 163 if (pwd != NULL) { 164 /* 165 * This loop does user username and homedir substitutions 166 * for unescaped $ (username) and ~ (homedir) 167 */ 168 while (*(p += strcspn(p, "~$")) != '\0') { 169 int l = strlen(p); 170 171 if (p > np && *(p-1) == '\\') /* Escaped: */ 172 memmove(p - 1, p, l + 1); /* Slide-out the backslash */ 173 else if (*p == '~') { 174 int v = pch && *(p+1) != '/'; /* Avoid double // */ 175 memmove(p + hlen + v, p + 1, l); /* Subst homedir */ 176 memmove(p, pwd->pw_dir, hlen); 177 if (v) 178 p[hlen] = '/'; 179 p += hlen + v; 180 } 181 else /* if (*p == '$') */ { 182 memmove(p + nlen, p + 1, l); /* Subst username */ 183 memmove(p, pwd->pw_name, nlen); 184 p += nlen; 185 } 186 } 187 } 188 } 189 } 190 191 return (np); 192} 193 194 195void 196setclassenvironment(login_cap_t *lc, const struct passwd * pwd, int paths) 197{ 198 struct login_vars *vars = paths ? pathvars : envars; 199 int hlen = pwd ? strlen(pwd->pw_dir) : 0; 200 int nlen = pwd ? strlen(pwd->pw_name) : 0; 201 char pch = 0; 202 203 if (hlen && pwd->pw_dir[hlen-1] != '/') 204 ++pch; 205 206 while (vars->tag != NULL) { 207 const char * var = paths ? login_getpath(lc, vars->tag, NULL) 208 : login_getcapstr(lc, vars->tag, NULL, NULL); 209 210 char * np = substvar(var, pwd, hlen, pch, nlen); 211 212 if (np != NULL) { 213 setenv(vars->var, np, vars->overwrite); 214 free(np); 215 } else if (vars->def != NULL) { 216 setenv(vars->var, vars->def, 0); 217 } 218 ++vars; 219 } 220 221 /* 222 * If we're not processing paths, then see if there is a setenv list by 223 * which the admin and/or user may set an arbitrary set of env vars. 224 */ 225 if (!paths) { 226 const char **set_env = login_getcaplist(lc, "setenv", ","); 227 228 if (set_env != NULL) { 229 while (*set_env != NULL) { 230 char *p = strchr(*set_env, '='); 231 232 if (p != NULL) { /* Discard invalid entries */ 233 char *np; 234 235 *p++ = '\0'; 236 if ((np = substvar(p, pwd, hlen, pch, nlen)) != NULL) { 237 setenv(*set_env, np, 1); 238 free(np); 239 } 240 } 241 ++set_env; 242 } 243 } 244 } 245} 246 247 248static int 249list2cpuset(const char *list, cpuset_t *mask) 250{ 251 enum { NONE, NUM, DASH } state; 252 int lastnum; 253 int curnum; 254 const char *l; 255 256 state = NONE; 257 curnum = lastnum = 0; 258 for (l = list; *l != '\0';) { 259 if (isdigit(*l)) { 260 curnum = atoi(l); 261 if (curnum > CPU_SETSIZE) 262 errx(EXIT_FAILURE, 263 "Only %d cpus supported", CPU_SETSIZE); 264 while (isdigit(*l)) 265 l++; 266 switch (state) { 267 case NONE: 268 lastnum = curnum; 269 state = NUM; 270 break; 271 case DASH: 272 for (; lastnum <= curnum; lastnum++) 273 CPU_SET(lastnum, mask); 274 state = NONE; 275 break; 276 case NUM: 277 default: 278 return (0); 279 } 280 continue; 281 } 282 switch (*l) { 283 case ',': 284 switch (state) { 285 case NONE: 286 break; 287 case NUM: 288 CPU_SET(curnum, mask); 289 state = NONE; 290 break; 291 case DASH: 292 return (0); 293 break; 294 } 295 break; 296 case '-': 297 if (state != NUM) 298 return (0); 299 state = DASH; 300 break; 301 default: 302 return (0); 303 } 304 l++; 305 } 306 switch (state) { 307 case NONE: 308 break; 309 case NUM: 310 CPU_SET(curnum, mask); 311 break; 312 case DASH: 313 return (0); 314 } 315 return (1); 316} 317 318 319void 320setclasscpumask(login_cap_t *lc) 321{ 322 const char *maskstr; 323 cpuset_t maskset; 324 cpusetid_t setid; 325 326 maskstr = login_getcapstr(lc, "cpumask", NULL, NULL); 327 CPU_ZERO(&maskset); 328 if (maskstr == NULL) 329 return; 330 if (strcasecmp("default", maskstr) == 0) 331 return; 332 if (!list2cpuset(maskstr, &maskset)) { 333 syslog(LOG_WARNING, 334 "list2cpuset(%s) invalid mask specification", maskstr); 335 return; 336 } 337 338 if (cpuset(&setid) != 0) { 339 syslog(LOG_ERR, "cpuset(): %s", strerror(errno)); 340 return; 341 } 342 343 if (cpuset_setaffinity(CPU_LEVEL_CPUSET, CPU_WHICH_PID, -1, 344 sizeof(maskset), &maskset) != 0) 345 syslog(LOG_ERR, "cpuset_setaffinity(%s): %s", maskstr, 346 strerror(errno)); 347} 348 349 350/* 351 * setclasscontext() 352 * 353 * For the login class <class>, set various class context values 354 * (limits, mainly) to the values for that class. Which values are 355 * set are controlled by <flags> -- see <login_class.h> for the 356 * possible values. 357 * 358 * setclasscontext() can only set resources, priority, and umask. 359 */ 360 361int 362setclasscontext(const char *classname, unsigned int flags) 363{ 364 int rc; 365 login_cap_t *lc; 366 367 lc = login_getclassbyname(classname, NULL); 368 369 flags &= LOGIN_SETRESOURCES | LOGIN_SETPRIORITY | 370 LOGIN_SETUMASK | LOGIN_SETPATH; 371 372 rc = lc ? setusercontext(lc, NULL, 0, flags) : -1; 373 login_close(lc); 374 return (rc); 375} 376 377 378 379/* 380 * Private function which takes care of processing 381 */ 382 383static mode_t 384setlogincontext(login_cap_t *lc, const struct passwd *pwd, 385 mode_t mymask, unsigned long flags) 386{ 387 if (lc) { 388 /* Set resources */ 389 if (flags & LOGIN_SETRESOURCES) 390 setclassresources(lc); 391 /* See if there's a umask override */ 392 if (flags & LOGIN_SETUMASK) 393 mymask = (mode_t)login_getcapnum(lc, "umask", mymask, mymask); 394 /* Set paths */ 395 if (flags & LOGIN_SETPATH) 396 setclassenvironment(lc, pwd, 1); 397 /* Set environment */ 398 if (flags & LOGIN_SETENV) 399 setclassenvironment(lc, pwd, 0); 400 /* Set cpu affinity */ 401 if (flags & LOGIN_SETCPUMASK) 402 setclasscpumask(lc); 403 } 404 return (mymask); 405} 406 407 408 409/* 410 * setusercontext() 411 * 412 * Given a login class <lc> and a user in <pwd>, with a uid <uid>, 413 * set the context as in setclasscontext(). <flags> controls which 414 * values are set. 415 * 416 * The difference between setclasscontext() and setusercontext() is 417 * that the former sets things up for an already-existing process, 418 * while the latter sets things up from a root context. Such as might 419 * be called from login(1). 420 * 421 */ 422 423int 424setusercontext(login_cap_t *lc, const struct passwd *pwd, uid_t uid, unsigned int flags) 425{ 426 quad_t p; 427 mode_t mymask; 428 login_cap_t *llc = NULL; 429 struct sigaction sa, prevsa; 430 struct rtprio rtp; 431 int error; 432 433 if (lc == NULL) { 434 if (pwd != NULL && (lc = login_getpwclass(pwd)) != NULL) 435 llc = lc; /* free this when we're done */ 436 } 437 438 if (flags & LOGIN_SETPATH) 439 pathvars[0].def = uid ? _PATH_DEFPATH : _PATH_STDPATH; 440 441 /* we need a passwd entry to set these */ 442 if (pwd == NULL) 443 flags &= ~(LOGIN_SETGROUP | LOGIN_SETLOGIN | LOGIN_SETMAC); 444 445 /* Set the process priority */ 446 if (flags & LOGIN_SETPRIORITY) { 447 p = login_getcapnum(lc, "priority", LOGIN_DEFPRI, LOGIN_DEFPRI); 448 449 if (p > PRIO_MAX) { 450 rtp.type = RTP_PRIO_IDLE; 451 rtp.prio = p - PRIO_MAX - 1; 452 p = (rtp.prio > RTP_PRIO_MAX) ? 31 : p; 453 if (rtprio(RTP_SET, 0, &rtp)) 454 syslog(LOG_WARNING, "rtprio '%s' (%s): %m", 455 pwd ? pwd->pw_name : "-", 456 lc ? lc->lc_class : LOGIN_DEFCLASS); 457 } else if (p < PRIO_MIN) { 458 rtp.type = RTP_PRIO_REALTIME; 459 rtp.prio = abs(p - PRIO_MIN + RTP_PRIO_MAX); 460 p = (rtp.prio > RTP_PRIO_MAX) ? 1 : p; 461 if (rtprio(RTP_SET, 0, &rtp)) 462 syslog(LOG_WARNING, "rtprio '%s' (%s): %m", 463 pwd ? pwd->pw_name : "-", 464 lc ? lc->lc_class : LOGIN_DEFCLASS); 465 } else { 466 if (setpriority(PRIO_PROCESS, 0, (int)p) != 0) 467 syslog(LOG_WARNING, "setpriority '%s' (%s): %m", 468 pwd ? pwd->pw_name : "-", 469 lc ? lc->lc_class : LOGIN_DEFCLASS); 470 } 471 } 472 473 /* Setup the user's group permissions */ 474 if (flags & LOGIN_SETGROUP) { 475 if (setgid(pwd->pw_gid) != 0) { 476 syslog(LOG_ERR, "setgid(%lu): %m", (u_long)pwd->pw_gid); 477 login_close(llc); 478 return (-1); 479 } 480 if (initgroups(pwd->pw_name, pwd->pw_gid) == -1) { 481 syslog(LOG_ERR, "initgroups(%s,%lu): %m", pwd->pw_name, 482 (u_long)pwd->pw_gid); 483 login_close(llc); 484 return (-1); 485 } 486 } 487 488 /* Set up the user's MAC label. */ 489 if ((flags & LOGIN_SETMAC) && mac_is_present(NULL) == 1) { 490 const char *label_string; 491 mac_t label; 492 493 label_string = login_getcapstr(lc, "label", NULL, NULL); 494 if (label_string != NULL) { 495 if (mac_from_text(&label, label_string) == -1) { 496 syslog(LOG_ERR, "mac_from_text('%s') for %s: %m", 497 pwd->pw_name, label_string); 498 return (-1); 499 } 500 if (mac_set_proc(label) == -1) 501 error = errno; 502 else 503 error = 0; 504 mac_free(label); 505 if (error != 0) { 506 syslog(LOG_ERR, "mac_set_proc('%s') for %s: %s", 507 label_string, pwd->pw_name, strerror(error)); 508 return (-1); 509 } 510 } 511 } 512 513 /* Set the sessions login */ 514 if ((flags & LOGIN_SETLOGIN) && setlogin(pwd->pw_name) != 0) { 515 syslog(LOG_ERR, "setlogin(%s): %m", pwd->pw_name); 516 login_close(llc); 517 return (-1); 518 } 519 520 /* Inform the kernel about current login class */ 521 if (lc != NULL && lc->lc_class != NULL && (flags & LOGIN_SETLOGINCLASS)) { 522 /* 523 * XXX: This is a workaround to fail gracefully in case the kernel 524 * does not support setloginclass(2). 525 */ 526 bzero(&sa, sizeof(sa)); 527 sa.sa_handler = SIG_IGN; 528 sigfillset(&sa.sa_mask); 529 sigaction(SIGSYS, &sa, &prevsa); 530 error = setloginclass(lc->lc_class); 531 sigaction(SIGSYS, &prevsa, NULL); 532 if (error != 0) { 533 syslog(LOG_ERR, "setloginclass(%s): %m", lc->lc_class); 534#ifdef notyet 535 login_close(llc); 536 return (-1); 537#endif 538 } 539 } 540 541 mymask = (flags & LOGIN_SETUMASK) ? umask(LOGIN_DEFUMASK) : 0; 542 mymask = setlogincontext(lc, pwd, mymask, flags); 543 login_close(llc); 544 545 /* This needs to be done after anything that needs root privs */ 546 if ((flags & LOGIN_SETUSER) && setuid(uid) != 0) { 547 syslog(LOG_ERR, "setuid(%lu): %m", (u_long)uid); 548 return (-1); /* Paranoia again */ 549 } 550 551 /* 552 * Now, we repeat some of the above for the user's private entries 553 */ 554 if (getuid() == uid && (lc = login_getuserclass(pwd)) != NULL) { 555 mymask = setlogincontext(lc, pwd, mymask, flags); 556 login_close(lc); 557 } 558 559 /* Finally, set any umask we've found */ 560 if (flags & LOGIN_SETUMASK) 561 umask(mymask); 562 563 return (0); 564} 565