/// <summary>Evaluate the server provided challenge.</summary> /// <remarks> /// Evaluate the server provided challenge. The server must send a token /// if it's not done. If the server is done, the challenge token is /// optional because not all mechanisms send a final token for the client to /// update its internal state. The client must also be done after /// evaluating the optional token to ensure a malicious server doesn't /// prematurely end the negotiation with a phony success. /// </remarks> /// <param name="saslResponse">- client response to challenge</param> /// <param name="serverIsDone">- server negotiation state</param> /// <exception cref="Javax.Security.Sasl.SaslException">- any problems with negotiation /// </exception> private byte[] SaslEvaluateToken(RpcHeaderProtos.RpcSaslProto saslResponse, bool serverIsDone) { byte[] saslToken = null; if (saslResponse.HasToken()) { saslToken = saslResponse.GetToken().ToByteArray(); saslToken = saslClient.EvaluateChallenge(saslToken); } else { if (!serverIsDone) { // the server may only omit a token when it's done throw new SaslException("Server challenge contains no token"); } } if (serverIsDone) { // server tried to report success before our client completed if (!saslClient.IsComplete()) { throw new SaslException("Client is out of sync with server"); } // a client cannot generate a response to a success message if (saslToken != null) { throw new SaslException("Client generated spurious response"); } } return(saslToken); }
/// <exception cref="System.IO.IOException"/> private void SendSaslMessage(DataOutputStream @out, RpcHeaderProtos.RpcSaslProto message) { if (Log.IsDebugEnabled()) { Log.Debug("Sending sasl message " + message); } ProtobufRpcEngine.RpcRequestMessageWrapper request = new ProtobufRpcEngine.RpcRequestMessageWrapper (saslHeader, message); @out.WriteInt(request.GetLength()); request.Write(@out); @out.Flush(); }
/// <exception cref="System.IO.IOException"/> public override void Write(byte[] buf, int off, int len) { if (SaslRpcClient.Log.IsDebugEnabled()) { SaslRpcClient.Log.Debug("wrapping token of length:" + len); } buf = this._enclosing.saslClient.Wrap(buf, off, len); RpcHeaderProtos.RpcSaslProto saslMessage = ((RpcHeaderProtos.RpcSaslProto)RpcHeaderProtos.RpcSaslProto .NewBuilder().SetState(RpcHeaderProtos.RpcSaslProto.SaslState.Wrap).SetToken(ByteString .CopyFrom(buf, 0, buf.Length)).Build()); ProtobufRpcEngine.RpcRequestMessageWrapper request = new ProtobufRpcEngine.RpcRequestMessageWrapper (SaslRpcClient.saslHeader, saslMessage); DataOutputStream dob = new DataOutputStream(this.@out); dob.WriteInt(request.GetLength()); request.Write(dob); }
/// <summary> /// Do client side SASL authentication with server via the given InputStream /// and OutputStream /// </summary> /// <param name="inS">InputStream to use</param> /// <param name="outS">OutputStream to use</param> /// <returns>AuthMethod used to negotiate the connection</returns> /// <exception cref="System.IO.IOException"/> public virtual SaslRpcServer.AuthMethod SaslConnect(InputStream inS, OutputStream outS) { DataInputStream inStream = new DataInputStream(new BufferedInputStream(inS)); DataOutputStream outStream = new DataOutputStream(new BufferedOutputStream(outS)); // redefined if/when a SASL negotiation starts, can be queried if the // negotiation fails authMethod = SaslRpcServer.AuthMethod.Simple; SendSaslMessage(outStream, negotiateRequest); // loop until sasl is complete or a rpc error occurs bool done = false; do { int totalLen = inStream.ReadInt(); ProtobufRpcEngine.RpcResponseMessageWrapper responseWrapper = new ProtobufRpcEngine.RpcResponseMessageWrapper (); responseWrapper.ReadFields(inStream); RpcHeaderProtos.RpcResponseHeaderProto header = responseWrapper.GetMessageHeader( ); switch (header.GetStatus()) { case RpcHeaderProtos.RpcResponseHeaderProto.RpcStatusProto.Error: case RpcHeaderProtos.RpcResponseHeaderProto.RpcStatusProto.Fatal: { // might get a RPC error during throw new RemoteException(header.GetExceptionClassName(), header.GetErrorMsg()); } default: { break; } } if (totalLen != responseWrapper.GetLength()) { throw new SaslException("Received malformed response length"); } if (header.GetCallId() != Server.AuthProtocol.Sasl.callId) { throw new SaslException("Non-SASL response during negotiation"); } RpcHeaderProtos.RpcSaslProto saslMessage = RpcHeaderProtos.RpcSaslProto.ParseFrom (responseWrapper.GetMessageBytes()); if (Log.IsDebugEnabled()) { Log.Debug("Received SASL message " + saslMessage); } // handle sasl negotiation process RpcHeaderProtos.RpcSaslProto.Builder response = null; switch (saslMessage.GetState()) { case RpcHeaderProtos.RpcSaslProto.SaslState.Negotiate: { // create a compatible SASL client, throws if no supported auths RpcHeaderProtos.RpcSaslProto.SaslAuth saslAuthType = SelectSaslClient(saslMessage .GetAuthsList()); // define auth being attempted, caller can query if connect fails authMethod = SaslRpcServer.AuthMethod.ValueOf(saslAuthType.GetMethod()); byte[] responseToken = null; if (authMethod == SaslRpcServer.AuthMethod.Simple) { // switching to SIMPLE done = true; } else { // not going to wait for success ack byte[] challengeToken = null; if (saslAuthType.HasChallenge()) { // server provided the first challenge challengeToken = saslAuthType.GetChallenge().ToByteArray(); saslAuthType = ((RpcHeaderProtos.RpcSaslProto.SaslAuth)RpcHeaderProtos.RpcSaslProto.SaslAuth .NewBuilder(saslAuthType).ClearChallenge().Build()); } else { if (saslClient.HasInitialResponse()) { challengeToken = new byte[0]; } } responseToken = (challengeToken != null) ? saslClient.EvaluateChallenge(challengeToken ) : new byte[0]; } response = CreateSaslReply(RpcHeaderProtos.RpcSaslProto.SaslState.Initiate, responseToken ); response.AddAuths(saslAuthType); break; } case RpcHeaderProtos.RpcSaslProto.SaslState.Challenge: { if (saslClient == null) { // should probably instantiate a client to allow a server to // demand a specific negotiation throw new SaslException("Server sent unsolicited challenge"); } byte[] responseToken = SaslEvaluateToken(saslMessage, false); response = CreateSaslReply(RpcHeaderProtos.RpcSaslProto.SaslState.Response, responseToken ); break; } case RpcHeaderProtos.RpcSaslProto.SaslState.Success: { // simple server sends immediate success to a SASL client for // switch to simple if (saslClient == null) { authMethod = SaslRpcServer.AuthMethod.Simple; } else { SaslEvaluateToken(saslMessage, true); } done = true; break; } default: { throw new SaslException("RPC client doesn't support SASL " + saslMessage.GetState ()); } } if (response != null) { SendSaslMessage(outStream, ((RpcHeaderProtos.RpcSaslProto)response.Build())); } }while (!done); return(authMethod); }