summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJeremy Allison <jra@samba.org>2007-12-26 17:12:36 -0800
committerJeremy Allison <jra@samba.org>2007-12-26 17:12:36 -0800
commitafc93255d183eefb68e45b8ec6275f6a62cf9795 (patch)
tree712efc0cd3c95d30c0e44055b25807c41533bc1f
parent23c965d9472058c566a1b9f8a44964acd5c8a446 (diff)
downloadsamba-afc93255d183eefb68e45b8ec6275f6a62cf9795.tar.gz
samba-afc93255d183eefb68e45b8ec6275f6a62cf9795.tar.bz2
samba-afc93255d183eefb68e45b8ec6275f6a62cf9795.zip
Add SMB encryption. Still fixing client decrypt but
negotiation works. Jeremy. (This used to be commit d78045601af787731f0737b8627450018902b104)
-rw-r--r--source3/Makefile.in4
-rw-r--r--source3/client/client.c43
-rw-r--r--source3/include/client.h26
-rw-r--r--source3/include/smb.h3
-rw-r--r--source3/include/smb_macros.h16
-rw-r--r--source3/include/trans2.h24
-rw-r--r--source3/lib/dummysmbd.c20
-rw-r--r--source3/lib/util.c38
-rw-r--r--source3/lib/util_sock.c30
-rw-r--r--source3/libads/ads_status.c9
-rw-r--r--source3/libsmb/cliconnect.c22
-rw-r--r--source3/libsmb/clidgram.c2
-rw-r--r--source3/libsmb/clientgen.c108
-rw-r--r--source3/libsmb/clifile.c44
-rw-r--r--source3/libsmb/clifsinfo.c333
-rw-r--r--source3/libsmb/clilist.c4
-rw-r--r--source3/libsmb/climessage.c6
-rw-r--r--source3/libsmb/clioplock.c2
-rw-r--r--source3/libsmb/cliprint.c4
-rw-r--r--source3/libsmb/clireadwrite.c31
-rw-r--r--source3/libsmb/clitrans.c8
-rw-r--r--source3/libsmb/errormap.c105
-rw-r--r--source3/libsmb/smb_seal.c496
-rw-r--r--source3/nmbd/nmbd_packets.c2
-rw-r--r--source3/param/loadparm.c7
-rw-r--r--source3/smbd/aio.c17
-rw-r--r--source3/smbd/error.c71
-rw-r--r--source3/smbd/notify.c3
-rw-r--r--source3/smbd/oplock.c6
-rw-r--r--source3/smbd/pipes.c3
-rw-r--r--source3/smbd/process.c56
-rw-r--r--source3/smbd/reply.c35
-rw-r--r--source3/smbd/seal.c703
-rw-r--r--source3/smbd/trans2.c49
34 files changed, 2116 insertions, 214 deletions
diff --git a/source3/Makefile.in b/source3/Makefile.in
index 10a14d0a55..9d8ad3e68b 100644
--- a/source3/Makefile.in
+++ b/source3/Makefile.in
@@ -374,7 +374,7 @@ LIBSMB_OBJ = libsmb/clientgen.o libsmb/cliconnect.o libsmb/clifile.o \
libsmb/clistr.o libsmb/cliquota.o libsmb/clifsinfo.o libsmb/clidfs.o \
libsmb/smberr.o libsmb/credentials.o libsmb/pwd_cache.o \
libsmb/clioplock.o $(ERRORMAP_OBJ) libsmb/clirap2.o \
- $(DOSERR_OBJ) \
+ libsmb/smb_seal.o $(DOSERR_OBJ) \
$(RPC_PARSE_OBJ1) $(LIBSAMBA_OBJ) $(LIBNMB_OBJ)
RPC_CLIENT_OBJ1 = rpc_client/cli_netlogon.o rpc_client/cli_srvsvc.o
@@ -551,7 +551,7 @@ SMBD_OBJ_SRV = smbd/files.o smbd/chgpasswd.o smbd/connection.o \
smbd/reply.o smbd/sesssetup.o smbd/trans2.o smbd/uid.o \
smbd/dosmode.o smbd/filename.o smbd/open.o smbd/close.o \
smbd/blocking.o smbd/sec_ctx.o smbd/srvstr.o \
- smbd/vfs.o smbd/statcache.o \
+ smbd/vfs.o smbd/statcache.o smbd/seal.o \
smbd/posix_acls.o lib/sysacls.o $(SERVER_MUTEX_OBJ) \
smbd/process.o smbd/service.o smbd/error.o \
printing/printfsp.o lib/sysquotas.o lib/sysquotas_linux.o \
diff --git a/source3/client/client.c b/source3/client/client.c
index f761d92bac..665a051190 100644
--- a/source3/client/client.c
+++ b/source3/client/client.c
@@ -2178,6 +2178,48 @@ static int cmd_open(void)
return 0;
}
+static int cmd_posix_encrypt(void)
+{
+ TALLOC_CTX *ctx = talloc_tos();
+ NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
+
+ if (cli->use_kerberos) {
+ status = cli_gss_smb_encryption_start(cli);
+ } else {
+ char *domain = NULL;
+ char *user = NULL;
+ char *password = NULL;
+
+ if (!next_token_talloc(ctx, &cmd_ptr,&domain,NULL)) {
+ d_printf("posix_encrypt domain user password\n");
+ return 1;
+ }
+
+ if (!next_token_talloc(ctx, &cmd_ptr,&user,NULL)) {
+ d_printf("posix_encrypt domain user password\n");
+ return 1;
+ }
+
+ if (!next_token_talloc(ctx, &cmd_ptr,&password,NULL)) {
+ d_printf("posix_encrypt domain user password\n");
+ return 1;
+ }
+
+ status = cli_raw_ntlm_smb_encryption_start(cli,
+ user,
+ password,
+ domain);
+ }
+
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("posix_encrypt failed with error %s\n", nt_errstr(status));
+ } else {
+ d_printf("encryption on\n");
+ }
+
+ return 0;
+}
+
/****************************************************************************
****************************************************************************/
@@ -3803,6 +3845,7 @@ static struct {
{"newer",cmd_newer,"<file> only mget files newer than the specified local file",{COMPL_LOCAL,COMPL_NONE}},
{"open",cmd_open,"<mask> open a file",{COMPL_REMOTE,COMPL_NONE}},
{"posix", cmd_posix, "turn on all POSIX capabilities", {COMPL_REMOTE,COMPL_NONE}},
+ {"posix_encrypt",cmd_posix_encrypt,"<domain> <user> <password> start up transport encryption",{COMPL_REMOTE,COMPL_NONE}},
{"posix_open",cmd_posix_open,"<name> 0<mode> open_flags mode open a file using POSIX interface",{COMPL_REMOTE,COMPL_NONE}},
{"posix_mkdir",cmd_posix_mkdir,"<name> 0<mode> creates a directory using POSIX interface",{COMPL_REMOTE,COMPL_NONE}},
{"posix_rmdir",cmd_posix_rmdir,"<name> removes a directory using POSIX interface",{COMPL_REMOTE,COMPL_NONE}},
diff --git a/source3/include/client.h b/source3/include/client.h
index 0047b2bf23..f8adf567de 100644
--- a/source3/include/client.h
+++ b/source3/include/client.h
@@ -27,7 +27,9 @@
will be a multiple of the page size on almost any system */
#define CLI_BUFFER_SIZE (0xFFFF)
#define CLI_SAMBA_MAX_LARGE_READX_SIZE (127*1024) /* Works for Samba servers */
+#define CLI_SAMBA_MAX_LARGE_WRITEX_SIZE (127*1024) /* Works for Samba servers */
#define CLI_WINDOWS_MAX_LARGE_READX_SIZE ((64*1024)-2) /* Windows servers are broken.... */
+#define CLI_WINDOWS_MAX_LARGE_WRITEX_SIZE ((64*1024)-2) /* Windows servers are broken.... */
#define CLI_SAMBA_MAX_POSIX_LARGE_READX_SIZE (0xFFFF00) /* 24-bit len. */
#define CLI_SAMBA_MAX_POSIX_LARGE_WRITEX_SIZE (0xFFFF00) /* 24-bit len. */
@@ -79,6 +81,28 @@ struct rpc_pipe_client {
struct dcinfo *dc;
};
+/* Transport encryption state. */
+enum smb_trans_enc_type { SMB_TRANS_ENC_NTLM, SMB_TRANS_ENC_GSS };
+
+#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 enc_ctx_num;
+ bool enc_on;
+ union {
+ NTLMSSP_STATE *ntlmssp_state;
+#if defined(HAVE_GSSAPI) && defined(HAVE_KRB5)
+ struct smb_tran_enc_state_gss *gss_state;
+#endif
+ } s;
+};
+
struct cli_state {
int port;
int fd;
@@ -142,6 +166,8 @@ struct cli_state {
smb_sign_info sign_info;
+ struct smb_trans_enc_state *trans_enc_state; /* Setup if we're encrypting SMB's. */
+
/* the session key for this CLI, outside
any per-pipe authenticaion */
DATA_BLOB user_session_key;
diff --git a/source3/include/smb.h b/source3/include/smb.h
index 63ae51ecd4..2ffd530fb0 100644
--- a/source3/include/smb.h
+++ b/source3/include/smb.h
@@ -80,7 +80,8 @@ enum smb_read_errors {
SMB_WRITE_ERROR, /* This error code can go into the client smb_rw_error. */
SMB_READ_BAD_SIG,
SMB_NO_MEMORY,
- SMB_DO_NOT_DO_TDIS /* cli_close_connection() check for this when smbfs wants to keep tree connected */
+ SMB_DO_NOT_DO_TDIS, /* cli_close_connection() check for this when smbfs wants to keep tree connected */
+ SMB_READ_BAD_DECRYPT
};
#define DIR_STRUCT_SIZE 43
diff --git a/source3/include/smb_macros.h b/source3/include/smb_macros.h
index 0dfb596994..9bacdce1db 100644
--- a/source3/include/smb_macros.h
+++ b/source3/include/smb_macros.h
@@ -158,11 +158,10 @@
#define SMB_LARGE_LKLEN_OFFSET_HIGH(indx) (12 + (20 * (indx)))
#define SMB_LARGE_LKLEN_OFFSET_LOW(indx) (16 + (20 * (indx)))
-#define ERROR_DOS(class,code) error_packet(outbuf,class,code,NT_STATUS_OK,__LINE__,__FILE__)
-#define ERROR_NT(status) error_packet(outbuf,0,0,status,__LINE__,__FILE__)
-#define ERROR_OPEN(status) error_open(outbuf,status,__LINE__,__FILE__)
-#define ERROR_FORCE_NT(status) error_packet(outbuf,-1,-1,status,__LINE__,__FILE__)
-#define ERROR_BOTH(status,class,code) error_packet(outbuf,class,code,status,__LINE__,__FILE__)
+#define ERROR_DOS(class,code) error_packet(inbuf,outbuf,class,code,NT_STATUS_OK,__LINE__,__FILE__)
+#define ERROR_NT(status) error_packet(inbuf,outbuf,0,0,status,__LINE__,__FILE__)
+#define ERROR_FORCE_NT(status) error_packet(inbuf,outbuf,-1,-1,status,__LINE__,__FILE__)
+#define ERROR_BOTH(status,class,code) error_packet(inbuf,outbuf,class,code,status,__LINE__,__FILE__)
#define reply_nterror(req,status) reply_nt_error(req,status,__LINE__,__FILE__)
#define reply_force_nterror(req,status) reply_force_nt_error(req,status,__LINE__,__FILE__)
@@ -170,9 +169,6 @@
#define reply_botherror(req,status,eclass,ecode) reply_both_error(req,eclass,ecode,status,__LINE__,__FILE__)
#define reply_unixerror(req,defclass,deferror) reply_unix_error(req,defclass,deferror,NT_STATUS_OK,__LINE__,__FILE__)
-/* this is how errors are generated */
-#define UNIXERROR(defclass,deferror) unix_error_packet(outbuf,defclass,deferror,NT_STATUS_OK,__LINE__,__FILE__)
-
/* these are the datagram types */
#define DGRAM_DIRECT_UNIQUE 0x10
@@ -189,8 +185,8 @@
#define smb_offset(p,buf) (PTR_DIFF(p,buf+4) + chain_size)
#define smb_len(buf) (PVAL(buf,3)|(PVAL(buf,2)<<8)|((PVAL(buf,1)&1)<<16))
-#define _smb_setlen(buf,len) do { buf[0] = 0; buf[1] = (len&0x10000)>>16; \
- buf[2] = (len&0xFF00)>>8; buf[3] = len&0xFF; } while (0)
+#define _smb_setlen(buf,len) do { buf[0] = 0; buf[1] = ((len)&0x10000)>>16; \
+ buf[2] = ((len)&0xFF00)>>8; buf[3] = (len)&0xFF; } while (0)
#define smb_len_large(buf) (PVAL(buf,3)|(PVAL(buf,2)<<8)|(PVAL(buf,1)<<16))
#define _smb_setlen_large(buf,len) do { buf[0] = 0; buf[1] = ((len)&0xFF0000)>>16; \
diff --git a/source3/include/trans2.h b/source3/include/trans2.h
index f7f3ef2149..8ed075d418 100644
--- a/source3/include/trans2.h
+++ b/source3/include/trans2.h
@@ -566,7 +566,6 @@ findfirst/findnext is SMB_FIND_FILE_UNIX_INFO2.
__u8 * psid_list may be empty
*/
-
/* ... more as we think of them :-). */
/* SMB POSIX ACL definitions. */
@@ -653,6 +652,29 @@ enum smb_whoami_flags {
DOM_SID[] - list of SIDs (may be empty)
*/
+/*
+ * The following trans2 is done between client and server
+ * as a FSINFO call to set up the encryption state for transport
+ * encryption.
+ * This is a subcommand of the TRANS2_QFSINFO.
+ *
+ * The request looks like :
+ *
+ * [data block] -> SPNEGO framed GSSAPI request.
+ *
+ * The reply looks like :
+ *
+ * [data block] -> SPNEGO framed GSSAPI reply - if error
+ * is NT_STATUS_OK then we're done, if it's
+ * NT_STATUS_MORE_PROCESSING_REQUIRED then the
+ * client needs to keep going. If it's an
+ * error it can be any NT_STATUS error.
+ *
+ */
+
+#define SMB_REQUEST_TRANSPORT_ENCRYPTION 0x203 /* QFSINFO */
+
+
/* The query/set info levels for POSIX ACLs. */
#define SMB_QUERY_POSIX_ACL 0x204
#define SMB_SET_POSIX_ACL 0x204
diff --git a/source3/lib/dummysmbd.c b/source3/lib/dummysmbd.c
index e3b179b763..464ba92306 100644
--- a/source3/lib/dummysmbd.c
+++ b/source3/lib/dummysmbd.c
@@ -52,3 +52,23 @@ NTSTATUS can_delete_directory(struct connection_struct *conn,
return NT_STATUS_OK;
}
+NTSTATUS srv_decrypt_buffer(char *buf)
+{
+ return NT_STATUS_OK;
+}
+
+NTSTATUS srv_encrypt_buffer(char *buffer, char **buf_out)
+{
+ *buf_out = buffer;
+ return NT_STATUS_OK;
+}
+
+void srv_free_enc_buffer(char *buf)
+{
+ ;
+}
+
+bool srv_encryption_on(void)
+{
+ return false;
+}
diff --git a/source3/lib/util.c b/source3/lib/util.c
index 11c14ea538..7f8a297fac 100644
--- a/source3/lib/util.c
+++ b/source3/lib/util.c
@@ -605,32 +605,30 @@ void show_msg(char *buf)
}
/*******************************************************************
- Set the length and marker of an smb packet.
+ Set the length and marker of an encrypted smb packet.
********************************************************************/
-void smb_setlen(char *buf,int len)
+void smb_set_enclen(char *buf,int len,uint16 enc_ctx_num)
{
_smb_setlen(buf,len);
SCVAL(buf,4,0xFF);
- SCVAL(buf,5,'S');
- SCVAL(buf,6,'M');
- SCVAL(buf,7,'B');
+ SCVAL(buf,5,'E');
+ SSVAL(buf,6,enc_ctx_num);
}
/*******************************************************************
- Setup the word count and byte count for a smb message.
+ Set the length and marker of an smb packet.
********************************************************************/
-int set_message(char *buf,int num_words,int num_bytes,bool zero)
+void smb_setlen(char *buf,int len)
{
- if (zero && (num_words || num_bytes)) {
- memset(buf + smb_size,'\0',num_words*2 + num_bytes);
- }
- SCVAL(buf,smb_wct,num_words);
- SSVAL(buf,smb_vwv + num_words*SIZEOFWORD,num_bytes);
- smb_setlen(buf,smb_size + num_words*2 + num_bytes - 4);
- return (smb_size + num_words*2 + num_bytes);
+ _smb_setlen(buf,len);
+
+ SCVAL(buf,4,0xFF);
+ SCVAL(buf,5,'S');
+ SCVAL(buf,6,'M');
+ SCVAL(buf,7,'B');
}
/*******************************************************************
@@ -641,21 +639,11 @@ int set_message_bcc(char *buf,int num_bytes)
{
int num_words = CVAL(buf,smb_wct);
SSVAL(buf,smb_vwv + num_words*SIZEOFWORD,num_bytes);
- smb_setlen(buf,smb_size + num_words*2 + num_bytes - 4);
+ _smb_setlen(buf,smb_size + num_words*2 + num_bytes - 4);
return (smb_size + num_words*2 + num_bytes);
}
/*******************************************************************
- Setup only the byte count for a smb message, using the end of the
- message as a marker.
-********************************************************************/
-
-int set_message_end(void *outbuf,void *end_ptr)
-{
- return set_message_bcc((char *)outbuf,PTR_DIFF(end_ptr,smb_buf((char *)outbuf)));
-}
-
-/*******************************************************************
Add a data blob to the end of a smb_buf, adjusting bcc and smb_len.
Return the bytes added
********************************************************************/
diff --git a/source3/lib/util_sock.c b/source3/lib/util_sock.c
index 8f1bd9e686..d16a8f079a 100644
--- a/source3/lib/util_sock.c
+++ b/source3/lib/util_sock.c
@@ -1287,6 +1287,17 @@ bool receive_smb(int fd, char *buffer, unsigned int timeout, enum smb_read_error
return false;
}
+ if (srv_encryption_on()) {
+ NTSTATUS status = srv_decrypt_buffer(buffer);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0, ("receive_smb: SMB decryption failed "
+ "on incoming packet! Error %s\n",
+ nt_errstr(status) ));
+ cond_set_smb_read_error(pre, SMB_READ_BAD_DECRYPT);
+ return false;
+ }
+ }
+
/* Check the incoming SMB signature. */
if (!srv_check_sign_mac(buffer, true)) {
DEBUG(0, ("receive_smb: SMB Signature verification "
@@ -1307,22 +1318,35 @@ bool send_smb(int fd, char *buffer)
size_t len;
size_t nwritten=0;
ssize_t ret;
+ char *buf_out = buffer;
/* Sign the outgoing packet if required. */
- srv_calculate_sign_mac(buffer);
+ srv_calculate_sign_mac(buf_out);
+
+ if (srv_encryption_on()) {
+ NTSTATUS status = srv_encrypt_buffer(buffer, &buf_out);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0, ("send_smb: SMB encryption failed "
+ "on outgoing packet! Error %s\n",
+ nt_errstr(status) ));
+ return false;
+ }
+ }
- len = smb_len(buffer) + 4;
+ len = smb_len(buf_out) + 4;
while (nwritten < len) {
- ret = write_data(fd,buffer+nwritten,len - nwritten);
+ ret = write_data(fd,buf_out+nwritten,len - nwritten);
if (ret <= 0) {
DEBUG(0,("Error writing %d bytes to client. %d. (%s)\n",
(int)len,(int)ret, strerror(errno) ));
+ srv_free_enc_buffer(buf_out);
return false;
}
nwritten += ret;
}
+ srv_free_enc_buffer(buf_out);
return true;
}
diff --git a/source3/libads/ads_status.c b/source3/libads/ads_status.c
index ecef9d224b..29148e8543 100644
--- a/source3/libads/ads_status.c
+++ b/source3/libads/ads_status.c
@@ -141,3 +141,12 @@ const char *ads_errstr(ADS_STATUS status)
}
}
+#ifdef HAVE_GSSAPI
+NTSTATUS gss_err_to_ntstatus(uint32 maj, uint32 min)
+{
+ ADS_STATUS adss = ADS_ERROR_GSS(maj, min);
+ DEBUG(10,("gss_err_to_ntstatus: Error %s\n",
+ ads_errstr(adss) ));
+ return ads_ntstatus(adss);
+}
+#endif
diff --git a/source3/libsmb/cliconnect.c b/source3/libsmb/cliconnect.c
index 33110c803f..4560521d4a 100644
--- a/source3/libsmb/cliconnect.c
+++ b/source3/libsmb/cliconnect.c
@@ -98,7 +98,7 @@ static NTSTATUS cli_session_setup_lanman2(struct cli_state *cli,
/* send a session setup command */
memset(cli->outbuf,'\0',smb_size);
- set_message(cli->outbuf,10, 0, True);
+ cli_set_message(cli->outbuf,10, 0, True);
SCVAL(cli->outbuf,smb_com,SMBsesssetupX);
cli_setup_packet(cli);
@@ -168,7 +168,7 @@ static NTSTATUS cli_session_setup_guest(struct cli_state *cli)
uint32 capabilities = cli_session_setup_capabilities(cli);
memset(cli->outbuf, '\0', smb_size);
- set_message(cli->outbuf,13,0,True);
+ cli_set_message(cli->outbuf,13,0,True);
SCVAL(cli->outbuf,smb_com,SMBsesssetupX);
cli_setup_packet(cli);
@@ -228,7 +228,7 @@ static NTSTATUS cli_session_setup_plaintext(struct cli_state *cli,
fstr_sprintf( lanman, "Samba %s", SAMBA_VERSION_STRING);
memset(cli->outbuf, '\0', smb_size);
- set_message(cli->outbuf,13,0,True);
+ cli_set_message(cli->outbuf,13,0,True);
SCVAL(cli->outbuf,smb_com,SMBsesssetupX);
cli_setup_packet(cli);
@@ -377,7 +377,7 @@ static NTSTATUS cli_session_setup_nt1(struct cli_state *cli, const char *user,
/* send a session setup command */
memset(cli->outbuf,'\0',smb_size);
- set_message(cli->outbuf,13,0,True);
+ cli_set_message(cli->outbuf,13,0,True);
SCVAL(cli->outbuf,smb_com,SMBsesssetupX);
cli_setup_packet(cli);
@@ -457,7 +457,7 @@ static bool cli_session_setup_blob_send(struct cli_state *cli, DATA_BLOB blob)
/* send a session setup command */
memset(cli->outbuf,'\0',smb_size);
- set_message(cli->outbuf,12,0,True);
+ cli_set_message(cli->outbuf,12,0,True);
SCVAL(cli->outbuf,smb_com,SMBsesssetupX);
cli_setup_packet(cli);
@@ -1028,7 +1028,7 @@ NTSTATUS cli_session_setup(struct cli_state *cli,
bool cli_ulogoff(struct cli_state *cli)
{
memset(cli->outbuf,'\0',smb_size);
- set_message(cli->outbuf,2,0,True);
+ cli_set_message(cli->outbuf,2,0,True);
SCVAL(cli->outbuf,smb_com,SMBulogoffX);
cli_setup_packet(cli);
SSVAL(cli->outbuf,smb_vwv0,0xFF);
@@ -1106,7 +1106,7 @@ bool cli_send_tconX(struct cli_state *cli,
slprintf(fullshare, sizeof(fullshare)-1,
"\\\\%s\\%s", cli->desthost, share);
- set_message(cli->outbuf,4, 0, True);
+ cli_set_message(cli->outbuf,4, 0, True);
SCVAL(cli->outbuf,smb_com,SMBtconX);
cli_setup_packet(cli);
@@ -1157,7 +1157,7 @@ bool cli_send_tconX(struct cli_state *cli,
bool cli_tdis(struct cli_state *cli)
{
memset(cli->outbuf,'\0',smb_size);
- set_message(cli->outbuf,0,0,True);
+ cli_set_message(cli->outbuf,0,0,True);
SCVAL(cli->outbuf,smb_com,SMBtdis);
SSVAL(cli->outbuf,smb_tid,cli->cnum);
cli_setup_packet(cli);
@@ -1189,7 +1189,7 @@ void cli_negprot_send(struct cli_state *cli)
memset(cli->outbuf,'\0',smb_size);
/* setup the protocol strings */
- set_message(cli->outbuf,0,0,True);
+ cli_set_message(cli->outbuf,0,0,True);
p = smb_buf(cli->outbuf);
for (numprots=0;
@@ -1229,7 +1229,7 @@ bool cli_negprot(struct cli_state *cli)
numprots++)
plength += strlen(prots[numprots].name)+2;
- set_message(cli->outbuf,0,plength,True);
+ cli_set_message(cli->outbuf,0,plength,True);
p = smb_buf(cli->outbuf);
for (numprots=0;
@@ -1806,7 +1806,7 @@ NTSTATUS cli_raw_tcon(struct cli_state *cli,
memset(cli->outbuf,'\0',smb_size);
memset(cli->inbuf,'\0',smb_size);
- set_message(cli->outbuf, 0, 0, True);
+ cli_set_message(cli->outbuf, 0, 0, True);
SCVAL(cli->outbuf,smb_com,SMBtcon);
cli_setup_packet(cli);
diff --git a/source3/libsmb/clidgram.c b/source3/libsmb/clidgram.c
index 76630bd504..66c6ee1022 100644
--- a/source3/libsmb/clidgram.c
+++ b/source3/libsmb/clidgram.c
@@ -81,7 +81,7 @@ bool cli_send_mailslot(struct messaging_context *msg_ctx,
return False;
}
- set_message(ptr,17,strlen(mailslot) + 1 + len,True);
+ cli_set_message(ptr,17,strlen(mailslot) + 1 + len,True);
memcpy(ptr,tmp,4);
SCVAL(ptr,smb_com,SMBtrans);
diff --git a/source3/libsmb/clientgen.c b/source3/libsmb/clientgen.c
index 0544b3d879..da225ebc24 100644
--- a/source3/libsmb/clientgen.c
+++ b/source3/libsmb/clientgen.c
@@ -20,6 +20,21 @@
#include "includes.h"
+/*******************************************************************
+ Setup the word count and byte count for a client smb message.
+********************************************************************/
+
+int cli_set_message(char *buf,int num_words,int num_bytes,bool zero)
+{
+ if (zero && (num_words || num_bytes)) {
+ memset(buf + smb_size,'\0',num_words*2 + num_bytes);
+ }
+ SCVAL(buf,smb_wct,num_words);
+ SSVAL(buf,smb_vwv + num_words*SIZEOFWORD,num_bytes);
+ smb_setlen(buf,smb_size + num_words*2 + num_bytes - 4);
+ return (smb_size + num_words*2 + num_bytes);
+}
+
/****************************************************************************
Change the timeout (in milliseconds).
****************************************************************************/
@@ -85,7 +100,7 @@ bool cli_receive_smb(struct cli_state *cli)
/* fd == -1 causes segfaults -- Tom (tom@ninja.nl) */
if (cli->fd == -1)
- return False;
+ return false;
again:
len = client_receive_smb(cli, 0);
@@ -100,7 +115,7 @@ bool cli_receive_smb(struct cli_state *cli)
int fnum = SVAL(cli->inbuf,smb_vwv2);
unsigned char level = CVAL(cli->inbuf,smb_vwv3+1);
if (!cli->oplock_handler(cli, fnum, level)) {
- return False;
+ return false;
}
}
/* try to prevent loops */
@@ -114,7 +129,7 @@ bool cli_receive_smb(struct cli_state *cli)
DEBUG(0, ("Receiving SMB: Server stopped responding\n"));
close(cli->fd);
cli->fd = -1;
- return False;
+ return false;
}
if (!cli_check_sign_mac(cli)) {
@@ -135,16 +150,16 @@ bool cli_receive_smb(struct cli_state *cli)
* Set bad sig but don't close fd.
*/
cli->smb_rw_error = SMB_READ_BAD_SIG;
- return True;
+ return true;
}
DEBUG(0, ("SMB Signature verification failed on incoming packet!\n"));
cli->smb_rw_error = SMB_READ_BAD_SIG;
close(cli->fd);
cli->fd = -1;
- return False;
+ return false;
};
- return True;
+ return true;
}
/****************************************************************************
@@ -164,6 +179,7 @@ ssize_t cli_receive_smb_data(struct cli_state *cli, char *buffer, size_t len)
/****************************************************************************
Read a smb readX header.
+ We can only use this if encryption and signing are off.
****************************************************************************/
bool cli_receive_smb_readX_header(struct cli_state *cli)
@@ -171,7 +187,7 @@ bool cli_receive_smb_readX_header(struct cli_state *cli)
ssize_t len, offset;
if (cli->fd == -1)
- return False;
+ return false;
again:
@@ -199,7 +215,7 @@ bool cli_receive_smb_readX_header(struct cli_state *cli)
if (cli->oplock_handler) {
int fnum = SVAL(cli->inbuf,smb_vwv2);
unsigned char level = CVAL(cli->inbuf,smb_vwv3+1);
- if (!cli->oplock_handler(cli, fnum, level)) return False;
+ if (!cli->oplock_handler(cli, fnum, level)) return false;
}
/* try to prevent loops */
SCVAL(cli->inbuf,smb_com,0xFF);
@@ -238,14 +254,14 @@ bool cli_receive_smb_readX_header(struct cli_state *cli)
}
}
- return True;
+ return true;
read_err:
cli->smb_rw_error = SMB_READ_ERROR;
close(cli->fd);
cli->fd = -1;
- return False;
+ return false;
}
static ssize_t write_socket(int fd, const char *buf, size_t len)
@@ -272,32 +288,54 @@ bool cli_send_smb(struct cli_state *cli)
size_t len;
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)
- return False;
+ return false;
cli_calculate_sign_mac(cli);
- len = smb_len(cli->outbuf) + 4;
+ if (enc_on) {
+ NTSTATUS status = cli_encrypt_message(cli, &buf_out);
+ if (!NT_STATUS_IS_OK(status)) {
+ close(cli->fd);
+ cli->fd = -1;
+ cli->smb_rw_error = SMB_WRITE_ERROR;
+ DEBUG(0,("Error in encrypting client message. Error %s\n",
+ nt_errstr(status) ));
+ return false;
+ }
+ }
+
+ len = smb_len(buf_out) + 4;
while (nwritten < len) {
- ret = write_socket(cli->fd,cli->outbuf+nwritten,len - nwritten);
+ ret = write_socket(cli->fd,buf_out+nwritten,len - nwritten);
if (ret <= 0) {
+ if (enc_on) {
+ cli_free_enc_buffer(cli, buf_out);
+ }
close(cli->fd);
cli->fd = -1;
cli->smb_rw_error = SMB_WRITE_ERROR;
DEBUG(0,("Error writing %d bytes to client. %d (%s)\n",
(int)len,(int)ret, strerror(errno) ));
- return False;
+ return false;
}
nwritten += ret;
}
+
+ if (enc_on) {
+ cli_free_enc_buffer(cli, buf_out);
+ }
+
/* Increment the mid so we can tell between responses. */
cli->mid++;
if (!cli->mid)
cli->mid++;
- return True;
+ return true;
}
/****************************************************************************
@@ -347,7 +385,7 @@ bool cli_send_smb_direct_writeX(struct cli_state *cli,
DEBUG(0,("Error writing %d extradata "
"bytes to client. %d (%s)\n",
(int)extradata,(int)ret, strerror(errno) ));
- return False;
+ return false;
}
nwritten += ret;
}
@@ -409,7 +447,7 @@ void cli_init_creds(struct cli_state *cli, const char *username, const char *dom
fstrcpy(cli->user_name, username);
pwd_set_cleartext(&cli->pwd, password);
if (!*username) {
- cli->pwd.null_pwd = True;
+ cli->pwd.null_pwd = true;
}
DEBUG(10,("cli_init_creds: user %s domain %s\n", cli->user_name, cli->domain));
@@ -424,16 +462,16 @@ void cli_setup_signing_state(struct cli_state *cli, int signing_state)
if (signing_state == Undefined)
return;
- if (signing_state == False) {
- cli->sign_info.allow_smb_signing = False;
- cli->sign_info.mandatory_signing = False;
+ if (signing_state == false) {
+ cli->sign_info.allow_smb_signing = false;
+ cli->sign_info.mandatory_signing = false;
return;
}
- cli->sign_info.allow_smb_signing = True;
+ cli->sign_info.allow_smb_signing = true;
if (signing_state == Required)
- cli->sign_info.mandatory_signing = True;
+ cli->sign_info.mandatory_signing = true;
}
/****************************************************************************
@@ -470,7 +508,7 @@ struct cli_state *cli_initialise(void)
cli->outbuf = (char *)SMB_MALLOC(cli->bufsize+SAFETY_MARGIN);
cli->inbuf = (char *)SMB_MALLOC(cli->bufsize+SAFETY_MARGIN);
cli->oplock_handler = cli_oplock_ack;
- cli->case_sensitive = False;
+ cli->case_sensitive = false;
cli->smb_rw_error = SMB_READ_OK;
cli->use_spnego = lp_client_use_spnego();
@@ -481,13 +519,13 @@ struct cli_state *cli_initialise(void)
client routines using DOS errors instead of STATUS32
ones. This intended only as a temporary hack. */
if (getenv("CLI_FORCE_DOSERR"))
- cli->force_dos_errors = True;
+ cli->force_dos_errors = true;
if (lp_client_signing())
- cli->sign_info.allow_smb_signing = True;
+ cli->sign_info.allow_smb_signing = true;
if (lp_client_signing() == Required)
- cli->sign_info.mandatory_signing = True;
+ cli->sign_info.mandatory_signing = true;
if (!cli->outbuf || !cli->inbuf)
goto error;
@@ -522,7 +560,7 @@ struct cli_state *cli_initialise(void)
/****************************************************************************
External interface.
Close an open named pipe over SMB. Free any authentication data.
- Returns False if the cli_close call failed.
+ Returns false if the cli_close call failed.
****************************************************************************/
bool cli_rpc_pipe_close(struct rpc_pipe_client *cli)
@@ -530,7 +568,7 @@ bool cli_rpc_pipe_close(struct rpc_pipe_client *cli)
bool ret;
if (!cli) {
- return False;
+ return false;
}
ret = cli_close(cli->cli, cli->fnum);
@@ -650,15 +688,15 @@ bool cli_send_keepalive(struct cli_state *cli)
{
if (cli->fd == -1) {
DEBUG(3, ("cli_send_keepalive: fd == -1\n"));
- return False;
+ return false;
}
if (!send_keepalive(cli->fd)) {
close(cli->fd);
cli->fd = -1;
DEBUG(0,("Error sending keepalive packet to client.\n"));
- return False;
+ return false;
}
- return True;
+ return true;
}
/****************************************************************************
@@ -674,7 +712,7 @@ bool cli_echo(struct cli_state *cli, uint16 num_echos,
SMB_ASSERT(length < 1024);
memset(cli->outbuf,'\0',smb_size);
- set_message(cli->outbuf,1,length,True);
+ cli_set_message(cli->outbuf,1,length,true);
SCVAL(cli->outbuf,smb_com,SMBecho);
SSVAL(cli->outbuf,smb_tid,65535);
SSVAL(cli->outbuf,smb_vwv0,num_echos);
@@ -689,13 +727,13 @@ bool cli_echo(struct cli_state *cli, uint16 num_echos,
for (i=0; i<num_echos; i++) {
if (!cli_receive_smb(cli)) {
- return False;
+ return false;
}
if (cli_is_error(cli)) {
- return False;
+ return false;
}
}
- return True;
+ return true;
}
diff --git a/source3/libsmb/clifile.c b/source3/libsmb/clifile.c
index 001a42d4b9..9b4c380d40 100644
--- a/source3/libsmb/clifile.c
+++ b/source3/libsmb/clifile.c
@@ -429,7 +429,7 @@ bool cli_rename(struct cli_state *cli, const char *fname_src, const char *fname_
memset(cli->outbuf,'\0',smb_size);
memset(cli->inbuf,'\0',smb_size);
- set_message(cli->outbuf,1, 0, true);
+ cli_set_message(cli->outbuf,1, 0, true);
SCVAL(cli->outbuf,smb_com,SMBmv);
SSVAL(cli->outbuf,smb_tid,cli->cnum);
@@ -470,7 +470,7 @@ bool cli_ntrename(struct cli_state *cli, const char *fname_src, const char *fnam
memset(cli->outbuf,'\0',smb_size);
memset(cli->inbuf,'\0',smb_size);
- set_message(cli->outbuf, 4, 0, true);
+ cli_set_message(cli->outbuf, 4, 0, true);
SCVAL(cli->outbuf,smb_com,SMBntrename);
SSVAL(cli->outbuf,smb_tid,cli->cnum);
@@ -512,7 +512,7 @@ bool cli_nt_hardlink(struct cli_state *cli, const char *fname_src, const char *f
memset(cli->outbuf,'\0',smb_size);
memset(cli->inbuf,'\0',smb_size);
- set_message(cli->outbuf, 4, 0, true);
+ cli_set_message(cli->outbuf, 4, 0, true);
SCVAL(cli->outbuf,smb_com,SMBntrename);
SSVAL(cli->outbuf,smb_tid,cli->cnum);
@@ -554,7 +554,7 @@ bool cli_unlink_full(struct cli_state *cli, const char *fname, uint16 attrs)
memset(cli->outbuf,'\0',smb_size);
memset(cli->inbuf,'\0',smb_size);
- set_message(cli->outbuf,1, 0, true);
+ cli_set_message(cli->outbuf,1, 0, true);
SCVAL(cli->outbuf,smb_com,SMBunlink);
SSVAL(cli->outbuf,smb_tid,cli->cnum);
@@ -600,7 +600,7 @@ bool cli_mkdir(struct cli_state *cli, const char *dname)
memset(cli->outbuf,'\0',smb_size);
memset(cli->inbuf,'\0',smb_size);
- set_message(cli->outbuf,0, 0, true);
+ cli_set_message(cli->outbuf,0, 0, true);
SCVAL(cli->outbuf,smb_com,SMBmkdir);
SSVAL(cli->outbuf,smb_tid,cli->cnum);
@@ -636,7 +636,7 @@ bool cli_rmdir(struct cli_state *cli, const char *dname)
memset(cli->outbuf,'\0',smb_size);
memset(cli->inbuf,'\0',smb_size);
- set_message(cli->outbuf,0, 0, true);
+ cli_set_message(cli->outbuf,0, 0, true);
SCVAL(cli->outbuf,smb_com,SMBrmdir);
SSVAL(cli->outbuf,smb_tid,cli->cnum);
@@ -719,7 +719,7 @@ int cli_nt_create_full(struct cli_state *cli, const char *fname,
memset(cli->outbuf,'\0',smb_size);
memset(cli->inbuf,'\0',smb_size);
- set_message(cli->outbuf,24,0, true);
+ cli_set_message(cli->outbuf,24,0, true);
SCVAL(cli->outbuf,smb_com,SMBntcreateX);
SSVAL(cli->outbuf,smb_tid,cli->cnum);
@@ -815,7 +815,7 @@ int cli_open(struct cli_state *cli, const char *fname, int flags, int share_mode
memset(cli->outbuf,'\0',smb_size);
memset(cli->inbuf,'\0',smb_size);
- set_message(cli->outbuf,15,0, true);
+ cli_set_message(cli->outbuf,15,0, true);
SCVAL(cli->outbuf,smb_com,SMBopenX);
SSVAL(cli->outbuf,smb_tid,cli->cnum);
@@ -863,7 +863,7 @@ bool cli_close(struct cli_state *cli, int fnum)
memset(cli->outbuf,'\0',smb_size);
memset(cli->inbuf,'\0',smb_size);
- set_message(cli->outbuf,3,0,True);
+ cli_set_message(cli->outbuf,3,0,True);
SCVAL(cli->outbuf,smb_com,SMBclose);
SSVAL(cli->outbuf,smb_tid,cli->cnum);
@@ -896,7 +896,7 @@ NTSTATUS cli_locktype(struct cli_state *cli, int fnum,
memset(cli->outbuf,'\0',smb_size);
memset(cli->inbuf,'\0', smb_size);
- set_message(cli->outbuf,8,0,True);
+ cli_set_message(cli->outbuf,8,0,True);
SCVAL(cli->outbuf,smb_com,SMBlockingX);
SSVAL(cli->outbuf,smb_tid,cli->cnum);
@@ -948,7 +948,7 @@ bool cli_lock(struct cli_state *cli, int fnum,
memset(cli->outbuf,'\0',smb_size);
memset(cli->inbuf,'\0', smb_size);
- set_message(cli->outbuf,8,0,True);
+ cli_set_message(cli->outbuf,8,0,True);
SCVAL(cli->outbuf,smb_com,SMBlockingX);
SSVAL(cli->outbuf,smb_tid,cli->cnum);
@@ -1001,7 +1001,7 @@ bool cli_unlock(struct cli_state *cli, int fnum, uint32 offset, uint32 len)
memset(cli->outbuf,'\0',smb_size);
memset(cli->inbuf,'\0',smb_size);
- set_message(cli->outbuf,8,0,True);
+ cli_set_message(cli->outbuf,8,0,True);
SCVAL(cli->outbuf,smb_com,SMBlockingX);
SSVAL(cli->outbuf,smb_tid,cli->cnum);
@@ -1053,7 +1053,7 @@ bool cli_lock64(struct cli_state *cli, int fnum,
memset(cli->outbuf,'\0',smb_size);
memset(cli->inbuf,'\0', smb_size);
- set_message(cli->outbuf,8,0,True);
+ cli_set_message(cli->outbuf,8,0,True);
SCVAL(cli->outbuf,smb_com,SMBlockingX);
SSVAL(cli->outbuf,smb_tid,cli->cnum);
@@ -1108,7 +1108,7 @@ bool cli_unlock64(struct cli_state *cli, int fnum, SMB_BIG_UINT offset, SMB_BIG_
memset(cli->outbuf,'\0',smb_size);
memset(cli->inbuf,'\0',smb_size);
- set_message(cli->outbuf,8,0,True);
+ cli_set_message(cli->outbuf,8,0,True);
SCVAL(cli->outbuf,smb_com,SMBlockingX);
SSVAL(cli->outbuf,smb_tid,cli->cnum);
@@ -1255,7 +1255,7 @@ bool cli_getattrE(struct cli_state *cli, int fd,
memset(cli->outbuf,'\0',smb_size);
memset(cli->inbuf,'\0',smb_size);
- set_message(cli->outbuf,1,0,True);
+ cli_set_message(cli->outbuf,1,0,True);
SCVAL(cli->outbuf,smb_com,SMBgetattrE);
SSVAL(cli->outbuf,smb_tid,cli->cnum);
@@ -1307,7 +1307,7 @@ bool cli_getatr(struct cli_state *cli, const char *fname,
memset(cli->outbuf,'\0',smb_size);
memset(cli->inbuf,'\0',smb_size);
- set_message(cli->outbuf,0,0,True);
+ cli_set_message(cli->outbuf,0,0,True);
SCVAL(cli->outbuf,smb_com,SMBgetatr);
SSVAL(cli->outbuf,smb_tid,cli->cnum);
@@ -1359,7 +1359,7 @@ bool cli_setattrE(struct cli_state *cli, int fd,
memset(cli->outbuf,'\0',smb_size);
memset(cli->inbuf,'\0',smb_size);
- set_message(cli->outbuf,7,0,True);
+ cli_set_message(cli->outbuf,7,0,True);
SCVAL(cli->outbuf,smb_com,SMBsetattrE);
SSVAL(cli->outbuf,smb_tid,cli->cnum);
@@ -1398,7 +1398,7 @@ bool cli_setatr(struct cli_state *cli, const char *fname, uint16 attr, time_t t)
memset(cli->outbuf,'\0',smb_size);
memset(cli->inbuf,'\0',smb_size);
- set_message(cli->outbuf,8,0,True);
+ cli_set_message(cli->outbuf,8,0,True);
SCVAL(cli->outbuf,smb_com,SMBsetatr);
SSVAL(cli->outbuf,smb_tid,cli->cnum);
@@ -1452,7 +1452,7 @@ bool cli_chkpath(struct cli_state *cli, const char *path)
}
memset(cli->outbuf,'\0',smb_size);
- set_message(cli->outbuf,0,0,True);
+ cli_set_message(cli->outbuf,0,0,True);
SCVAL(cli->outbuf,smb_com,SMBcheckpath);
SSVAL(cli->outbuf,smb_tid,cli->cnum);
cli_setup_packet(cli);
@@ -1483,7 +1483,7 @@ bool cli_chkpath(struct cli_state *cli, const char *path)
bool cli_dskattr(struct cli_state *cli, int *bsize, int *total, int *avail)
{
memset(cli->outbuf,'\0',smb_size);
- set_message(cli->outbuf,0,0,True);
+ cli_set_message(cli->outbuf,0,0,True);
SCVAL(cli->outbuf,smb_com,SMBdskattr);
SSVAL(cli->outbuf,smb_tid,cli->cnum);
cli_setup_packet(cli);
@@ -1512,7 +1512,7 @@ int cli_ctemp(struct cli_state *cli, const char *path, char **tmp_path)
memset(cli->outbuf,'\0',smb_size);
memset(cli->inbuf,'\0',smb_size);
- set_message(cli->outbuf,3,0,True);
+ cli_set_message(cli->outbuf,3,0,True);
SCVAL(cli->outbuf,smb_com,SMBctemp);
SSVAL(cli->outbuf,smb_tid,cli->cnum);
@@ -1565,7 +1565,7 @@ NTSTATUS cli_raw_ioctl(struct cli_state *cli, int fnum, uint32 code, DATA_BLOB *
memset(cli->outbuf,'\0',smb_size);
memset(cli->inbuf,'\0',smb_size);
- set_message(cli->outbuf, 3, 0, True);
+ cli_set_message(cli->outbuf, 3, 0, True);
SCVAL(cli->outbuf,smb_com,SMBioctl);
cli_setup_packet(cli);
diff --git a/source3/libsmb/clifsinfo.c b/source3/libsmb/clifsinfo.c
index 1a75d144b2..107613c618 100644
--- a/source3/libsmb/clifsinfo.c
+++ b/source3/libsmb/clifsinfo.c
@@ -2,6 +2,7 @@
Unix SMB/CIFS implementation.
FS info functions
Copyright (C) Stefan (metze) Metzmacher 2003
+ 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
@@ -301,3 +302,335 @@ cleanup:
return ret;
}
+
+/******************************************************************************
+ Send/receive the request encryption blob.
+******************************************************************************/
+
+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];
+ char *rparam=NULL, *rdata=NULL;
+ unsigned int rparam_count=0, rdata_count=0;
+ NTSTATUS status = NT_STATUS_OK;
+
+ setup = TRANSACT2_SETFSINFO;
+
+ SSVAL(param,0,0);
+ SSVAL(param,2,SMB_REQUEST_TRANSPORT_ENCRYPTION);
+
+ if (!cli_send_trans(cli, SMBtrans2,
+ NULL,
+ 0, 0,
+ &setup, 1, 0,
+ param, 4, 0,
+ (char *)in->data, in->length, CLI_BUFFER_SIZE)) {
+ status = cli_nt_error(cli);
+ goto out;
+ }
+
+ if (!cli_receive_trans(cli, SMBtrans2,
+ &rparam, &rparam_count,
+ &rdata, &rdata_count)) {
+ status = cli_nt_error(cli);
+ goto out;
+ }
+
+ if (cli_is_error(cli)) {
+ status = cli_nt_error(cli);
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
+ goto out;
+ }
+ }
+
+ *out = data_blob(rdata, rdata_count);
+ *param_out = data_blob(rparam, rparam_count);
+
+ out:
+
+ SAFE_FREE(rparam);
+ SAFE_FREE(rdata);
+ return status;
+}
+
+/******************************************************************************
+ Make a client state struct.
+******************************************************************************/
+
+static struct smb_trans_enc_state *make_cli_enc_state(enum smb_trans_enc_type smb_enc_type)
+{
+ struct smb_trans_enc_state *es = NULL;
+ es = SMB_MALLOC_P(struct smb_trans_enc_state);
+ if (!es) {
+ return NULL;
+ }
+ ZERO_STRUCTP(es);
+ es->smb_enc_type = smb_enc_type;
+
+ if (smb_enc_type == SMB_TRANS_ENC_GSS) {
+#if defined(HAVE_GSSAPI) && defined(HAVE_KRB5)
+ es->s.gss_state = SMB_MALLOC_P(struct smb_tran_enc_state_gss);
+ if (!es->s.gss_state) {
+ SAFE_FREE(es);
+ return NULL;
+ }
+ ZERO_STRUCTP(es->s.gss_state);
+#else
+ DEBUG(0,("make_cli_enc_state: no krb5 compiled.\n"));
+ SAFE_FREE(es);
+ return NULL;
+#endif
+ }
+ return es;
+}
+
+/******************************************************************************
+ Start a raw ntlmssp encryption.
+******************************************************************************/
+
+NTSTATUS cli_raw_ntlm_smb_encryption_start(struct cli_state *cli,
+ const char *user,
+ const char *pass,
+ const char *domain)
+{
+ DATA_BLOB blob_in = data_blob_null;
+ DATA_BLOB blob_out = data_blob_null;
+ DATA_BLOB param_out = data_blob_null;
+ NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
+ struct smb_trans_enc_state *es = make_cli_enc_state(SMB_TRANS_ENC_NTLM);
+
+ if (!es) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ status = ntlmssp_client_start(&es->s.ntlmssp_state);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto fail;
+ }
+
+ ntlmssp_want_feature(es->s.ntlmssp_state, NTLMSSP_FEATURE_SESSION_KEY);
+ es->s.ntlmssp_state->neg_flags |= (NTLMSSP_NEGOTIATE_SIGN|NTLMSSP_NEGOTIATE_SEAL);
+
+ if (!NT_STATUS_IS_OK(status = ntlmssp_set_username(es->s.ntlmssp_state, user))) {
+ goto fail;
+ }
+ if (!NT_STATUS_IS_OK(status = ntlmssp_set_domain(es->s.ntlmssp_state, domain))) {
+ goto fail;
+ }
+ if (!NT_STATUS_IS_OK(status = ntlmssp_set_password(es->s.ntlmssp_state, pass))) {
+ goto fail;
+ }
+
+ do {
+ status = ntlmssp_update(es->s.ntlmssp_state, blob_in, &blob_out);
+ data_blob_free(&blob_in);
+ data_blob_free(&param_out);
+ if (NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED) || NT_STATUS_IS_OK(status)) {
+ NTSTATUS trans_status = enc_blob_send_receive(cli,
+ &blob_out,
+ &blob_in,
+ &param_out);
+ if (!NT_STATUS_EQUAL(trans_status,
+ NT_STATUS_MORE_PROCESSING_REQUIRED) &&
+ !NT_STATUS_IS_OK(trans_status)) {
+ status = trans_status;
+ } else {
+ 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));
+
+ data_blob_free(&blob_in);
+
+ if (NT_STATUS_IS_OK(status)) {
+ /* Replace the old state, if any. */
+ if (cli->trans_enc_state) {
+ common_free_encryption_state(&cli->trans_enc_state);
+ }
+ cli->trans_enc_state = es;
+ cli->trans_enc_state->enc_on = True;
+ es = NULL;
+ }
+
+ fail:
+
+ common_free_encryption_state(&es);
+ return status;
+}
+
+#if defined(HAVE_GSSAPI) && defined(HAVE_KRB5)
+
+#ifndef SMB_GSS_REQUIRED_FLAGS
+#define SMB_GSS_REQUIRED_FLAGS (GSS_C_CONF_FLAG|GSS_C_INTEG_FLAG|GSS_C_MUTUAL_FLAG|GSS_C_REPLAY_FLAG|GSS_C_SEQUENCE_FLAG)
+#endif
+
+/******************************************************************************
+ Get client gss blob to send to a server.
+******************************************************************************/
+
+static NTSTATUS make_cli_gss_blob(struct smb_trans_enc_state *es,
+ const char *service,
+ const char *host,
+ NTSTATUS status_in,
+ DATA_BLOB spnego_blob_in,
+ DATA_BLOB *p_blob_out)
+{
+ const char *krb_mechs[] = {OID_KERBEROS5, NULL};
+ OM_uint32 ret;
+ OM_uint32 min;
+ gss_name_t srv_name;
+ gss_buffer_desc input_name;
+ gss_buffer_desc *p_tok_in;
+ gss_buffer_desc tok_out, tok_in;
+ DATA_BLOB blob_out = data_blob_null;
+ DATA_BLOB blob_in = data_blob_null;
+ char *host_princ_s = NULL;
+ OM_uint32 ret_flags = 0;
+ NTSTATUS status = NT_STATUS_OK;
+
+ gss_OID_desc nt_hostbased_service =
+ {10, CONST_DISCARD(char *,"\x2a\x86\x48\x86\xf7\x12\x01\x02\x01\x04")};
+
+ memset(&tok_out, '\0', sizeof(tok_out));
+
+ /* Get a ticket for the service@host */
+ asprintf(&host_princ_s, "%s@%s", service, host);
+ if (host_princ_s == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ input_name.value = host_princ_s;
+ input_name.length = strlen(host_princ_s) + 1;
+
+ ret = gss_import_name(&min,
+ &input_name,
+ &nt_hostbased_service,
+ &srv_name);
+
+ if (ret != GSS_S_COMPLETE) {
+ SAFE_FREE(host_princ_s);
+ return map_nt_error_from_gss(ret, min);
+ }
+
+ if (spnego_blob_in.length == 0) {
+ p_tok_in = GSS_C_NO_BUFFER;
+ } else {
+ /* Remove the SPNEGO wrapper */
+ if (!spnego_parse_auth_response(spnego_blob_in, status_in, OID_KERBEROS5, &blob_in)) {
+ status = NT_STATUS_UNSUCCESSFUL;
+ goto fail;
+ }
+ tok_in.value = blob_in.data;
+ tok_in.length = blob_in.length;
+ p_tok_in = &tok_in;
+ }
+
+ ret = gss_init_sec_context(&min,
+ GSS_C_NO_CREDENTIAL, /* Use our default cred. */
+ &es->s.gss_state->gss_ctx,
+ srv_name,
+ GSS_C_NO_OID, /* default OID. */
+ GSS_C_MUTUAL_FLAG | GSS_C_REPLAY_FLAG | GSS_C_SEQUENCE_FLAG,
+ GSS_C_INDEFINITE, /* requested ticket lifetime. */
+ NULL, /* no channel bindings */
+ p_tok_in,
+ NULL, /* ignore mech type */
+ &tok_out,
+ &ret_flags,
+ NULL); /* ignore time_rec */
+
+ status = map_nt_error_from_gss(ret, min);
+ if (!NT_STATUS_IS_OK(status) && !NT_STATUS_EQUAL(status,NT_STATUS_MORE_PROCESSING_REQUIRED)) {
+ ADS_STATUS adss = ADS_ERROR_GSS(ret, min);
+ DEBUG(10,("make_cli_gss_blob: gss_init_sec_context failed with %s\n",
+ ads_errstr(adss)));
+ goto fail;
+ }
+
+ if ((ret_flags & SMB_GSS_REQUIRED_FLAGS) != SMB_GSS_REQUIRED_FLAGS) {
+ status = NT_STATUS_ACCESS_DENIED;
+ }
+
+ blob_out = data_blob(tok_out.value, tok_out.length);
+
+ /* Wrap in an SPNEGO wrapper */
+ *p_blob_out = gen_negTokenTarg(krb_mechs, blob_out);
+
+ fail:
+
+ data_blob_free(&blob_out);
+ data_blob_free(&blob_in);
+ SAFE_FREE(host_princ_s);
+ gss_release_name(&min, &srv_name);
+ if (tok_out.value) {
+ gss_release_buffer(&min, &tok_out);
+ }
+ return status;
+}
+
+/******************************************************************************
+ Start a SPNEGO gssapi encryption context.
+******************************************************************************/
+
+NTSTATUS cli_gss_smb_encryption_start(struct cli_state *cli)
+{
+ DATA_BLOB blob_recv = data_blob_null;
+ DATA_BLOB blob_send = data_blob_null;
+ DATA_BLOB param_out = data_blob_null;
+ NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
+ fstring fqdn;
+ const char *servicename;
+ struct smb_trans_enc_state *es = make_cli_enc_state(SMB_TRANS_ENC_GSS);
+
+ if (!es) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ name_to_fqdn(fqdn, cli->desthost);
+ strlower_m(fqdn);
+
+ servicename = "cifs";
+ status = make_cli_gss_blob(es, servicename, fqdn, NT_STATUS_OK, blob_recv, &blob_send);
+ if (!NT_STATUS_EQUAL(status,NT_STATUS_MORE_PROCESSING_REQUIRED)) {
+ servicename = "host";
+ status = make_cli_gss_blob(es, servicename, fqdn, NT_STATUS_OK, blob_recv, &blob_send);
+ if (!NT_STATUS_EQUAL(status,NT_STATUS_MORE_PROCESSING_REQUIRED)) {
+ goto fail;
+ }
+ }
+
+ do {
+ data_blob_free(&blob_recv);
+ status = enc_blob_send_receive(cli, &blob_send, &blob_recv, &param_out);
+ if (param_out.length == 2) {
+ es->enc_ctx_num = SVAL(param_out.data, 0);
+ }
+ data_blob_free(&blob_send);
+ status = make_cli_gss_blob(es, servicename, fqdn, status, blob_recv, &blob_send);
+ } while (NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED));
+ data_blob_free(&blob_recv);
+
+ if (NT_STATUS_IS_OK(status)) {
+ /* Replace the old state, if any. */
+ if (cli->trans_enc_state) {
+ common_free_encryption_state(&cli->trans_enc_state);
+ }
+ cli->trans_enc_state = es;
+ cli->trans_enc_state->enc_on = True;
+ es = NULL;
+ }
+
+ fail:
+
+ common_free_encryption_state(&es);
+ return status;
+}
+#else
+NTSTATUS cli_gss_smb_encryption_start(struct cli_state *cli)
+{
+ return NT_STATUS_NOT_SUPPORTED;
+}
+#endif
diff --git a/source3/libsmb/clilist.c b/source3/libsmb/clilist.c
index 2e4c360507..d5c7db09e9 100644
--- a/source3/libsmb/clilist.c
+++ b/source3/libsmb/clilist.c
@@ -521,7 +521,7 @@ int cli_list_old(struct cli_state *cli,const char *Mask,uint16 attribute,
memset(cli->outbuf,'\0',smb_size);
memset(cli->inbuf,'\0',smb_size);
- set_message(cli->outbuf,2,0,True);
+ cli_set_message(cli->outbuf,2,0,True);
SCVAL(cli->outbuf,smb_com,SMBsearch);
@@ -581,7 +581,7 @@ int cli_list_old(struct cli_state *cli,const char *Mask,uint16 attribute,
memset(cli->outbuf,'\0',smb_size);
memset(cli->inbuf,'\0',smb_size);
- set_message(cli->outbuf,2,0,True);
+ cli_set_message(cli->outbuf,2,0,True);
SCVAL(cli->outbuf,smb_com,SMBfclose);
SSVAL(cli->outbuf,smb_tid,cli->cnum);
cli_setup_packet(cli);
diff --git a/source3/libsmb/climessage.c b/source3/libsmb/climessage.c
index 13ef1d43d4..00c25aa725 100644
--- a/source3/libsmb/climessage.c
+++ b/source3/libsmb/climessage.c
@@ -29,7 +29,7 @@ int cli_message_start_build(struct cli_state *cli, const char *host, const char
/* construct a SMBsendstrt command */
memset(cli->outbuf,'\0',smb_size);
- set_message(cli->outbuf,0,0,True);
+ cli_set_message(cli->outbuf,0,0,True);
SCVAL(cli->outbuf,smb_com,SMBsendstrt);
SSVAL(cli->outbuf,smb_tid,cli->cnum);
cli_setup_packet(cli);
@@ -75,7 +75,7 @@ int cli_message_text_build(struct cli_state *cli, const char *msg, int len, int
char *p;
memset(cli->outbuf,'\0',smb_size);
- set_message(cli->outbuf,1,0,True);
+ cli_set_message(cli->outbuf,1,0,True);
SCVAL(cli->outbuf,smb_com,SMBsendtxt);
SSVAL(cli->outbuf,smb_tid,cli->cnum);
cli_setup_packet(cli);
@@ -132,7 +132,7 @@ int cli_message_end_build(struct cli_state *cli, int grp)
char *p;
memset(cli->outbuf,'\0',smb_size);
- set_message(cli->outbuf,1,0,True);
+ cli_set_message(cli->outbuf,1,0,True);
SCVAL(cli->outbuf,smb_com,SMBsendend);
SSVAL(cli->outbuf,smb_tid,cli->cnum);
diff --git a/source3/libsmb/clioplock.c b/source3/libsmb/clioplock.c
index 2e54f5a781..ef8b396461 100644
--- a/source3/libsmb/clioplock.c
+++ b/source3/libsmb/clioplock.c
@@ -32,7 +32,7 @@ bool cli_oplock_ack(struct cli_state *cli, int fnum, unsigned char level)
cli->outbuf = buf;
memset(buf,'\0',smb_size);
- set_message(buf,8,0,True);
+ cli_set_message(buf,8,0,True);
SCVAL(buf,smb_com,SMBlockingX);
SSVAL(buf,smb_tid, cli->cnum);
diff --git a/source3/libsmb/cliprint.c b/source3/libsmb/cliprint.c
index 7fbdb97c01..223ddb4186 100644
--- a/source3/libsmb/cliprint.c
+++ b/source3/libsmb/cliprint.c
@@ -195,7 +195,7 @@ int cli_spl_open(struct cli_state *cli, const char *fname, int flags, int share_
memset(cli->outbuf,'\0',smb_size);
memset(cli->inbuf,'\0',smb_size);
- set_message(cli->outbuf,15,0,True);
+ cli_set_message(cli->outbuf,15,0,True);
SCVAL(cli->outbuf,smb_com,SMBsplopen);
SSVAL(cli->outbuf,smb_tid,cli->cnum);
@@ -242,7 +242,7 @@ bool cli_spl_close(struct cli_state *cli, int fnum)
memset(cli->outbuf,'\0',smb_size);
memset(cli->inbuf,'\0',smb_size);
- set_message(cli->outbuf,3,0,True);
+ cli_set_message(cli->outbuf,3,0,True);
SCVAL(cli->outbuf,smb_com,SMBsplclose);
SSVAL(cli->outbuf,smb_tid,cli->cnum);
diff --git a/source3/libsmb/clireadwrite.c b/source3/libsmb/clireadwrite.c
index d77875bae5..0b33e43563 100644
--- a/source3/libsmb/clireadwrite.c
+++ b/source3/libsmb/clireadwrite.c
@@ -34,7 +34,7 @@ static bool cli_issue_read(struct cli_state *cli, int fnum, off_t offset,
if ((SMB_BIG_UINT)offset >> 32)
bigoffset = True;
- set_message(cli->outbuf,bigoffset ? 12 : 10,0,True);
+ cli_set_message(cli->outbuf,bigoffset ? 12 : 10,0,True);
SCVAL(cli->outbuf,smb_com,SMBreadX);
SSVAL(cli->outbuf,smb_tid,cli->cnum);
@@ -65,8 +65,8 @@ ssize_t cli_read(struct cli_state *cli, int fnum, char *buf, off_t offset, size_
size_t size2;
size_t readsize;
ssize_t total = 0;
- /* We can only do direct reads if not signing. */
- bool direct_reads = !client_is_signing_on(cli);
+ /* We can only do direct reads if not signing or encrypting. */
+ bool direct_reads = !client_is_signing_on(cli) && !cli_encryption_on(cli);
if (size == 0)
return 0;
@@ -76,7 +76,9 @@ ssize_t cli_read(struct cli_state *cli, int fnum, char *buf, off_t offset, size_
* rounded down to a multiple of 1024.
*/
- if (client_is_signing_on(cli) == False && (cli->posix_capabilities & CIFS_UNIX_LARGE_READ_CAP)) {
+ if (client_is_signing_on(cli) == false &&
+ cli_encryption_on(cli) == false &&
+ (cli->posix_capabilities & CIFS_UNIX_LARGE_READ_CAP)) {
readsize = CLI_SAMBA_MAX_POSIX_LARGE_READX_SIZE;
} else if (cli->capabilities & CAP_LARGE_READX) {
if (cli->is_samba) {
@@ -203,7 +205,7 @@ static bool cli_issue_readraw(struct cli_state *cli, int fnum, off_t offset,
memset(cli->outbuf,'\0',smb_size);
memset(cli->inbuf,'\0',smb_size);
- set_message(cli->outbuf,10,0,True);
+ cli_set_message(cli->outbuf,10,0,True);
SCVAL(cli->outbuf,smb_com,SMBreadbraw);
SSVAL(cli->outbuf,smb_tid,cli->cnum);
@@ -295,8 +297,8 @@ static bool cli_issue_write(struct cli_state *cli,
{
char *p;
bool large_writex = false;
- /* We can only do direct writes if not signing. */
- bool direct_writes = !client_is_signing_on(cli);
+ /* We can only do direct writes if not signing and not encrypting. */
+ bool direct_writes = !client_is_signing_on(cli) && !cli_encryption_on(cli);
if (!direct_writes && size + 1 > cli->bufsize) {
cli->outbuf = (char *)SMB_REALLOC(cli->outbuf, size + 1024);
@@ -319,9 +321,9 @@ static bool cli_issue_write(struct cli_state *cli,
}
if (large_writex) {
- set_message(cli->outbuf,14,0,True);
+ cli_set_message(cli->outbuf,14,0,True);
} else {
- set_message(cli->outbuf,12,0,True);
+ cli_set_message(cli->outbuf,12,0,True);
}
SCVAL(cli->outbuf,smb_com,SMBwriteX);
@@ -404,16 +406,17 @@ ssize_t cli_write(struct cli_state *cli,
if (write_mode == 0 &&
!client_is_signing_on(cli) &&
+ !cli_encryption_on(cli) &&
(cli->posix_capabilities & CIFS_UNIX_LARGE_WRITE_CAP) &&
(cli->capabilities & CAP_LARGE_FILES)) {
/* Only do massive writes if we can do them direct
- * with no signing - not on a pipe. */
+ * with no signing or encrypting - not on a pipe. */
writesize = CLI_SAMBA_MAX_POSIX_LARGE_WRITEX_SIZE;
- } else if (cli->capabilities & CAP_LARGE_READX) {
+ } else if (cli->capabilities & CAP_LARGE_WRITEX) {
if (cli->is_samba) {
- writesize = CLI_SAMBA_MAX_LARGE_READX_SIZE;
+ writesize = CLI_SAMBA_MAX_LARGE_WRITEX_SIZE;
} else {
- writesize = CLI_WINDOWS_MAX_LARGE_READX_SIZE;
+ writesize = CLI_WINDOWS_MAX_LARGE_WRITEX_SIZE;
}
} else {
writesize = (cli->max_xmit - (smb_size+32)) & ~1023;
@@ -471,7 +474,7 @@ ssize_t cli_smbwrite(struct cli_state *cli,
memset(cli->outbuf,'\0',smb_size);
memset(cli->inbuf,'\0',smb_size);
- set_message(cli->outbuf,5, 0,True);
+ cli_set_message(cli->outbuf,5, 0,True);
SCVAL(cli->outbuf,smb_com,SMBwrite);
SSVAL(cli->outbuf,smb_tid,cli->cnum);
diff --git a/source3/libsmb/clitrans.c b/source3/libsmb/clitrans.c
index a6f7f7fec1..bfb31fdb74 100644
--- a/source3/libsmb/clitrans.c
+++ b/source3/libsmb/clitrans.c
@@ -43,7 +43,7 @@ bool cli_send_trans(struct cli_state *cli, int trans,
this_ldata = MIN(ldata,cli->max_xmit - (500+lsetup*2+this_lparam));
memset(cli->outbuf,'\0',smb_size);
- set_message(cli->outbuf,14+lsetup,0,True);
+ cli_set_message(cli->outbuf,14+lsetup,0,True);
SCVAL(cli->outbuf,smb_com,trans);
SSVAL(cli->outbuf,smb_tid, cli->cnum);
cli_setup_packet(cli);
@@ -107,7 +107,7 @@ bool cli_send_trans(struct cli_state *cli, int trans,
this_lparam = MIN(lparam-tot_param,cli->max_xmit - 500); /* hack */
this_ldata = MIN(ldata-tot_data,cli->max_xmit - (500+this_lparam));
- set_message(cli->outbuf,trans==SMBtrans?8:9,0,True);
+ cli_set_message(cli->outbuf,trans==SMBtrans?8:9,0,True);
SCVAL(cli->outbuf,smb_com,(trans==SMBtrans ? SMBtranss : SMBtranss2));
outparam = smb_buf(cli->outbuf);
@@ -368,7 +368,7 @@ bool cli_send_nt_trans(struct cli_state *cli,
this_ldata = MIN(ldata,cli->max_xmit - (500+lsetup*2+this_lparam));
memset(cli->outbuf,'\0',smb_size);
- set_message(cli->outbuf,19+lsetup,0,True);
+ cli_set_message(cli->outbuf,19+lsetup,0,True);
SCVAL(cli->outbuf,smb_com,SMBnttrans);
SSVAL(cli->outbuf,smb_tid, cli->cnum);
cli_setup_packet(cli);
@@ -424,7 +424,7 @@ bool cli_send_nt_trans(struct cli_state *cli,
this_lparam = MIN(lparam-tot_param,cli->max_xmit - 500); /* hack */
this_ldata = MIN(ldata-tot_data,cli->max_xmit - (500+this_lparam));
- set_message(cli->outbuf,18,0,True);
+ cli_set_message(cli->outbuf,18,0,True);
SCVAL(cli->outbuf,smb_com,SMBnttranss);
/* XXX - these should probably be aligned */
diff --git a/source3/libsmb/errormap.c b/source3/libsmb/errormap.c
index ce826ae999..4ec30f7e17 100644
--- a/source3/libsmb/errormap.c
+++ b/source3/libsmb/errormap.c
@@ -1502,3 +1502,108 @@ WERROR ntstatus_to_werror(NTSTATUS error)
/* a lame guess */
return W_ERROR(NT_STATUS_V(error) & 0xffff);
}
+
+#if defined(HAVE_GSSAPI)
+/*******************************************************************************
+ Map between gssapi errors and NT status. I made these up :-(. JRA.
+*******************************************************************************/
+
+static const struct {
+ unsigned long gss_err;
+ NTSTATUS ntstatus;
+} gss_to_ntstatus_errormap[] = {
+#if defined(GSS_S_CALL_INACCESSIBLE_READ)
+ {GSS_S_CALL_INACCESSIBLE_READ, NT_STATUS_INVALID_PARAMETER},
+#endif
+#if defined(GSS_S_CALL_INACCESSIBLE_WRITE)
+ {GSS_S_CALL_INACCESSIBLE_WRITE, NT_STATUS_INVALID_PARAMETER},
+#endif
+#if defined(GSS_S_CALL_BAD_STRUCTURE)
+ {GSS_S_CALL_BAD_STRUCTURE, NT_STATUS_INVALID_PARAMETER},
+#endif
+#if defined(GSS_S_BAD_MECH)
+ {GSS_S_BAD_MECH, NT_STATUS_INVALID_PARAMETER},
+#endif
+#if defined(GSS_S_BAD_NAME)
+ {GSS_S_BAD_NAME, NT_STATUS_INVALID_ACCOUNT_NAME},
+#endif
+#if defined(GSS_S_BAD_NAMETYPE)
+ {GSS_S_BAD_NAMETYPE, NT_STATUS_INVALID_PARAMETER},
+#endif
+#if defined(GSS_S_BAD_BINDINGS)
+ {GSS_S_BAD_BINDINGS, NT_STATUS_INVALID_PARAMETER},
+#endif
+#if defined(GSS_S_BAD_STATUS)
+ {GSS_S_BAD_STATUS, NT_STATUS_UNSUCCESSFUL},
+#endif
+#if defined(GSS_S_BAD_SIG)
+ {GSS_S_BAD_SIG, NT_STATUS_ACCESS_DENIED},
+#endif
+#if defined(GSS_S_NO_CRED)
+ {GSS_S_NO_CRED, NT_STATUS_ACCESS_DENIED},
+#endif
+#if defined(GSS_S_NO_CONTEXT)
+ {GSS_S_NO_CONTEXT, NT_STATUS_ACCESS_DENIED},
+#endif
+#if defined(GSS_S_DEFECTIVE_TOKEN)
+ {GSS_S_DEFECTIVE_TOKEN, NT_STATUS_ACCESS_DENIED},
+#endif
+#if defined(GSS_S_DEFECTIVE_CREDENTIAL)
+ {GSS_S_DEFECTIVE_CREDENTIAL, NT_STATUS_ACCESS_DENIED},
+#endif
+#if defined(GSS_S_CREDENTIALS_EXPIRED)
+ {GSS_S_CREDENTIALS_EXPIRED, NT_STATUS_PASSWORD_EXPIRED},
+#endif
+#if defined(GSS_S_CONTEXT_EXPIRED)
+ {GSS_S_CONTEXT_EXPIRED, NT_STATUS_PASSWORD_EXPIRED},
+#endif
+#if defined(GSS_S_BAD_QOP)
+ {GSS_S_BAD_QOP, NT_STATUS_ACCESS_DENIED},
+#endif
+#if defined(GSS_S_UNAUTHORIZED)
+ {GSS_S_UNAUTHORIZED, NT_STATUS_ACCESS_DENIED},
+#endif
+#if defined(GSS_S_UNAVAILABLE)
+ {GSS_S_UNAVAILABLE, NT_STATUS_UNSUCCESSFUL},
+#endif
+#if defined(GSS_S_DUPLICATE_ELEMENT)
+ {GSS_S_DUPLICATE_ELEMENT, NT_STATUS_INVALID_PARAMETER},
+#endif
+#if defined(GSS_S_NAME_NOT_MN)
+ {GSS_S_NAME_NOT_MN, NT_STATUS_INVALID_PARAMETER},
+#endif
+ { 0, NT_STATUS_OK }
+};
+
+/*********************************************************************
+ Map an NT error code from a gssapi error code.
+*********************************************************************/
+
+NTSTATUS map_nt_error_from_gss(uint32 gss_maj, uint32 minor)
+{
+ int i = 0;
+
+ if (gss_maj == GSS_S_COMPLETE) {
+ return NT_STATUS_OK;
+ }
+
+ if (gss_maj == GSS_S_CONTINUE_NEEDED) {
+ return NT_STATUS_MORE_PROCESSING_REQUIRED;
+ }
+
+ if (gss_maj == GSS_S_FAILURE) {
+ return map_nt_error_from_unix((int)minor);
+ }
+
+ /* Look through list */
+ while(gss_to_ntstatus_errormap[i].gss_err != 0) {
+ if (gss_to_ntstatus_errormap[i].gss_err == gss_maj) {
+ return gss_to_ntstatus_errormap[i].ntstatus;
+ }
+ i++;
+ }
+
+ /* Default return */
+ return NT_STATUS_ACCESS_DENIED;
+}
+#endif
diff --git a/source3/libsmb/smb_seal.c b/source3/libsmb/smb_seal.c
new file mode 100644
index 0000000000..055a27d05a
--- /dev/null
+++ b/source3/libsmb/smb_seal.c
@@ -0,0 +1,496 @@
+/*
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+
+/******************************************************************************
+ Pull out the encryption context for this packet. 0 means global context.
+******************************************************************************/
+
+NTSTATUS get_enc_ctx_num(const char *buf, uint16 *p_enc_ctx_num)
+{
+ if (smb_len(buf) < 8) {
+ return NT_STATUS_INVALID_BUFFER_SIZE;
+ }
+
+ if (buf[4] == (char)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;
+}
+
+/******************************************************************************
+ 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.
+ NTLM decrypt an incoming buffer.
+ Abartlett tells me that SSPI puts the signature first before the encrypted
+ output, so cope with the same for compatibility.
+******************************************************************************/
+
+NTSTATUS common_ntlm_decrypt_buffer(NTLMSSP_STATE *ntlmssp_state, char *buf)
+{
+ NTSTATUS status;
+ size_t buf_len = smb_len(buf) + 4; /* Don't forget the 4 length bytes. */
+ size_t data_len;
+ char *inbuf;
+ DATA_BLOB sig;
+
+ if (buf_len < 8 + NTLMSSP_SIG_SIZE) {
+ return NT_STATUS_BUFFER_TOO_SMALL;
+ }
+
+ inbuf = (char *)smb_xmemdup(buf, buf_len);
+
+ /* Adjust for the signature. */
+ data_len = buf_len - 8 - NTLMSSP_SIG_SIZE;
+
+ /* Point at the signature. */
+ sig = data_blob_const(inbuf+8, NTLMSSP_SIG_SIZE);
+
+ status = ntlmssp_unseal_packet(ntlmssp_state,
+ (unsigned char *)inbuf + 8 + NTLMSSP_SIG_SIZE, /* 4 byte len + 0xFF 'E' <enc> <ctx> */
+ data_len,
+ (unsigned char *)inbuf + 8 + NTLMSSP_SIG_SIZE,
+ data_len,
+ &sig);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ SAFE_FREE(inbuf);
+ return status;
+ }
+
+ memcpy(buf + 8, inbuf + 8 + NTLMSSP_SIG_SIZE, data_len);
+
+ /* Reset the length. */
+ _smb_setlen(buf,data_len + 4);
+
+ SAFE_FREE(inbuf);
+ return NT_STATUS_OK;
+}
+
+/******************************************************************************
+ Generic code for client and server.
+ NTLM encrypt an outgoing buffer. Return the encrypted pointer in ppbuf_out.
+ Abartlett tells me that SSPI puts the signature first before the encrypted
+ output, so do the same for compatibility.
+******************************************************************************/
+
+NTSTATUS common_ntlm_encrypt_buffer(NTLMSSP_STATE *ntlmssp_state,
+ uint16 enc_ctx_num,
+ char *buf,
+ char **ppbuf_out)
+{
+ NTSTATUS status;
+ char *buf_out;
+ size_t data_len = smb_len(buf) - 4; /* Ignore the 0xFF SMB bytes. */
+ DATA_BLOB sig;
+
+ *ppbuf_out = NULL;
+
+ if (data_len == 0) {
+ return NT_STATUS_BUFFER_TOO_SMALL;
+ }
+
+ /*
+ * We know smb_len can't return a value > 128k, so no int overflow
+ * check needed.
+ */
+
+ buf_out = SMB_XMALLOC_ARRAY(char, 8 + NTLMSSP_SIG_SIZE + data_len);
+
+ /* Copy the data from the original buffer. */
+
+ memcpy(buf_out + 8 + NTLMSSP_SIG_SIZE, buf + 8, data_len);
+
+ 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 + NTLMSSP_SIG_SIZE, /* 4 byte len + 0xFF 'S' <enc> <ctx> */
+ data_len,
+ (unsigned char *)buf_out + 8 + NTLMSSP_SIG_SIZE,
+ data_len,
+ &sig);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ data_blob_free(&sig);
+ SAFE_FREE(buf_out);
+ return status;
+ }
+
+ /* First 16 data bytes are signature for SSPI compatibility. */
+ memcpy(buf_out + 8, sig.data, NTLMSSP_SIG_SIZE);
+ *ppbuf_out = buf_out;
+ 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(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) {
+ ADS_STATUS adss = ADS_ERROR_GSS(ret, minor);
+ DEBUG(0,("common_gss_encrypt_buffer: gss_unwrap failed. Error %s\n",
+ ads_errstr(adss) ));
+ return map_nt_error_from_gss(ret, minor);
+ }
+
+ if (out_buf.length > in_buf.length) {
+ DEBUG(0,("common_gss_encrypt_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);
+ _smb_setlen(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 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(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) {
+ ADS_STATUS adss = ADS_ERROR_GSS(ret, minor);
+ DEBUG(0,("common_gss_encrypt_buffer: gss_wrap failed. Error %s\n",
+ ads_errstr(adss) ));
+ return map_nt_error_from_gss(ret, minor);
+ }
+
+ 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 *)SMB_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_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, 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_ntlm_decrypt_buffer(es->s.ntlmssp_state, 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.ntlmssp_state) {
+ ntlmssp_end(&es->s.ntlmssp_state);
+ }
+ }
+#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)
+{
+ if (!common_encryption_on(es)) {
+ return;
+ }
+
+ if (es->smb_enc_type == SMB_TRANS_ENC_NTLM) {
+ SAFE_FREE(buf);
+ return;
+ }
+
+#if defined(HAVE_GSSAPI) && defined(HAVE_KRB5)
+ 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
+}
+
+/******************************************************************************
+ Client side encryption.
+******************************************************************************/
+
+/******************************************************************************
+ Is client encryption on ?
+******************************************************************************/
+
+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);
+}
+
+/******************************************************************************
+ Shutdown a client encryption state.
+******************************************************************************/
+
+void cli_free_encryption_context(struct cli_state *cli)
+{
+ common_free_encryption_state(&cli->trans_enc_state);
+}
+
+/******************************************************************************
+ Free an encryption-allocated buffer.
+******************************************************************************/
+
+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);
+}
+
+/******************************************************************************
+ Decrypt an incoming buffer.
+******************************************************************************/
+
+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);
+}
+
+/******************************************************************************
+ Encrypt an outgoing buffer. Return the encrypted pointer in buf_out.
+******************************************************************************/
+
+NTSTATUS cli_encrypt_message(struct cli_state *cli, char **buf_out)
+{
+ /* Ignore session keepalives. */
+ if(CVAL(cli->outbuf,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);
+}
diff --git a/source3/nmbd/nmbd_packets.c b/source3/nmbd/nmbd_packets.c
index 349d36ce70..3bb1514203 100644
--- a/source3/nmbd/nmbd_packets.c
+++ b/source3/nmbd/nmbd_packets.c
@@ -1924,7 +1924,7 @@ bool send_mailslot(bool unique, const char *mailslot,char *buf, size_t len,
return false;
}
- set_message(ptr,17,strlen(mailslot) + 1 + len,True);
+ cli_set_message(ptr,17,strlen(mailslot) + 1 + len,True);
memcpy(ptr,tmp,4);
SCVAL(ptr,smb_com,SMBtrans);
diff --git a/source3/param/loadparm.c b/source3/param/loadparm.c
index 5b009fc964..7186d4f075 100644
--- a/source3/param/loadparm.c
+++ b/source3/param/loadparm.c
@@ -6209,7 +6209,7 @@ uint32 lp_get_spoolss_state( void )
}
/*******************************************************************
- Ensure we don't use sendfile if server smb signing is active.
+ Ensure we don't use sendfile if server smb signing or selaing is active.
********************************************************************/
bool lp_use_sendfile(int snum)
@@ -6218,7 +6218,10 @@ bool lp_use_sendfile(int snum)
if (Protocol < PROTOCOL_NT1) {
return False;
}
- return (_lp_use_sendfile(snum) && (get_remote_arch() != RA_WIN95) && !srv_is_signing_active());
+ return (_lp_use_sendfile(snum) &&
+ (get_remote_arch() != RA_WIN95) &&
+ !srv_is_signing_active() &&
+ !srv_encryption_on());
}
/*******************************************************************
diff --git a/source3/smbd/aio.c b/source3/smbd/aio.c
index 994b10d6a8..f13393b764 100644
--- a/source3/smbd/aio.c
+++ b/source3/smbd/aio.c
@@ -236,7 +236,7 @@ bool schedule_aio_read_and_X(connection_struct *conn,
}
construct_reply_common((char *)req->inbuf, aio_ex->outbuf);
- set_message(aio_ex->outbuf, 12, 0, True);
+ srv_set_message((const char *)req->inbuf, aio_ex->outbuf, 12, 0, True);
SCVAL(aio_ex->outbuf,smb_vwv0,0xFF); /* Never a chained reply. */
a = &aio_ex->acb;
@@ -387,6 +387,7 @@ static int handle_aio_read_complete(struct aio_extra *aio_ex)
int ret = 0;
int outsize;
char *outbuf = aio_ex->outbuf;
+ const char *inbuf = aio_ex->inbuf;
char *data = smb_buf(outbuf);
ssize_t nread = SMB_VFS_AIO_RETURN(aio_ex->fsp,&aio_ex->acb);
@@ -407,10 +408,11 @@ static int handle_aio_read_complete(struct aio_extra *aio_ex)
"Error = %s\n",
aio_ex->fsp->fsp_name, strerror(errno) ));
- outsize = (UNIXERROR(ERRDOS,ERRnoaccess));
ret = errno;
+ ERROR_NT(map_nt_error_from_unix(ret));
+ outsize = srv_set_message(inbuf,outbuf,0,0,true);
} else {
- outsize = set_message(outbuf,12,nread,False);
+ outsize = srv_set_message(inbuf, outbuf,12,nread,False);
SSVAL(outbuf,smb_vwv2,0xFFFF); /* Remaining - must be * -1. */
SSVAL(outbuf,smb_vwv5,nread);
SSVAL(outbuf,smb_vwv6,smb_offset(data,outbuf));
@@ -423,7 +425,7 @@ static int handle_aio_read_complete(struct aio_extra *aio_ex)
(int)aio_ex->acb.aio_nbytes, (int)nread ) );
}
- smb_setlen(outbuf,outsize - 4);
+ _smb_setlen(outbuf,outsize - 4);
show_msg(outbuf);
if (!send_smb(smbd_server_fd(),outbuf)) {
exit_server_cleanly("handle_aio_read_complete: send_smb "
@@ -448,6 +450,7 @@ static int handle_aio_write_complete(struct aio_extra *aio_ex)
int ret = 0;
files_struct *fsp = aio_ex->fsp;
char *outbuf = aio_ex->outbuf;
+ const char *inbuf = aio_ex->inbuf;
ssize_t numtowrite = aio_ex->acb.aio_nbytes;
ssize_t nwritten = SMB_VFS_AIO_RETURN(fsp,&aio_ex->acb);
@@ -492,8 +495,9 @@ static int handle_aio_write_complete(struct aio_extra *aio_ex)
return 0;
}
- UNIXERROR(ERRHRD,ERRdiskfull);
ret = errno;
+ ERROR_BOTH(ERRHRD, ERRdiskfull, map_nt_error_from_unix(ret));
+ srv_set_message(inbuf,outbuf,0,0,true);
} else {
bool write_through = BITSETW(aio_ex->inbuf+smb_vwv7,0);
NTSTATUS status;
@@ -509,8 +513,9 @@ static int handle_aio_write_complete(struct aio_extra *aio_ex)
fsp->fnum, (int)numtowrite, (int)nwritten));
status = sync_file(fsp->conn,fsp, write_through);
if (!NT_STATUS_IS_OK(status)) {
- UNIXERROR(ERRHRD,ERRdiskfull);
ret = errno;
+ ERROR_BOTH(ERRHRD, ERRdiskfull, map_nt_error_from_unix(ret));
+ srv_set_message(inbuf,outbuf,0,0,true);
DEBUG(5,("handle_aio_write: sync_file for %s returned %s\n",
fsp->fsp_name, nt_errstr(status) ));
}
diff --git a/source3/smbd/error.c b/source3/smbd/error.c
index 12eff42023..c669e74146 100644
--- a/source3/smbd/error.c
+++ b/source3/smbd/error.c
@@ -24,34 +24,6 @@ extern struct unix_error_map unix_dos_nt_errmap[];
extern uint32 global_client_caps;
-/****************************************************************************
- Create an error packet from errno.
-****************************************************************************/
-
-int unix_error_packet(char *outbuf,int def_class,uint32 def_code, NTSTATUS def_status, int line, const char *file)
-{
- int eclass=def_class;
- int ecode=def_code;
- NTSTATUS ntstatus = def_status;
- int i=0;
-
- if (errno != 0) {
- DEBUG(3,("unix_error_packet: error string = %s\n",strerror(errno)));
-
- while (unix_dos_nt_errmap[i].dos_class != 0) {
- if (unix_dos_nt_errmap[i].unix_error == errno) {
- eclass = unix_dos_nt_errmap[i].dos_class;
- ecode = unix_dos_nt_errmap[i].dos_code;
- ntstatus = unix_dos_nt_errmap[i].nt_error;
- break;
- }
- i++;
- }
- }
-
- return error_packet(outbuf,eclass,ecode,ntstatus,line,file);
-}
-
bool use_nt_status(void)
{
return lp_nt_status_support() && (global_client_caps & CAP_STATUS32);
@@ -109,9 +81,9 @@ void error_packet_set(char *outbuf, uint8 eclass, uint32 ecode, NTSTATUS ntstatu
}
}
-int error_packet(char *outbuf, uint8 eclass, uint32 ecode, NTSTATUS ntstatus, int line, const char *file)
+int error_packet(const char *inbuf, char *outbuf, uint8 eclass, uint32 ecode, NTSTATUS ntstatus, int line, const char *file)
{
- int outsize = set_message(outbuf,0,0,True);
+ int outsize = srv_set_message(inbuf, outbuf,0,0,True);
error_packet_set(outbuf, eclass, ecode, ntstatus, line, file);
return outsize;
}
@@ -150,8 +122,24 @@ void reply_both_error(struct smb_request *req, uint8 eclass, uint32 ecode,
line, file);
}
+void reply_openerror(struct smb_request *req, NTSTATUS status)
+{
+ if (NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_COLLISION)) {
+ /*
+ * We hit an existing file, and if we're returning DOS
+ * error codes OBJECT_NAME_COLLISION would map to
+ * ERRDOS/183, we need to return ERRDOS/80, see bug
+ * 4852.
+ */
+ reply_botherror(req, NT_STATUS_OBJECT_NAME_COLLISION,
+ ERRDOS, ERRfilexists);
+ } else {
+ reply_nterror(req, status);
+ }
+}
+
void reply_unix_error(struct smb_request *req, uint8 defclass, uint32 defcode,
- NTSTATUS defstatus, int line, const char *file)
+ NTSTATUS defstatus, int line, const char *file)
{
int eclass=defclass;
int ecode=defcode;
@@ -163,7 +151,7 @@ void reply_unix_error(struct smb_request *req, uint8 defclass, uint32 defcode,
if (errno != 0) {
DEBUG(3,("unix_error_packet: error string = %s\n",
- strerror(errno)));
+ strerror(errno)));
while (unix_dos_nt_errmap[i].dos_class != 0) {
if (unix_dos_nt_errmap[i].unix_error == errno) {
@@ -177,22 +165,5 @@ void reply_unix_error(struct smb_request *req, uint8 defclass, uint32 defcode,
}
error_packet_set((char *)req->outbuf, eclass, ecode, ntstatus,
- line, file);
+ line, file);
}
-
-void reply_openerror(struct smb_request *req, NTSTATUS status)
-{
- if (NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_COLLISION)) {
- /*
- * We hit an existing file, and if we're returning DOS
- * error codes OBJECT_NAME_COLLISION would map to
- * ERRDOS/183, we need to return ERRDOS/80, see bug
- * 4852.
- */
- reply_botherror(req, NT_STATUS_OBJECT_NAME_COLLISION,
- ERRDOS, ERRfilexists);
- } else {
- reply_nterror(req, status);
- }
-}
-
diff --git a/source3/smbd/notify.c b/source3/smbd/notify.c
index 0dd7fbb20e..baab48f77e 100644
--- a/source3/smbd/notify.c
+++ b/source3/smbd/notify.c
@@ -131,6 +131,7 @@ static bool notify_marshall_changes(int num_changes,
static void change_notify_reply_packet(const uint8 *request_buf,
NTSTATUS error_code)
{
+ const char *inbuf = (const char *)request_buf;
char outbuf[smb_size+38];
memset(outbuf, '\0', sizeof(outbuf));
@@ -142,7 +143,7 @@ static void change_notify_reply_packet(const uint8 *request_buf,
* Seems NT needs a transact command with an error code
* in it. This is a longer packet than a simple error.
*/
- set_message(outbuf,18,0,False);
+ srv_set_message((const char *)request_buf, outbuf,18,0,False);
show_msg(outbuf);
if (!send_smb(smbd_server_fd(),outbuf))
diff --git a/source3/smbd/oplock.c b/source3/smbd/oplock.c
index 961abd277b..2c3313606a 100644
--- a/source3/smbd/oplock.c
+++ b/source3/smbd/oplock.c
@@ -252,7 +252,11 @@ static char *new_break_smb_message(TALLOC_CTX *mem_ctx,
}
memset(result,'\0',smb_size);
- set_message(result,8,0,True);
+ /* We use cli_set_message here as this is an
+ * asynchronous message that doesn't belong in
+ * the stream.
+ */
+ cli_set_message(result,8,0,True);
SCVAL(result,smb_com,SMBlockingX);
SSVAL(result,smb_tid,fsp->conn->cnum);
SSVAL(result,smb_pid,0xFFFF);
diff --git a/source3/smbd/pipes.c b/source3/smbd/pipes.c
index 0ddc00c767..88b67c03e5 100644
--- a/source3/smbd/pipes.c
+++ b/source3/smbd/pipes.c
@@ -291,7 +291,8 @@ void reply_pipe_read_and_X(struct smb_request *req)
return;
}
- set_message((char *)req->outbuf, 12, nread, False);
+ srv_set_message((const char *)req->inbuf,
+ (char *)req->outbuf, 12, nread, False);
SSVAL(req->outbuf,smb_vwv5,nread);
SSVAL(req->outbuf,smb_vwv6,smb_offset(data,req->outbuf));
diff --git a/source3/smbd/process.c b/source3/smbd/process.c
index ee76f90bf5..1260d52c77 100644
--- a/source3/smbd/process.c
+++ b/source3/smbd/process.c
@@ -50,6 +50,43 @@ enum smb_read_errors *get_srv_read_error(void)
return &smb_read_error;
}
+/*******************************************************************
+ Setup the word count and byte count for a smb message.
+ copying the '0xFF X X X' bytes from incoming
+ buffer (so we copy any encryption context).
+********************************************************************/
+
+int srv_set_message(const char *frombuf,
+ char *buf,
+ int num_words,
+ int num_bytes,
+ bool zero)
+{
+ if (zero && (num_words || num_bytes)) {
+ memset(buf + smb_size,'\0',num_words*2 + num_bytes);
+ }
+ SCVAL(buf,smb_wct,num_words);
+ SSVAL(buf,smb_vwv + num_words*SIZEOFWORD,num_bytes);
+ _smb_setlen(buf,(smb_size + num_words*2 + num_bytes - 4));
+ if (buf != frombuf) {
+ memcpy(buf+4, frombuf+4, 4);
+ }
+ return (smb_size + num_words*2 + num_bytes);
+}
+
+static bool valid_smb_header(const char *inbuf)
+{
+ if (srv_encryption_on()) {
+ uint16_t enc_num;
+ NTSTATUS status = get_enc_ctx_num(inbuf, &enc_num);
+ if (!NT_STATUS_IS_OK(status)) {
+ return false;
+ }
+ return (enc_num == 0);
+ }
+ return (strncmp(smb_base(inbuf),"\377SMB",4) == 0);
+}
+
/* Socket functions for smbd packet processing. */
static bool valid_packet_size(size_t len)
@@ -324,6 +361,18 @@ ssize_t receive_smb_talloc(TALLOC_CTX *mem_ctx, int fd, char **buffer,
return -1;
}
+ if (srv_encryption_on()) {
+ NTSTATUS status = srv_decrypt_buffer(*buffer);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0, ("receive_smb_talloc: SMB decryption failed on "
+ "incoming packet! Error %s\n",
+ nt_errstr(status) ));
+ cond_set_smb_read_error(get_srv_read_error(),
+ SMB_READ_BAD_DECRYPT);
+ return -1;
+ }
+ }
+
/* Check the incoming SMB signature. */
if (!srv_check_sign_mac(*buffer, true)) {
DEBUG(0, ("receive_smb: SMB Signature verification failed on "
@@ -1239,7 +1288,8 @@ void reply_outbuf(struct smb_request *req, uint8 num_words, uint32 num_bytes)
}
construct_reply_common((char *)req->inbuf, (char *)req->outbuf);
- set_message((char *)req->outbuf, num_words, num_bytes, False);
+ srv_set_message((const char *)req->inbuf,
+ (char *)req->outbuf, num_words, num_bytes, false);
/*
* Zero out the word area, the caller has to take care of the bcc area
* himself
@@ -1309,7 +1359,7 @@ static void switch_message(uint8 type, struct smb_request *req, int size)
/* Make sure this is an SMB packet. smb_size contains NetBIOS header
* so subtract 4 from it. */
- if ((strncmp(smb_base(req->inbuf),"\377SMB",4) != 0)
+ if (!valid_smb_header((const char *)req->inbuf)
|| (size < (smb_size - 4))) {
DEBUG(2,("Non-SMB packet of length %d. Terminating server\n",
smb_len(req->inbuf)));
@@ -1551,7 +1601,7 @@ void remove_from_common_flags2(uint32 v)
void construct_reply_common(const char *inbuf, char *outbuf)
{
- set_message(outbuf,0,0,False);
+ srv_set_message(inbuf,outbuf,0,0,false);
SCVAL(outbuf,smb_com,CVAL(inbuf,smb_com));
SIVAL(outbuf,smb_rcls,0);
diff --git a/source3/smbd/reply.c b/source3/smbd/reply.c
index 575ca13ff6..2707aee9c8 100644
--- a/source3/smbd/reply.c
+++ b/source3/smbd/reply.c
@@ -2791,8 +2791,8 @@ void reply_readbraw(connection_struct *conn, struct smb_request *req)
START_PROFILE(SMBreadbraw);
- if (srv_is_signing_active()) {
- exit_server_cleanly("reply_readbraw: SMB signing is active - "
+ if (srv_is_signing_active() || srv_encryption_on()) {
+ exit_server_cleanly("reply_readbraw: SMB signing/sealing is active - "
"raw reads/writes are disallowed.");
}
@@ -3017,7 +3017,8 @@ Returning short read of maximum allowed for compatibility with Windows 2000.\n",
return;
}
- set_message((char *)req->outbuf, 5, nread+3, False);
+ srv_set_message((const char *)req->inbuf,
+ (char *)req->outbuf, 5, nread+3, False);
SSVAL(req->outbuf,smb_vwv0,nread);
SSVAL(req->outbuf,smb_vwv5,nread+3);
@@ -3104,7 +3105,8 @@ Returning short read of maximum allowed for compatibility with Windows 2000.\n",
return;
}
- set_message((char *)req->outbuf, 5, nread+3, False);
+ srv_set_message((const char *)req->inbuf,
+ (char *)req->outbuf, 5, nread+3, False);
SSVAL(req->outbuf,smb_vwv0,nread);
SSVAL(req->outbuf,smb_vwv5,nread+3);
@@ -3122,12 +3124,12 @@ Returning short read of maximum allowed for compatibility with Windows 2000.\n",
Setup readX header.
****************************************************************************/
-static int setup_readX_header(char *outbuf, size_t smb_maxcnt)
+static int setup_readX_header(const char *inbuf, char *outbuf, size_t smb_maxcnt)
{
int outsize;
char *data;
- outsize = set_message(outbuf,12,smb_maxcnt,False);
+ outsize = srv_set_message(inbuf, outbuf,12,smb_maxcnt,False);
data = smb_buf(outbuf);
memset(outbuf+smb_vwv0,'\0',24); /* valgrind init. */
@@ -3190,7 +3192,8 @@ static void send_file_readX(connection_struct *conn, struct smb_request *req,
header = data_blob_const(headerbuf, sizeof(headerbuf));
construct_reply_common((char *)req->inbuf, (char *)headerbuf);
- setup_readX_header((char *)headerbuf, smb_maxcnt);
+ setup_readX_header((const char *)req->inbuf,
+ (char *)headerbuf, smb_maxcnt);
if ((nread = SMB_VFS_SENDFILE( smbd_server_fd(), fsp, fsp->fh->fd, &header, startpos, smb_maxcnt)) == -1) {
/* Returning ENOSYS means no data at all was sent. Do this as a normal read. */
@@ -3241,7 +3244,8 @@ normal_read:
uint8 headerbuf[smb_size + 2*12];
construct_reply_common((char *)req->inbuf, (char *)headerbuf);
- setup_readX_header((char *)headerbuf, smb_maxcnt);
+ setup_readX_header((const char *)req->inbuf,
+ (char *)headerbuf, smb_maxcnt);
/* Send out the header. */
if (write_data(smbd_server_fd(), (char *)headerbuf,
@@ -3268,7 +3272,8 @@ normal_read:
return;
}
- setup_readX_header((char *)req->outbuf, nread);
+ setup_readX_header((const char *)req->inbuf,
+ (char *)req->outbuf, nread);
DEBUG( 3, ( "send_file_readX fnum=%d max=%d nread=%d\n",
fsp->fnum, (int)smb_maxcnt, (int)nread ) );
@@ -3332,8 +3337,8 @@ void reply_read_and_X(connection_struct *conn, struct smb_request *req)
END_PROFILE(SMBreadX);
return;
}
- /* We currently don't do this on signed data. */
- if (srv_is_signing_active()) {
+ /* We currently don't do this on signed or sealed data. */
+ if (srv_is_signing_active() || srv_encryption_on()) {
reply_nterror(req, NT_STATUS_NOT_SUPPORTED);
END_PROFILE(SMBreadX);
return;
@@ -3524,7 +3529,7 @@ void reply_writebraw(connection_struct *conn, struct smb_request *req)
* it to send more bytes */
memcpy(buf, req->inbuf, smb_size);
- outsize = set_message(buf,
+ outsize = srv_set_message((const char *)req->inbuf, buf,
Protocol>PROTOCOL_COREPLUS?1:0,0,True);
SCVAL(buf,smb_com,SMBwritebraw);
SSVALS(buf,smb_vwv0,0xFFFF);
@@ -3856,6 +3861,12 @@ bool is_valid_writeX_buffer(const char *inbuf)
unsigned int doff = 0;
size_t len = smb_len_large(inbuf);
+ if (srv_encryption_on()) {
+ /* Can't do this on encrypted
+ * connections. */
+ return false;
+ }
+
if (CVAL(inbuf,smb_com) != SMBwriteX) {
return false;
}
diff --git a/source3/smbd/seal.c b/source3/smbd/seal.c
new file mode 100644
index 0000000000..14a427bb9c
--- /dev/null
+++ b/source3/smbd/seal.c
@@ -0,0 +1,703 @@
+/*
+ Unix SMB/CIFS implementation.
+ SMB Transport encryption (sealing) code - server 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+
+/******************************************************************************
+ Server side encryption.
+******************************************************************************/
+
+/******************************************************************************
+ Global server state.
+******************************************************************************/
+
+struct smb_srv_trans_enc_ctx {
+ struct smb_trans_enc_state *es;
+ AUTH_NTLMSSP_STATE *auth_ntlmssp_state; /* Must be kept in sync with pointer in ec->ntlmssp_state. */
+};
+
+static struct smb_srv_trans_enc_ctx *partial_srv_trans_enc_ctx;
+static struct smb_srv_trans_enc_ctx *srv_trans_enc_ctx;
+
+/******************************************************************************
+ Is server encryption on ?
+******************************************************************************/
+
+bool srv_encryption_on(void)
+{
+ if (srv_trans_enc_ctx) {
+ return common_encryption_on(srv_trans_enc_ctx->es);
+ }
+ return false;
+}
+
+/******************************************************************************
+ Create an auth_ntlmssp_state and ensure pointer copy is correct.
+******************************************************************************/
+
+static NTSTATUS make_auth_ntlmssp(struct smb_srv_trans_enc_ctx *ec)
+{
+ NTSTATUS status = auth_ntlmssp_start(&ec->auth_ntlmssp_state);
+ if (!NT_STATUS_IS_OK(status)) {
+ return nt_status_squash(status);
+ }
+
+ /*
+ * We must remember to update the pointer copy for the common
+ * functions after any auth_ntlmssp_start/auth_ntlmssp_end.
+ */
+ ec->es->s.ntlmssp_state = ec->auth_ntlmssp_state->ntlmssp_state;
+ return status;
+}
+
+/******************************************************************************
+ Destroy an auth_ntlmssp_state and ensure pointer copy is correct.
+******************************************************************************/
+
+static void destroy_auth_ntlmssp(struct smb_srv_trans_enc_ctx *ec)
+{
+ /*
+ * We must remember to update the pointer copy for the common
+ * functions after any auth_ntlmssp_start/auth_ntlmssp_end.
+ */
+
+ if (ec->auth_ntlmssp_state) {
+ auth_ntlmssp_end(&ec->auth_ntlmssp_state);
+ /* The auth_ntlmssp_end killed this already. */
+ ec->es->s.ntlmssp_state = NULL;
+ }
+}
+
+#if defined(HAVE_GSSAPI) && defined(HAVE_KRB5)
+
+/******************************************************************************
+ Import a name.
+******************************************************************************/
+
+static NTSTATUS get_srv_gss_creds(const char *service,
+ const char *name,
+ gss_cred_usage_t cred_type,
+ gss_cred_id_t *p_srv_cred)
+{
+ OM_uint32 ret;
+ OM_uint32 min;
+ gss_name_t srv_name;
+ gss_buffer_desc input_name;
+ char *host_princ_s = NULL;
+ NTSTATUS status = NT_STATUS_OK;
+
+ gss_OID_desc nt_hostbased_service =
+ {10, CONST_DISCARD(char *,"\x2a\x86\x48\x86\xf7\x12\x01\x02\x01\x04")};
+
+ asprintf(&host_princ_s, "%s@%s", service, name);
+ if (host_princ_s == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ input_name.value = host_princ_s;
+ input_name.length = strlen(host_princ_s) + 1;
+
+ ret = gss_import_name(&min,
+ &input_name,
+ &nt_hostbased_service,
+ &srv_name);
+
+ DEBUG(10,("get_srv_gss_creds: imported name %s\n",
+ host_princ_s ));
+
+ if (ret != GSS_S_COMPLETE) {
+ SAFE_FREE(host_princ_s);
+ return map_nt_error_from_gss(ret, min);
+ }
+
+ /*
+ * We're accessing the krb5.keytab file here.
+ * ensure we have permissions to do so.
+ */
+ become_root();
+
+ ret = gss_acquire_cred(&min,
+ srv_name,
+ GSS_C_INDEFINITE,
+ GSS_C_NULL_OID_SET,
+ cred_type,
+ p_srv_cred,
+ NULL,
+ NULL);
+ unbecome_root();
+
+ if (ret != GSS_S_COMPLETE) {
+ ADS_STATUS adss = ADS_ERROR_GSS(ret, min);
+ DEBUG(10,("get_srv_gss_creds: gss_acquire_cred failed with %s\n",
+ ads_errstr(adss)));
+ status = map_nt_error_from_gss(ret, min);
+ }
+
+ SAFE_FREE(host_princ_s);
+ gss_release_name(&min, &srv_name);
+ return status;
+}
+
+/******************************************************************************
+ Create a gss state.
+ Try and get the cifs/server@realm principal first, then fall back to
+ host/server@realm.
+******************************************************************************/
+
+static NTSTATUS make_auth_gss(struct smb_srv_trans_enc_ctx *ec)
+{
+ NTSTATUS status;
+ gss_cred_id_t srv_cred;
+ fstring fqdn;
+
+ name_to_fqdn(fqdn, global_myname());
+ strlower_m(fqdn);
+
+ status = get_srv_gss_creds("cifs", fqdn, GSS_C_ACCEPT, &srv_cred);
+ if (!NT_STATUS_IS_OK(status)) {
+ status = get_srv_gss_creds("host", fqdn, GSS_C_ACCEPT, &srv_cred);
+ if (!NT_STATUS_IS_OK(status)) {
+ return nt_status_squash(status);
+ }
+ }
+
+ ec->es->s.gss_state = SMB_MALLOC_P(struct smb_tran_enc_state_gss);
+ if (!ec->es->s.gss_state) {
+ OM_uint32 min;
+ gss_release_cred(&min, &srv_cred);
+ return NT_STATUS_NO_MEMORY;
+ }
+ ZERO_STRUCTP(ec->es->s.gss_state);
+ ec->es->s.gss_state->creds = srv_cred;
+
+ /* No context yet. */
+ ec->es->s.gss_state->gss_ctx = GSS_C_NO_CONTEXT;
+
+ return NT_STATUS_OK;
+}
+#endif
+
+/******************************************************************************
+ Shutdown a server encryption context.
+******************************************************************************/
+
+static void srv_free_encryption_context(struct smb_srv_trans_enc_ctx **pp_ec)
+{
+ struct smb_srv_trans_enc_ctx *ec = *pp_ec;
+
+ if (!ec) {
+ return;
+ }
+
+ if (ec->es) {
+ switch (ec->es->smb_enc_type) {
+ case SMB_TRANS_ENC_NTLM:
+ destroy_auth_ntlmssp(ec);
+ break;
+#if defined(HAVE_GSSAPI) && defined(HAVE_KRB5)
+ case SMB_TRANS_ENC_GSS:
+ break;
+#endif
+ }
+ common_free_encryption_state(&ec->es);
+ }
+
+ SAFE_FREE(ec);
+ *pp_ec = NULL;
+}
+
+/******************************************************************************
+ Create a server encryption context.
+******************************************************************************/
+
+static NTSTATUS make_srv_encryption_context(enum smb_trans_enc_type smb_enc_type, struct smb_srv_trans_enc_ctx **pp_ec)
+{
+ struct smb_srv_trans_enc_ctx *ec;
+
+ *pp_ec = NULL;
+
+ ec = SMB_MALLOC_P(struct smb_srv_trans_enc_ctx);
+ if (!ec) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ ZERO_STRUCTP(partial_srv_trans_enc_ctx);
+ ec->es = SMB_MALLOC_P(struct smb_trans_enc_state);
+ if (!ec->es) {
+ SAFE_FREE(ec);
+ return NT_STATUS_NO_MEMORY;
+ }
+ ZERO_STRUCTP(ec->es);
+ ec->es->smb_enc_type = smb_enc_type;
+ switch (smb_enc_type) {
+ case SMB_TRANS_ENC_NTLM:
+ {
+ NTSTATUS status = make_auth_ntlmssp(ec);
+ if (!NT_STATUS_IS_OK(status)) {
+ srv_free_encryption_context(&ec);
+ return status;
+ }
+ }
+ break;
+
+#if defined(HAVE_GSSAPI) && defined(HAVE_KRB5)
+ case SMB_TRANS_ENC_GSS:
+ /* Acquire our credentials by calling gss_acquire_cred here. */
+ {
+ NTSTATUS status = make_auth_gss(ec);
+ if (!NT_STATUS_IS_OK(status)) {
+ srv_free_encryption_context(&ec);
+ return status;
+ }
+ }
+ break;
+#endif
+ default:
+ srv_free_encryption_context(&ec);
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+ *pp_ec = ec;
+ return NT_STATUS_OK;
+}
+
+/******************************************************************************
+ Free an encryption-allocated buffer.
+******************************************************************************/
+
+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);
+ }
+}
+
+/******************************************************************************
+ Decrypt an incoming buffer.
+******************************************************************************/
+
+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 *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, buf, buf_out);
+ }
+ /* Not encrypting. */
+ return NT_STATUS_OK;
+}
+
+/******************************************************************************
+ Do the gss encryption negotiation. Parameters are in/out.
+ Until success we do everything on the partial enc ctx.
+******************************************************************************/
+
+#if defined(HAVE_GSSAPI) && defined(HAVE_KRB5)
+static NTSTATUS srv_enc_spnego_gss_negotiate(unsigned char **ppdata, size_t *p_data_size, DATA_BLOB secblob)
+{
+ OM_uint32 ret;
+ OM_uint32 min;
+ OM_uint32 flags = 0;
+ gss_buffer_desc in_buf, out_buf;
+ struct smb_tran_enc_state_gss *gss_state;
+ DATA_BLOB auth_reply = data_blob_null;
+ DATA_BLOB response = data_blob_null;
+ NTSTATUS status;
+
+ if (!partial_srv_trans_enc_ctx) {
+ status = make_srv_encryption_context(SMB_TRANS_ENC_GSS, &partial_srv_trans_enc_ctx);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ }
+
+ gss_state = partial_srv_trans_enc_ctx->es->s.gss_state;
+
+ in_buf.value = secblob.data;
+ in_buf.length = secblob.length;
+
+ out_buf.value = NULL;
+ out_buf.length = 0;
+
+ become_root();
+
+ ret = gss_accept_sec_context(&min,
+ &gss_state->gss_ctx,
+ gss_state->creds,
+ &in_buf,
+ GSS_C_NO_CHANNEL_BINDINGS,
+ NULL,
+ NULL, /* Ignore oids. */
+ &out_buf, /* To return. */
+ &flags,
+ NULL, /* Ingore time. */
+ NULL); /* Ignore delegated creds. */
+ unbecome_root();
+
+ status = gss_err_to_ntstatus(ret, min);
+ if (ret != GSS_S_COMPLETE && ret != GSS_S_CONTINUE_NEEDED) {
+ return status;
+ }
+
+ /* Ensure we've got sign+seal available. */
+ if (ret == GSS_S_COMPLETE) {
+ if ((flags & (GSS_C_INTEG_FLAG|GSS_C_CONF_FLAG|GSS_C_REPLAY_FLAG|GSS_C_SEQUENCE_FLAG)) !=
+ (GSS_C_INTEG_FLAG|GSS_C_CONF_FLAG|GSS_C_REPLAY_FLAG|GSS_C_SEQUENCE_FLAG)) {
+ DEBUG(0,("srv_enc_spnego_gss_negotiate: quality of service not good enough "
+ "for SMB sealing.\n"));
+ gss_release_buffer(&min, &out_buf);
+ return NT_STATUS_ACCESS_DENIED;
+ }
+ }
+
+ auth_reply = data_blob(out_buf.value, out_buf.length);
+ gss_release_buffer(&min, &out_buf);
+
+ /* Wrap in SPNEGO. */
+ response = spnego_gen_auth_response(&auth_reply, status, OID_KERBEROS5);
+ data_blob_free(&auth_reply);
+
+ SAFE_FREE(*ppdata);
+ *ppdata = response.data;
+ *p_data_size = response.length;
+
+ return status;
+}
+#endif
+
+/******************************************************************************
+ Do the NTLM SPNEGO (or raw) encryption negotiation. Parameters are in/out.
+ Until success we do everything on the partial enc ctx.
+******************************************************************************/
+
+static NTSTATUS srv_enc_ntlm_negotiate(unsigned char **ppdata, size_t *p_data_size, DATA_BLOB secblob, bool spnego_wrap)
+{
+ NTSTATUS status;
+ DATA_BLOB chal = data_blob_null;
+ DATA_BLOB response = data_blob_null;
+
+ status = make_srv_encryption_context(SMB_TRANS_ENC_NTLM, &partial_srv_trans_enc_ctx);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ status = auth_ntlmssp_update(partial_srv_trans_enc_ctx->auth_ntlmssp_state, secblob, &chal);
+
+ /* status here should be NT_STATUS_MORE_PROCESSING_REQUIRED
+ * for success ... */
+
+ if (spnego_wrap) {
+ response = spnego_gen_auth_response(&chal, status, OID_NTLMSSP);
+ data_blob_free(&chal);
+ } else {
+ /* Return the raw blob. */
+ response = chal;
+ }
+
+ SAFE_FREE(*ppdata);
+ *ppdata = response.data;
+ *p_data_size = response.length;
+ return status;
+}
+
+/******************************************************************************
+ Do the SPNEGO encryption negotiation. Parameters are in/out.
+ Based off code in smbd/sesssionsetup.c
+ Until success we do everything on the partial enc ctx.
+******************************************************************************/
+
+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;
+ DATA_BLOB secblob = data_blob_null;
+ bool got_kerberos_mechanism = false;
+
+ blob = data_blob_const(*ppdata, *p_data_size);
+
+ status = parse_spnego_mechanisms(blob, &secblob, &got_kerberos_mechanism);
+ if (!NT_STATUS_IS_OK(status)) {
+ return nt_status_squash(status);
+ }
+
+ /* We should have no partial context at this point. */
+
+ srv_free_encryption_context(&partial_srv_trans_enc_ctx);
+
+#if defined(HAVE_GSSAPI) && defined(HAVE_KRB5)
+ if (got_kerberos_mechanism && lp_use_kerberos_keytab() ) {
+ status = srv_enc_spnego_gss_negotiate(ppdata, p_data_size, secblob);
+ } else
+#endif
+ {
+ status = srv_enc_ntlm_negotiate(ppdata, p_data_size, secblob, true);
+ }
+
+ data_blob_free(&secblob);
+
+ 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. */
+ if (!(*pparam = SMB_MALLOC_ARRAY(unsigned char, 2))) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ SSVAL(*pparam,0,partial_srv_trans_enc_ctx->es->enc_ctx_num);
+ *p_param_size = 2;
+ }
+
+ return status;
+}
+
+/******************************************************************************
+ Complete a SPNEGO encryption negotiation. Parameters are in/out.
+ We only get this for a NTLM auth second stage.
+******************************************************************************/
+
+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;
+ DATA_BLOB auth = data_blob_null;
+ DATA_BLOB auth_reply = data_blob_null;
+ DATA_BLOB response = data_blob_null;
+ struct smb_srv_trans_enc_ctx *ec = partial_srv_trans_enc_ctx;
+
+ /* We must have a partial context here. */
+
+ if (!ec || !ec->es || ec->auth_ntlmssp_state == NULL || ec->es->smb_enc_type != SMB_TRANS_ENC_NTLM) {
+ srv_free_encryption_context(&partial_srv_trans_enc_ctx);
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ blob = data_blob_const(*ppdata, *p_data_size);
+ if (!spnego_parse_auth(blob, &auth)) {
+ srv_free_encryption_context(&partial_srv_trans_enc_ctx);
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ status = auth_ntlmssp_update(ec->auth_ntlmssp_state, auth, &auth_reply);
+ data_blob_free(&auth);
+
+ 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. */
+ if (!(*pparam = SMB_MALLOC_ARRAY(unsigned char, 2))) {
+ 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;
+ return status;
+}
+
+/******************************************************************************
+ Raw NTLM encryption negotiation. Parameters are in/out.
+ This function does both steps.
+******************************************************************************/
+
+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);
+ DATA_BLOB response = data_blob_null;
+ struct smb_srv_trans_enc_ctx *ec;
+
+ if (!partial_srv_trans_enc_ctx) {
+ /* This is the initial step. */
+ status = srv_enc_ntlm_negotiate(ppdata, p_data_size, blob, false);
+ 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);
+ }
+ return status;
+ }
+
+ ec = partial_srv_trans_enc_ctx;
+ if (!ec || !ec->es || ec->auth_ntlmssp_state == NULL || ec->es->smb_enc_type != SMB_TRANS_ENC_NTLM) {
+ srv_free_encryption_context(&partial_srv_trans_enc_ctx);
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ /* 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. */
+ if (!(*pparam = SMB_MALLOC_ARRAY(unsigned char, 2))) {
+ 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;
+ *p_data_size = response.length;
+ return status;
+}
+
+/******************************************************************************
+ Do the SPNEGO encryption negotiation. Parameters are in/out.
+******************************************************************************/
+
+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)) {
+ /* its a negTokenTarg packet */
+ 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(conn, ppdata, p_data_size, pparam, p_param_size);
+ }
+
+ /* Maybe it's a raw unwrapped auth ? */
+ if (*p_data_size < 7) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ if (strncmp((char *)pdata, "NTLMSSP", 7) == 0) {
+ return srv_enc_raw_ntlm_auth(conn, ppdata, p_data_size, pparam, p_param_size);
+ }
+
+ DEBUG(1,("srv_request_encryption_setup: Unknown packet\n"));
+
+ return NT_STATUS_LOGON_FAILURE;
+}
+
+/******************************************************************************
+ Negotiation was successful - turn on server-side encryption.
+******************************************************************************/
+
+static NTSTATUS check_enc_good(struct smb_srv_trans_enc_ctx *ec)
+{
+ if (!ec || !ec->es) {
+ return NT_STATUS_LOGON_FAILURE;
+ }
+
+ if (ec->es->smb_enc_type == SMB_TRANS_ENC_NTLM) {
+ if ((ec->es->s.ntlmssp_state->neg_flags & (NTLMSSP_NEGOTIATE_SIGN|NTLMSSP_NEGOTIATE_SEAL)) !=
+ (NTLMSSP_NEGOTIATE_SIGN|NTLMSSP_NEGOTIATE_SEAL)) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+ }
+ /* Todo - check gssapi case. */
+
+ return NT_STATUS_OK;
+}
+
+/******************************************************************************
+ Negotiation was successful - turn on server-side encryption.
+******************************************************************************/
+
+NTSTATUS srv_encryption_start(connection_struct *conn)
+{
+ NTSTATUS status;
+
+ /* Check that we are really doing sign+seal. */
+ status = check_enc_good(partial_srv_trans_enc_ctx);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ /* Throw away the context we're using currently (if any). */
+ srv_free_encryption_context(&srv_trans_enc_ctx);
+
+ /* Steal the partial pointer. Deliberate shallow copy. */
+ srv_trans_enc_ctx = partial_srv_trans_enc_ctx;
+ srv_trans_enc_ctx->es->enc_on = true;
+
+ partial_srv_trans_enc_ctx = NULL;
+ return NT_STATUS_OK;
+}
+
+/******************************************************************************
+ Shutdown all server contexts.
+******************************************************************************/
+
+void server_encryption_shutdown(void)
+{
+ srv_free_encryption_context(&partial_srv_trans_enc_ctx);
+ srv_free_encryption_context(&srv_trans_enc_ctx);
+}
diff --git a/source3/smbd/trans2.c b/source3/smbd/trans2.c
index e37f6ffbfd..0e34284443 100644
--- a/source3/smbd/trans2.c
+++ b/source3/smbd/trans2.c
@@ -2998,6 +2998,55 @@ cap_low = 0x%x, cap_high = 0x%x\n",
}
break;
}
+
+ case SMB_REQUEST_TRANSPORT_ENCRYPTION:
+ {
+ NTSTATUS status;
+ size_t param_len = 0;
+ size_t data_len = total_data;
+
+ if (!lp_unix_extensions()) {
+ reply_nterror(
+ req,
+ NT_STATUS_INVALID_LEVEL);
+ return;
+ }
+
+ DEBUG( 4,("call_trans2setfsinfo: "
+ "request transport encrption.\n"));
+
+ status = srv_request_encryption_setup(conn,
+ (unsigned char **)ppdata,
+ &data_len,
+ (unsigned char **)pparams,
+ &param_len);
+
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED) &&
+ !NT_STATUS_IS_OK(status)) {
+ reply_nterror(req, status);
+ return;
+ }
+
+ send_trans2_replies(req,
+ *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(conn);
+ if (!NT_STATUS_IS_OK(status)) {
+ exit_server_cleanly(
+ "Failure in setting "
+ "up encrypted transport");
+ }
+ }
+ return;
+ }
+
case SMB_FS_QUOTA_INFORMATION:
{
files_struct *fsp = NULL;