Merge tag 'for-linus' of git://git.armlinux.org.uk/~rmk/linux-arm
[linux-2.6-microblaze.git] / fs / ksmbd / ndr.c
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
12 static inline char *ndr_get_field(struct ndr *n)
13 {
14         return n->data + n->offset;
15 }
16
17 static 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
31 static void ndr_write_int16(struct ndr *n, __u16 value)
32 {
33         if (n->length <= n->offset + sizeof(value))
34                 try_to_realloc_ndr_blob(n, sizeof(value));
35
36         *(__le16 *)ndr_get_field(n) = cpu_to_le16(value);
37         n->offset += sizeof(value);
38 }
39
40 static void ndr_write_int32(struct ndr *n, __u32 value)
41 {
42         if (n->length <= n->offset + sizeof(value))
43                 try_to_realloc_ndr_blob(n, sizeof(value));
44
45         *(__le32 *)ndr_get_field(n) = cpu_to_le32(value);
46         n->offset += sizeof(value);
47 }
48
49 static void ndr_write_int64(struct ndr *n, __u64 value)
50 {
51         if (n->length <= n->offset + sizeof(value))
52                 try_to_realloc_ndr_blob(n, sizeof(value));
53
54         *(__le64 *)ndr_get_field(n) = cpu_to_le64(value);
55         n->offset += sizeof(value);
56 }
57
58 static int ndr_write_bytes(struct ndr *n, void *value, size_t sz)
59 {
60         if (n->length <= n->offset + sz)
61                 try_to_realloc_ndr_blob(n, sz);
62
63         memcpy(ndr_get_field(n), value, sz);
64         n->offset += sz;
65         return 0;
66 }
67
68 static int ndr_write_string(struct ndr *n, char *value)
69 {
70         size_t sz;
71
72         sz = strlen(value) + 1;
73         if (n->length <= n->offset + sz)
74                 try_to_realloc_ndr_blob(n, sz);
75
76         memcpy(ndr_get_field(n), value, sz);
77         n->offset += sz;
78         n->offset = ALIGN(n->offset, 2);
79         return 0;
80 }
81
82 static int ndr_read_string(struct ndr *n, void *value, size_t sz)
83 {
84         int len = strnlen(ndr_get_field(n), sz);
85
86         memcpy(value, ndr_get_field(n), len);
87         len++;
88         n->offset += len;
89         n->offset = ALIGN(n->offset, 2);
90         return 0;
91 }
92
93 static int ndr_read_bytes(struct ndr *n, void *value, size_t sz)
94 {
95         memcpy(value, ndr_get_field(n), sz);
96         n->offset += sz;
97         return 0;
98 }
99
100 static __u16 ndr_read_int16(struct ndr *n)
101 {
102         __u16 ret;
103
104         ret = le16_to_cpu(*(__le16 *)ndr_get_field(n));
105         n->offset += sizeof(__u16);
106         return ret;
107 }
108
109 static __u32 ndr_read_int32(struct ndr *n)
110 {
111         __u32 ret;
112
113         ret = le32_to_cpu(*(__le32 *)ndr_get_field(n));
114         n->offset += sizeof(__u32);
115         return ret;
116 }
117
118 static __u64 ndr_read_int64(struct ndr *n)
119 {
120         __u64 ret;
121
122         ret = le64_to_cpu(*(__le64 *)ndr_get_field(n));
123         n->offset += sizeof(__u64);
124         return ret;
125 }
126
127 int ndr_encode_dos_attr(struct ndr *n, struct xattr_dos_attrib *da)
128 {
129         char hex_attr[12] = {0};
130
131         n->offset = 0;
132         n->length = 1024;
133         n->data = kzalloc(n->length, GFP_KERNEL);
134         if (!n->data)
135                 return -ENOMEM;
136
137         if (da->version == 3) {
138                 snprintf(hex_attr, 10, "0x%x", da->attr);
139                 ndr_write_string(n, hex_attr);
140         } else {
141                 ndr_write_string(n, "");
142         }
143         ndr_write_int16(n, da->version);
144         ndr_write_int32(n, da->version);
145
146         ndr_write_int32(n, da->flags);
147         ndr_write_int32(n, da->attr);
148         if (da->version == 3) {
149                 ndr_write_int32(n, da->ea_size);
150                 ndr_write_int64(n, da->size);
151                 ndr_write_int64(n, da->alloc_size);
152         } else {
153                 ndr_write_int64(n, da->itime);
154         }
155         ndr_write_int64(n, da->create_time);
156         if (da->version == 3)
157                 ndr_write_int64(n, da->change_time);
158         return 0;
159 }
160
161 int ndr_decode_dos_attr(struct ndr *n, struct xattr_dos_attrib *da)
162 {
163         char *hex_attr;
164         int version2;
165
166         hex_attr = kzalloc(n->length, GFP_KERNEL);
167         if (!hex_attr)
168                 return -ENOMEM;
169
170         n->offset = 0;
171         ndr_read_string(n, hex_attr, n->length);
172         kfree(hex_attr);
173         da->version = ndr_read_int16(n);
174
175         if (da->version != 3 && da->version != 4) {
176                 pr_err("v%d version is not supported\n", da->version);
177                 return -EINVAL;
178         }
179
180         version2 = ndr_read_int32(n);
181         if (da->version != version2) {
182                 pr_err("ndr version mismatched(version: %d, version2: %d)\n",
183                        da->version, version2);
184                 return -EINVAL;
185         }
186
187         ndr_read_int32(n);
188         da->attr = ndr_read_int32(n);
189         if (da->version == 4) {
190                 da->itime = ndr_read_int64(n);
191                 da->create_time = ndr_read_int64(n);
192         } else {
193                 ndr_read_int32(n);
194                 ndr_read_int64(n);
195                 ndr_read_int64(n);
196                 da->create_time = ndr_read_int64(n);
197                 ndr_read_int64(n);
198         }
199
200         return 0;
201 }
202
203 static int ndr_encode_posix_acl_entry(struct ndr *n, struct xattr_smb_acl *acl)
204 {
205         int i;
206
207         ndr_write_int32(n, acl->count);
208         n->offset = ALIGN(n->offset, 8);
209         ndr_write_int32(n, acl->count);
210         ndr_write_int32(n, 0);
211
212         for (i = 0; i < acl->count; i++) {
213                 n->offset = ALIGN(n->offset, 8);
214                 ndr_write_int16(n, acl->entries[i].type);
215                 ndr_write_int16(n, acl->entries[i].type);
216
217                 if (acl->entries[i].type == SMB_ACL_USER) {
218                         n->offset = ALIGN(n->offset, 8);
219                         ndr_write_int64(n, acl->entries[i].uid);
220                 } else if (acl->entries[i].type == SMB_ACL_GROUP) {
221                         n->offset = ALIGN(n->offset, 8);
222                         ndr_write_int64(n, acl->entries[i].gid);
223                 }
224
225                 /* push permission */
226                 ndr_write_int32(n, acl->entries[i].perm);
227         }
228
229         return 0;
230 }
231
232 int ndr_encode_posix_acl(struct ndr *n,
233                          struct user_namespace *user_ns,
234                          struct inode *inode,
235                          struct xattr_smb_acl *acl,
236                          struct xattr_smb_acl *def_acl)
237 {
238         int ref_id = 0x00020000;
239
240         n->offset = 0;
241         n->length = 1024;
242         n->data = kzalloc(n->length, GFP_KERNEL);
243         if (!n->data)
244                 return -ENOMEM;
245
246         if (acl) {
247                 /* ACL ACCESS */
248                 ndr_write_int32(n, ref_id);
249                 ref_id += 4;
250         } else {
251                 ndr_write_int32(n, 0);
252         }
253
254         if (def_acl) {
255                 /* DEFAULT ACL ACCESS */
256                 ndr_write_int32(n, ref_id);
257                 ref_id += 4;
258         } else {
259                 ndr_write_int32(n, 0);
260         }
261
262         ndr_write_int64(n, from_kuid(user_ns, inode->i_uid));
263         ndr_write_int64(n, from_kgid(user_ns, inode->i_gid));
264         ndr_write_int32(n, inode->i_mode);
265
266         if (acl) {
267                 ndr_encode_posix_acl_entry(n, acl);
268                 if (def_acl)
269                         ndr_encode_posix_acl_entry(n, def_acl);
270         }
271         return 0;
272 }
273
274 int ndr_encode_v4_ntacl(struct ndr *n, struct xattr_ntacl *acl)
275 {
276         int ref_id = 0x00020004;
277
278         n->offset = 0;
279         n->length = 2048;
280         n->data = kzalloc(n->length, GFP_KERNEL);
281         if (!n->data)
282                 return -ENOMEM;
283
284         ndr_write_int16(n, acl->version);
285         ndr_write_int32(n, acl->version);
286         ndr_write_int16(n, 2);
287         ndr_write_int32(n, ref_id);
288
289         /* push hash type and hash 64bytes */
290         ndr_write_int16(n, acl->hash_type);
291         ndr_write_bytes(n, acl->hash, XATTR_SD_HASH_SIZE);
292         ndr_write_bytes(n, acl->desc, acl->desc_len);
293         ndr_write_int64(n, acl->current_time);
294         ndr_write_bytes(n, acl->posix_acl_hash, XATTR_SD_HASH_SIZE);
295
296         /* push ndr for security descriptor */
297         ndr_write_bytes(n, acl->sd_buf, acl->sd_size);
298
299         return 0;
300 }
301
302 int ndr_decode_v4_ntacl(struct ndr *n, struct xattr_ntacl *acl)
303 {
304         int version2;
305
306         n->offset = 0;
307         acl->version = ndr_read_int16(n);
308         if (acl->version != 4) {
309                 pr_err("v%d version is not supported\n", acl->version);
310                 return -EINVAL;
311         }
312
313         version2 = ndr_read_int32(n);
314         if (acl->version != version2) {
315                 pr_err("ndr version mismatched(version: %d, version2: %d)\n",
316                        acl->version, version2);
317                 return -EINVAL;
318         }
319
320         /* Read Level */
321         ndr_read_int16(n);
322         /* Read Ref Id */
323         ndr_read_int32(n);
324         acl->hash_type = ndr_read_int16(n);
325         ndr_read_bytes(n, acl->hash, XATTR_SD_HASH_SIZE);
326
327         ndr_read_bytes(n, acl->desc, 10);
328         if (strncmp(acl->desc, "posix_acl", 9)) {
329                 pr_err("Invalid acl description : %s\n", acl->desc);
330                 return -EINVAL;
331         }
332
333         /* Read Time */
334         ndr_read_int64(n);
335         /* Read Posix ACL hash */
336         ndr_read_bytes(n, acl->posix_acl_hash, XATTR_SD_HASH_SIZE);
337         acl->sd_size = n->length - n->offset;
338         acl->sd_buf = kzalloc(acl->sd_size, GFP_KERNEL);
339         if (!acl->sd_buf)
340                 return -ENOMEM;
341
342         ndr_read_bytes(n, acl->sd_buf, acl->sd_size);
343
344         return 0;
345 }