Coverage Report

Created: 2026-05-24 06:47

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/samba/source3/libsmb/clidfs.c
Line
Count
Source
1
/*
2
   Unix SMB/CIFS implementation.
3
   client connect/disconnect routines
4
   Copyright (C) Andrew Tridgell                  1994-1998
5
   Copyright (C) Gerald (Jerry) Carter            2004
6
   Copyright (C) Jeremy Allison                   2007-2009
7
8
   This program is free software; you can redistribute it and/or modify
9
   it under the terms of the GNU General Public License as published by
10
   the Free Software Foundation; either version 3 of the License, or
11
   (at your option) any later version.
12
13
   This program is distributed in the hope that it will be useful,
14
   but WITHOUT ANY WARRANTY; without even the implied warranty of
15
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16
   GNU General Public License for more details.
17
18
   You should have received a copy of the GNU General Public License
19
   along with this program.  If not, see <http://www.gnu.org/licenses/>.
20
*/
21
22
#include "includes.h"
23
#include "source3/include/client.h"
24
#include "source3/libsmb/proto.h"
25
#include "libsmb/clirap.h"
26
#include "msdfs.h"
27
#include "trans2.h"
28
#include "libsmb/nmblib.h"
29
#include "../libcli/smb/smbXcli_base.h"
30
#include "auth/credentials/credentials.h"
31
#include "lib/param/param.h"
32
#include "libcli/smb/smb2_negotiate_context.h"
33
34
/********************************************************************
35
 Important point.
36
37
 DFS paths are *always* of the form \server\share\<pathname> (the \ characters
38
 are not C escaped here).
39
40
 - but if we're using POSIX paths then <pathname> may contain
41
   '/' separators, not '\\' separators. So cope with '\\' or '/'
42
   as a separator when looking at the pathname part.... JRA.
43
********************************************************************/
44
45
/********************************************************************
46
 Ensure a connection is encrypted.
47
********************************************************************/
48
49
static NTSTATUS cli_cm_force_encryption_creds(struct cli_state *c,
50
                struct cli_credentials *creds,
51
                const char *sharename)
52
0
{
53
0
  uint16_t major, minor;
54
0
  uint32_t caplow, caphigh;
55
0
  NTSTATUS status;
56
0
  bool temp_ipc = false;
57
58
0
  if (smbXcli_conn_protocol(c->conn) >= PROTOCOL_SMB2_02) {
59
0
    status = smb2cli_session_encryption_on(c->smb2.session);
60
0
    if (NT_STATUS_EQUAL(status,NT_STATUS_NOT_SUPPORTED)) {
61
0
      d_printf("Encryption required and "
62
0
        "server doesn't support "
63
0
        "SMB3 encryption - failing connect\n");
64
0
    } else if (!NT_STATUS_IS_OK(status)) {
65
0
      d_printf("Encryption required and "
66
0
        "setup failed with error %s.\n",
67
0
        nt_errstr(status));
68
0
    }
69
0
    return status;
70
0
  }
71
72
0
  if (!SERVER_HAS_UNIX_CIFS(c)) {
73
0
    d_printf("Encryption required and "
74
0
      "server that doesn't support "
75
0
      "UNIX extensions - failing connect\n");
76
0
    return NT_STATUS_NOT_SUPPORTED;
77
0
  }
78
79
0
  if (c->smb1.tcon == NULL) {
80
0
    status = cli_tree_connect_creds(c, "IPC$", "IPC", creds);
81
0
    if (!NT_STATUS_IS_OK(status)) {
82
0
      d_printf("Encryption required and "
83
0
        "can't connect to IPC$ to check "
84
0
        "UNIX CIFS extensions.\n");
85
0
      return NT_STATUS_UNKNOWN_REVISION;
86
0
    }
87
0
    temp_ipc = true;
88
0
  }
89
90
0
  status = cli_unix_extensions_version(c, &major, &minor, &caplow,
91
0
               &caphigh);
92
0
  if (!NT_STATUS_IS_OK(status)) {
93
0
    d_printf("Encryption required and "
94
0
      "can't get UNIX CIFS extensions "
95
0
      "version from server.\n");
96
0
    if (temp_ipc) {
97
0
      cli_tdis(c);
98
0
    }
99
0
    return NT_STATUS_UNKNOWN_REVISION;
100
0
  }
101
102
0
  if (!(caplow & CIFS_UNIX_TRANSPORT_ENCRYPTION_CAP)) {
103
0
    d_printf("Encryption required and "
104
0
      "share %s doesn't support "
105
0
      "encryption.\n", sharename);
106
0
    if (temp_ipc) {
107
0
      cli_tdis(c);
108
0
    }
109
0
    return NT_STATUS_UNSUPPORTED_COMPRESSION;
110
0
  }
111
112
0
  status = cli_smb1_setup_encryption(c, creds);
113
0
  if (!NT_STATUS_IS_OK(status)) {
114
0
    d_printf("Encryption required and "
115
0
      "setup failed with error %s.\n",
116
0
      nt_errstr(status));
117
0
    if (temp_ipc) {
118
0
      cli_tdis(c);
119
0
    }
120
0
    return status;
121
0
  }
122
123
0
  if (temp_ipc) {
124
0
    cli_tdis(c);
125
0
  }
126
0
  return NT_STATUS_OK;
127
0
}
128
129
/********************************************************************
130
 Return a connection to a server.
131
********************************************************************/
132
133
static NTSTATUS do_connect(TALLOC_CTX *ctx,
134
          const char *server,
135
          const char *share,
136
          struct cli_credentials *creds,
137
          const struct sockaddr_storage *dest_ss,
138
          const struct smb_transports *transports,
139
          int name_type,
140
          struct cli_state **pcli)
141
0
{
142
0
  struct cli_state *c = NULL;
143
0
  char *servicename;
144
0
  char *sharename;
145
0
  char *newserver, *newshare;
146
0
  NTSTATUS status;
147
0
  int flags = 0;
148
0
  enum protocol_types protocol = PROTOCOL_NONE;
149
0
  enum smb_signing_setting signing_state =
150
0
    cli_credentials_get_smb_signing(creds);
151
0
  enum smb_encryption_setting encryption_state =
152
0
    cli_credentials_get_smb_encryption(creds);
153
0
  struct smb2_negotiate_contexts *in_contexts = NULL;
154
155
0
  if (encryption_state >= SMB_ENCRYPTION_DESIRED) {
156
0
    signing_state = SMB_SIGNING_REQUIRED;
157
0
  }
158
159
  /* make a copy so we don't modify the global string 'service' */
160
0
  servicename = talloc_strdup(ctx,share);
161
0
  if (!servicename) {
162
0
    return NT_STATUS_NO_MEMORY;
163
0
  }
164
0
  sharename = servicename;
165
0
  if (*sharename == '\\') {
166
0
    sharename += 2;
167
0
    if (server == NULL) {
168
0
      server = sharename;
169
0
    }
170
0
    sharename = strchr_m(sharename,'\\');
171
0
    if (!sharename) {
172
0
      return NT_STATUS_NO_MEMORY;
173
0
    }
174
0
    *sharename = 0;
175
0
    sharename++;
176
0
  }
177
0
  if (server == NULL) {
178
0
    return NT_STATUS_INVALID_PARAMETER;
179
0
  }
180
181
  /*
182
   * The functions cli_resolve_path() and cli_cm_open() might not create a
183
   * new cli context, but might return an already existing one. This
184
   * requires that a long living context is used. It should be freed after
185
   * the client is done with its connection.
186
   */
187
0
  status = cli_connect_nb(ctx,
188
0
        server,
189
0
        dest_ss,
190
0
        transports,
191
0
        name_type,
192
0
        NULL,
193
0
        signing_state,
194
0
        flags,
195
0
        &c);
196
197
0
  if (!NT_STATUS_IS_OK(status)) {
198
0
    if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_SUPPORTED)) {
199
0
      DBG_ERR("NetBIOS support disabled, unable to connect\n");
200
0
    }
