1 #ifndef _GNU_SOURCE
2 # define _GNU_SOURCE //asprintf
3 #endif
4 #include "perms.h"
5 #include "support/nls-enable.h"
6 #include <time.h>
7 #include <sys/stat.h>
8
9 #ifndef XATTR_SELINUX_SUFFIX
10 # define XATTR_SELINUX_SUFFIX "selinux"
11 #endif
12 #ifndef XATTR_CAPS_SUFFIX
13 # define XATTR_CAPS_SUFFIX "capability"
14 #endif
15
16 struct inode_params {
17 ext2_filsys fs;
18 char *path;
19 char *filename;
20 char *src_dir;
21 char *target_out;
22 char *mountpoint;
23 fs_config_f fs_config_func;
24 struct selabel_handle *sehnd;
25 time_t fixed_time;
26 const struct ugid_map* uid_map;
27 const struct ugid_map* gid_map;
28 errcode_t error;
29 };
30
ino_add_xattr(ext2_filsys fs,ext2_ino_t ino,const char * name,const void * value,int value_len)31 static errcode_t ino_add_xattr(ext2_filsys fs, ext2_ino_t ino, const char *name,
32 const void *value, int value_len)
33 {
34 errcode_t retval, close_retval;
35 struct ext2_xattr_handle *xhandle;
36
37 retval = ext2fs_xattrs_open(fs, ino, &xhandle);
38 if (retval) {
39 com_err(__func__, retval, _("while opening inode %u"), ino);
40 return retval;
41 }
42 retval = ext2fs_xattrs_read(xhandle);
43 if (retval) {
44 com_err(__func__, retval,
45 _("while reading xattrs of inode %u"), ino);
46 goto xattrs_close;
47 }
48 retval = ext2fs_xattr_set(xhandle, name, value, value_len);
49 if (retval) {
50 com_err(__func__, retval,
51 _("while setting xattrs of inode %u"), ino);
52 goto xattrs_close;
53 }
54 xattrs_close:
55 close_retval = ext2fs_xattrs_close(&xhandle);
56 if (close_retval) {
57 com_err(__func__, close_retval,
58 _("while closing xattrs of inode %u"), ino);
59 return retval ? retval : close_retval;
60 }
61 return retval;
62 }
63
set_selinux_xattr(ext2_filsys fs,ext2_ino_t ino,struct inode_params * params)64 static errcode_t set_selinux_xattr(ext2_filsys fs, ext2_ino_t ino,
65 struct inode_params *params)
66 {
67 errcode_t retval;
68 char *secontext = NULL;
69 struct ext2_inode inode;
70
71 if (params->sehnd == NULL)
72 return 0;
73
74 retval = ext2fs_read_inode(fs, ino, &inode);
75 if (retval) {
76 com_err(__func__, retval,
77 _("while reading inode %u"), ino);
78 return retval;
79 }
80
81 retval = selabel_lookup(params->sehnd, &secontext, params->filename,
82 inode.i_mode);
83 if (retval < 0) {
84 int saved_errno = errno;
85 com_err(__func__, errno,
86 _("searching for label \"%s\""), params->filename);
87 return saved_errno;
88 }
89
90 retval = ino_add_xattr(fs, ino, "security." XATTR_SELINUX_SUFFIX,
91 secontext, strlen(secontext) + 1);
92
93 freecon(secontext);
94 return retval;
95 }
96
97 /*
98 * Returns mapped UID/GID if there is a corresponding entry in |mapping|.
99 * Otherwise |id| as is.
100 */
resolve_ugid(const struct ugid_map * mapping,unsigned int id)101 static unsigned int resolve_ugid(const struct ugid_map* mapping,
102 unsigned int id)
103 {
104 size_t i;
105 for (i = 0; i < mapping->size; ++i) {
106 const struct ugid_map_entry* entry = &mapping->entries[i];
107 if (entry->parent_id <= id &&
108 id < entry->parent_id + entry->length) {
109 return id + entry->child_id - entry->parent_id;
110 }
111 }
112
113 /* No entry is found. */
114 return id;
115 }
116
set_perms_and_caps(ext2_filsys fs,ext2_ino_t ino,struct inode_params * params)117 static errcode_t set_perms_and_caps(ext2_filsys fs, ext2_ino_t ino,
118 struct inode_params *params)
119 {
120 errcode_t retval;
121 uint64_t capabilities = 0;
122 struct ext2_inode inode;
123 struct vfs_cap_data cap_data;
124 unsigned int uid = 0, gid = 0, imode = 0;
125
126 retval = ext2fs_read_inode(fs, ino, &inode);
127 if (retval) {
128 com_err(__func__, retval, _("while reading inode %u"), ino);
129 return retval;
130 }
131
132 /* Permissions */
133 if (params->fs_config_func != NULL) {
134 const char *filename = params->filename;
135 if (strcmp(filename, params->mountpoint) == 0) {
136 /* The root of the filesystem needs to be an empty string. */
137 filename = "";
138 }
139 params->fs_config_func(filename, S_ISDIR(inode.i_mode),
140 params->target_out, &uid, &gid, &imode,
141 &capabilities);
142 uid = resolve_ugid(params->uid_map, uid);
143 gid = resolve_ugid(params->gid_map, gid);
144 inode.i_uid = (__u16) uid;
145 inode.i_gid = (__u16) gid;
146 ext2fs_set_i_uid_high(inode, (__u16) (uid >> 16));
147 ext2fs_set_i_gid_high(inode, (__u16) (gid >> 16));
148 inode.i_mode = (inode.i_mode & S_IFMT) | (imode & 0xffff);
149 retval = ext2fs_write_inode(fs, ino, &inode);
150 if (retval) {
151 com_err(__func__, retval,
152 _("while writing inode %u"), ino);
153 return retval;
154 }
155 }
156
157 /* Capabilities */
158 if (!capabilities)
159 return 0;
160 memset(&cap_data, 0, sizeof(cap_data));
161 cap_data.magic_etc = VFS_CAP_REVISION_2 | VFS_CAP_FLAGS_EFFECTIVE;
162 cap_data.data[0].permitted = (uint32_t) (capabilities & 0xffffffff);
163 cap_data.data[1].permitted = (uint32_t) (capabilities >> 32);
164 return ino_add_xattr(fs, ino, "security." XATTR_CAPS_SUFFIX,
165 &cap_data, sizeof(cap_data));
166 }
167
set_timestamp(ext2_filsys fs,ext2_ino_t ino,struct inode_params * params)168 static errcode_t set_timestamp(ext2_filsys fs, ext2_ino_t ino,
169 struct inode_params *params)
170 {
171 errcode_t retval;
172 struct ext2_inode inode;
173 struct stat stat;
174 char *src_filename = NULL;
175
176 retval = ext2fs_read_inode(fs, ino, &inode);
177 if (retval) {
178 com_err(__func__, retval,
179 _("while reading inode %u"), ino);
180 return retval;
181 }
182
183 if (params->fixed_time == -1 && params->src_dir) {
184 /* replace mountpoint from filename with src_dir */
185 if (asprintf(&src_filename, "%s/%s", params->src_dir,
186 params->filename + strlen(params->mountpoint)) < 0) {
187 return -ENOMEM;
188 }
189 retval = lstat(src_filename, &stat);
190 if (retval < 0) {
191 com_err(__func__, errno,
192 _("while lstat file %s"), src_filename);
193 goto end;
194 }
195 inode.i_atime = inode.i_ctime = inode.i_mtime = stat.st_mtime;
196 } else {
197 inode.i_atime = inode.i_ctime = inode.i_mtime = params->fixed_time;
198 }
199
200 retval = ext2fs_write_inode(fs, ino, &inode);
201 if (retval) {
202 com_err(__func__, retval,
203 _("while writing inode %u"), ino);
204 goto end;
205 }
206
207 end:
208 free(src_filename);
209 return retval;
210 }
211
is_dir(ext2_filsys fs,ext2_ino_t ino)212 static int is_dir(ext2_filsys fs, ext2_ino_t ino)
213 {
214 struct ext2_inode inode;
215
216 if (ext2fs_read_inode(fs, ino, &inode))
217 return 0;
218 return S_ISDIR(inode.i_mode);
219 }
220
androidify_inode(ext2_filsys fs,ext2_ino_t ino,struct inode_params * params)221 static errcode_t androidify_inode(ext2_filsys fs, ext2_ino_t ino,
222 struct inode_params *params)
223 {
224 errcode_t retval;
225
226 retval = set_timestamp(fs, ino, params);
227 if (retval)
228 return retval;
229
230 retval = set_selinux_xattr(fs, ino, params);
231 if (retval)
232 return retval;
233
234 return set_perms_and_caps(fs, ino, params);
235 }
236
walk_dir(ext2_ino_t dir EXT2FS_ATTR ((unused)),int flags EXT2FS_ATTR ((unused)),struct ext2_dir_entry * de,int offset EXT2FS_ATTR ((unused)),int blocksize EXT2FS_ATTR ((unused)),char * buf EXT2FS_ATTR ((unused)),void * priv_data)237 static int walk_dir(ext2_ino_t dir EXT2FS_ATTR((unused)),
238 int flags EXT2FS_ATTR((unused)),
239 struct ext2_dir_entry *de,
240 int offset EXT2FS_ATTR((unused)),
241 int blocksize EXT2FS_ATTR((unused)),
242 char *buf EXT2FS_ATTR((unused)), void *priv_data)
243 {
244 __u16 name_len;
245 errcode_t retval;
246 struct inode_params *params = (struct inode_params *)priv_data;
247
248 name_len = de->name_len & 0xff;
249 if (!strncmp(de->name, ".", name_len)
250 || (!strncmp(de->name, "..", name_len)))
251 return 0;
252
253 if (asprintf(¶ms->filename, "%s/%.*s", params->path, name_len,
254 de->name) < 0) {
255 params->error = ENOMEM;
256 return -ENOMEM;
257 }
258
259 if (!strncmp(de->name, "lost+found", 10)) {
260 retval = set_selinux_xattr(params->fs, de->inode, params);
261 if (retval)
262 goto end;
263 } else {
264 retval = androidify_inode(params->fs, de->inode, params);
265 if (retval)
266 goto end;
267 if (is_dir(params->fs, de->inode)) {
268 char *cur_path = params->path;
269 char *cur_filename = params->filename;
270 params->path = params->filename;
271 retval = ext2fs_dir_iterate2(params->fs, de->inode, 0, NULL,
272 walk_dir, params);
273 if (retval)
274 goto end;
275 params->path = cur_path;
276 params->filename = cur_filename;
277 }
278 }
279
280 end:
281 free(params->filename);
282 params->error |= retval;
283 return retval;
284 }
285
__android_configure_fs(ext2_filsys fs,char * src_dir,char * target_out,char * mountpoint,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)286 errcode_t __android_configure_fs(ext2_filsys fs, char *src_dir,
287 char *target_out,
288 char *mountpoint,
289 fs_config_f fs_config_func,
290 struct selabel_handle *sehnd,
291 time_t fixed_time,
292 const struct ugid_map* uid_map,
293 const struct ugid_map* gid_map)
294 {
295 errcode_t retval;
296 struct inode_params params = {
297 .fs = fs,
298 .src_dir = src_dir,
299 .target_out = target_out,
300 .fs_config_func = fs_config_func,
301 .sehnd = sehnd,
302 .fixed_time = fixed_time,
303 .path = mountpoint,
304 .filename = mountpoint,
305 .mountpoint = mountpoint,
306 .uid_map = uid_map,
307 .gid_map = gid_map,
308 .error = 0
309 };
310
311 /* walk_dir will add the "/". Don't add it twice. */
312 if (strlen(mountpoint) == 1 && mountpoint[0] == '/')
313 params.path = "";
314
315 retval = androidify_inode(fs, EXT2_ROOT_INO, ¶ms);
316 if (retval)
317 return retval;
318
319 retval = ext2fs_dir_iterate2(fs, EXT2_ROOT_INO, 0, NULL, walk_dir,
320 ¶ms);
321 if (retval)
322 return retval;
323 return params.error;
324 }
325
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,const struct ugid_map * uid_map,const struct ugid_map * gid_map)326 errcode_t android_configure_fs(ext2_filsys fs, char *src_dir, char *target_out,
327 char *mountpoint,
328 struct selinux_opt *seopts EXT2FS_ATTR((unused)),
329 unsigned int nopt EXT2FS_ATTR((unused)),
330 char *fs_config_file, time_t fixed_time,
331 const struct ugid_map* uid_map,
332 const struct ugid_map* gid_map)
333 {
334 errcode_t retval;
335 fs_config_f fs_config_func = NULL;
336 struct selabel_handle *sehnd = NULL;
337
338 /* Retrieve file contexts */
339 #if !defined(__ANDROID__)
340 if (nopt > 0) {
341 sehnd = selabel_open(SELABEL_CTX_FILE, seopts, nopt);
342 if (!sehnd) {
343 int saved_errno = errno;
344 com_err(__func__, errno,
345 _("while opening file contexts \"%s\""),
346 seopts[0].value);
347 return saved_errno;
348 }
349 }
350 #else
351 sehnd = selinux_android_file_context_handle();
352 if (!sehnd) {
353 com_err(__func__, EINVAL,
354 _("while opening android file_contexts"));
355 return EINVAL;
356 }
357 #endif
358
359 /* Load the FS config */
360 if (fs_config_file) {
361 retval = load_canned_fs_config(fs_config_file);
362 if (retval < 0) {
363 com_err(__func__, retval,
364 _("while loading fs_config \"%s\""),
365 fs_config_file);
366 return retval;
367 }
368 fs_config_func = canned_fs_config;
369 } else if (mountpoint)
370 fs_config_func = fs_config;
371
372 return __android_configure_fs(fs, src_dir, target_out, mountpoint,
373 fs_config_func, sehnd, fixed_time,
374 uid_map, gid_map);
375 }
376