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