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