Line data Source code
1 : /*
2 : * Unix SMB/CIFS implementation.
3 : *
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.
8 : *
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.
13 : *
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/>.
16 : */
17 :
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"
28 :
29 2 : 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)
34 : {
35 0 : uint16_t reparse_data_length;
36 :
37 2 : if (in_len == 0) {
38 0 : DBG_DEBUG("in_len=0\n");
39 0 : return NT_STATUS_INVALID_BUFFER_SIZE;
40 : }
41 2 : if (in_len < 8) {
42 0 : DBG_DEBUG("in_len=%zu\n", in_len);
43 0 : return NT_STATUS_IO_REPARSE_DATA_INVALID;
44 : }
45 :
46 2 : reparse_data_length = PULL_LE_U16(in_data, 4);
47 :
48 2 : if (reparse_data_length > (in_len - 8)) {
49 0 : DBG_DEBUG("in_len=%zu, reparse_data_length=%" PRIu16 "\n",
50 : in_len,
51 : reparse_data_length);
52 0 : return NT_STATUS_IO_REPARSE_DATA_INVALID;
53 : }
54 :
55 2 : *reparse_tag = PULL_LE_U32(in_data, 0);
56 2 : *_reparse_data = in_data + 8;
57 2 : *_reparse_data_length = reparse_data_length;
58 :
59 2 : return NT_STATUS_OK;
60 : }
61 :
62 0 : 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)
66 : {
67 0 : uint64_t type;
68 :
69 0 : if (srclen < 8) {
70 0 : DBG_DEBUG("srclen=%zu too short\n", srclen);
71 0 : return EINVAL;
72 : }
73 :
74 0 : type = PULL_LE_U64(src, 0);
75 :
76 0 : switch (type) {
77 0 : case NFS_SPECFILE_CHR:
78 0 : FALL_THROUGH;
79 : case NFS_SPECFILE_BLK:
80 0 : if (srclen < 16) {
81 0 : DBG_DEBUG("srclen %zu too short for type %" PRIx64 "\n",
82 : srclen,
83 : type);
84 0 : return EINVAL;
85 : }
86 0 : dst->data.dev.major = PULL_LE_U32(src, 8);
87 0 : dst->data.dev.minor = PULL_LE_U32(src, 12);
88 0 : break;
89 0 : case NFS_SPECFILE_LNK: {
90 0 : bool ok;
91 :
92 0 : ok = convert_string_talloc(mem_ctx,
93 : CH_UTF16,
94 : CH_UNIX,
95 0 : src + 8,
96 : srclen - 8,
97 0 : &dst->data.lnk_target,
98 : NULL);
99 0 : if (!ok) {
100 0 : return errno;
101 : }
102 0 : break;
103 : }
104 0 : case NFS_SPECFILE_FIFO:
105 0 : break; /* empty, no data */
106 0 : case NFS_SPECFILE_SOCK:
107 0 : break; /* empty, no data */
108 0 : default:
109 0 : DBG_DEBUG("Unknown NFS reparse type %" PRIx64 "\n", type);
110 0 : return EINVAL;
111 : }
112 :
113 0 : dst->type = type;
114 :
115 0 : return 0;
116 : }
117 :
118 2 : 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)
122 : {
123 0 : uint16_t reparse_data_length;
124 0 : uint16_t substitute_name_offset, substitute_name_length;
125 0 : uint16_t print_name_offset, print_name_length;
126 0 : bool ok;
127 :
128 2 : if (srclen < 20) {
129 0 : DBG_DEBUG("srclen = %zu, expected >= 20\n", srclen);
130 0 : return EINVAL;
131 : }
132 2 : if (PULL_LE_U32(src, 0) != IO_REPARSE_TAG_SYMLINK) {
133 0 : DBG_DEBUG("Got ReparseTag %8.8x, expected %8.8x\n",
134 : PULL_LE_U32(src, 0),
135 : IO_REPARSE_TAG_SYMLINK);
136 0 : return EINVAL;
137 : }
138 :
139 2 : reparse_data_length = PULL_LE_U16(src, 4);
140 2 : substitute_name_offset = PULL_LE_U16(src, 8);
141 2 : substitute_name_length = PULL_LE_U16(src, 10);
142 2 : print_name_offset = PULL_LE_U16(src, 12);
143 2 : print_name_length = PULL_LE_U16(src, 14);
144 :
145 2 : if (reparse_data_length < 12) {
146 0 : DBG_DEBUG("reparse_data_length = %"PRIu16", expected >= 12\n",
147 : reparse_data_length);
148 0 : return EINVAL;
149 : }
150 2 : if (smb_buffer_oob(srclen - 8, reparse_data_length, 0)) {
151 0 : DBG_DEBUG("reparse_data_length (%"PRIu16") too large for "
152 : "src_len (%zu)\n",
153 : reparse_data_length,
154 : srclen);
155 0 : return EINVAL;
156 : }
157 2 : if (smb_buffer_oob(reparse_data_length - 12, substitute_name_offset,
158 : substitute_name_length)) {
159 0 : 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 0 : return EINVAL;
165 : }
166 2 : if (smb_buffer_oob(reparse_data_length - 12, print_name_offset,
167 : print_name_length)) {
168 0 : 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 0 : return EINVAL;
174 : }
175 :
176 2 : *dst = (struct symlink_reparse_struct) {
177 2 : .unparsed_path_length = PULL_LE_U16(src, 6),
178 2 : .flags = PULL_LE_U32(src, 16),
179 : };
180 :
181 2 : ok = convert_string_talloc(mem_ctx,
182 : CH_UTF16,
183 : CH_UNIX,
184 2 : src + 20 + substitute_name_offset,
185 : substitute_name_length,
186 2 : &dst->substitute_name,
187 : NULL);
188 2 : if (!ok) {
189 0 : int ret = errno;
190 0 : DBG_DEBUG("convert_string_talloc for substitute_name "
191 : "failed\n");
192 0 : return ret;
193 : }
194 :
195 2 : ok = convert_string_talloc(mem_ctx,
196 : CH_UTF16,
197 : CH_UNIX,
198 2 : src + 20 + print_name_offset,
199 : print_name_length,
200 2 : &dst->print_name,
201 : NULL);
202 2 : if (!ok) {
203 0 : int ret = errno;
204 0 : DBG_DEBUG("convert_string_talloc for print_name failed\n");
205 0 : TALLOC_FREE(dst->substitute_name);
206 0 : return ret;
207 : }
208 :
209 2 : return 0;
210 : }
211 :
212 2 : NTSTATUS reparse_data_buffer_parse(TALLOC_CTX *mem_ctx,
213 : struct reparse_data_buffer *dst,
214 : const uint8_t *buf,
215 : size_t buflen)
216 : {
217 0 : const uint8_t *reparse_data;
218 0 : size_t reparse_data_length;
219 0 : NTSTATUS status;
220 0 : int ret;
221 :
222 2 : status = reparse_buffer_check(buf,
223 : buflen,
224 : &dst->tag,
225 : &reparse_data,
226 : &reparse_data_length);
227 2 : if (!NT_STATUS_IS_OK(status)) {
228 0 : return status;
229 : }
230 :
231 2 : switch (dst->tag) {
232 2 : case IO_REPARSE_TAG_SYMLINK:
233 2 : ret = symlink_reparse_buffer_parse(mem_ctx,
234 : &dst->parsed.lnk,
235 : buf,
236 : buflen);
237 2 : if (ret != 0) {
238 0 : return map_nt_error_from_unix_common(ret);
239 : }
240 2 : break;
241 0 : case IO_REPARSE_TAG_NFS:
242 0 : ret = nfs_reparse_buffer_parse(mem_ctx,
243 : &dst->parsed.nfs,
244 : reparse_data,
245 : reparse_data_length);
246 0 : if (ret != 0) {
247 0 : return map_nt_error_from_unix_common(ret);
248 : }
249 0 : break;
250 0 : default:
251 0 : dst->parsed.raw.data = talloc_memdup(mem_ctx,
252 : reparse_data,
253 : reparse_data_length);
254 0 : if (dst->parsed.raw.data == NULL) {
255 0 : return NT_STATUS_NO_MEMORY;
256 : }
257 0 : dst->parsed.raw.length = reparse_data_length;
258 0 : dst->parsed.raw.reserved = PULL_LE_U16(buf, 6);
259 0 : break;
260 : }
261 :
262 2 : return NT_STATUS_OK;
263 : }
264 :
265 0 : char *reparse_data_buffer_str(TALLOC_CTX *mem_ctx,
266 : const struct reparse_data_buffer *dst)
267 : {
268 0 : char *s = talloc_strdup(mem_ctx, "");
269 :
270 0 : switch (dst->tag) {
271 0 : case IO_REPARSE_TAG_SYMLINK: {
272 0 : const struct symlink_reparse_struct *lnk = &dst->parsed.lnk;
273 0 : talloc_asprintf_addbuf(&s,
274 : "0x%" PRIx32
275 : " (IO_REPARSE_TAG_SYMLINK)\n",
276 0 : dst->tag);
277 0 : talloc_asprintf_addbuf(&s,
278 : "unparsed=%" PRIu16 "\n",
279 0 : lnk->unparsed_path_length);
280 0 : talloc_asprintf_addbuf(&s,
281 : "substitute_name=%s\n",
282 0 : lnk->substitute_name);
283 0 : talloc_asprintf_addbuf(&s, "print_name=%s\n", lnk->print_name);
284 0 : talloc_asprintf_addbuf(&s, "flags=%" PRIu32 "\n", lnk->flags);
285 0 : break;
286 : }
287 0 : case IO_REPARSE_TAG_NFS: {
288 0 : const struct nfs_reparse_data_buffer *nfs = &dst->parsed.nfs;
289 :
290 0 : talloc_asprintf_addbuf(&s,
291 : "0x%" PRIx32 " (IO_REPARSE_TAG_NFS)\n",
292 0 : dst->tag);
293 :
294 0 : switch (nfs->type) {
295 0 : case NFS_SPECFILE_FIFO:
296 0 : talloc_asprintf_addbuf(&s,
297 : " 0x%" PRIx64
298 : " (NFS_SPECFILE_FIFO)\n",
299 0 : nfs->type);
300 0 : break;
301 0 : case NFS_SPECFILE_SOCK:
302 0 : talloc_asprintf_addbuf(&s,
303 : " 0x%" PRIx64
304 : " (NFS_SPECFILE_SOCK)\n",
305 0 : nfs->type);
306 0 : break;
307 0 : case NFS_SPECFILE_LNK:
308 0 : talloc_asprintf_addbuf(&s,
309 : " 0x%" PRIx64
310 : " (NFS_SPECFILE_LNK)\n",
311 0 : nfs->type);
312 0 : talloc_asprintf_addbuf(&s,
313 : " -> %s\n ",
314 0 : nfs->data.lnk_target);
315 0 : break;
316 0 : case NFS_SPECFILE_BLK:
317 0 : talloc_asprintf_addbuf(&s,
318 : " 0x%" PRIx64
319 : " (NFS_SPECFILE_BLK)\n",
320 0 : nfs->type);
321 0 : talloc_asprintf_addbuf(&s,
322 : " %" PRIu32 "/%" PRIu32 "\n",
323 0 : nfs->data.dev.major,
324 0 : nfs->data.dev.minor);
325 0 : break;
326 0 : case NFS_SPECFILE_CHR:
327 0 : talloc_asprintf_addbuf(&s,
328 : " 0x%" PRIx64
329 : " (NFS_SPECFILE_CHR)\n",
330 0 : nfs->type);
331 0 : talloc_asprintf_addbuf(&s,
332 : " %" PRIu32 "/%" PRIu32 "\n",
333 0 : nfs->data.dev.major,
334 0 : nfs->data.dev.minor);
335 0 : break;
336 0 : default:
337 0 : talloc_asprintf_addbuf(&s,
338 : " 0x%" PRIu64
339 : " (Unknown type)\n",
340 0 : nfs->type);
341 0 : break;
342 : }
343 0 : break;
344 : }
345 0 : default:
346 0 : talloc_asprintf_addbuf(&s, "%" PRIu32 "\n", dst->tag);
347 0 : break;
348 : }
349 0 : return s;
350 : }
351 :
352 28 : 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)
358 : {
359 28 : ssize_t reparse_data_length = iov_buflen(iov, iovlen);
360 0 : size_t needed;
361 :
362 28 : if (reparse_data_length == -1) {
363 0 : return -1;
364 : }
365 28 : if (reparse_data_length > UINT16_MAX) {
366 0 : return -1;
367 : }
368 :
369 28 : needed = reparse_data_length + 8;
370 28 : if (needed < reparse_data_length) {
371 0 : return -1;
372 : }
373 :
374 28 : if (buflen >= needed) {
375 14 : PUSH_LE_U32(buf, 0, reparse_tag);
376 14 : PUSH_LE_U16(buf, 4, reparse_data_length);
377 14 : PUSH_LE_U16(buf, 6, reserved);
378 14 : iov_buf(iov, iovlen, buf + 8, buflen - 8);
379 : }
380 :
381 28 : return needed;
382 : }
383 :
384 : static ssize_t
385 28 : reparse_data_buffer_marshall_syml(const struct symlink_reparse_struct *src,
386 : uint8_t *buf,
387 : size_t buflen)
388 : {
389 0 : uint8_t sbuf[12];
390 0 : struct iovec iov[3];
391 28 : const char *print_name = src->print_name;
392 28 : uint8_t *subst_utf16 = NULL;
393 28 : uint8_t *print_utf16 = NULL;
394 28 : size_t subst_len = 0;
395 28 : size_t print_len = 0;
396 28 : ssize_t ret = -1;
397 0 : bool ok;
398 :
399 28 : if (src->substitute_name == NULL) {
400 0 : return -1;
401 : }
402 28 : if (src->print_name == NULL) {
403 0 : print_name = src->substitute_name;
404 : }
405 :
406 28 : iov[0] = (struct iovec){
407 : .iov_base = sbuf,
408 : .iov_len = sizeof(sbuf),
409 : };
410 :
411 28 : ok = convert_string_talloc(talloc_tos(),
412 : CH_UNIX,
413 : CH_UTF16,
414 28 : src->substitute_name,
415 28 : strlen(src->substitute_name),
416 : &subst_utf16,
417 : &subst_len);
418 28 : if (!ok) {
419 0 : goto fail;
420 : }
421 28 : if (subst_len > UINT16_MAX) {
422 0 : goto fail;
423 : }
424 28 : iov[1] = (struct iovec){
425 : .iov_base = subst_utf16,
426 : .iov_len = subst_len,
427 : };
428 :
429 28 : 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 28 : if (!ok) {
437 0 : goto fail;
438 : }
439 28 : if (print_len > UINT16_MAX) {
440 0 : goto fail;
441 : }
442 28 : iov[2] = (struct iovec){
443 : .iov_base = print_utf16,
444 : .iov_len = print_len,
445 : };
446 :
447 28 : PUSH_LE_U16(sbuf, 0, 0); /* SubstituteNameOffset */
448 28 : PUSH_LE_U16(sbuf, 2, subst_len); /* SubstituteNameLength */
449 28 : PUSH_LE_U16(sbuf, 4, subst_len); /* PrintNameOffset */
450 28 : PUSH_LE_U16(sbuf, 6, print_len); /* PrintNameLength */
451 28 : PUSH_LE_U32(sbuf, 8, src->flags); /* Flags */
452 :
453 28 : ret = reparse_buffer_marshall(IO_REPARSE_TAG_SYMLINK,
454 28 : src->unparsed_path_length,
455 : iov,
456 : ARRAY_SIZE(iov),
457 : buf,
458 : buflen);
459 :
460 28 : fail:
461 28 : TALLOC_FREE(subst_utf16);
462 28 : TALLOC_FREE(print_utf16);
463 28 : return ret;
464 : }
465 :
466 : static ssize_t
467 0 : reparse_data_buffer_marshall_nfs(const struct nfs_reparse_data_buffer *src,
468 : uint8_t *buf,
469 : size_t buflen)
470 : {
471 0 : uint8_t typebuf[8];
472 0 : uint8_t devbuf[8];
473 0 : struct iovec iov[2] = {};
474 0 : size_t iovlen;
475 0 : uint8_t *lnk_utf16 = NULL;
476 0 : size_t lnk_len = 0;
477 0 : ssize_t ret;
478 :
479 0 : PUSH_LE_U64(typebuf, 0, src->type);
480 0 : iov[0] = (struct iovec){
481 : .iov_base = typebuf,
482 : .iov_len = sizeof(typebuf),
483 : };
484 0 : iovlen = 1;
485 :
486 0 : switch (src->type) {
487 0 : case NFS_SPECFILE_LNK: {
488 0 : bool ok = convert_string_talloc(talloc_tos(),
489 : CH_UNIX,
490 : CH_UTF16,
491 0 : src->data.lnk_target,
492 0 : strlen(src->data.lnk_target),
493 : &lnk_utf16,
494 : &lnk_len);
495 0 : if (!ok) {
496 0 : return -1;
497 : }
498 0 : iov[1] = (struct iovec){
499 : .iov_base = lnk_utf16,
500 : .iov_len = lnk_len,
501 : };
502 0 : iovlen = 2;
503 0 : break;
504 : }
505 0 : case NFS_SPECFILE_CHR:
506 0 : FALL_THROUGH;
507 : case NFS_SPECFILE_BLK:
508 0 : PUSH_LE_U32(devbuf, 0, src->data.dev.major);
509 0 : PUSH_LE_U32(devbuf, 4, src->data.dev.minor);
510 0 : iov[1] = (struct iovec){
511 : .iov_base = devbuf,
512 : .iov_len = sizeof(devbuf),
513 : };
514 0 : iovlen = 2;
515 0 : break;
516 0 : default:
517 0 : break;
518 : /* Nothing to do for NFS_SPECFILE_FIFO and _SOCK */
519 : }
520 :
521 0 : ret = reparse_buffer_marshall(IO_REPARSE_TAG_NFS,
522 : 0,
523 : iov,
524 : iovlen,
525 : buf,
526 : buflen);
527 0 : TALLOC_FREE(lnk_utf16);
528 0 : return ret;
529 : }
530 :
531 28 : ssize_t reparse_data_buffer_marshall(const struct reparse_data_buffer *src,
532 : uint8_t *buf,
533 : size_t buflen)
534 : {
535 28 : ssize_t ret = -1;
536 :
537 28 : switch (src->tag) {
538 28 : case IO_REPARSE_TAG_SYMLINK:
539 :
540 28 : ret = reparse_data_buffer_marshall_syml(&src->parsed.lnk,
541 : buf,
542 : buflen);
543 28 : break;
544 :
545 0 : case IO_REPARSE_TAG_NFS:
546 :
547 0 : ret = reparse_data_buffer_marshall_nfs(&src->parsed.nfs,
548 : buf,
549 : buflen);
550 0 : break;
551 :
552 0 : default: {
553 0 : struct iovec iov = {
554 0 : .iov_base = src->parsed.raw.data,
555 0 : .iov_len = src->parsed.raw.length,
556 : };
557 0 : ret = reparse_buffer_marshall(src->tag,
558 0 : src->parsed.raw.reserved,
559 : &iov,
560 : 1,
561 : buf,
562 : buflen);
563 : }
564 : }
565 :
566 28 : return ret;
567 : }
|