vfs_gpfs: Move vfs_gpfs_lstat to nfs4_acls.c and rename function
[Samba.git] / libcli / smb / reparse.c
blob49ecc77725de74a243800dca7e2011f7b5c6bc36
1 /*
2 * Unix SMB/CIFS implementation.
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 3 of the License, or
7 * (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 #include "replace.h"
19 #include "libcli/smb/reparse.h"
20 #include "lib/util/iov_buf.h"
21 #include "libcli/smb/smb_constants.h"
22 #include "libcli/util/error.h"
23 #include "lib/util/debug.h"
24 #include "lib/util/bytearray.h"
25 #include "lib/util/talloc_stack.h"
26 #include "lib/util/charset/charset.h"
27 #include "smb_util.h"
29 static NTSTATUS reparse_buffer_check(const uint8_t *in_data,
30 size_t in_len,
31 uint32_t *reparse_tag,
32 const uint8_t **_reparse_data,
33 size_t *_reparse_data_length)
35 uint16_t reparse_data_length;
37 if (in_len == 0) {
38 DBG_DEBUG("in_len=0\n");
39 return NT_STATUS_INVALID_BUFFER_SIZE;
41 if (in_len < 8) {
42 DBG_DEBUG("in_len=%zu\n", in_len);
43 return NT_STATUS_IO_REPARSE_DATA_INVALID;
46 reparse_data_length = PULL_LE_U16(in_data, 4);
48 if (reparse_data_length > (in_len - 8)) {
49 DBG_DEBUG("in_len=%zu, reparse_data_length=%" PRIu16 "\n",
50 in_len,
51 reparse_data_length);
52 return NT_STATUS_IO_REPARSE_DATA_INVALID;
55 *reparse_tag = PULL_LE_U32(in_data, 0);
56 *_reparse_data = in_data + 8;
57 *_reparse_data_length = reparse_data_length;
59 return NT_STATUS_OK;
62 static int nfs_reparse_buffer_parse(TALLOC_CTX *mem_ctx,
63 struct nfs_reparse_data_buffer *dst,
64 const uint8_t *src,
65 size_t srclen)
67 uint64_t type;
69 if (srclen < 8) {
70 DBG_DEBUG("srclen=%zu too short\n", srclen);
71 return EINVAL;
74 type = PULL_LE_U64(src, 0);
76 switch (type) {
77 case NFS_SPECFILE_CHR:
78 FALL_THROUGH;
79 case NFS_SPECFILE_BLK:
80 if (srclen < 16) {
81 DBG_DEBUG("srclen %zu too short for type %" PRIx64 "\n",
82 srclen,
83 type);
84 return EINVAL;
86 dst->data.dev.major = PULL_LE_U32(src, 8);
87 dst->data.dev.minor = PULL_LE_U32(src, 12);
88 break;
89 case NFS_SPECFILE_LNK: {
90 bool ok;
92 ok = convert_string_talloc(mem_ctx,
93 CH_UTF16,
94 CH_UNIX,
95 src + 8,
96 srclen - 8,
97 &dst->data.lnk_target,
98 NULL);
99 if (!ok) {
100 return errno;
102 break;
104 case NFS_SPECFILE_FIFO:
105 break; /* empty, no data */
106 case NFS_SPECFILE_SOCK:
107 break; /* empty, no data */
108 default:
109 DBG_DEBUG("Unknown NFS reparse type %" PRIx64 "\n", type);
110 return EINVAL;
113 dst->type = type;
115 return 0;
118 static int symlink_reparse_buffer_parse(TALLOC_CTX *mem_ctx,
119 struct symlink_reparse_struct *dst,
120 const uint8_t *src,
121 size_t srclen)
123 uint16_t reparse_data_length;
124 uint16_t substitute_name_offset, substitute_name_length;
125 uint16_t print_name_offset, print_name_length;
126 bool ok;
128 if (srclen < 20) {
129 DBG_DEBUG("srclen = %zu, expected >= 20\n", srclen);
130 return EINVAL;
132 if (PULL_LE_U32(src, 0) != IO_REPARSE_TAG_SYMLINK) {
133 DBG_DEBUG("Got ReparseTag %8.8x, expected %8.8x\n",
134 PULL_LE_U32(src, 0),
135 IO_REPARSE_TAG_SYMLINK);
136 return EINVAL;
139 reparse_data_length = PULL_LE_U16(src, 4);
140 substitute_name_offset = PULL_LE_U16(src, 8);
141 substitute_name_length = PULL_LE_U16(src, 10);
142 print_name_offset = PULL_LE_U16(src, 12);
143 print_name_length = PULL_LE_U16(src, 14);
145 if (reparse_data_length < 12) {
146 DBG_DEBUG("reparse_data_length = %"PRIu16", expected >= 12\n",
147 reparse_data_length);
148 return EINVAL;
150 if (smb_buffer_oob(srclen - 8, reparse_data_length, 0)) {
151 DBG_DEBUG("reparse_data_length (%"PRIu16") too large for "
152 "src_len (%zu)\n",
153 reparse_data_length,
154 srclen);
155 return EINVAL;
157 if (smb_buffer_oob(reparse_data_length - 12, substitute_name_offset,
158 substitute_name_length)) {
159 DBG_DEBUG("substitute_name (%"PRIu16"/%"PRIu16") does not fit "
160 "in reparse_data_length (%"PRIu16")\n",
161 substitute_name_offset,
162 substitute_name_length,
163 reparse_data_length - 12);
164 return EINVAL;
166 if (smb_buffer_oob(reparse_data_length - 12, print_name_offset,
167 print_name_length)) {
168 DBG_DEBUG("print_name (%"PRIu16"/%"PRIu16") does not fit in "
169 "reparse_data_length (%"PRIu16")\n",
170 print_name_offset,
171 print_name_length,
172 reparse_data_length - 12);
173 return EINVAL;
176 *dst = (struct symlink_reparse_struct) {
177 .unparsed_path_length = PULL_LE_U16(src, 6),
178 .flags = PULL_LE_U32(src, 16),
181 ok = convert_string_talloc(mem_ctx,
182 CH_UTF16,
183 CH_UNIX,
184 src + 20 + substitute_name_offset,
185 substitute_name_length,
186 &dst->substitute_name,
187 NULL);
188 if (!ok) {
189 int ret = errno;
190 DBG_DEBUG("convert_string_talloc for substitute_name "
191 "failed\n");
192 return ret;
195 ok = convert_string_talloc(mem_ctx,
196 CH_UTF16,
197 CH_UNIX,
198 src + 20 + print_name_offset,
199 print_name_length,
200 &dst->print_name,
201 NULL);
202 if (!ok) {
203 int ret = errno;
204 DBG_DEBUG("convert_string_talloc for print_name failed\n");
205 TALLOC_FREE(dst->substitute_name);
206 return ret;
209 return 0;
212 NTSTATUS reparse_data_buffer_parse(TALLOC_CTX *mem_ctx,
213 struct reparse_data_buffer *dst,
214 const uint8_t *buf,
215 size_t buflen)
217 const uint8_t *reparse_data;
218 size_t reparse_data_length;
219 NTSTATUS status;
220 int ret;
222 status = reparse_buffer_check(buf,
223 buflen,
224 &dst->tag,
225 &reparse_data,
226 &reparse_data_length);
227 if (!NT_STATUS_IS_OK(status)) {
228 return status;
231 switch (dst->tag) {
232 case IO_REPARSE_TAG_SYMLINK:
233 ret = symlink_reparse_buffer_parse(mem_ctx,
234 &dst->parsed.lnk,
235 buf,
236 buflen);
237 if (ret != 0) {
238 return map_nt_error_from_unix_common(ret);
240 break;
241 case IO_REPARSE_TAG_NFS:
242 ret = nfs_reparse_buffer_parse(mem_ctx,
243 &dst->parsed.nfs,
244 reparse_data,
245 reparse_data_length);
246 if (ret != 0) {
247 return map_nt_error_from_unix_common(ret);
249 break;
250 default:
251 dst->parsed.raw.data = talloc_memdup(mem_ctx,
252 reparse_data,
253 reparse_data_length);
254 if (dst->parsed.raw.data == NULL) {
255 return NT_STATUS_NO_MEMORY;
257 dst->parsed.raw.length = reparse_data_length;
258 dst->parsed.raw.reserved = PULL_LE_U16(buf, 6);
259 break;
262 return NT_STATUS_OK;
265 char *reparse_data_buffer_str(TALLOC_CTX *mem_ctx,
266 const struct reparse_data_buffer *dst)
268 char *s = talloc_strdup(mem_ctx, "");
270 switch (dst->tag) {
271 case IO_REPARSE_TAG_SYMLINK: {
272 const struct symlink_reparse_struct *lnk = &dst->parsed.lnk;
273 talloc_asprintf_addbuf(&s,
274 "0x%" PRIx32
275 " (IO_REPARSE_TAG_SYMLINK)\n",
276 dst->tag);
277 talloc_asprintf_addbuf(&s,
278 "unparsed=%" PRIu16 "\n",
279 lnk->unparsed_path_length);
280 talloc_asprintf_addbuf(&s,
281 "substitute_name=%s\n",
282 lnk->substitute_name);
283 talloc_asprintf_addbuf(&s, "print_name=%s\n", lnk->print_name);
284 talloc_asprintf_addbuf(&s, "flags=%" PRIu32 "\n", lnk->flags);
285 break;
287 case IO_REPARSE_TAG_NFS: {
288 const struct nfs_reparse_data_buffer *nfs = &dst->parsed.nfs;
290 talloc_asprintf_addbuf(&s,
291 "0x%" PRIx32 " (IO_REPARSE_TAG_NFS)\n",
292 dst->tag);
294 switch (nfs->type) {
295 case NFS_SPECFILE_FIFO:
296 talloc_asprintf_addbuf(&s,
297 " 0x%" PRIx64
298 " (NFS_SPECFILE_FIFO)\n",
299 nfs->type);
300 break;
301 case NFS_SPECFILE_SOCK:
302 talloc_asprintf_addbuf(&s,
303 " 0x%" PRIx64
304 " (NFS_SPECFILE_SOCK)\n",
305 nfs->type);
306 break;
307 case NFS_SPECFILE_LNK:
308 talloc_asprintf_addbuf(&s,
309 " 0x%" PRIx64
310 " (NFS_SPECFILE_LNK)\n",
311 nfs->type);
312 talloc_asprintf_addbuf(&s,
313 " -> %s\n ",
314 nfs->data.lnk_target);
315 break;
316 case NFS_SPECFILE_BLK:
317 talloc_asprintf_addbuf(&s,
318 " 0x%" PRIx64
319 " (NFS_SPECFILE_BLK)\n",
320 nfs->type);
321 talloc_asprintf_addbuf(&s,
322 " %" PRIu32 "/%" PRIu32 "\n",
323 nfs->data.dev.major,
324 nfs->data.dev.minor);
325 break;
326 case NFS_SPECFILE_CHR:
327 talloc_asprintf_addbuf(&s,
328 " 0x%" PRIx64
329 " (NFS_SPECFILE_CHR)\n",
330 nfs->type);
331 talloc_asprintf_addbuf(&s,
332 " %" PRIu32 "/%" PRIu32 "\n",
333 nfs->data.dev.major,
334 nfs->data.dev.minor);
335 break;
336 default:
337 talloc_asprintf_addbuf(&s,
338 " 0x%" PRIu64
339 " (Unknown type)\n",
340 nfs->type);
341 break;
343 break;
345 default:
346 talloc_asprintf_addbuf(&s, "%" PRIu32 "\n", dst->tag);
347 break;
349 return s;
352 static ssize_t reparse_buffer_marshall(uint32_t reparse_tag,
353 uint16_t reserved,
354 const struct iovec *iov,
355 int iovlen,
356 uint8_t *buf,
357 size_t buflen)
359 ssize_t reparse_data_length = iov_buflen(iov, iovlen);
360 size_t needed;
362 if (reparse_data_length == -1) {
363 return -1;
365 if (reparse_data_length > UINT16_MAX) {
366 return -1;
369 needed = reparse_data_length + 8;
370 if (needed < reparse_data_length) {
371 return -1;
374 if (buflen >= needed) {
375 PUSH_LE_U32(buf, 0, reparse_tag);
376 PUSH_LE_U16(buf, 4, reparse_data_length);
377 PUSH_LE_U16(buf, 6, reserved);
378 iov_buf(iov, iovlen, buf + 8, buflen - 8);
381 return needed;
384 static ssize_t
385 reparse_data_buffer_marshall_syml(const struct symlink_reparse_struct *src,
386 uint8_t *buf,
387 size_t buflen)
389 uint8_t sbuf[12];
390 struct iovec iov[3];
391 const char *print_name = src->print_name;
392 uint8_t *subst_utf16 = NULL;
393 uint8_t *print_utf16 = NULL;
394 size_t subst_len = 0;
395 size_t print_len = 0;
396 ssize_t ret = -1;
397 bool ok;
399 if (src->substitute_name == NULL) {
400 return -1;
402 if (src->print_name == NULL) {
403 print_name = src->substitute_name;
406 iov[0] = (struct iovec){
407 .iov_base = sbuf,
408 .iov_len = sizeof(sbuf),
411 ok = convert_string_talloc(talloc_tos(),
412 CH_UNIX,
413 CH_UTF16,
414 src->substitute_name,
415 strlen(src->substitute_name),
416 &subst_utf16,
417 &subst_len);
418 if (!ok) {
419 goto fail;
421 if (subst_len > UINT16_MAX) {
422 goto fail;
424 iov[1] = (struct iovec){
425 .iov_base = subst_utf16,
426 .iov_len = subst_len,
429 ok = convert_string_talloc(talloc_tos(),
430 CH_UNIX,
431 CH_UTF16,
432 print_name,
433 strlen(print_name),
434 &print_utf16,
435 &print_len);
436 if (!ok) {
437 goto fail;
439 if (print_len > UINT16_MAX) {
440 goto fail;
442 iov[2] = (struct iovec){
443 .iov_base = print_utf16,
444 .iov_len = print_len,
447 PUSH_LE_U16(sbuf, 0, 0); /* SubstituteNameOffset */
448 PUSH_LE_U16(sbuf, 2, subst_len); /* SubstituteNameLength */
449 PUSH_LE_U16(sbuf, 4, subst_len); /* PrintNameOffset */
450 PUSH_LE_U16(sbuf, 6, print_len); /* PrintNameLength */
451 PUSH_LE_U32(sbuf, 8, src->flags); /* Flags */
453 ret = reparse_buffer_marshall(IO_REPARSE_TAG_SYMLINK,
454 src->unparsed_path_length,
455 iov,
456 ARRAY_SIZE(iov),
457 buf,
458 buflen);
460 fail:
461 TALLOC_FREE(subst_utf16);
462 TALLOC_FREE(print_utf16);
463 return ret;
466 static ssize_t
467 reparse_data_buffer_marshall_nfs(const struct nfs_reparse_data_buffer *src,
468 uint8_t *buf,
469 size_t buflen)
471 uint8_t typebuf[8];
472 uint8_t devbuf[8];
473 struct iovec iov[2] = {};
474 size_t iovlen;
475 uint8_t *lnk_utf16 = NULL;
476 size_t lnk_len = 0;
477 ssize_t ret;
479 PUSH_LE_U64(typebuf, 0, src->type);
480 iov[0] = (struct iovec){
481 .iov_base = typebuf,
482 .iov_len = sizeof(typebuf),
484 iovlen = 1;
486 switch (src->type) {
487 case NFS_SPECFILE_LNK: {
488 bool ok = convert_string_talloc(talloc_tos(),
489 CH_UNIX,
490 CH_UTF16,
491 src->data.lnk_target,
492 strlen(src->data.lnk_target),
493 &lnk_utf16,
494 &lnk_len);
495 if (!ok) {
496 return -1;
498 iov[1] = (struct iovec){
499 .iov_base = lnk_utf16,
500 .iov_len = lnk_len,
502 iovlen = 2;
503 break;
505 case NFS_SPECFILE_CHR:
506 FALL_THROUGH;
507 case NFS_SPECFILE_BLK:
508 PUSH_LE_U32(devbuf, 0, src->data.dev.major);
509 PUSH_LE_U32(devbuf, 4, src->data.dev.minor);
510 iov[1] = (struct iovec){
511 .iov_base = devbuf,
512 .iov_len = sizeof(devbuf),
514 iovlen = 2;
515 break;
516 default:
517 break;
518 /* Nothing to do for NFS_SPECFILE_FIFO and _SOCK */
521 ret = reparse_buffer_marshall(IO_REPARSE_TAG_NFS,
523 iov,
524 iovlen,
525 buf,
526 buflen);
527 TALLOC_FREE(lnk_utf16);
528 return ret;
531 ssize_t reparse_data_buffer_marshall(const struct reparse_data_buffer *src,
532 uint8_t *buf,
533 size_t buflen)
535 ssize_t ret = -1;
537 switch (src->tag) {
538 case IO_REPARSE_TAG_SYMLINK:
540 ret = reparse_data_buffer_marshall_syml(&src->parsed.lnk,
541 buf,
542 buflen);
543 break;
545 case IO_REPARSE_TAG_NFS:
547 ret = reparse_data_buffer_marshall_nfs(&src->parsed.nfs,
548 buf,
549 buflen);
550 break;
552 default: {
553 struct iovec iov = {
554 .iov_base = src->parsed.raw.data,
555 .iov_len = src->parsed.raw.length,
557 ret = reparse_buffer_marshall(src->tag,
558 src->parsed.raw.reserved,
559 &iov,
561 buf,
562 buflen);
566 return ret;