• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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(&params->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, &params);
317 	if (retval)
318 		return retval;
319 
320 	retval = ext2fs_dir_iterate2(fs, EXT2_ROOT_INO, 0, NULL, walk_dir,
321 				     &params);
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