diff options
author | Hidehiko Abe <hidehiko@google.com> | 2018-05-11 02:31:37 +0900 |
---|---|---|
committer | Theodore Ts'o <tytso@mit.edu> | 2018-06-21 10:02:19 -0400 |
commit | 32272a37d7ab987f2b8b62047e7807fd45263c30 (patch) | |
tree | 5043a33049baaae952ed590618730954bf4bbd47 /contrib | |
parent | 7c40d7bc9e89329535fcb566ccd708531d8ea41b (diff) |
AOSP: Support UID/GID mapping
This reverts commit 797c9c47d8419f58f752a96ae972423ca32b4c30.
with the fix.
Signed-off-by: Theodore Ts'o <tytso@mit.edu>
Google-Bug-Id: 78561186
Test: Ran on DUT.
Change-Id: I0d903cb08373aa5e9d79a1601d2e5ea9a59573fe
From AOSP commit: 5250966f2b7b7b4643235551b125ddbcfbd3d609
Diffstat (limited to 'contrib')
-rw-r--r-- | contrib/android/e2fsdroid.c | 157 | ||||
-rw-r--r-- | contrib/android/perms.c | 43 | ||||
-rw-r--r-- | contrib/android/perms.h | 27 |
3 files changed, 216 insertions, 11 deletions
diff --git a/contrib/android/e2fsdroid.c b/contrib/android/e2fsdroid.c index 2fe922d8..237df51f 100644 --- a/contrib/android/e2fsdroid.c +++ b/contrib/android/e2fsdroid.c @@ -13,6 +13,15 @@ #include "basefs_allocator.h" #include "create_inode.h" +#ifndef UID_GID_MAP_MAX_EXTENTS +/* + * The value is defined in linux/user_namspace.h. + * The value is (arbitrarily) 5 in 4.14 and earlier, or 340 in 4.15 and later. + * Here, the bigger value is taken. See also man user_namespace(7). + */ +#define UID_GID_MAP_MAX_EXTENTS 340 +#endif + static char *prog_name = "e2fsdroid"; static char *in_file; static char *block_list; @@ -32,7 +41,8 @@ static void usage(int ret) { fprintf(stderr, "%s [-B block_list] [-D basefs_out] [-T timestamp]\n" "\t[-C fs_config] [-S file_contexts] [-p product_out]\n" - "\t[-a mountpoint] [-d basefs_in] [-f src_dir] [-e] [-s] image\n", + "\t[-a mountpoint] [-d basefs_in] [-f src_dir] [-e] [-s]\n" + "\t[-u uid-mapping] [-g gid-mapping] image\n", prog_name); exit(ret); } @@ -55,6 +65,135 @@ static char *absolute_path(const char *file) return ret; } +static int parse_ugid_map_entry(char* line, struct ugid_map_entry* result) +{ + char *token, *token_saveptr; + size_t num_tokens; + unsigned int *parsed[] = {&result->child_id, + &result->parent_id, + &result->length}; + for (token = strtok_r(line, " ", &token_saveptr), num_tokens = 0; + token && num_tokens < 3; + token = strtok_r(NULL, " ", &token_saveptr), ++num_tokens) { + char* endptr = NULL; + *parsed[num_tokens] = strtoul(token, &endptr, 10); + if ((*parsed[num_tokens] == ULONG_MAX && errno) || *endptr) { + fprintf(stderr, "Malformed u/gid mapping line\n"); + return 0; + } + } + if (num_tokens < 3 || strtok_r(NULL, " ", &token_saveptr) != NULL) { + fprintf(stderr, "Malformed u/gid mapping line\n"); + return 0; + } + if (result->child_id + result->length < result->child_id || + result->parent_id + result->length < result->parent_id) { + fprintf(stderr, "u/gid mapping overflow\n"); + return 0; + } + return 1; +} + +/* + * Returns 1 if [begin1, begin1+length1) and [begin2, begin2+length2) have + * overlapping range. Otherwise 0. + */ +static int is_overlapping(unsigned int begin1, unsigned int length1, + unsigned int begin2, unsigned int length2) +{ + unsigned int end1 = begin1 + length1; + unsigned int end2 = begin2 + length2; + return !(end1 <= begin2 || end2 <= begin1); +} + +/* + * Verifies if the given mapping works. + * - Checks if the number of entries is less than or equals to + * UID_GID_MAP_MAX_EXTENTS. + * - Checks if there is no overlapped ranges. + * Returns 1 if valid, otherwise 0. + */ +static int is_valid_ugid_map(const struct ugid_map* mapping) +{ + size_t i, j; + + if (mapping->size > UID_GID_MAP_MAX_EXTENTS) { + fprintf(stderr, "too many u/gid mapping entries\n"); + return 0; + } + + for (i = 0; i < mapping->size; ++i) { + const struct ugid_map_entry *entry1 = &mapping->entries[i]; + for (j = i + 1; j < mapping->size; ++j) { + const struct ugid_map_entry *entry2 = + &mapping->entries[j]; + if (is_overlapping(entry1->child_id, entry1->length, + entry2->child_id, entry2->length)) { + fprintf(stderr, + "Overlapping child u/gid: [%d %d %d]," + " [%d %d %d]\n", + entry1->child_id, entry1->parent_id, + entry1->length, entry2->child_id, + entry2->parent_id, entry2->length); + return 0; + } + if (is_overlapping(entry1->parent_id, entry1->length, + entry2->parent_id, entry2->length)) { + fprintf(stderr, + "Overlapping parent u/gid: [%d %d %d]," + " [%d %d %d]\n", + entry1->child_id, entry1->parent_id, + entry1->length, entry2->child_id, + entry2->parent_id, entry2->length); + return 0; + } + } + } + return 1; +} + +/* + * Parses the UID/GID mapping argument. The argument could be a multi-line + * string (separated by '\n', no trailing '\n' is allowed). Each line must + * contain exact three integer tokens; the first token is |child_id|, + * the second is |parent_id|, and the last is |length| of the mapping range. + * See also user_namespace(7) man page. + * On success, the parsed entries are stored in |result|, and it returns 1. + * Otherwise, returns 0. + */ +static int parse_ugid_map(char* arg, struct ugid_map* result) +{ + int i; + char *line, *line_saveptr; + size_t current_index; + + /* Count the number of lines. */ + result->size = 1; + for (i = 0; arg[i]; ++i) { + if (arg[i] == '\n') + ++result->size; + } + + /* Allocate memory for entries. */ + result->entries = malloc(sizeof(struct ugid_map_entry) * result->size); + if (!result->entries) { + result->size = 0; + return 0; + } + + /* Parse each line */ + for (line = strtok_r(arg, "\n", &line_saveptr), current_index = 0; + line; + line = strtok_r(NULL, "\n", &line_saveptr), ++current_index) { + if (!parse_ugid_map_entry( + line, &result->entries[current_index])) { + return 0; + } + } + + return is_valid_ugid_map(result); +} + int main(int argc, char *argv[]) { int c; @@ -70,10 +209,11 @@ int main(int argc, char *argv[]) ext2_ino_t free_inodes_count; blk64_t blocks_count; blk64_t free_blocks_count; + struct ugid_map uid_map = { 0, NULL }, gid_map = { 0, NULL }; add_error_table(&et_ext2_error_table); - while ((c = getopt (argc, argv, "T:C:S:p:a:D:d:B:f:es")) != EOF) { + while ((c = getopt (argc, argv, "T:C:S:p:a:D:d:B:f:esu:g:")) != EOF) { switch (c) { case 'T': fixed_time = strtoul(optarg, &p, 0); @@ -122,6 +262,14 @@ int main(int argc, char *argv[]) case 's': flags |= EXT2_FLAG_SHARE_DUP; break; + case 'u': + if (!parse_ugid_map(optarg, &uid_map)) + exit(EXIT_FAILURE); + break; + case 'g': + if (!parse_ugid_map(optarg, &gid_map)) + exit(EXIT_FAILURE); + break; default: usage(EXIT_FAILURE); } @@ -173,8 +321,9 @@ int main(int argc, char *argv[]) } if (android_configure) { - retval = android_configure_fs(fs, src_dir, product_out, mountpoint, - seopt_file, nr_opt, fs_config_file, fixed_time); + retval = android_configure_fs( + fs, src_dir, product_out, mountpoint, seopt_file, + nr_opt, fs_config_file, fixed_time, &uid_map, &gid_map); if (retval) { com_err(prog_name, retval, "%s", "while configuring the file system"); diff --git a/contrib/android/perms.c b/contrib/android/perms.c index e67aa1ef..74ef4b88 100644 --- a/contrib/android/perms.c +++ b/contrib/android/perms.c @@ -23,6 +23,8 @@ struct inode_params { fs_config_f fs_config_func; struct selabel_handle *sehnd; time_t fixed_time; + const struct ugid_map* uid_map; + const struct ugid_map* gid_map; }; static errcode_t ino_add_xattr(ext2_filsys fs, ext2_ino_t ino, const char *name, @@ -90,6 +92,26 @@ static errcode_t set_selinux_xattr(ext2_filsys fs, ext2_ino_t ino, return retval; } +/* + * Returns mapped UID/GID if there is a corresponding entry in |mapping|. + * Otherwise |id| as is. + */ +static unsigned int resolve_ugid(const struct ugid_map* mapping, + unsigned int id) +{ + size_t i; + for (i = 0; i < mapping->size; ++i) { + const struct ugid_map_entry* entry = &mapping->entries[i]; + if (entry->parent_id <= id && + id < entry->parent_id + entry->length) { + return id + entry->child_id - entry->parent_id; + } + } + + /* No entry is found. */ + return id; +} + static errcode_t set_perms_and_caps(ext2_filsys fs, ext2_ino_t ino, struct inode_params *params) { @@ -110,8 +132,12 @@ static errcode_t set_perms_and_caps(ext2_filsys fs, ext2_ino_t ino, params->fs_config_func(params->filename, S_ISDIR(inode.i_mode), params->target_out, &uid, &gid, &imode, &capabilities); - inode.i_uid = uid & 0xffff; - inode.i_gid = gid & 0xffff; + uid = resolve_ugid(params->uid_map, uid); + gid = resolve_ugid(params->gid_map, gid); + inode.i_uid = (__u16) uid; + inode.i_gid = (__u16) gid; + ext2fs_set_i_uid_high(inode, (__u16) (uid >> 16)); + ext2fs_set_i_gid_high(inode, (__u16) (gid >> 16)); inode.i_mode = (inode.i_mode & S_IFMT) | (imode & 0xffff); retval = ext2fs_write_inode(fs, ino, &inode); if (retval) { @@ -250,7 +276,9 @@ errcode_t __android_configure_fs(ext2_filsys fs, char *src_dir, char *mountpoint, fs_config_f fs_config_func, struct selabel_handle *sehnd, - time_t fixed_time) + time_t fixed_time, + const struct ugid_map* uid_map, + const struct ugid_map* gid_map) { errcode_t retval; struct inode_params params = { @@ -263,6 +291,8 @@ errcode_t __android_configure_fs(ext2_filsys fs, char *src_dir, .path = mountpoint, .filename = mountpoint, .mountpoint = mountpoint, + .uid_map = uid_map, + .gid_map = gid_map, }; /* walk_dir will add the "/". Don't add it twice. */ @@ -284,7 +314,9 @@ errcode_t android_configure_fs(ext2_filsys fs, char *src_dir, char *target_out, char *mountpoint, struct selinux_opt *seopts EXT2FS_ATTR((unused)), unsigned int nopt EXT2FS_ATTR((unused)), - char *fs_config_file, time_t fixed_time) + char *fs_config_file, time_t fixed_time, + const struct ugid_map* uid_map, + const struct ugid_map* gid_map) { errcode_t retval; fs_config_f fs_config_func = NULL; @@ -324,5 +356,6 @@ errcode_t android_configure_fs(ext2_filsys fs, char *src_dir, char *target_out, fs_config_func = fs_config; return __android_configure_fs(fs, src_dir, target_out, mountpoint, - fs_config_func, sehnd, fixed_time); + fs_config_func, sehnd, fixed_time, + uid_map, gid_map); } diff --git a/contrib/android/perms.h b/contrib/android/perms.h index c404cb90..6d6a2129 100644 --- a/contrib/android/perms.h +++ b/contrib/android/perms.h @@ -9,6 +9,25 @@ typedef void (*fs_config_f)(const char *path, int dir, unsigned *uid, unsigned *gid, unsigned *mode, uint64_t *capabilities); +/* + * Represents a range of UID/GID mapping. + * This maps the id in [|parent_id|, |parent_id| + |length|) into + * [|child_id|, |child_id| + |length|) + */ +struct ugid_map_entry { + unsigned int child_id; + unsigned int parent_id; + unsigned int length; +}; + +struct ugid_map { + /* The number of elements in |entries|. */ + size_t size; + + /* An array of entries. If |size| is 0, this is a null pointer. */ + struct ugid_map_entry* entries; +}; + # ifdef _WIN32 struct selabel_handle; static inline errcode_t android_configure_fs(ext2_filsys fs, @@ -18,7 +37,9 @@ static inline errcode_t android_configure_fs(ext2_filsys fs, void *seopts, unsigned int nopt, char *fs_config_file, - time_t fixed_time) + time_t fixed_time, + const struct ugid_map* uid_map, + const struct ugdi_map* gid_map) { return 0; } @@ -36,7 +57,9 @@ errcode_t android_configure_fs(ext2_filsys fs, char *src_dir, char *mountpoint, struct selinux_opt *seopts, unsigned int nopt, - char *fs_config_file, time_t fixed_time); + char *fs_config_file, time_t fixed_time, + const struct ugid_map* uid_map, + const struct ugid_map* gid_map); # endif #endif /* !ANDROID_PERMS_H */ |