201
202
0
    DBG_WARNING("Connection to %s failed (Error %s)\n",
203
0
          server,
204
0
          nt_errstr(status));
205
0
    return status;
206
0
  }
207
208
0
  DEBUG(4,(" session request ok\n"));
209
210
0
  in_contexts = talloc_zero(ctx, struct smb2_negotiate_contexts);
211
0
  if (in_contexts == NULL) {
212
0
    return NT_STATUS_NO_MEMORY;
213
0
  }
214
215
0
  status = smb2_negotiate_context_add(
216
0
    in_contexts,
217
0
    in_contexts,
218
0
    SMB2_POSIX_EXTENSIONS_AVAILABLE,
219
0
    (const uint8_t *)SMB2_CREATE_TAG_POSIX,
220
0
    strlen(SMB2_CREATE_TAG_POSIX));
221
0
  if (!NT_STATUS_IS_OK(status)) {
222
0
    return status;
223
0
  }
224
225
0
  status = smbXcli_negprot(c->conn,
226
0
         c->timeout,
227
0
         lp_client_min_protocol(),
228
0
         lp_client_max_protocol(),
229
0
         in_contexts,
230
0
         NULL,
231
0
         NULL);
232
0
  TALLOC_FREE(in_contexts);
233
234
0
  if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT)) {
235
0
    d_printf("Protocol negotiation (with timeout %d ms) timed out against server %s\n",
236
0
       c->timeout,
237
0
       smbXcli_conn_remote_name(c->conn));
238
0
    cli_shutdown(c);
239
0
    return status;
240
0
  }
241
242
0
  if (!NT_STATUS_IS_OK(status)) {
243
0
    d_printf("Protocol negotiation to server %s (for a protocol between %s and %s) failed: %s\n",
244
0
       smbXcli_conn_remote_name(c->conn),
245
0
       lpcfg_get_smb_protocol(lp_client_min_protocol()),
246
0
       lpcfg_get_smb_protocol(lp_client_max_protocol()),
247
0
       nt_errstr(status));
248
0
    cli_shutdown(c);
249
0
    return status;
250
0
  }
251
0
  protocol = smbXcli_conn_protocol(c->conn);
252
0
  DEBUG(4,(" negotiated dialect[%s] against server[%s]\n",
253
0
     smb_protocol_types_string(protocol),
254
0
     smbXcli_conn_remote_name(c->conn)));
255
256
0
  if (protocol >= PROTOCOL_SMB2_02) {
257
    /* Ensure we ask for some initial credits. */
258
0
    smb2cli_conn_set_max_credits(c->conn, DEFAULT_SMB2_MAX_CREDITS);
259
0
  }
260
261
0
  status = cli_session_setup_creds(c, creds);
262
0
  if (!NT_STATUS_IS_OK(status)) {
263
    /* If a password was not supplied then
264
     * try again with a null username. */
265
0
    if (encryption_state == SMB_ENCRYPTION_REQUIRED ||
266
0
      smbXcli_conn_signing_mandatory(c->conn) ||
267
0
      cli_credentials_authentication_requested(creds) ||
268
0
      cli_credentials_is_anonymous(creds) ||
269
0
      !NT_STATUS_IS_OK(status = cli_session_setup_anon(c)))
270
0
    {
271
0
      d_printf("session setup failed: %s\n",
272
0
         nt_errstr(status));
273
0
      if (NT_STATUS_EQUAL(status,
274
0
              NT_STATUS_MORE_PROCESSING_REQUIRED))
275
0
        d_printf("did you forget to run kinit?\n");
276
0
      cli_shutdown(c);
277
0
      return status;
278
0
    }
279
0
    d_printf("Anonymous login successful\n");
280
0
  }
281
282
0
  if (!NT_STATUS_IS_OK(status)) {
283
0
    DEBUG(10,("cli_init_creds() failed: %s\n", nt_errstr(status)));
284
0
    cli_shutdown(c);
285
0
    return status;
286
0
  }
287
288
0
  DEBUG(4,(" session setup ok\n"));
289
290
0
  if (encryption_state >= SMB_ENCRYPTION_DESIRED) {
291
0
    status = cli_cm_force_encryption_creds(c,
292
0
                   creds,
293
0
                   sharename);
294
0
    if (!NT_STATUS_IS_OK(status)) {
295
0
      switch (encryption_state) {
296
0
      case SMB_ENCRYPTION_DESIRED:
297
0
        break;
298
0
      case SMB_ENCRYPTION_REQUIRED:
299
0
      default:
300
0
        cli_shutdown(c);
301
0
        return status;
302
0
      }
303
0
    }
304
0
  }
305
306
  /* here's the fun part....to support 'msdfs proxy' shares
307
     (on Samba or windows) we have to issues a TRANS_GET_DFS_REFERRAL
308
     here before trying to connect to the original share.
309
     cli_check_msdfs_proxy() will fail if it is a normal share. */
310
311
0
  if (smbXcli_conn_dfs_supported(c->conn) &&
312
0
      cli_check_msdfs_proxy(ctx, c, sharename,
313
0
        &newserver, &newshare,
314
0
        creds)) {
315
0
    cli_shutdown(c);
316
0
    return do_connect(ctx, newserver,
317
0
        newshare, creds,
318
0
        NULL, transports, name_type, pcli);
319
0
  }
320
321
  /* must be a normal share */
322
323
0
  status = cli_tree_connect_creds(c, sharename, "?????", creds);
324
0
  if (!NT_STATUS_IS_OK(status)) {
325
0
    d_printf("tree connect failed: %s\n", nt_errstr(status));
326
0
    cli_shutdown(c);
327
0
    return status;
328
0
  }
329
330
0
  DEBUG(4,(" tconx ok\n"));
331
0
  *pcli = c;
332
0
  return NT_STATUS_OK;
333
0
}
334
335
/********************************************************************
336
 Add a new connection to the list.
337
 referring_cli == NULL means a new initial connection.
338
********************************************************************/
339
340
static NTSTATUS cli_cm_connect(TALLOC_CTX *ctx,
341
             struct cli_state *referring_cli,
342
             const char *server,
343
             const char *share,
344
             struct cli_credentials *creds,
345
             const struct sockaddr_storage *dest_ss,
346
             const struct smb_transports *transports,
347
             int name_type,
348
             struct cli_state **pcli)
