contexts....
Jeremy.
struct smb_trans_enc_state {
enum smb_trans_enc_type smb_enc_type;
+ uint16 enc_ctx_num;
BOOL enc_on;
union {
NTLMSSP_STATE *ntlmssp_state;
const krb5_principal server,
krb5_data *reply);
-/* Call for SMB transport encryption. */
-#if defined(HAVE_GSSAPI)
-NTSTATUS common_gss_decrypt_buffer(struct smb_tran_enc_state_gss *gss_state, char *buf);
-#endif
-#if defined(HAVE_GSSAPI)
-NTSTATUS common_gss_encrypt_buffer(struct smb_tran_enc_state_gss *gss_state, char *buf, char **buf_out);
-#endif
-
#endif /* HAVE_KRB5 */
dump_data(10, smb_buf(buf), bcc);
}
+/*******************************************************************
+ Set the length and marker of an encrypted smb packet.
+********************************************************************/
+
+void smb_set_enclen(char *buf,int len,uint16 enc_ctx_num)
+{
+ _smb_setlen(buf,len);
+
+ SCVAL(buf,4,0xFF);
+ SCVAL(buf,5,'S');
+ SSVAL(buf,6,enc_ctx_num);
+}
+
/*******************************************************************
Set the length and marker of an smb packet.
********************************************************************/
size_t nwritten=0;
ssize_t ret;
char *buf_out = cli->outbuf;
+ BOOL enc_on = cli_encryption_on(cli);
/* fd == -1 causes segfaults -- Tom (tom@ninja.nl) */
if (cli->fd == -1) {
cli_calculate_sign_mac(cli);
- if (cli_encryption_on(cli)) {
+ if (enc_on) {
NTSTATUS status = cli_encrypt_message(cli, &buf_out);
if (!NT_STATUS_IS_OK(status)) {
close(cli->fd);
while (nwritten < len) {
ret = write_socket(cli->fd,buf_out+nwritten,len - nwritten);
if (ret <= 0) {
- cli_free_enc_buffer(cli, buf_out);
+ if (enc_on) {
+ cli_free_enc_buffer(cli, buf_out);
+ }
close(cli->fd);
cli->fd = -1;
cli->smb_rw_error = WRITE_ERROR;
Send/receive the request encryption blob.
******************************************************************************/
-static NTSTATUS enc_blob_send_receive(struct cli_state *cli, DATA_BLOB *in, DATA_BLOB *out)
+static NTSTATUS enc_blob_send_receive(struct cli_state *cli, DATA_BLOB *in, DATA_BLOB *out, DATA_BLOB *param_out)
{
uint16 setup;
char param[4];
}
*out = data_blob(rdata, rdata_count);
+ *param_out = data_blob(rparam, rparam_count);
out:
{
DATA_BLOB blob_in = data_blob(NULL, 0);
DATA_BLOB blob_out = data_blob(NULL, 0);
+ DATA_BLOB param_out = data_blob(NULL, 0);
NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
struct smb_trans_enc_state *es = NULL;
do {
status = ntlmssp_update(es->s.ntlmssp_state, blob_in, &blob_out);
data_blob_free(&blob_in);
+ data_blob_free(¶m_out);
if (NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED) || NT_STATUS_IS_OK(status)) {
- status = enc_blob_send_receive(cli, &blob_out, &blob_in);
+ status = enc_blob_send_receive(cli, &blob_out, &blob_in, ¶m_out);
+ }
+ if (param_out.length == 2) {
+ es->enc_ctx_num = SVAL(param_out.data, 0);
}
data_blob_free(&blob_out);
} while (NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED));
#include "includes.h"
+/******************************************************************************
+ Pull out the encryption context for this packet. 0 means global context.
+******************************************************************************/
+
+NTSTATUS get_enc_ctx_num(char *buf, uint16 *p_enc_ctx_num)
+{
+ if (smb_len(buf) < 8) {
+ return NT_STATUS_INVALID_BUFFER_SIZE;
+ }
+
+ if (buf[4] == (char)0xFF && buf[5] == 'S') {
+ if (buf [6] == 'M' && buf[7] == 'B') {
+ /* Not an encrypted buffer. */
+ return NT_STATUS_NOT_FOUND;
+ }
+ *p_enc_ctx_num = SVAL(buf,6);
+ return NT_STATUS_OK;
+ }
+ return NT_STATUS_INVALID_NETWORK_RESPONSE;
+}
+
/******************************************************************************
Generic code for client and server.
Is encryption turned on ?
sig = data_blob(buf+buf_len, NTLMSSP_SIG_SIZE);
status = ntlmssp_unseal_packet(ntlmssp_state,
- (unsigned char *)buf + 8, /* 4 byte len + 0xFF 'S' 'M' 'B' */
+ (unsigned char *)buf + 8, /* 4 byte len + 0xFF 'S' <enc> <ctx> */
buf_len - 8,
(unsigned char *)buf + 8,
buf_len - 8,
NTLM encrypt an outgoing buffer. Return the encrypted pointer in ppbuf_out.
******************************************************************************/
-NTSTATUS common_ntlm_encrypt_buffer(NTLMSSP_STATE *ntlmssp_state, char *buf, char **ppbuf_out)
+NTSTATUS common_ntlm_encrypt_buffer(NTLMSSP_STATE *ntlmssp_state,
+ uint16 enc_ctx_num,
+ char *buf,
+ char **ppbuf_out)
{
NTSTATUS status;
char *buf_out;
memcpy(buf_out, buf, buf_len);
/* Last 16 bytes undefined here... */
- smb_setlen(buf_out, smb_len(buf) + NTLMSSP_SIG_SIZE);
+ smb_set_enclen(buf_out, smb_len(buf) + NTLMSSP_SIG_SIZE, enc_ctx_num);
sig = data_blob(NULL, NTLMSSP_SIG_SIZE);
status = ntlmssp_seal_packet(ntlmssp_state,
- (unsigned char *)buf_out + 8, /* 4 byte len + 0xFF 'S' 'M' 'B' */
+ (unsigned char *)buf_out + 8, /* 4 byte len + 0xFF 'S' <enc> <ctx> */
buf_len - 8,
(unsigned char *)buf_out + 8,
buf_len - 8,
******************************************************************************/
#if defined(HAVE_GSSAPI) && defined(HAVE_KRB5)
- NTSTATUS common_gss_decrypt_buffer(struct smb_tran_enc_state_gss *gss_state, char *buf)
+NTSTATUS common_gss_decrypt_buffer(struct smb_tran_enc_state_gss *gss_state, char *buf)
{
gss_ctx_id_t gss_ctx = gss_state->gss_ctx;
OM_uint32 ret = 0;
******************************************************************************/
#if defined(HAVE_GSSAPI) && defined(HAVE_KRB5)
- NTSTATUS common_gss_encrypt_buffer(struct smb_tran_enc_state_gss *gss_state, char *buf, char **ppbuf_out)
+NTSTATUS common_gss_encrypt_buffer(struct smb_tran_enc_state_gss *gss_state,
+ uint16 enc_ctx_num,
+ char *buf,
+ char **ppbuf_out)
{
gss_ctx_id_t gss_ctx = gss_state->gss_ctx;
OM_uint32 ret = 0;
}
memcpy(*ppbuf_out+8, out_buf.value, out_buf.length);
- smb_setlen(*ppbuf_out, out_buf.length + 4);
+ smb_set_enclen(*ppbuf_out, out_buf.length + 4, enc_ctx_num);
gss_release_buffer(&minor, &out_buf);
return NT_STATUS_OK;
return NT_STATUS_OK;
}
- /* Ignore session keepalives. */
- if(CVAL(buffer,0) == SMBkeepalive) {
- *buf_out = buffer;
- return NT_STATUS_OK;
- }
-
switch (es->smb_enc_type) {
case SMB_TRANS_ENC_NTLM:
- return common_ntlm_encrypt_buffer(es->s.ntlmssp_state, buffer, buf_out);
+ return common_ntlm_encrypt_buffer(es->s.ntlmssp_state, es->enc_ctx_num, buffer, buf_out);
#if defined(HAVE_GSSAPI) && defined(HAVE_KRB5)
case SMB_TRANS_ENC_GSS:
- return common_gss_encrypt_buffer(es->s.gss_state, buffer, buf_out);
+ return common_gss_encrypt_buffer(es->s.gss_state, es->enc_ctx_num, buffer, buf_out);
#endif
default:
return NT_STATUS_NOT_SUPPORTED;
return NT_STATUS_OK;
}
- /* Ignore session keepalives. */
- if(CVAL(buf,0) == SMBkeepalive) {
- return NT_STATUS_OK;
- }
-
switch (es->smb_enc_type) {
case SMB_TRANS_ENC_NTLM:
return common_ntlm_decrypt_buffer(es->s.ntlmssp_state, buf);
return;
}
- /* We know this is an smb buffer, and we
- * didn't malloc, only copy, for a keepalive,
- * so ignore session keepalives. */
-
- if(CVAL(buf,0) == SMBkeepalive) {
- return;
- }
-
if (es->smb_enc_type == SMB_TRANS_ENC_NTLM) {
SAFE_FREE(buf);
return;
}
#if defined(HAVE_GSSAPI) && defined(HAVE_KRB5)
- /* gss-api free buffer.... */
+ if (es->smb_enc_type == SMB_TRANS_ENC_GSS) {
+ OM_uint32 min;
+ gss_buffer_desc rel_buf;
+ rel_buf.value = buf;
+ rel_buf.length = smb_len(buf) + 4;
+ gss_release_buffer(&min, &rel_buf);
+ }
#endif
}
BOOL cli_encryption_on(struct cli_state *cli)
{
+ /* If we supported multiple encrytion contexts
+ * here we'd look up based on tid.
+ */
return common_encryption_on(cli->trans_enc_state);
}
void cli_free_enc_buffer(struct cli_state *cli, char *buf)
{
+ /* We know this is an smb buffer, and we
+ * didn't malloc, only copy, for a keepalive,
+ * so ignore session keepalives. */
+
+ if(CVAL(buf,0) == SMBkeepalive) {
+ return;
+ }
+
+ /* If we supported multiple encrytion contexts
+ * here we'd look up based on tid.
+ */
common_free_enc_buffer(cli->trans_enc_state, buf);
}
NTSTATUS cli_decrypt_message(struct cli_state *cli)
{
+ NTSTATUS status;
+ uint16 enc_ctx_num;
+
+ /* Ignore session keepalives. */
+ if(CVAL(cli->inbuf,0) == SMBkeepalive) {
+ return NT_STATUS_OK;
+ }
+
+ status = get_enc_ctx_num(cli->inbuf, &enc_ctx_num);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ if (enc_ctx_num != cli->trans_enc_state->enc_ctx_num) {
+ return NT_STATUS_INVALID_HANDLE;
+ }
+
return common_decrypt_buffer(cli->trans_enc_state, cli->inbuf);
}
NTSTATUS cli_encrypt_message(struct cli_state *cli, char **buf_out)
{
+ /* Ignore session keepalives. */
+ if(CVAL(cli->inbuf,0) == SMBkeepalive) {
+ return NT_STATUS_OK;
+ }
+
+ /* If we supported multiple encrytion contexts
+ * here we'd look up based on tid.
+ */
return common_encrypt_buffer(cli->trans_enc_state, cli->outbuf, buf_out);
}
void srv_free_enc_buffer(char *buf)
{
+ /* We know this is an smb buffer, and we
+ * didn't malloc, only copy, for a keepalive,
+ * so ignore session keepalives. */
+
+ if(CVAL(buf,0) == SMBkeepalive) {
+ return;
+ }
+
if (srv_trans_enc_ctx) {
common_free_enc_buffer(srv_trans_enc_ctx->es, buf);
}
NTSTATUS srv_decrypt_buffer(char *buf)
{
+ /* Ignore session keepalives. */
+ if(CVAL(buf,0) == SMBkeepalive) {
+ return NT_STATUS_OK;
+ }
+
if (srv_trans_enc_ctx) {
return common_decrypt_buffer(srv_trans_enc_ctx->es, buf);
}
+
return NT_STATUS_OK;
}
Encrypt an outgoing buffer. Return the encrypted pointer in buf_out.
******************************************************************************/
-NTSTATUS srv_encrypt_buffer(char *buffer, char **buf_out)
+NTSTATUS srv_encrypt_buffer(char *buf, char **buf_out)
{
+ *buf_out = buf;
+
+ /* Ignore session keepalives. */
+ if(CVAL(buf,0) == SMBkeepalive) {
+ return NT_STATUS_OK;
+ }
+
if (srv_trans_enc_ctx) {
- return common_encrypt_buffer(srv_trans_enc_ctx->es, buffer, buf_out);
+ return common_encrypt_buffer(srv_trans_enc_ctx->es, buf, buf_out);
}
/* Not encrypting. */
- *buf_out = buffer;
return NT_STATUS_OK;
}
Until success we do everything on the partial enc ctx.
******************************************************************************/
-static NTSTATUS srv_enc_spnego_negotiate(unsigned char **ppdata, size_t *p_data_size)
+static NTSTATUS srv_enc_spnego_negotiate(connection_struct *conn,
+ unsigned char **ppdata,
+ size_t *p_data_size,
+ unsigned char **pparam,
+ size_t *p_param_size)
{
NTSTATUS status;
DATA_BLOB blob = data_blob(NULL,0);
if (!NT_STATUS_EQUAL(status,NT_STATUS_MORE_PROCESSING_REQUIRED) && !NT_STATUS_IS_OK(status)) {
srv_free_encryption_context(&partial_srv_trans_enc_ctx);
+ return nt_status_squash(status);
+ }
+
+ if (NT_STATUS_IS_OK(status)) {
+ /* Return the context we're using for this encryption state. */
+ *pparam = SMB_MALLOC(2);
+ if (!*pparam) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ SSVAL(*pparam,0,partial_srv_trans_enc_ctx->es->enc_ctx_num);
+ *p_param_size = 2;
}
return status;
We only get this for a NTLM auth second stage.
******************************************************************************/
-static NTSTATUS srv_enc_spnego_ntlm_auth(unsigned char **ppdata, size_t *p_data_size)
+static NTSTATUS srv_enc_spnego_ntlm_auth(connection_struct *conn,
+ unsigned char **ppdata,
+ size_t *p_data_size,
+ unsigned char **pparam,
+ size_t *p_param_size)
{
NTSTATUS status;
DATA_BLOB blob = data_blob(NULL,0);
response = spnego_gen_auth_response(&auth_reply, status, OID_NTLMSSP);
data_blob_free(&auth_reply);
+ if (NT_STATUS_IS_OK(status)) {
+ /* Return the context we're using for this encryption state. */
+ *pparam = SMB_MALLOC(2);
+ if (!*pparam) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ SSVAL(*pparam,0,ec->es->enc_ctx_num);
+ *p_param_size = 2;
+ }
+
SAFE_FREE(*ppdata);
*ppdata = response.data;
*p_data_size = response.length;
This function does both steps.
******************************************************************************/
-static NTSTATUS srv_enc_raw_ntlm_auth(unsigned char **ppdata, size_t *p_data_size)
+static NTSTATUS srv_enc_raw_ntlm_auth(connection_struct *conn,
+ unsigned char **ppdata,
+ size_t *p_data_size,
+ unsigned char **pparam,
+ size_t *p_param_size)
{
NTSTATUS status;
DATA_BLOB blob = data_blob_const(*ppdata, *p_data_size);
/* Second step. */
status = auth_ntlmssp_update(partial_srv_trans_enc_ctx->auth_ntlmssp_state, blob, &response);
+ if (NT_STATUS_IS_OK(status)) {
+ /* Return the context we're using for this encryption state. */
+ *pparam = SMB_MALLOC(2);
+ if (!*pparam) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ SSVAL(*pparam,0,ec->es->enc_ctx_num);
+ *p_param_size = 2;
+ }
+
/* Return the raw blob. */
SAFE_FREE(*ppdata);
*ppdata = response.data;
Do the SPNEGO encryption negotiation. Parameters are in/out.
******************************************************************************/
-NTSTATUS srv_request_encryption_setup(unsigned char **ppdata, size_t *p_data_size)
+NTSTATUS srv_request_encryption_setup(connection_struct *conn,
+ unsigned char **ppdata,
+ size_t *p_data_size,
+ unsigned char **pparam,
+ size_t *p_param_size)
{
unsigned char *pdata = *ppdata;
+ SAFE_FREE(*pparam);
+ *p_param_size = 0;
+
if (*p_data_size < 1) {
return NT_STATUS_INVALID_PARAMETER;
}
if (pdata[0] == ASN1_APPLICATION(0)) {
- /*
- * Until success we do everything on the partial
- * enc state.
- */
/* its a negTokenTarg packet */
- return srv_enc_spnego_negotiate(ppdata, p_data_size);
+ return srv_enc_spnego_negotiate(conn, ppdata, p_data_size, pparam, p_param_size);
}
if (pdata[0] == ASN1_CONTEXT(1)) {
/* It's an auth packet */
- return srv_enc_spnego_ntlm_auth(ppdata, p_data_size);
+ return srv_enc_spnego_ntlm_auth(conn, ppdata, p_data_size, pparam, p_param_size);
}
/* Maybe it's a raw unwrapped auth ? */
}
if (strncmp((char *)pdata, "NTLMSSP", 7) == 0) {
- return srv_enc_raw_ntlm_auth(ppdata, p_data_size);
+ return srv_enc_raw_ntlm_auth(conn, ppdata, p_data_size, pparam, p_param_size);
}
DEBUG(1,("srv_request_encryption_setup: Unknown packet\n"));
Negotiation was successful - turn on server-side encryption.
******************************************************************************/
-NTSTATUS srv_encryption_start(void)
+NTSTATUS srv_encryption_start(connection_struct *conn)
{
NTSTATUS status;
case SMB_REQUEST_TRANSPORT_ENCRYPTION:
{
NTSTATUS status;
+ size_t param_len = 0;
size_t data_len = total_data;
if (!lp_unix_extensions()) {
DEBUG( 4,("call_trans2setfsinfo: request transport encrption.\n"));
- status = srv_request_encryption_setup((unsigned char **)ppdata, &data_len);
+ status = srv_request_encryption_setup(conn,
+ (unsigned char **)ppdata,
+ &data_len,
+ (unsigned char **)pparams,
+ ¶m_len
+ );
if (NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
error_packet_set(outbuf, 0, 0, status, __LINE__,__FILE__);
return ERROR_NT(status);
}
- send_trans2_replies( outbuf, bufsize, params, 0, *ppdata, data_len, max_data_bytes);
+ send_trans2_replies(outbuf, bufsize, *pparams, param_len, *ppdata, data_len, max_data_bytes);
if (NT_STATUS_IS_OK(status)) {
/* Server-side transport encryption is now *on*. */
- status = srv_encryption_start();
+ status = srv_encryption_start(conn);
if (!NT_STATUS_IS_OK(status)) {
exit_server_cleanly("Failure in setting up encrypted transport");
}