void TSaslTransport::open() { NegotiationStatus status = TSASL_INVALID; uint32_t resLength; // Only client should open the underlying transport. if (isClient_ && !transport_->isOpen()) { transport_->open(); } // initiate SASL message handleSaslStartMessage(); // SASL connection handshake while (!sasl_->isComplete()) { uint8_t* message = receiveSaslMessage(&status, &resLength); if (status == TSASL_COMPLETE) { if (isClient_) { if (!sasl_->isComplete()) { // Server sent COMPLETE out of order. throw TTransportException("Received COMPLETE but no handshake occurred"); } break; // handshake complete } } else if (status != TSASL_OK) { stringstream ss; ss << "Expected COMPLETE or OK, got " << status; throw TTransportException(ss.str()); } uint32_t challengeLength; uint8_t* challenge = sasl_->evaluateChallengeOrResponse( message, resLength, &challengeLength); sendSaslMessage(sasl_->isComplete() ? TSASL_COMPLETE : TSASL_OK, challenge, challengeLength); } // If the server isn't complete yet, we need to wait for its response. // This will occur with ANONYMOUS auth, for example, where we send an // initial response and are immediately complete. if (isClient_ && (status == TSASL_INVALID || status == TSASL_OK)) { receiveSaslMessage(&status, &resLength); if (status != TSASL_COMPLETE) { stringstream ss; ss << "Expected COMPLETE or OK, got " << status; throw TTransportException(ss.str()); } } // TODO : need to set the shouldWrap_ based on QOP /* String qop = (String) sasl.getNegotiatedProperty(Sasl.QOP); if (qop != null && !qop.equalsIgnoreCase("auth")) shouldWrap_ = true; */ }
/** * Performs the server side of the initial portion of the Thrift SASL protocol. * Receives the initial response from the client, creates a SASL server using * the mechanism requested by the client (if this server supports it), and * sends the first challenge back to the client. */ void TSaslServerTransport::handleSaslStartMessage() { uint32_t msgLength; NegotiationStatus status; uint8_t* message = receiveSaslMessage(&status, &msgLength); if (status != TSASL_START) { stringstream ss; ss << "Expecting START status, received " << status; sendSaslMessage(TSASL_ERROR, reinterpret_cast<const uint8_t*>(ss.str().c_str()), ss.str().size()); throw TTransportException(ss.str()); } // Message is a non-null terminated string; to use it like a // C-string we have to copy it into a null-terminated buffer. // The first message should be the mechanism string. string mechanism(reinterpret_cast<char*>(message), msgLength); map<string, TSaslServerDefinition*>::iterator defn = TSaslServerTransport::serverDefinitionMap_.find(mechanism); if (defn == TSaslServerTransport::serverDefinitionMap_.end()) { stringstream ss; ss << "Unsupported mechanism type " << mechanism; sendSaslMessage(TSASL_BAD, reinterpret_cast<const uint8_t*>(ss.str().c_str()), ss.str().size()); throw TTransportException(TTransportException::BAD_ARGS, ss.str()); } TSaslServerDefinition* serverDefinition = defn->second; sasl_.reset(new TSaslServer(mechanism, serverDefinition->protocol_, serverDefinition->serverName_, serverDefinition->realm_, serverDefinition->flags_, &serverDefinition->callbacks_[0])); uint32_t challengeLength; uint8_t* challenge = sasl_->evaluateChallengeOrResponse( reinterpret_cast<const uint8_t*>(mechanism.c_str()), msgLength, &challengeLength); // TODO: this is necessary for DIGEST-MD5 but not for GSSAPI. There should be a more // general way to do this rather than checking the challengeLength. For GSSAPI, this // is zero, the server just authenticates. For DIGEST-MD5, this begins some back and // forth to send additional information. if (challengeLength != 0) { sendSaslMessage(sasl_->isComplete() ? TSASL_COMPLETE : TSASL_OK, challenge, challengeLength); } }
void TSaslServerTransport::handleSaslStartMessage() { uint32_t resLength; NegotiationStatus status; uint8_t* message = receiveSaslMessage(&status, &resLength); // Message is a non-null terminated string; to use it like a // C-string we have to copy it into a null-terminated buffer. string message_str(reinterpret_cast<char*>(message), resLength); if (status != TSASL_START) { stringstream ss; ss << "Expecting START status, received " << status; sendSaslMessage(TSASL_ERROR, reinterpret_cast<const uint8_t*>(ss.str().c_str()), ss.str().size()); throw TTransportException(ss.str()); } map<string, TSaslServerDefinition*>::iterator defn = TSaslServerTransport::serverDefinitionMap_.find(message_str); if (defn == TSaslServerTransport::serverDefinitionMap_.end()) { stringstream ss; ss << "Unsupported mechanism type " << message_str; sendSaslMessage(TSASL_BAD, reinterpret_cast<const uint8_t*>(ss.str().c_str()), ss.str().size()); throw TTransportException(TTransportException::BAD_ARGS, ss.str()); } // TODO: when should realm be used? string realm; TSaslServerDefinition* serverDefinition = defn->second; sasl_.reset(new TSaslServer(serverDefinition->protocol_, serverDefinition->serverName_, realm, serverDefinition->flags_, &serverDefinition->callbacks_[0])); // First argument is interpreted as C-string sasl_->evaluateChallengeOrResponse( reinterpret_cast<const uint8_t*>(message_str.c_str()), resLength, &resLength); }
void TSaslTransport::open() { // Only client should open the underlying transport. if (isClient_ && !transport_->isOpen()) transport_->open(); // initiate SASL message handleSaslStartMessage(); // The number of messages received so far. int numMessagesReceived = 0; NegotiationStatus status = TSASL_INVALID; uint32_t msgLength; // SASL connection handshake while (!sasl_->isComplete()) { uint8_t* message = receiveSaslMessage(&status, &msgLength); ++numMessagesReceived; if (status != TSASL_COMPLETE && status != TSASL_OK) { stringstream ss; ss << "Expected COMPLETE or OK, got " << status; throw TTransportException(ss.str()); } if (numMessagesReceived == 1 && msgLength == 0 && sasl_->getMechanismName() == "DIGEST-MD5") { // TODO: this is a hack, not sure what the proper behavior is. These messages // are optional and checking for QOP support. // What's the general way to implement the protocol? continue; } uint32_t challengeLength; uint8_t* challenge = sasl_->evaluateChallengeOrResponse( message, msgLength, &challengeLength); if (status == TSASL_COMPLETE && isClient_) { // If we are the client, and the server indicates COMPLETE, we don't need to // send back any further response. break; } sendSaslMessage(sasl_->isComplete() ? TSASL_COMPLETE : TSASL_OK, challenge, challengeLength); } // If the server isn't complete yet, we need to wait for its response. // This will occur with ANONYMOUS auth, for example, where we send an // initial response and are immediately complete. if (isClient_ && (status == TSASL_INVALID || status == TSASL_OK)) { receiveSaslMessage(&status, &msgLength); if (status != TSASL_COMPLETE) { stringstream ss; ss << "Expected COMPLETE or OK, got " << status; throw TTransportException(ss.str()); } } // TODO : need to set the shouldWrap_ based on QOP /* String qop = (String) sasl.getNegotiatedProperty(Sasl.QOP); if (qop != null && !qop.equalsIgnoreCase("auth")) shouldWrap_ = true; */ }