349
0
{
350
0
  struct cli_state *cli = NULL;
351
0
  NTSTATUS status;
352
353
0
  status = do_connect(ctx, server, share,
354
0
        creds,
355
0
        dest_ss, transports, name_type, &cli);
356
357
0
  if (!NT_STATUS_IS_OK(status)) {
358
0
    return status;
359
0
  }
360
361
  /*
362
   * This can't happen, this test is to satisfy static
363
   * checkers (clang)
364
   */
365
0
  if (cli == NULL) {
366
0
    return NT_STATUS_NO_MEMORY;
367
0
  }
368
369
  /* Enter into the list. */
370
0
  if (referring_cli) {
371
0
    DLIST_ADD_END(referring_cli, cli);
372
0
  }
373
374
0
  if (referring_cli && referring_cli->requested_posix_capabilities) {
375
0
    uint16_t major, minor;
376
0
    uint32_t caplow, caphigh;
377
0
    status = cli_unix_extensions_version(cli, &major, &minor,
378
0
                 &caplow, &caphigh);
379
0
    if (NT_STATUS_IS_OK(status)) {
380
0
      cli_set_unix_extensions_capabilities(cli,
381
0
          major, minor,
382
0
          caplow, caphigh);
383
0
    }
384
0
  }
385
386
0
  *pcli = cli;
387
0
  return NT_STATUS_OK;
388
0
}
389
390
/********************************************************************
391
 Return a connection to a server on a particular share.
392
********************************************************************/
393
394
static struct cli_state *cli_cm_find(struct cli_state *cli,
395
        const char *server,
396
        const char *share)
397
0
{
398
0
  struct cli_state *p;
399
400
0
  if (cli == NULL) {
401
0
    return NULL;
402
0
  }
403
404
  /* Search to the start of the list. */
405
0
  for (p = cli; p; p = DLIST_PREV(p)) {
406
0
    const char *remote_name =
407
0
      smbXcli_conn_remote_name(p->conn);
408
409
0
    if (strequal(server, remote_name) &&
410
0
        strequal(share,p->share)) {
411
0
      return p;
412
0
    }
413
0
  }
414
415
  /* Search to the end of the list. */
416
0
  for (p = cli->next; p; p = p->next) {
417
0
    const char *remote_name =
418
0
      smbXcli_conn_remote_name(p->conn);
419
420
0
    if (strequal(server, remote_name) &&
421
0
        strequal(share,p->share)) {
422
0
      return p;
423
0
    }
424
0
  }
425
426
0
  return NULL;
427
0
}
428
429
/**
430
 * @brief Open a client connection to a \\server\share.
431
 *
432
 * This function attempts to establish a connection to a specified SMB server
433
 * and share. It first checks for an existing connection in the referring_cli
434
 * connection list to avoid creating duplicate connections. If no existing
435
 * connection is found, it creates a new one.
436
 *
437
 * @param[in] ctx           The memory context. This context needs to be a
438
 *                          long-living context, as cli_resolve_path() and
439
 *                          cli_cm_open() might not create a new cli context,
440
 *                          but might return an existing one.
441
 *                          The context should be freed after the client is
442
 *                          fully done with its connections.
443
 *
444
 * @param[in] referring_cli An existing cli_state to search for cached
445
 *                          connections. This is used to find previously
446
 *                          established connections to avoid creating duplicates.
447
 *                          Can be NULL if no existing connections exist.
448
 *
449
 * @param[in] server        The server name to connect to. This is the NetBIOS
450
 *                          name or hostname of the target server.
451
 *
452
 * @param[in] share         The share name to connect to (can also be "IPC$").
453
 *
454
 * @param[in] creds         Credentials for authentication. Must not be NULL.
455
 *                          The function will return NT_STATUS_INVALID_PARAMETER
456
 *                          if credentials are not provided.
457
 *
458
 * @param[in] dest_ss       Destination socket address. Can be NULL, in which
459
 *                          case the address will be resolved from the server
460
 *                          name using name resolution.
461
 *
462
 * @param[in] transports    SMB transport configuration specifying which SMB
463
 *                          protocol versions to use (e.g., SMB1, SMB2, SMB3).
464
 *
465
 * @param[in] name_type     NetBIOS name type for name resolution. Common values
466
 *                          include 0x00 (workstation), 0x20 (file server).
467
 *                          This is used when resolving the server name via
468
 *                          NetBIOS.
469
 *
470
 * @param[out] pcli         Pointer to receive the cli_state structure. On
471
 *                          success, this will point to either a newly created
472
 *                          connection or an existing cached connection.
473
 *
474
 * @return                  NT_STATUS_OK on success. NT_STATUS_INVALID_PARAMETER
475
 *                          if credentials are NULL. Other NTSTATUS error codes
476
 *                          on connection or authentication failures.
477
 */
478
NTSTATUS cli_cm_open(TALLOC_CTX *ctx,
479
         struct cli_state *referring_cli,
480
         const char *server,
481
         const char *share,
482
         struct cli_credentials *creds,
483
         const struct sockaddr_storage *dest_ss,
484
         const struct smb_transports *transports,
485
         int name_type,
486
         struct cli_state **pcli)
487
0
{
488
  /* Try to reuse an existing connection in this list. */
489
0
  struct cli_state *c = cli_cm_find(referring_cli, server, share);
490
0
  NTSTATUS status;
491
492
0
  if (c) {
493
0
    *pcli = c;
494
0
    return NT_STATUS_OK;
495
0
  }
496
497
0
  if (creds == NULL) {
498
    /* Can't do a new connection
499
     * without auth info. */
500
0
    d_printf("cli_cm_open() Unable to open connection [\\%s\\%s] "
501
0
      "without client credentials\n",
502
0
      server, share );
503
0
    return NT_STATUS_INVALID_PARAMETER;
504
0
  }
505
506
0
  status = cli_cm_connect(ctx,
507
0
        referring_cli,
508
0
        server,
509
0
        share,
510
0
        creds,
511
0
        dest_ss,
512
0
        transports,
513
0
        name_type,
514
0
        &c);
515
0
  if (!NT_STATUS_IS_OK(status)) {
516
0
    return status;
517
0
  }
518
0
  *pcli = c;
519
0
  return NT_STATUS_OK;
520
0
}
521
522
/****************************************************************************
523
****************************************************************************/
524
525
void cli_cm_display(struct cli_state *cli)
526
0
{
527
0
  int i;
528
529
0
  for (i=0; cli; cli = cli->next,i++ ) {
530
0
    d_printf("%d:\tserver=%s, share=%s\n",
531
0
      i, smbXcli_conn_remote_name(cli->conn), cli->share);
532
0
  }
533
0
}
534
535
/**********************************************************************
536
 split a dfs path into the server, share name, and extrapath components
537
**********************************************************************/
538
539
static bool split_dfs_path(TALLOC_CTX *ctx,
540
        const char *nodepath,
541
        char **pp_server,
542
        char **pp_share,
543
        char **pp_extrapath)
