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