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