From 75d146d3ed4196b0856ea6eb56482a1a67fdf707 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Wed, 19 Oct 2011 13:47:39 +0200 Subject: libcli/smb: move smb_seal.c to the toplevel metze Autobuild-User: Stefan Metzmacher Autobuild-Date: Fri Oct 21 10:22:39 CEST 2011 on sn-devel-104 --- libcli/smb/smb_common.h | 1 + libcli/smb/smb_seal.c | 428 +++++++++++++++++++++++++++++++++++++++++++++++ libcli/smb/smb_seal.h | 71 ++++++++ libcli/smb/wscript_build | 5 +- 4 files changed, 503 insertions(+), 2 deletions(-) create mode 100644 libcli/smb/smb_seal.c create mode 100644 libcli/smb/smb_seal.h (limited to 'libcli/smb') diff --git a/libcli/smb/smb_common.h b/libcli/smb/smb_common.h index 1f21e553ba..228e451b8d 100644 --- a/libcli/smb/smb_common.h +++ b/libcli/smb/smb_common.h @@ -28,5 +28,6 @@ #include "libcli/smb/smb_constants.h" #include "libcli/smb/smb_util.h" #include "libcli/smb/smb_unix_ext.h" +#include "libcli/smb/smb_seal.h" #endif diff --git a/libcli/smb/smb_seal.c b/libcli/smb/smb_seal.c new file mode 100644 index 0000000000..bf9576f288 --- /dev/null +++ b/libcli/smb/smb_seal.c @@ -0,0 +1,428 @@ +/* + Unix SMB/CIFS implementation. + SMB Transport encryption (sealing) code. + Copyright (C) Jeremy Allison 2007. + + 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 . +*/ + +#include "includes.h" +#include "smb_common.h" +#include "libcli/auth/krb5_wrap.h" +#include "auth/gensec/gensec.h" + +#undef malloc + +/****************************************************************************** + Pull out the encryption context for this packet. 0 means global context. +******************************************************************************/ + +NTSTATUS get_enc_ctx_num(const uint8_t *buf, uint16_t *p_enc_ctx_num) +{ + if (smb_len_nbt(buf) < 8) { + return NT_STATUS_INVALID_BUFFER_SIZE; + } + + if (buf[4] == 0xFF) { + if (buf[5] == 'S' && buf [6] == 'M' && buf[7] == 'B') { + /* Not an encrypted buffer. */ + return NT_STATUS_NOT_FOUND; + } + if (buf[5] == 'E') { + *p_enc_ctx_num = SVAL(buf,6); + return NT_STATUS_OK; + } + } + return NT_STATUS_INVALID_NETWORK_RESPONSE; +} + +/******************************************************************* + Set the length and marker of an encrypted smb packet. +********************************************************************/ + +static void smb_set_enclen(char *buf,int len,uint16_t enc_ctx_num) +{ + _smb_setlen_nbt(buf,len); + + SCVAL(buf,4,0xFF); + SCVAL(buf,5,'E'); + SSVAL(buf,6,enc_ctx_num); +} + +/****************************************************************************** + Generic code for client and server. + Is encryption turned on ? +******************************************************************************/ + +bool common_encryption_on(struct smb_trans_enc_state *es) +{ + return ((es != NULL) && es->enc_on); +} + +/****************************************************************************** + Generic code for client and server. + GENSEC decrypt an incoming buffer. +******************************************************************************/ + +static NTSTATUS common_gensec_decrypt_buffer(struct gensec_security *gensec, + char *buf) +{ + NTSTATUS status; + size_t buf_len = smb_len_nbt(buf) + 4; /* Don't forget the 4 length bytes. */ + DATA_BLOB in_buf, out_buf; + TALLOC_CTX *frame; + + if (buf_len < 8) { + return NT_STATUS_BUFFER_TOO_SMALL; + } + + frame = talloc_stackframe(); + + in_buf = data_blob_const(buf + 8, buf_len - 8); + + status = gensec_unwrap(gensec, frame, &in_buf, &out_buf); + + if (!NT_STATUS_IS_OK(status)) { + DEBUG(0,("common_gensec_decrypt_buffer: gensec_unwrap failed. Error %s\n", + nt_errstr(status))); + TALLOC_FREE(frame); + return status; + } + + if (out_buf.length > in_buf.length) { + DEBUG(0,("common_gensec_decrypt_buffer: gensec_unwrap size (%u) too large (%u) !\n", + (unsigned int)out_buf.length, + (unsigned int)in_buf.length )); + TALLOC_FREE(frame); + return NT_STATUS_INVALID_PARAMETER; + } + + memcpy(buf + 8, out_buf.data, out_buf.length); + + /* Reset the length and overwrite the header. */ + smb_setlen_nbt(buf, out_buf.length + 4); + + TALLOC_FREE(frame); + + return NT_STATUS_OK; +} + +/****************************************************************************** + Generic code for client and server. + NTLM encrypt an outgoing buffer. Return the encrypted pointer in ppbuf_out. +******************************************************************************/ + +static NTSTATUS common_gensec_encrypt_buffer(struct gensec_security *gensec, + uint16_t enc_ctx_num, + char *buf, + char **ppbuf_out) +{ + NTSTATUS status; + DATA_BLOB in_buf, out_buf; + size_t buf_len = smb_len_nbt(buf) + 4; /* Don't forget the 4 length bytes. */ + TALLOC_CTX *frame; + + *ppbuf_out = NULL; + + if (buf_len < 8) { + return NT_STATUS_BUFFER_TOO_SMALL; + } + in_buf = data_blob_const(buf + 8, buf_len - 8); + + frame = talloc_stackframe(); + + status = gensec_wrap(gensec, frame, &in_buf, &out_buf); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(0,("common_gensec_encrypt_buffer: gensec_wrap failed. Error %s\n", + nt_errstr(status))); + TALLOC_FREE(frame); + return status; + } + + *ppbuf_out = (char *)malloc(out_buf.length + 8); /* We know this can't wrap. */ + if (!*ppbuf_out) { + TALLOC_FREE(frame); + return NT_STATUS_NO_MEMORY; + } + + memcpy(*ppbuf_out+8, out_buf.data, out_buf.length); + smb_set_enclen(*ppbuf_out, out_buf.length + 4, enc_ctx_num); + + TALLOC_FREE(frame); + + return NT_STATUS_OK; +} + +/****************************************************************************** + Generic code for client and server. + gss-api decrypt an incoming buffer. We insist that the size of the + unwrapped buffer must be smaller or identical to the incoming buffer. +******************************************************************************/ + +#if defined(HAVE_GSSAPI) && defined(HAVE_KRB5) +static 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; + OM_uint32 minor = 0; + int flags_got = 0; + gss_buffer_desc in_buf, out_buf; + size_t buf_len = smb_len_nbt(buf) + 4; /* Don't forget the 4 length bytes. */ + + if (buf_len < 8) { + return NT_STATUS_BUFFER_TOO_SMALL; + } + + in_buf.value = buf + 8; + in_buf.length = buf_len - 8; + + ret = gss_unwrap(&minor, + gss_ctx, + &in_buf, + &out_buf, + &flags_got, /* did we get sign+seal ? */ + (gss_qop_t *) NULL); + + if (ret != GSS_S_COMPLETE) { + NTSTATUS status = NT_STATUS_ACCESS_DENIED; + char *gss_err; + + gss_err = gssapi_error_string(talloc_tos(), + ret, minor, + GSS_C_NULL_OID); + DEBUG(0,("common_gss_decrypt_buffer: gss_unwrap failed. " + "Error [%d/%d] - %s - %s\n", + ret, minor, nt_errstr(status), + gss_err ? gss_err : "")); + talloc_free(gss_err); + + return status; + } + + if (out_buf.length > in_buf.length) { + DEBUG(0,("common_gss_decrypt_buffer: gss_unwrap size (%u) too large (%u) !\n", + (unsigned int)out_buf.length, + (unsigned int)in_buf.length )); + gss_release_buffer(&minor, &out_buf); + return NT_STATUS_INVALID_PARAMETER; + } + + memcpy(buf + 8, out_buf.value, out_buf.length); + /* Reset the length and overwrite the header. */ + smb_setlen_nbt(buf, out_buf.length + 4); + + gss_release_buffer(&minor, &out_buf); + return NT_STATUS_OK; +} + +/****************************************************************************** + Generic code for client and server. + gss-api encrypt an outgoing buffer. Return the alloced encrypted pointer in buf_out. +******************************************************************************/ + +static NTSTATUS common_gss_encrypt_buffer(struct smb_tran_enc_state_gss *gss_state, + uint16_t enc_ctx_num, + char *buf, + char **ppbuf_out) +{ + gss_ctx_id_t gss_ctx = gss_state->gss_ctx; + OM_uint32 ret = 0; + OM_uint32 minor = 0; + int flags_got = 0; + gss_buffer_desc in_buf, out_buf; + size_t buf_len = smb_len_nbt(buf) + 4; /* Don't forget the 4 length bytes. */ + + *ppbuf_out = NULL; + + if (buf_len < 8) { + return NT_STATUS_BUFFER_TOO_SMALL; + } + + in_buf.value = buf + 8; + in_buf.length = buf_len - 8; + + ret = gss_wrap(&minor, + gss_ctx, + true, /* we want sign+seal. */ + GSS_C_QOP_DEFAULT, + &in_buf, + &flags_got, /* did we get sign+seal ? */ + &out_buf); + + if (ret != GSS_S_COMPLETE) { + NTSTATUS status = NT_STATUS_ACCESS_DENIED; + char *gss_err; + + gss_err = gssapi_error_string(talloc_tos(), + ret, minor, + GSS_C_NULL_OID); + DEBUG(0,("common_gss_encrypt_buffer: gss_unwrap failed. " + "Error [%d/%d] - %s - %s\n", + ret, minor, nt_errstr(status), + gss_err ? gss_err : "")); + talloc_free(gss_err); + + return status; + } + + if (!flags_got) { + /* Sign+seal not supported. */ + gss_release_buffer(&minor, &out_buf); + return NT_STATUS_NOT_SUPPORTED; + } + + /* Ya see - this is why I *hate* gss-api. I don't + * want to have to malloc another buffer of the + * same size + 8 bytes just to get a continuous + * header + buffer, but gss won't let me pass in + * a pre-allocated buffer. Bastards (and you know + * who you are....). I might fix this by + * going to "encrypt_and_send" passing in a file + * descriptor and doing scatter-gather write with + * TCP cork on Linux. But I shouldn't have to + * bother :-*(. JRA. + */ + + *ppbuf_out = (char *)malloc(out_buf.length + 8); /* We know this can't wrap. */ + if (!*ppbuf_out) { + gss_release_buffer(&minor, &out_buf); + return NT_STATUS_NO_MEMORY; + } + + memcpy(*ppbuf_out+8, out_buf.value, out_buf.length); + smb_set_enclen(*ppbuf_out, out_buf.length + 4, enc_ctx_num); + + gss_release_buffer(&minor, &out_buf); + return NT_STATUS_OK; +} +#endif + +/****************************************************************************** + Generic code for client and server. + Encrypt an outgoing buffer. Return the alloced encrypted pointer in buf_out. +******************************************************************************/ + +NTSTATUS common_encrypt_buffer(struct smb_trans_enc_state *es, char *buffer, char **buf_out) +{ + if (!common_encryption_on(es)) { + /* Not encrypting. */ + *buf_out = buffer; + return NT_STATUS_OK; + } + + switch (es->smb_enc_type) { + case SMB_TRANS_ENC_NTLM: + return common_gensec_encrypt_buffer(es->s.gensec_security, 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, es->enc_ctx_num, buffer, buf_out); +#endif + default: + return NT_STATUS_NOT_SUPPORTED; + } +} + +/****************************************************************************** + Generic code for client and server. + Decrypt an incoming SMB buffer. Replaces the data within it. + New data must be less than or equal to the current length. +******************************************************************************/ + +NTSTATUS common_decrypt_buffer(struct smb_trans_enc_state *es, char *buf) +{ + if (!common_encryption_on(es)) { + /* Not decrypting. */ + return NT_STATUS_OK; + } + + switch (es->smb_enc_type) { + case SMB_TRANS_ENC_NTLM: + return common_gensec_decrypt_buffer(es->s.gensec_security, buf); +#if defined(HAVE_GSSAPI) && defined(HAVE_KRB5) + case SMB_TRANS_ENC_GSS: + return common_gss_decrypt_buffer(es->s.gss_state, buf); +#endif + default: + return NT_STATUS_NOT_SUPPORTED; + } +} + +#if defined(HAVE_GSSAPI) && defined(HAVE_KRB5) +/****************************************************************************** + Shutdown a gss encryption state. +******************************************************************************/ + +static void common_free_gss_state(struct smb_tran_enc_state_gss **pp_gss_state) +{ + OM_uint32 minor = 0; + struct smb_tran_enc_state_gss *gss_state = *pp_gss_state; + + if (gss_state->creds != GSS_C_NO_CREDENTIAL) { + gss_release_cred(&minor, &gss_state->creds); + } + if (gss_state->gss_ctx != GSS_C_NO_CONTEXT) { + gss_delete_sec_context(&minor, &gss_state->gss_ctx, NULL); + } + SAFE_FREE(*pp_gss_state); +} +#endif + +/****************************************************************************** + Shutdown an encryption state. +******************************************************************************/ + +void common_free_encryption_state(struct smb_trans_enc_state **pp_es) +{ + struct smb_trans_enc_state *es = *pp_es; + + if (es == NULL) { + return; + } + + if (es->smb_enc_type == SMB_TRANS_ENC_NTLM) { + if (es->s.gensec_security) { + TALLOC_FREE(es->s.gensec_security); + } + } +#if defined(HAVE_GSSAPI) && defined(HAVE_KRB5) + if (es->smb_enc_type == SMB_TRANS_ENC_GSS) { + /* Free the gss context handle. */ + if (es->s.gss_state) { + common_free_gss_state(&es->s.gss_state); + } + } +#endif + SAFE_FREE(es); + *pp_es = NULL; +} + +/****************************************************************************** + Free an encryption-allocated buffer. +******************************************************************************/ + +void common_free_enc_buffer(struct smb_trans_enc_state *es, char *buf) +{ + uint16_t enc_ctx_num; + + if (!common_encryption_on(es)) { + return; + } + + if (!NT_STATUS_IS_OK(get_enc_ctx_num((const uint8_t *)buf, + &enc_ctx_num))) { + return; + } + + SAFE_FREE(buf); +} diff --git a/libcli/smb/smb_seal.h b/libcli/smb/smb_seal.h new file mode 100644 index 0000000000..fcee2057d8 --- /dev/null +++ b/libcli/smb/smb_seal.h @@ -0,0 +1,71 @@ +/* + Unix SMB/CIFS implementation. + SMB Transport encryption code. + Copyright (C) Jeremy Allison 2007. + + 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 . +*/ + +#ifndef _HEADER_SMB_CRYPT_H +#define _HEADER_SMB_CRYPT_H + +#if HAVE_GSSAPI_GSSAPI_H +#include +#elif HAVE_GSSAPI_GSSAPI_GENERIC_H +#include +#elif HAVE_GSSAPI_H +#include +#endif + +#if HAVE_COM_ERR_H +#include +#endif + +/* Transport encryption state. */ +enum smb_trans_enc_type { + SMB_TRANS_ENC_NTLM +#if defined(HAVE_GSSAPI) && defined(HAVE_KRB5) + , SMB_TRANS_ENC_GSS +#endif +}; + +#if defined(HAVE_GSSAPI) && defined(HAVE_KRB5) +struct smb_tran_enc_state_gss { + gss_ctx_id_t gss_ctx; + gss_cred_id_t creds; +}; +#endif + +struct smb_trans_enc_state { + enum smb_trans_enc_type smb_enc_type; + uint16_t enc_ctx_num; + bool enc_on; + union { + struct gensec_security *gensec_security; +#if defined(HAVE_GSSAPI) && defined(HAVE_KRB5) + struct smb_tran_enc_state_gss *gss_state; +#endif + } s; +}; + +/* The following definitions come from smb_seal.c */ + +NTSTATUS get_enc_ctx_num(const uint8_t *buf, uint16_t *p_enc_ctx_num); +bool common_encryption_on(struct smb_trans_enc_state *es); +NTSTATUS common_encrypt_buffer(struct smb_trans_enc_state *es, char *buffer, char **buf_out); +NTSTATUS common_decrypt_buffer(struct smb_trans_enc_state *es, char *buf); +void common_free_encryption_state(struct smb_trans_enc_state **pp_es); +void common_free_enc_buffer(struct smb_trans_enc_state *es, char *buf); + +#endif /* _HEADER_SMB_CRYPT_H */ diff --git a/libcli/smb/wscript_build b/libcli/smb/wscript_build index 8043c8049b..63349586f4 100644 --- a/libcli/smb/wscript_build +++ b/libcli/smb/wscript_build @@ -2,12 +2,13 @@ bld.SAMBA_LIBRARY('cli_smb_common', - source='smb2_create_blob.c smb2_signing.c util.c', + source='smb_seal.c smb2_create_blob.c smb2_signing.c util.c', autoproto='smb_common_proto.h', - deps='LIBCRYPTO', + deps='LIBCRYPTO errors gssapi gensec KRB5_WRAP', public_deps='talloc samba-util', private_library=True, public_headers='''smb_common.h smb2_constants.h smb_constants.h + smb_seal.h smb2_create_blob.h smb2_signing.h smb_util.h smb_unix_ext.h ''', ) -- cgit