544
0
{
545
0
  char *p, *q;
546
0
  char *path;
547
548
0
  *pp_server = NULL;
549
0
  *pp_share = NULL;
550
0
  *pp_extrapath = NULL;
551
552
0
  path = talloc_strdup(ctx, nodepath);
553
0
  if (!path) {
554
0
    goto fail;
555
0
  }
556
557
0
  if ( path[0] != '\\' ) {
558
0
    goto fail;
559
0
  }
560
561
0
  p = strchr_m( path + 1, '\\' );
562
0
  if ( !p ) {
563
0
    goto fail;
564
0
  }
565
566
0
  *p = '\0';
567
0
  p++;
568
569
  /* Look for any extra/deep path */
570
0
  q = strchr_m(p, '\\');
571
0
  if (q != NULL) {
572
0
    *q = '\0';
573
0
    q++;
574
0
    *pp_extrapath = talloc_strdup(ctx, q);
575
0
  } else {
576
0
    *pp_extrapath = talloc_strdup(ctx, "");
577
0
  }
578
0
  if (*pp_extrapath == NULL) {
579
0
    goto fail;
580
0
  }
581
582
0
  *pp_share = talloc_strdup(ctx, p);
583
0
  if (*pp_share == NULL) {
584
0
    goto fail;
585
0
  }
586
587
0
  *pp_server = talloc_strdup(ctx, &path[1]);
588
0
  if (*pp_server == NULL) {
589
0
    goto fail;
590
0
  }
591
592
0
  TALLOC_FREE(path);
593
0
  return true;
594
595
0
fail:
596
0
  TALLOC_FREE(*pp_share);
597
0
  TALLOC_FREE(*pp_extrapath);
598
0
  TALLOC_FREE(path);
599
0
  return false;
600
0
}
601
602
/****************************************************************************
603
 Return the original path truncated at the directory component before
604
 the first wildcard character. Trust the caller to provide a NULL
605
 terminated string
606
****************************************************************************/
607
608
static char *clean_path(TALLOC_CTX *ctx, const char *path)
609
0
{
610
0
  size_t len;
611
0
  char *p1, *p2, *p;
612
0
  char *path_out;
613
614
  /* No absolute paths. */
615
0
  while (IS_DIRECTORY_SEP(*path)) {
616
0
    path++;
617
0
  }
618
619
0
  path_out = talloc_strdup(ctx, path);
620
0
  if (!path_out) {
621
0
    return NULL;
622
0
  }
623
624
0
  p1 = strchr_m(path_out, '*');
625
0
  p2 = strchr_m(path_out, '?');
626
627
0
  if (p1 || p2) {
628
0
    if (p1 && p2) {
629
0
      p = MIN(p1,p2);
630
0
    } else if (!p1) {
631
0
      p = p2;
632
0
    } else {
633
0
      p = p1;
634
0
    }
635
0
    *p = '\0';
636
637
    /* Now go back to the start of this component. */
638
0
    p1 = strrchr_m(path_out, '/');
639
0
    p2 = strrchr_m(path_out, '\\');
640
0
    p = MAX(p1,p2);
641
0
    if (p) {
642
0
      *p = '\0';
643
0
    }
644
0
  }
645
646
  /* Strip any trailing separator */
647
648
0
  len = strlen(path_out);
649
0
  if ( (len > 0) && IS_DIRECTORY_SEP(path_out[len-1])) {
650
0
    path_out[len-1] = '\0';
651
0
  }
652
653
0
  return path_out;
654
0
}
655
656
/****************************************************************************
657
****************************************************************************/
658
659
static char *cli_dfs_make_full_path(TALLOC_CTX *ctx,
660
          struct cli_state *cli,
661
          const char *dir)
662
0
{
663
0
  char path_sep = '\\';
664
665
  /* Ensure the extrapath doesn't start with a separator. */
666
0
  while (IS_DIRECTORY_SEP(*dir)) {
667
0
    dir++;
668
0
  }
669
670
0
  if (cli->requested_posix_capabilities & CIFS_UNIX_POSIX_PATHNAMES_CAP) {
671
0
    path_sep = '/';
672
0
  }
673
0
  return talloc_asprintf(ctx, "%c%s%c%s%c%s",
674
0
      path_sep,
675
0
      smbXcli_conn_remote_name(cli->conn),
676
0
      path_sep,
677
0
      cli->share,
678
0
      path_sep,
679
0
      dir);
680
0
}
681
682
/********************************************************************
683
 Check if a path has already been converted to DFS.
684
********************************************************************/
685
686
bool cli_dfs_is_already_full_path(struct cli_state *cli, const char *path)
687
0
{
688
0
  const char *server = smbXcli_conn_remote_name(cli->conn);
689
0
  size_t server_len = strlen(server);
690
0
  bool found_server = false;
691
0
  const char *share = cli->share;
692
0
  size_t share_len = strlen(share);
693
0
  bool found_share = false;
694
695
0
  if (!IS_DIRECTORY_SEP(path[0])) {
696
0
    return false;
697
0
  }
698
0
  path++;
699
0
  found_server = (strncasecmp_m(path, server, server_len) == 0);
700
0
  if (!found_server) {
701
0
    return false;
702
0
  }
703
0
  path += server_len;
704
0
  if (!IS_DIRECTORY_SEP(path[0])) {
705
0
    return false;
706
0
  }
707
0
  path++;
708
0
  found_share = (strncasecmp_m(path, share, share_len) == 0);
709
0
  if (!found_share) {
710
0
    return false;
711
0
  }
712
0
  path += share_len;
713
0
  if (path[0] == '\0') {
714
0
    return true;
715
0
  }
716
0
  if (IS_DIRECTORY_SEP(path[0])) {
717
0
    return true;
718
0
  }
719
0
  return false;
720
0
}
721
722
/********************************************************************
723
 Get the dfs referral link.
724
********************************************************************/
725
726
NTSTATUS cli_dfs_get_referral_ex(TALLOC_CTX *ctx,
727
      struct cli_state *cli,
728
      const char *path,
729
      uint16_t max_referral_level,
730
      struct client_dfs_referral **refs,
731
      size_t *num_refs,
732
      size_t *consumed)
