diff options
Diffstat (limited to 'source3/libsmb')
-rw-r--r-- | source3/libsmb/clientgen.c | 127 | ||||
-rw-r--r-- | source3/libsmb/clifsinfo.c | 2 | ||||
-rw-r--r-- | source3/libsmb/clireadwrite.c | 50 | ||||
-rw-r--r-- | source3/libsmb/smb_signing.c | 10 |
4 files changed, 164 insertions, 25 deletions
diff --git a/source3/libsmb/clientgen.c b/source3/libsmb/clientgen.c index e1dacb3921..1a4b1f770f 100644 --- a/source3/libsmb/clientgen.c +++ b/source3/libsmb/clientgen.c @@ -2,6 +2,7 @@ Unix SMB/CIFS implementation. SMB client generic functions Copyright (C) Andrew Tridgell 1994-1998 + 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 @@ -54,20 +55,20 @@ int cli_set_port(struct cli_state *cli, int port) should never go into a blocking read. ****************************************************************************/ -static BOOL client_receive_smb(struct cli_state *cli, BOOL eat_keepalives) +static ssize_t client_receive_smb(struct cli_state *cli, BOOL eat_keepalives, size_t maxlen) { - BOOL ret; + ssize_t len; int fd = cli->fd; char *buffer = cli->inbuf; unsigned int timeout = cli->timeout; for(;;) { - ret = receive_smb_raw(fd, buffer, timeout); + len = receive_smb_raw(fd, buffer, timeout, maxlen); - if (!ret) { + if (len < 0) { DEBUG(10,("client_receive_smb failed\n")); show_msg(buffer); - return ret; + return len; } /* Ignore session keepalive packets. */ @@ -85,11 +86,11 @@ static BOOL client_receive_smb(struct cli_state *cli, BOOL eat_keepalives) cli->smb_rw_error = READ_BAD_DECRYPT; close(cli->fd); cli->fd = -1; - return False; + return -1; } } show_msg(buffer); - return ret; + return len; } /**************************************************************************** @@ -98,21 +99,21 @@ static BOOL client_receive_smb(struct cli_state *cli, BOOL eat_keepalives) BOOL cli_receive_smb_internal(struct cli_state *cli, BOOL eat_keepalives) { - BOOL ret; + ssize_t len; /* fd == -1 causes segfaults -- Tom (tom@ninja.nl) */ if (cli->fd == -1) return False; again: - ret = client_receive_smb(cli, eat_keepalives); + len = client_receive_smb(cli, eat_keepalives, 0); - if (ret && !eat_keepalives && (CVAL(cli->inbuf,0) == SMBkeepalive)) { + if (len >= 0 && !eat_keepalives && (CVAL(cli->inbuf,0) == SMBkeepalive)) { /* Give back the keepalive. */ return True; } - if (ret) { + if (len > 0) { /* it might be an oplock break request */ if (!(CVAL(cli->inbuf, smb_flg) & FLAG_REPLY) && CVAL(cli->inbuf,smb_com) == SMBlockingX && @@ -121,7 +122,9 @@ BOOL cli_receive_smb_internal(struct cli_state *cli, BOOL eat_keepalives) 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); @@ -130,12 +133,12 @@ BOOL cli_receive_smb_internal(struct cli_state *cli, BOOL eat_keepalives) } /* If the server is not responding, note that now */ - if (!ret) { + if (len <= 0) { DEBUG(0, ("Receiving SMB: Server stopped responding\n")); cli->smb_rw_error = smb_read_error; close(cli->fd); cli->fd = -1; - return ret; + return False; } if (!cli_check_sign_mac(cli)) { @@ -187,6 +190,102 @@ BOOL cli_receive_smb_return_keepalive(struct cli_state *cli) return cli_receive_smb_internal(cli, False); } +/**************************************************************************** + Read the data portion of a readX smb. + The timeout is in milliseconds +****************************************************************************/ + +ssize_t cli_receive_smb_data(struct cli_state *cli, char *buffer, size_t len) +{ + if (cli->timeout > 0) { + return read_socket_with_timeout(cli->fd, buffer, len, len, cli->timeout); + } else { + return read_data(cli->fd, buffer, 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) +{ + ssize_t len, offset; + + if (cli->fd == -1) + return False; + + again: + + /* Read up to the size of a readX header reply. */ + len = client_receive_smb(cli, True, (smb_size - 4) + 24); + + if (len > 0) { + /* it might be an oplock break request */ + if (!(CVAL(cli->inbuf, smb_flg) & FLAG_REPLY) && + CVAL(cli->inbuf,smb_com) == SMBlockingX && + SVAL(cli->inbuf,smb_vwv6) == 0 && + SVAL(cli->inbuf,smb_vwv7) == 0) { + ssize_t total_len = smb_len(cli->inbuf); + + if (total_len > CLI_SAMBA_MAX_LARGE_READX_SIZE+SAFETY_MARGIN) { + goto read_err; + } + + /* Read the rest of the data. */ + if (!cli_receive_smb_data(cli,cli->inbuf+len,total_len - len)) { + goto read_err; + } + + 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; + } + /* try to prevent loops */ + SCVAL(cli->inbuf,smb_com,0xFF); + goto again; + } + } + + /* Check it's a non-chained readX reply. */ + if (!(CVAL(cli->inbuf, smb_flg) & FLAG_REPLY) || + (CVAL(cli->inbuf,smb_vwv0) != 0xFF) || + (CVAL(cli->inbuf,smb_com) != SMBreadX)) { + /* + * We're not coping here with asnyc replies to + * other calls. Punt here - we need async client + * libs for this. + */ + goto read_err; + } + + /* + * We know it's a readX reply - ensure we've read the + * padding bytes also. + */ + + offset = SVAL(cli->inbuf,smb_vwv6); + if (offset > len) { + ssize_t ret; + size_t padbytes = offset - len; + ret = cli_receive_smb_data(cli,smb_buf(cli->inbuf),padbytes); + if (ret != padbytes) { + goto read_err; + } + } + + return True; + + read_err: + + cli->smb_rw_error = smb_read_error = READ_ERROR; + close(cli->fd); + cli->fd = -1; + return False; +} + static ssize_t write_socket(int fd, const char *buf, size_t len) { ssize_t ret=0; diff --git a/source3/libsmb/clifsinfo.c b/source3/libsmb/clifsinfo.c index d8ada1a896..28facb511d 100644 --- a/source3/libsmb/clifsinfo.c +++ b/source3/libsmb/clifsinfo.c @@ -66,7 +66,7 @@ BOOL cli_unix_extensions_version(struct cli_state *cli, uint16 *pmajor, uint16 * *pmajor = SVAL(rdata,0); *pminor = SVAL(rdata,2); - *pcaplow = IVAL(rdata,4); + cli->posix_capabilities = *pcaplow = IVAL(rdata,4); *pcaphigh = IVAL(rdata,8); /* todo: but not yet needed diff --git a/source3/libsmb/clireadwrite.c b/source3/libsmb/clireadwrite.c index 1c72cb2942..35d2afe2e7 100644 --- a/source3/libsmb/clireadwrite.c +++ b/source3/libsmb/clireadwrite.c @@ -46,7 +46,7 @@ static BOOL cli_issue_read(struct cli_state *cli, int fnum, off_t offset, SIVAL(cli->outbuf,smb_vwv3,offset); SSVAL(cli->outbuf,smb_vwv5,size); SSVAL(cli->outbuf,smb_vwv6,size); - SSVAL(cli->outbuf,smb_vwv7,((size >> 16) & 1)); + SSVAL(cli->outbuf,smb_vwv7,(size >> 16)); SSVAL(cli->outbuf,smb_mid,cli->mid + i); if (bigoffset) { @@ -63,9 +63,11 @@ static BOOL cli_issue_read(struct cli_state *cli, int fnum, off_t offset, ssize_t cli_read(struct cli_state *cli, int fnum, char *buf, off_t offset, size_t size) { char *p; - int size2; - int readsize; + 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); if (size == 0) return 0; @@ -75,7 +77,11 @@ ssize_t cli_read(struct cli_state *cli, int fnum, char *buf, off_t offset, size_ * rounded down to a multiple of 1024. */ - if (cli->capabilities & CAP_LARGE_READX) { + 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) { readsize = CLI_SAMBA_MAX_LARGE_READX_SIZE; } else { @@ -93,8 +99,13 @@ ssize_t cli_read(struct cli_state *cli, int fnum, char *buf, off_t offset, size_ if (!cli_issue_read(cli, fnum, offset, readsize, 0)) return -1; - if (!cli_receive_smb(cli)) - return -1; + if (direct_reads) { + if (!cli_receive_smb_readX_header(cli)) + return -1; + } else { + if (!cli_receive_smb(cli)) + return -1; + } /* Check for error. Make sure to check for DOS and NT errors. */ @@ -125,7 +136,7 @@ ssize_t cli_read(struct cli_state *cli, int fnum, char *buf, off_t offset, size_ } size2 = SVAL(cli->inbuf, smb_vwv5); - size2 |= (((unsigned int)(SVAL(cli->inbuf, smb_vwv7) & 1)) << 16); + size2 |= (((unsigned int)(SVAL(cli->inbuf, smb_vwv7))) << 16); if (size2 > readsize) { DEBUG(5,("server returned more than we wanted!\n")); @@ -135,10 +146,29 @@ ssize_t cli_read(struct cli_state *cli, int fnum, char *buf, off_t offset, size_ return -1; } - /* Copy data into buffer */ + if (!direct_reads) { + /* Copy data into buffer */ + p = smb_base(cli->inbuf) + SVAL(cli->inbuf,smb_vwv6); + memcpy(buf + total, p, size2); + } else { + /* Ensure the remaining data matches the return size. */ + ssize_t toread = smb_len_large(cli->inbuf) - SVAL(cli->inbuf,smb_vwv6); - p = smb_base(cli->inbuf) + SVAL(cli->inbuf,smb_vwv6); - memcpy(buf + total, p, size2); + /* Ensure the size is correct. */ + if (toread != size2) { + DEBUG(5,("direct read logic fail toread (%d) != size2 (%u)\n", + (int)toread, (unsigned int)size2 )); + return -1; + } + + /* Read data directly into buffer */ + toread = cli_receive_smb_data(cli,buf+total,size2); + if (toread != size2) { + DEBUG(5,("direct read read failure toread (%d) != size2 (%u)\n", + (int)toread, (unsigned int)size2 )); + return -1; + } + } total += size2; offset += size2; diff --git a/source3/libsmb/smb_signing.c b/source3/libsmb/smb_signing.c index d3384ce365..30f41476e3 100644 --- a/source3/libsmb/smb_signing.c +++ b/source3/libsmb/smb_signing.c @@ -658,6 +658,16 @@ BOOL client_set_trans_sign_state_off(struct cli_state *cli, uint16 mid) } /*********************************************************** + Is client signing on ? +************************************************************/ + +BOOL client_is_signing_on(struct cli_state *cli) +{ + struct smb_sign_info *si = &cli->sign_info; + return si->doing_signing; +} + +/*********************************************************** SMB signing - Server implementation - send the MAC. ************************************************************/ |