/// <summary>This method actually executes the client-side SASL handshake.</summary> /// <param name="underlyingOut">connection output stream</param> /// <param name="underlyingIn">connection input stream</param> /// <param name="userName">SASL user name</param> /// <param name="saslProps">properties of SASL negotiation</param> /// <param name="callbackHandler">for responding to SASL callbacks</param> /// <returns>new pair of streams, wrapped after SASL negotiation</returns> /// <exception cref="System.IO.IOException">for any error</exception> private IOStreamPair DoSaslHandshake(OutputStream underlyingOut, InputStream underlyingIn , string userName, IDictionary <string, string> saslProps, CallbackHandler callbackHandler ) { DataOutputStream @out = new DataOutputStream(underlyingOut); DataInputStream @in = new DataInputStream(underlyingIn); SaslParticipant sasl = SaslParticipant.CreateClientSaslParticipant(userName, saslProps , callbackHandler); @out.WriteInt(SaslTransferMagicNumber); @out.Flush(); try { // Start of handshake - "initial response" in SASL terminology. DataTransferSaslUtil.SendSaslMessage(@out, new byte[0]); // step 1 byte[] remoteResponse = DataTransferSaslUtil.ReadSaslMessage(@in); byte[] localResponse = sasl.EvaluateChallengeOrResponse(remoteResponse); IList <CipherOption> cipherOptions = null; if (DataTransferSaslUtil.RequestedQopContainsPrivacy(saslProps)) { // Negotiate cipher suites if configured. Currently, the only supported // cipher suite is AES/CTR/NoPadding, but the protocol allows multiple // values for future expansion. string cipherSuites = conf.Get(DFSConfigKeys.DfsEncryptDataTransferCipherSuitesKey ); if (cipherSuites != null && !cipherSuites.IsEmpty()) { if (!cipherSuites.Equals(CipherSuite.AesCtrNopadding.GetName())) { throw new IOException(string.Format("Invalid cipher suite, %s=%s", DFSConfigKeys. DfsEncryptDataTransferCipherSuitesKey, cipherSuites)); } CipherOption option = new CipherOption(CipherSuite.AesCtrNopadding); cipherOptions = Lists.NewArrayListWithCapacity(1); cipherOptions.AddItem(option); } } DataTransferSaslUtil.SendSaslMessageAndNegotiationCipherOptions(@out, localResponse , cipherOptions); // step 2 (client-side only) SaslResponseWithNegotiatedCipherOption response = DataTransferSaslUtil.ReadSaslMessageAndNegotiatedCipherOption (@in); localResponse = sasl.EvaluateChallengeOrResponse(response.payload); System.Diagnostics.Debug.Assert(localResponse == null); // SASL handshake is complete DataTransferSaslUtil.CheckSaslComplete(sasl, saslProps); CipherOption cipherOption = null; if (sasl.IsNegotiatedQopPrivacy()) { // Unwrap the negotiated cipher option cipherOption = DataTransferSaslUtil.Unwrap(response.cipherOption, sasl); } // If negotiated cipher option is not null, we will use it to create // stream pair. return(cipherOption != null?DataTransferSaslUtil.CreateStreamPair(conf, cipherOption , underlyingOut, underlyingIn, false) : sasl.CreateStreamPair(@out, @in)); } catch (IOException ioe) { DataTransferSaslUtil.SendGenericSaslErrorMessage(@out, ioe.Message); throw; } }
/// <summary>This method actually executes the server-side SASL handshake.</summary> /// <param name="underlyingOut">connection output stream</param> /// <param name="underlyingIn">connection input stream</param> /// <param name="saslProps">properties of SASL negotiation</param> /// <param name="callbackHandler">for responding to SASL callbacks</param> /// <returns>new pair of streams, wrapped after SASL negotiation</returns> /// <exception cref="System.IO.IOException">for any error</exception> private IOStreamPair DoSaslHandshake(OutputStream underlyingOut, InputStream underlyingIn , IDictionary <string, string> saslProps, CallbackHandler callbackHandler) { DataInputStream @in = new DataInputStream(underlyingIn); DataOutputStream @out = new DataOutputStream(underlyingOut); SaslParticipant sasl = SaslParticipant.CreateServerSaslParticipant(saslProps, callbackHandler ); int magicNumber = @in.ReadInt(); if (magicNumber != SaslTransferMagicNumber) { throw new InvalidMagicNumberException(magicNumber, dnConf.GetEncryptDataTransfer( )); } try { // step 1 byte[] remoteResponse = DataTransferSaslUtil.ReadSaslMessage(@in); byte[] localResponse = sasl.EvaluateChallengeOrResponse(remoteResponse); DataTransferSaslUtil.SendSaslMessage(@out, localResponse); // step 2 (server-side only) IList <CipherOption> cipherOptions = Lists.NewArrayList(); remoteResponse = DataTransferSaslUtil.ReadSaslMessageAndNegotiationCipherOptions( @in, cipherOptions); localResponse = sasl.EvaluateChallengeOrResponse(remoteResponse); // SASL handshake is complete DataTransferSaslUtil.CheckSaslComplete(sasl, saslProps); CipherOption cipherOption = null; if (sasl.IsNegotiatedQopPrivacy()) { // Negotiate a cipher option cipherOption = DataTransferSaslUtil.NegotiateCipherOption(dnConf.GetConf(), cipherOptions ); if (cipherOption != null) { if (Log.IsDebugEnabled()) { Log.Debug("Server using cipher suite " + cipherOption.GetCipherSuite().GetName()); } } } // If negotiated cipher option is not null, wrap it before sending. DataTransferSaslUtil.SendSaslMessageAndNegotiatedCipherOption(@out, localResponse , DataTransferSaslUtil.Wrap(cipherOption, sasl)); // If negotiated cipher option is not null, we will use it to create // stream pair. return(cipherOption != null?DataTransferSaslUtil.CreateStreamPair(dnConf.GetConf (), cipherOption, underlyingOut, underlyingIn, true) : sasl.CreateStreamPair(@out , @in)); } catch (IOException ioe) { if (ioe is SaslException && ioe.InnerException != null && ioe.InnerException is InvalidEncryptionKeyException) { // This could just be because the client is long-lived and hasn't gotten // a new encryption key from the NN in a while. Upon receiving this // error, the client will get a new encryption key from the NN and retry // connecting to this DN. SendInvalidKeySaslErrorMessage(@out, ioe.InnerException.Message); } else { DataTransferSaslUtil.SendGenericSaslErrorMessage(@out, ioe.Message); } throw; } }