733
0
{
734
0
  unsigned int param_len = 0;
735
0
  uint16_t recv_flags2;
736
0
  uint8_t *param = NULL;
737
0
  uint8_t *rdata = NULL;
738
0
  char *p;
739
0
  char *endp;
740
0
  smb_ucs2_t *path_ucs;
741
0
  char *consumed_path = NULL;
742
0
  uint16_t consumed_ucs;
743
0
  uint16_t num_referrals;
744
0
  struct client_dfs_referral *referrals = NULL;
745
0
  NTSTATUS status;
746
0
  TALLOC_CTX *frame = talloc_stackframe();
747
748
0
  *num_refs = 0;
749
0
  *refs = NULL;
750
751
0
  param = talloc_array(talloc_tos(), uint8_t, 2);
752
0
  if (!param) {
753
0
    status = NT_STATUS_NO_MEMORY;
754
0
    goto out;
755
0
  }
756
0
  SSVAL(param, 0, max_referral_level);
757
758
0
  param = trans2_bytes_push_str(param, smbXcli_conn_use_unicode(cli->conn),
759
0
              path, strlen(path)+1,
760
0
              NULL);
761
0
  if (!param) {
762
0
    status = NT_STATUS_NO_MEMORY;
763
0
    goto out;
764
0
  }
765
0
  param_len = talloc_get_size(param);
766
0
  path_ucs = (smb_ucs2_t *)&param[2];
767
768
0
  if (smbXcli_conn_protocol(cli->conn) >= PROTOCOL_SMB2_02) {
769
0
    DATA_BLOB in_input_buffer;
770
0
    DATA_BLOB in_output_buffer = data_blob_null;
771
0
    DATA_BLOB out_input_buffer = data_blob_null;
772
0
    DATA_BLOB out_output_buffer = data_blob_null;
773
774
0
    in_input_buffer.data = param;
775
0
    in_input_buffer.length = param_len;
776
777
0
    status = smb2cli_ioctl(cli->conn,
778
0
               cli->timeout,
779
0
               cli->smb2.session,
780
0
               cli->smb2.tcon,
781
0
               UINT64_MAX, /* in_fid_persistent */
782
0
               UINT64_MAX, /* in_fid_volatile */
783
0
               FSCTL_DFS_GET_REFERRALS,
784
0
               0, /* in_max_input_length */
785
0
               &in_input_buffer,
786
0
               CLI_BUFFER_SIZE, /* in_max_output_length */
787
0
               &in_output_buffer,
788
0
               SMB2_IOCTL_FLAG_IS_FSCTL,
789
0
               talloc_tos(),
790
0
               &out_input_buffer,
791
0
               &out_output_buffer);
792
0
    if (!NT_STATUS_IS_OK(status)) {
793
0
      goto out;
794
0
    }
795
796
0
    if (out_output_buffer.length < 4) {
797
0
      status = NT_STATUS_INVALID_NETWORK_RESPONSE;
798
0
      goto out;
799
0
    }
800
801
0
    recv_flags2 = FLAGS2_UNICODE_STRINGS;
802
0
    rdata = out_output_buffer.data;
803
0
    endp = (char *)rdata + out_output_buffer.length;
804
0
  } else {
805
0
    unsigned int data_len = 0;
806
0
    uint16_t setup[1];
807
808
0
    SSVAL(setup, 0, TRANSACT2_GET_DFS_REFERRAL);
809
810
0
    status = cli_trans(talloc_tos(), cli, SMBtrans2,
811
0
           NULL, 0xffff, 0, 0,
812
0
           setup, 1, 0,
813
0
           param, param_len, 2,
814
0
           NULL, 0, CLI_BUFFER_SIZE,
815
0
           &recv_flags2,
816
0
           NULL, 0, NULL, /* rsetup */
817
0
           NULL, 0, NULL,
818
0
           &rdata, 4, &data_len);
819
0
    if (!NT_STATUS_IS_OK(status)) {
820
0
      goto out;
821
0
    }
822
823
0
    endp = (char *)rdata + data_len;
824
0
  }
825
826
0
  consumed_ucs  = SVAL(rdata, 0);
827
0
  num_referrals = SVAL(rdata, 2);
828
829
  /* consumed_ucs is the number of bytes
830
   * of the UCS2 path consumed not counting any
831
   * terminating null. We need to convert
832
   * back to unix charset and count again
833
   * to get the number of bytes consumed from
834
   * the incoming path. */
835
836
0
  errno = 0;
837
0
  if (pull_string_talloc(talloc_tos(),
838
0
      NULL,
839
0
      0,
840
0
      &consumed_path,
841
0
      path_ucs,
842
0
      consumed_ucs,
843
0
      STR_UNICODE) == 0) {
844
0
    if (errno != 0) {
845
0
      status = map_nt_error_from_unix(errno);
846
0
    } else {
847
0
      status = NT_STATUS_INVALID_NETWORK_RESPONSE;
848
0
    }
849
0
    goto out;
850
0
  }
851
0
  if (consumed_path == NULL) {
852
0
    status = map_nt_error_from_unix(errno);
853
0
    goto out;
854
0
  }
855
0
  *consumed = strlen(consumed_path);
856
857
0
  if (num_referrals != 0) {
858
0
    uint16_t ref_version;
859
0
    uint16_t ref_size;
860
0
    int i;
861
0
    uint16_t node_offset;
862
863
0
    referrals = talloc_array(ctx, struct client_dfs_referral,
864
0
           num_referrals);
865
866
0
    if (!referrals) {
867
0
      status = NT_STATUS_NO_MEMORY;
868
0
      goto out;
869
0
    }
870
    /* start at the referrals array */
871
872
0
    p = (char *)rdata+8;
873
0
    for (i=0; i<num_referrals && p < endp; i++) {
874
0
      if (p + 18 > endp) {
875
0
        goto out;
876
0
      }
877
0
      ref_version = SVAL(p, 0);
878
0
      ref_size    = SVAL(p, 2);
879
0
      node_offset = SVAL(p, 16);
880
881
0
      if (ref_version != 3) {
882
0
        p += ref_size;
883
0
        continue;
884
0
      }
885
886
0
      referrals[i].proximity = SVAL(p, 8);
887
0
      referrals[i].ttl       = SVAL(p, 10);
888
889
0
      if (p + node_offset > endp) {
890
0
        status = NT_STATUS_INVALID_NETWORK_RESPONSE;
891
0
        goto out;
892
0
      }
893
0
      pull_string_talloc(referrals,
894
0
             (const char *)rdata,
895
0
             recv_flags2,
896
0
             &referrals[i].dfspath,
897
0
             p+node_offset,
898
0
             PTR_DIFF(endp, p+node_offset),
899
0
             STR_TERMINATE|STR_UNICODE);
900
901
0
      if (!referrals[i].dfspath) {
902
0
        status = map_nt_error_from_unix(errno);
903
0
        goto out;
904
0
      }
905
0
      p += ref_size;
906
0
    }
907
0
    if (i < num_referrals) {
908
0
      status = NT_STATUS_INVALID_NETWORK_RESPONSE;
909
0
      goto out;
910
0
    }
911
0
  }
912
913
0
  *num_refs = num_referrals;
914
0
  *refs = referrals;
915
916
0
  out:
917
918
0
  TALLOC_FREE(frame);
919
0
  return status;
920
0
}
921
922
NTSTATUS cli_dfs_get_referral(TALLOC_CTX *ctx,
923
      struct cli_state *cli,
924
      const char *path,
925
      struct client_dfs_referral **refs,
926
      size_t *num_refs,
927
      size_t *consumed)
928
0
{
929
0
  return cli_dfs_get_referral_ex(ctx,
930
0
        cli,
931
0
        path,
932
0
        3,
933
0
        refs, /* Max referral level we want */
934
0
        num_refs,
935
0
        consumed);
936
0
}
937
938
static bool cli_conn_have_dfs(struct cli_state *cli)
939
0
{
940
0
  struct smbXcli_conn *conn = cli->conn;
941
0
  struct smbXcli_tcon *tcon = NULL;
942
0
  bool ok;
943
944
0
  if (smbXcli_conn_protocol(conn) < PROTOCOL_SMB2_02) {
945
0
    uint32_t capabilities = smb1cli_conn_capabilities(conn);
946
947
0
    if ((capabilities & CAP_STATUS32) == 0) {
948
0
      return false;
949
0
    }
950
0
    if ((capabilities & CAP_UNICODE) == 0) {
951
0
      return false;
952
0
    }
953
954
0
    tcon = cli->smb1.tcon;
955
0
  } else {
956
0
    tcon = cli->smb2.tcon;
957
0
  }
958
959
0
  ok = smbXcli_tcon_is_dfs_share(tcon);
960
0
  return ok;
961
0
}
962
963
/********************************************************************
964
********************************************************************/
965
struct cli_dfs_path_split {
966
  char *server;
967
  char *share;
968
  char *extrapath;
969
};
970
971
NTSTATUS cli_resolve_path(TALLOC_CTX *ctx,
972
        const char *mountpt,
973
        struct cli_credentials *creds,
974
        struct cli_state *rootcli,
975
        const char *path,
976
        struct cli_state **targetcli,
977
        char **pp_targetpath)
