#include "includes.h"
#include "libsmb/libsmb.h"
+#include "libsmb/namequery.h"
#include "auth_info.h"
#include "../libcli/auth/libcli_auth.h"
#include "../libcli/auth/spnego.h"
const char *user_account = NULL;
const char *user_domain = NULL;
const char *pass = NULL;
+ char *canon_principal = NULL;
+ char *canon_realm = NULL;
const char *target_hostname = NULL;
- const DATA_BLOB *server_blob = NULL;
- bool got_kerberos_mechanism = false;
enum credentials_use_kerberos krb5_state;
bool try_kerberos = false;
bool need_kinit = false;
bool auth_requested = true;
int ret;
+ bool ok;
target_hostname = smbXcli_conn_remote_name(cli->conn);
- server_blob = smbXcli_conn_server_gss_blob(cli->conn);
-
- /* the server might not even do spnego */
- if (server_blob != NULL && server_blob->length != 0) {
- char *OIDs[ASN1_MAX_OIDS] = { NULL, };
- size_t i;
- bool ok;
-
- /*
- * The server sent us the first part of the SPNEGO exchange in the
- * negprot reply. It is WRONG to depend on the principal sent in the
- * negprot reply, but right now we do it. If we don't receive one,
- * we try to best guess, then fall back to NTLM.
- */
- ok = spnego_parse_negTokenInit(frame,
- *server_blob,
- OIDs,
- NULL,
- NULL);
- if (!ok) {
- TALLOC_FREE(frame);
- return NT_STATUS_INVALID_PARAMETER;
- }
- if (OIDs[0] == NULL) {
- TALLOC_FREE(frame);
- return NT_STATUS_INVALID_PARAMETER;
- }
-
- /* make sure the server understands kerberos */
- for (i = 0; OIDs[i] != NULL; i++) {
- if (i == 0) {
- DEBUG(3,("got OID=%s\n", OIDs[i]));
- } else {
- DEBUGADD(3,("got OID=%s\n", OIDs[i]));
- }
-
- if (strcmp(OIDs[i], OID_KERBEROS5_OLD) == 0 ||
- strcmp(OIDs[i], OID_KERBEROS5) == 0) {
- got_kerberos_mechanism = true;
- break;
- }
- }
- }
auth_requested = cli_credentials_authentication_requested(creds);
if (auth_requested) {
+ errno = 0;
user_principal = cli_credentials_get_principal(creds, frame);
- if (user_principal == NULL) {
+ if (errno != 0) {
TALLOC_FREE(frame);
return NT_STATUS_NO_MEMORY;
}
try_kerberos = true;
}
+ if (user_principal == NULL) {
+ try_kerberos = false;
+ }
+
if (target_hostname == NULL) {
try_kerberos = false;
} else if (is_ipaddress(target_hostname)) {
need_kinit = false;
} else if (krb5_state == CRED_MUST_USE_KERBEROS) {
need_kinit = try_kerberos;
- } else if (!got_kerberos_mechanism) {
- /*
- * Most likely the server doesn't support
- * Kerberos, don't waste time doing a kinit
- */
- need_kinit = false;
} else {
need_kinit = try_kerberos;
}
return NT_STATUS_OK;
}
+ DBG_INFO("Doing kinit for %s to access %s\n",
+ user_principal, target_hostname);
/*
* TODO: This should be done within the gensec layer
* only if required!
*/
setenv(KRB5_ENV_CCNAME, "MEMORY:cliconnect", 1);
- ret = kerberos_kinit_password(user_principal, pass,
- 0 /* no time correction for now */,
- NULL);
+ ret = kerberos_kinit_password_ext(user_principal,
+ pass,
+ 0,
+ 0,
+ 0,
+ NULL,
+ false,
+ false,
+ 0,
+ frame,
+ &canon_principal,
+ &canon_realm,
+ NULL);
if (ret != 0) {
- int dbglvl = DBGLVL_WARNING;
+ int dbglvl = DBGLVL_NOTICE;
if (krb5_state == CRED_MUST_USE_KERBEROS) {
dbglvl = DBGLVL_ERR;
/*
* Ignore the error and hope that NTLM will work
*/
+ TALLOC_FREE(frame);
+ return NT_STATUS_OK;
+ }
+
+ ok = cli_credentials_set_principal(creds,
+ canon_principal,
+ CRED_SPECIFIED);
+ if (!ok) {
+ TALLOC_FREE(frame);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ ok = cli_credentials_set_realm(creds,
+ canon_realm,
+ CRED_SPECIFIED);
+ if (!ok) {
+ TALLOC_FREE(frame);
+ return NT_STATUS_NO_MEMORY;
}
+ DBG_DEBUG("Successfully authenticated as %s (%s) to access %s using "
+ "Kerberos\n",
+ user_principal,
+ canon_principal,
+ target_hostname);
+
TALLOC_FREE(frame);
return NT_STATUS_OK;
}
+static NTSTATUS cli_state_update_after_sesssetup(struct cli_state *cli,
+ const char *native_os,
+ const char *native_lm,
+ const char *primary_domain)
+{
+#define _VALID_STR(p) ((p) != NULL && (p)[0] != '\0')
+
+ if (!_VALID_STR(cli->server_os) && _VALID_STR(native_os)) {
+ cli->server_os = talloc_strdup(cli, native_os);
+ if (cli->server_os == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ }
+
+ if (!_VALID_STR(cli->server_type) && _VALID_STR(native_lm)) {
+ cli->server_type = talloc_strdup(cli, native_lm);
+ if (cli->server_type == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ }
+
+ if (!_VALID_STR(cli->server_domain) && _VALID_STR(primary_domain)) {
+ cli->server_domain = talloc_strdup(cli, primary_domain);
+ if (cli->server_domain == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ }
+
+#undef _VALID_STRING
+ return NT_STATUS_OK;
+}
+
/********************************************************
Utility function to ensure we always return at least
a valid char * pointer to an empty string for the
tevent_req_nterror(req, status);
return;
}
- p += ret;
tevent_req_done(req);
}
subreq, struct tevent_req);
struct cli_sesssetup_blob_state *state = tevent_req_data(
req, struct cli_sesssetup_blob_state);
- struct cli_state *cli = state->cli;
NTSTATUS status;
if (smbXcli_conn_protocol(state->cli->conn) >= PROTOCOL_SMB2_02) {
return;
}
- if (cli->server_os == NULL) {
- cli->server_os = talloc_move(cli, &state->out_native_os);
- }
- if (cli->server_type == NULL) {
- cli->server_type = talloc_move(cli, &state->out_native_lm);
- }
-
state->status = status;
+ status = cli_state_update_after_sesssetup(state->cli,
+ state->out_native_os,
+ state->out_native_lm,
+ NULL);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+
if (state->blob.length != 0) {
/*
* More to send
cli_session_setup_gensec_local_next(req);
}
+static void cli_session_dump_keys(TALLOC_CTX *mem_ctx,
+ struct smbXcli_session *session,
+ DATA_BLOB session_key)
+{
+ NTSTATUS status;
+ DATA_BLOB sig = data_blob_null;
+ DATA_BLOB app = data_blob_null;
+ DATA_BLOB enc = data_blob_null;
+ DATA_BLOB dec = data_blob_null;
+ uint64_t sid = smb2cli_session_current_id(session);
+
+ status = smb2cli_session_signing_key(session, mem_ctx, &sig);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto out;
+ }
+ status = smbXcli_session_application_key(session, mem_ctx, &app);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto out;
+ }
+ status = smb2cli_session_encryption_key(session, mem_ctx, &enc);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto out;
+ }
+ status = smb2cli_session_decryption_key(session, mem_ctx, &dec);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto out;
+ }
+
+ DEBUG(0, ("debug encryption: dumping generated session keys\n"));
+ DEBUGADD(0, ("Session Id "));
+ dump_data(0, (uint8_t*)&sid, sizeof(sid));
+ DEBUGADD(0, ("Session Key "));
+ dump_data(0, session_key.data, session_key.length);
+ DEBUGADD(0, ("Signing Key "));
+ dump_data(0, sig.data, sig.length);
+ DEBUGADD(0, ("App Key "));
+ dump_data(0, app.data, app.length);
+
+ /* In client code, ServerIn is the encryption key */
+
+ DEBUGADD(0, ("ServerIn Key "));
+ dump_data(0, enc.data, enc.length);
+ DEBUGADD(0, ("ServerOut Key "));
+ dump_data(0, dec.data, dec.length);
+
+out:
+ data_blob_clear_free(&sig);
+ data_blob_clear_free(&app);
+ data_blob_clear_free(&enc);
+ data_blob_clear_free(&dec);
+}
+
static void cli_session_setup_gensec_ready(struct tevent_req *req)
{
struct cli_session_setup_gensec_state *state =
if (tevent_req_nterror(req, status)) {
return;
}
+ if (smbXcli_conn_protocol(state->cli->conn) >= PROTOCOL_SMB3_00
+ && lp_debug_encryption())
+ {
+ cli_session_dump_keys(state, session, state->session_key);
+ }
} else {
struct smbXcli_session *session = state->cli->smb1.session;
bool active;
status = cli_session_creds_prepare_krb5(cli, creds);
if (tevent_req_nterror(req, status)) {
- return tevent_req_post(req, ev);;
+ return tevent_req_post(req, ev);
}
+ DBG_INFO("Connect to %s as %s using SPNEGO\n",
+ target_hostname,
+ cli_credentials_get_principal(creds, talloc_tos()));
+
subreq = cli_session_setup_gensec_send(state, ev, cli, creds,
target_service, target_hostname);
if (tevent_req_nomem(subreq, req)) {
* We only call data_blob_clear() as
* some of the blobs point to the same memory.
*
- * We let the talloc hierachy free the memory.
+ * We let the talloc hierarchy free the memory.
*/
data_blob_clear(&state->apassword_blob);
data_blob_clear(&state->upassword_blob);
return tevent_req_post(req, ev);
}
+ DBG_INFO("Connect to %s as %s using NTLM\n", domain, username);
+
if ((sec_mode & NEGOTIATE_SECURITY_CHALLENGE_RESPONSE) == 0) {
bool use_unicode = smbXcli_conn_use_unicode(cli->conn);
uint8_t *bytes = NULL;
return;
}
- if (cli->server_os == NULL) {
- cli->server_os = talloc_move(cli, &state->out_native_os);
- }
- if (cli->server_type == NULL) {
- cli->server_type = talloc_move(cli, &state->out_native_lm);
- }
- if (cli->server_domain == NULL) {
- cli->server_domain = talloc_move(cli, &state->out_primary_domain);
+ status = cli_state_update_after_sesssetup(state->cli,
+ state->out_native_os,
+ state->out_native_lm,
+ state->out_primary_domain);
+ if (tevent_req_nterror(req, status)) {
+ return;
}
ok = smb1cli_conn_activate_signing(cli->conn,
subreq, struct tevent_req);
struct cli_session_setup_creds_state *state = tevent_req_data(
req, struct cli_session_setup_creds_state);
- struct cli_state *cli = state->cli;
NTSTATUS status;
status = smb1cli_session_setup_lm21_recv(subreq, state,
return;
}
- if (cli->server_os == NULL) {
- cli->server_os = talloc_move(cli, &state->out_native_os);
- }
- if (cli->server_type == NULL) {
- cli->server_type = talloc_move(cli, &state->out_native_lm);
+ status = cli_state_update_after_sesssetup(state->cli,
+ state->out_native_os,
+ state->out_native_lm,
+ NULL);
+ if (tevent_req_nterror(req, status)) {
+ return;
}
tevent_req_done(req);
NTSTATUS cli_session_setup_anon(struct cli_state *cli)
{
- NTSTATUS status = NT_STATUS_NO_MEMORY;
+ NTSTATUS status;
struct cli_credentials *creds = NULL;
creds = cli_credentials_init_anon(cli);
state->cli = cli;
vwv = state->vwv;
+ TALLOC_FREE(cli->smb1.tcon);
+ cli->smb1.tcon = smbXcli_tcon_create(cli);
+ if (tevent_req_nomem(cli->smb1.tcon, req)) {
+ return tevent_req_post(req, ev);
+ }
+ smb1cli_tcon_set_id(cli->smb1.tcon, UINT16_MAX);
+
cli->share = talloc_strdup(cli, share);
if (!cli->share) {
return NULL;
if (smbXcli_conn_protocol(cli->conn) >= PROTOCOL_SMB2_02) {
char *unc;
+ TALLOC_FREE(cli->smb2.tcon);
cli->smb2.tcon = smbXcli_tcon_create(cli);
if (tevent_req_nomem(cli->smb2.tcon, req)) {
return tevent_req_post(req, ev);
tevent_req_nterror(req, status);
return;
}
- cli_state_set_tid(state->cli, UINT16_MAX);
+ TALLOC_FREE(state->cli->smb1.tcon);
tevent_req_done(req);
}
NTSTATUS status = NT_STATUS_NO_MEMORY;
if (smbXcli_conn_protocol(cli->conn) >= PROTOCOL_SMB2_02) {
- return smb2cli_tdis(cli->conn,
+ status = smb2cli_tdis(cli->conn,
cli->timeout,
cli->smb2.session,
cli->smb2.tcon);
+ if (NT_STATUS_IS_OK(status)) {
+ TALLOC_FREE(cli->smb2.tcon);
+ }
+ return status;
}
if (smbXcli_conn_has_async_calls(cli->conn)) {
{
struct tevent_req *req, *subreq;
struct cli_connect_sock_state *state;
- const char *prog;
struct sockaddr_storage *addrs;
unsigned i, num_addrs;
NTSTATUS status;
return NULL;
}
- prog = getenv("LIBSMB_PROG");
- if (prog != NULL) {
- state->fd = sock_exec(prog);
- if (state->fd == -1) {
- status = map_nt_error_from_unix(errno);
- tevent_req_nterror(req, status);
- } else {
- state->port = 0;
- tevent_req_done(req);
- }
- return tevent_req_post(req, ev);
- }
-
if ((pss == NULL) || is_zero_addr(pss)) {
/*
return;
}
- state->cli = cli_state_create(state, fd, state->desthost, NULL,
+ state->cli = cli_state_create(state, fd, state->desthost,
state->signing_state, state->flags);
if (tevent_req_nomem(state->cli, req)) {
close(fd);
state->max_protocol = lp_client_max_protocol();
}
+ if (flags & CLI_FULL_CONNECTION_FORCE_SMB1) {
+ state->max_protocol = MIN(state->max_protocol, PROTOCOL_NT1);
+ }
+
+ if (flags & CLI_FULL_CONNECTION_DISABLE_SMB1) {
+ state->min_protocol = MAX(state->max_protocol, PROTOCOL_SMB2_02);
+ state->max_protocol = MAX(state->max_protocol, PROTOCOL_LATEST);
+ }
+
subreq = cli_connect_nb_send(state, ev, dest_host, dest_ss, port,
0x20, my_name, signing_state, flags);
if (tevent_req_nomem(subreq, req)) {
return tevent_req_post(req, ev);
}
+ TALLOC_FREE(cli->smb1.tcon);
+ cli->smb1.tcon = smbXcli_tcon_create(cli);
+ if (tevent_req_nomem(cli->smb1.tcon, req)) {
+ return tevent_req_post(req, ev);
+ }
+ smb1cli_tcon_set_id(cli->smb1.tcon, UINT16_MAX);
+
bytes = talloc_array(state, uint8_t, 0);
bytes = smb_bytes_push_bytes(bytes, 4, NULL, 0);
bytes = smb_bytes_push_str(bytes, smbXcli_conn_use_unicode(cli->conn),
flags |= CLI_FULL_CONNECTION_USE_KERBEROS;
}
+ flags |= CLI_FULL_CONNECTION_FORCE_SMB1;
+
nt_status = cli_full_connection(&cli, NULL, server, server_ss, 0, "IPC$", "IPC",
get_cmdline_auth_info_username(user_info),
lp_workgroup(),
return cli;
}
-
-/*
- * Return the IP address and workgroup of a master browser on the network, and
- * connect to it.
- */
-
-struct cli_state *get_ipc_connect_master_ip_bcast(TALLOC_CTX *ctx,
- const struct user_auth_info *user_info,
- char **pp_workgroup_out)
-{
- struct sockaddr_storage *ip_list;
- struct cli_state *cli;
- int i, count;
- NTSTATUS status;
-
- *pp_workgroup_out = NULL;
-
- DEBUG(99, ("Do broadcast lookup for workgroups on local network\n"));
-
- /* Go looking for workgroups by broadcasting on the local network */
-
- status = name_resolve_bcast(MSBROWSE, 1, talloc_tos(),
- &ip_list, &count);
- if (!NT_STATUS_IS_OK(status)) {
- DEBUG(99, ("No master browsers responded: %s\n",
- nt_errstr(status)));
- return NULL;
- }
-
- for (i = 0; i < count; i++) {
- char addr[INET6_ADDRSTRLEN];
- print_sockaddr(addr, sizeof(addr), &ip_list[i]);
- DEBUG(99, ("Found master browser %s\n", addr));
-
- cli = get_ipc_connect_master_ip(ctx, &ip_list[i],
- user_info, pp_workgroup_out);
- if (cli)
- return(cli);
- }
-
- return NULL;
-}