LCOV - code coverage report
Current view: top level - source3/libsmb - clidfs.c (source / functions) Hit Total Coverage
Test: coverage report for recycleplus df22b230 Lines: 409 603 67.8 %
Date: 2024-02-14 10:14:15 Functions: 16 17 94.1 %

          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        1308 : 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        1308 :         struct cli_state *c = NULL;
     140             :         char *servicename;
     141             :         char *sharename;
     142             :         char *newserver, *newshare;
     143             :         NTSTATUS status;
     144        1308 :         int flags = 0;
     145        1308 :         enum protocol_types protocol = PROTOCOL_NONE;
     146             :         enum smb_signing_setting signing_state =
     147        1308 :                 cli_credentials_get_smb_signing(creds);
     148             :         enum smb_encryption_setting encryption_state =
     149        1308 :                 cli_credentials_get_smb_encryption(creds);
     150             : 
     151        1308 :         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        1308 :         servicename = talloc_strdup(ctx,share);
     157        1308 :         if (!servicename) {
     158           0 :                 return NT_STATUS_NO_MEMORY;
     159             :         }
     160        1308 :         sharename = servicename;
     161        1308 :         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        1308 :         if (server == NULL) {
     174           0 :                 return NT_STATUS_INVALID_PARAMETER;
     175             :         }
     176             : 
     177        1308 :         status = cli_connect_nb(
     178             :                 server, dest_ss, port, name_type, NULL,
     179             :                 signing_state,
     180             :                 flags, &c);
     181             : 
     182        1308 :         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        1308 :         DEBUG(4,(" session request ok\n"));
     194             : 
     195        1308 :         status = smbXcli_negprot(c->conn, c->timeout,
     196        1308 :                                  lp_client_min_protocol(),
     197        1308 :                                  lp_client_max_protocol());
     198             : 
     199        1308 :         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        1136 :         protocol = smbXcli_conn_protocol(c->conn);
     206        1136 :         DEBUG(4,(" negotiated dialect[%s] against server[%s]\n",
     207             :                  smb_protocol_types_string(protocol),
     208             :                  smbXcli_conn_remote_name(c->conn)));
     209             : 
     210        1136 :         if (protocol >= PROTOCOL_SMB2_02) {
     211             :                 /* Ensure we ask for some initial credits. */
     212        1118 :                 smb2cli_conn_set_max_credits(c->conn, DEFAULT_SMB2_MAX_CREDITS);
     213             :         }
     214             : 
     215        1136 :         status = cli_session_setup_creds(c, creds);
     216        1136 :         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          20 :                         cli_credentials_is_anonymous(creds) ||
     223          10 :                         !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        1016 :         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        1016 :         DEBUG(4,(" session setup ok\n"));
     243             : 
     244        1016 :         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             :                 }
     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        1923 :         if (smbXcli_conn_dfs_supported(c->conn) &&
     266         907 :                         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        1016 :         status = cli_tree_connect_creds(c, sharename, "?????", creds);
     278        1016 :         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        1002 :         DEBUG(4,(" tconx ok\n"));
     285        1002 :         *pcli = c;
     286        1002 :         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        1308 : 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        1308 :         struct cli_state *cli = NULL;
     305             :         NTSTATUS status;
     306             : 
     307        1308 :         status = do_connect(ctx, server, share,
     308             :                                 creds,
     309             :                                 dest_ss, port, name_type, &cli);
     310             : 
     311        1308 :         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        1002 :         if (cli == NULL) {
     320           0 :                 return NT_STATUS_NO_MEMORY;
     321             :         }
     322             : 
     323             :         /* Enter into the list. */
     324        1002 :         if (referring_cli) {
     325          74 :                 DLIST_ADD_END(referring_cli, cli);
     326             :         }
     327             : 
     328        1002 :         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        1002 :         *pcli = cli;
     341        1002 :         return NT_STATUS_OK;
     342             : }
     343             : 
     344             : /********************************************************************
     345             :  Return a connection to a server on a particular share.
     346             : ********************************************************************/
     347             : 
     348        3146 : 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        3146 :         if (cli == NULL) {
     355        1234 :                 return NULL;
     356             :         }
     357             : 
     358             :         /* Search to the start of the list. */
     359        8484 :         for (p = cli; p; p = DLIST_PREV(p)) {
     360             :                 const char *remote_name =
     361        8170 :                         smbXcli_conn_remote_name(p->conn);
     362             : 
     363       11906 :                 if (strequal(server, remote_name) &&
     364        3736 :                                 strequal(share,p->share)) {
     365        1598 :                         return p;
     366             :                 }
     367             :         }
     368             : 
     369             :         /* Search to the end of the list. */
     370         374 :         for (p = cli->next; p; p = p->next) {
     371             :                 const char *remote_name =
     372          60 :                         smbXcli_conn_remote_name(p->conn);
     373             : 
     374          71 :                 if (strequal(server, remote_name) &&
     375          11 :                                 strequal(share,p->share)) {
     376           0 :                         return p;
     377             :                 }
     378             :         }
     379             : 
     380         314 :         return NULL;
     381             : }
     382             : 
     383             : /****************************************************************************
     384             :  Open a client connection to a \\server\share.
     385             : ****************************************************************************/
     386             : 
     387        2070 : 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        2070 :         struct cli_state *c = cli_cm_find(referring_cli, server, share);
     399             :         NTSTATUS status;
     400             : 
     401        2070 :         if (c) {
     402         804 :                 *pcli = c;
     403         804 :                 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             :  split a dfs path into the server, share name, and extrapath components
     446             : **********************************************************************/
     447             : 
     448        1120 : static bool split_dfs_path(TALLOC_CTX *ctx,
     449             :                                 const char *nodepath,
     450             :                                 char **pp_server,
     451             :                                 char **pp_share,
     452             :                                 char **pp_extrapath)
     453             : {
     454             :         char *p, *q;
     455             :         char *path;
     456             : 
     457        1120 :         *pp_server = NULL;
     458        1120 :         *pp_share = NULL;
     459        1120 :         *pp_extrapath = NULL;
     460             : 
     461        1120 :         path = talloc_strdup(ctx, nodepath);
     462        1120 :         if (!path) {
     463           0 :                 goto fail;
     464             :         }
     465             : 
     466        1120 :         if ( path[0] != '\\' ) {
     467           0 :                 goto fail;
     468             :         }
     469             : 
     470        1120 :         p = strchr_m( path + 1, '\\' );
     471        1120 :         if ( !p ) {
     472           0 :                 goto fail;
     473             :         }
     474             : 
     475        1120 :         *p = '\0';
     476        1120 :         p++;
     477             : 
     478             :         /* Look for any extra/deep path */
     479        1120 :         q = strchr_m(p, '\\');
     480        1120 :         if (q != NULL) {
     481           0 :                 *q = '\0';
     482           0 :                 q++;
     483           0 :                 *pp_extrapath = talloc_strdup(ctx, q);
     484             :         } else {
     485        1120 :                 *pp_extrapath = talloc_strdup(ctx, "");
     486             :         }
     487        1120 :         if (*pp_extrapath == NULL) {
     488           0 :                 goto fail;
     489             :         }
     490             : 
     491        1120 :         *pp_share = talloc_strdup(ctx, p);
     492        1120 :         if (*pp_share == NULL) {
     493           0 :                 goto fail;
     494             :         }
     495             : 
     496        1120 :         *pp_server = talloc_strdup(ctx, &path[1]);
     497        1120 :         if (*pp_server == NULL) {
     498           0 :                 goto fail;
     499             :         }
     500             : 
     501        1120 :         TALLOC_FREE(path);
     502        1120 :         return true;
     503             : 
     504           0 : fail:
     505           0 :         TALLOC_FREE(*pp_share);
     506           0 :         TALLOC_FREE(*pp_extrapath);
     507           0 :         TALLOC_FREE(path);
     508           0 :         return false;
     509             : }
     510             : 
     511             : /****************************************************************************
     512             :  Return the original path truncated at the directory component before
     513             :  the first wildcard character. Trust the caller to provide a NULL
     514             :  terminated string
     515             : ****************************************************************************/
     516             : 
     517         924 : static char *clean_path(TALLOC_CTX *ctx, const char *path)
     518             : {
     519             :         size_t len;
     520             :         char *p1, *p2, *p;
     521             :         char *path_out;
     522             : 
     523             :         /* No absolute paths. */
     524        1848 :         while (IS_DIRECTORY_SEP(*path)) {
     525         924 :                 path++;
     526             :         }
     527             : 
     528         924 :         path_out = talloc_strdup(ctx, path);
     529         924 :         if (!path_out) {
     530           0 :                 return NULL;
     531             :         }
     532             : 
     533         924 :         p1 = strchr_m(path_out, '*');
     534         924 :         p2 = strchr_m(path_out, '?');
     535             : 
     536         924 :         if (p1 || p2) {
     537         724 :                 if (p1 && p2) {
     538           0 :                         p = MIN(p1,p2);
     539         724 :                 } else if (!p1) {
     540           0 :                         p = p2;
     541             :                 } else {
     542         724 :                         p = p1;
     543             :                 }
     544         724 :                 *p = '\0';
     545             : 
     546             :                 /* Now go back to the start of this component. */
     547         724 :                 p1 = strrchr_m(path_out, '/');
     548         724 :                 p2 = strrchr_m(path_out, '\\');
     549         724 :                 p = MAX(p1,p2);
     550         724 :                 if (p) {
     551         704 :                         *p = '\0';
     552             :                 }
     553             :         }
     554             : 
     555             :         /* Strip any trailing separator */
     556             : 
     557         924 :         len = strlen(path_out);
     558         924 :         if ( (len > 0) && IS_DIRECTORY_SEP(path_out[len-1])) {
     559          56 :                 path_out[len-1] = '\0';
     560             :         }
     561             : 
     562         924 :         return path_out;
     563             : }
     564             : 
     565             : /****************************************************************************
     566             : ****************************************************************************/
     567             : 
     568        1868 : static char *cli_dfs_make_full_path(TALLOC_CTX *ctx,
     569             :                                         struct cli_state *cli,
     570             :                                         const char *dir)
     571             : {
     572        1868 :         char path_sep = '\\';
     573             : 
     574             :         /* Ensure the extrapath doesn't start with a separator. */
     575        2800 :         while (IS_DIRECTORY_SEP(*dir)) {
     576         932 :                 dir++;
     577             :         }
     578             : 
     579        1868 :         if (cli->requested_posix_capabilities & CIFS_UNIX_POSIX_PATHNAMES_CAP) {
     580           0 :                 path_sep = '/';
     581             :         }
     582        1868 :         return talloc_asprintf(ctx, "%c%s%c%s%c%s",
     583             :                         path_sep,
     584             :                         smbXcli_conn_remote_name(cli->conn),
     585             :                         path_sep,
     586             :                         cli->share,
     587             :                         path_sep,
     588             :                         dir);
     589             : }
     590             : 
     591             : /********************************************************************
     592             :  Check if a path has already been converted to DFS.
     593             : ********************************************************************/
     594             : 
     595        1972 : bool cli_dfs_is_already_full_path(struct cli_state *cli, const char *path)
     596             : {
     597        1972 :         const char *server = smbXcli_conn_remote_name(cli->conn);
     598        1972 :         size_t server_len = strlen(server);
     599        1972 :         bool found_server = false;
     600        1972 :         const char *share = cli->share;
     601        1972 :         size_t share_len = strlen(share);
     602        1972 :         bool found_share = false;
     603             : 
     604        1972 :         if (!IS_DIRECTORY_SEP(path[0])) {
     605           0 :                 return false;
     606             :         }
     607        1972 :         path++;
     608        1972 :         found_server = (strncasecmp_m(path, server, server_len) == 0);
     609        1972 :         if (!found_server) {
     610         924 :                 return false;
     611             :         }
     612        1048 :         path += server_len;
     613        1048 :         if (!IS_DIRECTORY_SEP(path[0])) {
     614           0 :                 return false;
     615             :         }
     616        1048 :         path++;
     617        1048 :         found_share = (strncasecmp_m(path, share, share_len) == 0);
     618        1048 :         if (!found_share) {
     619           0 :                 return false;
     620             :         }
     621        1048 :         path += share_len;
     622        1048 :         if (path[0] == '\0') {
     623          72 :                 return true;
     624             :         }
     625         976 :         if (IS_DIRECTORY_SEP(path[0])) {
     626         976 :                 return true;
     627             :         }
     628           0 :         return false;
     629             : }
     630             : 
     631             : /********************************************************************
     632             :  Get the dfs referral link.
     633             : ********************************************************************/
     634             : 
     635        1661 : NTSTATUS cli_dfs_get_referral_ex(TALLOC_CTX *ctx,
     636             :                         struct cli_state *cli,
     637             :                         const char *path,
     638             :                         uint16_t max_referral_level,
     639             :                         struct client_dfs_referral **refs,
     640             :                         size_t *num_refs,
     641             :                         size_t *consumed)
     642             : {
     643        1661 :         unsigned int param_len = 0;
     644             :         uint16_t recv_flags2;
     645        1661 :         uint8_t *param = NULL;
     646        1661 :         uint8_t *rdata = NULL;
     647             :         char *p;
     648             :         char *endp;
     649             :         smb_ucs2_t *path_ucs;
     650        1661 :         char *consumed_path = NULL;
     651             :         uint16_t consumed_ucs;
     652             :         uint16_t num_referrals;
     653        1661 :         struct client_dfs_referral *referrals = NULL;
     654             :         NTSTATUS status;
     655        1661 :         TALLOC_CTX *frame = talloc_stackframe();
     656             : 
     657        1661 :         *num_refs = 0;
     658        1661 :         *refs = NULL;
     659             : 
     660        1661 :         param = talloc_array(talloc_tos(), uint8_t, 2);
     661        1661 :         if (!param) {
     662           0 :                 status = NT_STATUS_NO_MEMORY;
     663           0 :                 goto out;
     664             :         }
     665        1661 :         SSVAL(param, 0, max_referral_level);
     666             : 
     667        1661 :         param = trans2_bytes_push_str(param, smbXcli_conn_use_unicode(cli->conn),
     668        1661 :                                       path, strlen(path)+1,
     669             :                                       NULL);
     670        1661 :         if (!param) {
     671           0 :                 status = NT_STATUS_NO_MEMORY;
     672           0 :                 goto out;
     673             :         }
     674        1661 :         param_len = talloc_get_size(param);
     675        1661 :         path_ucs = (smb_ucs2_t *)&param[2];
     676             : 
     677        1661 :         if (smbXcli_conn_protocol(cli->conn) >= PROTOCOL_SMB2_02) {
     678             :                 DATA_BLOB in_input_buffer;
     679        1661 :                 DATA_BLOB in_output_buffer = data_blob_null;
     680        1661 :                 DATA_BLOB out_input_buffer = data_blob_null;
     681        1661 :                 DATA_BLOB out_output_buffer = data_blob_null;
     682             : 
     683        1661 :                 in_input_buffer.data = param;
     684        1661 :                 in_input_buffer.length = param_len;
     685             : 
     686        1661 :                 status = smb2cli_ioctl(cli->conn,
     687        1661 :                                        cli->timeout,
     688             :                                        cli->smb2.session,
     689             :                                        cli->smb2.tcon,
     690             :                                        UINT64_MAX, /* in_fid_persistent */
     691             :                                        UINT64_MAX, /* in_fid_volatile */
     692             :                                        FSCTL_DFS_GET_REFERRALS,
     693             :                                        0, /* in_max_input_length */
     694             :                                        &in_input_buffer,
     695             :                                        CLI_BUFFER_SIZE, /* in_max_output_length */
     696             :                                        &in_output_buffer,
     697             :                                        SMB2_IOCTL_FLAG_IS_FSCTL,
     698             :                                        talloc_tos(),
     699             :                                        &out_input_buffer,
     700             :                                        &out_output_buffer);
     701        1661 :                 if (!NT_STATUS_IS_OK(status)) {
     702         781 :                         goto out;
     703             :                 }
     704             : 
     705         880 :                 if (out_output_buffer.length < 4) {
     706           0 :                         status = NT_STATUS_INVALID_NETWORK_RESPONSE;
     707           0 :                         goto out;
     708             :                 }
     709             : 
     710         880 :                 recv_flags2 = FLAGS2_UNICODE_STRINGS;
     711         880 :                 rdata = out_output_buffer.data;
     712         880 :                 endp = (char *)rdata + out_output_buffer.length;
     713             :         } else {
     714           0 :                 unsigned int data_len = 0;
     715             :                 uint16_t setup[1];
     716             : 
     717           0 :                 SSVAL(setup, 0, TRANSACT2_GET_DFS_REFERRAL);
     718             : 
     719           0 :                 status = cli_trans(talloc_tos(), cli, SMBtrans2,
     720             :                                    NULL, 0xffff, 0, 0,
     721             :                                    setup, 1, 0,
     722             :                                    param, param_len, 2,
     723             :                                    NULL, 0, CLI_BUFFER_SIZE,
     724             :                                    &recv_flags2,
     725             :                                    NULL, 0, NULL, /* rsetup */
     726             :                                    NULL, 0, NULL,
     727             :                                    &rdata, 4, &data_len);
     728           0 :                 if (!NT_STATUS_IS_OK(status)) {
     729           0 :                         goto out;
     730             :                 }
     731             : 
     732           0 :                 endp = (char *)rdata + data_len;
     733             :         }
     734             : 
     735         880 :         consumed_ucs  = SVAL(rdata, 0);
     736         880 :         num_referrals = SVAL(rdata, 2);
     737             : 
     738             :         /* consumed_ucs is the number of bytes
     739             :          * of the UCS2 path consumed not counting any
     740             :          * terminating null. We need to convert
     741             :          * back to unix charset and count again
     742             :          * to get the number of bytes consumed from
     743             :          * the incoming path. */
     744             : 
     745         880 :         errno = 0;
     746         880 :         if (pull_string_talloc(talloc_tos(),
     747             :                         NULL,
     748             :                         0,
     749             :                         &consumed_path,
     750             :                         path_ucs,
     751             :                         consumed_ucs,
     752             :                         STR_UNICODE) == 0) {
     753           0 :                 if (errno != 0) {
     754           0 :                         status = map_nt_error_from_unix(errno);
     755             :                 } else {
     756           0 :                         status = NT_STATUS_INVALID_NETWORK_RESPONSE;
     757             :                 }
     758           0 :                 goto out;
     759             :         }
     760         880 :         if (consumed_path == NULL) {
     761           0 :                 status = map_nt_error_from_unix(errno);
     762           0 :                 goto out;
     763             :         }
     764         880 :         *consumed = strlen(consumed_path);
     765             : 
     766         880 :         if (num_referrals != 0) {
     767             :                 uint16_t ref_version;
     768             :                 uint16_t ref_size;
     769             :                 int i;
     770             :                 uint16_t node_offset;
     771             : 
     772         880 :                 referrals = talloc_array(ctx, struct client_dfs_referral,
     773             :                                          num_referrals);
     774             : 
     775         880 :                 if (!referrals) {
     776           0 :                         status = NT_STATUS_NO_MEMORY;
     777           0 :                         goto out;
     778             :                 }
     779             :                 /* start at the referrals array */
     780             : 
     781         880 :                 p = (char *)rdata+8;
     782        2580 :                 for (i=0; i<num_referrals && p < endp; i++) {
     783        1700 :                         if (p + 18 > endp) {
     784           0 :                                 goto out;
     785             :                         }
     786        1700 :                         ref_version = SVAL(p, 0);
     787        1700 :                         ref_size    = SVAL(p, 2);
     788        1700 :                         node_offset = SVAL(p, 16);
     789             : 
     790        1700 :                         if (ref_version != 3) {
     791           0 :                                 p += ref_size;
     792           0 :                                 continue;
     793             :                         }
     794             : 
     795        1700 :                         referrals[i].proximity = SVAL(p, 8);
     796        1700 :                         referrals[i].ttl       = SVAL(p, 10);
     797             : 
     798        1700 :                         if (p + node_offset > endp) {
     799           0 :                                 status = NT_STATUS_INVALID_NETWORK_RESPONSE;
     800           0 :                                 goto out;
     801             :                         }
     802        1700 :                         pull_string_talloc(referrals,
     803             :                                            (const char *)rdata,
     804             :                                            recv_flags2,
     805        1700 :                                            &referrals[i].dfspath,
     806        1700 :                                            p+node_offset,
     807        1700 :                                            PTR_DIFF(endp, p+node_offset),
     808             :                                            STR_TERMINATE|STR_UNICODE);
     809             : 
     810        1700 :                         if (!referrals[i].dfspath) {
     811           0 :                                 status = map_nt_error_from_unix(errno);
     812           0 :                                 goto out;
     813             :                         }
     814        1700 :                         p += ref_size;
     815             :                 }
     816         880 :                 if (i < num_referrals) {
     817           0 :                         status = NT_STATUS_INVALID_NETWORK_RESPONSE;
     818           0 :                         goto out;
     819             :                 }
     820             :         }
     821             : 
     822         880 :         *num_refs = num_referrals;
     823         880 :         *refs = referrals;
     824             : 
     825        1661 :   out:
     826             : 
     827        1661 :         TALLOC_FREE(frame);
     828        1661 :         return status;
     829             : }
     830             : 
     831        1661 : NTSTATUS cli_dfs_get_referral(TALLOC_CTX *ctx,
     832             :                         struct cli_state *cli,
     833             :                         const char *path,
     834             :                         struct client_dfs_referral **refs,
     835             :                         size_t *num_refs,
     836             :                         size_t *consumed)
     837             : {
     838        1661 :         return cli_dfs_get_referral_ex(ctx,
     839             :                                 cli,
     840             :                                 path,
     841             :                                 3,
     842             :                                 refs, /* Max referral level we want */
     843             :                                 num_refs,
     844             :                                 consumed);
     845             : }
     846             : 
     847        2646 : static bool cli_conn_have_dfs(struct cli_state *cli)
     848             : {
     849        2646 :         struct smbXcli_conn *conn = cli->conn;
     850        2646 :         struct smbXcli_tcon *tcon = NULL;
     851             :         bool ok;
     852             : 
     853        2646 :         if (smbXcli_conn_protocol(conn) < PROTOCOL_SMB2_02) {
     854           0 :                 uint32_t capabilities = smb1cli_conn_capabilities(conn);
     855             : 
     856           0 :                 if ((capabilities & CAP_STATUS32) == 0) {
     857           0 :                         return false;
     858             :                 }
     859           0 :                 if ((capabilities & CAP_UNICODE) == 0) {
     860           0 :                         return false;
     861             :                 }
     862             : 
     863           0 :                 tcon = cli->smb1.tcon;
     864             :         } else {
     865        2646 :                 tcon = cli->smb2.tcon;
     866             :         }
     867             : 
     868        2646 :         ok = smbXcli_tcon_is_dfs_share(tcon);
     869        2646 :         return ok;
     870             : }
     871             : 
     872             : /********************************************************************
     873             : ********************************************************************/
     874             : struct cli_dfs_path_split {
     875             :         char *server;
     876             :         char *share;
     877             :         char *extrapath;
     878             : };
     879             : 
     880        2646 : NTSTATUS cli_resolve_path(TALLOC_CTX *ctx,
     881             :                           const char *mountpt,
     882             :                           struct cli_credentials *creds,
     883             :                           struct cli_state *rootcli,
     884             :                           const char *path,
     885             :                           struct cli_state **targetcli,
     886             :                           char **pp_targetpath)
     887             : {
     888        2646 :         struct client_dfs_referral *refs = NULL;
     889        2646 :         size_t num_refs = 0;
     890        2646 :         size_t consumed = 0;
     891        2646 :         struct cli_state *cli_ipc = NULL;
     892        2646 :         char *dfs_path = NULL;
     893        2646 :         char *cleanpath = NULL;
     894        2646 :         char *extrapath = NULL;
     895             :         int pathlen;
     896        2646 :         struct cli_state *newcli = NULL;
     897        2646 :         struct cli_state *ccli = NULL;
     898        2646 :         size_t count = 0;
     899        2646 :         char *newpath = NULL;
     900        2646 :         char *newmount = NULL;
     901        2646 :         char *ppath = NULL;
     902             :         SMB_STRUCT_STAT sbuf;
     903             :         uint32_t attributes;
     904             :         NTSTATUS status;
     905        2646 :         struct smbXcli_tcon *target_tcon = NULL;
     906        2646 :         struct cli_dfs_path_split *dfs_refs = NULL;
     907             :         bool ok;
     908        2646 :         bool is_already_dfs = false;
     909             : 
     910        2646 :         if ( !rootcli || !path || !targetcli ) {
     911           0 :                 return NT_STATUS_INVALID_PARAMETER;
     912             :         }
     913             : 
     914             :         /*
     915             :          * Avoid more than one leading directory separator
     916             :          */
     917        2646 :         while (IS_DIRECTORY_SEP(path[0]) && IS_DIRECTORY_SEP(path[1])) {
     918           0 :                 path++;
     919             :         }
     920             : 
     921        2646 :         ok = cli_conn_have_dfs(rootcli);
     922        2646 :         if (!ok) {
     923        1722 :                 *targetcli = rootcli;
     924        1722 :                 *pp_targetpath = talloc_strdup(ctx, path);
     925        1722 :                 if (!*pp_targetpath) {
     926           0 :                         return NT_STATUS_NO_MEMORY;
     927             :                 }
     928        1722 :                 return NT_STATUS_OK;
     929             :         }
     930             : 
     931         924 :         *targetcli = NULL;
     932             : 
     933         924 :         is_already_dfs = cli_dfs_is_already_full_path(rootcli, path);
     934         924 :         if (is_already_dfs) {
     935           0 :                 const char *localpath = NULL;
     936             :                 /*
     937             :                  * Given path is already converted to DFS.
     938             :                  * Convert to a local path so clean_path()
     939             :                  * can correctly strip any wildcards.
     940             :                  */
     941           0 :                 status = cli_dfs_target_check(ctx,
     942             :                                               rootcli,
     943             :                                               path,
     944             :                                               &localpath);
     945           0 :                 if (!NT_STATUS_IS_OK(status)) {
     946           0 :                         return status;
     947             :                 }
     948           0 :                 path = localpath;
     949             :         }
     950             : 
     951             :         /* Send a trans2_query_path_info to check for a referral. */
     952             : 
     953         924 :         cleanpath = clean_path(ctx, path);
     954         924 :         if (!cleanpath) {
     955           0 :                 return NT_STATUS_NO_MEMORY;
     956             :         }
     957             : 
     958         924 :         dfs_path = cli_dfs_make_full_path(ctx, rootcli, cleanpath);
     959         924 :         if (!dfs_path) {
     960           0 :                 return NT_STATUS_NO_MEMORY;
     961             :         }
     962             : 
     963         924 :         status = cli_qpathinfo_basic( rootcli, dfs_path, &sbuf, &attributes);
     964         924 :         if (NT_STATUS_IS_OK(status)) {
     965             :                 /* This is an ordinary path, just return it. */
     966          72 :                 *targetcli = rootcli;
     967          72 :                 *pp_targetpath = talloc_strdup(ctx, path);
     968          72 :                 if (!*pp_targetpath) {
     969           0 :                         return NT_STATUS_NO_MEMORY;
     970             :                 }
     971          72 :                 goto done;
     972             :         }
     973             : 
     974             :         /* Special case where client asked for a path that does not exist */
     975             : 
     976         852 :         if (NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
     977          16 :                 *targetcli = rootcli;
     978          16 :                 *pp_targetpath = talloc_strdup(ctx, path);
     979          16 :                 if (!*pp_targetpath) {
     980           0 :                         return NT_STATUS_NO_MEMORY;
     981             :                 }
     982          16 :                 goto done;
     983             :         }
     984             : 
     985             :         /* We got an error, check for DFS referral. */
     986             : 
     987         836 :         if (!NT_STATUS_EQUAL(status, NT_STATUS_PATH_NOT_COVERED)) {
     988           0 :                 return status;
     989             :         }
     990             : 
     991             :         /* Check for the referral. */
     992             : 
     993         836 :         status = cli_cm_open(ctx,
     994             :                              rootcli,
     995             :                              smbXcli_conn_remote_name(rootcli->conn),
     996             :                              "IPC$",
     997             :                              creds,
     998             :                              NULL, /* dest_ss not needed, we reuse the transport */
     999             :                              0,
    1000             :                              0x20,
    1001             :                              &cli_ipc);
    1002         836 :         if (!NT_STATUS_IS_OK(status)) {
    1003           0 :                 return status;
    1004             :         }
    1005             : 
    1006         836 :         status = cli_dfs_get_referral(ctx, cli_ipc, dfs_path, &refs,
    1007             :                                       &num_refs, &consumed);
    1008         836 :         if (!NT_STATUS_IS_OK(status)) {
    1009           0 :                 return status;
    1010             :         }
    1011             : 
    1012         836 :         if (!num_refs || !refs[0].dfspath) {
    1013           0 :                 return NT_STATUS_NOT_FOUND;
    1014             :         }
    1015             : 
    1016             :         /*
    1017             :          * Bug#10123 - DFS referal entries can be provided in a random order,
    1018             :          * so check the connection cache for each item to avoid unnecessary
    1019             :          * reconnections.
    1020             :          */
    1021         836 :         dfs_refs = talloc_array(ctx, struct cli_dfs_path_split, num_refs);
    1022         836 :         if (dfs_refs == NULL) {
    1023           0 :                 return NT_STATUS_NO_MEMORY;
    1024             :         }
    1025             : 
    1026        1118 :         for (count = 0; count < num_refs; count++) {
    1027        1076 :                 if (!split_dfs_path(dfs_refs, refs[count].dfspath,
    1028        1076 :                                     &dfs_refs[count].server,
    1029        1076 :                                     &dfs_refs[count].share,
    1030        1076 :                                     &dfs_refs[count].extrapath)) {
    1031           0 :                         TALLOC_FREE(dfs_refs);
    1032           0 :                         return NT_STATUS_NOT_FOUND;
    1033             :                 }
    1034             : 
    1035        1076 :                 ccli = cli_cm_find(rootcli, dfs_refs[count].server,
    1036        1076 :                                    dfs_refs[count].share);
    1037        1076 :                 if (ccli != NULL) {
    1038         794 :                         extrapath = dfs_refs[count].extrapath;
    1039         794 :                         *targetcli = ccli;
    1040         794 :                         break;
    1041             :                 }
    1042             :         }
    1043             : 
    1044             :         /*
    1045             :          * If no cached connection was found, then connect to the first live
    1046             :          * referral server in the list.
    1047             :          */
    1048         836 :         for (count = 0; (ccli == NULL) && (count < num_refs); count++) {
    1049             :                 /* Connect to the target server & share */
    1050          42 :                 status = cli_cm_connect(ctx, rootcli,
    1051          42 :                                 dfs_refs[count].server,
    1052          42 :                                 dfs_refs[count].share,
    1053             :                                 creds,
    1054             :                                 NULL, /* dest_ss */
    1055             :                                 0, /* port */
    1056             :                                 0x20,
    1057             :                                 targetcli);
    1058          42 :                 if (!NT_STATUS_IS_OK(status)) {
    1059           0 :                         d_printf("Unable to follow dfs referral [\\%s\\%s]\n",
    1060           0 :                                  dfs_refs[count].server,
    1061           0 :                                  dfs_refs[count].share);
    1062           0 :                         continue;
    1063             :                 } else {
    1064          42 :                         extrapath = dfs_refs[count].extrapath;
    1065          42 :                         break;
    1066             :                 }
    1067             :         }
    1068             : 
    1069             :         /* No available referral server for the connection */
    1070         836 :         if (*targetcli == NULL) {
    1071           0 :                 TALLOC_FREE(dfs_refs);
    1072           0 :                 return status;
    1073             :         }
    1074             : 
    1075             :         /* Make sure to recreate the original string including any wildcards. */
    1076             : 
    1077         836 :         dfs_path = cli_dfs_make_full_path(ctx, rootcli, path);
    1078         836 :         if (!dfs_path) {
    1079           0 :                 TALLOC_FREE(dfs_refs);
    1080           0 :                 return NT_STATUS_NO_MEMORY;
    1081             :         }
    1082         836 :         pathlen = strlen(dfs_path);
    1083         836 :         consumed = MIN(pathlen, consumed);
    1084         836 :         *pp_targetpath = talloc_strdup(ctx, &dfs_path[consumed]);
    1085         836 :         if (!*pp_targetpath) {
    1086           0 :                 TALLOC_FREE(dfs_refs);
    1087           0 :                 return NT_STATUS_NO_MEMORY;
    1088             :         }
    1089         836 :         dfs_path[consumed] = '\0';
    1090             : 
    1091             :         /*
    1092             :          * *pp_targetpath is now the unconsumed part of the path.
    1093             :          * dfs_path is now the consumed part of the path
    1094             :          * (in \server\share\path format).
    1095             :          */
    1096             : 
    1097         836 :         if (extrapath && strlen(extrapath) > 0) {
    1098             :                 /* EMC Celerra NAS version 5.6.50 (at least) doesn't appear to */
    1099             :                 /* put the trailing \ on the path, so to be safe we put one in if needed */
    1100           0 :                 if (extrapath[strlen(extrapath)-1] != '\\' && **pp_targetpath != '\\') {
    1101           0 :                         *pp_targetpath = talloc_asprintf(ctx,
    1102             :                                                   "%s\\%s",
    1103             :                                                   extrapath,
    1104             :                                                   *pp_targetpath);
    1105             :                 } else {
    1106           0 :                         *pp_targetpath = talloc_asprintf(ctx,
    1107             :                                                   "%s%s",
    1108             :                                                   extrapath,
    1109             :                                                   *pp_targetpath);
    1110             :                 }
    1111           0 :                 if (!*pp_targetpath) {
    1112           0 :                         TALLOC_FREE(dfs_refs);
    1113           0 :                         return NT_STATUS_NO_MEMORY;
    1114             :                 }
    1115             :         }
    1116             : 
    1117             :         /* parse out the consumed mount path */
    1118             :         /* trim off the \server\share\ */
    1119             : 
    1120         836 :         ppath = dfs_path;
    1121             : 
    1122         836 :         if (*ppath != '\\') {
    1123           0 :                 d_printf("cli_resolve_path: "
    1124             :                         "dfs_path (%s) not in correct format.\n",
    1125             :                         dfs_path );
    1126           0 :                 TALLOC_FREE(dfs_refs);
    1127           0 :                 return NT_STATUS_NOT_FOUND;
    1128             :         }
    1129             : 
    1130         836 :         ppath++; /* Now pointing at start of server name. */
    1131             : 
    1132         836 :         if ((ppath = strchr_m( dfs_path, '\\' )) == NULL) {
    1133           0 :                 TALLOC_FREE(dfs_refs);
    1134           0 :                 return NT_STATUS_NOT_FOUND;
    1135             :         }
    1136             : 
    1137         836 :         ppath++; /* Now pointing at start of share name. */
    1138             : 
    1139         836 :         if ((ppath = strchr_m( ppath+1, '\\' )) == NULL) {
    1140           0 :                 TALLOC_FREE(dfs_refs);
    1141           0 :                 return NT_STATUS_NOT_FOUND;
    1142             :         }
    1143             : 
    1144         836 :         ppath++; /* Now pointing at path component. */
    1145             : 
    1146         836 :         newmount = talloc_asprintf(ctx, "%s\\%s", mountpt, ppath );
    1147         836 :         if (!newmount) {
    1148           0 :                 TALLOC_FREE(dfs_refs);
    1149           0 :                 return NT_STATUS_NOT_FOUND;
    1150             :         }
    1151             : 
    1152             :         /* Check for another dfs referral, note that we are not
    1153             :            checking for loops here. */
    1154             : 
    1155         836 :         if (!strequal(*pp_targetpath, "\\") && !strequal(*pp_targetpath, "/")) {
    1156         800 :                 status = cli_resolve_path(ctx,
    1157             :                                           newmount,
    1158             :                                           creds,
    1159             :                                           *targetcli,
    1160             :                                           *pp_targetpath,
    1161             :                                           &newcli,
    1162             :                                           &newpath);
    1163         800 :                 if (NT_STATUS_IS_OK(status)) {
    1164             :                         /*
    1165             :                          * When cli_resolve_path returns true here it's always
    1166             :                          * returning the complete path in newpath, so we're done
    1167             :                          * here.
    1168             :                          */
    1169         800 :                         *targetcli = newcli;
    1170         800 :                         *pp_targetpath = newpath;
    1171         800 :                         TALLOC_FREE(dfs_refs);
    1172         800 :                         return status;
    1173             :                 }
    1174             :         }
    1175             : 
    1176          36 :   done:
    1177             : 
    1178         124 :         if (smbXcli_conn_protocol((*targetcli)->conn) >= PROTOCOL_SMB2_02) {
    1179         124 :                 target_tcon = (*targetcli)->smb2.tcon;
    1180             :         } else {
    1181           0 :                 target_tcon = (*targetcli)->smb1.tcon;
    1182             :         }
    1183             : 
    1184             :         /* If returning true ensure we return a dfs root full path. */
    1185         124 :         if (smbXcli_tcon_is_dfs_share(target_tcon)) {
    1186          96 :                 dfs_path = talloc_strdup(ctx, *pp_targetpath);
    1187          96 :                 if (!dfs_path) {
    1188           0 :                         TALLOC_FREE(dfs_refs);
    1189           0 :                         return NT_STATUS_NO_MEMORY;
    1190             :                 }
    1191          96 :                 *pp_targetpath = cli_dfs_make_full_path(ctx, *targetcli, dfs_path);
    1192          96 :                 if (*pp_targetpath == NULL) {
    1193           0 :                         TALLOC_FREE(dfs_refs);
    1194           0 :                         return NT_STATUS_NO_MEMORY;
    1195             :                 }
    1196             :         }
    1197             : 
    1198         124 :         TALLOC_FREE(dfs_refs);
    1199         124 :         return NT_STATUS_OK;
    1200             : }
    1201             : 
    1202             : /********************************************************************
    1203             : ********************************************************************/
    1204             : 
    1205         907 : bool cli_check_msdfs_proxy(TALLOC_CTX *ctx,
    1206             :                                 struct cli_state *cli,
    1207             :                                 const char *sharename,
    1208             :                                 char **pp_newserver,
    1209             :                                 char **pp_newshare,
    1210             :                                 struct cli_credentials *creds)
    1211             : {
    1212         907 :         struct client_dfs_referral *refs = NULL;
    1213         907 :         size_t num_refs = 0;
    1214         907 :         size_t consumed = 0;
    1215         907 :         char *fullpath = NULL;
    1216             :         bool res;
    1217         907 :         struct smbXcli_tcon *orig_tcon = NULL;
    1218         907 :         char *orig_share = NULL;
    1219         907 :         char *newextrapath = NULL;
    1220             :         NTSTATUS status;
    1221             :         const char *remote_name;
    1222             :         enum smb_encryption_setting encryption_state =
    1223         907 :                 cli_credentials_get_smb_encryption(creds);
    1224             : 
    1225         907 :         if (!cli || !sharename) {
    1226           0 :                 return false;
    1227             :         }
    1228             : 
    1229         907 :         remote_name = smbXcli_conn_remote_name(cli->conn);
    1230             : 
    1231             :         /* special case.  never check for a referral on the IPC$ share */
    1232             : 
    1233         907 :         if (strequal(sharename, "IPC$")) {
    1234          82 :                 return false;
    1235             :         }
    1236             : 
    1237             :         /* send a trans2_query_path_info to check for a referral */
    1238             : 
    1239         825 :         fullpath = talloc_asprintf(ctx, "\\%s\\%s", remote_name, sharename);
    1240         825 :         if (!fullpath) {
    1241           0 :                 return false;
    1242             :         }
    1243             : 
    1244             :         /* Store tcon state. */
    1245         825 :         if (cli_state_has_tcon(cli)) {
    1246           0 :                 cli_state_save_tcon_share(cli, &orig_tcon, &orig_share);
    1247             :         }
    1248             : 
    1249             :         /* check for the referral */
    1250             : 
    1251         825 :         if (!NT_STATUS_IS_OK(cli_tree_connect(cli, "IPC$", "IPC", NULL))) {
    1252           0 :                 cli_state_restore_tcon_share(cli, orig_tcon, orig_share);
    1253           0 :                 return false;
    1254             :         }
    1255             : 
    1256         825 :         if (encryption_state >= SMB_ENCRYPTION_DESIRED) {
    1257           6 :                 status = cli_cm_force_encryption_creds(cli, creds, "IPC$");
    1258           6 :                 if (!NT_STATUS_IS_OK(status)) {
    1259           0 :                         switch (encryption_state) {
    1260           0 :                         case SMB_ENCRYPTION_DESIRED:
    1261           0 :                                 break;
    1262           0 :                         case SMB_ENCRYPTION_REQUIRED:
    1263             :                         default:
    1264             :                                 /*
    1265             :                                  * Failed to set up encryption.
    1266             :                                  * Disconnect the temporary IPC$
    1267             :                                  * tcon before restoring the original
    1268             :                                  * tcon so we don't leak it.
    1269             :                                  */
    1270           0 :                                 cli_tdis(cli);
    1271           0 :                                 cli_state_restore_tcon_share(cli,
    1272             :                                                              orig_tcon,
    1273             :                                                              orig_share);
    1274           0 :                                 return false;
    1275             :                         }
    1276             :                 }
    1277             :         }
    1278             : 
    1279         825 :         status = cli_dfs_get_referral(ctx, cli, fullpath, &refs,
    1280             :                                       &num_refs, &consumed);
    1281         825 :         res = NT_STATUS_IS_OK(status);
    1282             : 
    1283         825 :         status = cli_tdis(cli);
    1284             : 
    1285         825 :         cli_state_restore_tcon_share(cli, orig_tcon, orig_share);
    1286             : 
    1287         825 :         if (!NT_STATUS_IS_OK(status)) {
    1288           0 :                 return false;
    1289             :         }
    1290             : 
    1291         825 :         if (!res || !num_refs) {
    1292         781 :                 return false;
    1293             :         }
    1294             : 
    1295          44 :         if (!refs[0].dfspath) {
    1296           0 :                 return false;
    1297             :         }
    1298             : 
    1299          44 :         if (!split_dfs_path(ctx, refs[0].dfspath, pp_newserver,
    1300             :                             pp_newshare, &newextrapath)) {
    1301           0 :                 return false;
    1302             :         }
    1303             : 
    1304             :         /* check that this is not a self-referral */
    1305             : 
    1306          88 :         if (strequal(remote_name, *pp_newserver) &&
    1307          44 :                         strequal(sharename, *pp_newshare)) {
    1308          44 :                 return false;
    1309             :         }
    1310             : 
    1311           0 :         return true;
    1312             : }
    1313             : 
    1314             : /********************************************************************
    1315             :  Windows and NetApp (and arguably the SMB1/2/3 specs) expect a non-DFS
    1316             :  path for the targets of rename and hardlink. If we have been given
    1317             :  a DFS path for these calls, convert it back into a local path by
    1318             :  stripping off the DFS prefix.
    1319             : ********************************************************************/
    1320             : 
    1321          25 : NTSTATUS cli_dfs_target_check(TALLOC_CTX *mem_ctx,
    1322             :                         struct cli_state *cli,
    1323             :                         const char *fname_dst,
    1324             :                         const char **fname_dst_out)
    1325             : {
    1326          25 :         char *dfs_prefix = NULL;
    1327          25 :         size_t prefix_len = 0;
    1328          25 :         struct smbXcli_tcon *tcon = NULL;
    1329             : 
    1330          25 :         if (!smbXcli_conn_dfs_supported(cli->conn)) {
    1331           0 :                 goto copy_fname_out;
    1332             :         }
    1333          25 :         if (smbXcli_conn_protocol(cli->conn) >= PROTOCOL_SMB2_02) {
    1334          25 :                 tcon = cli->smb2.tcon;
    1335             :         } else {
    1336           0 :                 tcon = cli->smb1.tcon;
    1337             :         }
    1338          25 :         if (!smbXcli_tcon_is_dfs_share(tcon)) {
    1339          13 :                 goto copy_fname_out;
    1340             :         }
    1341          12 :         dfs_prefix = cli_dfs_make_full_path(mem_ctx, cli, "");
    1342          12 :         if (dfs_prefix == NULL) {
    1343           0 :                 return NT_STATUS_NO_MEMORY;
    1344             :         }
    1345          12 :         prefix_len = strlen(dfs_prefix);
    1346          12 :         if (strncmp(fname_dst, dfs_prefix, prefix_len) != 0) {
    1347             :                 /*
    1348             :                  * Prefix doesn't match. Assume it was
    1349             :                  * already stripped or not added in the
    1350             :                  * first place.
    1351             :                  */
    1352           4 :                 goto copy_fname_out;
    1353             :         }
    1354             :         /* Return the trailing name after the prefix. */
    1355           8 :         *fname_dst_out = &fname_dst[prefix_len];
    1356           8 :         TALLOC_FREE(dfs_prefix);
    1357           8 :         return NT_STATUS_OK;
    1358             : 
    1359          17 :   copy_fname_out:
    1360             : 
    1361             :         /*
    1362             :          * No change to the destination name. Just
    1363             :          * point it at the incoming destination name.
    1364             :          */
    1365          17 :         *fname_dst_out = fname_dst;
    1366          17 :         TALLOC_FREE(dfs_prefix);
    1367          17 :         return NT_STATUS_OK;
    1368             : }
    1369             : 
    1370             : /********************************************************************
    1371             :  Convert a pathname into a DFS path if it hasn't already been converted.
    1372             :  Always returns a talloc'ed path, makes it easy to pass const paths in.
    1373             : ********************************************************************/
    1374             : 
    1375         499 : char *smb1_dfs_share_path(TALLOC_CTX *ctx,
    1376             :                           struct cli_state *cli,
    1377             :                           const char *path)
    1378             : {
    1379         998 :         bool is_dfs = smbXcli_conn_dfs_supported(cli->conn) &&
    1380         499 :                         smbXcli_tcon_is_dfs_share(cli->smb1.tcon);
    1381         499 :         bool is_already_dfs_path = false;
    1382         499 :         bool posix = (cli->requested_posix_capabilities &
    1383             :                         CIFS_UNIX_POSIX_PATHNAMES_CAP);
    1384         499 :         char sepchar = (posix ? '/' : '\\');
    1385             : 
    1386         499 :         if (!is_dfs) {
    1387         499 :                 return talloc_strdup(ctx, path);
    1388             :         }
    1389           0 :         is_already_dfs_path = cli_dfs_is_already_full_path(cli, path);
    1390           0 :         if (is_already_dfs_path) {
    1391           0 :                 return talloc_strdup(ctx, path);
    1392             :         }
    1393             :         /*
    1394             :          * We don't use cli_dfs_make_full_path() as,
    1395             :          * when given a null path, cli_dfs_make_full_path
    1396             :          * deliberately adds a trailing '\\' (this is by
    1397             :          * design to check for an existing DFS prefix match).
    1398             :          */
    1399           0 :         if (path[0] == '\0') {
    1400           0 :                 return talloc_asprintf(ctx,
    1401             :                                        "%c%s%c%s",
    1402             :                                        sepchar,
    1403             :                                        smbXcli_conn_remote_name(cli->conn),
    1404             :                                        sepchar,
    1405             :                                        cli->share);
    1406             :         }
    1407           0 :         while (*path == sepchar) {
    1408           0 :                 path++;
    1409             :         }
    1410           0 :         return talloc_asprintf(ctx,
    1411             :                                "%c%s%c%s%c%s",
    1412             :                                sepchar,
    1413             :                                smbXcli_conn_remote_name(cli->conn),
    1414             :                                sepchar,
    1415             :                                cli->share,
    1416             :                                sepchar,
    1417             :                                path);
    1418             : }

Generated by: LCOV version 1.14