978
0
{
979
0
  struct client_dfs_referral *refs = NULL;
980
0
  size_t num_refs = 0;
981
0
  size_t consumed = 0;
982
0
  struct cli_state *cli_ipc = NULL;
983
0
  char *dfs_path = NULL;
984
0
  char *cleanpath = NULL;
985
0
  char *extrapath = NULL;
986
0
  int pathlen;
987
0
  struct cli_state *newcli = NULL;
988
0
  struct cli_state *ccli = NULL;
989
0
  size_t count = 0;
990
0
  char *newpath = NULL;
991
0
  char *newmount = NULL;
992
0
  char *ppath = NULL;
993
0
  SMB_STRUCT_STAT sbuf;
994
0
  uint32_t attributes;
995
0
  NTSTATUS status;
996
0
  struct smbXcli_tcon *target_tcon = NULL;
997
0
  struct cli_dfs_path_split *dfs_refs = NULL;
998
0
  bool ok;
999
0
  bool is_already_dfs = false;
1000
0
  struct smb_transports ts =
1001
0
    smb_transports_parse("client smb transports",
1002
0
             lp_client_smb_transports());
1003
1004
0
  if ( !rootcli || !path || !targetcli ) {
1005
0
    return NT_STATUS_INVALID_PARAMETER;
1006
0
  }
1007
1008
  /*
1009
   * Avoid more than one leading directory separator
1010
   */
1011
0
  while (IS_DIRECTORY_SEP(path[0]) && IS_DIRECTORY_SEP(path[1])) {
1012
0
    path++;
1013
0
  }
1014
1015
0
  ok = cli_conn_have_dfs(rootcli);
1016
0
  if (!ok) {
1017
0
    *targetcli = rootcli;
1018
0
    *pp_targetpath = talloc_strdup(ctx, path);
1019
0
    if (!*pp_targetpath) {
1020
0
      return NT_STATUS_NO_MEMORY;
1021
0
    }
1022
0
    return NT_STATUS_OK;
1023
0
  }
1024
1025
0
  *targetcli = NULL;
1026
1027
0
  is_already_dfs = cli_dfs_is_already_full_path(rootcli, path);
1028
0
  if (is_already_dfs) {
1029
0
    const char *localpath = NULL;
1030
    /*
1031
     * Given path is already converted to DFS.
1032
     * Convert to a local path so clean_path()
1033
     * can correctly strip any wildcards.
1034
     */
1035
0
    status = cli_dfs_target_check(ctx,
1036
0
                rootcli,
1037
0
                path,
1038
0
                &localpath);
1039
0
    if (!NT_STATUS_IS_OK(status)) {
1040
0
      return status;
1041
0
    }
1042
0
    path = localpath;
1043
0
  }
1044
1045
  /* Send a trans2_query_path_info to check for a referral. */
1046
1047
0
  cleanpath = clean_path(ctx, path);
1048
0
  if (!cleanpath) {
1049
0
    return NT_STATUS_NO_MEMORY;
1050
0
  }
1051
1052
0
  dfs_path = cli_dfs_make_full_path(ctx, rootcli, cleanpath);
1053
0
  if (!dfs_path) {
1054
0
    return NT_STATUS_NO_MEMORY;
1055
0
  }
1056
1057
0
  status = cli_qpathinfo_basic( rootcli, dfs_path, &sbuf, &attributes);
1058
0
  if (NT_STATUS_IS_OK(status)) {
1059
    /* This is an ordinary path, just return it. */
1060
0
    *targetcli = rootcli;
1061
0
    *pp_targetpath = talloc_strdup(ctx, path);
1062
0
    if (!*pp_targetpath) {
1063
0
      return NT_STATUS_NO_MEMORY;
1064
0
    }
1065
0
    goto done;
1066
0
  }
1067
1068
  /* Special case where client asked for a path that does not exist */
1069
1070
0
  if (NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
1071
0
    *targetcli = rootcli;
1072
0
    *pp_targetpath = talloc_strdup(ctx, path);
1073
0
    if (!*pp_targetpath) {
1074
0
      return NT_STATUS_NO_MEMORY;
1075
0
    }
1076
0
    goto done;
1077
0
  }
1078
1079
  /* We got an error, check for DFS referral. */
1080
1081
0
  if (!NT_STATUS_EQUAL(status, NT_STATUS_PATH_NOT_COVERED)) {
1082
0
    return status;
1083
0
  }
1084
1085
  /* Check for the referral. */
1086
1087
0
  status = cli_cm_open(ctx,
1088
0
           rootcli,
1089
0
           smbXcli_conn_remote_name(rootcli->conn),
1090
0
           "IPC$",
1091
0
           creds,
1092
0
           NULL, /* dest_ss not needed, we reuse the transport */
1093
0
           &ts,
1094
0
           0x20,
1095
0
           &cli_ipc);
1096
0
  if (!NT_STATUS_IS_OK(status)) {
1097
0
    return status;
1098
0
  }
1099
1100
0
  status = cli_dfs_get_referral(ctx, cli_ipc, dfs_path, &refs,
1101
0
              &num_refs, &consumed);
1102
0
  if (!NT_STATUS_IS_OK(status)) {
1103
0
    return status;
1104
0
  }
1105
1106
0
  if (!num_refs || !refs[0].dfspath) {
1107
0
    return NT_STATUS_NOT_FOUND;
1108
0
  }
1109
1110
  /*
1111
   * Bug#10123 - DFS referral entries can be provided in a random order,
1112
   * so check the connection cache for each item to avoid unnecessary
1113
   * reconnections.
1114
   */
1115
0
  dfs_refs = talloc_array(ctx, struct cli_dfs_path_split, num_refs);
1116
0
  if (dfs_refs == NULL) {
1117
0
    return NT_STATUS_NO_MEMORY;
1118
0
  }
1119
1120
0
  for (count = 0; count < num_refs; count++) {
1121
0
    if (!split_dfs_path(dfs_refs, refs[count].dfspath,
1122
0
            &dfs_refs[count].server,
1123
0
            &dfs_refs[count].share,
1124
0
            &dfs_refs[count].extrapath)) {
1125
0
      TALLOC_FREE(dfs_refs);
1126
0
      return NT_STATUS_NOT_FOUND;
1127
0
    }
1128
1129
0
    ccli = cli_cm_find(rootcli, dfs_refs[count].server,
1130
0
           dfs_refs[count].share);
1131
0
    if (ccli != NULL) {
1132
0
      extrapath = dfs_refs[count].extrapath;
1133
0
      *targetcli = ccli;
1134
0
      break;
1135
0
    }
1136
0
  }
1137
1138
  /*
1139
   * If no cached connection was found, then connect to the first live
1140
   * referral server in the list.
1141
   */
1142
0
  for (count = 0; (ccli == NULL) && (count < num_refs); count++) {
1143
    /* Connect to the target server & share */
1144
0
    status = cli_cm_connect(ctx, rootcli,
1145
0
        dfs_refs[count].server,
1146
0
        dfs_refs[count].share,
1147
0
        creds,
1148
0
        NULL, /* dest_ss */
1149
0
        &ts,
1150
0
        0x20,
1151
0
        targetcli);
1152
0
    if (!NT_STATUS_IS_OK(status)) {
1153
0
      d_printf("Unable to follow dfs referral [\\%s\\%s]\n",
1154
0
         dfs_refs[count].server,
1155
0
         dfs_refs[count].share);
1156
0
      continue;
1157
0
    } else {
1158
0
      extrapath = dfs_refs[count].extrapath;
1159
0
      break;
1160
0
    }
1161
0
  }
1162
1163
  /* No available referral server for the connection */
1164
0
  if (*targetcli == NULL) {
1165
0
    TALLOC_FREE(dfs_refs);
1166
0
    return status;
1167
0
  }
1168
1169
  /* Make sure to recreate the original string including any wildcards. */
1170
1171
0
  dfs_path = cli_dfs_make_full_path(ctx, rootcli, path);
1172
0
  if (!dfs_path) {
1173
0
    TALLOC_FREE(dfs_refs);
1174
0
    return NT_STATUS_NO_MEMORY;
1175
0
  }
1176
0
  pathlen = strlen(dfs_path);
1177
0
  consumed = MIN(pathlen, consumed);
1178
0
  *pp_targetpath = talloc_strdup(ctx, &dfs_path[consumed]);
1179
0
  if (!*pp_targetpath) {
1180
0
    TALLOC_FREE(dfs_refs);
1181
0
    return NT_STATUS_NO_MEMORY;
1182
0
  }
1183
0
  dfs_path[consumed] = '\0';
1184
1185
  /*
1186
   * *pp_targetpath is now the unconsumed part of the path.
1187
   * dfs_path is now the consumed part of the path
1188
   * (in \server\share\path format).
1189
   */
1190
1191
0
  if (extrapath && strlen(extrapath) > 0) {
1192
    /* EMC Celerra NAS version 5.6.50 (at least) doesn't appear to */
1193
    /* put the trailing \ on the path, so to be safe we put one in if needed */
1194
0
    if (extrapath[strlen(extrapath)-1] != '\\' && **pp_targetpath != '\\') {
1195
0
      *pp_targetpath = talloc_asprintf(ctx,
1196
0
              "%s\\%s",
1197
0
              extrapath,
1198
0
              *pp_targetpath);
1199
0
    } else {
1200
0
      *pp_targetpath = talloc_asprintf(ctx,
1201
0
              "%s%s",
1202
0
              extrapath,
1203
0
              *pp_targetpath);
1204
0
    }
1205
0
    if (!*pp_targetpath) {
1206
0
      TALLOC_FREE(dfs_refs);
1207
0
      return NT_STATUS_NO_MEMORY;
1208
0
    }
1209
0
  }
1210
1211
  /* parse out the consumed mount path */
1212
  /* trim off the \server\share\ */
1213
1214
0
  ppath = dfs_path;
1215
1216
0
  if (*ppath != '\\') {
1217
0
    d_printf("cli_resolve_path: "
1218
0
      "dfs_path (%s) not in correct format.\n",
1219
0
      dfs_path );
1220
0
    TALLOC_FREE(dfs_refs);
1221
0
    return NT_STATUS_NOT_FOUND;
1222
0
  }
1223
1224
0
  ppath++; /* Now pointing at start of server name. */
1225
1226
0
  if ((ppath = strchr_m( dfs_path, '\\' )) == NULL) {
1227
0
    TALLOC_FREE(dfs_refs);
1228
0
    return NT_STATUS_NOT_FOUND;
1229
0
  }
1230
1231
0
  ppath++; /* Now pointing at start of share name. */
1232
1233
0
  if ((ppath = strchr_m( ppath+1, '\\' )) == NULL) {
1234
0
    TALLOC_FREE(dfs_refs);
1235
0
    return NT_STATUS_NOT_FOUND;
1236
0
  }
1237
1238
0
  ppath++; /* Now pointing at path component. */
1239
1240
0
  newmount = talloc_asprintf(ctx, "%s\\%s", mountpt, ppath );
1241
0
  if (!newmount) {
1242
0
    TALLOC_FREE(dfs_refs);
1243
0
    return NT_STATUS_NOT_FOUND;
1244
0
  }
1245
1246
  /* Check for another dfs referral, note that we are not
1247
     checking for loops here. */
1248
1249
0
  if (!strequal(*pp_targetpath, "\\") && !strequal(*pp_targetpath, "/")) {
1250
0
    status = cli_resolve_path(ctx,
1251
0
            newmount,
1252
0
            creds,
1253
0
            *targetcli,
1254
0
            *pp_targetpath,
1255
0
            &newcli,
1256
0
            &newpath);
1257
0
    if (NT_STATUS_IS_OK(status)) {
1258
      /*
1259
       * When cli_resolve_path returns true here it's always
1260
       * returning the complete path in newpath, so we're done
1261
       * here.
1262
       */
1263
0
      *targetcli = newcli;
1264
0
      *pp_targetpath = newpath;
1265
0
      TALLOC_FREE(dfs_refs);
1266
0
      return status;
1267
0
    }
1268
0
  }
1269
1270
0
  done:
1271
1272
0
  if (smbXcli_conn_protocol((*targetcli)->conn) >= PROTOCOL_SMB2_02) {
1273
0
    target_tcon = (*targetcli)->smb2.tcon;
1274
0
  } else {
1275
0
    target_tcon = (*targetcli)->smb1.tcon;
1276
0
  }
1277
1278
  /* If returning true ensure we return a dfs root full path. */
1279
0
  if (smbXcli_tcon_is_dfs_share(target_tcon)) {
1280
0
    dfs_path = talloc_strdup(ctx, *pp_targetpath);
1281
0
    if (!dfs_path) {
1282
0
      TALLOC_FREE(dfs_refs);
1283
0
      return NT_STATUS_NO_MEMORY;
1284
0
    }
1285
0
    *pp_targetpath = cli_dfs_make_full_path(ctx, *targetcli, dfs_path);
1286
0
    if (*pp_targetpath == NULL) {
1287
0
      TALLOC_FREE(dfs_refs);
1288
0
      return NT_STATUS_NO_MEMORY;
1289
0
    }
1290
0
  }
1291
1292
0
  TALLOC_FREE(dfs_refs);
1293
0
  return NT_STATUS_OK;
1294
0
}
1295
1296
/********************************************************************
1297
********************************************************************/
1298
1299
bool cli_check_msdfs_proxy(TALLOC_CTX *ctx,
1300
        struct cli_state *cli,
1301
        const char *sharename,
1302
        char **pp_newserver,
1303
        char **pp_newshare,
1304
        struct cli_credentials *creds)
