Annotation of embedaddon/strongswan/src/libstrongswan/utils/capabilities.c, revision 1.1
1.1 ! misho 1: /*
! 2: * Copyright (C) 2012-2015 Tobias Brunner
! 3: * HSR Hochschule fuer Technik Rapperswil
! 4: * Copyright (C) 2012 Martin Willi
! 5: * Copyright (C) 2012 revosec AG
! 6: *
! 7: * This program is free software; you can redistribute it and/or modify it
! 8: * under the terms of the GNU General Public License as published by the
! 9: * Free Software Foundation; either version 2 of the License, or (at your
! 10: * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
! 11: *
! 12: * This program is distributed in the hope that it will be useful, but
! 13: * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
! 14: * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
! 15: * for more details.
! 16: */
! 17:
! 18: #include "capabilities.h"
! 19:
! 20: #include <utils/debug.h>
! 21:
! 22: #include <errno.h>
! 23: #include <string.h>
! 24: #include <sys/types.h>
! 25: #include <unistd.h>
! 26:
! 27: #ifndef WIN32
! 28: #include <pwd.h>
! 29: #include <grp.h>
! 30: #ifdef HAVE_PRCTL
! 31: # include <sys/prctl.h>
! 32: #endif /* HAVE_PRCTL */
! 33:
! 34: #if !defined(HAVE_GETPWNAM_R) || \
! 35: !defined(HAVE_GETGRNAM_R) || \
! 36: !defined(HAVE_GETPWUID_R)
! 37: # include <threading/mutex.h>
! 38: # define EMULATE_R_FUNCS
! 39: #endif
! 40: #endif /* !WIN32 */
! 41:
! 42: typedef struct private_capabilities_t private_capabilities_t;
! 43:
! 44: /**
! 45: * Private data of an capabilities_t object.
! 46: */
! 47: struct private_capabilities_t {
! 48:
! 49: /**
! 50: * Public capabilities_t interface.
! 51: */
! 52: capabilities_t public;
! 53:
! 54: /**
! 55: * user ID to switch during rights dropping
! 56: */
! 57: uid_t uid;
! 58:
! 59: /**
! 60: * group ID to switch during rights dropping
! 61: */
! 62: gid_t gid;
! 63:
! 64: /**
! 65: * capabilities to keep
! 66: */
! 67: #ifdef CAPABILITIES_LIBCAP
! 68: cap_t caps;
! 69: #endif /* CAPABILITIES_LIBCAP */
! 70: #ifdef CAPABILITIES_NATIVE
! 71: struct __user_cap_data_struct caps[2];
! 72: #endif /* CAPABILITIES_NATIVE */
! 73:
! 74: #ifdef EMULATE_R_FUNCS
! 75: /**
! 76: * mutex to emulate get(pw|gr)nam_r functions
! 77: */
! 78: mutex_t *mutex;
! 79: #endif
! 80: };
! 81:
! 82: #ifndef WIN32
! 83:
! 84: /**
! 85: * Returns TRUE if the current process/user is member of the given group
! 86: */
! 87: static bool has_group(gid_t group)
! 88: {
! 89: gid_t *groups;
! 90: long ngroups, i;
! 91: bool found = FALSE;
! 92:
! 93: if (group == getegid())
! 94: { /* it's unspecified if this is part of the list below or not */
! 95: return TRUE;
! 96: }
! 97: ngroups = sysconf(_SC_NGROUPS_MAX);
! 98: if (ngroups == -1)
! 99: {
! 100: DBG1(DBG_LIB, "getting groups for current process failed: %s",
! 101: strerror(errno));
! 102: return FALSE;
! 103: }
! 104: groups = calloc(ngroups + 1, sizeof(gid_t));
! 105: ngroups = getgroups(ngroups, groups);
! 106: if (ngroups == -1)
! 107: {
! 108: DBG1(DBG_LIB, "getting groups for current process failed: %s",
! 109: strerror(errno));
! 110: free(groups);
! 111: return FALSE;
! 112: }
! 113: for (i = 0; i < ngroups; i++)
! 114: {
! 115: if (group == groups[i])
! 116: {
! 117: found = TRUE;
! 118: break;
! 119: }
! 120: }
! 121: free(groups);
! 122: return found;
! 123: }
! 124:
! 125: /**
! 126: * Verify that the current process has the given capability
! 127: */
! 128: static bool has_capability(private_capabilities_t *this, u_int cap,
! 129: bool *ignore)
! 130: {
! 131: if (cap == CAP_CHOWN)
! 132: { /* if new files/UNIX sockets are created they should be owned by the
! 133: * configured user and group. This requires a call to chown(2). But
! 134: * CAP_CHOWN is not always required. */
! 135: if (!this->uid || geteuid() == this->uid)
! 136: { /* if the owner does not change CAP_CHOWN is not needed */
! 137: if (!this->gid || has_group(this->gid))
! 138: { /* the same applies if the owner is a member of the group */
! 139: if (ignore)
! 140: { /* we don't have to keep this, if requested */
! 141: *ignore = TRUE;
! 142: }
! 143: return TRUE;
! 144: }
! 145: }
! 146: }
! 147: #ifndef CAPABILITIES
! 148: /* if we can't check the actual capabilities assume only root has it */
! 149: return geteuid() == 0;
! 150: #endif /* !CAPABILITIES */
! 151: #ifdef CAPABILITIES_LIBCAP
! 152: cap_flag_value_t val;
! 153: cap_t caps;
! 154: bool ok;
! 155:
! 156: caps = cap_get_proc();
! 157: if (!caps)
! 158: {
! 159: return FALSE;
! 160: }
! 161: ok = cap_get_flag(caps, cap, CAP_PERMITTED, &val) == 0 && val == CAP_SET;
! 162: cap_free(caps);
! 163: return ok;
! 164: #endif /* CAPABILITIES_LIBCAP */
! 165: #ifdef CAPABILITIES_NATIVE
! 166: struct __user_cap_header_struct header = {
! 167: #if defined(_LINUX_CAPABILITY_VERSION_3)
! 168: .version = _LINUX_CAPABILITY_VERSION_3,
! 169: #elif defined(_LINUX_CAPABILITY_VERSION_2)
! 170: .version = _LINUX_CAPABILITY_VERSION_2,
! 171: #elif defined(_LINUX_CAPABILITY_VERSION_1)
! 172: .version = _LINUX_CAPABILITY_VERSION_1,
! 173: #else
! 174: .version = _LINUX_CAPABILITY_VERSION,
! 175: #endif
! 176: };
! 177: struct __user_cap_data_struct caps[2];
! 178: int i = 0;
! 179:
! 180: if (cap >= 32)
! 181: {
! 182: i++;
! 183: cap -= 32;
! 184: }
! 185: return capget(&header, caps) == 0 && caps[i].permitted & (1 << cap);
! 186: #endif /* CAPABILITIES_NATIVE */
! 187: }
! 188:
! 189: #else /* WIN32 */
! 190:
! 191: /**
! 192: * Verify that the current process has the given capability, dummy variant
! 193: */
! 194: static bool has_capability(private_capabilities_t *this, u_int cap,
! 195: bool *ignore)
! 196: {
! 197: return TRUE;
! 198: }
! 199:
! 200: #endif /* WIN32 */
! 201:
! 202: /**
! 203: * Keep the given capability if it is held by the current process. Returns
! 204: * FALSE, if this is not the case.
! 205: */
! 206: static bool keep_capability(private_capabilities_t *this, u_int cap)
! 207: {
! 208: #ifdef CAPABILITIES_LIBCAP
! 209: cap_set_flag(this->caps, CAP_EFFECTIVE, 1, &cap, CAP_SET);
! 210: cap_set_flag(this->caps, CAP_INHERITABLE, 1, &cap, CAP_SET);
! 211: cap_set_flag(this->caps, CAP_PERMITTED, 1, &cap, CAP_SET);
! 212: #endif /* CAPABILITIES_LIBCAP */
! 213: #ifdef CAPABILITIES_NATIVE
! 214: int i = 0;
! 215:
! 216: if (cap >= 32)
! 217: {
! 218: i++;
! 219: cap -= 32;
! 220: }
! 221: this->caps[i].effective |= 1 << cap;
! 222: this->caps[i].permitted |= 1 << cap;
! 223: this->caps[i].inheritable |= 1 << cap;
! 224: #endif /* CAPABILITIES_NATIVE */
! 225: return TRUE;
! 226: }
! 227:
! 228: METHOD(capabilities_t, keep, bool,
! 229: private_capabilities_t *this, u_int cap)
! 230: {
! 231: bool ignore = FALSE;
! 232:
! 233: if (!has_capability(this, cap, &ignore))
! 234: {
! 235: return FALSE;
! 236: }
! 237: else if (ignore)
! 238: { /* don't keep capabilities that are not required */
! 239: return TRUE;
! 240: }
! 241: return keep_capability(this, cap);
! 242: }
! 243:
! 244: METHOD(capabilities_t, check, bool,
! 245: private_capabilities_t *this, u_int cap)
! 246: {
! 247: return has_capability(this, cap, NULL);
! 248: }
! 249:
! 250: METHOD(capabilities_t, get_uid, uid_t,
! 251: private_capabilities_t *this)
! 252: {
! 253: #ifdef WIN32
! 254: return this->uid;
! 255: #else
! 256: return this->uid ?: geteuid();
! 257: #endif
! 258: }
! 259:
! 260: METHOD(capabilities_t, get_gid, gid_t,
! 261: private_capabilities_t *this)
! 262: {
! 263: #ifdef WIN32
! 264: return this->gid;
! 265: #else
! 266: return this->gid ?: getegid();
! 267: #endif
! 268: }
! 269:
! 270: METHOD(capabilities_t, set_uid, void,
! 271: private_capabilities_t *this, uid_t uid)
! 272: {
! 273: this->uid = uid;
! 274: }
! 275:
! 276: METHOD(capabilities_t, set_gid, void,
! 277: private_capabilities_t *this, gid_t gid)
! 278: {
! 279: this->gid = gid;
! 280: }
! 281:
! 282: METHOD(capabilities_t, resolve_uid, bool,
! 283: private_capabilities_t *this, char *username)
! 284: {
! 285: #ifndef WIN32
! 286: struct passwd *pwp;
! 287: int err;
! 288:
! 289: #ifdef HAVE_GETPWNAM_R
! 290: struct passwd passwd;
! 291: size_t buflen = 1024;
! 292: char *buf = NULL;
! 293:
! 294: while (TRUE)
! 295: {
! 296: buf = realloc(buf, buflen);
! 297: err = getpwnam_r(username, &passwd, buf, buflen, &pwp);
! 298: if (err == ERANGE)
! 299: {
! 300: buflen *= 2;
! 301: continue;
! 302: }
! 303: if (pwp)
! 304: {
! 305: this->uid = pwp->pw_uid;
! 306: }
! 307: break;
! 308: }
! 309: free(buf);
! 310: #else /* HAVE GETPWNAM_R */
! 311: this->mutex->lock(this->mutex);
! 312: pwp = getpwnam(username);
! 313: if (pwp)
! 314: {
! 315: this->uid = pwp->pw_uid;
! 316: }
! 317: err = errno;
! 318: this->mutex->unlock(this->mutex);
! 319: #endif /* HAVE GETPWNAM_R */
! 320: if (pwp)
! 321: {
! 322: return TRUE;
! 323: }
! 324: DBG1(DBG_LIB, "resolving user '%s' failed: %s", username,
! 325: err ? strerror(err) : "user not found");
! 326: #endif /* !WIN32 */
! 327: return FALSE;
! 328: }
! 329:
! 330: METHOD(capabilities_t, resolve_gid, bool,
! 331: private_capabilities_t *this, char *groupname)
! 332: {
! 333: #ifndef WIN32
! 334: struct group *grp;
! 335: int err;
! 336:
! 337: #ifdef HAVE_GETGRNAM_R
! 338: struct group group;
! 339: size_t buflen = 1024;
! 340: char *buf = NULL;
! 341:
! 342: while (TRUE)
! 343: {
! 344: buf = realloc(buf, buflen);
! 345: err = getgrnam_r(groupname, &group, buf, buflen, &grp);
! 346: if (err == ERANGE)
! 347: {
! 348: buflen *= 2;
! 349: continue;
! 350: }
! 351: if (grp)
! 352: {
! 353: this->gid = grp->gr_gid;
! 354: }
! 355: break;
! 356: }
! 357: free(buf);
! 358: #else /* HAVE_GETGRNAM_R */
! 359: this->mutex->lock(this->mutex);
! 360: grp = getgrnam(groupname);
! 361: if (grp)
! 362: {
! 363: this->gid = grp->gr_gid;
! 364: }
! 365: err = errno;
! 366: this->mutex->unlock(this->mutex);
! 367: #endif /* HAVE_GETGRNAM_R */
! 368: if (grp)
! 369: {
! 370: return TRUE;
! 371: }
! 372: DBG1(DBG_LIB, "resolving user '%s' failed: %s", groupname,
! 373: err ? strerror(err) : "group not found");
! 374: #endif /* !WIN32 */
! 375: return FALSE;
! 376: }
! 377:
! 378: #ifndef WIN32
! 379: /**
! 380: * Initialize supplementary groups for unprivileged user
! 381: */
! 382: static bool init_supplementary_groups(private_capabilities_t *this)
! 383: {
! 384: struct passwd *pwp;
! 385: int res = -1;
! 386:
! 387: #ifdef HAVE_GETPWUID_R
! 388: struct passwd pwd;
! 389: size_t buflen = 1024;
! 390: char *buf = NULL;
! 391:
! 392: while (TRUE)
! 393: {
! 394: buf = realloc(buf, buflen);
! 395: if (getpwuid_r(this->uid, &pwd, buf, buflen, &pwp) == ERANGE)
! 396: {
! 397: buflen *= 2;
! 398: continue;
! 399: }
! 400: if (pwp)
! 401: {
! 402: res = initgroups(pwp->pw_name, this->gid);
! 403: }
! 404: break;
! 405: }
! 406: free(buf);
! 407: #else /* HAVE_GETPWUID_R */
! 408: this->mutex->lock(this->mutex);
! 409: pwp = getpwuid(this->uid);
! 410: if (pwp)
! 411: {
! 412: res = initgroups(pwp->pw_name, this->gid);
! 413: }
! 414: this->mutex->unlock(this->mutex);
! 415: #endif /* HAVE_GETPWUID_R */
! 416: return res == 0;
! 417: }
! 418: #endif /* WIN32 */
! 419:
! 420: METHOD(capabilities_t, drop, bool,
! 421: private_capabilities_t *this)
! 422: {
! 423: #ifndef WIN32
! 424: #ifdef HAVE_PRCTL
! 425: if (has_capability(this, CAP_SETPCAP, NULL))
! 426: {
! 427: prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0);
! 428: }
! 429: #endif
! 430:
! 431: if (this->uid && !init_supplementary_groups(this))
! 432: {
! 433: DBG1(DBG_LIB, "initializing supplementary groups for %u failed",
! 434: this->uid);
! 435: return FALSE;
! 436: }
! 437: if (this->gid && setgid(this->gid) != 0)
! 438: {
! 439: DBG1(DBG_LIB, "change to unprivileged group %u failed: %s",
! 440: this->gid, strerror(errno));
! 441: return FALSE;
! 442: }
! 443: if (this->uid && setuid(this->uid) != 0)
! 444: {
! 445: DBG1(DBG_LIB, "change to unprivileged user %u failed: %s",
! 446: this->uid, strerror(errno));
! 447: return FALSE;
! 448: }
! 449:
! 450: #ifdef CAPABILITIES_LIBCAP
! 451: if (cap_set_proc(this->caps) != 0)
! 452: {
! 453: DBG1(DBG_LIB, "dropping capabilities failed: %s", strerror(errno));
! 454: return FALSE;
! 455: }
! 456: #endif /* CAPABILITIES_LIBCAP */
! 457: #ifdef CAPABILITIES_NATIVE
! 458: struct __user_cap_header_struct header = {
! 459: #if defined(_LINUX_CAPABILITY_VERSION_3)
! 460: .version = _LINUX_CAPABILITY_VERSION_3,
! 461: #elif defined(_LINUX_CAPABILITY_VERSION_2)
! 462: .version = _LINUX_CAPABILITY_VERSION_2,
! 463: #elif defined(_LINUX_CAPABILITY_VERSION_1)
! 464: .version = _LINUX_CAPABILITY_VERSION_1,
! 465: #else
! 466: .version = _LINUX_CAPABILITY_VERSION,
! 467: #endif
! 468: };
! 469: if (capset(&header, this->caps) != 0)
! 470: {
! 471: DBG1(DBG_LIB, "dropping capabilities failed: %s", strerror(errno));
! 472: return FALSE;
! 473: }
! 474: #endif /* CAPABILITIES_NATIVE */
! 475: #ifdef CAPABILITIES
! 476: DBG1(DBG_LIB, "dropped capabilities, running as uid %u, gid %u",
! 477: geteuid(), getegid());
! 478: #endif /* CAPABILITIES */
! 479: #endif /*!WIN32 */
! 480: return TRUE;
! 481: }
! 482:
! 483: METHOD(capabilities_t, destroy, void,
! 484: private_capabilities_t *this)
! 485: {
! 486: #ifdef EMULATE_R_FUNCS
! 487: this->mutex->destroy(this->mutex);
! 488: #endif /* EMULATE_R_FUNCS */
! 489: #ifdef CAPABILITIES_LIBCAP
! 490: cap_free(this->caps);
! 491: #endif /* CAPABILITIES_LIBCAP */
! 492: free(this);
! 493: }
! 494:
! 495: /**
! 496: * See header
! 497: */
! 498: capabilities_t *capabilities_create()
! 499: {
! 500: private_capabilities_t *this;
! 501:
! 502: INIT(this,
! 503: .public = {
! 504: .keep = _keep,
! 505: .check = _check,
! 506: .get_uid = _get_uid,
! 507: .get_gid = _get_gid,
! 508: .set_uid = _set_uid,
! 509: .set_gid = _set_gid,
! 510: .resolve_uid = _resolve_uid,
! 511: .resolve_gid = _resolve_gid,
! 512: .drop = _drop,
! 513: .destroy = _destroy,
! 514: },
! 515: );
! 516:
! 517: #ifdef CAPABILITIES_LIBCAP
! 518: this->caps = cap_init();
! 519: #endif /* CAPABILITIES_LIBCAP */
! 520:
! 521: #ifdef EMULATE_R_FUNCS
! 522: this->mutex = mutex_create(MUTEX_TYPE_DEFAULT);
! 523: #endif /* EMULATE_R_FUNCS */
! 524:
! 525: return &this->public;
! 526: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>