s3: torture: Add a comprehensive SMB1 DFS path torture tester.
[Samba.git] / source3 / torture / test_smb1_dfs.c
blob136acde5b9d4e8aa8e955891f4028f933f85fbf5
1 /*
2 Unix SMB/CIFS implementation.
3 SMB1 DFS tests.
4 Copyright (C) Jeremy Allison 2022.
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 3 of the License, or
9 (at your option) any later version.
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with this program. If not, see <http://www.gnu.org/licenses/>.
20 #include "includes.h"
21 #include "torture/proto.h"
22 #include "client.h"
23 #include "trans2.h"
24 #include "../libcli/smb/smbXcli_base.h"
25 #include "libcli/security/security.h"
26 #include "libsmb/proto.h"
27 #include "auth/credentials/credentials.h"
28 #include "auth/gensec/gensec.h"
29 #include "auth_generic.h"
30 #include "../librpc/ndr/libndr.h"
31 #include "libsmb/clirap.h"
32 #include "async_smb.h"
33 #include "../lib/util/tevent_ntstatus.h"
35 extern fstring host, workgroup, share, password, username, myname;
36 extern struct cli_credentials *torture_creds;
39 * Open an SMB1 file readonly and return the inode number.
41 static NTSTATUS get_smb1_inode(struct cli_state *cli,
42 const char *pathname,
43 uint64_t *ino_ret)
45 NTSTATUS status;
46 uint16_t fnum = 0;
47 SMB_INO_T ino = 0;
50 * Open the file.
53 status = smb1cli_ntcreatex(cli->conn,
54 cli->timeout,
55 cli->smb1.pid,
56 cli->smb1.tcon,
57 cli->smb1.session,
58 pathname,
59 OPLOCK_NONE, /* CreatFlags */
60 0, /* RootDirectoryFid */
61 SEC_STD_SYNCHRONIZE|
62 SEC_FILE_READ_DATA|
63 SEC_FILE_READ_ATTRIBUTE, /* DesiredAccess */
64 0, /* AllocationSize */
65 FILE_ATTRIBUTE_NORMAL, /* FileAttributes */
66 FILE_SHARE_READ|
67 FILE_SHARE_WRITE|
68 FILE_SHARE_DELETE, /* ShareAccess */
69 FILE_OPEN, /* CreateDisposition */
70 0, /* CreateOptions */
71 2, /* ImpersonationLevel */
72 0, /* SecurityFlags */
73 &fnum);
74 if (!NT_STATUS_IS_OK(status)) {
75 return status;
79 * Get the inode. Note - we can use
80 * a higher-level cli_XXX function here
81 * for SMB1 as cli_qfileinfo_basic()
82 * doesn't use any pathnames, only fnums
83 * so it isn't affected by DFS pathnames.
85 status = cli_qfileinfo_basic(cli,
86 fnum,
87 NULL, /* attr */
88 NULL, /* size */
89 NULL, /* create_time */
90 NULL, /* access_time */
91 NULL, /* write_time */
92 NULL, /* change_time */
93 &ino);
94 if (NT_STATUS_IS_OK(status)) {
95 *ino_ret = (uint64_t)ino;
98 (void)smb1cli_close(cli->conn,
99 cli->timeout,
100 cli->smb1.pid,
101 cli->smb1.tcon,
102 cli->smb1.session,
103 fnum,
104 0); /* last_modified */
105 return status;
109 * Check an inode matches a given SMB1 path.
111 static bool smb1_inode_matches(struct cli_state *cli,
112 const char *match_pathname,
113 uint64_t ino_tomatch,
114 const char *test_pathname)
116 uint64_t test_ino = 0;
117 NTSTATUS status;
119 status = get_smb1_inode(cli,
120 test_pathname,
121 &test_ino);
122 if (!NT_STATUS_IS_OK(status)) {
123 printf("%s: Failed to get ino "
124 "number for %s, (%s)\n",
125 __func__,
126 test_pathname,
127 nt_errstr(status));
128 return false;
130 if (test_ino != ino_tomatch) {
131 printf("%s: Inode missmatch, ino_tomatch (%s) "
132 "ino=%"PRIu64" test (%s) "
133 "ino=%"PRIu64"\n",
134 __func__,
135 match_pathname,
136 ino_tomatch,
137 test_pathname,
138 test_ino);
139 return false;
141 return true;
145 * Delete an SMB1 file on a DFS share.
147 static NTSTATUS smb1_dfs_delete(struct cli_state *cli,
148 const char *pathname)
150 NTSTATUS status;
151 uint16_t fnum = 0;
154 * Open the file.
157 status = smb1cli_ntcreatex(cli->conn,
158 cli->timeout,
159 cli->smb1.pid,
160 cli->smb1.tcon,
161 cli->smb1.session,
162 pathname,
163 OPLOCK_NONE, /* CreatFlags */
164 0, /* RootDirectoryFid */
165 SEC_STD_SYNCHRONIZE|
166 SEC_STD_DELETE, /* DesiredAccess */
167 0, /* AllocationSize */
168 FILE_ATTRIBUTE_NORMAL, /* FileAttributes */
169 FILE_SHARE_READ|
170 FILE_SHARE_WRITE|
171 FILE_SHARE_DELETE, /* ShareAccess */
172 FILE_OPEN, /* CreateDisposition */
173 0, /* CreateOptions */
174 2, /* ImpersonationLevel */
175 0, /* SecurityFlags */
176 &fnum);
177 if (!NT_STATUS_IS_OK(status)) {
178 return status;
182 * Set delete on close. Note - we can use
183 * a higher-level cli_XXX function here
184 * for SMB1 as cli_nt_delete_on_close()
185 * doesn't use any pathnames, only fnums
186 * so it isn't affected by DFS pathnames.
190 status = cli_nt_delete_on_close(cli, fnum, 1);
191 if (!NT_STATUS_IS_OK(status)) {
192 return status;
194 return smb1cli_close(cli->conn,
195 cli->timeout,
196 cli->smb1.pid,
197 cli->smb1.tcon,
198 cli->smb1.session,
199 fnum,
200 0); /* last_modified */
203 static void smb1_mv_done(struct tevent_req *subreq);
205 struct smb1_mv_state {
206 uint16_t vwv[1];
209 static struct tevent_req *smb1_mv_send(TALLOC_CTX *mem_ctx,
210 struct tevent_context *ev,
211 struct cli_state *cli,
212 const char *src_dfs_name,
213 const char *target_name)
215 uint8_t *bytes = NULL;
216 struct tevent_req *req = NULL;
217 struct tevent_req *subreq = NULL;
218 struct smb1_mv_state *state = NULL;
220 req = tevent_req_create(mem_ctx,
221 &state,
222 struct smb1_mv_state);
223 if (req == NULL) {
224 return NULL;
227 PUSH_LE_U16(state->vwv,
229 FILE_ATTRIBUTE_SYSTEM |
230 FILE_ATTRIBUTE_HIDDEN |
231 FILE_ATTRIBUTE_DIRECTORY);
233 bytes = talloc_array(state, uint8_t, 1);
234 if (tevent_req_nomem(bytes, req)) {
235 return tevent_req_post(req, ev);
237 bytes[0] = 4;
238 bytes = smb_bytes_push_str(bytes,
239 smbXcli_conn_use_unicode(cli->conn),
240 src_dfs_name,
241 strlen(src_dfs_name)+1,
242 NULL);
243 if (tevent_req_nomem(bytes, req)) {
244 return tevent_req_post(req, ev);
247 bytes = talloc_realloc(state,
248 bytes,
249 uint8_t,
250 talloc_get_size(bytes)+1);
251 if (tevent_req_nomem(bytes, req)) {
252 return tevent_req_post(req, ev);
255 bytes[talloc_get_size(bytes)-1] = 4;
256 bytes = smb_bytes_push_str(bytes,
257 smbXcli_conn_use_unicode(cli->conn),
258 target_name,
259 strlen(target_name)+1,
260 NULL);
261 if (tevent_req_nomem(bytes, req)) {
262 return tevent_req_post(req, ev);
265 subreq = cli_smb_send(state,
267 cli,
268 SMBmv,
269 0, /* additional_flags */
270 0, /* additional_flags2 */
272 state->vwv,
273 talloc_get_size(bytes),
274 bytes);
275 if (tevent_req_nomem(subreq, req)) {
276 return tevent_req_post(req, ev);
278 tevent_req_set_callback(subreq, smb1_mv_done, req);
279 return req;
282 static void smb1_mv_done(struct tevent_req *subreq)
284 NTSTATUS status = cli_smb_recv(subreq,
285 NULL,
286 NULL,
288 NULL,
289 NULL,
290 NULL,
291 NULL);
292 tevent_req_simple_finish_ntstatus(subreq,
293 status);
296 static NTSTATUS smb1_mv_recv(struct tevent_req *req)
298 return tevent_req_simple_recv_ntstatus(req);
302 * Rename an SMB1 file on a DFS share. SMBmv version.
304 static NTSTATUS smb1_mv(struct cli_state *cli,
305 const char *src_dfs_name,
306 const char *target_name)
308 TALLOC_CTX *frame = NULL;
309 struct tevent_context *ev;
310 struct tevent_req *req;
311 NTSTATUS status;
313 frame = talloc_stackframe();
315 ev = samba_tevent_context_init(frame);
316 if (ev == NULL) {
317 status = NT_STATUS_NO_MEMORY;
318 goto fail;
321 req = smb1_mv_send(frame,
323 cli,
324 src_dfs_name,
325 target_name);
326 if (req == NULL) {
327 status = NT_STATUS_NO_MEMORY;
328 goto fail;
331 if (!tevent_req_poll_ntstatus(req, ev, &status)) {
332 goto fail;
335 status = smb1_mv_recv(req);
337 fail:
339 TALLOC_FREE(frame);
340 return status;
343 static bool test_smb1_mv(struct cli_state *cli,
344 const char *src_dfs_name)
346 uint64_t test_ino = 0;
347 NTSTATUS status;
349 status = smb1_mv(cli,
350 src_dfs_name,
351 "BAD\\BAD\\renamed_file");
352 if (!NT_STATUS_IS_OK(status)) {
353 printf("%s:%d SMBmv of %s -> %s should succeed "
354 "got %s\n",
355 __FILE__,
356 __LINE__,
357 src_dfs_name,
358 "BAD\\BAD\\renamed_file",
359 nt_errstr(status));
360 return false;
363 /* Ensure we did rename. */
364 status = get_smb1_inode(cli,
365 "BAD\\BAD\\renamed_file",
366 &test_ino);
367 if (!NT_STATUS_IS_OK(status)) {
368 printf("%s:%d Failed to get ino "
369 "number for %s, (%s)\n",
370 __FILE__,
371 __LINE__,
372 "BAD\\BAD\\renamed_file",
373 nt_errstr(status));
374 return false;
377 /* Put it back. */
378 status = smb1_mv(cli,
379 "BAD\\BAD\\renamed_file",
380 src_dfs_name);
381 if (!NT_STATUS_IS_OK(status)) {
382 printf("%s:%d SMBmv of %s -> %s should succeed "
383 "got %s\n",
384 __FILE__,
385 __LINE__,
386 "BAD\\BAD\\renamed_file",
387 src_dfs_name,
388 nt_errstr(status));
389 return false;
392 /* Ensure we did put it back. */
393 status = get_smb1_inode(cli,
394 src_dfs_name,
395 &test_ino);
396 if (!NT_STATUS_IS_OK(status)) {
397 printf("%s:%d Failed to get ino "
398 "number for %s, (%s)\n",
399 __FILE__,
400 __LINE__,
401 src_dfs_name,
402 nt_errstr(status));
403 return false;
406 /* Try with a non-DFS name. */
407 status = smb1_mv(cli,
408 src_dfs_name,
409 "renamed_file");
410 if (!NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_PATH_SYNTAX_BAD)) {
411 /* Fails I think as target becomes "" on server. */
412 printf("%s:%d SMBmv of %s -> %s should get "
413 "NT_STATUS_OBJECT_PATH_SYNTAX_BAD got %s\n",
414 __FILE__,
415 __LINE__,
416 src_dfs_name,
417 "renamed_file",
418 nt_errstr(status));
419 return false;
422 /* Try with a non-DFS name. */
423 status = smb1_mv(cli,
424 src_dfs_name,
425 "BAD\\renamed_file");
426 if (!NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_PATH_SYNTAX_BAD)) {
427 /* Fails I think as target becomes "" on server. */
428 printf("%s:%d SMBmv of %s -> %s should get "
429 "NT_STATUS_OBJECT_PATH_SYNTAX_BAD got %s\n",
430 __FILE__,
431 __LINE__,
432 src_dfs_name,
433 "BAD\\renamed_file",
434 nt_errstr(status));
435 return false;
437 return true;
440 static void smb1_setpathinfo_done(struct tevent_req *subreq);
442 struct smb1_setpathinfo_state {
443 uint16_t setup;
444 uint8_t *param;
445 uint8_t *data;
448 static struct tevent_req *smb1_setpathinfo_send(TALLOC_CTX *mem_ctx,
449 struct tevent_context *ev,
450 struct cli_state *cli,
451 const char *src_dfs_name,
452 const char *target_name,
453 uint16_t info_level)
455 struct tevent_req *req = NULL;
456 struct tevent_req *subreq = NULL;
457 struct smb1_setpathinfo_state *state = NULL;
458 smb_ucs2_t *converted_str = NULL;
459 size_t converted_size_bytes = 0;
460 bool ok = false;
462 req = tevent_req_create(mem_ctx,
463 &state,
464 struct smb1_setpathinfo_state);
465 if (req == NULL) {
466 return NULL;
469 PUSH_LE_U16(&state->setup, 0, TRANSACT2_SETPATHINFO);
471 state->param = talloc_zero_array(state, uint8_t, 6);
472 if (tevent_req_nomem(state->param, req)) {
473 return tevent_req_post(req, ev);
475 PUSH_LE_U16(state->param, 0, info_level);
477 state->param = trans2_bytes_push_str(state->param,
478 smbXcli_conn_use_unicode(cli->conn),
479 src_dfs_name,
480 strlen(src_dfs_name)+1,
481 NULL);
482 if (tevent_req_nomem(state->param, req)) {
483 return tevent_req_post(req, ev);
486 ok = push_ucs2_talloc(state,
487 &converted_str,
488 target_name,
489 &converted_size_bytes);
490 if (!ok) {
491 tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
492 return tevent_req_post(req, ev);
496 * W2K8 insists the dest name is not null
497 * terminated. Remove the last 2 zero bytes
498 * and reduce the name length.
501 if (converted_size_bytes < 2) {
502 tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
503 return tevent_req_post(req, ev);
505 converted_size_bytes -= 2;
507 state->data = talloc_zero_array(state,
508 uint8_t,
509 12 + converted_size_bytes);
510 if (tevent_req_nomem(state->data, req)) {
511 return tevent_req_post(req, ev);
514 SIVAL(state->data, 8, converted_size_bytes);
515 memcpy(state->data + 12, converted_str, converted_size_bytes);
517 subreq = cli_trans_send(state, /* mem ctx. */
518 ev,/* event ctx. */
519 cli,/* cli_state. */
520 0,/* additional_flags2 */
521 SMBtrans2, /* cmd. */
522 NULL,/* pipe name. */
523 -1,/* fid. */
524 0,/* function. */
525 0,/* flags. */
526 &state->setup,/* setup. */
527 1,/* num setup uint16_t words. */
528 0,/* max returned setup. */
529 state->param,/* param. */
530 talloc_get_size(state->param),/* num param. */
531 2,/* max returned param. */
532 state->data,/* data. */
533 talloc_get_size(state->data),/* num data. */
534 0);/* max returned data. */
536 if (tevent_req_nomem(subreq, req)) {
537 return tevent_req_post(req, ev);
539 tevent_req_set_callback(subreq, smb1_setpathinfo_done, req);
540 return req;
543 static void smb1_setpathinfo_done(struct tevent_req *subreq)
545 NTSTATUS status = cli_trans_recv(subreq,
546 NULL,
547 NULL,
548 NULL,
550 NULL,
551 NULL,
553 NULL,
554 NULL,
556 NULL);
557 tevent_req_simple_finish_ntstatus(subreq,
558 status);
561 static NTSTATUS smb1_setpathinfo_recv(struct tevent_req *req)
563 return tevent_req_simple_recv_ntstatus(req);
567 * Rename or hardlink an SMB1 file on a DFS share. SMB1 setpathinfo
568 * (pathnames only) version.
570 static NTSTATUS smb1_setpathinfo(struct cli_state *cli,
571 const char *src_dfs_name,
572 const char *target_name,
573 uint16_t info_level)
575 TALLOC_CTX *frame = NULL;
576 struct tevent_context *ev;
577 struct tevent_req *req;
578 NTSTATUS status;
580 frame = talloc_stackframe();
582 ev = samba_tevent_context_init(frame);
583 if (ev == NULL) {
584 status = NT_STATUS_NO_MEMORY;
585 goto fail;
588 req = smb1_setpathinfo_send(frame,
590 cli,
591 src_dfs_name,
592 target_name,
593 info_level);
594 if (req == NULL) {
595 status = NT_STATUS_NO_MEMORY;
596 goto fail;
599 if (!tevent_req_poll_ntstatus(req, ev, &status)) {
600 goto fail;
603 status = smb1_setpathinfo_recv(req);
605 fail:
607 TALLOC_FREE(frame);
608 return status;
611 static NTSTATUS smb1_setpathinfo_rename(struct cli_state *cli,
612 const char *src_dfs_name,
613 const char *target_name)
615 return smb1_setpathinfo(cli,
616 src_dfs_name,
617 target_name,
618 SMB_FILE_RENAME_INFORMATION);
621 static bool test_smb1_setpathinfo_rename(struct cli_state *cli,
622 const char *src_dfs_name)
624 uint64_t test_ino = 0;
625 NTSTATUS status;
626 const char *putback_path = NULL;
629 * On Windows, setpathinfo rename where the target contains
630 * any directory separator returns STATUS_NOT_SUPPORTED.
632 * MS-SMB behavior note: <133> Section 3.3.5.10.6:
634 * "If the file name pointed to by the FileName parameter of the
635 * FILE_RENAME_INFORMATION structure contains a separator character,
636 * then the request fails with STATUS_NOT_SUPPORTED."
638 status = smb1_setpathinfo_rename(cli,
639 src_dfs_name,
640 "BAD\\BAD\\renamed_file");
641 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_SUPPORTED)) {
642 printf("%s:%d SMB1 setpathinfo rename of %s -> %s should get "
643 "NT_STATUS_NOT_SUPPORTED got %s\n",
644 __FILE__,
645 __LINE__,
646 src_dfs_name,
647 "BAD\\BAD\\renamed_file",
648 nt_errstr(status));
649 return false;
652 /* Try with a non-DFS name. */
653 status = smb1_setpathinfo_rename(cli,
654 src_dfs_name,
655 "renamed_file");
656 if (!NT_STATUS_IS_OK(status)) {
657 printf("%s:%d SMB1 setpathinfo rename of %s -> %s "
658 "should succeed got %s\n",
659 __FILE__,
660 __LINE__,
661 src_dfs_name,
662 "renamed_file",
663 nt_errstr(status));
664 return false;
667 /* Ensure we did rename. */
668 status = get_smb1_inode(cli,
669 "BAD\\BAD\\renamed_file",
670 &test_ino);
671 if (!NT_STATUS_IS_OK(status)) {
672 printf("%s:%d Failed to get ino "
673 "number for %s, (%s)\n",
674 __FILE__,
675 __LINE__,
676 "BAD\\BAD\\renamed_file",
677 nt_errstr(status));
678 return false;
682 * To put it back we need to reverse the DFS-ness of src
683 * and destination paths.
685 putback_path = strrchr(src_dfs_name, '\\');
686 if (putback_path == NULL) {
687 printf("%s:%d non DFS path %s passed. Internal error\n",
688 __FILE__,
689 __LINE__,
690 src_dfs_name);
691 return false;
693 /* Walk past the last '\\' */
694 putback_path++;
696 /* Put it back. */
697 status = smb1_setpathinfo_rename(cli,
698 "BAD\\BAD\\renamed_file",
699 putback_path);
700 if (!NT_STATUS_IS_OK(status)) {
701 printf("%s:%d SMB1 setpathinfo rename of %s -> %s "
702 "should succeed got %s\n",
703 __FILE__,
704 __LINE__,
705 "BAD\\BAD\\renamed_file",
706 putback_path,
707 nt_errstr(status));
708 return false;
711 /* Ensure we did rename. */
712 status = get_smb1_inode(cli,
713 src_dfs_name,
714 &test_ino);
715 if (!NT_STATUS_IS_OK(status)) {
716 printf("%s:%d Failed to get ino "
717 "number for %s, (%s)\n",
718 __FILE__,
719 __LINE__,
720 src_dfs_name,
721 nt_errstr(status));
722 return false;
725 return true;
728 static NTSTATUS smb1_setpathinfo_hardlink(struct cli_state *cli,
729 const char *src_dfs_name,
730 const char *target_name)
732 return smb1_setpathinfo(cli,
733 src_dfs_name,
734 target_name,
735 SMB_FILE_LINK_INFORMATION);
738 static bool test_smb1_setpathinfo_hardlink(struct cli_state *cli,
739 const char *src_dfs_name)
741 NTSTATUS status;
744 * On Windows, setpathinfo rename where the target contains
745 * any directory separator returns STATUS_NOT_SUPPORTED.
747 * MS-SMB behavior note: <133> Section 3.3.5.10.6:
749 * "If the file name pointed to by the FileName parameter of the
750 * FILE_RENAME_INFORMATION structure contains a separator character,
751 * then the request fails with STATUS_NOT_SUPPORTED."
753 * setpathinfo info level SMB_FILE_LINK_INFORMATION
754 * seems to do the same, but this could be an artifact
755 * of the Windows version tested (Win2K8). I will
756 * revisit this when I'm able to test against
757 * a later Windows version with a DFS server.
759 status = smb1_setpathinfo_hardlink(cli,
760 src_dfs_name,
761 "BAD\\BAD\\hlink");
762 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_SUPPORTED)) {
763 printf("%s:%d SMB1 setpathinfo hardlink of %s -> %s should get "
764 "NT_STATUS_NOT_SUPPORTED got %s\n",
765 __FILE__,
766 __LINE__,
767 src_dfs_name,
768 "BAD\\BAD\\hlink",
769 nt_errstr(status));
770 return false;
773 /* Try with a non-DFS name. */
775 * At least on Windows 2008 this also fails with
776 * NT_STATUS_NOT_SUPPORTED, leading me to believe
777 * setting hardlinks is only supported via NTrename
778 * in SMB1.
780 status = smb1_setpathinfo_hardlink(cli,
781 src_dfs_name,
782 "hlink");
783 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_SUPPORTED)) {
784 printf("%s:%d SMB1 setpathinfo hardlink of %s -> %s should get "
785 "NT_STATUS_NOT_SUPPORTED got %s\n",
786 __FILE__,
787 __LINE__,
788 src_dfs_name,
789 "hlink",
790 nt_errstr(status));
791 return false;
793 return true;
796 static void smb1_ntrename_done(struct tevent_req *subreq);
798 struct smb1_ntrename_state {
799 uint16_t vwv[4];
802 static struct tevent_req *smb1_ntrename_send(TALLOC_CTX *mem_ctx,
803 struct tevent_context *ev,
804 struct cli_state *cli,
805 const char *src_dfs_name,
806 const char *target_name,
807 uint16_t rename_flag)
809 struct tevent_req *req = NULL;
810 struct tevent_req *subreq = NULL;
811 struct smb1_ntrename_state *state = NULL;
812 uint8_t *bytes = NULL;
814 req = tevent_req_create(mem_ctx,
815 &state,
816 struct smb1_ntrename_state);
817 if (req == NULL) {
818 return NULL;
821 PUSH_LE_U16(state->vwv,
823 FILE_ATTRIBUTE_SYSTEM |
824 FILE_ATTRIBUTE_HIDDEN |
825 FILE_ATTRIBUTE_DIRECTORY);
826 PUSH_LE_U16(state->vwv, 2, rename_flag);
828 bytes = talloc_array(state, uint8_t, 1);
829 if (tevent_req_nomem(bytes, req)) {
830 return tevent_req_post(req, ev);
833 bytes[0] = 4;
834 bytes = smb_bytes_push_str(bytes,
835 smbXcli_conn_use_unicode(cli->conn),
836 src_dfs_name,
837 strlen(src_dfs_name)+1,
838 NULL);
839 if (tevent_req_nomem(bytes, req)) {
840 return tevent_req_post(req, ev);
842 bytes = talloc_realloc(state,
843 bytes,
844 uint8_t,
845 talloc_get_size(bytes)+1);
846 if (tevent_req_nomem(bytes, req)) {
847 return tevent_req_post(req, ev);
850 bytes[talloc_get_size(bytes)-1] = 4;
851 bytes = smb_bytes_push_str(bytes,
852 smbXcli_conn_use_unicode(cli->conn),
853 target_name,
854 strlen(target_name)+1,
855 NULL);
856 if (tevent_req_nomem(bytes, req)) {
857 return tevent_req_post(req, ev);
860 subreq = cli_smb_send(state,
862 cli,
863 SMBntrename,
864 0, /* additional_flags */
865 0, /* additional_flags2 */
867 state->vwv,
868 talloc_get_size(bytes),
869 bytes);
870 if (tevent_req_nomem(subreq, req)) {
871 return tevent_req_post(req, ev);
873 tevent_req_set_callback(subreq, smb1_ntrename_done, req);
874 return req;
877 static void smb1_ntrename_done(struct tevent_req *subreq)
879 NTSTATUS status = cli_smb_recv(subreq,
880 NULL,
881 NULL,
883 NULL,
884 NULL,
885 NULL,
886 NULL);
887 tevent_req_simple_finish_ntstatus(subreq, status);
890 static NTSTATUS smb1_ntrename_recv(struct tevent_req *req)
892 return tevent_req_simple_recv_ntstatus(req);
896 * Rename or hardlink an SMB1 file on a DFS share. SMB1 ntrename version.
897 * (pathnames only).
899 static NTSTATUS smb1_ntrename(struct cli_state *cli,
900 const char *src_dfs_name,
901 const char *target_name,
902 uint16_t rename_flag)
904 TALLOC_CTX *frame = NULL;
905 struct tevent_context *ev;
906 struct tevent_req *req;
907 NTSTATUS status;
909 frame = talloc_stackframe();
911 ev = samba_tevent_context_init(frame);
912 if (ev == NULL) {
913 status = NT_STATUS_NO_MEMORY;
914 goto fail;
917 req = smb1_ntrename_send(frame,
919 cli,
920 src_dfs_name,
921 target_name,
922 rename_flag);
923 if (req == NULL) {
924 status = NT_STATUS_NO_MEMORY;
925 goto fail;
928 if (!tevent_req_poll_ntstatus(req, ev, &status)) {
929 goto fail;
932 status = smb1_ntrename_recv(req);
934 fail:
936 TALLOC_FREE(frame);
937 return status;
940 * Rename an SMB1 file on a DFS share. SMB1 ntrename version.
942 static NTSTATUS smb1_ntrename_rename(struct cli_state *cli,
943 const char *src_dfs_name,
944 const char *target_name)
946 return smb1_ntrename(cli,
947 src_dfs_name,
948 target_name,
949 RENAME_FLAG_RENAME);
953 static bool test_smb1_ntrename_rename(struct cli_state *cli,
954 const char *src_dfs_name)
956 uint64_t test_ino = 0;
957 NTSTATUS status;
959 /* Try with a non-DFS name. */
960 status = smb1_ntrename_rename(cli,
961 src_dfs_name,
962 "renamed_file");
963 if (!NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_PATH_SYNTAX_BAD)) {
964 /* Fails I think as target becomes "" on server. */
965 printf("%s:%d SMB1 ntrename rename of %s -> %s should get "
966 "NT_STATUS_OBJECT_PATH_SYNTAX_BAD got %s\n",
967 __FILE__,
968 __LINE__,
969 src_dfs_name,
970 "renamed_file",
971 nt_errstr(status));
972 return false;
975 status = smb1_ntrename_rename(cli,
976 src_dfs_name,
977 "BAD\\BAD\\renamed_file");
978 if (!NT_STATUS_IS_OK(status)) {
979 printf("%s:%d SMB1 ntrename rename of %s -> %s should "
980 "succeed got %s\n",
981 __FILE__,
982 __LINE__,
983 src_dfs_name,
984 "BAD\\BAD\\renamed_file",
985 nt_errstr(status));
986 return false;
989 /* Ensure we did rename. */
990 status = get_smb1_inode(cli,
991 "BAD\\BAD\\renamed_file",
992 &test_ino);
993 if (!NT_STATUS_IS_OK(status)) {
994 printf("%s:%d Failed to get ino "
995 "number for %s, (%s)\n",
996 __FILE__,
997 __LINE__,
998 "BAD\\BAD\\renamed_file",
999 nt_errstr(status));
1000 return false;
1003 /* Put it back. */
1004 status = smb1_ntrename_rename(cli,
1005 "BAD\\BAD\\renamed_file",
1006 src_dfs_name);
1007 if (!NT_STATUS_IS_OK(status)) {
1008 printf("%s:%d SMB1 ntrename rename of %s -> %s "
1009 "should succeed got %s\n",
1010 __FILE__,
1011 __LINE__,
1012 "BAD\\BAD\\renamed_file",
1013 src_dfs_name,
1014 nt_errstr(status));
1015 return false;
1018 /* Ensure we did rename. */
1019 status = get_smb1_inode(cli,
1020 src_dfs_name,
1021 &test_ino);
1022 if (!NT_STATUS_IS_OK(status)) {
1023 printf("%s:%d Failed to get ino "
1024 "number for %s, (%s)\n",
1025 __FILE__,
1026 __LINE__,
1027 src_dfs_name,
1028 nt_errstr(status));
1029 return false;
1032 return true;
1036 * Hard link an SMB1 file on a DFS share. SMB1 ntrename version.
1038 static NTSTATUS smb1_ntrename_hardlink(struct cli_state *cli,
1039 const char *src_dfs_name,
1040 const char *target_name)
1042 return smb1_ntrename(cli,
1043 src_dfs_name,
1044 target_name,
1045 RENAME_FLAG_HARD_LINK);
1048 static bool test_smb1_ntrename_hardlink(struct cli_state *cli,
1049 const char *src_dfs_name)
1051 uint64_t test_ino = 0;
1052 NTSTATUS status;
1053 bool retval = false;
1055 /* Try with a non-DFS name. */
1056 status = smb1_ntrename_hardlink(cli,
1057 src_dfs_name,
1058 "hlink");
1059 if (!NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_PATH_SYNTAX_BAD)) {
1060 /* Fails I think as target becomes "" on server. */
1061 printf("%s:%d SMB1 ntrename of %s -> %s should get "
1062 "NT_STATUS_OBJECT_PATH_SYNTAX_BAD got %s\n",
1063 __FILE__,
1064 __LINE__,
1065 src_dfs_name,
1066 "hlink",
1067 nt_errstr(status));
1068 return false;
1071 status = smb1_ntrename_hardlink(cli,
1072 src_dfs_name,
1073 "BAD\\BAD\\hlink");
1074 if (!NT_STATUS_IS_OK(status)) {
1075 printf("%s:%d SMB1 ntrename hardlink of %s -> %s "
1076 "should succeed got %s\n",
1077 __FILE__,
1078 __LINE__,
1079 src_dfs_name,
1080 "BAD\\BAD\\hlink",
1081 nt_errstr(status));
1082 goto out;
1085 /* Ensure we did hardlink. */
1086 status = get_smb1_inode(cli,
1087 "BAD\\BAD\\hlink",
1088 &test_ino);
1089 if (!NT_STATUS_IS_OK(status)) {
1090 printf("%s:%d Failed to get ino "
1091 "number for %s, (%s)\n",
1092 __FILE__,
1093 __LINE__,
1094 "BAD\\BAD\\hlink",
1095 nt_errstr(status));
1096 goto out;
1099 retval = smb1_inode_matches(cli,
1100 "BAD\\BAD\\hlink",
1101 test_ino,
1102 src_dfs_name);
1103 if (!retval) {
1104 printf("%s:%d smb1_inode_matches failed for "
1105 "%s %s\n",
1106 __FILE__,
1107 __LINE__,
1108 src_dfs_name,
1109 "BAD\\BAD\\hlink");
1110 goto out;
1113 out:
1115 /* Remove the hardlink to clean up. */
1116 (void)smb1_dfs_delete(cli, "BAD\\BAD\\hlink");
1117 return retval;
1120 static void smb1_setfileinfo_done(struct tevent_req *subreq);
1122 struct smb1_setfileinfo_state {
1123 uint16_t setup;
1124 uint8_t param[6];
1125 uint8_t *data;
1128 static struct tevent_req *smb1_setfileinfo_send(TALLOC_CTX *mem_ctx,
1129 struct tevent_context *ev,
1130 struct cli_state *cli,
1131 uint16_t fnum,
1132 const char *target_name,
1133 uint16_t info_level)
1135 struct tevent_req *req = NULL;
1136 struct tevent_req *subreq = NULL;
1137 struct smb1_setfileinfo_state *state = NULL;
1138 smb_ucs2_t *converted_str = NULL;
1139 size_t converted_size_bytes = 0;
1140 bool ok = false;
1142 req = tevent_req_create(mem_ctx,
1143 &state,
1144 struct smb1_setfileinfo_state);
1145 if (req == NULL) {
1146 return NULL;
1149 PUSH_LE_U16(&state->setup, 0, TRANSACT2_SETPATHINFO);
1151 PUSH_LE_U16(state->param, 0, fnum);
1152 PUSH_LE_U16(state->param, 2, info_level);
1154 ok = push_ucs2_talloc(state,
1155 &converted_str,
1156 target_name,
1157 &converted_size_bytes);
1158 if (!ok) {
1159 tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
1160 return tevent_req_post(req, ev);
1164 * W2K8 insists the dest name is not null
1165 * terminated. Remove the last 2 zero bytes
1166 * and reduce the name length.
1169 if (converted_size_bytes < 2) {
1170 tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
1171 return tevent_req_post(req, ev);
1173 converted_size_bytes -= 2;
1175 state->data = talloc_zero_array(state,
1176 uint8_t,
1177 12 + converted_size_bytes);
1178 if (tevent_req_nomem(state->data, req)) {
1179 return tevent_req_post(req, ev);
1182 SIVAL(state->data, 8, converted_size_bytes);
1183 memcpy(state->data + 12, converted_str, converted_size_bytes);
1185 subreq = cli_trans_send(state, /* mem ctx. */
1186 ev,/* event ctx. */
1187 cli,/* cli_state. */
1188 0,/* additional_flags2 */
1189 SMBtrans2, /* cmd. */
1190 NULL,/* pipe name. */
1191 -1,/* fid. */
1192 0,/* function. */
1193 0,/* flags. */
1194 &state->setup,/* setup. */
1195 1,/* num setup uint16_t words. */
1196 0,/* max returned setup. */
1197 state->param,/* param. */
1198 6,/* num param. */
1199 2,/* max returned param. */
1200 state->data,/* data. */
1201 talloc_get_size(state->data),/* num data. */
1202 0);/* max returned data. */
1204 if (tevent_req_nomem(subreq, req)) {
1205 return tevent_req_post(req, ev);
1207 tevent_req_set_callback(subreq, smb1_setfileinfo_done, req);
1208 return req;
1211 static void smb1_setfileinfo_done(struct tevent_req *subreq)
1213 NTSTATUS status = cli_trans_recv(subreq,
1214 NULL,
1215 NULL,
1216 NULL,
1218 NULL,
1219 NULL,
1221 NULL,
1222 NULL,
1224 NULL);
1225 tevent_req_simple_finish_ntstatus(subreq,
1226 status);
1229 static NTSTATUS smb1_setfileinfo_recv(struct tevent_req *req)
1231 return tevent_req_simple_recv_ntstatus(req);
1235 * Rename or hardlink an SMB1 file on a DFS share.
1236 * setfileinfo (file handle + target pathname) version.
1238 static NTSTATUS smb1_setfileinfo(struct cli_state *cli,
1239 uint16_t fnum,
1240 const char *target_name,
1241 uint16_t info_level)
1243 TALLOC_CTX *frame = NULL;
1244 struct tevent_context *ev;
1245 struct tevent_req *req;
1246 NTSTATUS status;
1248 frame = talloc_stackframe();
1250 ev = samba_tevent_context_init(frame);
1251 if (ev == NULL) {
1252 status = NT_STATUS_NO_MEMORY;
1253 goto fail;
1256 req = smb1_setfileinfo_send(frame,
1258 cli,
1259 fnum,
1260 target_name,
1261 info_level);
1262 if (req == NULL) {
1263 status = NT_STATUS_NO_MEMORY;
1264 goto fail;
1267 if (!tevent_req_poll_ntstatus(req, ev, &status)) {
1268 goto fail;
1271 status = smb1_setfileinfo_recv(req);
1273 fail:
1275 TALLOC_FREE(frame);
1276 return status;
1279 static NTSTATUS smb1_setfileinfo_rename(struct cli_state *cli,
1280 uint16_t fnum,
1281 const char *target_name)
1283 return smb1_setfileinfo(cli,
1284 fnum,
1285 target_name,
1286 SMB_FILE_RENAME_INFORMATION);
1290 * On Windows, rename using a file handle as source
1291 * is not supported.
1294 static bool test_smb1_setfileinfo_rename(struct cli_state *cli,
1295 const char *src_dfs_name)
1297 uint16_t fnum = (uint16_t)-1;
1298 NTSTATUS status;
1299 bool retval = false;
1301 /* First open the source file. */
1302 status = smb1cli_ntcreatex(cli->conn,
1303 cli->timeout,
1304 cli->smb1.pid,
1305 cli->smb1.tcon,
1306 cli->smb1.session,
1307 src_dfs_name,
1308 OPLOCK_NONE, /* CreatFlags */
1309 0, /* RootDirectoryFid */
1310 SEC_STD_SYNCHRONIZE|
1311 SEC_STD_DELETE, /* DesiredAccess */
1312 0, /* AllocationSize */
1313 FILE_ATTRIBUTE_NORMAL, /* FileAttributes */
1314 FILE_SHARE_READ|
1315 FILE_SHARE_WRITE|
1316 FILE_SHARE_DELETE, /* ShareAccess */
1317 FILE_OPEN, /* CreateDisposition */
1318 0, /* CreateOptions */
1319 2, /* ImpersonationLevel */
1320 0, /* SecurityFlags */
1321 &fnum);
1322 if (!NT_STATUS_IS_OK(status)) {
1323 printf("%s:%d failed to open %s, %s\n",
1324 __FILE__,
1325 __LINE__,
1326 src_dfs_name,
1327 nt_errstr(status));
1328 goto out;
1332 * On Windows rename given a file handle returns
1333 * NT_STATUS_UNSUCCESSFUL (not documented in MS-SMB).
1336 status = smb1_setfileinfo_rename(cli,
1337 fnum,
1338 "BAD\\BAD\\renamed_file");
1339 if (!NT_STATUS_EQUAL(status, NT_STATUS_UNSUCCESSFUL)) {
1340 printf("%s:%d SMB1 setfileinfo rename of %s -> %s should get "
1341 "NT_STATUS_UNSUCCESSFUL got %s\n",
1342 __FILE__,
1343 __LINE__,
1344 src_dfs_name,
1345 "BAD\\BAD\\hlink",
1346 nt_errstr(status));
1347 goto out;
1350 /* Try with a non-DFS name - still gets NT_STATUS_UNSUCCESSFUL. */
1351 status = smb1_setfileinfo_rename(cli,
1352 fnum,
1353 "renamed_file");
1354 if (!NT_STATUS_EQUAL(status, NT_STATUS_UNSUCCESSFUL)) {
1355 printf("%s:%d SMB1 setfileinfo rename of %s -> %s should get "
1356 "NT_STATUS_UNSUCCESSFUL got %s\n",
1357 __FILE__,
1358 __LINE__,
1359 src_dfs_name,
1360 "hlink",
1361 nt_errstr(status));
1362 goto out;
1365 retval = true;
1367 out:
1369 if (fnum != (uint16_t)-1) {
1370 (void)smb1cli_close(cli->conn,
1371 cli->timeout,
1372 cli->smb1.pid,
1373 cli->smb1.tcon,
1374 cli->smb1.session,
1375 fnum,
1376 0); /* last_modified */
1379 (void)smb1_dfs_delete(cli, "BAD\\BAD\\renamed_file");
1380 return retval;
1384 static NTSTATUS smb1_setfileinfo_hardlink(struct cli_state *cli,
1385 uint16_t fnum,
1386 const char *target_name)
1388 return smb1_setfileinfo(cli,
1389 fnum,
1390 target_name,
1391 SMB_FILE_LINK_INFORMATION);
1395 * On Windows, hardlink using a file handle as source
1396 * is not supported.
1399 static bool test_smb1_setfileinfo_hardlink(struct cli_state *cli,
1400 const char *src_dfs_name)
1402 uint16_t fnum = (uint16_t)-1;
1403 NTSTATUS status;
1404 bool retval = false;
1406 /* First open the source file. */
1407 status = smb1cli_ntcreatex(cli->conn,
1408 cli->timeout,
1409 cli->smb1.pid,
1410 cli->smb1.tcon,
1411 cli->smb1.session,
1412 src_dfs_name,
1413 OPLOCK_NONE, /* CreatFlags */
1414 0, /* RootDirectoryFid */
1415 SEC_STD_SYNCHRONIZE|
1416 SEC_RIGHTS_FILE_READ, /* DesiredAccess */
1417 0, /* AllocationSize */
1418 FILE_ATTRIBUTE_NORMAL, /* FileAttributes */
1419 FILE_SHARE_READ|
1420 FILE_SHARE_WRITE|
1421 FILE_SHARE_DELETE, /* ShareAccess */
1422 FILE_OPEN, /* CreateDisposition */
1423 0, /* CreateOptions */
1424 2, /* ImpersonationLevel */
1425 0, /* SecurityFlags */
1426 &fnum);
1427 if (!NT_STATUS_IS_OK(status)) {
1428 printf("%s:%d failed to open %s, %s\n",
1429 __FILE__,
1430 __LINE__,
1431 src_dfs_name,
1432 nt_errstr(status));
1433 goto out;
1437 * On Windows hardlink given a file handle returns
1438 * NT_STATUS_UNSUCCESSFUL (not documented in MS-SMB).
1441 status = smb1_setfileinfo_hardlink(cli,
1442 fnum,
1443 "BAD\\BAD\\hlink");
1444 if (!NT_STATUS_EQUAL(status, NT_STATUS_UNSUCCESSFUL)) {
1445 printf("%s:%d SMB1 setfileinfo hardlink of %s -> %s should get "
1446 "NT_STATUS_UNSUCCESSFUL got %s\n",
1447 __FILE__,
1448 __LINE__,
1449 src_dfs_name,
1450 "BAD\\BAD\\hlink",
1451 nt_errstr(status));
1452 goto out;
1455 /* Try with a non-DFS name - still gets NT_STATUS_UNSUCCESSFUL. */
1456 status = smb1_setfileinfo_hardlink(cli,
1457 fnum,
1458 "hlink");
1459 if (!NT_STATUS_EQUAL(status, NT_STATUS_UNSUCCESSFUL)) {
1460 printf("%s:%d SMB1 setfileinfo hardlink of %s -> %s should get "
1461 "NT_STATUS_UNSUCCESSFUL got %s\n",
1462 __FILE__,
1463 __LINE__,
1464 src_dfs_name,
1465 "hlink",
1466 nt_errstr(status));
1467 goto out;
1470 retval = true;
1472 out:
1474 if (fnum != (uint16_t)-1) {
1475 (void)smb1cli_close(cli->conn,
1476 cli->timeout,
1477 cli->smb1.pid,
1478 cli->smb1.tcon,
1479 cli->smb1.session,
1480 fnum,
1481 0); /* last_modified */
1484 (void)smb1_dfs_delete(cli, "BAD\\BAD\\hlink");
1485 return retval;
1489 * According to:
1491 * https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-fscc/dc9978d7-6299-4c5a-a22d-a039cdc716ea
1493 * (Characters " \ / [ ] : | < > + = ; , * ?,
1494 * and control characters in range 0x00 through
1495 * 0x1F, inclusive, are illegal in a share name)
1497 * But Windows server only checks in DFS sharenames ':'. All other
1498 * share names are allowed.
1501 static bool test_smb1_dfs_sharenames(struct cli_state *cli,
1502 const char *dfs_root_share_name,
1503 uint64_t root_ino)
1505 char test_path[20];
1506 const char *test_str = "/[]:|<>+=;,*?";
1507 const char *p;
1508 unsigned int i;
1509 bool ino_matched = false;
1511 /* Setup template pathname. */
1512 memcpy(test_path, "\\SERVER\\X", 10);
1514 /* Test invalid control characters. */
1515 for (i = 1; i < 0x20; i++) {
1516 test_path[8] = i;
1517 ino_matched = smb1_inode_matches(cli,
1518 dfs_root_share_name,
1519 root_ino,
1520 test_path);
1521 if (!ino_matched) {
1522 return false;
1526 /* Test explicit invalid characters. */
1527 for (p = test_str; *p != '\0'; p++) {
1528 test_path[8] = *p;
1529 if (*p == ':') {
1531 * Only ':' is treated as an INVALID sharename
1532 * for a DFS SERVER\\SHARE path.
1534 uint64_t test_ino = 0;
1535 NTSTATUS status = get_smb1_inode(cli,
1536 test_path,
1537 &test_ino);
1538 if (!NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_INVALID)) {
1539 printf("%s:%d Open of %s should get "
1540 "NT_STATUS_OBJECT_NAME_INVALID, got %s\n",
1541 __FILE__,
1542 __LINE__,
1543 test_path,
1544 nt_errstr(status));
1545 return false;
1547 } else {
1548 ino_matched = smb1_inode_matches(cli,
1549 dfs_root_share_name,
1550 root_ino,
1551 test_path);
1552 if (!ino_matched) {
1553 return false;
1557 return true;
1561 * "Raw" test of SMB1 paths to a DFS share.
1562 * We must (mostly) use the lower level smb1cli_XXXX() interfaces,
1563 * not the cli_XXX() ones here as the ultimate goal is to fix our
1564 * cli_XXX() interfaces to work transparently over DFS.
1566 * So here, we're testing the server code, not the client code.
1568 * Passes cleanly against Windows.
1571 bool run_smb1_dfs_paths(int dummy)
1573 struct cli_state *cli = NULL;
1574 NTSTATUS status;
1575 bool dfs_supported = false;
1576 char *dfs_root_share_name = NULL;
1577 uint64_t root_ino = 0;
1578 uint64_t test_ino = 0;
1579 bool ino_matched = false;
1580 bool retval = false;
1581 bool ok = false;
1582 unsigned int i;
1583 uint16_t fnum = (uint16_t)-1;
1585 printf("Starting SMB1-DFS-PATHS\n");
1587 if (!torture_init_connection(&cli)) {
1588 return false;
1591 if (!torture_open_connection(&cli, 0)) {
1592 return false;
1595 /* Ensure this is a DFS share. */
1596 dfs_supported = smbXcli_conn_dfs_supported(cli->conn);
1597 if (!dfs_supported) {
1598 printf("Server %s does not support DFS\n",
1599 smbXcli_conn_remote_name(cli->conn));
1600 return false;
1602 dfs_supported = smbXcli_tcon_is_dfs_share(cli->smb1.tcon);
1603 if (!dfs_supported) {
1604 printf("Share %s does not support DFS\n",
1605 cli->share);
1606 return false;
1609 /* Start with an empty share. */
1610 (void)smb1_dfs_delete(cli, "BAD\\BAD\\BAD");
1611 (void)smb1_dfs_delete(cli, "BAD\\BAD\\file");
1612 (void)smb1_dfs_delete(cli, "BAD\\BAD\\renamed_file");
1613 (void)smb1_dfs_delete(cli, "BAD\\BAD\\hlink");
1616 * Create the "official" DFS share root name.
1618 dfs_root_share_name = talloc_asprintf(talloc_tos(),
1619 "\\%s\\%s",
1620 smbXcli_conn_remote_name(cli->conn),
1621 cli->share);
1622 if (dfs_root_share_name == NULL) {
1623 printf("Out of memory\n");
1624 return false;
1627 /* Get the share root inode number. */
1628 status = get_smb1_inode(cli,
1629 dfs_root_share_name,
1630 &root_ino);
1631 if (!NT_STATUS_IS_OK(status)) {
1632 printf("%s:%d Failed to get ino number for share root %s, (%s)\n",
1633 __FILE__,
1634 __LINE__,
1635 dfs_root_share_name,
1636 nt_errstr(status));
1637 return false;
1641 * Test the Windows algorithm for parsing DFS names.
1644 * A single "SERVER" element should open and match the share root.
1646 ino_matched = smb1_inode_matches(cli,
1647 dfs_root_share_name,
1648 root_ino,
1649 smbXcli_conn_remote_name(cli->conn));
1650 if (!ino_matched) {
1651 printf("%s:%d Failed to match ino number for %s\n",
1652 __FILE__,
1653 __LINE__,
1654 smbXcli_conn_remote_name(cli->conn));
1655 return false;
1658 /* An "" (empty) server name should open and match the share root. */
1659 ino_matched = smb1_inode_matches(cli,
1660 dfs_root_share_name,
1661 root_ino,
1662 "");
1663 if (!ino_matched) {
1664 printf("%s:%d Failed to match ino number for %s\n",
1665 __FILE__,
1666 __LINE__,
1667 "");
1668 return false;
1672 * For SMB1 the server just strips off any number of leading '\\'
1673 * characters. Show this is the case.
1675 for (i = 0; i < 10; i++) {
1676 char leading_backslash_name[20];
1677 leading_backslash_name[i] = '\\';
1678 memcpy(&leading_backslash_name[i+1],
1679 "SERVER",
1680 strlen("SERVER")+1);
1682 ino_matched = smb1_inode_matches(cli,
1683 dfs_root_share_name,
1684 root_ino,
1685 leading_backslash_name);
1686 if (!ino_matched) {
1687 printf("%s:%d Failed to match ino number for %s\n",
1688 __FILE__,
1689 __LINE__,
1690 leading_backslash_name);
1691 return false;
1695 /* A "BAD" server name should open and match the share root. */
1696 ino_matched = smb1_inode_matches(cli,
1697 dfs_root_share_name,
1698 root_ino,
1699 "BAD");
1700 if (!ino_matched) {
1701 printf("%s:%d Failed to match ino number for %s\n",
1702 __FILE__,
1703 __LINE__,
1704 "BAD");
1705 return false;
1708 * A "BAD\\BAD" server and share name should open
1709 * and match the share root.
1711 ino_matched = smb1_inode_matches(cli,
1712 dfs_root_share_name,
1713 root_ino,
1714 "BAD\\BAD");
1715 if (!ino_matched) {
1716 printf("%s:%d Failed to match ino number for %s\n",
1717 __FILE__,
1718 __LINE__,
1719 "BAD\\BAD");
1720 return false;
1723 * Trying to open "BAD\\BAD\\BAD" should get
1724 * NT_STATUS_OBJECT_NAME_NOT_FOUND.
1726 status = get_smb1_inode(cli,
1727 "BAD\\BAD\\BAD",
1728 &test_ino);
1729 if (!NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
1730 printf("%s:%d Open of %s should get "
1731 "STATUS_OBJECT_NAME_NOT_FOUND, got %s\n",
1732 __FILE__,
1733 __LINE__,
1734 "BAD\\BAD\\BAD",
1735 nt_errstr(status));
1736 return false;
1739 * Trying to open "BAD\\BAD\\BAD\\BAD" should get
1740 * NT_STATUS_OBJECT_PATH_NOT_FOUND.
1742 status = get_smb1_inode(cli,
1743 "BAD\\BAD\\BAD\\BAD",
1744 &test_ino);
1745 if (!NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_PATH_NOT_FOUND)) {
1746 printf("%s:%d Open of %s should get "
1747 "STATUS_OBJECT_NAME_NOT_FOUND, got %s\n",
1748 __FILE__,
1749 __LINE__,
1750 "BAD\\BAD\\BAD\\BAD",
1751 nt_errstr(status));
1752 return false;
1755 * Test for invalid pathname characters in the servername.
1756 * They are ignored, and it still opens the share root.
1758 ino_matched = smb1_inode_matches(cli,
1759 dfs_root_share_name,
1760 root_ino,
1761 "::::");
1762 if (!ino_matched) {
1763 printf("%s:%d Failed to match ino number for %s\n",
1764 __FILE__,
1765 __LINE__,
1766 "::::");
1767 return false;
1771 * Test for invalid pathname characters in the sharename.
1772 * Invalid sharename characters should still be flagged as
1773 * NT_STATUS_OBJECT_NAME_INVALID. It turns out only ':'
1774 * is considered an invalid sharename character.
1776 ok = test_smb1_dfs_sharenames(cli,
1777 dfs_root_share_name,
1778 root_ino);
1779 if (!ok) {
1780 return false;
1783 status = smb1cli_ntcreatex(cli->conn,
1784 cli->timeout,
1785 cli->smb1.pid,
1786 cli->smb1.tcon,
1787 cli->smb1.session,
1788 "BAD\\BAD\\file",
1789 OPLOCK_NONE, /* CreatFlags */
1790 0, /* RootDirectoryFid */
1791 SEC_STD_SYNCHRONIZE|
1792 SEC_STD_DELETE |
1793 SEC_FILE_READ_DATA|
1794 SEC_FILE_READ_ATTRIBUTE, /* DesiredAccess */
1795 0, /* AllocationSize */
1796 FILE_ATTRIBUTE_NORMAL, /* FileAttributes */
1797 FILE_SHARE_READ|
1798 FILE_SHARE_WRITE|
1799 FILE_SHARE_DELETE, /* ShareAccess */
1800 FILE_CREATE, /* CreateDisposition */
1801 0, /* CreateOptions */
1802 2, /* ImpersonationLevel */
1803 0, /* SecurityFlags */
1804 &fnum);
1805 if (!NT_STATUS_IS_OK(status)) {
1806 printf("%s:%d smb1cli_ntcreatex on %s returned %s\n",
1807 __FILE__,
1808 __LINE__,
1809 "BAD\\BAD\\file",
1810 nt_errstr(status));
1811 return false;
1814 /* Close "file" handle. */
1815 (void)smb1cli_close(cli->conn,
1816 cli->timeout,
1817 cli->smb1.pid,
1818 cli->smb1.tcon,
1819 cli->smb1.session,
1820 fnum,
1821 0); /* last_modified */
1822 fnum = (uint16_t)-1;
1825 * Trying to open "BAD\\BAD\\file" should now get
1826 * a valid inode.
1828 status = get_smb1_inode(cli,
1829 "BAD\\BAD\\file",
1830 &test_ino);
1831 if (!NT_STATUS_IS_OK(status)) {
1832 printf("%s:%d Open of %s should succeed "
1833 "got %s\n",
1834 __FILE__,
1835 __LINE__,
1836 "BAD\\BAD\\file",
1837 nt_errstr(status));
1838 goto err;
1842 * Test different SMB1 renames
1843 * and hard links.
1846 /* SMBmv only does rename. */
1847 ok = test_smb1_mv(cli,
1848 "BAD\\BAD\\file");
1849 if (!ok) {
1850 goto err;
1853 ok = test_smb1_setpathinfo_rename(cli,
1854 "BAD\\BAD\\file");
1855 if (!ok) {
1856 goto err;
1859 ok = test_smb1_setpathinfo_hardlink(cli,
1860 "BAD\\BAD\\file");
1861 if (!ok) {
1862 goto err;
1865 ok = test_smb1_setfileinfo_rename(cli,
1866 "BAD\\BAD\\file");
1867 if (!ok) {
1868 goto err;
1871 ok = test_smb1_setfileinfo_hardlink(cli,
1872 "BAD\\BAD\\file");
1873 if (!ok) {
1874 goto err;
1877 ok = test_smb1_ntrename_rename(cli,
1878 "BAD\\BAD\\file");
1879 if (!ok) {
1880 goto err;
1883 ok = test_smb1_ntrename_hardlink(cli,
1884 "BAD\\BAD\\file");
1885 if (!ok) {
1886 goto err;
1889 retval = true;
1891 err:
1893 if (fnum != (uint16_t)-1) {
1894 (void)smb1cli_close(cli->conn,
1895 cli->timeout,
1896 cli->smb1.pid,
1897 cli->smb1.tcon,
1898 cli->smb1.session,
1899 fnum,
1900 0); /* last_modified */
1903 /* Delete anything we made. */
1904 (void)smb1_dfs_delete(cli, "BAD\\BAD\\BAD");
1905 (void)smb1_dfs_delete(cli, "BAD\\BAD\\file");
1906 (void)smb1_dfs_delete(cli, "BAD\\BAD\\renamed_file");
1907 (void)smb1_dfs_delete(cli, "BAD\\BAD\\hlink");
1908 return retval;
1912 * SMB1 Findfirst. This is a minimal implementation
1913 * that expects all filename returns in one packet.
1914 * We're only using this to test the search DFS pathname
1915 * parsing.
1918 /****************************************************************************
1919 Calculate a safe next_entry_offset.
1920 ****************************************************************************/
1922 static size_t calc_next_entry_offset(const uint8_t *base,
1923 const uint8_t *pdata_end)
1925 size_t next_entry_offset = (size_t)PULL_LE_U32(base,0);
1927 if (next_entry_offset == 0 ||
1928 base + next_entry_offset < base ||
1929 base + next_entry_offset > pdata_end) {
1930 next_entry_offset = pdata_end - base;
1932 return next_entry_offset;
1935 static size_t get_filename(TALLOC_CTX *ctx,
1936 struct cli_state *cli,
1937 const uint8_t *base_ptr,
1938 uint16_t recv_flags2,
1939 const uint8_t *p,
1940 const uint8_t *pdata_end,
1941 struct file_info *finfo)
1943 size_t ret = 0;
1944 const uint8_t *base = p;
1945 size_t namelen = 0;
1946 size_t slen = 0;
1948 ZERO_STRUCTP(finfo);
1950 if (pdata_end - base < 94) {
1951 return pdata_end - base;
1953 p += 4; /* next entry offset */
1954 p += 4; /* fileindex */
1955 /* Offset zero is "create time", not "change time". */
1956 p += 8;
1957 finfo->atime_ts = interpret_long_date((const char *)p);
1958 p += 8;
1959 finfo->mtime_ts = interpret_long_date((const char *)p);
1960 p += 8;
1961 finfo->ctime_ts = interpret_long_date((const char *)p);
1962 p += 8;
1963 finfo->size = PULL_LE_U64(p, 0);
1964 p += 8;
1965 p += 8; /* alloc size */
1966 finfo->attr = PULL_LE_U32(p, 0);
1967 p += 4;
1968 namelen = PULL_LE_U32(p, 0);
1969 p += 4;
1970 p += 4; /* EA size */
1971 slen = PULL_LE_U8(p, 0);
1972 if (slen > 24) {
1973 /* Bad short name length. */
1974 return pdata_end - base;
1976 p += 2;
1977 ret = pull_string_talloc(ctx,
1978 base_ptr,
1979 recv_flags2,
1980 &finfo->short_name,
1982 slen,
1983 STR_UNICODE);
1984 if (ret == (size_t)-1) {
1985 return pdata_end - base;
1987 p += 24; /* short name */
1988 if (p + namelen < p || p + namelen > pdata_end) {
1989 return pdata_end - base;
1991 ret = pull_string_talloc(ctx,
1992 base_ptr,
1993 recv_flags2,
1994 &finfo->name,
1996 namelen,
1998 if (ret == (size_t)-1) {
1999 return pdata_end - base;
2001 return calc_next_entry_offset(base, pdata_end);
2004 /* Single shot SMB1 TRANS2 FindFirst. */
2006 static NTSTATUS smb1_findfirst(TALLOC_CTX *mem_ctx,
2007 struct cli_state *cli,
2008 const char *search_name,
2009 struct file_info **names,
2010 size_t *num_names)
2012 NTSTATUS status;
2013 uint16_t setup[1];
2014 uint8_t *param = NULL;
2015 uint16_t recv_flags2 = 0;
2016 uint8_t *rparam = NULL;
2017 uint32_t num_rparam = 0;
2018 uint8_t *rdata = NULL;
2019 uint32_t num_rdata = 0;
2020 uint16_t num_names_returned = 0;
2021 struct file_info *finfo = NULL;
2022 uint8_t *p2 = NULL;
2023 uint8_t *data_end = NULL;
2024 uint16_t i = 0;
2026 PUSH_LE_U16(&setup[0], 0, TRANSACT2_FINDFIRST);
2028 param = talloc_array(mem_ctx, uint8_t, 12);
2029 if (param == NULL) {
2030 return NT_STATUS_NO_MEMORY;
2033 PUSH_LE_U16(param, 0, FILE_ATTRIBUTE_DIRECTORY |
2034 FILE_ATTRIBUTE_SYSTEM |
2035 FILE_ATTRIBUTE_HIDDEN);
2036 PUSH_LE_U16(param, 2, 1366); /* max_matches */
2037 PUSH_LE_U16(param, 4, FLAG_TRANS2_FIND_CLOSE_IF_END);
2038 PUSH_LE_U16(param, 6, SMB_FIND_FILE_BOTH_DIRECTORY_INFO); /* info_level */
2040 param = trans2_bytes_push_str(param,
2041 smbXcli_conn_use_unicode(cli->conn),
2042 search_name,
2043 strlen(search_name)+1,
2044 NULL);
2045 if (param == NULL) {
2046 return NT_STATUS_NO_MEMORY;
2050 * A one shot SMB1 findfirst will be enough to
2051 * return ".", "..", and "file".
2053 status = cli_trans(mem_ctx,
2054 cli,
2055 SMBtrans2, /* cmd */
2056 NULL, /* pipe_name */
2057 0, /* fid */
2058 0, /* function */
2059 0, /* flags */
2060 &setup[0],
2061 1, /* num_setup uint16_t words */
2062 0, /* max returned setup */
2063 param,
2064 talloc_get_size(param), /* num_param */
2065 10, /* max returned param */
2066 NULL, /* data */
2067 0, /* num_data */
2068 SMB_BUFFER_SIZE_MAX, /* max retured data */
2069 /* Return values from here on.. */
2070 &recv_flags2, /* recv_flags2 */
2071 NULL, /* rsetup */
2072 0, /* min returned rsetup */
2073 NULL, /* num_rsetup */
2074 &rparam,
2075 6, /* min returned rparam */
2076 &num_rparam, /* number of returned rparam */
2077 &rdata,
2078 0, /* min returned rdata */
2079 &num_rdata);
2080 if (!NT_STATUS_IS_OK(status)) {
2081 return status;
2084 num_names_returned = PULL_LE_U16(rparam, 2);
2086 finfo = talloc_array(mem_ctx, struct file_info, num_names_returned);
2087 if (param == NULL) {
2088 return NT_STATUS_NO_MEMORY;
2091 p2 = rdata;
2092 data_end = rdata + num_rdata;
2094 for (i = 0; i < num_names_returned; i++) {
2095 if (p2 >= data_end) {
2096 break;
2098 if (i == num_names_returned - 1) {
2099 /* Last entry - fixup the last offset length. */
2100 PUSH_LE_U32(p2, 0, PTR_DIFF((rdata + num_rdata), p2));
2103 p2 += get_filename(mem_ctx,
2104 cli,
2105 rdata,
2106 recv_flags2,
2108 data_end,
2109 &finfo[i]);
2111 if (finfo->name == NULL) {
2112 printf("%s:%d Unable to parse name from listing "
2113 "of %s, position %u\n",
2114 __FILE__,
2115 __LINE__,
2116 search_name,
2117 (unsigned int)i);
2118 return NT_STATUS_INVALID_NETWORK_RESPONSE;
2121 *num_names = i;
2122 *names = finfo;
2123 return NT_STATUS_OK;
2127 * Test a specific SMB1 findfirst path to see if it
2128 * matches a given file array.
2130 static bool test_smb1_findfirst_path(struct cli_state *cli,
2131 const char *search_path,
2132 struct file_info *root_finfo,
2133 size_t num_root_finfo)
2135 size_t i = 0;
2136 size_t num_finfo = 0;
2137 struct file_info *finfo = NULL;
2138 NTSTATUS status;
2140 status = smb1_findfirst(talloc_tos(),
2141 cli,
2142 search_path,
2143 &finfo,
2144 &num_finfo);
2145 if (!NT_STATUS_IS_OK(status)) {
2146 printf("%s:%d smb1findfirst on %s returned %s\n",
2147 __FILE__,
2148 __LINE__,
2149 search_path,
2150 nt_errstr(status));
2151 return false;
2154 if (num_finfo != num_root_finfo) {
2155 printf("%s:%d On %s, num_finfo = %zu, num_root_finfo = %zu\n",
2156 __FILE__,
2157 __LINE__,
2158 search_path,
2159 num_finfo,
2160 num_root_finfo);
2161 return false;
2163 for (i = 0; i < num_finfo; i++) {
2164 bool match = strequal_m(finfo[i].name,
2165 root_finfo[i].name);
2166 if (!match) {
2167 printf("%s:%d Missmatch. For %s, at position %zu, "
2168 "finfo[i].name = %s, "
2169 "root_finfo[i].name = %s\n",
2170 __FILE__,
2171 __LINE__,
2172 search_path,
2174 finfo[i].name,
2175 root_finfo[i].name);
2176 return false;
2179 TALLOC_FREE(finfo);
2180 return true;
2184 * "Raw" test of doing a SMB1 findfirst to a DFS share.
2185 * We must (mostly) use the lower level smb1cli_XXXX() interfaces,
2186 * not the cli_XXX() ones here as the ultimate goal is to fix our
2187 * cli_XXX() interfaces to work transparently over DFS.
2189 * So here, we're testing the server code, not the client code.
2191 * Passes cleanly against Windows.
2194 bool run_smb1_dfs_search_paths(int dummy)
2196 struct cli_state *cli = NULL;
2197 NTSTATUS status;
2198 bool dfs_supported = false;
2199 struct file_info *root_finfo = NULL;
2200 size_t num_root_finfo = 0;
2201 bool retval = false;
2202 bool ok = false;
2203 uint16_t fnum = (uint16_t)-1;
2205 printf("Starting SMB1-DFS-SEARCH-PATHS\n");
2207 if (!torture_init_connection(&cli)) {
2208 return false;
2211 if (!torture_open_connection(&cli, 0)) {
2212 return false;
2215 /* Ensure this is a DFS share. */
2216 dfs_supported = smbXcli_conn_dfs_supported(cli->conn);
2217 if (!dfs_supported) {
2218 printf("Server %s does not support DFS\n",
2219 smbXcli_conn_remote_name(cli->conn));
2220 return false;
2222 dfs_supported = smbXcli_tcon_is_dfs_share(cli->smb1.tcon);
2223 if (!dfs_supported) {
2224 printf("Share %s does not support DFS\n",
2225 cli->share);
2226 return false;
2229 /* Start clean. */
2230 (void)smb1_dfs_delete(cli, "BAD\\BAD\\file");
2232 /* Create a test file to search for. */
2233 status = smb1cli_ntcreatex(cli->conn,
2234 cli->timeout,
2235 cli->smb1.pid,
2236 cli->smb1.tcon,
2237 cli->smb1.session,
2238 "BAD\\BAD\\file",
2239 OPLOCK_NONE, /* CreatFlags */
2240 0, /* RootDirectoryFid */
2241 SEC_STD_SYNCHRONIZE|
2242 SEC_STD_DELETE |
2243 SEC_FILE_READ_DATA|
2244 SEC_FILE_READ_ATTRIBUTE, /* DesiredAccess */
2245 0, /* AllocationSize */
2246 FILE_ATTRIBUTE_NORMAL, /* FileAttributes */
2247 FILE_SHARE_READ|
2248 FILE_SHARE_WRITE|
2249 FILE_SHARE_DELETE, /* ShareAccess */
2250 FILE_CREATE, /* CreateDisposition */
2251 0, /* CreateOptions */
2252 2, /* ImpersonationLevel */
2253 0, /* SecurityFlags */
2254 &fnum);
2255 if (!NT_STATUS_IS_OK(status)) {
2256 printf("%s:%d smb1cli_ntcreatex on %s returned %s\n",
2257 __FILE__,
2258 __LINE__,
2259 "BAD\\BAD\\file",
2260 nt_errstr(status));
2261 return false;
2264 /* Close "file" handle. */
2265 (void)smb1cli_close(cli->conn,
2266 cli->timeout,
2267 cli->smb1.pid,
2268 cli->smb1.tcon,
2269 cli->smb1.session,
2270 fnum,
2271 0); /* last_modified */
2272 fnum = (uint16_t)-1;
2274 /* Get the list of files in the share. */
2275 status = smb1_findfirst(talloc_tos(),
2276 cli,
2277 "SERVER\\SHARE\\*",
2278 &root_finfo,
2279 &num_root_finfo);
2280 if (!NT_STATUS_IS_OK(status)) {
2281 printf("%s:%d smb1findfirst on %s returned %s\n",
2282 __FILE__,
2283 __LINE__,
2284 "SERVER\\SHARE\\*",
2285 nt_errstr(status));
2286 return false;
2290 * Try different search names. They should
2291 * all match the root directory list.
2293 ok = test_smb1_findfirst_path(cli,
2294 "\\SERVER\\SHARE\\*",
2295 root_finfo,
2296 num_root_finfo);
2297 if (!ok) {
2298 goto err;
2301 ok = test_smb1_findfirst_path(cli,
2302 "*",
2303 root_finfo,
2304 num_root_finfo);
2305 if (!ok) {
2306 goto err;
2308 ok = test_smb1_findfirst_path(cli,
2309 "\\*",
2310 root_finfo,
2311 num_root_finfo);
2312 if (!ok) {
2313 goto err;
2315 ok = test_smb1_findfirst_path(cli,
2316 "\\SERVER\\*",
2317 root_finfo,
2318 num_root_finfo);
2319 if (!ok) {
2320 goto err;
2322 retval = true;
2324 err:
2326 if (fnum != (uint16_t)-1) {
2327 (void)smb1cli_close(cli->conn,
2328 cli->timeout,
2329 cli->smb1.pid,
2330 cli->smb1.tcon,
2331 cli->smb1.session,
2332 fnum,
2333 0); /* last_modified */
2336 /* Delete anything we made. */
2337 (void)smb1_dfs_delete(cli, "BAD\\BAD\\file");
2338 return retval;