1// SPDX-License-Identifier: GPL-2.0
2/*
3 * fs/hmdfs/comm/authority/authentication.c
4 *
5 * Copyright (c) 2020-2021 Huawei Device Co., Ltd.
6 */
7
8#include "authentication.h"
9#include <linux/fsnotify.h>
10#include <linux/security.h>
11
12#include "hmdfs.h"
13
14struct fs_struct *hmdfs_override_fsstruct(struct fs_struct *saved_fs)
15{
16#if (defined CONFIG_HMDFS_FS_PERMISSION) && (defined CONFIG_SDCARD_FS)
17	struct fs_struct *copied_fs = copy_fs_struct(saved_fs);
18
19	if (!copied_fs)
20		return NULL;
21	copied_fs->umask = 0;
22	task_lock(current);
23	current->fs = copied_fs;
24	task_unlock(current);
25	return copied_fs;
26#else
27	return saved_fs;
28#endif
29}
30
31void hmdfs_revert_fsstruct(struct fs_struct *saved_fs,
32			   struct fs_struct *copied_fs)
33{
34#if (defined CONFIG_HMDFS_FS_PERMISSION) && (defined CONFIG_SDCARD_FS)
35	task_lock(current);
36	current->fs = saved_fs;
37	task_unlock(current);
38	free_fs_struct(copied_fs);
39#endif
40}
41
42const struct cred *hmdfs_override_fsids(bool is_recv_thread)
43{
44	struct cred *cred = NULL;
45	const struct cred *old_cred = NULL;
46
47	cred = prepare_creds();
48	if (!cred)
49		return NULL;
50
51	cred->fsuid = is_recv_thread ? SYSTEM_UID : USER_DATA_RW_UID;
52	cred->fsgid = is_recv_thread ? SYSTEM_GID : USER_DATA_RW_GID;
53
54	old_cred = override_creds(cred);
55
56	return old_cred;
57}
58
59const struct cred *hmdfs_override_dir_fsids(struct inode *dir,
60					    struct dentry *dentry, __u16 *_perm)
61{
62	struct hmdfs_inode_info *hii = hmdfs_i(dir);
63	struct cred *cred = NULL;
64	const struct cred *old_cred = NULL;
65	__u16 level = hmdfs_perm_get_next_level(hii->perm);
66	__u16 perm = 0;
67
68	cred = prepare_creds();
69	if (!cred)
70		return NULL;
71
72	switch (level) {
73	case HMDFS_PERM_MNT:
74		/* system : media_rw */
75		cred->fsuid = USER_DATA_RW_UID;
76		cred->fsgid = USER_DATA_RW_GID;
77		perm = (hii->perm & HMDFS_DIR_TYPE_MASK) | level;
78		break;
79	case HMDFS_PERM_DFS:
80		/*
81		 * data  : system : media_rw
82		 * system: system : media_rw, need authority
83		 * services: dfs_share : dfs_share
84		 * other : media_rw : media_rw
85		 **/
86		if (!strcmp(dentry->d_name.name, DFS_SHARE_NAME)) {
87			perm = HMDFS_DIR_SERVICES | level;
88			cred->fsuid = DFS_SHARE_UID;
89			cred->fsgid = DFS_SHARE_GID;
90			break;
91		}
92		if (!strcmp(dentry->d_name.name, PKG_ROOT_NAME)) {
93			perm = HMDFS_DIR_DATA | level;
94		} else {
95			perm = HMDFS_DIR_PUBLIC | level;
96		}
97		cred->fsuid = USER_DATA_RW_UID;
98		cred->fsgid = USER_DATA_RW_GID;
99		break;
100	case HMDFS_PERM_PKG:
101		if (is_service_dir(hii->perm)) {
102			cred->fsuid = DFS_SHARE_UID;
103			cred->fsgid = DFS_SHARE_GID;
104			perm = AUTH_SERVICES | HMDFS_DIR_PKG | level;
105			break;
106		}
107		if (is_data_dir(hii->perm)) {
108			/*
109			 * Mkdir for app pkg.
110			 * Get the appid by passing pkgname to configfs.
111			 * Set ROOT + media_rw for remote install,
112			 * local uninstall.
113			 * Set appid + media_rw for local install.
114			 */
115			int bid = get_bundle_uid(hmdfs_sb(dentry->d_sb),
116				dentry->d_name.name);
117
118			if (bid != 0) {
119				cred->fsuid = KUIDT_INIT(bid);
120				cred->fsgid = KGIDT_INIT(bid);
121			} else {
122				cred->fsuid = ROOT_UID;
123				cred->fsgid = ROOT_GID;
124			}
125			perm = AUTH_PKG | HMDFS_DIR_PKG | level;
126		} else {
127			cred->fsuid = dir->i_uid;
128			cred->fsgid = dir->i_gid;
129			perm = (hii->perm & AUTH_MASK) | HMDFS_DIR_DEFAULT | level;
130		}
131		break;
132	case HMDFS_PERM_OTHER:
133		cred->fsuid = dir->i_uid;
134		cred->fsgid = dir->i_gid;
135		if (is_pkg_auth(hii->perm))
136			perm = AUTH_PKG | HMDFS_DIR_PKG_SUB | level;
137		else
138			perm = (hii->perm & AUTH_MASK) | HMDFS_DIR_DEFAULT | level;
139		break;
140	default:
141		/* ! it should not get to here */
142		hmdfs_err("hmdfs perm incorrect got default case, level:%u", level);
143		break;
144	}
145
146	*_perm = perm;
147	old_cred = override_creds(cred);
148
149	return old_cred;
150}
151
152int hmdfs_override_dir_id_fs(struct cache_fs_override *or,
153			struct inode *dir,
154			struct dentry *dentry,
155			__u16 *perm)
156{
157	or->saved_cred = hmdfs_override_dir_fsids(dir, dentry, perm);
158	if (!or->saved_cred)
159		return -ENOMEM;
160
161	or->saved_fs = current->fs;
162	or->copied_fs = hmdfs_override_fsstruct(or->saved_fs);
163	if (!or->copied_fs) {
164		hmdfs_revert_fsids(or->saved_cred);
165		return -ENOMEM;
166	}
167
168	return 0;
169}
170
171void hmdfs_revert_dir_id_fs(struct cache_fs_override *or)
172{
173	hmdfs_revert_fsstruct(or->saved_fs, or->copied_fs);
174	hmdfs_revert_fsids(or->saved_cred);
175}
176
177const struct cred *hmdfs_override_file_fsids(struct inode *dir, __u16 *_perm)
178{
179	struct hmdfs_inode_info *hii = hmdfs_i(dir);
180	struct cred *cred = NULL;
181	const struct cred *old_cred = NULL;
182	__u16 level = hmdfs_perm_get_next_level(hii->perm);
183	uint16_t perm;
184
185	perm = HMDFS_FILE_DEFAULT | level;
186
187	cred = prepare_creds();
188	if (!cred)
189		return NULL;
190
191	cred->fsuid = dir->i_uid;
192	cred->fsgid = dir->i_gid;
193	if (is_pkg_auth(hii->perm))
194		perm = AUTH_PKG | HMDFS_FILE_PKG_SUB | level;
195	else
196		perm = (hii->perm & AUTH_MASK) | HMDFS_FILE_DEFAULT | level;
197
198	*_perm = perm;
199	old_cred = override_creds(cred);
200
201	return old_cred;
202}
203
204void hmdfs_revert_fsids(const struct cred *old_cred)
205{
206	const struct cred *cur_cred;
207
208	cur_cred = current->cred;
209	revert_creds(old_cred);
210	put_cred(cur_cred);
211}
212
213int hmdfs_persist_perm(struct dentry *dentry, __u16 *perm)
214{
215	int err;
216	struct inode *minode = d_inode(dentry);
217
218	if (!minode)
219		return -EINVAL;
220
221	inode_lock(minode);
222	err = __vfs_setxattr(dentry, minode, HMDFS_PERM_XATTR, perm,
223			     sizeof(*perm), XATTR_CREATE);
224	if (!err)
225		fsnotify_xattr(dentry);
226	else if (err && err != -EEXIST)
227		hmdfs_err("failed to setxattr, err=%d", err);
228	inode_unlock(minode);
229	return err;
230}
231
232__u16 hmdfs_read_perm(struct inode *inode)
233{
234	__u16 ret = 0;
235	int size = 0;
236	struct dentry *dentry = d_find_alias(inode);
237
238	if (!dentry)
239		return ret;
240
241	size = __vfs_getxattr(dentry, inode, HMDFS_PERM_XATTR, &ret,
242			     sizeof(ret));
243	 /*
244	  * some file may not set setxattr with perm
245	  * eg. files created in sdcard dir by other user
246	  **/
247	if (size < 0 || size != sizeof(ret))
248		ret = HMDFS_ALL_MASK;
249
250	dput(dentry);
251	return ret;
252}
253
254static __u16 __inherit_perm_dir(struct inode *parent, struct inode *inode)
255{
256	__u16 perm = 0;
257	struct hmdfs_inode_info *info = hmdfs_i(parent);
258	__u16 level = hmdfs_perm_get_next_level(info->perm);
259	struct dentry *dentry = d_find_alias(inode);
260
261	if (!dentry)
262		return perm;
263
264	switch (level) {
265	case HMDFS_PERM_MNT:
266		/* system : media_rw */
267		perm = (info->perm & HMDFS_DIR_TYPE_MASK) | level;
268		break;
269	case HMDFS_PERM_DFS:
270		/*
271		 * data  : system : media_rw
272		 * system: system : media_rw, need authority
273		 * services: dfs_share : dfs_share
274		 * other : media_rw : media_rw
275		 **/
276		if (!strcmp(dentry->d_name.name, DFS_SHARE_NAME)) {
277			// "services"
278			perm = HMDFS_DIR_SERVICES | level;
279		} else if (!strcmp(dentry->d_name.name, PKG_ROOT_NAME)) {
280			// "data"
281			perm = HMDFS_DIR_DATA | level;
282		} else if (!strcmp(dentry->d_name.name, SYSTEM_NAME)) {
283			 // "system"
284			perm = AUTH_SYSTEM | HMDFS_DIR_SYSTEM | level;
285		} else {
286			perm = HMDFS_DIR_PUBLIC | level;
287		}
288		break;
289	case HMDFS_PERM_PKG:
290		if (is_service_dir(info->perm)) {
291			perm = AUTH_SERVICES | HMDFS_DIR_PKG | level;
292			break;
293		}
294		if (is_data_dir(info->perm)) {
295			/*
296			 * Mkdir for app pkg.
297			 * Get the appid by passing pkgname to configfs.
298			 * Set ROOT + media_rw for remote install,
299			 * local uninstall.
300			 * Set appid + media_rw for local install.
301			 */
302			perm = AUTH_PKG | HMDFS_DIR_PKG | level;
303		} else {
304			perm = (info->perm & AUTH_MASK) | HMDFS_DIR_DEFAULT | level;
305		}
306		break;
307	case HMDFS_PERM_OTHER:
308		if (is_pkg_auth(info->perm))
309			perm = AUTH_PKG | HMDFS_DIR_PKG_SUB | level;
310		else
311			perm = (info->perm & AUTH_MASK) | HMDFS_DIR_DEFAULT | level;
312		break;
313	default:
314		/* ! it should not get to here */
315		hmdfs_err("hmdfs perm incorrect got default case, level:%u", level);
316		break;
317	}
318	dput(dentry);
319	return perm;
320}
321
322static __u16 __inherit_perm_file(struct inode *parent)
323{
324	struct hmdfs_inode_info *hii = hmdfs_i(parent);
325	__u16 level = hmdfs_perm_get_next_level(hii->perm);
326	uint16_t perm;
327
328	perm = HMDFS_FILE_DEFAULT | level;
329
330	if (is_pkg_auth(hii->perm))
331		perm = AUTH_PKG | HMDFS_FILE_PKG_SUB | level;
332	else
333		perm = (hii->perm & AUTH_MASK) | HMDFS_FILE_DEFAULT | level;
334
335	return perm;
336}
337
338__u16 hmdfs_perm_inherit(struct inode *parent_inode, struct inode *child)
339{
340	__u16 perm;
341
342	if (S_ISDIR(child->i_mode))
343		perm = __inherit_perm_dir(parent_inode, child);
344	else
345		perm = __inherit_perm_file(parent_inode);
346	return perm;
347}
348
349void check_and_fixup_ownership(struct inode *parent_inode, struct inode *child)
350{
351	struct hmdfs_inode_info *info = hmdfs_i(child);
352	struct hmdfs_inode_info *dir = hmdfs_i(parent_inode);
353
354	if (info->perm == HMDFS_ALL_MASK)
355		info->perm = hmdfs_perm_inherit(parent_inode, child);
356	if (is_service_dir(dir->perm))
357		child->i_mode = child->i_mode | S_IRWXG;
358}
359
360void check_and_fixup_ownership_remote(struct inode *dir,
361				      struct inode *dinode,
362				      struct dentry *dentry)
363{
364	struct hmdfs_inode_info *hii = hmdfs_i(dir);
365	struct hmdfs_inode_info *dinfo = hmdfs_i(dinode);
366	__u16 level = hmdfs_perm_get_next_level(hii->perm);
367	__u16 perm = 0;
368
369	if (IS_ERR_OR_NULL(dinode))
370		return;
371
372	hmdfs_debug("level:0x%X", level);
373	switch (level) {
374	case HMDFS_PERM_MNT:
375		/* system : media_rw */
376		dinode->i_uid = USER_DATA_RW_UID;
377		dinode->i_gid = USER_DATA_RW_GID;
378		perm = (hii->perm & HMDFS_DIR_TYPE_MASK) | level;
379		break;
380	case HMDFS_PERM_DFS:
381		/*
382		 * data  : system : media_rw
383		 * system: system : media_rw, need authority
384		 * other : media_rw : media_rw
385		 **/
386		if (!strcmp(dentry->d_name.name, DFS_SHARE_NAME)) {
387			perm = HMDFS_DIR_SERVICES | level;
388			dinode->i_uid = DFS_SHARE_UID;
389			dinode->i_gid = DFS_SHARE_GID;
390			dinode->i_mode = dinode->i_mode | S_IRWXG;
391			break;
392		}
393		if (!strcmp(dentry->d_name.name, PKG_ROOT_NAME)) {
394			perm = HMDFS_DIR_DATA | level;
395		} else {
396			perm = HMDFS_DIR_PUBLIC | level;
397		}
398		dinode->i_uid = USER_DATA_RW_UID;
399		dinode->i_gid = USER_DATA_RW_GID;
400		break;
401	case HMDFS_PERM_PKG:
402		if (is_service_dir(hii->perm)) {
403			dinode->i_uid = DFS_SHARE_UID;
404			dinode->i_gid = DFS_SHARE_GID;
405			dinode->i_mode = dinode->i_mode | S_IRWXG;
406			perm = AUTH_SERVICES | HMDFS_DIR_PKG | level;
407			break;
408		}
409		if (is_data_dir(hii->perm)) {
410			/*
411			 * Mkdir for app pkg.
412			 * Get the appid by passing pkgname to configfs.
413			 * Set ROOT + media_rw for remote install,
414			 * local uninstall.
415			 * Set appid + media_rw for local install.
416			 */
417			int bid = get_bundle_uid(hmdfs_sb(dentry->d_sb),
418				dentry->d_name.name);
419			if (bid != 0) {
420				dinode->i_uid = KUIDT_INIT(bid);
421				dinode->i_gid = KGIDT_INIT(bid);
422			} else {
423				dinode->i_uid = ROOT_UID;
424				dinode->i_gid = ROOT_GID;
425			}
426			perm = AUTH_PKG | HMDFS_DIR_PKG | level;
427		} else {
428			dinode->i_uid = dir->i_uid;
429			dinode->i_gid = dir->i_gid;
430			perm = (hii->perm & AUTH_MASK) | HMDFS_DIR_DEFAULT | level;
431		}
432		break;
433	case HMDFS_PERM_OTHER:
434		dinode->i_uid = dir->i_uid;
435		dinode->i_gid = dir->i_gid;
436		if (is_service_auth(hii->perm)) {
437			dinode->i_mode = dir->i_mode | S_IRWXG;
438			perm = AUTH_PKG | HMDFS_DIR_PKG_SUB | level;
439			break;
440		}
441		if (is_pkg_auth(hii->perm))
442			perm = AUTH_PKG | HMDFS_DIR_PKG_SUB | level;
443		else
444			perm = (hii->perm & AUTH_MASK) | HMDFS_DIR_DEFAULT | level;
445		break;
446	default:
447		/* ! it should not get to here */
448		hmdfs_err("hmdfs perm incorrect got default case, level:%u", level);
449		break;
450	}
451
452	dinfo->perm = perm;
453}
454
455void hmdfs_root_inode_perm_init(struct inode *root_inode)
456{
457	struct hmdfs_inode_info *hii = hmdfs_i(root_inode);
458
459	hii->perm = HMDFS_DIR_ROOT | HMDFS_PERM_MNT;
460	set_inode_uid(root_inode, USER_DATA_RW_UID);
461	set_inode_gid(root_inode, USER_DATA_RW_GID);
462}
463