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

Generated by: LCOV version 1.14