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

Generated by: LCOV version 1.14