From afc93255d183eefb68e45b8ec6275f6a62cf9795 Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Wed, 26 Dec 2007 17:12:36 -0800 Subject: Add SMB encryption. Still fixing client decrypt but negotiation works. Jeremy. (This used to be commit d78045601af787731f0737b8627450018902b104) --- source3/libsmb/cliconnect.c | 22 +- source3/libsmb/clidgram.c | 2 +- source3/libsmb/clientgen.c | 108 ++++++--- source3/libsmb/clifile.c | 44 ++-- source3/libsmb/clifsinfo.c | 333 ++++++++++++++++++++++++++++ source3/libsmb/clilist.c | 4 +- source3/libsmb/climessage.c | 6 +- source3/libsmb/clioplock.c | 2 +- source3/libsmb/cliprint.c | 4 +- source3/libsmb/clireadwrite.c | 31 +-- source3/libsmb/clitrans.c | 8 +- source3/libsmb/errormap.c | 105 +++++++++ source3/libsmb/smb_seal.c | 496 ++++++++++++++++++++++++++++++++++++++++++ 13 files changed, 1070 insertions(+), 95 deletions(-) create mode 100644 source3/libsmb/smb_seal.c (limited to 'source3/libsmb') 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; ioutbuf,'\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(¶m_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, + ¶m_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, ¶m_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 . +*/ + +#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' */ + 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' */ + 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); +} -- cgit