1// SPDX-License-Identifier: GPL-2.0-or-later
2/*
3 *   Copyright (C) 2021 Samsung Electronics Co., Ltd.
4 *   Author(s): Namjae Jeon <linkinjeon@kernel.org>
5 */
6
7#include <linux/fs.h>
8
9#include "glob.h"
10#include "ndr.h"
11
12static inline char *ndr_get_field(struct ndr *n)
13{
14	return n->data + n->offset;
15}
16
17static int try_to_realloc_ndr_blob(struct ndr *n, size_t sz)
18{
19	char *data;
20
21	data = krealloc(n->data, n->offset + sz + 1024, GFP_KERNEL);
22	if (!data)
23		return -ENOMEM;
24
25	n->data = data;
26	n->length += 1024;
27	memset(n->data + n->offset, 0, 1024);
28	return 0;
29}
30
31static int ndr_write_int16(struct ndr *n, __u16 value)
32{
33	if (n->length <= n->offset + sizeof(value)) {
34		int ret;
35
36		ret = try_to_realloc_ndr_blob(n, sizeof(value));
37		if (ret)
38			return ret;
39	}
40
41	*(__le16 *)ndr_get_field(n) = cpu_to_le16(value);
42	n->offset += sizeof(value);
43	return 0;
44}
45
46static int ndr_write_int32(struct ndr *n, __u32 value)
47{
48	if (n->length <= n->offset + sizeof(value)) {
49		int ret;
50
51		ret = try_to_realloc_ndr_blob(n, sizeof(value));
52		if (ret)
53			return ret;
54	}
55
56	*(__le32 *)ndr_get_field(n) = cpu_to_le32(value);
57	n->offset += sizeof(value);
58	return 0;
59}
60
61static int ndr_write_int64(struct ndr *n, __u64 value)
62{
63	if (n->length <= n->offset + sizeof(value)) {
64		int ret;
65
66		ret = try_to_realloc_ndr_blob(n, sizeof(value));
67		if (ret)
68			return ret;
69	}
70
71	*(__le64 *)ndr_get_field(n) = cpu_to_le64(value);
72	n->offset += sizeof(value);
73	return 0;
74}
75
76static int ndr_write_bytes(struct ndr *n, void *value, size_t sz)
77{
78	if (n->length <= n->offset + sz) {
79		int ret;
80
81		ret = try_to_realloc_ndr_blob(n, sz);
82		if (ret)
83			return ret;
84	}
85
86	memcpy(ndr_get_field(n), value, sz);
87	n->offset += sz;
88	return 0;
89}
90
91static int ndr_write_string(struct ndr *n, char *value)
92{
93	size_t sz;
94
95	sz = strlen(value) + 1;
96	if (n->length <= n->offset + sz) {
97		int ret;
98
99		ret = try_to_realloc_ndr_blob(n, sz);
100		if (ret)
101			return ret;
102	}
103
104	memcpy(ndr_get_field(n), value, sz);
105	n->offset += sz;
106	n->offset = ALIGN(n->offset, 2);
107	return 0;
108}
109
110static int ndr_read_string(struct ndr *n, void *value, size_t sz)
111{
112	int len;
113
114	if (n->offset + sz > n->length)
115		return -EINVAL;
116
117	len = strnlen(ndr_get_field(n), sz);
118	if (value)
119		memcpy(value, ndr_get_field(n), len);
120	len++;
121	n->offset += len;
122	n->offset = ALIGN(n->offset, 2);
123	return 0;
124}
125
126static int ndr_read_bytes(struct ndr *n, void *value, size_t sz)
127{
128	if (n->offset + sz > n->length)
129		return -EINVAL;
130
131	if (value)
132		memcpy(value, ndr_get_field(n), sz);
133	n->offset += sz;
134	return 0;
135}
136
137static int ndr_read_int16(struct ndr *n, __u16 *value)
138{
139	if (n->offset + sizeof(__u16) > n->length)
140		return -EINVAL;
141
142	if (value)
143		*value = le16_to_cpu(*(__le16 *)ndr_get_field(n));
144	n->offset += sizeof(__u16);
145	return 0;
146}
147
148static int ndr_read_int32(struct ndr *n, __u32 *value)
149{
150	if (n->offset + sizeof(__u32) > n->length)
151		return -EINVAL;
152
153	if (value)
154		*value = le32_to_cpu(*(__le32 *)ndr_get_field(n));
155	n->offset += sizeof(__u32);
156	return 0;
157}
158
159static int ndr_read_int64(struct ndr *n, __u64 *value)
160{
161	if (n->offset + sizeof(__u64) > n->length)
162		return -EINVAL;
163
164	if (value)
165		*value = le64_to_cpu(*(__le64 *)ndr_get_field(n));
166	n->offset += sizeof(__u64);
167	return 0;
168}
169
170int ndr_encode_dos_attr(struct ndr *n, struct xattr_dos_attrib *da)
171{
172	char hex_attr[12] = {0};
173	int ret;
174
175	n->offset = 0;
176	n->length = 1024;
177	n->data = kzalloc(n->length, GFP_KERNEL);
178	if (!n->data)
179		return -ENOMEM;
180
181	if (da->version == 3) {
182		snprintf(hex_attr, 10, "0x%x", da->attr);
183		ret = ndr_write_string(n, hex_attr);
184	} else {
185		ret = ndr_write_string(n, "");
186	}
187	if (ret)
188		return ret;
189
190	ret = ndr_write_int16(n, da->version);
191	if (ret)
192		return ret;
193
194	ret = ndr_write_int32(n, da->version);
195	if (ret)
196		return ret;
197
198	ret = ndr_write_int32(n, da->flags);
199	if (ret)
200		return ret;
201
202	ret = ndr_write_int32(n, da->attr);
203	if (ret)
204		return ret;
205
206	if (da->version == 3) {
207		ret = ndr_write_int32(n, da->ea_size);
208		if (ret)
209			return ret;
210		ret = ndr_write_int64(n, da->size);
211		if (ret)
212			return ret;
213		ret = ndr_write_int64(n, da->alloc_size);
214	} else {
215		ret = ndr_write_int64(n, da->itime);
216	}
217	if (ret)
218		return ret;
219
220	ret = ndr_write_int64(n, da->create_time);
221	if (ret)
222		return ret;
223
224	if (da->version == 3)
225		ret = ndr_write_int64(n, da->change_time);
226	return ret;
227}
228
229int ndr_decode_dos_attr(struct ndr *n, struct xattr_dos_attrib *da)
230{
231	char hex_attr[12];
232	unsigned int version2;
233	int ret;
234
235	n->offset = 0;
236	ret = ndr_read_string(n, hex_attr, sizeof(hex_attr));
237	if (ret)
238		return ret;
239
240	ret = ndr_read_int16(n, &da->version);
241	if (ret)
242		return ret;
243
244	if (da->version != 3 && da->version != 4) {
245		ksmbd_debug(VFS, "v%d version is not supported\n", da->version);
246		return -EINVAL;
247	}
248
249	ret = ndr_read_int32(n, &version2);
250	if (ret)
251		return ret;
252
253	if (da->version != version2) {
254		ksmbd_debug(VFS, "ndr version mismatched(version: %d, version2: %d)\n",
255		       da->version, version2);
256		return -EINVAL;
257	}
258
259	ret = ndr_read_int32(n, NULL);
260	if (ret)
261		return ret;
262
263	ret = ndr_read_int32(n, &da->attr);
264	if (ret)
265		return ret;
266
267	if (da->version == 4) {
268		ret = ndr_read_int64(n, &da->itime);
269		if (ret)
270			return ret;
271
272		ret = ndr_read_int64(n, &da->create_time);
273	} else {
274		ret = ndr_read_int32(n, NULL);
275		if (ret)
276			return ret;
277
278		ret = ndr_read_int64(n, NULL);
279		if (ret)
280			return ret;
281
282		ret = ndr_read_int64(n, NULL);
283		if (ret)
284			return ret;
285
286		ret = ndr_read_int64(n, &da->create_time);
287		if (ret)
288			return ret;
289
290		ret = ndr_read_int64(n, NULL);
291	}
292
293	return ret;
294}
295
296static int ndr_encode_posix_acl_entry(struct ndr *n, struct xattr_smb_acl *acl)
297{
298	int i, ret;
299
300	ret = ndr_write_int32(n, acl->count);
301	if (ret)
302		return ret;
303
304	n->offset = ALIGN(n->offset, 8);
305	ret = ndr_write_int32(n, acl->count);
306	if (ret)
307		return ret;
308
309	ret = ndr_write_int32(n, 0);
310	if (ret)
311		return ret;
312
313	for (i = 0; i < acl->count; i++) {
314		n->offset = ALIGN(n->offset, 8);
315		ret = ndr_write_int16(n, acl->entries[i].type);
316		if (ret)
317			return ret;
318
319		ret = ndr_write_int16(n, acl->entries[i].type);
320		if (ret)
321			return ret;
322
323		if (acl->entries[i].type == SMB_ACL_USER) {
324			n->offset = ALIGN(n->offset, 8);
325			ret = ndr_write_int64(n, acl->entries[i].uid);
326		} else if (acl->entries[i].type == SMB_ACL_GROUP) {
327			n->offset = ALIGN(n->offset, 8);
328			ret = ndr_write_int64(n, acl->entries[i].gid);
329		}
330		if (ret)
331			return ret;
332
333		/* push permission */
334		ret = ndr_write_int32(n, acl->entries[i].perm);
335	}
336
337	return ret;
338}
339
340int ndr_encode_posix_acl(struct ndr *n,
341			 struct mnt_idmap *idmap,
342			 struct inode *inode,
343			 struct xattr_smb_acl *acl,
344			 struct xattr_smb_acl *def_acl)
345{
346	unsigned int ref_id = 0x00020000;
347	int ret;
348	vfsuid_t vfsuid;
349	vfsgid_t vfsgid;
350
351	n->offset = 0;
352	n->length = 1024;
353	n->data = kzalloc(n->length, GFP_KERNEL);
354	if (!n->data)
355		return -ENOMEM;
356
357	if (acl) {
358		/* ACL ACCESS */
359		ret = ndr_write_int32(n, ref_id);
360		ref_id += 4;
361	} else {
362		ret = ndr_write_int32(n, 0);
363	}
364	if (ret)
365		return ret;
366
367	if (def_acl) {
368		/* DEFAULT ACL ACCESS */
369		ret = ndr_write_int32(n, ref_id);
370		ref_id += 4;
371	} else {
372		ret = ndr_write_int32(n, 0);
373	}
374	if (ret)
375		return ret;
376
377	vfsuid = i_uid_into_vfsuid(idmap, inode);
378	ret = ndr_write_int64(n, from_kuid(&init_user_ns, vfsuid_into_kuid(vfsuid)));
379	if (ret)
380		return ret;
381	vfsgid = i_gid_into_vfsgid(idmap, inode);
382	ret = ndr_write_int64(n, from_kgid(&init_user_ns, vfsgid_into_kgid(vfsgid)));
383	if (ret)
384		return ret;
385	ret = ndr_write_int32(n, inode->i_mode);
386	if (ret)
387		return ret;
388
389	if (acl) {
390		ret = ndr_encode_posix_acl_entry(n, acl);
391		if (def_acl && !ret)
392			ret = ndr_encode_posix_acl_entry(n, def_acl);
393	}
394	return ret;
395}
396
397int ndr_encode_v4_ntacl(struct ndr *n, struct xattr_ntacl *acl)
398{
399	unsigned int ref_id = 0x00020004;
400	int ret;
401
402	n->offset = 0;
403	n->length = 2048;
404	n->data = kzalloc(n->length, GFP_KERNEL);
405	if (!n->data)
406		return -ENOMEM;
407
408	ret = ndr_write_int16(n, acl->version);
409	if (ret)
410		return ret;
411
412	ret = ndr_write_int32(n, acl->version);
413	if (ret)
414		return ret;
415
416	ret = ndr_write_int16(n, 2);
417	if (ret)
418		return ret;
419
420	ret = ndr_write_int32(n, ref_id);
421	if (ret)
422		return ret;
423
424	/* push hash type and hash 64bytes */
425	ret = ndr_write_int16(n, acl->hash_type);
426	if (ret)
427		return ret;
428
429	ret = ndr_write_bytes(n, acl->hash, XATTR_SD_HASH_SIZE);
430	if (ret)
431		return ret;
432
433	ret = ndr_write_bytes(n, acl->desc, acl->desc_len);
434	if (ret)
435		return ret;
436
437	ret = ndr_write_int64(n, acl->current_time);
438	if (ret)
439		return ret;
440
441	ret = ndr_write_bytes(n, acl->posix_acl_hash, XATTR_SD_HASH_SIZE);
442	if (ret)
443		return ret;
444
445	/* push ndr for security descriptor */
446	ret = ndr_write_bytes(n, acl->sd_buf, acl->sd_size);
447	return ret;
448}
449
450int ndr_decode_v4_ntacl(struct ndr *n, struct xattr_ntacl *acl)
451{
452	unsigned int version2;
453	int ret;
454
455	n->offset = 0;
456	ret = ndr_read_int16(n, &acl->version);
457	if (ret)
458		return ret;
459	if (acl->version != 4) {
460		ksmbd_debug(VFS, "v%d version is not supported\n", acl->version);
461		return -EINVAL;
462	}
463
464	ret = ndr_read_int32(n, &version2);
465	if (ret)
466		return ret;
467	if (acl->version != version2) {
468		ksmbd_debug(VFS, "ndr version mismatched(version: %d, version2: %d)\n",
469		       acl->version, version2);
470		return -EINVAL;
471	}
472
473	/* Read Level */
474	ret = ndr_read_int16(n, NULL);
475	if (ret)
476		return ret;
477
478	/* Read Ref Id */
479	ret = ndr_read_int32(n, NULL);
480	if (ret)
481		return ret;
482
483	ret = ndr_read_int16(n, &acl->hash_type);
484	if (ret)
485		return ret;
486
487	ret = ndr_read_bytes(n, acl->hash, XATTR_SD_HASH_SIZE);
488	if (ret)
489		return ret;
490
491	ndr_read_bytes(n, acl->desc, 10);
492	if (strncmp(acl->desc, "posix_acl", 9)) {
493		pr_err("Invalid acl description : %s\n", acl->desc);
494		return -EINVAL;
495	}
496
497	/* Read Time */
498	ret = ndr_read_int64(n, NULL);
499	if (ret)
500		return ret;
501
502	/* Read Posix ACL hash */
503	ret = ndr_read_bytes(n, acl->posix_acl_hash, XATTR_SD_HASH_SIZE);
504	if (ret)
505		return ret;
506
507	acl->sd_size = n->length - n->offset;
508	acl->sd_buf = kzalloc(acl->sd_size, GFP_KERNEL);
509	if (!acl->sd_buf)
510		return -ENOMEM;
511
512	ret = ndr_read_bytes(n, acl->sd_buf, acl->sd_size);
513	return ret;
514}
515