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(&params->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, &params);
316 	if (retval)
317 		return retval;
318 
319 	retval = ext2fs_dir_iterate2(fs, EXT2_ROOT_INO, 0, NULL, walk_dir,
320 				     &params);
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