/*
Unix SMB/CIFS implementation.
client connect/disconnect routines
Copyright (C) Andrew Tridgell 1994-1998
Copyright (C) Gerald (Jerry) Carter 2004
Copyright (C) Jeremy Allison 2007-2009
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "includes.h"
#include "source3/include/client.h"
#include "source3/libsmb/proto.h"
#include "libsmb/clirap.h"
#include "msdfs.h"
#include "trans2.h"
#include "libsmb/nmblib.h"
#include "../libcli/smb/smbXcli_base.h"
#include "auth/credentials/credentials.h"
#include "lib/param/param.h"
#include "libcli/smb/smb2_negotiate_context.h"
/********************************************************************
Important point.
DFS paths are *always* of the form \server\share\<pathname> (the \ characters
are not C escaped here).
- but if we're using POSIX paths then <pathname> may contain
'/' separators, not '\\' separators. So cope with '\\' or '/'
as a separator when looking at the pathname part.... JRA.
********************************************************************/
/********************************************************************
Ensure a connection is encrypted.
********************************************************************/
static NTSTATUS cli_cm_force_encryption_creds(struct cli_state *c,
struct cli_credentials *creds,
const char *sharename)
{
uint16_t major, minor;
uint32_t caplow, caphigh;
NTSTATUS status;
bool temp_ipc = false;
if (smbXcli_conn_protocol(c->conn) >= PROTOCOL_SMB2_02) {
status = smb2cli_session_encryption_on(c->smb2.session);
if (NT_STATUS_EQUAL(status,NT_STATUS_NOT_SUPPORTED)) {
d_printf("Encryption required and "
"server doesn't support "
"SMB3 encryption - failing connect\n");
} else if (!NT_STATUS_IS_OK(status)) {
d_printf("Encryption required and "
"setup failed with error %s.\n",
nt_errstr(status));
}
return status;
}
if (!SERVER_HAS_UNIX_CIFS(c)) {
d_printf("Encryption required and "
"server that doesn't support "
"UNIX extensions - failing connect\n");
return NT_STATUS_NOT_SUPPORTED;
}
if (c->smb1.tcon == NULL) {
status = cli_tree_connect_creds(c, "IPC$", "IPC", creds);
if (!NT_STATUS_IS_OK(status)) {
d_printf("Encryption required and "
"can't connect to IPC$ to check "
"UNIX CIFS extensions.\n");
return NT_STATUS_UNKNOWN_REVISION;
}
temp_ipc = true;
}
status = cli_unix_extensions_version(c, &major, &minor, &caplow,
&caphigh);
if (!NT_STATUS_IS_OK(status)) {
d_printf("Encryption required and "
"can't get UNIX CIFS extensions "
"version from server.\n");
if (temp_ipc) {
cli_tdis(c);
}
return NT_STATUS_UNKNOWN_REVISION;
}
if (!(caplow & CIFS_UNIX_TRANSPORT_ENCRYPTION_CAP)) {
d_printf("Encryption required and "
"share %s doesn't support "
"encryption.\n", sharename);
if (temp_ipc) {
cli_tdis(c);
}
return NT_STATUS_UNSUPPORTED_COMPRESSION;
}
status = cli_smb1_setup_encryption(c, creds);
if (!NT_STATUS_IS_OK(status)) {
d_printf("Encryption required and "
"setup failed with error %s.\n",
nt_errstr(status));
if (temp_ipc) {
cli_tdis(c);
}
return status;
}
if (temp_ipc) {
cli_tdis(c);
}
return NT_STATUS_OK;
}
/********************************************************************
Return a connection to a server.
********************************************************************/
static NTSTATUS do_connect(TALLOC_CTX *ctx,
const char *server,
const char *share,
struct cli_credentials *creds,
const struct sockaddr_storage *dest_ss,
const struct smb_transports *transports,
int name_type,
struct cli_state **pcli)
{
struct cli_state *c = NULL;
char *servicename;
char *sharename;
char *newserver, *newshare;
NTSTATUS status;
int flags = 0;
enum protocol_types protocol = PROTOCOL_NONE;
enum smb_signing_setting signing_state =
cli_credentials_get_smb_signing(creds);
enum smb_encryption_setting encryption_state =
cli_credentials_get_smb_encryption(creds);
struct smb2_negotiate_contexts *in_contexts = NULL;
if (encryption_state >= SMB_ENCRYPTION_DESIRED) {
signing_state = SMB_SIGNING_REQUIRED;
}
/* make a copy so we don't modify the global string 'service' */
servicename = talloc_strdup(ctx,share);
if (!servicename) {
return NT_STATUS_NO_MEMORY;
}
sharename = servicename;
if (*sharename == '\\') {
sharename += 2;
if (server == NULL) {
server = sharename;
}
sharename = strchr_m(sharename,'\\');
if (!sharename) {
return NT_STATUS_NO_MEMORY;
}
*sharename = 0;
sharename++;
}
if (server == NULL) {
return NT_STATUS_INVALID_PARAMETER;
}
/*
* The functions cli_resolve_path() and cli_cm_open() might not create a
* new cli context, but might return an already existing one. This
* requires that a long living context is used. It should be freed after
* the client is done with its connection.
*/
status = cli_connect_nb(ctx,
server,
dest_ss,
transports,
name_type,
NULL,
signing_state,
flags,
&c);
if (!NT_STATUS_IS_OK(status)) {
if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_SUPPORTED)) {
DBG_ERR("NetBIOS support disabled, unable to connect\n");
}
DBG_WARNING("Connection to %s failed (Error %s)\n",
server,
nt_errstr(status));
return status;
}
DEBUG(4,(" session request ok\n"));
in_contexts = talloc_zero(ctx, struct smb2_negotiate_contexts);
if (in_contexts == NULL) {
return NT_STATUS_NO_MEMORY;
}
status = smb2_negotiate_context_add(
in_contexts,
in_contexts,
SMB2_POSIX_EXTENSIONS_AVAILABLE,
(const uint8_t *)SMB2_CREATE_TAG_POSIX,
strlen(SMB2_CREATE_TAG_POSIX));
if (!NT_STATUS_IS_OK(status)) {
return status;
}
status = smbXcli_negprot(c->conn,
c->timeout,
lp_client_min_protocol(),
lp_client_max_protocol(),
in_contexts,
NULL,
NULL);
TALLOC_FREE(in_contexts);
if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT)) {
d_printf("Protocol negotiation (with timeout %d ms) timed out against server %s\n",
c->timeout,
smbXcli_conn_remote_name(c->conn));
cli_shutdown(c);
return status;
}
if (!NT_STATUS_IS_OK(status)) {
d_printf("Protocol negotiation to server %s (for a protocol between %s and %s) failed: %s\n",
smbXcli_conn_remote_name(c->conn),
lpcfg_get_smb_protocol(lp_client_min_protocol()),
lpcfg_get_smb_protocol(lp_client_max_protocol()),
nt_errstr(st
|