1305
0
{
1306
0
  struct client_dfs_referral *refs = NULL;
1307
0
  size_t num_refs = 0;
1308
0
  size_t consumed = 0;
1309
0
  char *fullpath = NULL;
1310
0
  bool res;
1311
0
  struct smbXcli_tcon *orig_tcon = NULL;
1312
0
  char *orig_share = NULL;
1313
0
  char *newextrapath = NULL;
1314
0
  NTSTATUS status;
1315
0
  const char *remote_name;
1316
0
  enum smb_encryption_setting encryption_state =
1317
0
    cli_credentials_get_smb_encryption(creds);
1318
1319
0
  if (!cli || !sharename) {
1320
0
    return false;
1321
0
  }
1322
1323
0
  remote_name = smbXcli_conn_remote_name(cli->conn);
1324
1325
  /* special case.  never check for a referral on the IPC$ share */
1326
1327
0
  if (strequal(sharename, "IPC$")) {
1328
0
    return false;
1329
0
  }
1330
1331
  /* send a trans2_query_path_info to check for a referral */
1332
1333
0
  fullpath = talloc_asprintf(ctx, "\\%s\\%s", remote_name, sharename);
1334
0
  if (!fullpath) {
1335
0
    return false;
1336
0
  }
1337
1338
  /* Store tcon state. */
1339
0
  if (cli_state_has_tcon(cli)) {
1340
0
    cli_state_save_tcon_share(cli, &orig_tcon, &orig_share);
1341
0
  }
1342
1343
  /* check for the referral */
1344
1345
0
  if (!NT_STATUS_IS_OK(cli_tree_connect(cli, "IPC$", "IPC", NULL))) {
1346
0
    cli_state_restore_tcon_share(cli, orig_tcon, orig_share);
1347
0
    return false;
1348
0
  }
1349
1350
0
  if (encryption_state >= SMB_ENCRYPTION_DESIRED) {
1351
0
    status = cli_cm_force_encryption_creds(cli, creds, "IPC$");
1352
0
    if (!NT_STATUS_IS_OK(status)) {
1353
0
      switch (encryption_state) {
1354
0
      case SMB_ENCRYPTION_DESIRED:
1355
0
        break;
1356
0
      case SMB_ENCRYPTION_REQUIRED:
1357
0
      default:
1358
        /*
1359
         * Failed to set up encryption.
1360
         * Disconnect the temporary IPC$
1361
         * tcon before restoring the original
1362
         * tcon so we don't leak it.
1363
         */
1364
0
        cli_tdis(cli);
1365
0
        cli_state_restore_tcon_share(cli,
1366
0
                   orig_tcon,
1367
0
                   orig_share);
1368
0
        return false;
1369
0
      }
1370
0
    }
1371
0
  }
1372
1373
0
  status = cli_dfs_get_referral(ctx, cli, fullpath, &refs,
1374
0
              &num_refs, &consumed);
1375
0
  res = NT_STATUS_IS_OK(status);
1376
1377
0
  status = cli_tdis(cli);
1378
1379
0
  cli_state_restore_tcon_share(cli, orig_tcon, orig_share);
1380
1381
0
  if (!NT_STATUS_IS_OK(status)) {
1382
0
    return false;
1383
0
  }
1384
1385
0
  if (!res || !num_refs) {
1386
0
    return false;
1387
0
  }
1388
1389
0
  if (!refs[0].dfspath) {
1390
0
    return false;
1391
0
  }
1392
1393
0
  if (!split_dfs_path(ctx, refs[0].dfspath, pp_newserver,
1394
0
          pp_newshare, &newextrapath)) {
1395
0
    return false;
1396
0
  }
1397
1398
  /* check that this is not a self-referral */
1399
1400
0
  if (strequal(remote_name, *pp_newserver) &&
1401
0
      strequal(sharename, *pp_newshare)) {
1402
0
    return false;
1403
0
  }
1404
1405
0
  return true;
1406
0
}
1407
1408
/********************************************************************
1409
 Windows and NetApp (and arguably the SMB1/2/3 specs) expect a non-DFS
1410
 path for the targets of rename and hardlink. If we have been given
1411
 a DFS path for these calls, convert it back into a local path by
1412
 stripping off the DFS prefix.
1413
********************************************************************/
1414
1415
NTSTATUS cli_dfs_target_check(TALLOC_CTX *mem_ctx,
1416
      struct cli_state *cli,
1417
      const char *fname_dst,
1418
      const char **fname_dst_out)
