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