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