Drill-4335: C++ client changes for supporting encryption using SASL#809
Drill-4335: C++ client changes for supporting encryption using SASL#809sohami wants to merge 1 commit intoapache:masterfrom
Conversation
8b711ba to
ee915e1
Compare
328b228 to
2997bbf
Compare
|
Adding @laurentgo, @sudheeshkatkam, @BitBlender to help review the changes. |
2997bbf to
293fc2d
Compare
| // Split the encoded message into the rawWrapSendSize and then encrypt each chunk. Each encrypted chunk along with | ||
| // its encrypted length in network order (added by Cyrus-SASL plugin) is sent over wire. | ||
| const int wrapChunkSize = m_encryptionCtxt.getRawWrapSendSize(); | ||
| int numChunks = ceil((double)m_wbuf.size() / wrapChunkSize); |
There was a problem hiding this comment.
'chunksRemaining' would be better a better name than 'numChunks' because the value is decremented per chunk
| int numChunks = ceil((double)m_wbuf.size() / wrapChunkSize); | ||
| int lengthToEncrypt = m_wbuf.size(); | ||
| int currentChunkLen = std::min(wrapChunkSize, lengthToEncrypt); | ||
| uint32_t startIndex = 0, wrappedLen = 0; |
There was a problem hiding this comment.
"currentChunkOffset" would be a better name than 'startIndex'.
| errorMsg << "Sasl wrap failed while encrypting chunk of length: " << currentChunkLen << " , EncodeError: " | ||
| << wrapResult; | ||
| DRILL_MT_LOG(DRILL_LOG(LOG_TRACE) << "DrillClientImpl::sendSyncEncrypted - " << errorMsg.str() | ||
| << " ,StartIndex: " << startIndex << ". ChunkNum: " << numChunks |
There was a problem hiding this comment.
Did you want to print the chunk number or chunks remaining? ChunkNum should be Total chunks - Chunks Remaining.
|
|
||
| if(ec || s==0){ | ||
| errorMsg << "Failure while sending encrypted chunk. Error: " << ec.message().c_str(); | ||
| DRILL_MT_LOG(DRILL_LOG(LOG_TRACE) << "DrillClientImpl::sendSyncEncrypted - " << errorMsg.str() << " Chunk:" |
There was a problem hiding this comment.
Did you want to print the chunk number or chunks remaining? ChunkNum should be Total chunks - Chunks Remaining.
| DRILL_MT_LOG(DRILL_LOG(LOG_TRACE) << "DrillClientImpl::sendSyncEncrypted - " << errorMsg.str() << " Chunk:" | ||
| << numChunks << ", Original Length: " << currentChunkLen | ||
| << ", StartIndex:" << startIndex << ", Closing connection.";) | ||
| return handleConnError(CONN_FAILURE, getMessage(ERR_CONN_WFAIL, errorMsg.str().c_str())); |
There was a problem hiding this comment.
What happens to the memory allocated for wrappedChunk when this path is taken? Does handleConnError() deal with it ?
| return handleQryError(QRY_INTERNAL_ERROR, getMessage(ERR_QRY_INVREADLEN), NULL); | ||
| } | ||
|
|
||
| return error ? handleQryError(QRY_COMM_ERROR, getMessage(ERR_QRY_COMMERR, error.message().c_str()), NULL) |
There was a problem hiding this comment.
what happens to memory allocated for bufferWithLenBytes on error?
There was a problem hiding this comment.
Memory for bufferWithLenBytes is deallocated by caller. Added a comment in the function header.
| * status_t - QRY_SUCCESS - In case of success. | ||
| * - QRY_COMM_ERROR/QRY_INTERNAL_ERROR/QRY_CLIENT_OUTOFMEM - In cases of error. | ||
| */ | ||
| status_t DrillClientImpl::readMsg(ByteBuf_t _buf, AllocatedBufferPtr* allocatedBuffer, |
There was a problem hiding this comment.
For the sake of consistency, I would recommend not using parameter names with a '' prefix. AFAIK '' prefix variables are used for class member names. Also 'inBuf' would be a better name than 'buf'
There was a problem hiding this comment.
Was kept as is based on previous implementation. Will change it to inBuf.
| { | ||
| // We need to protect the readLength and read buffer, and the pending requests counter, | ||
| // but we don't have to keep the lock while we decode the rest of the buffer. | ||
| boost::lock_guard<boost::mutex> lock(this->m_dcMutex); |
There was a problem hiding this comment.
lock usage seems to be inconsistent with the comment. the lock remains taken when decode() is called.
There was a problem hiding this comment.
preserved the old behavior.
| * Read all the encrypted chunk needed to form a complete RPC message. Read an entire chunk from network, decrypt it | ||
| * and put in a buffer. The same process is repeated until the entire buffer to form a completed RPC message is read. | ||
| * Parameters: | ||
| * _buf - in param - ByteBuf_t containing atleast the length bytes. |
There was a problem hiding this comment.
For the sake of consistency, I would recommend not using parameter names with a '' prefix. AFAIK '' prefix variables are used for class member names.
| { | ||
| // We need to protect the readLength and read buffer, and the pending requests counter, | ||
| // but we don't have to keep the lock while we decode the rest of the buffer. | ||
| boost::lock_guard<boost::mutex> lock(this->m_dcMutex); |
There was a problem hiding this comment.
lock is still held when unwrap is called.
There was a problem hiding this comment.
Same as above. the decode referenced here is not unwrap but decode function below which creates a valid RpcMsg.
293fc2d to
2ade1d4
Compare
sohami
left a comment
There was a problem hiding this comment.
Need to look more on below comment, it looks like on ConnError/QryError we are just closing the socket. I am not sure if socket is dead then do we re-use DrillClientImpl instance or not. If not then in DrillClientImpl destructor, m_saslAuthenticator instance is deleted which will take care of freeing memory for decode/encode. If we re-use the DrillClientImpl instance then there can be leaks for m_saslAuthenticator
What happens to the memory allocated for wrappedChunk when this path is taken? Does handleConnError() deal with it ?
| } | ||
|
|
||
| // Loop through the property to find USERPROP_ENCRYPTION and it's value | ||
| for (size_t i = 0; i < userProperties->size(); i++) { |
There was a problem hiding this comment.
DrillUserProperties has a map and a vector. The vector stores the actual prop key/value pair and in map we store the prop key and corresponding bits indicating USERPROP_FLAGS_SERVERPROP|USERPROP_FLAGS_STRING. This is later used in handshake message to filter out the properties passed by client and the one which is needed for server side to send along with handshake msg.
| } | ||
| } | ||
|
|
||
| std::stringstream errorMsg; |
There was a problem hiding this comment.
Fixed name and a bug where in success case with auth only it was printing wrong error message.
| * bufWithLen - in param - buffer containing the bytes which has length of the RPC message/encrypted chunk | ||
| * bufferWithLenBytes - out param - buffer pointer which points to memory allocated in this function and has the | ||
| * entire one RPC message / encrypted chunk along with the length of the message | ||
| * lengthBytesLength - out param - bytes of bufWithLen which contains the length of the entire RPC message or |
There was a problem hiding this comment.
Changed to "lengthFieldLength" as on Java side which is borrowed from LengthFieldBasedFrameDecoder
| * Decode the length of the message from bufWithLen and then read entire message from the socket. | ||
| * Parameters: | ||
| * bufWithLen - in param - buffer containing the bytes which has length of the RPC message/encrypted chunk | ||
| * bufferWithLenBytes - out param - buffer pointer which points to memory allocated in this function and has the |
There was a problem hiding this comment.
bufferWithLenBytes may not be a complete RPC Msg. Changed to bufWithLen --> bufWithLenField and bufferWithLenBytes --> bufWithDataAndLenBytes
| *bufferWithLenBytes = NULL; | ||
| size_t bufferWithLenBytesSize = 0; | ||
|
|
||
| bytes_read = (this->*lengthDecodeHandler)(bufWithLen, rmsgLen); |
There was a problem hiding this comment.
lengthDecoder is a function pointer signature that accepts function of DrillClientImpl class with that signature.
| { | ||
| // We need to protect the readLength and read buffer, and the pending requests counter, | ||
| // but we don't have to keep the lock while we decode the rest of the buffer. | ||
| boost::lock_guard<boost::mutex> lock(this->m_dcMutex); |
There was a problem hiding this comment.
preserved the old behavior.
| { | ||
| // We need to protect the readLength and read buffer, and the pending requests counter, | ||
| // but we don't have to keep the lock while we decode the rest of the buffer. | ||
| boost::lock_guard<boost::mutex> lock(this->m_dcMutex); |
There was a problem hiding this comment.
Same as above. the decode referenced here is not unwrap but decode function below which creates a valid RpcMsg.
| * Read all the encrypted chunk needed to form a complete RPC message. Read an entire chunk from network, decrypt it | ||
| * and put in a buffer. The same process is repeated until the entire buffer to form a completed RPC message is read. | ||
| * Parameters: | ||
| * _buf - in param - ByteBuf_t containing atleast the length bytes. |
| * Return: | ||
| * size_t - length bytes read to decode | ||
| */ | ||
| size_t DrillClientImpl::rpcLengthDecode(const ByteBuf_t _buf, uint32_t& rmsgLen) { |
There was a problem hiding this comment.
lengthDecode or rpcLengthDecode is passed as function pointer to readlenBytesFromSocket based on encryption is enabled or not. The function pointer signature accepting these functions is:
typedef size_t (DrillClientImpl::*lengthDecoder)(const ByteBuf_t, uint32_t&); which means a fynction of this signature and belonging to class DrillClientImpl. Hence the wrapper is created.
| EncryptionContext::EncryptionContext() { | ||
| this->m_bEncryptionReqd = false; | ||
| // SASL Framework only allows 3 octet length field during negotiation so maximum wrap message | ||
| // length can be 16MB i.e. 0XFFFFFF |
There was a problem hiding this comment.
Nice catch!. I am changing it to be 65536 now based on recent findings.
2ade1d4 to
695d836
Compare
karthik-man
left a comment
There was a problem hiding this comment.
Have some questions. Otherwise, LGTM. +1
| * errorCode - out param - Error code set by boost. | ||
| */ | ||
| void DrillClientImpl::doWriteToSocket(const char* dataPtr, size_t bytesToWrite, | ||
| boost::system::error_code& errorCode) { |
There was a problem hiding this comment.
Should you check for a NULL dataPtr ?
There was a problem hiding this comment.
Not really. Since write_some will set the proper error code in that case. The handing for bytesToWrite == 0 was done since that's a success case and didn't want to call write_some on it.
| boost::system::error_code& errorCode) { | ||
|
|
||
| // Check if bytesToRead is zero | ||
| if(0 == bytesToRead) { |
There was a problem hiding this comment.
Does a NULL inBuf have to be handled ?
695d836 to
2af2d63
Compare
|
Squashed commits. |
NOTE: This pull request provides support for on-wire encryption using SASL framework. Communication channel covered is:
1) C++ Drill Client and Drillbit channel.
2af2d63 to
8f39be2
Compare
|
+1 |
No description provided.