1419
0
{
1420
0
  char *dfs_prefix = NULL;
1421
0
  size_t prefix_len = 0;
1422
0
  struct smbXcli_tcon *tcon = NULL;
1423
1424
0
  if (!smbXcli_conn_dfs_supported(cli->conn)) {
1425
0
    goto copy_fname_out;
1426
0
  }
1427
0
  if (smbXcli_conn_protocol(cli->conn) >= PROTOCOL_SMB2_02) {
1428
0
    tcon = cli->smb2.tcon;
1429
0
  } else {
1430
0
    tcon = cli->smb1.tcon;
1431
0
  }
1432
0
  if (!smbXcli_tcon_is_dfs_share(tcon)) {
1433
0
    goto copy_fname_out;
1434
0
  }
1435
0
  dfs_prefix = cli_dfs_make_full_path(mem_ctx, cli, "");
1436
0
  if (dfs_prefix == NULL) {
1437
0
    return NT_STATUS_NO_MEMORY;
1438
0
  }
1439
0
  prefix_len = strlen(dfs_prefix);
1440
0
  if (strncmp(fname_dst, dfs_prefix, prefix_len) != 0) {
1441
    /*
1442
     * Prefix doesn't match. Assume it was
1443
     * already stripped or not added in the
1444
     * first place.
1445
     */
1446
0
    goto copy_fname_out;
1447
0
  }
1448
  /* Return the trailing name after the prefix. */
1449
0
  *fname_dst_out = &fname_dst[prefix_len];
1450
0
  TALLOC_FREE(dfs_prefix);
1451
0
  return NT_STATUS_OK;
1452
1453
0
  copy_fname_out:
1454
1455
  /*
1456
   * No change to the destination name. Just
1457
   * point it at the incoming destination name.
1458
   */
1459
0
  *fname_dst_out = fname_dst;
1460
0
  TALLOC_FREE(dfs_prefix);
1461
0
  return NT_STATUS_OK;
1462
0
}
1463
1464
/********************************************************************
1465
 Convert a pathname into a DFS path if it hasn't already been converted.
1466
 Always returns a talloc'ed path, makes it easy to pass const paths in.
1467
********************************************************************/
1468
1469
char *smb1_dfs_share_path(TALLOC_CTX *ctx,
1470
        struct cli_state *cli,
1471
        const char *path)
1472
0
{
1473
0
  bool is_dfs = smbXcli_conn_dfs_supported(cli->conn) &&
1474
0
      smbXcli_tcon_is_dfs_share(cli->smb1.tcon);
1475
0
  bool is_already_dfs_path = false;
1476
0
  bool posix = (cli->requested_posix_capabilities &
1477
0
      CIFS_UNIX_POSIX_PATHNAMES_CAP);
1478
0
  char sepchar = (posix ? '/' : '\\');
1479
1480
0
  if (!is_dfs) {
1481
0
    return talloc_strdup(ctx, path);
1482
0
  }
1483
0
  is_already_dfs_path = cli_dfs_is_already_full_path(cli, path);
1484
0
  if (is_already_dfs_path) {
1485
0
    return talloc_strdup(ctx, path);
1486
0
  }
1487
  /*
1488
   * We don't use cli_dfs_make_full_path() as,
1489
   * when given a null path, cli_dfs_make_full_path
1490
   * deliberately adds a trailing '\\' (this is by
1491
   * design to check for an existing DFS prefix match).
1492
   */
1493
0
  if (path[0] == '\0') {
1494
0
    return talloc_asprintf(ctx,
1495
0
               "%c%s%c%s",
1496
0
               sepchar,
1497
0
               smbXcli_conn_remote_name(cli->conn),
1498
0
               sepchar,
1499
0
               cli->share);
1500
0
  }
1501
0
  while (*path == sepchar) {
1502
0
    path++;
1503
0
  }
1504
0
  return talloc_asprintf(ctx,
1505
0
             "%c%s%c%s%c%s",
1506
0
             sepchar,
1507
0
             smbXcli_conn_remote_name(cli->conn),
1508
0
             sepchar,
1509
0
             cli->share,
1510
0
             sepchar,
1511
0
             path);
1512
0
}