LCOV - code coverage report
Current view: top level - source3/libsmb - clidfs.c (source / functions) Hit Total Coverage
Test: coverage report for v4-17-test 1498b464 Lines: 383 561 68.3 %
Date: 2024-06-13 04:01:37 Functions: 14 15 93.3 %

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

Generated by: LCOV version 1.13