/* * Copyright (c) 1997,2007 Andrew G Morgan * * This file deals with setting capabilities on files. */ #include #include #include #include #include #define XATTR_SECURITY_PREFIX "security." #include "libcap.h" #ifdef VFS_CAP_U32 #if VFS_CAP_U32 != __CAP_BLKS # error VFS representation of capabilities is not the same size as kernel #endif #if __BYTE_ORDER == __BIG_ENDIAN #define FIXUP_32BITS(x) bswap_32(x) #else #define FIXUP_32BITS(x) (x) #endif static cap_t _fcaps_load(struct vfs_cap_data *rawvfscap, cap_t result, int bytes) { __u32 magic_etc; unsigned tocopy, i; magic_etc = FIXUP_32BITS(rawvfscap->magic_etc); switch (magic_etc & VFS_CAP_REVISION_MASK) { #ifdef VFS_CAP_REVISION_1 case VFS_CAP_REVISION_1: tocopy = VFS_CAP_U32_1; bytes -= XATTR_CAPS_SZ_1; break; #endif #ifdef VFS_CAP_REVISION_2 case VFS_CAP_REVISION_2: tocopy = VFS_CAP_U32_2; bytes -= XATTR_CAPS_SZ_2; break; #endif default: cap_free(result); result = NULL; return result; } /* * Verify that we loaded exactly the right number of bytes */ if (bytes != 0) { cap_free(result); result = NULL; return result; } for (i=0; i < tocopy; i++) { result->u[i].flat[CAP_INHERITABLE] = FIXUP_32BITS(rawvfscap->data[i].inheritable); result->u[i].flat[CAP_PERMITTED] = FIXUP_32BITS(rawvfscap->data[i].permitted); if (magic_etc & VFS_CAP_FLAGS_EFFECTIVE) { result->u[i].flat[CAP_EFFECTIVE] = result->u[i].flat[CAP_INHERITABLE] | result->u[i].flat[CAP_PERMITTED]; } } while (i < __CAP_BLKS) { result->u[i].flat[CAP_INHERITABLE] = result->u[i].flat[CAP_PERMITTED] = result->u[i].flat[CAP_EFFECTIVE] = 0; i++; } return result; } static int _fcaps_save(struct vfs_cap_data *rawvfscap, cap_t cap_d, int *bytes_p) { __u32 eff_not_zero, magic; unsigned tocopy, i; if (!good_cap_t(cap_d)) { errno = EINVAL; return -1; } switch (cap_d->head.version) { #ifdef _LINUX_CAPABILITY_VERSION_1 case _LINUX_CAPABILITY_VERSION_1: magic = VFS_CAP_REVISION_1; tocopy = VFS_CAP_U32_1; *bytes_p = XATTR_CAPS_SZ_1; break; #endif #ifdef _LINUX_CAPABILITY_VERSION_2 case _LINUX_CAPABILITY_VERSION_2: magic = VFS_CAP_REVISION_2; tocopy = VFS_CAP_U32_2; *bytes_p = XATTR_CAPS_SZ_2; break; #endif #ifdef _LINUX_CAPABILITY_VERSION_3 case _LINUX_CAPABILITY_VERSION_3: magic = VFS_CAP_REVISION_2; tocopy = VFS_CAP_U32_2; *bytes_p = XATTR_CAPS_SZ_2; break; #endif default: errno = EINVAL; return -1; } _cap_debug("setting named file capabilities"); for (eff_not_zero = 0, i = 0; i < tocopy; i++) { eff_not_zero |= cap_d->u[i].flat[CAP_EFFECTIVE]; } while (i < __CAP_BLKS) { if ((cap_d->u[i].flat[CAP_EFFECTIVE] || cap_d->u[i].flat[CAP_INHERITABLE] || cap_d->u[i].flat[CAP_PERMITTED])) { /* * System does not support these capabilities */ errno = EINVAL; return -1; } i++; } for (i=0; i < tocopy; i++) { rawvfscap->data[i].permitted = FIXUP_32BITS(cap_d->u[i].flat[CAP_PERMITTED]); rawvfscap->data[i].inheritable = FIXUP_32BITS(cap_d->u[i].flat[CAP_INHERITABLE]); if (eff_not_zero && ((~(cap_d->u[i].flat[CAP_EFFECTIVE])) & (cap_d->u[i].flat[CAP_PERMITTED] | cap_d->u[i].flat[CAP_INHERITABLE]))) { errno = EINVAL; return -1; } } if (eff_not_zero == 0) { rawvfscap->magic_etc = FIXUP_32BITS(magic); } else { rawvfscap->magic_etc = FIXUP_32BITS(magic|VFS_CAP_FLAGS_EFFECTIVE); } return 0; /* success */ } /* * Get the capabilities of an open file, as specified by its file * descriptor. */ cap_t cap_get_fd(int fildes) { cap_t result; /* allocate a new capability set */ result = cap_init(); if (result) { struct vfs_cap_data rawvfscap; int sizeofcaps; _cap_debug("getting fildes capabilities"); /* fill the capability sets via a system call */ sizeofcaps = fgetxattr(fildes, XATTR_NAME_CAPS, &rawvfscap, sizeof(rawvfscap)); if (sizeofcaps < sizeof(rawvfscap.magic_etc)) { cap_free(result); result = NULL; } else { result = _fcaps_load(&rawvfscap, result, sizeofcaps); } } return result; } /* * Get the capabilities from a named file. */ cap_t cap_get_file(const char *filename) { cap_t result; /* allocate a new capability set */ result = cap_init(); if (result) { struct vfs_cap_data rawvfscap; int sizeofcaps; _cap_debug("getting filename capabilities"); /* fill the capability sets via a system call */ sizeofcaps = getxattr(filename, XATTR_NAME_CAPS, &rawvfscap, sizeof(rawvfscap)); if (sizeofcaps < sizeof(rawvfscap.magic_etc)) { cap_free(result); result = NULL; } else { result = _fcaps_load(&rawvfscap, result, sizeofcaps); } } return result; } /* * Set the capabilities of an open file, as specified by its file * descriptor. */ int cap_set_fd(int fildes, cap_t cap_d) { struct vfs_cap_data rawvfscap; int sizeofcaps; struct stat buf; if (fstat(fildes, &buf) != 0) { _cap_debug("unable to stat file descriptor %d", fildes); return -1; } if (S_ISLNK(buf.st_mode) || !S_ISREG(buf.st_mode)) { _cap_debug("file descriptor %d for non-regular file", fildes); errno = EINVAL; return -1; } if (cap_d == NULL) { _cap_debug("deleting fildes capabilities"); return fremovexattr(fildes, XATTR_NAME_CAPS); } else if (_fcaps_save(&rawvfscap, cap_d, &sizeofcaps) != 0) { return -1; } _cap_debug("setting fildes capabilities"); return fsetxattr(fildes, XATTR_NAME_CAPS, &rawvfscap, sizeofcaps, 0); } /* * Set the capabilities of a named file. */ int cap_set_file(const char *filename, cap_t cap_d) { struct vfs_cap_data rawvfscap; int sizeofcaps; struct stat buf; if (lstat(filename, &buf) != 0) { _cap_debug("unable to stat file [%s]", filename); return -1; } if (S_ISLNK(buf.st_mode) || !S_ISREG(buf.st_mode)) { _cap_debug("file [%s] is not a regular file", filename); errno = EINVAL; return -1; } if (cap_d == NULL) { _cap_debug("removing filename capabilities"); return removexattr(filename, XATTR_NAME_CAPS); } else if (_fcaps_save(&rawvfscap, cap_d, &sizeofcaps) != 0) { return -1; } _cap_debug("setting filename capabilities"); return setxattr(filename, XATTR_NAME_CAPS, &rawvfscap, sizeofcaps, 0); } #else /* ie. ndef VFS_CAP_U32 */ cap_t cap_get_fd(int fildes) { errno = EINVAL; return NULL; } cap_t cap_get_file(const char *filename) { errno = EINVAL; return NULL; } int cap_set_fd(int fildes, cap_t cap_d) { errno = EINVAL; return -1; } int cap_set_file(const char *filename, cap_t cap_d) { errno = EINVAL; return -1; } #endif /* def VFS_CAP_U32 */