1 /* $OpenBSD: login_cap.c,v 1.46 2022/12/27 17:10:06 jmc Exp $ */ 2 3 /* 4 * Copyright (c) 2000-2004 Todd C. Miller <millert@openbsd.org> 5 * 6 * Permission to use, copy, modify, and distribute this software for any 7 * purpose with or without fee is hereby granted, provided that the above 8 * copyright notice and this permission notice appear in all copies. 9 * 10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 */ 18 /*- 19 * Copyright (c) 1995,1997 Berkeley Software Design, Inc. All rights reserved. 20 * 21 * Redistribution and use in source and binary forms, with or without 22 * modification, are permitted provided that the following conditions 23 * are met: 24 * 1. Redistributions of source code must retain the above copyright 25 * notice, this list of conditions and the following disclaimer. 26 * 2. Redistributions in binary form must reproduce the above copyright 27 * notice, this list of conditions and the following disclaimer in the 28 * documentation and/or other materials provided with the distribution. 29 * 3. All advertising materials mentioning features or use of this software 30 * must display the following acknowledgement: 31 * This product includes software developed by Berkeley Software Design, 32 * Inc. 33 * 4. The name of Berkeley Software Design, Inc. may not be used to endorse 34 * or promote products derived from this software without specific prior 35 * written permission. 36 * 37 * THIS SOFTWARE IS PROVIDED BY BERKELEY SOFTWARE DESIGN, INC. ``AS IS'' AND 38 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 39 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 40 * ARE DISCLAIMED. IN NO EVENT SHALL BERKELEY SOFTWARE DESIGN, INC. BE LIABLE 41 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 42 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 43 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 44 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 45 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 46 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 47 * SUCH DAMAGE. 48 * 49 * BSDI $From: login_cap.c,v 2.16 2000/03/22 17:10:55 donn Exp $ 50 */ 51 #include <sys/types.h> 52 #include <sys/stat.h> 53 #include <sys/time.h> 54 #include <sys/resource.h> 55 #include <sys/socket.h> 56 57 #include <err.h> 58 #include <errno.h> 59 #include <fcntl.h> 60 #include <limits.h> 61 #include <login_cap.h> 62 #include <paths.h> 63 #include <pwd.h> 64 #include <stdio.h> 65 #include <stdlib.h> 66 #include <string.h> 67 #include <syslog.h> 68 #include <unistd.h> 69 70 71 static char *_authtypes[] = { LOGIN_DEFSTYLE, 0 }; 72 static char *expandstr(const char *, const struct passwd *, int); 73 static int login_setenv(char *, char *, const struct passwd *, int); 74 static int setuserenv(login_cap_t *, const struct passwd *); 75 static int setuserpath(login_cap_t *, const struct passwd *); 76 static u_quad_t multiply(u_quad_t, u_quad_t); 77 static u_quad_t strtolimit(char *, char **, int); 78 static u_quad_t strtosize(char *, char **, int); 79 static int gsetrl(login_cap_t *, int, char *, int); 80 81 login_cap_t * 82 login_getclass(char *class) 83 { 84 char *classfiles[] = { NULL, NULL, NULL }; 85 char classpath[PATH_MAX]; 86 login_cap_t *lc; 87 int res, i = 0; 88 89 if ((lc = calloc(1, sizeof(login_cap_t))) == NULL) { 90 syslog(LOG_ERR, "%s:%d malloc: %m", __FILE__, __LINE__); 91 return (0); 92 } 93 94 if (class == NULL || class[0] == '\0') 95 class = LOGIN_DEFCLASS; 96 else { 97 res = snprintf(classpath, PATH_MAX, "%s/%s", 98 _PATH_LOGIN_CONF_D, class); 99 if (res >= 0 && res < PATH_MAX) 100 classfiles[i++] = classpath; 101 } 102 103 classfiles[i++] = _PATH_LOGIN_CONF; 104 classfiles[i] = NULL; 105 106 if ((lc->lc_class = strdup(class)) == NULL) { 107 syslog(LOG_ERR, "%s:%d strdup: %m", __FILE__, __LINE__); 108 free(lc); 109 return (0); 110 } 111 112 if ((res = cgetent(&lc->lc_cap, classfiles, lc->lc_class)) != 0) { 113 lc->lc_cap = 0; 114 switch (res) { 115 case 1: 116 syslog(LOG_ERR, "%s: couldn't resolve 'tc'", 117 lc->lc_class); 118 break; 119 case -1: 120 if ((res = open(_PATH_LOGIN_CONF, O_RDONLY)) >= 0) 121 close(res); 122 if (strcmp(lc->lc_class, LOGIN_DEFCLASS) == 0 && 123 res < 0) 124 return (lc); 125 syslog(LOG_ERR, "%s: unknown class", lc->lc_class); 126 break; 127 case -2: 128 /* 129 * A missing login.conf file is not an error condition. 130 * The individual routines deal reasonably with missing 131 * capabilities and use default values. 132 */ 133 if (errno == ENOENT) 134 return (lc); 135 syslog(LOG_ERR, "%s: getting class information: %m", 136 lc->lc_class); 137 break; 138 case -3: 139 syslog(LOG_ERR, "%s: 'tc' reference loop", 140 lc->lc_class); 141 break; 142 default: 143 syslog(LOG_ERR, "%s: unexpected cgetent error", 144 lc->lc_class); 145 break; 146 } 147 free(lc->lc_class); 148 free(lc); 149 return (0); 150 } 151 return (lc); 152 } 153 DEF_WEAK(login_getclass); 154 155 char * 156 login_getstyle(login_cap_t *lc, char *style, char *atype) 157 { 158 char **authtypes = _authtypes; 159 char *auths, *ta; 160 char *f1 = NULL, **f2 = NULL; 161 int i; 162 163 /* Silently convert 's/key' -> 'skey' */ 164 if (style && strcmp(style, "s/key") == 0) 165 style = "skey"; 166 167 free(lc->lc_style); 168 lc->lc_style = NULL; 169 170 if (!atype || !(auths = login_getcapstr(lc, atype, NULL, NULL))) 171 auths = login_getcapstr(lc, "auth", NULL, NULL); 172 173 if (auths) { 174 f1 = ta = auths; /* auths malloced by login_getcapstr */ 175 i = 2; 176 while (*ta) 177 if (*ta++ == ',') 178 ++i; 179 f2 = authtypes = calloc(sizeof(char *), i); 180 if (!authtypes) { 181 syslog(LOG_ERR, "malloc: %m"); 182 free(f1); 183 return (0); 184 } 185 i = 0; 186 while (*auths) { 187 authtypes[i] = auths; 188 while (*auths && *auths != ',') 189 ++auths; 190 if (*auths) 191 *auths++ = 0; 192 if (!*authtypes[i]) 193 authtypes[i] = LOGIN_DEFSTYLE; 194 ++i; 195 } 196 authtypes[i] = 0; 197 } 198 199 if (!style) 200 style = authtypes[0]; 201 202 while (*authtypes && strcmp(style, *authtypes)) 203 ++authtypes; 204 205 if (*authtypes) { 206 lc->lc_style = strdup(*authtypes); 207 if (lc->lc_style == NULL) 208 syslog(LOG_ERR, "strdup: %m"); 209 } 210 free(f1); 211 free(f2); 212 return (lc->lc_style); 213 } 214 DEF_WEAK(login_getstyle); 215 216 char * 217 login_getcapstr(login_cap_t *lc, char *cap, char *def, char *e) 218 { 219 char *res = NULL, *str = e; 220 int stat; 221 222 errno = 0; 223 224 if (!lc->lc_cap) 225 return (def); 226 227 switch (stat = cgetstr(lc->lc_cap, cap, &res)) { 228 case -1: 229 str = def; 230 break; 231 case -2: 232 syslog(LOG_ERR, "%s: getting capability %s: %m", 233 lc->lc_class, cap); 234 break; 235 default: 236 if (stat >= 0) 237 str = res; 238 else 239 syslog(LOG_ERR, 240 "%s: unexpected error with capability %s", 241 lc->lc_class, cap); 242 break; 243 } 244 245 if (res != NULL && str != res) 246 free(res); 247 return(str); 248 } 249 DEF_WEAK(login_getcapstr); 250 251 quad_t 252 login_getcaptime(login_cap_t *lc, char *cap, quad_t def, quad_t e) 253 { 254 char *ep; 255 char *res = NULL, *sres; 256 int stat; 257 quad_t q, r; 258 259 errno = 0; 260 261 if (!lc->lc_cap) 262 return (def); 263 264 switch (stat = cgetstr(lc->lc_cap, cap, &res)) { 265 case -1: 266 free(res); 267 return (def); 268 case -2: 269 free(res); 270 syslog(LOG_ERR, "%s: getting capability %s: %m", 271 lc->lc_class, cap); 272 errno = ERANGE; 273 return (e); 274 default: 275 if (stat >= 0) 276 break; 277 free(res); 278 syslog(LOG_ERR, "%s: unexpected error with capability %s", 279 lc->lc_class, cap); 280 errno = ERANGE; 281 return (e); 282 } 283 284 errno = 0; 285 286 if (strcasecmp(res, "infinity") == 0) { 287 free(res); 288 return (RLIM_INFINITY); 289 } 290 291 q = 0; 292 sres = res; 293 while (*res) { 294 r = strtoll(res, &ep, 0); 295 if (!ep || ep == res || 296 ((r == QUAD_MIN || r == QUAD_MAX) && errno == ERANGE)) { 297 invalid: 298 syslog(LOG_ERR, "%s:%s=%s: invalid time", 299 lc->lc_class, cap, sres); 300 free(sres); 301 errno = ERANGE; 302 return (e); 303 } 304 switch (*ep++) { 305 case '\0': 306 --ep; 307 break; 308 case 's': case 'S': 309 break; 310 case 'm': case 'M': 311 r *= 60; 312 break; 313 case 'h': case 'H': 314 r *= 60 * 60; 315 break; 316 case 'd': case 'D': 317 r *= 60 * 60 * 24; 318 break; 319 case 'w': case 'W': 320 r *= 60 * 60 * 24 * 7; 321 break; 322 case 'y': case 'Y': /* Pretty absurd */ 323 r *= 60 * 60 * 24 * 365; 324 break; 325 default: 326 goto invalid; 327 } 328 res = ep; 329 q += r; 330 } 331 free(sres); 332 return (q); 333 } 334 DEF_WEAK(login_getcaptime); 335 336 quad_t 337 login_getcapnum(login_cap_t *lc, char *cap, quad_t def, quad_t e) 338 { 339 char *ep; 340 char *res = NULL; 341 int stat; 342 quad_t q; 343 344 errno = 0; 345 346 if (!lc->lc_cap) 347 return (def); 348 349 switch (stat = cgetstr(lc->lc_cap, cap, &res)) { 350 case -1: 351 free(res); 352 return (def); 353 case -2: 354 free(res); 355 syslog(LOG_ERR, "%s: getting capability %s: %m", 356 lc->lc_class, cap); 357 errno = ERANGE; 358 return (e); 359 default: 360 if (stat >= 0) 361 break; 362 free(res); 363 syslog(LOG_ERR, "%s: unexpected error with capability %s", 364 lc->lc_class, cap); 365 errno = ERANGE; 366 return (e); 367 } 368 369 errno = 0; 370 371 if (strcasecmp(res, "infinity") == 0) { 372 free(res); 373 return (RLIM_INFINITY); 374 } 375 376 q = strtoll(res, &ep, 0); 377 if (!ep || ep == res || ep[0] || 378 ((q == QUAD_MIN || q == QUAD_MAX) && errno == ERANGE)) { 379 syslog(LOG_ERR, "%s:%s=%s: invalid number", 380 lc->lc_class, cap, res); 381 free(res); 382 errno = ERANGE; 383 return (e); 384 } 385 free(res); 386 return (q); 387 } 388 DEF_WEAK(login_getcapnum); 389 390 quad_t 391 login_getcapsize(login_cap_t *lc, char *cap, quad_t def, quad_t e) 392 { 393 char *ep; 394 char *res = NULL; 395 int stat; 396 quad_t q; 397 398 errno = 0; 399 400 if (!lc->lc_cap) 401 return (def); 402 403 switch (stat = cgetstr(lc->lc_cap, cap, &res)) { 404 case -1: 405 free(res); 406 return (def); 407 case -2: 408 free(res); 409 syslog(LOG_ERR, "%s: getting capability %s: %m", 410 lc->lc_class, cap); 411 errno = ERANGE; 412 return (e); 413 default: 414 if (stat >= 0) 415 break; 416 free(res); 417 syslog(LOG_ERR, "%s: unexpected error with capability %s", 418 lc->lc_class, cap); 419 errno = ERANGE; 420 return (e); 421 } 422 423 errno = 0; 424 q = strtolimit(res, &ep, 0); 425 if (!ep || ep == res || (ep[0] && ep[1]) || 426 ((q == QUAD_MIN || q == QUAD_MAX) && errno == ERANGE)) { 427 syslog(LOG_ERR, "%s:%s=%s: invalid size", 428 lc->lc_class, cap, res); 429 free(res); 430 errno = ERANGE; 431 return (e); 432 } 433 free(res); 434 return (q); 435 } 436 DEF_WEAK(login_getcapsize); 437 438 int 439 login_getcapbool(login_cap_t *lc, char *cap, u_int def) 440 { 441 if (!lc->lc_cap) 442 return (def); 443 444 return (cgetcap(lc->lc_cap, cap, ':') != NULL); 445 } 446 DEF_WEAK(login_getcapbool); 447 448 void 449 login_close(login_cap_t *lc) 450 { 451 if (lc) { 452 free(lc->lc_class); 453 free(lc->lc_cap); 454 free(lc->lc_style); 455 free(lc); 456 } 457 } 458 DEF_WEAK(login_close); 459 460 #define CTIME 1 461 #define CSIZE 2 462 #define CNUMB 3 463 464 static struct { 465 int what; 466 int type; 467 char * name; 468 } r_list[] = { 469 { RLIMIT_CPU, CTIME, "cputime", }, 470 { RLIMIT_FSIZE, CSIZE, "filesize", }, 471 { RLIMIT_DATA, CSIZE, "datasize", }, 472 { RLIMIT_STACK, CSIZE, "stacksize", }, 473 { RLIMIT_RSS, CSIZE, "memoryuse", }, 474 { RLIMIT_MEMLOCK, CSIZE, "memorylocked", }, 475 { RLIMIT_NPROC, CNUMB, "maxproc", }, 476 { RLIMIT_NOFILE, CNUMB, "openfiles", }, 477 { RLIMIT_CORE, CSIZE, "coredumpsize", }, 478 #ifdef RLIMIT_VMEM 479 { RLIMIT_VMEM, CSIZE, "vmemoryuse", }, 480 #endif 481 { -1, 0, 0 } 482 }; 483 484 static int 485 gsetrl(login_cap_t *lc, int what, char *name, int type) 486 { 487 struct rlimit rl; 488 struct rlimit r; 489 char name_cur[32]; 490 char name_max[32]; 491 char *v; 492 int len; 493 494 /* 495 * If we have no capabilities then there is nothing to do and 496 * we can just return success. 497 */ 498 if (lc->lc_cap == NULL) 499 return (0); 500 501 len = snprintf(name_cur, sizeof name_cur, "%s-cur", name); 502 if (len < 0 || len >= sizeof name_cur) { 503 syslog(LOG_ERR, "current resource limit name too large"); 504 return (-1); 505 } 506 len = snprintf(name_max, sizeof name_max, "%s-max", name); 507 if (len < 0 || len >= sizeof name_max) { 508 syslog(LOG_ERR, "max resource limit name too large"); 509 return (-1); 510 } 511 512 if (getrlimit(what, &r)) { 513 syslog(LOG_ERR, "getting resource limit: %m"); 514 return (-1); 515 } 516 517 /* 518 * We need to pre-fetch the 3 possible strings we will look 519 * up to see what order they come in. If the one without 520 * the -cur or -max comes in first then we ignore any later 521 * -cur or -max entries. 522 * Note that the cgetent routines will always return failure 523 * on the entry "". This will cause our login_get* routines 524 * to use the default entry. 525 */ 526 if ((v = cgetcap(lc->lc_cap, name, '=')) != NULL) { 527 if (v < cgetcap(lc->lc_cap, name_cur, '=')) 528 name_cur[0] = '\0'; 529 if (v < cgetcap(lc->lc_cap, name_max, '=')) 530 name_max[0] = '\0'; 531 } 532 533 #define RCUR r.rlim_cur 534 #define RMAX r.rlim_max 535 536 switch (type) { 537 case CTIME: 538 RCUR = (rlim_t)login_getcaptime(lc, name, RCUR, RCUR); 539 RMAX = (rlim_t)login_getcaptime(lc, name, RMAX, RMAX); 540 rl.rlim_cur = (rlim_t)login_getcaptime(lc, name_cur, RCUR, RCUR); 541 rl.rlim_max = (rlim_t)login_getcaptime(lc, name_max, RMAX, RMAX); 542 break; 543 case CSIZE: 544 RCUR = (rlim_t)login_getcapsize(lc, name, RCUR, RCUR); 545 RMAX = (rlim_t)login_getcapsize(lc, name, RMAX, RMAX); 546 rl.rlim_cur = (rlim_t)login_getcapsize(lc, name_cur, RCUR, RCUR); 547 rl.rlim_max = (rlim_t)login_getcapsize(lc, name_max, RMAX, RMAX); 548 break; 549 case CNUMB: 550 RCUR = (rlim_t)login_getcapnum(lc, name, RCUR, RCUR); 551 RMAX = (rlim_t)login_getcapnum(lc, name, RMAX, RMAX); 552 rl.rlim_cur = (rlim_t)login_getcapnum(lc, name_cur, RCUR, RCUR); 553 rl.rlim_max = (rlim_t)login_getcapnum(lc, name_max, RMAX, RMAX); 554 break; 555 default: 556 return (-1); 557 } 558 559 if (setrlimit(what, &rl)) { 560 syslog(LOG_ERR, "%s: setting resource limit %s: %m", 561 lc->lc_class, name); 562 return (-1); 563 } 564 #undef RCUR 565 #undef RMAX 566 return (0); 567 } 568 569 int 570 setclasscontext(char *class, u_int flags) 571 { 572 int ret; 573 login_cap_t *lc; 574 575 flags &= LOGIN_SETRESOURCES | LOGIN_SETPRIORITY | LOGIN_SETUMASK | 576 LOGIN_SETPATH | LOGIN_SETRTABLE; 577 578 lc = login_getclass(class); 579 ret = lc ? setusercontext(lc, NULL, 0, flags) : -1; 580 login_close(lc); 581 return (ret); 582 } 583 584 int 585 setusercontext(login_cap_t *lc, struct passwd *pwd, uid_t uid, u_int flags) 586 { 587 login_cap_t *flc; 588 quad_t p, rtable; 589 int i; 590 591 flc = NULL; 592 593 if (!lc && !(flc = lc = login_getclass(pwd ? pwd->pw_class : NULL))) 594 return (-1); 595 596 /* 597 * Without the pwd entry being passed we cannot set either 598 * the group or the login. We could complain about it. 599 */ 600 if (pwd == NULL) 601 flags &= ~(LOGIN_SETGROUP|LOGIN_SETLOGIN); 602 603 /* 604 * Verify that we haven't been given invalid values. 605 */ 606 if (flags & LOGIN_SETGROUP) { 607 if (pwd->pw_gid == -1) { 608 syslog(LOG_ERR, "setusercontext with invalid gid"); 609 login_close(flc); 610 return (-1); 611 } 612 } 613 if (flags & LOGIN_SETUSER) { 614 if (uid == -1) { 615 syslog(LOG_ERR, "setusercontext with invalid uid"); 616 login_close(flc); 617 return (-1); 618 } 619 } 620 621 if (flags & LOGIN_SETRESOURCES) 622 for (i = 0; r_list[i].name; ++i) 623 if (gsetrl(lc, r_list[i].what, r_list[i].name, 624 r_list[i].type)) 625 /* XXX - call syslog()? */; 626 627 if (flags & LOGIN_SETPRIORITY) { 628 p = login_getcapnum(lc, "priority", 0, 0); 629 630 if (setpriority(PRIO_PROCESS, 0, (int)p) == -1) 631 syslog(LOG_ERR, "%s: setpriority: %m", lc->lc_class); 632 } 633 634 if (flags & LOGIN_SETUMASK) { 635 p = login_getcapnum(lc, "umask", LOGIN_DEFUMASK,LOGIN_DEFUMASK); 636 umask((mode_t)p); 637 } 638 639 if (flags & LOGIN_SETRTABLE) { 640 rtable = login_getcapnum(lc, "rtable", -1, -1); 641 642 if (rtable >= 0 && setrtable((int)rtable) == -1) 643 syslog(LOG_ERR, "%s: setrtable: %m", lc->lc_class); 644 } 645 646 if (flags & LOGIN_SETGROUP) { 647 if (setresgid(pwd->pw_gid, pwd->pw_gid, pwd->pw_gid) == -1) { 648 syslog(LOG_ERR, "setresgid(%u,%u,%u): %m", 649 pwd->pw_gid, pwd->pw_gid, pwd->pw_gid); 650 login_close(flc); 651 return (-1); 652 } 653 654 if (initgroups(pwd->pw_name, pwd->pw_gid) == -1) { 655 syslog(LOG_ERR, "initgroups(%s,%u): %m", 656 pwd->pw_name, pwd->pw_gid); 657 login_close(flc); 658 return (-1); 659 } 660 } 661 662 if (flags & LOGIN_SETLOGIN) 663 if (setlogin(pwd->pw_name) == -1) { 664 syslog(LOG_ERR, "setlogin(%s) failure: %m", 665 pwd->pw_name); 666 login_close(flc); 667 return (-1); 668 } 669 670 if (flags & LOGIN_SETUSER) { 671 if (setresuid(uid, uid, uid) == -1) { 672 syslog(LOG_ERR, "setresuid(%u,%u,%u): %m", 673 uid, uid, uid); 674 login_close(flc); 675 return (-1); 676 } 677 } 678 679 if (flags & LOGIN_SETENV) { 680 if (setuserenv(lc, pwd) == -1) { 681 syslog(LOG_ERR, "could not set user environment: %m"); 682 login_close(flc); 683 return (-1); 684 } 685 } 686 687 if (flags & LOGIN_SETPATH) { 688 if (setuserpath(lc, pwd) == -1) { 689 syslog(LOG_ERR, "could not set PATH: %m"); 690 login_close(flc); 691 return (-1); 692 } 693 } 694 695 login_close(flc); 696 return (0); 697 } 698 DEF_WEAK(setusercontext); 699 700 /* 701 * Look up "path" for this user in login.conf and replace whitespace 702 * with ':' while expanding '~' and '$'. Sets the PATH environment 703 * variable to the result or _PATH_DEFPATH on error. 704 */ 705 static int 706 setuserpath(login_cap_t *lc, const struct passwd *pwd) 707 { 708 char *path = NULL, *opath = NULL, *op, *np; 709 int len, error; 710 711 /* 712 * If we have no capabilities then set _PATH_DEFPATH. 713 */ 714 if (lc->lc_cap == NULL) 715 goto setit; 716 717 if ((len = cgetustr(lc->lc_cap, "path", &opath)) <= 0) 718 goto setit; 719 720 if ((path = malloc(len + 1)) == NULL) 721 goto setit; 722 723 /* Convert opath from space-separated to colon-separated path. */ 724 for (op = opath, np = path; *op != '\0'; ) { 725 switch (*op) { 726 case ' ': 727 case '\t': 728 /* 729 * Collapse consecutive spaces and trim any space 730 * at the very end. 731 */ 732 do { 733 op++; 734 } while (*op == ' ' || *op == '\t'); 735 if (*op != '\0') 736 *np++ = ':'; 737 break; 738 case '\\': 739 /* check for escaped whitespace */ 740 if (*(op + 1) == ' ' || *(op + 1) == '\t') 741 *np++ = *op++; 742 /* FALLTHROUGH */ 743 default: 744 *np++ = *op++; 745 break; 746 } 747 748 } 749 *np = '\0'; 750 setit: 751 error = login_setenv("PATH", path ? path : _PATH_DEFPATH, pwd, 1); 752 free(opath); 753 free(path); 754 return (error); 755 } 756 757 /* 758 * Look up "setenv" for this user in login.conf and set the comma-separated 759 * list of environment variables, expanding '~' and '$'. 760 */ 761 static int 762 setuserenv(login_cap_t *lc, const struct passwd *pwd) 763 { 764 char *beg, *end, *ep, *list, *value; 765 int len, error; 766 767 /* 768 * If we have no capabilities then there is nothing to do and 769 * we can just return success. 770 */ 771 if (lc->lc_cap == NULL) 772 return (0); 773 774 if ((len = cgetustr(lc->lc_cap, "setenv", &list)) <= 0) 775 return (0); 776 777 for (beg = end = list, ep = list + len + 1; end < ep; end++) { 778 switch (*end) { 779 case '\\': 780 if (*(end + 1) == ',') 781 end++; /* skip escaped comma */ 782 continue; 783 case ',': 784 case '\0': 785 *end = '\0'; 786 if (beg == end) { 787 beg++; 788 continue; 789 } 790 break; 791 default: 792 continue; 793 } 794 795 if ((value = strchr(beg, '=')) != NULL) 796 *value++ = '\0'; 797 else 798 value = ""; 799 if ((error = login_setenv(beg, value, pwd, 0)) != 0) { 800 free(list); 801 return (error); 802 } 803 beg = end + 1; 804 } 805 free(list); 806 return (0); 807 } 808 809 /* 810 * Set an environment variable, substituting for ~ and $ 811 */ 812 static int 813 login_setenv(char *name, char *ovalue, const struct passwd *pwd, int ispath) 814 { 815 char *value = NULL; 816 int error; 817 818 if (*ovalue != '\0') 819 value = expandstr(ovalue, pwd, ispath); 820 error = setenv(name, value ? value : ovalue, 1); 821 free(value); 822 return (error); 823 } 824 825 /* 826 * Convert an expression of the following forms 827 * 1) A number. 828 * 2) A number followed by a b (mult by 512). 829 * 3) A number followed by a k (mult by 1024). 830 * 5) A number followed by a m (mult by 1024 * 1024). 831 * 6) A number followed by a g (mult by 1024 * 1024 * 1024). 832 * 7) A number followed by a t (mult by 1024 * 1024 * 1024 * 1024). 833 * 8) Two or more numbers (with/without k,b,m,g, or t). 834 * separated by x (also * for backwards compatibility), specifying 835 * the product of the indicated values. 836 */ 837 static 838 u_quad_t 839 strtosize(char *str, char **endptr, int radix) 840 { 841 u_quad_t num, num2; 842 char *expr, *expr2; 843 844 errno = 0; 845 num = strtoull(str, &expr, radix); 846 if (errno || expr == str) { 847 if (endptr) 848 *endptr = expr; 849 return (num); 850 } 851 852 switch(*expr) { 853 case 'b': case 'B': 854 num = multiply(num, (u_quad_t)512); 855 ++expr; 856 break; 857 case 'k': case 'K': 858 num = multiply(num, (u_quad_t)1024); 859 ++expr; 860 break; 861 case 'm': case 'M': 862 num = multiply(num, (u_quad_t)1024 * 1024); 863 ++expr; 864 break; 865 case 'g': case 'G': 866 num = multiply(num, (u_quad_t)1024 * 1024 * 1024); 867 ++expr; 868 break; 869 case 't': case 'T': 870 num = multiply(num, (u_quad_t)1024 * 1024); 871 num = multiply(num, (u_quad_t)1024 * 1024); 872 ++expr; 873 break; 874 } 875 876 if (errno) 877 goto erange; 878 879 switch(*expr) { 880 case '*': /* Backward compatible. */ 881 case 'x': 882 num2 = strtosize(expr+1, &expr2, radix); 883 if (errno) { 884 expr = expr2; 885 goto erange; 886 } 887 888 if (expr2 == expr + 1) { 889 if (endptr) 890 *endptr = expr; 891 return (num); 892 } 893 expr = expr2; 894 num = multiply(num, num2); 895 if (errno) 896 goto erange; 897 break; 898 } 899 if (endptr) 900 *endptr = expr; 901 return (num); 902 erange: 903 if (endptr) 904 *endptr = expr; 905 errno = ERANGE; 906 return (UQUAD_MAX); 907 } 908 909 static 910 u_quad_t 911 strtolimit(char *str, char **endptr, int radix) 912 { 913 if (strcasecmp(str, "infinity") == 0 || strcasecmp(str, "inf") == 0) { 914 if (endptr) 915 *endptr = str + strlen(str); 916 return ((u_quad_t)RLIM_INFINITY); 917 } 918 return (strtosize(str, endptr, radix)); 919 } 920 921 static u_quad_t 922 multiply(u_quad_t n1, u_quad_t n2) 923 { 924 static int bpw = 0; 925 u_quad_t m; 926 u_quad_t r; 927 int b1, b2; 928 929 /* 930 * Get rid of the simple cases 931 */ 932 if (n1 == 0 || n2 == 0) 933 return (0); 934 if (n1 == 1) 935 return (n2); 936 if (n2 == 1) 937 return (n1); 938 939 /* 940 * sizeof() returns number of bytes needed for storage. 941 * This may be different from the actual number of useful bits. 942 */ 943 if (!bpw) { 944 bpw = sizeof(u_quad_t) * 8; 945 while (((u_quad_t)1 << (bpw-1)) == 0) 946 --bpw; 947 } 948 949 /* 950 * First check the magnitude of each number. If the sum of the 951 * magnitude is way to high, reject the number. (If this test 952 * is not done then the first multiply below may overflow.) 953 */ 954 for (b1 = bpw; (((u_quad_t)1 << (b1-1)) & n1) == 0; --b1) 955 ; 956 for (b2 = bpw; (((u_quad_t)1 << (b2-1)) & n2) == 0; --b2) 957 ; 958 if (b1 + b2 - 2 > bpw) { 959 errno = ERANGE; 960 return (UQUAD_MAX); 961 } 962 963 /* 964 * Decompose the multiplication to be: 965 * h1 = n1 & ~1 966 * h2 = n2 & ~1 967 * l1 = n1 & 1 968 * l2 = n2 & 1 969 * (h1 + l1) * (h2 + l2) 970 * (h1 * h2) + (h1 * l2) + (l1 * h2) + (l1 * l2) 971 * 972 * Since h1 && h2 do not have the low bit set, we can then say: 973 * 974 * (h1>>1 * h2>>1 * 4) + ... 975 * 976 * So if (h1>>1 * h2>>1) > (1<<(bpw - 2)) then the result will 977 * overflow. 978 * 979 * Finally, if MAX - ((h1 * l2) + (l1 * h2) + (l1 * l2)) < (h1*h2) 980 * then adding in residual amount will cause an overflow. 981 */ 982 983 m = (n1 >> 1) * (n2 >> 1); 984 985 if (m >= ((u_quad_t)1 << (bpw-2))) { 986 errno = ERANGE; 987 return (UQUAD_MAX); 988 } 989 990 m *= 4; 991 992 r = (n1 & n2 & 1) 993 + (n2 & 1) * (n1 & ~(u_quad_t)1) 994 + (n1 & 1) * (n2 & ~(u_quad_t)1); 995 996 if ((u_quad_t)(m + r) < m) { 997 errno = ERANGE; 998 return (UQUAD_MAX); 999 } 1000 m += r; 1001 1002 return (m); 1003 } 1004 1005 /* 1006 * Check whether or not a tilde in a string should be expanded. 1007 * We only do expansion for things like "~", "~/...", ~me", "~me/...". 1008 * Additionally, for paths the tilde must be a the beginning. 1009 */ 1010 #define tilde_valid(s, b, u, l, ip) \ 1011 ((!(ip) || (s) == (b) || (s)[-1] == ':') && \ 1012 ((s)[1] == '/' || (s)[1] == '\0' || \ 1013 (strncmp((s)+1, u, l) == 0 && ((s)[l+1] == '/' || (s)[l+1] == '\0')))) 1014 1015 /* 1016 * Make a copy of a string, expanding '~' to the user's homedir, '$' to the 1017 * login name and other escape sequences as per cgetstr(3). 1018 */ 1019 static char * 1020 expandstr(const char *ostr, const struct passwd *pwd, int ispath) 1021 { 1022 size_t n, olen, nlen, ulen, dlen; 1023 const char *ep, *eo, *op; 1024 char *nstr, *np; 1025 int ch; 1026 1027 if (pwd != NULL) { 1028 ulen = strlen(pwd->pw_name); 1029 dlen = strlen(pwd->pw_dir); 1030 } 1031 1032 /* calculate the size of the new string */ 1033 olen = nlen = strlen(ostr); 1034 for (op = ostr, ep = ostr + olen; op < ep; op++) { 1035 switch (*op) { 1036 case '~': 1037 if (pwd == NULL || 1038 !tilde_valid(op, ostr, pwd->pw_name, ulen, ispath)) 1039 break; 1040 if (op[1] != '/' && op[1] != '\0') { 1041 op += ulen; /* ~username */ 1042 nlen = nlen - ulen - 1 + dlen; 1043 } else 1044 nlen += dlen - 1; 1045 break; 1046 case '$': 1047 if (pwd != NULL) 1048 nlen += ulen - 1; 1049 break; 1050 case '^': 1051 /* control char */ 1052 if (*++op != '\0') 1053 nlen--; 1054 break; 1055 case '\\': 1056 if (op[1] == '\0') 1057 break; 1058 /* 1059 * Byte in octal notation (\123) or an escaped char (\t) 1060 */ 1061 eo = op + 4; 1062 do { 1063 op++; 1064 nlen--; 1065 } while (op < eo && *op >= '0' && *op <= '7'); 1066 break; 1067 } 1068 } 1069 if ((np = nstr = malloc(++nlen)) == NULL) 1070 return (NULL); 1071 1072 for (op = ostr, ep = ostr + olen; op < ep; op++) { 1073 switch ((ch = *op)) { 1074 case '~': 1075 if (pwd == NULL || 1076 !tilde_valid(op, ostr, pwd->pw_name, ulen, ispath)) 1077 break; 1078 if (op[1] != '/' && op[1] != '\0') 1079 op += ulen; /* ~username */ 1080 strlcpy(np, pwd->pw_dir, nlen); 1081 nlen -= dlen; 1082 np += dlen; 1083 continue; 1084 case '$': 1085 if (pwd == NULL) 1086 break; 1087 strlcpy(np, pwd->pw_name, nlen); 1088 nlen -= ulen; 1089 np += ulen; 1090 continue; 1091 case '^': 1092 if (op[1] != '\0') 1093 ch = *++op & 037; 1094 break; 1095 case '\\': 1096 if (op[1] == '\0') 1097 break; 1098 switch(*++op) { 1099 case '0': case '1': case '2': case '3': 1100 case '4': case '5': case '6': case '7': 1101 /* byte in octal up to 3 digits long */ 1102 ch = 0; 1103 n = 3; 1104 do { 1105 ch = ch * 8 + (*op++ - '0'); 1106 } while (--n && *op >= '0' && *op <= '7'); 1107 break; 1108 case 'b': case 'B': 1109 ch = '\b'; 1110 break; 1111 case 't': case 'T': 1112 ch = '\t'; 1113 break; 1114 case 'n': case 'N': 1115 ch = '\n'; 1116 break; 1117 case 'f': case 'F': 1118 ch = '\f'; 1119 break; 1120 case 'r': case 'R': 1121 ch = '\r'; 1122 break; 1123 case 'e': case 'E': 1124 ch = '\033'; 1125 break; 1126 case 'c': case 'C': 1127 ch = ':'; 1128 break; 1129 default: 1130 ch = *op; 1131 break; 1132 } 1133 break; 1134 } 1135 *np++ = ch; 1136 nlen--; 1137 } 1138 *np = '\0'; 1139 return (nstr); 1140 } 1141