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 * $FreeBSD$ 25 */ 26 27 #include <stdio.h> 28 #include <stdlib.h> 29 #include <string.h> 30 #include <unistd.h> 31 #include <errno.h> 32 #include <sys/types.h> 33 #include <sys/stat.h> 34 #include <sys/time.h> 35 #include <sys/resource.h> 36 #include <fcntl.h> 37 #include <pwd.h> 38 #include <syslog.h> 39 #include <login_cap.h> 40 #include <paths.h> 41 #include <sys/rtprio.h> 42 43 44 #undef UNKNOWN 45 #define UNKNOWN "su" 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_getcapnum, RLIMIT_SBSIZE }, 63 { NULL, 0, 0 } 64 }; 65 66 67 void 68 setclassresources(login_cap_t *lc) 69 { 70 struct login_res *lr; 71 72 if (lc == NULL) 73 return; 74 75 for (lr = resources; lr->what != NULL; ++lr) { 76 struct rlimit rlim; 77 78 /* 79 * The login.conf file can have <limit>, <limit>-max, and 80 * <limit>-cur entries. 81 * What we do is get the current current- and maximum- limits. 82 * Then, we try to get an entry for <limit> from the capability, 83 * using the current and max limits we just got as the 84 * default/error values. 85 * *Then*, we try looking for <limit>-cur and <limit>-max, 86 * again using the appropriate values as the default/error 87 * conditions. 88 */ 89 90 if (getrlimit(lr->why, &rlim) != 0) 91 syslog(LOG_ERR, "getting %s resource limit: %m", lr->what); 92 else { 93 char name_cur[40]; 94 char name_max[40]; 95 rlim_t rcur = rlim.rlim_cur; 96 rlim_t rmax = rlim.rlim_max; 97 98 sprintf(name_cur, "%s-cur", lr->what); 99 sprintf(name_max, "%s-max", lr->what); 100 101 rcur = (*lr->who)(lc, lr->what, rcur, rcur); 102 rmax = (*lr->who)(lc, lr->what, rmax, rmax); 103 rlim.rlim_cur = (*lr->who)(lc, name_cur, rcur, rcur); 104 rlim.rlim_max = (*lr->who)(lc, name_max, rmax, rmax); 105 106 if (setrlimit(lr->why, &rlim) == -1) 107 syslog(LOG_WARNING, "set class '%s' resource limit %s: %m", lc->lc_class, lr->what); 108 } 109 } 110 } 111 112 113 114 static struct login_vars { 115 const char *tag; 116 const char *var; 117 const char *def; 118 } pathvars[] = { 119 { "path", "PATH", NULL }, 120 { "cdpath", "CDPATH", NULL }, 121 { "manpath", "MANPATH", NULL }, 122 { NULL, NULL, NULL } 123 }, envars[] = { 124 { "lang", "LANG", NULL }, 125 { "charset", "MM_CHARSET", NULL }, 126 { "timezone", "TZ", NULL }, 127 { "term", "TERM", UNKNOWN }, 128 { NULL, NULL, NULL } 129 }; 130 131 static char * 132 substvar(char * var, const struct passwd * pwd, int hlen, int pch, int nlen) 133 { 134 char *np = NULL; 135 136 if (var != NULL) { 137 int tildes = 0; 138 int dollas = 0; 139 char *p; 140 141 if (pwd != NULL) { 142 /* Count the number of ~'s in var to substitute */ 143 p = var; 144 for (p = var; (p = strchr(p, '~')) != NULL; p++) 145 ++tildes; 146 /* Count the number of $'s in var to substitute */ 147 p = var; 148 for (p = 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 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, 1); 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 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 /* 245 * setclasscontext() 246 * 247 * For the login class <class>, set various class context values 248 * (limits, mainly) to the values for that class. Which values are 249 * set are controlled by <flags> -- see <login_class.h> for the 250 * possible values. 251 * 252 * setclasscontext() can only set resources, priority, and umask. 253 */ 254 255 int 256 setclasscontext(const char *classname, unsigned int flags) 257 { 258 int rc; 259 login_cap_t *lc; 260 261 lc = login_getclassbyname(classname, NULL); 262 263 flags &= LOGIN_SETRESOURCES | LOGIN_SETPRIORITY | 264 LOGIN_SETUMASK | LOGIN_SETPATH; 265 266 rc = lc ? setusercontext(lc, NULL, 0, flags) : -1; 267 login_close(lc); 268 return rc; 269 } 270 271 272 273 /* 274 * Private functionw which takes care of processing 275 */ 276 277 static mode_t 278 setlogincontext(login_cap_t *lc, const struct passwd *pwd, 279 mode_t mymask, unsigned long flags) 280 { 281 if (lc) { 282 /* Set resources */ 283 if (flags & LOGIN_SETRESOURCES) 284 setclassresources(lc); 285 /* See if there's a umask override */ 286 if (flags & LOGIN_SETUMASK) 287 mymask = (mode_t)login_getcapnum(lc, "umask", mymask, mymask); 288 /* Set paths */ 289 if (flags & LOGIN_SETPATH) 290 setclassenvironment(lc, pwd, 1); 291 /* Set environment */ 292 if (flags & LOGIN_SETENV) 293 setclassenvironment(lc, pwd, 0); 294 } 295 return mymask; 296 } 297 298 299 300 /* 301 * setusercontext() 302 * 303 * Given a login class <lc> and a user in <pwd>, with a uid <uid>, 304 * set the context as in setclasscontext(). <flags> controls which 305 * values are set. 306 * 307 * The difference between setclasscontext() and setusercontext() is 308 * that the former sets things up for an already-existing process, 309 * while the latter sets things up from a root context. Such as might 310 * be called from login(1). 311 * 312 */ 313 314 int 315 setusercontext(login_cap_t *lc, const struct passwd *pwd, uid_t uid, unsigned int flags) 316 { 317 quad_t p; 318 mode_t mymask; 319 login_cap_t *llc = NULL; 320 #ifndef __NETBSD_SYSCALLS 321 struct rtprio rtp; 322 #endif 323 324 if (lc == NULL) { 325 if (pwd != NULL && (lc = login_getpwclass(pwd)) != NULL) 326 llc = lc; /* free this when we're done */ 327 } 328 329 if (flags & LOGIN_SETPATH) 330 pathvars[0].def = uid ? _PATH_DEFPATH : _PATH_STDPATH; 331 332 /* we need a passwd entry to set these */ 333 if (pwd == NULL) 334 flags &= ~(LOGIN_SETGROUP | LOGIN_SETLOGIN); 335 336 /* Set the process priority */ 337 if (flags & LOGIN_SETPRIORITY) { 338 p = login_getcapnum(lc, "priority", LOGIN_DEFPRI, LOGIN_DEFPRI); 339 340 if(p > PRIO_MAX) { 341 #ifndef __NETBSD_SYSCALLS 342 rtp.type = RTP_PRIO_IDLE; 343 rtp.prio = p - PRIO_MAX - 1; 344 p = (rtp.prio > RTP_PRIO_MAX) ? 31 : p; 345 if(rtprio(RTP_SET, 0, &rtp)) 346 syslog(LOG_WARNING, "rtprio '%s' (%s): %m", 347 pwd->pw_name, lc ? lc->lc_class : LOGIN_DEFCLASS); 348 #endif 349 } else if(p < PRIO_MIN) { 350 #ifndef __NETBSD_SYSCALLS 351 rtp.type = RTP_PRIO_REALTIME; 352 rtp.prio = abs(p - PRIO_MIN + RTP_PRIO_MAX); 353 p = (rtp.prio > RTP_PRIO_MAX) ? 1 : p; 354 if(rtprio(RTP_SET, 0, &rtp)) 355 syslog(LOG_WARNING, "rtprio '%s' (%s): %m", 356 pwd->pw_name, lc ? lc->lc_class : LOGIN_DEFCLASS); 357 #endif 358 } else { 359 if (setpriority(PRIO_PROCESS, 0, (int)p) != 0) 360 syslog(LOG_WARNING, "setpriority '%s' (%s): %m", 361 pwd->pw_name, lc ? lc->lc_class : LOGIN_DEFCLASS); 362 } 363 } 364 365 /* Setup the user's group permissions */ 366 if (flags & LOGIN_SETGROUP) { 367 if (setgid(pwd->pw_gid) != 0) { 368 syslog(LOG_ERR, "setgid(%lu): %m", (u_long)pwd->pw_gid); 369 login_close(llc); 370 return -1; 371 } 372 if (initgroups(pwd->pw_name, pwd->pw_gid) == -1) { 373 syslog(LOG_ERR, "initgroups(%s,%lu): %m", pwd->pw_name, 374 (u_long)pwd->pw_gid); 375 login_close(llc); 376 return -1; 377 } 378 } 379 380 /* Set the sessions login */ 381 if ((flags & LOGIN_SETLOGIN) && setlogin(pwd->pw_name) != 0) { 382 syslog(LOG_ERR, "setlogin(%s): %m", pwd->pw_name); 383 login_close(llc); 384 return -1; 385 } 386 387 mymask = (flags & LOGIN_SETUMASK) ? umask(LOGIN_DEFUMASK) : 0; 388 mymask = setlogincontext(lc, pwd, mymask, flags); 389 login_close(llc); 390 391 /* This needs to be done after anything that needs root privs */ 392 if ((flags & LOGIN_SETUSER) && setuid(uid) != 0) { 393 syslog(LOG_ERR, "setuid(%lu): %m", (u_long)uid); 394 return -1; /* Paranoia again */ 395 } 396 397 /* 398 * Now, we repeat some of the above for the user's private entries 399 */ 400 if ((lc = login_getuserclass(pwd)) != NULL) { 401 mymask = setlogincontext(lc, pwd, mymask, flags); 402 login_close(lc); 403 } 404 405 /* Finally, set any umask we've found */ 406 if (flags & LOGIN_SETUMASK) 407 umask(mymask); 408 409 return 0; 410 } 411 412