ndr.c revision 303fff2b
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 0;
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		pr_err("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		pr_err("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		ndr_read_int64(n, NULL);
279		if (ret)
280			return ret;
281
282		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 user_namespace *user_ns,
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
349	n->offset = 0;
350	n->length = 1024;
351	n->data = kzalloc(n->length, GFP_KERNEL);
352	if (!n->data)
353		return -ENOMEM;
354
355	if (acl) {
356		/* ACL ACCESS */
357		ret = ndr_write_int32(n, ref_id);
358		ref_id += 4;
359	} else {
360		ret = ndr_write_int32(n, 0);
361	}
362	if (ret)
363		return ret;
364
365	if (def_acl) {
366		/* DEFAULT ACL ACCESS */
367		ret = ndr_write_int32(n, ref_id);
368		ref_id += 4;
369	} else {
370		ret = ndr_write_int32(n, 0);
371	}
372	if (ret)
373		return ret;
374
375	ret = ndr_write_int64(n, from_kuid(&init_user_ns, i_uid_into_mnt(user_ns, inode)));
376	if (ret)
377		return ret;
378	ret = ndr_write_int64(n, from_kgid(&init_user_ns, i_gid_into_mnt(user_ns, inode)));
379	if (ret)
380		return ret;
381	ret = ndr_write_int32(n, inode->i_mode);
382	if (ret)
383		return ret;
384
385	if (acl) {
386		ret = ndr_encode_posix_acl_entry(n, acl);
387		if (def_acl && !ret)
388			ret = ndr_encode_posix_acl_entry(n, def_acl);
389	}
390	return ret;
391}
392
393int ndr_encode_v4_ntacl(struct ndr *n, struct xattr_ntacl *acl)
394{
395	unsigned int ref_id = 0x00020004;
396	int ret;
397
398	n->offset = 0;
399	n->length = 2048;
400	n->data = kzalloc(n->length, GFP_KERNEL);
401	if (!n->data)
402		return -ENOMEM;
403
404	ret = ndr_write_int16(n, acl->version);
405	if (ret)
406		return ret;
407
408	ret = ndr_write_int32(n, acl->version);
409	if (ret)
410		return ret;
411
412	ret = ndr_write_int16(n, 2);
413	if (ret)
414		return ret;
415
416	ret = ndr_write_int32(n, ref_id);
417	if (ret)
418		return ret;
419
420	/* push hash type and hash 64bytes */
421	ret = ndr_write_int16(n, acl->hash_type);
422	if (ret)
423		return ret;
424
425	ret = ndr_write_bytes(n, acl->hash, XATTR_SD_HASH_SIZE);
426	if (ret)
427		return ret;
428
429	ret = ndr_write_bytes(n, acl->desc, acl->desc_len);
430	if (ret)
431		return ret;
432
433	ret = ndr_write_int64(n, acl->current_time);
434	if (ret)
435		return ret;
436
437	ret = ndr_write_bytes(n, acl->posix_acl_hash, XATTR_SD_HASH_SIZE);
438	if (ret)
439		return ret;
440
441	/* push ndr for security descriptor */
442	ret = ndr_write_bytes(n, acl->sd_buf, acl->sd_size);
443	return ret;
444}
445
446int ndr_decode_v4_ntacl(struct ndr *n, struct xattr_ntacl *acl)
447{
448	unsigned int version2;
449	int ret;
450
451	n->offset = 0;
452	ret = ndr_read_int16(n, &acl->version);
453	if (ret)
454		return ret;
455	if (acl->version != 4) {
456		pr_err("v%d version is not supported\n", acl->version);
457		return -EINVAL;
458	}
459
460	ret = ndr_read_int32(n, &version2);
461	if (ret)
462		return ret;
463	if (acl->version != version2) {
464		pr_err("ndr version mismatched(version: %d, version2: %d)\n",
465		       acl->version, version2);
466		return -EINVAL;
467	}
468
469	/* Read Level */
470	ret = ndr_read_int16(n, NULL);
471	if (ret)
472		return ret;
473
474	/* Read Ref Id */
475	ret = ndr_read_int32(n, NULL);
476	if (ret)
477		return ret;
478
479	ret = ndr_read_int16(n, &acl->hash_type);
480	if (ret)
481		return ret;
482
483	ret = ndr_read_bytes(n, acl->hash, XATTR_SD_HASH_SIZE);
484	if (ret)
485		return ret;
486
487	ndr_read_bytes(n, acl->desc, 10);
488	if (strncmp(acl->desc, "posix_acl", 9)) {
489		pr_err("Invalid acl description : %s\n", acl->desc);
490		return -EINVAL;
491	}
492
493	/* Read Time */
494	ret = ndr_read_int64(n, NULL);
495	if (ret)
496		return ret;
497
498	/* Read Posix ACL hash */
499	ret = ndr_read_bytes(n, acl->posix_acl_hash, XATTR_SD_HASH_SIZE);
500	if (ret)
501		return ret;
502
503	acl->sd_size = n->length - n->offset;
504	acl->sd_buf = kzalloc(acl->sd_size, GFP_KERNEL);
505	if (!acl->sd_buf)
506		return -ENOMEM;
507
508	ret = ndr_read_bytes(n, acl->sd_buf, acl->sd_size);
509	return ret;
510}
511