updater: introduce and set_metadata and set_metadata_recursive
Introduce two new updater functions:
* set_metadata
* set_metadata_recursive
Long term, these functions are intended to be more flexible replacements
for the following methods:
* set_perm
* set_perm_recursive
Usage:
set_metadata("filename", "key1", "value1", "key2", "value2", ...)
set_metadata_recursive("dirname", "key1", "value1", "key2", "value2", ...)
Description:
set_metadata() and set_metadata_recursive() set the attributes on a file/directory
according to the key/value pairs provided. Today, the following keys are
supported:
* uid
* gid
* mode (set_perm_extd only)
* fmode (set_perm_extd_recursive only)
* dmode (set_perm_extd_recursive only)
* selabel
* capabilities
Unknown keys are logged as warnings, but are not fatal errors.
Examples:
* set_metadata("/system/bin/netcfg", "selabel", "u:object_r:system_file:s0");
This sets the SELinux label of /system/bin/netcfg to u:object_r:system_file:s0.
No other changes occur.
* set_metadata("/system/bin/netcfg", "uid", 0, "gid", 3003, "mode", 02750, "selabel", "u:object_r:system_file:s0", "capabilities", 0x0);
This sets /system/bin/netcfg to uid=0, gid=3003, mode=02750,
selinux label=u:object_r:system_file:s0, and clears the capabilities
associated with the file.
* set_metadata_recursive("/system", "uid", 0, "gid", 0, "fmode", 0644, "dmode", 0755, "selabel", "u:object_r:system_file:s0", "capabilities", 0x0);
All files and directories under /system are set to uid=0, gid=0,
and selinux label=u:object_r:system_file:s0. Directories are set to
mode=0755. Files are set to mode=0644 and all capabilities are cleared.
Bug: 10183961
Bug: 10186213
Bug: 8985290
Change-Id: Ifdcf186a7ed45265511dc493c4036e1ac5e3d0af
This commit is contained in:
@@ -27,6 +27,12 @@
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <time.h>
|
||||
#include <selinux/selinux.h>
|
||||
#include <ftw.h>
|
||||
#include <sys/capability.h>
|
||||
#include <sys/xattr.h>
|
||||
#include <linux/xattr.h>
|
||||
#include <inttypes.h>
|
||||
|
||||
#include "cutils/misc.h"
|
||||
#include "cutils/properties.h"
|
||||
@@ -600,6 +606,259 @@ done:
|
||||
return StringValue(result);
|
||||
}
|
||||
|
||||
struct perm_parsed_args {
|
||||
bool has_uid;
|
||||
uid_t uid;
|
||||
bool has_gid;
|
||||
gid_t gid;
|
||||
bool has_mode;
|
||||
mode_t mode;
|
||||
bool has_fmode;
|
||||
mode_t fmode;
|
||||
bool has_dmode;
|
||||
mode_t dmode;
|
||||
bool has_selabel;
|
||||
char* selabel;
|
||||
bool has_capabilities;
|
||||
uint64_t capabilities;
|
||||
};
|
||||
|
||||
static struct perm_parsed_args ParsePermArgs(int argc, char** args) {
|
||||
int i;
|
||||
struct perm_parsed_args parsed;
|
||||
int bad = 0;
|
||||
static int max_warnings = 20;
|
||||
|
||||
memset(&parsed, 0, sizeof(parsed));
|
||||
|
||||
for (i = 1; i < argc; i += 2) {
|
||||
if (strcmp("uid", args[i]) == 0) {
|
||||
int64_t uid;
|
||||
if (sscanf(args[i+1], "%" SCNd64, &uid) == 1) {
|
||||
parsed.uid = uid;
|
||||
parsed.has_uid = true;
|
||||
} else {
|
||||
printf("ParsePermArgs: invalid UID \"%s\"\n", args[i + 1]);
|
||||
bad++;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (strcmp("gid", args[i]) == 0) {
|
||||
int64_t gid;
|
||||
if (sscanf(args[i+1], "%" SCNd64, &gid) == 1) {
|
||||
parsed.gid = gid;
|
||||
parsed.has_gid = true;
|
||||
} else {
|
||||
printf("ParsePermArgs: invalid GID \"%s\"\n", args[i + 1]);
|
||||
bad++;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (strcmp("mode", args[i]) == 0) {
|
||||
int32_t mode;
|
||||
if (sscanf(args[i+1], "%" SCNi32, &mode) == 1) {
|
||||
parsed.mode = mode;
|
||||
parsed.has_mode = true;
|
||||
} else {
|
||||
printf("ParsePermArgs: invalid mode \"%s\"\n", args[i + 1]);
|
||||
bad++;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (strcmp("dmode", args[i]) == 0) {
|
||||
int32_t mode;
|
||||
if (sscanf(args[i+1], "%" SCNi32, &mode) == 1) {
|
||||
parsed.dmode = mode;
|
||||
parsed.has_dmode = true;
|
||||
} else {
|
||||
printf("ParsePermArgs: invalid dmode \"%s\"\n", args[i + 1]);
|
||||
bad++;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (strcmp("fmode", args[i]) == 0) {
|
||||
int32_t mode;
|
||||
if (sscanf(args[i+1], "%" SCNi32, &mode) == 1) {
|
||||
parsed.fmode = mode;
|
||||
parsed.has_fmode = true;
|
||||
} else {
|
||||
printf("ParsePermArgs: invalid fmode \"%s\"\n", args[i + 1]);
|
||||
bad++;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (strcmp("capabilities", args[i]) == 0) {
|
||||
int64_t capabilities;
|
||||
if (sscanf(args[i+1], "%" SCNi64, &capabilities) == 1) {
|
||||
parsed.capabilities = capabilities;
|
||||
parsed.has_capabilities = true;
|
||||
} else {
|
||||
printf("ParsePermArgs: invalid capabilities \"%s\"\n", args[i + 1]);
|
||||
bad++;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (strcmp("selabel", args[i]) == 0) {
|
||||
if (args[i+1][0] != '\0') {
|
||||
parsed.selabel = args[i+1];
|
||||
parsed.has_selabel = true;
|
||||
} else {
|
||||
printf("ParsePermArgs: invalid selabel \"%s\"\n", args[i + 1]);
|
||||
bad++;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (max_warnings != 0) {
|
||||
printf("ParsedPermArgs: unknown key \"%s\", ignoring\n", args[i]);
|
||||
max_warnings--;
|
||||
if (max_warnings == 0) {
|
||||
printf("ParsedPermArgs: suppressing further warnings\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
return parsed;
|
||||
}
|
||||
|
||||
static int ApplyParsedPerms(
|
||||
const char* filename,
|
||||
const struct stat *statptr,
|
||||
struct perm_parsed_args parsed)
|
||||
{
|
||||
int bad = 0;
|
||||
|
||||
if (parsed.has_uid) {
|
||||
if (chown(filename, parsed.uid, -1) < 0) {
|
||||
printf("ApplyParsedPerms: chown of %s to %d failed: %s\n",
|
||||
filename, parsed.uid, strerror(errno));
|
||||
bad++;
|
||||
}
|
||||
}
|
||||
|
||||
if (parsed.has_gid) {
|
||||
if (chown(filename, -1, parsed.gid) < 0) {
|
||||
printf("ApplyParsedPerms: chgrp of %s to %d failed: %s\n",
|
||||
filename, parsed.gid, strerror(errno));
|
||||
bad++;
|
||||
}
|
||||
}
|
||||
|
||||
if (parsed.has_mode) {
|
||||
if (chmod(filename, parsed.mode) < 0) {
|
||||
printf("ApplyParsedPerms: chmod of %s to %d failed: %s\n",
|
||||
filename, parsed.mode, strerror(errno));
|
||||
bad++;
|
||||
}
|
||||
}
|
||||
|
||||
if (parsed.has_dmode && S_ISDIR(statptr->st_mode)) {
|
||||
if (chmod(filename, parsed.dmode) < 0) {
|
||||
printf("ApplyParsedPerms: chmod of %s to %d failed: %s\n",
|
||||
filename, parsed.dmode, strerror(errno));
|
||||
bad++;
|
||||
}
|
||||
}
|
||||
|
||||
if (parsed.has_fmode && S_ISREG(statptr->st_mode)) {
|
||||
if (chmod(filename, parsed.fmode) < 0) {
|
||||
printf("ApplyParsedPerms: chmod of %s to %d failed: %s\n",
|
||||
filename, parsed.fmode, strerror(errno));
|
||||
bad++;
|
||||
}
|
||||
}
|
||||
|
||||
if (parsed.has_selabel) {
|
||||
// TODO: Don't silently ignore ENOTSUP
|
||||
if (lsetfilecon(filename, parsed.selabel) && (errno != ENOTSUP)) {
|
||||
printf("ApplyParsedPerms: lsetfilecon of %s to %s failed: %s\n",
|
||||
filename, parsed.selabel, strerror(errno));
|
||||
bad++;
|
||||
}
|
||||
}
|
||||
|
||||
if (parsed.has_capabilities && S_ISREG(statptr->st_mode)) {
|
||||
if (parsed.capabilities == 0) {
|
||||
if ((removexattr(filename, XATTR_NAME_CAPS) == -1) && (errno != ENODATA)) {
|
||||
// Report failure unless it's ENODATA (attribute not set)
|
||||
printf("ApplyParsedPerms: removexattr of %s to %" PRIx64 " failed: %s\n",
|
||||
filename, parsed.capabilities, strerror(errno));
|
||||
bad++;
|
||||
}
|
||||
} else {
|
||||
struct vfs_cap_data cap_data;
|
||||
memset(&cap_data, 0, sizeof(cap_data));
|
||||
cap_data.magic_etc = VFS_CAP_REVISION | VFS_CAP_FLAGS_EFFECTIVE;
|
||||
cap_data.data[0].permitted = (uint32_t) (parsed.capabilities & 0xffffffff);
|
||||
cap_data.data[0].inheritable = 0;
|
||||
cap_data.data[1].permitted = (uint32_t) (parsed.capabilities >> 32);
|
||||
cap_data.data[1].inheritable = 0;
|
||||
if (setxattr(filename, XATTR_NAME_CAPS, &cap_data, sizeof(cap_data), 0) < 0) {
|
||||
printf("ApplyParsedPerms: setcap of %s to %" PRIx64 " failed: %s\n",
|
||||
filename, parsed.capabilities, strerror(errno));
|
||||
bad++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return bad;
|
||||
}
|
||||
|
||||
// nftw doesn't allow us to pass along context, so we need to use
|
||||
// global variables. *sigh*
|
||||
static struct perm_parsed_args recursive_parsed_args;
|
||||
|
||||
static int do_SetMetadataRecursive(const char* filename, const struct stat *statptr,
|
||||
int fileflags, struct FTW *pfwt) {
|
||||
return ApplyParsedPerms(filename, statptr, recursive_parsed_args);
|
||||
}
|
||||
|
||||
static Value* SetMetadataFn(const char* name, State* state, int argc, Expr* argv[]) {
|
||||
int i;
|
||||
int bad = 0;
|
||||
static int nwarnings = 0;
|
||||
struct stat sb;
|
||||
Value* result = NULL;
|
||||
|
||||
bool recursive = (strcmp(name, "set_metadata_recursive") == 0);
|
||||
|
||||
if ((argc % 2) != 1) {
|
||||
return ErrorAbort(state, "%s() expects an odd number of arguments, got %d",
|
||||
name, argc);
|
||||
}
|
||||
|
||||
char** args = ReadVarArgs(state, argc, argv);
|
||||
if (args == NULL) return NULL;
|
||||
|
||||
if (lstat(args[0], &sb) == -1) {
|
||||
result = ErrorAbort(state, "%s: Error on lstat of \"%s\": %s", name, args[0], strerror(errno));
|
||||
goto done;
|
||||
}
|
||||
|
||||
struct perm_parsed_args parsed = ParsePermArgs(argc, args);
|
||||
|
||||
if (recursive) {
|
||||
recursive_parsed_args = parsed;
|
||||
bad += nftw(args[0], do_SetMetadataRecursive, 30, FTW_CHDIR | FTW_DEPTH | FTW_PHYS);
|
||||
memset(&recursive_parsed_args, 0, sizeof(recursive_parsed_args));
|
||||
} else {
|
||||
bad += ApplyParsedPerms(args[0], &sb, parsed);
|
||||
}
|
||||
|
||||
done:
|
||||
for (i = 0; i < argc; ++i) {
|
||||
free(args[i]);
|
||||
}
|
||||
free(args);
|
||||
|
||||
if (result != NULL) {
|
||||
return result;
|
||||
}
|
||||
|
||||
if (bad > 0) {
|
||||
return ErrorAbort(state, "%s: some changes failed", name);
|
||||
}
|
||||
|
||||
return StringValue(strdup(""));
|
||||
}
|
||||
|
||||
Value* GetPropFn(const char* name, State* state, int argc, Expr* argv[]) {
|
||||
if (argc != 1) {
|
||||
@@ -1133,9 +1392,24 @@ void RegisterInstallFunctions() {
|
||||
RegisterFunction("package_extract_dir", PackageExtractDirFn);
|
||||
RegisterFunction("package_extract_file", PackageExtractFileFn);
|
||||
RegisterFunction("symlink", SymlinkFn);
|
||||
|
||||
// Maybe, at some future point, we can delete these functions? They have been
|
||||
// replaced by perm_set and perm_set_recursive.
|
||||
RegisterFunction("set_perm", SetPermFn);
|
||||
RegisterFunction("set_perm_recursive", SetPermFn);
|
||||
|
||||
// Usage:
|
||||
// set_metadata("filename", "key1", "value1", "key2", "value2", ...)
|
||||
// Example:
|
||||
// set_metadata("/system/bin/netcfg", "uid", 0, "gid", 3003, "mode", 02750, "selabel", "u:object_r:system_file:s0", "capabilities", 0x0);
|
||||
RegisterFunction("set_metadata", SetMetadataFn);
|
||||
|
||||
// Usage:
|
||||
// set_metadata_recursive("dirname", "key1", "value1", "key2", "value2", ...)
|
||||
// Example:
|
||||
// set_metadata_recursive("/system", "uid", 0, "gid", 0, "fmode", 0644, "dmode", 0755, "selabel", "u:object_r:system_file:s0", "capabilities", 0x0);
|
||||
RegisterFunction("set_metadata_recursive", SetMetadataFn);
|
||||
|
||||
RegisterFunction("getprop", GetPropFn);
|
||||
RegisterFunction("file_getprop", FileGetPropFn);
|
||||
RegisterFunction("write_raw_image", WriteRawImageFn);
|
||||
|
||||
Reference in New Issue
Block a user