diff options
author | Rajith Muditha Attapattu <rajith@apache.org> | 2011-05-27 15:44:23 +0000 |
---|---|---|
committer | Rajith Muditha Attapattu <rajith@apache.org> | 2011-05-27 15:44:23 +0000 |
commit | 66765100f4257159622cefe57bed50125a5ad017 (patch) | |
tree | a88ee23bb194eb91f0ebb2d9b23ff423e3ea8e37 /qpid/dotnet/Qpid.Sasl | |
parent | 1aeaa7b16e5ce54f10c901d75c4d40f9f88b9db6 (diff) | |
parent | 88b98b2f4152ef59a671fad55a0d08338b6b78ca (diff) | |
download | qpid-python-rajith_jms_client.tar.gz |
Creating a branch for experimenting with some ideas for JMS client.rajith_jms_client
git-svn-id: https://svn.apache.org/repos/asf/qpid/branches/rajith_jms_client@1128369 13f79535-47bb-0310-9956-ffa450edef68
Diffstat (limited to 'qpid/dotnet/Qpid.Sasl')
21 files changed, 2156 insertions, 0 deletions
diff --git a/qpid/dotnet/Qpid.Sasl/Callbacks.cs b/qpid/dotnet/Qpid.Sasl/Callbacks.cs new file mode 100644 index 0000000000..f4fcc1c54b --- /dev/null +++ b/qpid/dotnet/Qpid.Sasl/Callbacks.cs @@ -0,0 +1,139 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +using System; +using System.Text; +using System.Globalization; +using System.Security.Cryptography; + +namespace Apache.Qpid.Sasl +{ + /// <summary> + /// Marker interface for Sasl Callbacks + /// </summary> + public interface ISaslCallback + { + } // interface ISaslCallback + + public abstract class TextSaslCallback : ISaslCallback + { + private string _prompt; + private string _text; + private string _defaultText; + + public string Prompt + { + get { return _prompt; } + set { _prompt = value; } + } + + public string Text + { + get { + if ( _text == null || _text.Length == 0 ) + return DefaultText; + else + return _text; + } + set { _text = value; } + } + + public string DefaultText + { + get { return _defaultText; } + set { _defaultText = value; } + } + + protected TextSaslCallback(string prompt, string text, string defaultText) + { + _prompt = prompt; + _text = text; + _defaultText = defaultText; + } + + } // class TextSaslCallback + + public class NameCallback : TextSaslCallback + { + public NameCallback() + : this(Environment.UserName) + { + } + public NameCallback(string defaultText) + : base("username:", "", defaultText) + { + } + } // class NameCallback + + public class PasswordCallback : TextSaslCallback + { + public PasswordCallback() + : base("password:", "", "") + { + } + + public byte[] HashedText + { + get + { + string _text = this.Text; + System.Security.Cryptography.MD5CryptoServiceProvider x = new System.Security.Cryptography.MD5CryptoServiceProvider(); + byte[] bs = x.ComputeHash(Encoding.UTF8.GetBytes(_text)); + return bs; + } + + } + } // class PasswordCallback + + public class HashedPasswordCallback : TextSaslCallback + { + public HashedPasswordCallback() + : base("password:", "", "") + { + } + + public byte[] HashedText + { + get { + string _text = this.Text; + System.Security.Cryptography.MD5CryptoServiceProvider x = new System.Security.Cryptography.MD5CryptoServiceProvider(); + _text = _text.PadRight(16, '\0'); + byte[] bs = x.ComputeHash(Encoding.UTF8.GetBytes(_text)); + return bs; + } + } + } // class PasswordCallback + + public class RealmCallback : TextSaslCallback + { + public RealmCallback() + : this("localhost") + { + } + public RealmCallback(string defaultText) + : base("realm:", "", defaultText) + { + } + } // class RealmCallback + +} // namespace Apache.Qpid.Sasl + + diff --git a/qpid/dotnet/Qpid.Sasl/Configuration/SaslConfiguration.cs b/qpid/dotnet/Qpid.Sasl/Configuration/SaslConfiguration.cs new file mode 100644 index 0000000000..7a71ec28da --- /dev/null +++ b/qpid/dotnet/Qpid.Sasl/Configuration/SaslConfiguration.cs @@ -0,0 +1,90 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +using System; +using System.Collections; +using System.Configuration; +using System.Text; +using System.Xml; + +namespace Apache.Qpid.Sasl.Configuration +{ + /// <summary> + /// Represents an Sasl configuration section + /// in the config file + /// </summary> + internal class SaslConfiguration + { + private IList _clientFactories; + + /// <summary> + /// Set of configured client factores + /// </summary> + public IList ClientFactories + { + get { return _clientFactories; } + } + + internal SaslConfiguration(IList clientFactoryTypes) + { + _clientFactories = new ArrayList(); + foreach ( Type type in clientFactoryTypes ) + { + _clientFactories.Add(Activator.CreateInstance(type)); + } + } + + /// <summary> + /// Get the configuration for the library + /// </summary> + /// <returns>The configuration from app.config or a default configuration</returns> + internal static SaslConfiguration GetConfiguration() + { + // 'obsolete' warning, but needed for .NET 1.1 compatibility + SaslConfiguration config = (SaslConfiguration) + ConfigurationSettings.GetConfig("qpid.sasl"); + if ( config == null ) + { + // create default configuration + IList clientFactories = GetDefaultClientFactories(); + config = new SaslConfiguration(clientFactories); + } + return config; + } + + /// <summary> + /// Create a list filled with the default client + /// factories supported by the library + /// </summary> + /// <returns>The list of client factory types</returns> + internal static IList GetDefaultClientFactories() + { + IList clientFactories = new ArrayList(); + clientFactories.Add(typeof(DefaultClientFactory)); + return clientFactories; + } + + + } // class SaslConfiguration + +} // namespace Apache.Qpid.Sasl.Configuration + + diff --git a/qpid/dotnet/Qpid.Sasl/Configuration/SaslConfigurationSectionHandler.cs b/qpid/dotnet/Qpid.Sasl/Configuration/SaslConfigurationSectionHandler.cs new file mode 100644 index 0000000000..ea8669f8c4 --- /dev/null +++ b/qpid/dotnet/Qpid.Sasl/Configuration/SaslConfigurationSectionHandler.cs @@ -0,0 +1,84 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +using System; +using System.Collections; +using System.Configuration; +using System.Text; +using System.Xml; + +namespace Apache.Qpid.Sasl.Configuration +{ + /// <summary> + /// Defines the configuration section to configure extra + /// Sasl client factories + /// </summary> + public class SaslConfigurationSectionHandler + : IConfigurationSectionHandler + { + public object Create(object parent, object configContext, XmlNode section) + { + IList clientFactories = SaslConfiguration.GetDefaultClientFactories(); + + foreach ( XmlNode node in section.ChildNodes ) + { + if ( node.LocalName == "clientFactories" ) + { + ProcessFactories(node, clientFactories); + } + } + + SaslConfiguration config = new SaslConfiguration(clientFactories); + return config; + } + + + private void ProcessFactories(XmlNode node, IList factories) + { + foreach ( XmlNode child in node.ChildNodes ) + { + Type type; + switch ( child.LocalName ) + { + case "add": + type = Type.GetType(child.Attributes["type"].Value); + if ( !factories.Contains(type) ) + factories.Add(type); + break; + case "remove": + type = Type.GetType(child.Attributes["type"].Value); + if ( factories.Contains(type) ) + factories.Remove(type); + break; + case "clear": + factories.Clear(); + break; + default: + // gives obsolete warning but needed for .NET 1.1 support + throw new ConfigurationException(string.Format("Unknown element '{0}' in section '{0}'", child.LocalName, node.LocalName)); + } + } + } + } // class SaslConfigurationSectionHandler + +} // namespace Apache.Qpid.Sasl.Configuration + + diff --git a/qpid/dotnet/Qpid.Sasl/DefaultClientFactory.cs b/qpid/dotnet/Qpid.Sasl/DefaultClientFactory.cs new file mode 100644 index 0000000000..744d7cae40 --- /dev/null +++ b/qpid/dotnet/Qpid.Sasl/DefaultClientFactory.cs @@ -0,0 +1,99 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +using System; +using System.Collections; + +using Apache.Qpid.Sasl.Mechanisms; + +namespace Apache.Qpid.Sasl +{ + public class DefaultClientFactory : ISaslClientFactory + { + private static readonly string[] SUPPORTED = new string[] { + DigestSaslClient.Mechanism, + CramMD5SaslClient.Mechanism, + CramMD5HexSaslClient.Mechanism, + PlainSaslClient.Mechanism, + AnonymousSaslClient.Mechanism, + ExternalSaslClient.Mechanism, + }; + + public string[] GetSupportedMechanisms(IDictionary props) + { + if ( props == null ) + throw new ArgumentNullException("props"); + + ArrayList vetoed = new ArrayList(); + + if ( props.Contains(SaslProperties.PolicyNoPlainText) || + props.Contains(SaslProperties.PolicyNoDictionary) || + props.Contains(SaslProperties.PolicyNoActive) || + props.Contains(SaslProperties.PolicyForwardSecrecy) || + props.Contains(SaslProperties.PolicyPassCredentials) ) + { + vetoed.Add(CramMD5SaslClient.Mechanism); + vetoed.Add(CramMD5HexSaslClient.Mechanism); + vetoed.Add(PlainSaslClient.Mechanism); + vetoed.Add(AnonymousSaslClient.Mechanism); + vetoed.Add(ExternalSaslClient.Mechanism); + } + if ( props.Contains(SaslProperties.PolicyNoAnonymous) ) + { + vetoed.Add(AnonymousSaslClient.Mechanism); + } + + ArrayList available = new ArrayList(); + foreach ( string mech in SUPPORTED ) + { + if ( !vetoed.Contains(mech) ) + available.Add(mech); + } + return (string[])available.ToArray(typeof(string)); + } + + public ISaslClient CreateClient( + string[] mechanisms, string authorizationId, + string protocol, string serverName, + IDictionary props, ISaslCallbackHandler handler + ) + { + IList mechs = mechanisms; + if ( mechs.Contains(ExternalSaslClient.Mechanism) ) + return new ExternalSaslClient(authorizationId, props, handler); + if ( mechs.Contains(DigestSaslClient.Mechanism) ) + return new DigestSaslClient(authorizationId, serverName, protocol, props, handler); + if ( mechs.Contains(CramMD5SaslClient.Mechanism) ) + return new CramMD5SaslClient(authorizationId, props, handler); + if ( mechs.Contains(CramMD5HexSaslClient.Mechanism) ) + return new CramMD5HexSaslClient(authorizationId, props, handler); + if ( mechs.Contains(PlainSaslClient.Mechanism) ) + return new PlainSaslClient(authorizationId, props, handler); + if ( mechs.Contains(AnonymousSaslClient.Mechanism) ) + return new AnonymousSaslClient(authorizationId, props, handler); + // unknown mechanism + return null; + } + } // class DefaultClientFactory + +} // namespace Apache.Qpid.Sasl + + diff --git a/qpid/dotnet/Qpid.Sasl/ISaslCallbackHandler.cs b/qpid/dotnet/Qpid.Sasl/ISaslCallbackHandler.cs new file mode 100644 index 0000000000..c2638f245e --- /dev/null +++ b/qpid/dotnet/Qpid.Sasl/ISaslCallbackHandler.cs @@ -0,0 +1,35 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +using System; +using System.Text; + +namespace Apache.Qpid.Sasl +{ + public interface ISaslCallbackHandler + { + void Handle(ISaslCallback[] callbacks); + + } // interface ISaslCallbackHandler + +} // namespace Apache.Qpid.Sasl + + diff --git a/qpid/dotnet/Qpid.Sasl/ISaslClient.cs b/qpid/dotnet/Qpid.Sasl/ISaslClient.cs new file mode 100644 index 0000000000..668ca05d26 --- /dev/null +++ b/qpid/dotnet/Qpid.Sasl/ISaslClient.cs @@ -0,0 +1,42 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +using System; +using System.Text; + +namespace Apache.Qpid.Sasl +{ + public interface ISaslClient + { + string MechanismName { get; } + bool HasInitialResponse { get; } + bool IsComplete { get; } + + byte[] EvaluateChallenge(byte[] challenge); + object GetNegotiatedProperty(string propName); + byte[] Unwrap(byte[] buffer, int offset, int length); + byte[] Wrap(byte[] buffer, int offset, int lenght); + + } // interface ISaslClient + +} // namespace Apache.Qpid.Sasl + + diff --git a/qpid/dotnet/Qpid.Sasl/ISaslClientFactory.cs b/qpid/dotnet/Qpid.Sasl/ISaslClientFactory.cs new file mode 100644 index 0000000000..f052e07ad9 --- /dev/null +++ b/qpid/dotnet/Qpid.Sasl/ISaslClientFactory.cs @@ -0,0 +1,40 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +using System; +using System.Collections; +using System.Text; + +namespace Apache.Qpid.Sasl +{ + public interface ISaslClientFactory + { + string[] GetSupportedMechanisms(IDictionary props); + ISaslClient CreateClient( + string[] mechanisms, string authorizationId, + string protocol, string serverName, + IDictionary props, ISaslCallbackHandler handler + ); + } // interface ISaslClientFactory + +} // namespace Apache.Qpid.Sasl + + diff --git a/qpid/dotnet/Qpid.Sasl/MD5HMAC.cs b/qpid/dotnet/Qpid.Sasl/MD5HMAC.cs new file mode 100644 index 0000000000..7e310c5364 --- /dev/null +++ b/qpid/dotnet/Qpid.Sasl/MD5HMAC.cs @@ -0,0 +1,115 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +using System; +using System.Security.Cryptography; + +namespace Apache.Qpid.Sasl +{ + /// <summary> + /// Rough HMAC MD5 implementation as presented in + /// RFC 2104. Used because the HMACMD5 class in the + /// .NET framework is not available in v1.1. + /// </summary> + public sealed class MD5HMAC : IDisposable + { + private const int BLOCK_LEN = 64; + private MD5 _hash; + private byte[] _key; + private byte[] _ipad; + private byte[] _opad; + + public MD5HMAC(byte[] key) + { + if ( key == null || key.Length == 0 ) + throw new ArgumentNullException("key"); + + _hash = new MD5CryptoServiceProvider(); + + byte[] theKey = key; + if ( theKey.Length > BLOCK_LEN ) + { + theKey = _hash.ComputeHash(theKey); + } + // pad key with 0's up to BLOCK_LEN + _key = new byte[BLOCK_LEN]; + Array.Copy(theKey, _key, theKey.Length); + + CreatePads(); + } + + public byte[] ComputeHash(byte[] input) + { + // H(K XOR opad, H(K XOR ipad, text)) + return H(_opad, H(_ipad, input)); + } + + public void Dispose() + { + if ( _hash != null ) + { + ((IDisposable)_hash).Dispose(); + _hash = null; + } + } + + #region Private Methods + // + // Private Methods + // + + private void CreatePads() + { + _ipad = new byte[BLOCK_LEN]; + _opad = new byte[BLOCK_LEN]; + for ( int i = 0; i < BLOCK_LEN; i++ ) + { + _ipad[i] = 0x36; + _opad[i] = 0x5c; + } + + XOR(_ipad, _key); + XOR(_opad, _key); + } + + private static void XOR(byte[] dest, byte[] other) + { + // assume both are same size + for ( int i = 0; i < dest.Length; i++ ) + { + dest[i] ^= other[i]; + } + } + + private byte[] H(byte[] v1, byte[] v2) + { + byte[] total = new byte[v1.Length + v2.Length]; + Array.Copy(v1, total, v1.Length); + Array.Copy(v2, 0, total, v1.Length, v2.Length); + + return _hash.ComputeHash(total); + } + + #endregion // Private Methods + + } // class MD5HMAC + +} // namespace Apache.Qpid.Sasl diff --git a/qpid/dotnet/Qpid.Sasl/Mechanisms/AnonymousSaslClient.cs b/qpid/dotnet/Qpid.Sasl/Mechanisms/AnonymousSaslClient.cs new file mode 100644 index 0000000000..e550d10d97 --- /dev/null +++ b/qpid/dotnet/Qpid.Sasl/Mechanisms/AnonymousSaslClient.cs @@ -0,0 +1,69 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +using System; +using System.Collections; +using System.Text; + +namespace Apache.Qpid.Sasl.Mechanisms +{ + /// <summary> + /// Implements the ANONYMOUS authentication mechanism + /// as outlined in RFC 2245 + /// </summary> + public class AnonymousSaslClient : SaslClient + { + public const string Mechanism = "ANONYMOUS"; + + public AnonymousSaslClient( + string authid, IDictionary properties, + ISaslCallbackHandler handler) + : base(authid, null, null, properties, handler) + { + } + + #region ISaslClient Implementation + // + // ISaslClient Implementation + // + + public override string MechanismName + { + get { return Mechanism; } + } + + public override bool HasInitialResponse + { + get { return true; } + } + + public override byte[] EvaluateChallenge(byte[] challenge) + { + // ignore challenge + SetComplete(); + return Encoding.UTF8.GetBytes(AuthorizationId); + } + + #endregion // ISaslClient Implementation + + } // class AnonymousSaslClient + +} // namespace Apache.Qpid.Sasl.Mechanisms diff --git a/qpid/dotnet/Qpid.Sasl/Mechanisms/CramMD5HexSaslClient.cs b/qpid/dotnet/Qpid.Sasl/Mechanisms/CramMD5HexSaslClient.cs new file mode 100644 index 0000000000..3cce0e3a2d --- /dev/null +++ b/qpid/dotnet/Qpid.Sasl/Mechanisms/CramMD5HexSaslClient.cs @@ -0,0 +1,93 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +using System; +using System.Collections; +using System.Security.Cryptography; +using System.Text; + +namespace Apache.Qpid.Sasl.Mechanisms +{ + /// <summary> + /// Implements the CRAM-MD5 authentication mechanism as outlined + /// in RFC 2195 + /// </summary> + public class CramMD5HexSaslClient : SaslClient + { + public const string Mechanism = "CRAM-MD5-HEX"; + private const int MinPwdLen = 16; + + public CramMD5HexSaslClient( + string authorizationId, + IDictionary properties, + ISaslCallbackHandler handler) + : base(authorizationId, null, null, properties, handler) + { + } + + #region ISaslClient Implementation + // + // ISaslClient Implementation + // + + public override string MechanismName + { + get { return Mechanism; } + } + + public override bool HasInitialResponse + { + get { return false; } + } + + + public override byte[] EvaluateChallenge(byte[] challenge) + { + if ( challenge == null || challenge.Length == 0 ) + throw new ArgumentNullException("challenge"); + + + NameCallback nameCB = new NameCallback(AuthorizationId); + PasswordCallback pwdCB = new PasswordCallback(); + ISaslCallback[] callbacks = { nameCB, pwdCB }; + Handler.Handle(callbacks); + + string username = nameCB.Text; + + //Encode the Hashed Password as Hex + byte[] passwd = Encoding.UTF8.GetBytes(ToHex(pwdCB.HashedText)); + + string s = System.Text.UTF8Encoding.UTF8.GetString(challenge); + + using ( HMAC hmac = new HMACMD5(passwd) ) + { + byte[] value = hmac.ComputeHash(challenge); + string encoded = ToHex(value); + SetComplete(); + return Encoding.UTF8.GetBytes(username + " " + encoded); + } + } + + #endregion // ISaslClient Implementation + + } // class CramMD5HashedSaslClient + +} // namespace Apache.Qpid.Sasl.Mechanisms diff --git a/qpid/dotnet/Qpid.Sasl/Mechanisms/CramMD5SaslClient.cs b/qpid/dotnet/Qpid.Sasl/Mechanisms/CramMD5SaslClient.cs new file mode 100644 index 0000000000..56b0f6ecd4 --- /dev/null +++ b/qpid/dotnet/Qpid.Sasl/Mechanisms/CramMD5SaslClient.cs @@ -0,0 +1,91 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +using System; +using System.Collections; +using System.Security.Cryptography; +using System.Text; + +namespace Apache.Qpid.Sasl.Mechanisms +{ + /// <summary> + /// Implements the CRAM-MD5 authentication mechanism as outlined + /// in RFC 2195 + /// </summary> + public class CramMD5SaslClient : SaslClient + { + public const string Mechanism = "CRAM-MD5"; + private const int MinPwdLen = 16; + + public CramMD5SaslClient( + string authorizationId, + IDictionary properties, + ISaslCallbackHandler handler) + : base(authorizationId, null, null, properties, handler) + { + } + + #region ISaslClient Implementation + // + // ISaslClient Implementation + // + + public override string MechanismName + { + get { return Mechanism; } + } + + public override bool HasInitialResponse + { + get { return false; } + } + + public override byte[] EvaluateChallenge(byte[] challenge) + { + if ( challenge == null || challenge.Length == 0 ) + throw new ArgumentNullException("challenge"); + + NameCallback nameCB = new NameCallback(AuthorizationId); + PasswordCallback pwdCB = new PasswordCallback(); + ISaslCallback[] callbacks = { nameCB, pwdCB }; + Handler.Handle(callbacks); + + string username = nameCB.Text; + string passwd = pwdCB.Text.PadRight(MinPwdLen, '\0'); + + byte[] secret = Encoding.UTF8.GetBytes(passwd); + + //using ( HMAC hmac = new HMACMD5(secret) ) + using ( MD5HMAC hmac = new MD5HMAC(secret) ) + { + byte[] value = hmac.ComputeHash(challenge); + string encoded = ToHex(value); + SetComplete(); + return Encoding.UTF8.GetBytes(username + " " + encoded); + } + + } + + #endregion // ISaslClient Implementation + + } // class CramMD5SaslClient + +} // namespace Apache.Qpid.Sasl.Mechanisms diff --git a/qpid/dotnet/Qpid.Sasl/Mechanisms/DigestSaslClient.cs b/qpid/dotnet/Qpid.Sasl/Mechanisms/DigestSaslClient.cs new file mode 100644 index 0000000000..79843587c7 --- /dev/null +++ b/qpid/dotnet/Qpid.Sasl/Mechanisms/DigestSaslClient.cs @@ -0,0 +1,576 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +using System; +using System.Collections; +using System.Collections.Specialized; +using System.Globalization; +using System.Security.Cryptography; +using System.Text; + +namespace Apache.Qpid.Sasl.Mechanisms +{ + + /// <summary> + /// Implements the DIGEST MD5 authentication mechanism + /// as outlined in RFC 2831 + /// </summary> + public class DigestSaslClient : SaslClient + { + public const string Mechanism = "DIGEST-MD5"; + private static readonly MD5 _md5 = new MD5CryptoServiceProvider(); + private int _state; + private string _cnonce; + private Encoding _encoding = Encoding.UTF8; + + public string Cnonce + { + get { return _cnonce; } + set { _cnonce = value; } + } + + public DigestSaslClient( + string authid, string serverName, string protocol, + IDictionary properties, ISaslCallbackHandler handler) + : base(authid, serverName, protocol, properties, handler) + { + _cnonce = Guid.NewGuid().ToString("N"); + } + + #region ISaslClient Implementation + // + // ISaslClient Implementation + // + + public override string MechanismName + { + get { return Mechanism; } + } + + public override bool HasInitialResponse + { + get { return false; } + } + + public override byte[] EvaluateChallenge(byte[] challenge) + { + if ( challenge == null || challenge.Length <= 0 ) + throw new ArgumentNullException("challenge"); + + switch ( _state++ ) + { + case 0: return OnInitialChallenge(challenge); + case 1: return OnFinalResponse(challenge); + } + throw new SaslException("Invalid State for authentication"); + } + + #endregion // ISaslClient Implementation + + + #region Private Methods + // + // Private Methods + // + + /// <summary> + /// Process the first challenge from the server + /// and calculate a response + /// </summary> + /// <param name="challenge">The server issued challenge</param> + /// <returns>Client response</returns> + private byte[] OnInitialChallenge(byte[] challenge) + { + DigestChallenge dch = + DigestChallenge.Parse(_encoding.GetString(challenge)); + // validate input challenge + if ( dch.Nonce == null || dch.Nonce.Length == 0 ) + throw new SaslException("Nonce value missing in server challenge"); + if ( dch.Algorithm != "md5-sess" ) + throw new SaslException("Invalid or missing algorithm value in server challenge"); + + + NameCallback nameCB = new NameCallback(AuthorizationId); + PasswordCallback pwdCB = new PasswordCallback(); + RealmCallback realmCB = new RealmCallback(dch.Realm); + ISaslCallback[] callbacks = { nameCB, pwdCB, realmCB }; + Handler.Handle(callbacks); + + DigestResponse response = new DigestResponse(); + response.Username = nameCB.Text; + response.Realm = realmCB.Text; + response.Nonce = dch.Nonce; + response.Cnonce = Cnonce; + response.NonceCount = 1; + response.Qop = DigestQop.Auth; // only auth supported for now + response.DigestUri = Protocol.ToLower() + "/" + ServerName; + response.MaxBuffer = dch.MaxBuffer; + response.Charset = dch.Charset; + response.Cipher = null; // not supported for now + response.Authzid = AuthorizationId; + response.AuthParam = dch.AuthParam; + + response.Response = CalculateResponse( + nameCB.Text, realmCB.Text, pwdCB.Text, + dch.Nonce, response.NonceCount, response.Qop, response.DigestUri + ); + + return _encoding.GetBytes(response.ToString()); + } + + /// <summary> + /// Process the second server challenge + /// </summary> + /// <param name="challenge">Server issued challenge</param> + /// <returns>The client response</returns> + private byte[] OnFinalResponse(byte[] challenge) + { + DigestChallenge dch = + DigestChallenge.Parse(_encoding.GetString(challenge)); + + if ( dch.Rspauth == null || dch.Rspauth.Length == 0 ) + throw new SaslException("Expected 'rspauth' in server challenge not found"); + + SetComplete(); + return new byte[0]; + } + + + + /// <summary> + /// Calculate the response field of the client response + /// </summary> + /// <param name="username">The user name</param> + /// <param name="realm">The realm</param> + /// <param name="passwd">The user's password</param> + /// <param name="nonce">Server nonce value</param> + /// <param name="nc">Client nonce count (always 1)</param> + /// <param name="qop">Quality of Protection</param> + /// <param name="digestUri">Digest-URI</param> + /// <returns>The value for the response field</returns> + private string CalculateResponse( + string username, string realm, string passwd, + string nonce, int nc, string qop, string digestUri + ) + { + string a1 = CalcHexA1(username, realm, passwd, nonce); + string a2 = CalcHexA2(digestUri, qop); + + string ncs = nc.ToString("x8", CultureInfo.InvariantCulture); + StringBuilder prekd = new StringBuilder(); + prekd.Append(a1).Append(':').Append(nonce).Append(':') + .Append(ncs).Append(':').Append(Cnonce) + .Append(':').Append(qop).Append(':').Append(a2); + + return ToHex(CalcH(_encoding.GetBytes(prekd.ToString()))); + } + + private string CalcHexA1( + string username, string realm, + string passwd, string nonce + ) + { + bool hasAuthId = AuthorizationId != null && AuthorizationId.Length > 0; + + string premd = username + ":" + realm + ":" + passwd; + byte[] temp1 = CalcH(_encoding.GetBytes(premd)); + + + int a1len = 16 + 1 + nonce.Length + 1 + Cnonce.Length; + if ( hasAuthId ) + a1len += 1 + AuthorizationId.Length; + + byte[] buffer = new byte[a1len]; + Array.Copy(temp1, buffer, temp1.Length); + + string p2 = ":" + nonce + ":" + Cnonce; + if ( hasAuthId ) + p2 += ":" + AuthorizationId; + + byte[] temp2 = _encoding.GetBytes(p2); + Array.Copy(temp2, 0, buffer, 16, temp2.Length); + + return ToHex(CalcH(buffer)); + } + + private string CalcHexA2(string digestUri, string qop) + { + string a2 = "AUTHENTICATE:" + digestUri; + if ( qop != DigestQop.Auth ) + a2 += ":00000000000000000000000000000000"; + return ToHex(CalcH(_encoding.GetBytes(a2))); + } + + private static byte[] CalcH(byte[] value) + { + return _md5.ComputeHash(value); + } + + #endregion // Private Methods + + + } // class DigestSaslClient + + + /// <summary> + /// Available QOP options in the DIGEST scheme + /// </summary> + public sealed class DigestQop + { + public const string Auth = "auth"; + public const string AuthInt = "auth-int"; + public const string AuthConf = "auth-conf"; + } // class DigestQop + + + /// <summary> + /// Represents and parses a digest server challenge + /// </summary> + public class DigestChallenge + { + private string _realm = "localhost"; + private string _nonce; + private string[] _qopOptions = { DigestQop.Auth }; + private bool _stale; + private int _maxBuffer = 65536; + private string _charset = "ISO 8859-1"; + private string _algorithm; + private string[] _cipherOptions; + private string _authParam; + private string _rspauth; + + #region Properties + // + // Properties + // + + public string Realm + { + get { return _realm; } + } + + public string Nonce + { + get { return _nonce; } + } + + public string[] QopOptions + { + get { return _qopOptions; } + } + + public bool Stale + { + get { return _stale; } + } + + public int MaxBuffer + { + get { return _maxBuffer; } + set { _maxBuffer = value; } + } + + public string Charset + { + get { return _charset; } + } + + public string Algorithm + { + get { return _algorithm; } + } + + public string[] CipherOptions + { + get { return _cipherOptions; } + } + + public string AuthParam + { + get { return _authParam; } + } + + public string Rspauth + { + get { return _rspauth; } + } + + #endregion // Properties + + public static DigestChallenge Parse(string challenge) + { + DigestChallenge parsed = new DigestChallenge(); + StringDictionary parts = ParseParameters(challenge); + foreach ( string optname in parts.Keys ) + { + switch ( optname ) + { + case "realm": + parsed._realm = parts[optname]; + break; + case "nonce": + parsed._nonce = parts[optname]; + break; + case "qop-options": + parsed._qopOptions = GetOptions(parts[optname]); + break; + case "cipher-opts": + parsed._cipherOptions = GetOptions(parts[optname]); + break; + case "stale": + parsed._stale = Convert.ToBoolean(parts[optname], CultureInfo.InvariantCulture); + break; + case "maxbuf": + parsed._maxBuffer = Convert.ToInt32(parts[optname], CultureInfo.InvariantCulture); + break; + case "charset": + parsed._charset = parts[optname]; + break; + case "algorithm": + parsed._algorithm = parts[optname]; + break; + case "auth-param": + parsed._authParam = parts[optname]; + break; + case "rspauth": + parsed._rspauth = parts[optname]; + break; + } + } + + return parsed; + } + + + public static StringDictionary ParseParameters(string source) + { + if ( source == null ) + throw new ArgumentNullException("source"); + + StringDictionary ret = new StringDictionary(); + + string remaining = source.Trim(); + while ( remaining.Length > 0 ) + { + int equals = remaining.IndexOf('='); + if ( equals < 0 ) + break; + + string optname = remaining.Substring(0, equals).Trim(); + remaining = remaining.Substring(equals + 1); + + string value = ParseQuoted(ref remaining); + ret[optname] = value.Trim(); + } + return ret; + } + + private static string ParseQuoted(ref string str) + { + string ns = str.TrimStart(); + + int start = 0; + bool quoted = ns[0] == '\"'; + if ( quoted ) start++; + bool inquotes = quoted; + bool escaped = false; + + int pos = start; + for ( ; pos < ns.Length; pos++ ) + { + if ( !inquotes && ns[pos] == ',' ) + break; + + // at end of quotes? + if ( quoted && !escaped && ns[pos] == '\"' ) + inquotes = false; + // is this char an escape for the next one? + escaped = inquotes && ns[pos] == '\\'; + } + // pos has end of string + string value = ns.Substring(start, pos-start).Trim(); + if ( quoted ) + { + // remove trailing quote + value = value.Substring(0, value.Length - 1); + } + str = ns.Substring(pos < ns.Length-1 ? pos+1 : pos); + return value; + } + + private static string[] GetOptions(string value) + { + return value.Split(' '); + } + + } // class DigestChallenge + + + /// <summary> + /// Represents and knows how to write a + /// digest client response + /// </summary> + public class DigestResponse + { + private string _username; + private string _realm; + private string _nonce; + private string _cnonce; + private int _nonceCount; + private string _qop; + private string _digestUri; + private string _response; + private int _maxBuffer; + private string _charset; + private string _cipher; + private string _authzid; + private string _authParam; + + #region Properties + // + // Properties + // + + public string Username + { + get { return _username; } + set { _username = value; } + } + + public string Realm + { + get { return _realm; } + set { _realm = value; } + } + + public string Nonce + { + get { return _nonce; } + set { _nonce = value; } + } + + public string Cnonce + { + get { return _cnonce; } + set { _cnonce = value; } + } + + public int NonceCount + { + get { return _nonceCount; } + set { _nonceCount = value; } + } + + public string Qop + { + get { return _qop; } + set { _qop = value; } + } + + public string DigestUri + { + get { return _digestUri; } + set { _digestUri = value; } + } + + public string Response + { + get { return _response; } + set { _response = value; } + } + + public int MaxBuffer + { + get { return _maxBuffer; } + set { _maxBuffer = value; } + } + + public string Charset + { + get { return _charset; } + set { _charset = value; } + } + + public string Cipher + { + get { return _cipher; } + set { _cipher = value; } + } + + public string Authzid + { + get { return _authzid; } + set { _authzid = value; } + } + + public string AuthParam + { + get { return _authParam; } + set { _authParam = value; } + } + + #endregion // Properties + + + public override string ToString() + { + StringBuilder buffer = new StringBuilder(); + Pair(buffer, "username", Username, true); + Pair(buffer, "realm", Realm, true); + Pair(buffer, "nonce", Nonce, true); + Pair(buffer, "cnonce", Cnonce, true); + string nc = NonceCount.ToString("x8", CultureInfo.InvariantCulture); + Pair(buffer, "nc", nc, false); + Pair(buffer, "qop", Qop, false); + Pair(buffer, "digest-uri", DigestUri, true); + Pair(buffer, "response", Response, true); + string maxBuffer = MaxBuffer.ToString(CultureInfo.InvariantCulture); + Pair(buffer, "maxbuf", maxBuffer, false); + Pair(buffer, "charset", Charset, false); + Pair(buffer, "cipher", Cipher, false); + Pair(buffer, "authzid", Authzid, true); + Pair(buffer, "auth-param", AuthParam, true); + + return buffer.ToString().TrimEnd(','); + } + + private static void Pair(StringBuilder buffer, string name, string value, bool quoted) + { + if ( value != null && value.Length > 0 ) + { + buffer.Append(name); + buffer.Append('='); + if ( quoted ) + { + buffer.Append('\"'); + buffer.Append(value.Replace("\"", "\\\"")); + buffer.Append('\"'); + } else + { + buffer.Append(value); + } + buffer.Append(','); + } + } + + } // class DigestResponse + +} // namespace Apache.Qpid.Sasl.Mechanisms diff --git a/qpid/dotnet/Qpid.Sasl/Mechanisms/ExternalSaslClient.cs b/qpid/dotnet/Qpid.Sasl/Mechanisms/ExternalSaslClient.cs new file mode 100644 index 0000000000..fec0d2d3c2 --- /dev/null +++ b/qpid/dotnet/Qpid.Sasl/Mechanisms/ExternalSaslClient.cs @@ -0,0 +1,69 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +using System; +using System.Collections; +using System.Text; + +namespace Apache.Qpid.Sasl.Mechanisms +{ + /// <summary> + /// Implements the EXTERNAL authentication mechanism + /// as outlined in RFC 2222 + /// </summary> + public class ExternalSaslClient : SaslClient + { + public const string Mechanism = "EXTERNAL"; + + public ExternalSaslClient( + string authid, IDictionary properties, + ISaslCallbackHandler handler) + : base(authid, null, null, properties, handler) + { + } + + #region ISaslClient Implementation + // + // ISaslClient Implementation + // + + public override string MechanismName + { + get { return Mechanism; } + } + + public override bool HasInitialResponse + { + get { return true; } + } + + public override byte[] EvaluateChallenge(byte[] challenge) + { + // ignore challenge + SetComplete(); + return Encoding.UTF8.GetBytes(AuthorizationId); + } + + #endregion // ISaslClient Implementation + + } // class ExternalSaslClient + +} // namespace Apache.Qpid.Sasl.Mechanisms diff --git a/qpid/dotnet/Qpid.Sasl/Mechanisms/PlainSaslClient.cs b/qpid/dotnet/Qpid.Sasl/Mechanisms/PlainSaslClient.cs new file mode 100644 index 0000000000..534be171b7 --- /dev/null +++ b/qpid/dotnet/Qpid.Sasl/Mechanisms/PlainSaslClient.cs @@ -0,0 +1,81 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +using System; +using System.Collections; +using System.Text; + +namespace Apache.Qpid.Sasl.Mechanisms +{ + + /// <summary> + /// Implements the PLAIN authentication mechanism + /// as outlined in RFC 4616 + /// </summary> + public class PlainSaslClient : SaslClient + { + public const string Mechanism = "PLAIN"; + + public PlainSaslClient( + string authid, IDictionary properties, + ISaslCallbackHandler handler) + : base(authid, null, null, properties, handler) + { + } + + #region ISaslClient Implementation + // + // ISaslClient Implementation + // + + public override string MechanismName + { + get { return Mechanism; } + } + + public override bool HasInitialResponse + { + get { return true; } + } + + public override byte[] EvaluateChallenge(byte[] challenge) + { + // ignore challenge + + NameCallback nameCB = new NameCallback(); + PasswordCallback pwdCB = new PasswordCallback(); + ISaslCallback[] callbacks = { nameCB, pwdCB }; + Handler.Handle(callbacks); + + string username = nameCB.Text; + string authid = AuthorizationId; + string passwd = pwdCB.Text; + + string response = + string.Format("{0}\0{1}\0{2}", authid, username, passwd); + SetComplete(); + return Encoding.UTF8.GetBytes(response); + } + + #endregion // ISaslClient Implementation + + } // class PlainSaslClient +} // namespace Apache.Qpid.Sasl.Mechanisms diff --git a/qpid/dotnet/Qpid.Sasl/Properties/AssemblyInfo.cs b/qpid/dotnet/Qpid.Sasl/Properties/AssemblyInfo.cs new file mode 100644 index 0000000000..5245b97d1f --- /dev/null +++ b/qpid/dotnet/Qpid.Sasl/Properties/AssemblyInfo.cs @@ -0,0 +1,57 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +using System; +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("Apache.Qpid.Sasl")] +[assembly: AssemblyDescription("Built from svn revision number: ")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("Apache Software Foundation")] +[assembly: AssemblyProduct("Apache.Qpid.Sasl")] +[assembly: AssemblyCopyright("Apache Software Foundation")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("27ea23e4-6f84-4a54-8f1f-5725e6d767cc")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Revision and Build Numbers +// by using the '*' as shown below: +[assembly: AssemblyVersion("0.5.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] +[assembly: CLSCompliant(true)] diff --git a/qpid/dotnet/Qpid.Sasl/Qpid.Sasl.csproj b/qpid/dotnet/Qpid.Sasl/Qpid.Sasl.csproj new file mode 100644 index 0000000000..8c1d568aa3 --- /dev/null +++ b/qpid/dotnet/Qpid.Sasl/Qpid.Sasl.csproj @@ -0,0 +1,73 @@ +<!-- + + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you under the Apache License, Version 2.0 (the + "License"); you may not use this file except in compliance + with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, + software distributed under the License is distributed on an + "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + KIND, either express or implied. See the License for the + specific language governing permissions and limitations + under the License. + +--> + +<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="3.5"> + <PropertyGroup> + <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> + <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform> + <ProductVersion>8.0.50727</ProductVersion> + <SchemaVersion>2.0</SchemaVersion> + <ProjectGuid>{1465B0EE-6452-42A6-AB73-B2F9EABEEE75}</ProjectGuid> + <OutputType>Library</OutputType> + <AppDesignerFolder>Properties</AppDesignerFolder> + <RootNamespace>Apache.Qpid.Sasl</RootNamespace> + <AssemblyName>Apache.Qpid.Sasl</AssemblyName> + <FileUpgradeFlags> + </FileUpgradeFlags> + <OldToolsVersion>2.0</OldToolsVersion> + <UpgradeBackupLocation> + </UpgradeBackupLocation> + </PropertyGroup> + <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "> + <DebugSymbols>true</DebugSymbols> + <DebugType>full</DebugType> + <Optimize>false</Optimize> + <OutputPath>..\bin\net-2.0\debug\</OutputPath> + <DefineConstants>DEBUG;TRACE</DefineConstants> + <ErrorReport>prompt</ErrorReport> + <WarningLevel>4</WarningLevel> + <UseVSHostingProcess>true</UseVSHostingProcess> + </PropertyGroup> + <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' "> + <DebugType>pdbonly</DebugType> + <Optimize>true</Optimize> + <OutputPath>bin\Release\</OutputPath> + <DefineConstants>TRACE</DefineConstants> + <ErrorReport>prompt</ErrorReport> + <WarningLevel>4</WarningLevel> + </PropertyGroup> + <ItemGroup> + <Reference Include="System" /> + <Reference Include="System.Data" /> + <Reference Include="System.Xml" /> + </ItemGroup> + <ItemGroup> + <Compile Include="**\*.cs" /> + </ItemGroup> + <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" /> + <!-- To modify your build process, add your task inside one of the targets below and uncomment it. + Other similar extension points exist, see Microsoft.Common.targets. + <Target Name="BeforeBuild"> + </Target> + <Target Name="AfterBuild"> + </Target> + --> +</Project> diff --git a/qpid/dotnet/Qpid.Sasl/Sasl.cs b/qpid/dotnet/Qpid.Sasl/Sasl.cs new file mode 100644 index 0000000000..2f7bacb939 --- /dev/null +++ b/qpid/dotnet/Qpid.Sasl/Sasl.cs @@ -0,0 +1,115 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +using System; +using System.Collections; +using System.Configuration; +using System.Text; + +using Apache.Qpid.Sasl.Configuration; + +namespace Apache.Qpid.Sasl +{ + /// <summary> + /// Static class used to access the SASL functionality. + /// The core SASL mechanism is described in RFC 2222. + /// </summary> + /// <remarks> + /// Only client side mechanisms are implemented. + /// <para> + /// New client side factories can be added programatically using the + /// RegisterClientFactory method, or through the application + /// configuration file, like this: + /// </para> + /// <example><![CDATA[ + /// <configuration> + /// <configSections> + /// <section name="qpid.sasl" type="Apache.Qpid.Sasl.Configuration.SaslConfigurationSectionHandler, Apache.Qpid.Sasl"/> + /// </configSections> + /// + /// <qpid.sasl> + /// <clientFactories> + /// <add type="Apache.Qpid.Sasl.Tests.TestClientFactory, Apache.Qpid.Sasl.Tests"/> + /// </clientFactories> + /// </qpid.sasl> + /// </configuration> + /// ]]></example> + /// </remarks> + public sealed class Sasl + { + private static IList _clientFactories; + + + static Sasl() + { + SaslConfiguration config = SaslConfiguration.GetConfiguration(); + _clientFactories = config.ClientFactories; + } + private Sasl() + { + } + + public static ISaslClient CreateClient( + string[] mechanisms, string authorizationId, + string protocol, string serverName, + IDictionary props, ISaslCallbackHandler handler + ) + { + ISaslClientFactory factory = FindFactory(mechanisms, props); + if ( factory == null ) + return null; + + return factory.CreateClient ( + mechanisms, authorizationId, + protocol, serverName, props, handler + ); + } + + public static void RegisterClientFactory(ISaslClientFactory factory) + { + lock ( _clientFactories ) + { + _clientFactories.Add(factory); + } + } + + private static ISaslClientFactory FindFactory(string[] mechanisms, IDictionary props) + { + lock ( _clientFactories ) + { + foreach ( ISaslClientFactory factory in _clientFactories ) + { + string[] mechs = factory.GetSupportedMechanisms(props); + foreach ( string m1 in mechs ) + { + foreach (string m2 in mechanisms ) + { + if ( m1 == m2 ) + return factory; + } + } + } + return null; + } + } + } // class Sasl + +} // namespace Apache.Qpid.Sasl.Mechanisms diff --git a/qpid/dotnet/Qpid.Sasl/SaslClient.cs b/qpid/dotnet/Qpid.Sasl/SaslClient.cs new file mode 100644 index 0000000000..a22013181b --- /dev/null +++ b/qpid/dotnet/Qpid.Sasl/SaslClient.cs @@ -0,0 +1,145 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +using System; +using System.Collections; +using System.Globalization; +using System.Text; + +namespace Apache.Qpid.Sasl +{ + public abstract class SaslClient : ISaslClient + { + private bool _isComplete; + private IDictionary _properties; + private string _authorizationId; + private string _serverName; + private string _protocol; + private ISaslCallbackHandler _handler; + + protected string AuthorizationId + { + get { return _authorizationId; } + } + protected string ServerName + { + get { return _serverName; } + } + + protected string Protocol + { + get { return _protocol; } + } + + protected ISaslCallbackHandler Handler + { + get { return _handler; } + } + + protected IDictionary Properties + { + get { return _properties; } + } + + protected SaslClient( + string authid, string serverName, + string protocol, IDictionary properties, + ISaslCallbackHandler handler) + { + if ( properties == null ) + throw new ArgumentNullException("properties"); + if ( handler == null ) + throw new ArgumentNullException("handler"); + + _authorizationId = authid==null ? "" : authid; + _serverName = serverName; + _protocol = protocol; + _properties = properties; + _handler = handler; + + if ( _serverName == null || _serverName.Length == 0 ) + { + _serverName = System.Net.Dns.GetHostName(); + } + } + + + + + #region ISaslClient Implementation + // + // ISaslClient Implementation + // + + public abstract string MechanismName { get; } + + public abstract bool HasInitialResponse { get; } + + public bool IsComplete + { + get { return _isComplete; } + } + + public abstract byte[] EvaluateChallenge(byte[] challenge); + + public virtual object GetNegotiatedProperty(string propName) + { + return null; + } + + public virtual byte[] Unwrap(byte[] buffer, int offset, int length) + { + throw new NotImplementedException(); + } + + public virtual byte[] Wrap(byte[] buffer, int offset, int lenght) + { + throw new NotImplementedException(); + } + + #endregion // ISaslClient Implementation + + + #region Helper Methods + // + // Helper Methods + // + + protected void SetComplete() + { + _isComplete = true; + } + + protected static string ToHex(byte[] buffer) + { + StringBuilder builder = new StringBuilder(); + foreach ( byte b in buffer ) + { + builder.Append(b.ToString("x2", CultureInfo.InvariantCulture)); + } + return builder.ToString(); + } + + #endregion // Helper Methods + + } // class SaslClient + +} // namespace Apache.Qpid.Sasl.Mechanisms diff --git a/qpid/dotnet/Qpid.Sasl/SaslException.cs b/qpid/dotnet/Qpid.Sasl/SaslException.cs new file mode 100644 index 0000000000..d770ee63fd --- /dev/null +++ b/qpid/dotnet/Qpid.Sasl/SaslException.cs @@ -0,0 +1,56 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +using System; +using System.Collections; +using System.Runtime.Serialization; +using System.Text; + +namespace Apache.Qpid.Sasl +{ + /// <summary> + /// Reports an exception during the processing of an SASL + /// Operation. Only used for authentication-relared errors + /// </summary> + [Serializable] + public class SaslException : Exception + { + public SaslException() + { + } + + public SaslException(string message) + : base(message) + { + } + public SaslException(string message, Exception innerException) + : base(message, innerException) + { + } + + protected SaslException(SerializationInfo info, StreamingContext ctxt) + : base(info, ctxt) + { + } + + } // class SaslException + +} // namespace Apache.Qpid.Sasl.Mechanisms diff --git a/qpid/dotnet/Qpid.Sasl/SaslProperties.cs b/qpid/dotnet/Qpid.Sasl/SaslProperties.cs new file mode 100644 index 0000000000..f9ad1c68cd --- /dev/null +++ b/qpid/dotnet/Qpid.Sasl/SaslProperties.cs @@ -0,0 +1,42 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +using System; +using System.Collections; +using System.Text; + +namespace Apache.Qpid.Sasl +{ + public sealed class SaslProperties + { + public const string PolicyNoPlainText = "NOPLAINTEXT"; + public const string PolicyNoActive = "NOACTIVE"; + public const string PolicyNoDictionary = "NODICTIONARY"; + public const string PolicyNoAnonymous = "NOANONYMOUS"; + public const string PolicyForwardSecrecy = "FORWARD_SECRECY"; + public const string PolicyPassCredentials = "PASS_CREDENTIALS"; + + public const string Qop = "QOP"; + public const string Strength = "STRENGTH"; + + } // class SaslProperties + +} // namespace Apache.Qpid.Sasl.Mechanisms diff --git a/qpid/dotnet/Qpid.Sasl/default.build b/qpid/dotnet/Qpid.Sasl/default.build new file mode 100644 index 0000000000..57049ee2ee --- /dev/null +++ b/qpid/dotnet/Qpid.Sasl/default.build @@ -0,0 +1,45 @@ +<?xml version="1.0"?> +<!-- + + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you under the Apache License, Version 2.0 (the + "License"); you may not use this file except in compliance + with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, + software distributed under the License is distributed on an + "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + KIND, either express or implied. See the License for the + specific language governing permissions and limitations + under the License. + +--> + +<project name="Apache.Qpid.Sasl" default="build"> + <!-- + Properties that come from master build file + - build.dir: root directory for build + - build.debug: true if building debug release + - build.defines: variables to define during build + --> + + <target name="build"> + <csc target="library" + define="${build.defines}" + debug="${build.debug}" + output="${build.dir}/${project::get-name()}.dll"> + + <sources> + <include name="**/*.cs" /> + </sources> + <references> + </references> + </csc> + </target> +</project> + |