]>
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); | |
64b39f4a | 162 | } else { |
e2f34481 | 163 | ndr_write_int64(n, da->itime); |
64b39f4a | 164 | } |
e2f34481 NJ |
165 | ndr_write_int64(n, da->create_time); |
166 | if (da->version == 3) | |
167 | ndr_write_int64(n, da->change_time); | |
168 | return 0; | |
169 | } | |
170 | ||
171 | int ndr_decode_dos_attr(struct ndr *n, struct xattr_dos_attrib *da) | |
172 | { | |
173 | char hex_attr[12] = {0}; | |
174 | int version2; | |
175 | ||
176 | n->offset = 0; | |
177 | ndr_read_string(n, hex_attr, n->length - n->offset); | |
178 | da->version = ndr_read_int16(n); | |
179 | ||
180 | if (da->version != 3 && da->version != 4) { | |
181 | ksmbd_err("v%d version is not supported\n", da->version); | |
182 | return -EINVAL; | |
183 | } | |
184 | ||
185 | version2 = ndr_read_int32(n); | |
186 | if (da->version != version2) { | |
187 | ksmbd_err("ndr version mismatched(version: %d, version2: %d)\n", | |
188 | da->version, version2); | |
189 | return -EINVAL; | |
190 | } | |
191 | ||
192 | ndr_read_int32(n); | |
193 | da->attr = ndr_read_int32(n); | |
194 | if (da->version == 4) { | |
195 | da->itime = ndr_read_int64(n); | |
196 | da->create_time = ndr_read_int64(n); | |
197 | } else { | |
198 | ndr_read_int32(n); | |
199 | ndr_read_int64(n); | |
200 | ndr_read_int64(n); | |
201 | da->create_time = ndr_read_int64(n); | |
202 | ndr_read_int64(n); | |
203 | } | |
204 | ||
205 | return 0; | |
206 | } | |
207 | ||
208 | static int ndr_encode_posix_acl_entry(struct ndr *n, struct xattr_smb_acl *acl) | |
209 | { | |
210 | int i; | |
211 | ||
212 | ndr_write_int32(n, acl->count); | |
213 | align_offset(n, 8); | |
214 | ndr_write_int32(n, acl->count); | |
215 | ndr_write_int32(n, 0); | |
216 | ||
217 | for (i = 0; i < acl->count; i++) { | |
218 | align_offset(n, 8); | |
219 | ndr_write_int16(n, acl->entries[i].type); | |
220 | ndr_write_int16(n, acl->entries[i].type); | |
221 | ||
222 | if (acl->entries[i].type == SMB_ACL_USER) { | |
223 | align_offset(n, 8); | |
224 | ndr_write_int64(n, acl->entries[i].uid); | |
225 | } else if (acl->entries[i].type == SMB_ACL_GROUP) { | |
226 | align_offset(n, 8); | |
227 | ndr_write_int64(n, acl->entries[i].gid); | |
228 | } | |
229 | ||
230 | /* push permission */ | |
231 | ndr_write_int32(n, acl->entries[i].perm); | |
232 | } | |
233 | ||
234 | return 0; | |
235 | } | |
236 | ||
237 | int ndr_encode_posix_acl(struct ndr *n, struct inode *inode, | |
238 | struct xattr_smb_acl *acl, struct xattr_smb_acl *def_acl) | |
239 | { | |
240 | int ref_id = 0x00020000; | |
241 | ||
242 | n->offset = 0; | |
243 | n->length = 1024; | |
244 | n->data = kzalloc(n->length, GFP_KERNEL); | |
245 | if (!n->data) | |
246 | return -ENOMEM; | |
247 | ||
248 | if (acl) { | |
249 | /* ACL ACCESS */ | |
250 | ndr_write_int32(n, ref_id); | |
251 | ref_id += 4; | |
64b39f4a | 252 | } else { |
e2f34481 | 253 | ndr_write_int32(n, 0); |
64b39f4a | 254 | } |
e2f34481 NJ |
255 | |
256 | if (def_acl) { | |
257 | /* DEFAULT ACL ACCESS */ | |
258 | ndr_write_int32(n, ref_id); | |
259 | ref_id += 4; | |
64b39f4a | 260 | } else { |
e2f34481 | 261 | ndr_write_int32(n, 0); |
64b39f4a | 262 | } |
e2f34481 NJ |
263 | |
264 | ndr_write_int64(n, from_kuid(&init_user_ns, inode->i_uid)); | |
265 | ndr_write_int64(n, from_kgid(&init_user_ns, inode->i_gid)); | |
266 | ndr_write_int32(n, inode->i_mode); | |
267 | ||
268 | if (acl) { | |
269 | ndr_encode_posix_acl_entry(n, acl); | |
270 | if (def_acl) | |
271 | ndr_encode_posix_acl_entry(n, def_acl); | |
272 | } | |
273 | return 0; | |
274 | } | |
275 | ||
276 | int ndr_encode_v4_ntacl(struct ndr *n, struct xattr_ntacl *acl) | |
277 | { | |
278 | int ref_id = 0x00020004; | |
279 | ||
280 | n->offset = 0; | |
281 | n->length = 2048; | |
282 | n->data = kzalloc(n->length, GFP_KERNEL); | |
283 | if (!n->data) | |
284 | return -ENOMEM; | |
285 | ||
286 | ndr_write_int16(n, acl->version); | |
287 | ndr_write_int32(n, acl->version); | |
288 | ndr_write_int16(n, 2); | |
289 | ndr_write_int32(n, ref_id); | |
290 | ||
291 | /* push hash type and hash 64bytes */ | |
292 | ndr_write_int16(n, acl->hash_type); | |
293 | ndr_write_bytes(n, acl->hash, XATTR_SD_HASH_SIZE); | |
294 | ndr_write_bytes(n, acl->desc, acl->desc_len); | |
295 | ndr_write_int64(n, acl->current_time); | |
296 | ndr_write_bytes(n, acl->posix_acl_hash, XATTR_SD_HASH_SIZE); | |
297 | ||
298 | /* push ndr for security descriptor */ | |
299 | ndr_write_bytes(n, acl->sd_buf, acl->sd_size); | |
300 | ||
301 | return 0; | |
302 | } | |
303 | ||
304 | int ndr_decode_v4_ntacl(struct ndr *n, struct xattr_ntacl *acl) | |
305 | { | |
306 | int version2; | |
307 | ||
308 | n->offset = 0; | |
309 | acl->version = ndr_read_int16(n); | |
310 | if (acl->version != 4) { | |
311 | ksmbd_err("v%d version is not supported\n", acl->version); | |
312 | return -EINVAL; | |
313 | } | |
314 | ||
315 | version2 = ndr_read_int32(n); | |
316 | if (acl->version != version2) { | |
317 | ksmbd_err("ndr version mismatched(version: %d, version2: %d)\n", | |
318 | acl->version, version2); | |
319 | return -EINVAL; | |
320 | } | |
321 | ||
322 | /* Read Level */ | |
323 | ndr_read_int16(n); | |
324 | /* Read Ref Id */ | |
325 | ndr_read_int32(n); | |
326 | acl->hash_type = ndr_read_int16(n); | |
327 | ndr_read_bytes(n, acl->hash, XATTR_SD_HASH_SIZE); | |
328 | ||
329 | ndr_read_bytes(n, acl->desc, 10); | |
330 | if (strncmp(acl->desc, "posix_acl", 9)) { | |
1e853b93 | 331 | ksmbd_err("Invalid acl description : %s\n", acl->desc); |
e2f34481 NJ |
332 | return -EINVAL; |
333 | } | |
334 | ||
335 | /* Read Time */ | |
336 | ndr_read_int64(n); | |
337 | /* Read Posix ACL hash */ | |
338 | ndr_read_bytes(n, acl->posix_acl_hash, XATTR_SD_HASH_SIZE); | |
339 | acl->sd_size = n->length - n->offset; | |
340 | acl->sd_buf = kzalloc(acl->sd_size, GFP_KERNEL); | |
341 | if (!acl->sd_buf) | |
342 | return -ENOMEM; | |
343 | ||
344 | ndr_read_bytes(n, acl->sd_buf, acl->sd_size); | |
345 | ||
346 | return 0; | |
347 | } |