Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

CSHARP-603: implemented Sasl authentication support as well as suppor…

…t for new delegated authentication support in server 2.4.
  • Loading branch information...
commit 401a45f2d6cbb46171a2212945c4fab4bb60b0a7 1 parent e0f958e
@craiggwilson craiggwilson authored
Showing with 5,206 additions and 1,888 deletions.
  1. +6 −213 MongoDB.Driver/Communication/MongoConnection.cs
  2. +10 −33 MongoDB.Driver/Communication/MongoConnectionPool.cs
  3. +6 −23 MongoDB.Driver/Communication/MongoServerInstance.cs
  4. +112 −0 MongoDB.Driver/Communication/Security/Authenticator.cs
  5. +50 −0 MongoDB.Driver/Communication/Security/IAuthenticationMethod.cs
  6. +51 −0 MongoDB.Driver/Communication/Security/ISaslMechanism.cs
  7. +36 −0 MongoDB.Driver/Communication/Security/ISaslStep.cs
  8. +66 −0 MongoDB.Driver/Communication/Security/Mechanisms/CramMD5Mechanism.cs
  9. +68 −0 MongoDB.Driver/Communication/Security/Mechanisms/DigestMD5Mechanism.cs
  10. +148 −0 MongoDB.Driver/Communication/Security/Mechanisms/Gsasl/Gsasl.cs
  11. +104 −0 MongoDB.Driver/Communication/Security/Mechanisms/Gsasl/GsaslContext.cs
  12. +57 −0 MongoDB.Driver/Communication/Security/Mechanisms/Gsasl/GsaslException.cs
  13. +57 −0 MongoDB.Driver/Communication/Security/Mechanisms/Gsasl/GsaslProperty.cs
  14. +144 −0 MongoDB.Driver/Communication/Security/Mechanisms/Gsasl/GsaslSession.cs
  15. +67 −0 MongoDB.Driver/Communication/Security/Mechanisms/GsaslCramMD5Implementation.cs
  16. +67 −0 MongoDB.Driver/Communication/Security/Mechanisms/GsaslGssapiImplementation.cs
  17. +144 −0 MongoDB.Driver/Communication/Security/Mechanisms/GsaslImplementationBase.cs
  18. +78 −0 MongoDB.Driver/Communication/Security/Mechanisms/GssapiMechanism.cs
  19. +79 −0 MongoDB.Driver/Communication/Security/Mechanisms/ManagedCramMD5Implementation.cs
  20. +334 −0 MongoDB.Driver/Communication/Security/Mechanisms/ManagedDigestMD5Implementation.cs
  21. +55 −0 MongoDB.Driver/Communication/Security/Mechanisms/SaslImplementationBase.cs
  22. +95 −0 MongoDB.Driver/Communication/Security/Mechanisms/Sspi/AuthIdentity.cs
  23. +32 −0 MongoDB.Driver/Communication/Security/Mechanisms/Sspi/AuthIdentityFlag.cs
  24. +36 −0 MongoDB.Driver/Communication/Security/Mechanisms/Sspi/DataRepresentation.cs
  25. +32 −0 MongoDB.Driver/Communication/Security/Mechanisms/Sspi/EncryptQualityOfProtection.cs
  26. +32 −0 MongoDB.Driver/Communication/Security/Mechanisms/Sspi/QueryContextAttribute.cs
  27. +94 −0 MongoDB.Driver/Communication/Security/Mechanisms/Sspi/SecurityBuffer.cs
  28. +175 −0 MongoDB.Driver/Communication/Security/Mechanisms/Sspi/SecurityBufferDescriptor.cs
  29. +48 −0 MongoDB.Driver/Communication/Security/Mechanisms/Sspi/SecurityBufferType.cs
  30. +371 −0 MongoDB.Driver/Communication/Security/Mechanisms/Sspi/SecurityContext.cs
  31. +31 −0 MongoDB.Driver/Communication/Security/Mechanisms/Sspi/SecurityCredentialUse.cs
  32. +124 −0 MongoDB.Driver/Communication/Security/Mechanisms/Sspi/SecurityCredentials.cs
  33. +34 −0 MongoDB.Driver/Communication/Security/Mechanisms/Sspi/SecurityPackageContextSizes.cs
  34. +44 −0 MongoDB.Driver/Communication/Security/Mechanisms/Sspi/SspiContextFlags.cs
  35. +68 −0 MongoDB.Driver/Communication/Security/Mechanisms/Sspi/SspiHandle.cs
  36. +28 −0 MongoDB.Driver/Communication/Security/Mechanisms/Sspi/SspiPackage.cs
  37. +321 −0 MongoDB.Driver/Communication/Security/Mechanisms/Sspi/Win32.cs
  38. +57 −0 MongoDB.Driver/Communication/Security/Mechanisms/Sspi/Win32Exception.cs
  39. +210 −0 MongoDB.Driver/Communication/Security/Mechanisms/WindowsGssapiImplementation.cs
  40. +84 −0 MongoDB.Driver/Communication/Security/MongoCRAuthenticationMethod.cs
  41. +56 −0 MongoDB.Driver/Communication/Security/MongoSecurityException.cs
  42. +114 −0 MongoDB.Driver/Communication/Security/SaslAuthenticationMethod.cs
  43. +67 −0 MongoDB.Driver/Communication/Security/SaslCompletionStep.cs
  44. +87 −0 MongoDB.Driver/Communication/Security/SaslConversation.cs
  45. +32 −0 MongoDB.Driver/MongoAuthenticationProtocol.cs
  46. +21 −28 MongoDB.Driver/MongoClientSettings.cs
  47. +3 −3 MongoDB.Driver/MongoCollection.cs
  48. +44 −0 MongoDB.Driver/MongoConnectionStringBuilder.cs
  49. +156 −52 MongoDB.Driver/MongoCredentials.cs
  50. +43 −62 MongoDB.Driver/MongoCredentialsStore.cs
  51. +3 −3 MongoDB.Driver/MongoCursorEnumerator.cs
  52. +48 −0 MongoDB.Driver/MongoDB.Driver.csproj
  53. +2 −60 MongoDB.Driver/MongoDatabase.cs
  54. +2 −64 MongoDB.Driver/MongoDatabaseSettings.cs
  55. +45 −0 MongoDB.Driver/MongoExternalIdentity.cs
  56. +137 −0 MongoDB.Driver/MongoIdentity.cs
  57. +84 −0 MongoDB.Driver/MongoIdentityEvidence.cs
  58. +37 −0 MongoDB.Driver/MongoInternalIdentity.cs
  59. +13 −137 MongoDB.Driver/MongoServer.cs
  60. +25 −63 MongoDB.Driver/MongoServerSettings.cs
  61. +40 −10 MongoDB.Driver/MongoUrl.cs
  62. +99 −29 MongoDB.Driver/MongoUrlBuilder.cs
  63. +13 −6 MongoDB.Driver/MongoUser.cs
  64. +165 −0 MongoDB.Driver/PasswordEvidence.cs
  65. +57 −0 MongoDB.Driver/ProcessEvidence.cs
  66. +6 −6 MongoDB.DriverUnitTests/Jira/CSharp346Tests.cs
  67. +13 −24 MongoDB.DriverUnitTests/MongoClientSettingsTests.cs
  68. +35 −0 MongoDB.DriverUnitTests/MongoConnectionStringBuilderTests.cs
  69. +9 −30 MongoDB.DriverUnitTests/MongoCredentialTests.cs
  70. +0 −26 MongoDB.DriverUnitTests/MongoDatabaseSettingsTests.cs
  71. +1 −1  MongoDB.DriverUnitTests/MongoDatabaseTests.cs
  72. +19 −52 MongoDB.DriverUnitTests/MongoServerSettingsTests.cs
  73. +0 −1  MongoDB.DriverUnitTests/MongoServerTests.cs
  74. +59 −17 MongoDB.DriverUnitTests/MongoUrlBuilderTests.cs
  75. +10 −939 MongoDB.DriverUnitTests/MongoUrlTests.cs
  76. +6 −6 MongoDB.DriverUnitTests/MongoUserTests.cs
View
219 MongoDB.Driver/Communication/MongoConnection.cs
@@ -14,7 +14,6 @@
*/
using System;
-using System.Collections.Generic;
using System.IO;
using System.Net.Security;
using System.Net.Sockets;
@@ -22,6 +21,8 @@
using MongoDB.Bson;
using MongoDB.Bson.IO;
using MongoDB.Bson.Serialization;
+using MongoDB.Driver.Communication;
+using MongoDB.Driver.Communication.Security;
namespace MongoDB.Driver.Internal
{
@@ -61,16 +62,13 @@ public class MongoConnection
private DateTime _lastUsedAt; // set every time the connection is Released
private int _messageCounter;
private int _requestId;
- private Dictionary<string, Authentication> _authentications = new Dictionary<string, Authentication>();
// constructors
internal MongoConnection(MongoConnectionPool connectionPool)
+ : this(connectionPool.ServerInstance)
{
- _serverInstance = connectionPool.ServerInstance;
_connectionPool = connectionPool;
_generationId = connectionPool.GenerationId;
- _createdAt = DateTime.UtcNow;
- _state = MongoConnectionState.Initial;
}
internal MongoConnection(MongoServerInstance serverInstance)
@@ -147,138 +145,6 @@ public MongoConnectionState State
}
// internal methods
- internal void Authenticate(string databaseName, MongoCredentials credentials)
- {
- if (_state == MongoConnectionState.Closed) { throw new InvalidOperationException("Connection is closed."); }
- lock (_connectionLock)
- {
- var nonceCommand = new CommandDocument("getnonce", 1);
- var commandResult = RunCommand(databaseName, QueryFlags.None, nonceCommand, false);
- if (!commandResult.Ok)
- {
- throw new MongoAuthenticationException(
- "Error getting nonce for authentication.",
- new MongoCommandException(commandResult));
- }
-
- var nonce = commandResult.Response["nonce"].AsString;
- var passwordDigest = MongoUtils.Hash(credentials.Username + ":mongo:" + credentials.Password);
- var digest = MongoUtils.Hash(nonce + credentials.Username + passwordDigest);
- var authenticateCommand = new CommandDocument
- {
- { "authenticate", 1 },
- { "user", credentials.Username },
- { "nonce", nonce },
- { "key", digest }
- };
-
- commandResult = RunCommand(databaseName, QueryFlags.None, authenticateCommand, false);
- if (!commandResult.Ok)
- {
- var message = string.Format("Invalid credentials for database '{0}'.", databaseName);
- throw new MongoAuthenticationException(
- message,
- new MongoCommandException(commandResult));
- }
-
- var authentication = new Authentication(credentials);
- _authentications.Add(databaseName, authentication);
- }
- }
-
- // check whether the connection can be used with the given database (and credentials)
- // the following are the only valid authentication states for a connection:
- // 1. the connection is not authenticated against any database
- // 2. the connection has a single authentication against the admin database (with a particular set of credentials)
- // 3. the connection has one or more authentications against any databases other than admin
- // (with the restriction that a particular database can only be authenticated against once and therefore with only one set of credentials)
-
- // assume that IsAuthenticated was called first and returned false
- internal bool CanAuthenticate(string databaseName, MongoCredentials credentials)
- {
- if (_state == MongoConnectionState.Closed) { throw new InvalidOperationException("Connection is closed."); }
- if (databaseName == null)
- {
- return true;
- }
-
- if (_authentications.Count == 0)
- {
- // a connection with no existing authentications can authenticate anything
- return true;
- }
- else
- {
- // a connection with existing authentications can't be used without credentials
- if (credentials == null)
- {
- return false;
- }
-
- // a connection with existing authentications can't be used with new admin credentials
- if (credentials.Admin)
- {
- return false;
- }
-
- // a connection with an existing authentication to the admin database can't be used with any other credentials
- if (_authentications.ContainsKey("admin"))
- {
- return false;
- }
-
- // a connection with an existing authentication to a database can't authenticate for the same database again
- if (_authentications.ContainsKey(databaseName))
- {
- return false;
- }
-
- return true;
- }
- }
-
- internal void CheckAuthentication(string databaseName, MongoCredentials credentials)
- {
- if (_state == MongoConnectionState.Closed) { throw new InvalidOperationException("Connection is closed."); }
- if (credentials == null)
- {
- if (_authentications.Count != 0)
- {
- throw new InvalidOperationException("Connection requires credentials.");
- }
- }
- else
- {
- var authenticationDatabaseName = credentials.Admin ? "admin" : databaseName;
- Authentication authentication;
- if (_authentications.TryGetValue(authenticationDatabaseName, out authentication))
- {
- if (authentication.Credentials != credentials)
- {
- // this shouldn't happen because a connection would have been chosen from the connection pool only if it was viable
- if (authenticationDatabaseName == "admin")
- {
- throw new MongoInternalException("Connection already authenticated to the admin database with different credentials.");
- }
- else
- {
- throw new MongoInternalException("Connection already authenticated to the database with different credentials.");
- }
- }
- authentication.LastUsed = DateTime.UtcNow;
- }
- else
- {
- if (authenticationDatabaseName == "admin" && _authentications.Count != 0)
- {
- // this shouldn't happen because a connection would have been chosen from the connection pool only if it was viable
- throw new MongoInternalException("The connection cannot be authenticated against the admin database because it is already authenticated against other databases.");
- }
- Authenticate(authenticationDatabaseName, credentials);
- }
- }
- }
-
internal void Close()
{
lock (_connectionLock)
@@ -305,36 +171,6 @@ internal void Close()
}
}
- internal bool IsAuthenticated(string databaseName, MongoCredentials credentials)
- {
- if (_state == MongoConnectionState.Closed) { throw new InvalidOperationException("Connection is closed."); }
- if (databaseName == null)
- {
- return true;
- }
-
- lock (_connectionLock)
- {
- if (credentials == null)
- {
- return _authentications.Count == 0;
- }
- else
- {
- var authenticationDatabaseName = credentials.Admin ? "admin" : databaseName;
- Authentication authentication;
- if (_authentications.TryGetValue(authenticationDatabaseName, out authentication))
- {
- return credentials == authentication.Credentials;
- }
- else
- {
- return false;
- }
- }
- }
- }
-
internal bool IsExpired()
{
var now = DateTime.UtcNow;
@@ -342,24 +178,6 @@ internal bool IsExpired()
|| now > _lastUsedAt + _serverInstance.Settings.MaxConnectionIdleTime;
}
- internal void Logout(string databaseName)
- {
- if (_state == MongoConnectionState.Closed) { throw new InvalidOperationException("Connection is closed."); }
- lock (_connectionLock)
- {
- var logoutCommand = new CommandDocument("logout", 1);
- var commandResult = RunCommand(databaseName, QueryFlags.None, logoutCommand, false);
- if (!commandResult.Ok)
- {
- throw new MongoAuthenticationException(
- "Error logging off.",
- new MongoCommandException(commandResult));
- }
-
- _authentications.Remove(databaseName);
- }
- }
-
internal void Open()
{
if (_state != MongoConnectionState.Initial)
@@ -405,6 +223,9 @@ internal void Open()
_tcpClient = tcpClient;
_stream = stream;
_state = MongoConnectionState.Open;
+
+ new Authenticator(this, _serverInstance.Settings.CredentialsStore)
+ .Authenticate();
}
// this is a low level method that doesn't require a MongoServer
@@ -629,33 +450,5 @@ private HandleExceptionAction DetermineAction(Exception ex)
return HandleExceptionAction.CloseConnection; // this should always be the default action
}
-
- // private nested classes
- // keeps track of what credentials were used with a given database
- // and when that database was last used on this connection
- private class Authentication
- {
- // private fields
- private MongoCredentials _credentials;
- private DateTime _lastUsed;
-
- // constructors
- public Authentication(MongoCredentials credentials)
- {
- _credentials = credentials;
- _lastUsed = DateTime.UtcNow;
- }
-
- public MongoCredentials Credentials
- {
- get { return _credentials; }
- }
-
- public DateTime LastUsed
- {
- get { return _lastUsed; }
- set { _lastUsed = value; }
- }
- }
}
}
View
43 MongoDB.Driver/Communication/MongoConnectionPool.cs
@@ -16,6 +16,7 @@
using System;
using System.Collections.Generic;
using System.Threading;
+using MongoDB.Driver.Communication;
namespace MongoDB.Driver.Internal
{
@@ -86,12 +87,12 @@ public MongoServerInstance ServerInstance
}
// internal methods
- internal MongoConnection AcquireConnection(string databaseName, MongoCredentials credentials)
+ internal MongoConnection AcquireConnection()
{
- return AcquireConnection(databaseName, credentials, _defaultAcquireConnectionOptions);
+ return AcquireConnection(_defaultAcquireConnectionOptions);
}
- internal MongoConnection AcquireConnection(string databaseName, MongoCredentials credentials, AcquireConnectionOptions options)
+ internal MongoConnection AcquireConnection(AcquireConnectionOptions options)
{
MongoConnection connectionToClose = null;
try
@@ -112,39 +113,15 @@ internal MongoConnection AcquireConnection(string databaseName, MongoCredentials
{
if (_availableConnections.Count > 0)
{
- // first try to find the most recently used connection that is already authenticated for this database
- for (int i = _availableConnections.Count - 1; i >= 0; i--)
+ var connection = _availableConnections[_availableConnections.Count - 1];
+ if (connection.IsExpired())
{
- var connection = _availableConnections[i];
- if (connection.IsExpired())
- {
- _availableConnections.RemoveAt(i);
- connectionToClose = connection;
- return new MongoConnection(this);
- }
- else if (connection.IsAuthenticated(databaseName, credentials))
- {
- _availableConnections.RemoveAt(i);
- return connection;
- }
+ connectionToClose = connection;
+ connection = new MongoConnection(this);
}
- // otherwise find the most recently used connection that can be authenticated for this database
- for (int i = _availableConnections.Count - 1; i >= 0; i--)
- {
- var connection = _availableConnections[i];
- if (connection.CanAuthenticate(databaseName, credentials))
- {
- _availableConnections.RemoveAt(i);
- return connection;
- }
- }
-
- // otherwise replace the least recently used connection with a brand new one
- // if this happens a lot the connection pool size should be increased
- connectionToClose = _availableConnections[0];
- _availableConnections.RemoveAt(0);
- return new MongoConnection(this);
+ _availableConnections.RemoveAt(_availableConnections.Count - 1);
+ return connection;
}
// avoid waiting by creating a new connection if options allow it
View
29 MongoDB.Driver/Communication/MongoServerInstance.cs
@@ -335,7 +335,7 @@ public IPEndPoint GetIPEndPoint()
/// </summary>
public void Ping()
{
- var connection = _connectionPool.AcquireConnection(null, null, _stateVerificationAcquireConnectionOptions);
+ var connection = _connectionPool.AcquireConnection(_stateVerificationAcquireConnectionOptions);
try
{
Ping(connection);
@@ -351,7 +351,7 @@ public void Ping()
/// </summary>
public void VerifyState()
{
- var connection = _connectionPool.AcquireConnection(null, null, _stateVerificationAcquireConnectionOptions);
+ var connection = _connectionPool.AcquireConnection(_stateVerificationAcquireConnectionOptions);
try
{
try
@@ -375,13 +375,9 @@ public void VerifyState()
/// <summary>
/// Acquires the connection.
/// </summary>
- /// <param name="databaseName">Name of the database.</param>
- /// <param name="credentials">The credentials.</param>
/// <returns>A MongoConnection.</returns>
- /// <exception cref="System.InvalidOperationException"></exception>
- internal MongoConnection AcquireConnection(string databaseName, MongoCredentials credentials)
+ internal MongoConnection AcquireConnection()
{
- MongoConnection connection;
lock (_serverInstanceLock)
{
if (_state != MongoServerState.Connected)
@@ -391,20 +387,7 @@ internal MongoConnection AcquireConnection(string databaseName, MongoCredentials
}
}
- connection = _connectionPool.AcquireConnection(databaseName, credentials);
-
- try
- {
- connection.CheckAuthentication(databaseName, credentials); // will authenticate if necessary
- }
- catch (MongoAuthenticationException)
- {
- // don't let the connection go to waste just because authentication failed
- _connectionPool.ReleaseConnection(connection);
- throw;
- }
-
- return connection;
+ return _connectionPool.AcquireConnection();
}
/// <summary>
@@ -432,7 +415,7 @@ internal void Connect()
try
{
- var connection = _connectionPool.AcquireConnection(null, null);
+ var connection = _connectionPool.AcquireConnection();
try
{
Ping(connection);
@@ -694,7 +677,7 @@ private void StateVerificationTimerCallback()
_inStateVerification = true;
try
{
- var connection = _connectionPool.AcquireConnection(null, null, _stateVerificationAcquireConnectionOptions);
+ var connection = _connectionPool.AcquireConnection(_stateVerificationAcquireConnectionOptions);
try
{
Ping(connection);
View
112 MongoDB.Driver/Communication/Security/Authenticator.cs
@@ -0,0 +1,112 @@
+/* Copyright 2010-2013 10gen Inc.
+*
+* Licensed 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.Generic;
+using System.Linq;
+using System.Text;
+using MongoDB.Driver.Communication.Security.Mechanisms;
+using MongoDB.Driver.Internal;
+
+namespace MongoDB.Driver.Communication.Security
+{
+ /// <summary>
+ /// Authenticates credentials against MongoDB.
+ /// </summary>
+ internal class Authenticator
+ {
+ // private static fields
+ private static readonly List<IAuthenticationMethod> __clientSupportedMethods = new List<IAuthenticationMethod>
+ {
+ new SaslAuthenticationMethod(new GssapiMechanism()),
+ new SaslAuthenticationMethod(new CramMD5Mechanism()),
+ new SaslAuthenticationMethod(new DigestMD5Mechanism()),
+ new MongoCRAuthenticationMethod()
+ };
+
+ // private fields
+ private readonly MongoConnection _connection;
+ private readonly IEnumerable<MongoCredentials> _credentials;
+
+ // constructors
+ /// <summary>
+ /// Initializes a new instance of the <see cref="Authenticator" /> class.
+ /// </summary>
+ /// <param name="connection">The connection.</param>
+ /// <param name="credentials">The credentials.</param>
+ public Authenticator(MongoConnection connection, IEnumerable<MongoCredentials> credentials)
+ {
+ _connection = connection;
+ _credentials = credentials;
+ }
+
+ // public methods
+ /// <summary>
+ /// Authenticates the specified connection.
+ /// </summary>
+ public void Authenticate()
+ {
+ if (!_credentials.Any())
+ {
+ return;
+ }
+
+ var serverSupportedMethods = GetServerSupportedMethods();
+ foreach (var credential in _credentials)
+ {
+ Authenticate(credential, serverSupportedMethods);
+ }
+ }
+
+ // private methods
+ private void Authenticate(MongoCredentials credentials, List<string> serverSupportedMethods)
+ {
+ foreach (var clientSupportedMethod in __clientSupportedMethods)
+ {
+ if (serverSupportedMethods.Contains(clientSupportedMethod.Name) && clientSupportedMethod.CanUse(credentials))
+ {
+ clientSupportedMethod.Authenticate(_connection, credentials);
+ return;
+ }
+ }
+
+ var message = string.Format("Unable to negotiate a protocol to authenticate. Credentials for source {0}, username {1} over protocol {2} could not be authenticated", credentials.Source, credentials.Username, credentials.AuthenticationProtocol);
+ throw new MongoSecurityException(message);
+ }
+
+ private List<string> GetServerSupportedMethods()
+ {
+ var command = new CommandDocument
+ {
+ { "saslStart", 1 },
+ { "mechanism", ""}, // forces a response that contains a list of supported mechanisms...
+ { "payload", new byte[0] }
+ };
+
+ var list = new List<string>();
+ var result = _connection.RunCommand("admin", QueryFlags.SlaveOk, command, false);
+ if (result.Response.Contains("supportedMechanisms"))
+ {
+ list.AddRange(result.Response["supportedMechanisms"].AsBsonArray.Select(x => x.AsString));
+ }
+
+ // because MONGO-CR is last in the list, we don't need to check if the server supports it...
+ // in the future, we may need to add a check.
+ list.Add("MONGO-CR");
+ return list;
+ }
+ }
+
+}
View
50 MongoDB.Driver/Communication/Security/IAuthenticationMethod.cs
@@ -0,0 +1,50 @@
+/* Copyright 2010-2013 10gen Inc.
+*
+* Licensed 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.Generic;
+using System.Linq;
+using System.Text;
+using MongoDB.Driver.Internal;
+
+namespace MongoDB.Driver.Communication.Security
+{
+ /// <summary>
+ /// Authenticates a MongoConnection.
+ /// </summary>
+ internal interface IAuthenticationMethod
+ {
+ /// <summary>
+ /// Gets the name.
+ /// </summary>
+ string Name { get; }
+
+ /// <summary>
+ /// Authenticates the specified connection with the given credentials.
+ /// </summary>
+ /// <param name="connection">The connection.</param>
+ /// <param name="credentials">The credentials.</param>
+ void Authenticate(MongoConnection connection, MongoCredentials credentials);
+
+ /// <summary>
+ /// Determines whether this instance can use the specified credentials.
+ /// </summary>
+ /// <param name="credentials">The credentials.</param>
+ /// <returns>
+ /// <c>true</c> if this instance can use the specified credentials; otherwise, <c>false</c>.
+ /// </returns>
+ bool CanUse(MongoCredentials credentials);
+ }
+}
View
51 MongoDB.Driver/Communication/Security/ISaslMechanism.cs
@@ -0,0 +1,51 @@
+/* Copyright 2010-2013 10gen Inc.
+*
+* Licensed 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.Generic;
+using System.Linq;
+using System.Text;
+using MongoDB.Driver.Internal;
+
+namespace MongoDB.Driver.Communication.Security
+{
+ /// <summary>
+ /// Represents a sasl mechanism.
+ /// </summary>
+ internal interface ISaslMechanism
+ {
+ /// <summary>
+ /// Gets the name of the mechanism.
+ /// </summary>
+ string Name { get; }
+
+ /// <summary>
+ /// Determines whether this instance can authenticate with the specified credentials.
+ /// </summary>
+ /// <param name="credentials">The credentials.</param>
+ /// <returns>
+ /// <c>true</c> if this instance can authenticate with the specified credentials; otherwise, <c>false</c>.
+ /// </returns>
+ bool CanUse(MongoCredentials credentials);
+
+ /// <summary>
+ /// Initializes the mechanism.
+ /// </summary>
+ /// <param name="connection">The connection.</param>
+ /// <param name="credentials">The credentials.</param>
+ /// <returns>The initial step.</returns>
+ ISaslStep Initialize(MongoConnection connection, MongoCredentials credentials);
+ }
+}
View
36 MongoDB.Driver/Communication/Security/ISaslStep.cs
@@ -0,0 +1,36 @@
+/* Copyright 2010-2013 10gen Inc.
+*
+* Licensed 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.
+*/
+
+namespace MongoDB.Driver.Communication.Security
+{
+ /// <summary>
+ /// A step in a Sasl Conversation.
+ /// </summary>
+ internal interface ISaslStep
+ {
+ /// <summary>
+ /// The bytes that should be sent to ther server before calling Transition.
+ /// </summary>
+ byte[] BytesToSendToServer { get; }
+
+ /// <summary>
+ /// Transitions to the next step in the conversation.
+ /// </summary>
+ /// <param name="conversation">The conversation.</param>
+ /// <param name="bytesReceivedFromServer">The bytes received from the server.</param>
+ /// <returns>An ISaslStep.</returns>
+ ISaslStep Transition(SaslConversation conversation, byte[] bytesReceivedFromServer);
+ }
+}
View
66 MongoDB.Driver/Communication/Security/Mechanisms/CramMD5Mechanism.cs
@@ -0,0 +1,66 @@
+/* Copyright 2010-2013 10gen Inc.
+*
+* Licensed 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.Generic;
+using System.Linq;
+using System.Security.Cryptography;
+using System.Text;
+using MongoDB.Driver.Internal;
+
+namespace MongoDB.Driver.Communication.Security.Mechanisms
+{
+ /// <summary>
+ /// A mechanism for CRAM-MD5.
+ /// </summary>
+ internal class CramMD5Mechanism : ISaslMechanism
+ {
+ // public properties
+ /// <summary>
+ /// Gets the name of the mechanism.
+ /// </summary>
+ public string Name
+ {
+ get { return "CRAM-MD5"; }
+ }
+
+ // public methods
+ /// <summary>
+ /// Determines whether this instance can authenticate with the specified credentials.
+ /// </summary>
+ /// <param name="credentials">The credentials.</param>
+ /// <returns>
+ /// <c>true</c> if this instance can authenticate with the specified credentials; otherwise, <c>false</c>.
+ /// </returns>
+ /// <exception cref="System.NotImplementedException"></exception>
+ public bool CanUse(MongoCredentials credentials)
+ {
+ return credentials.AuthenticationProtocol == MongoAuthenticationProtocol.Strongest &&
+ credentials.Evidence is PasswordEvidence;
+ }
+
+ /// <summary>
+ /// Initializes the mechanism.
+ /// </summary>
+ /// <param name="connection">The connection.</param>
+ /// <param name="credentials">The credentials.</param>
+ /// <returns>The initial step.</returns>
+ public ISaslStep Initialize(MongoConnection connection, MongoCredentials credentials)
+ {
+ return new ManagedCramMD5Implementation(credentials.Username, ((PasswordEvidence)credentials.Evidence).Password);
+ //return new GsaslCramMD5Implementation(identity);
+ }
+ }
+}
View
68 MongoDB.Driver/Communication/Security/Mechanisms/DigestMD5Mechanism.cs
@@ -0,0 +1,68 @@
+/* Copyright 2010-2013 10gen Inc.
+*
+* Licensed 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.Generic;
+using System.IO;
+using System.Linq;
+using System.Security.Cryptography;
+using System.Text;
+
+namespace MongoDB.Driver.Communication.Security.Mechanisms
+{
+ /// <summary>
+ /// A mechanism for DIGEST-MD5.
+ /// </summary>
+ internal class DigestMD5Mechanism : ISaslMechanism
+ {
+ // public properties
+ /// <summary>
+ /// Gets the name of the mechanism.
+ /// </summary>
+ public string Name
+ {
+ get { return "DIGEST-MD5"; }
+ }
+
+ // public methods
+ /// <summary>
+ /// Determines whether this instance can authenticate with the specified credentials.
+ /// </summary>
+ /// <param name="credentials">The credentials.</param>
+ /// <returns>
+ /// <c>true</c> if this instance can authenticate with the specified credentials; otherwise, <c>false</c>.
+ /// </returns>
+ /// <exception cref="System.NotImplementedException"></exception>
+ public bool CanUse(MongoCredentials credentials)
+ {
+ return credentials.AuthenticationProtocol == MongoAuthenticationProtocol.Strongest &&
+ credentials.Evidence is PasswordEvidence;
+ }
+
+ /// <summary>
+ /// Initializes the mechanism.
+ /// </summary>
+ /// <param name="connection">The connection.</param>
+ /// <param name="credentials">The credentials.</param>
+ /// <returns>The initial step.</returns>
+ public ISaslStep Initialize(Internal.MongoConnection connection, MongoCredentials credentials)
+ {
+ return new ManagedDigestMD5Implementation(
+ connection.ServerInstance.Address.Host,
+ credentials.Username,
+ ((PasswordEvidence)credentials.Evidence).Password);
+ }
+ }
+}
View
148 MongoDB.Driver/Communication/Security/Mechanisms/Gsasl/Gsasl.cs
@@ -0,0 +1,148 @@
+/* Copyright 2010-2013 10gen Inc.
+*
+* Licensed 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.Runtime.ConstrainedExecution;
+using System.Runtime.InteropServices;
+using System.Security;
+
+namespace MongoDB.Driver.Communication.Security.Mechanisms.Gsasl
+{
+ internal static class Gsasl
+ {
+ // public constants
+ public const int GSASL_OK = 0;
+ public const int GSASL_NEEDS_MORE = 1;
+ public const int GSASL_UNKNOWN_MECHANISM = 2;
+ public const int GSASL_MECHANISM_CALLED_TOO_MANY_TIMES = 3;
+ public const int GSASL_MALLOC_ERROR = 7;
+ public const int GSASL_BASE64_ERROR = 8;
+ public const int GSASL_CRYPTO_ERROR = 9;
+ public const int GSASL_SASLPREP_ERROR = 29;
+ public const int GSASL_MECHANISM_PARSE_ERROR = 30;
+ public const int GSASL_AUTHENTICATION_ERROR = 31;
+ public const int GSASL_INTEGRITY_ERROR = 33;
+ public const int GSASL_NO_CLIENT_CODE = 35;
+ public const int GSASL_NO_SERVER_CODE = 36;
+ public const int GSASL_NO_CALLBACK = 51;
+ public const int GSASL_NO_ANONYMOUS_TOKEN = 52;
+ public const int GSASL_NO_AUTHID = 53;
+ public const int GSASL_NO_AUTHZID = 54;
+ public const int GSASL_NO_PASSWORD = 55;
+ public const int GSASL_NO_PASSCODE = 56;
+ public const int GSASL_NO_PIN = 57;
+ public const int GSASL_NO_SERVICE = 58;
+ public const int GSASL_NO_HOSTNAME = 59;
+ public const int GSASL_NO_CB_TLS_UNIQUE = 65;
+ public const int GSASL_NO_SAML20_IDP_IDENTIFIER = 66;
+ public const int GSASL_NO_SAML20_REDIRECT_URL = 67;
+ public const int GSASL_NO_OPENID20_REDIRECT_URL = 68;
+
+ // public static methods
+ /// <summary>
+ /// Gets the description of an error code.
+ /// </summary>
+ /// <param name="error">The error.</param>
+ /// <returns>The description.</returns>
+ public static string GetError(int error)
+ {
+ var messagePtr = gsasl_strerror(error);
+ return Marshal.PtrToStringAnsi(messagePtr);
+ }
+
+ /// <summary>
+ /// Begins a GsaslSession.
+ /// </summary>
+ /// <param name="context">The context.</param>
+ /// <param name="mechanism">The mechanism.</param>
+ /// <param name="session">The session.</param>
+ /// <returns>A result code.</returns>
+ [DllImport("libgsasl-7.dll", CharSet = CharSet.Ansi)]
+ public static extern int gsasl_client_start(
+ GsaslContext context,
+ [MarshalAs(UnmanagedType.LPStr)]string mechanism,
+ out GsaslSession session);
+
+ /// <summary>
+ /// Frees a GsaslContext.
+ /// </summary>
+ /// <param name="context">The context.</param>
+ [DllImport("libgsasl-7.dll", CharSet = CharSet.Ansi)]
+ [SuppressUnmanagedCodeSecurity]
+ [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
+ public static extern void gsasl_done(IntPtr context);
+
+ /// <summary>
+ /// Frees a GsaslSession.
+ /// </summary>
+ /// <param name="session">The session.</param>
+ [DllImport("libgsasl-7.dll", CharSet = CharSet.Ansi)]
+ [SuppressUnmanagedCodeSecurity]
+ [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
+ public static extern void gsasl_finish (IntPtr session);
+
+ /// <summary>
+ /// Frees memory allocated by libgsasl.
+ /// </summary>
+ /// <param name="ptr">The PTR.</param>
+ [DllImport("libgsasl-7.dll", CharSet = CharSet.Ansi)]
+ public static extern void gsasl_free(IntPtr ptr);
+
+ /// <summary>
+ /// Initiates a GsaslContext.
+ /// </summary>
+ /// <param name="context">The context.</param>
+ /// <returns></returns>
+ [DllImport("libgsasl-7.dll", CharSet = CharSet.Ansi)]
+ public static extern int gsasl_init(out GsaslContext context);
+
+ /// <summary>
+ /// Sets a property on a GsaslSession.
+ /// </summary>
+ /// <param name="session">The session.</param>
+ /// <param name="prop">The prop.</param>
+ /// <param name="value">The value.</param>
+ [DllImport("libgsasl-7.dll", CharSet = CharSet.Ansi)]
+ public static extern void gsasl_property_set(
+ GsaslSession session,
+ GsaslProperty prop,
+ [MarshalAs(UnmanagedType.LPStr)]string value);
+
+ /// <summary>
+ /// Steps through the state machine.
+ /// </summary>
+ /// <param name="session">The session.</param>
+ /// <param name="input">The input.</param>
+ /// <param name="input_len">The input_len.</param>
+ /// <param name="output">The output.</param>
+ /// <param name="output_len">The output_len.</param>
+ /// <returns></returns>
+ [DllImport("libgsasl-7.dll", CharSet = CharSet.Ansi)]
+ public static extern int gsasl_step(
+ GsaslSession session,
+ IntPtr input,
+ int input_len,
+ out IntPtr output,
+ out int output_len);
+
+ /// <summary>
+ /// Gets a description for the error code.
+ /// </summary>
+ /// <param name="err">The err.</param>
+ /// <returns>A string describing the error.</returns>
+ [DllImport("libgsasl-7.dll", CharSet = CharSet.Ansi)]
+ public static extern IntPtr gsasl_strerror(int err);
+ }
+}
View
104 MongoDB.Driver/Communication/Security/Mechanisms/Gsasl/GsaslContext.cs
@@ -0,0 +1,104 @@
+/* Copyright 2010-2013 10gen Inc.
+*
+* Licensed 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.Runtime.InteropServices;
+
+namespace MongoDB.Driver.Communication.Security.Mechanisms.Gsasl
+{
+ /// <summary>
+ /// A handle to a gsasl context.
+ /// </summary>
+ internal class GsaslContext : SafeHandle
+ {
+ // constructors
+ /// <summary>
+ /// Initializes a new instance of the <see cref="GsaslContext" /> class.
+ /// </summary>
+ public GsaslContext()
+ : base(IntPtr.Zero, true)
+ {
+ }
+
+ // public static methods
+ /// <summary>
+ /// Initializes this instance.
+ /// </summary>
+ /// <returns></returns>
+ /// <exception cref="System.Exception">Unable to initialize context.</exception>
+ public static GsaslContext Initialize()
+ {
+ GsaslContext context;
+ var rc = Gsasl.gsasl_init(out context);
+ if (rc != Gsasl.GSASL_OK)
+ {
+ var message = Gsasl.GetError(rc);
+ throw new GsaslException(rc, message);
+ }
+
+ return context;
+ }
+
+ // public properties
+ /// <summary>
+ /// When overridden in a derived class, gets a value indicating whether the handle value is invalid.
+ /// </summary>
+ /// <returns>true if the handle value is invalid; otherwise, false.</returns>
+ /// <PermissionSet>
+ /// <IPermission class="System.Security.Permissions.SecurityPermission, mscorlib, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Flags="UnmanagedCode" />
+ /// </PermissionSet>
+ public override bool IsInvalid
+ {
+ get { return base.IsClosed || handle == IntPtr.Zero; }
+ }
+
+ // public methods
+ /// <summary>
+ /// Begins the session.
+ /// </summary>
+ /// <param name="mechanism">The mechanism.</param>
+ /// <returns>A GsaslSession.</returns>
+ /// <exception cref="System.Exception">Unable to being session.</exception>
+ public GsaslSession BeginSession(string mechanism)
+ {
+ GsaslSession session;
+ var rc = Gsasl.gsasl_client_start(
+ this,
+ mechanism,
+ out session);
+
+ if (rc != Gsasl.GSASL_OK)
+ {
+ var message = Gsasl.GetError(rc);
+ throw new GsaslException(rc, message);
+ }
+
+ return session;
+ }
+
+ // protected methods
+ /// <summary>
+ /// When overridden in a derived class, executes the code required to free the handle.
+ /// </summary>
+ /// <returns>
+ /// true if the handle is released successfully; otherwise, in the event of a catastrophic failure, false. In this case, it generates a releaseHandleFailed MDA Managed Debugging Assistant.
+ /// </returns>
+ protected override bool ReleaseHandle()
+ {
+ Gsasl.gsasl_done(handle);
+ return true;
+ }
+ }
+}
View
57 MongoDB.Driver/Communication/Security/Mechanisms/Gsasl/GsaslException.cs
@@ -0,0 +1,57 @@
+/* Copyright 2010-2013 10gen Inc.
+*
+* Licensed 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.Runtime.Serialization;
+
+namespace MongoDB.Driver.Communication.Security.Mechanisms.Gsasl
+{
+ /// <summary>
+ /// Thrown from a gsasl wrapped operation.
+ /// </summary>
+ [Serializable]
+ public class GsaslException : Exception
+ {
+ /// <summary>
+ /// Initializes a new instance of the <see cref="GsaslException" /> class.
+ /// </summary>
+ /// <param name="errorCode">The error code.</param>
+ public GsaslException(int errorCode)
+ {
+ HResult = errorCode;
+ }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="GsaslException" /> class.
+ /// </summary>
+ /// <param name="errorCode">The error code.</param>
+ /// <param name="message">The message.</param>
+ public GsaslException(int errorCode, string message)
+ : base(message)
+ {
+ HResult = errorCode;
+ }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="GsaslException" /> class.
+ /// </summary>
+ /// <param name="info">The info.</param>
+ /// <param name="context">The context.</param>
+ protected GsaslException(SerializationInfo info, StreamingContext context)
+ : base(info, context)
+ {
+ }
+ }
+}
View
57 MongoDB.Driver/Communication/Security/Mechanisms/Gsasl/GsaslProperty.cs
@@ -0,0 +1,57 @@
+/* Copyright 2010-2013 10gen Inc.
+*
+* Licensed 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.
+*/
+
+namespace MongoDB.Driver.Communication.Security.Mechanisms.Gsasl
+{
+ /// <summary>
+ /// Valid properties used in a GsaslSession.
+ /// </summary>
+ internal enum GsaslProperty
+ {
+ GSASL_AUTHID = 1,
+ GSASL_AUTHZID = 2,
+ GSASL_PASSWORD = 3,
+ GSASL_ANONYMOUS_TOKEN = 4,
+ GSASL_SERVICE = 5,
+ GSASL_HOSTNAME = 6,
+ GSASL_GSSAPI_DISPLAY_NAME = 7,
+ GSASL_PASSCODE = 8,
+ GSASL_SUGGESTED_PIN = 9,
+ GSASL_PIN = 10,
+ GSASL_REALM = 11,
+ GSASL_DIGEST_MD5_HASHED_PASSWORD = 12,
+ GSASL_QOPS = 13,
+ GSASL_QOP = 14,
+ GSASL_SCRAM_ITER = 15,
+ GSASL_SCRAM_SALT = 16,
+ GSASL_SCRAM_SALTED_PASSWORD = 17,
+ GSASL_CB_TLS_UNIQUE = 18,
+ GSASL_SAML20_IDP_IDENTIFIER = 19,
+ GSASL_SAML20_REDIRECT_URL = 20,
+ GSASL_OPENID20_REDIRECT_URL = 21,
+ GSASL_OPENID20_OUTCOME_DATA = 22,
+ /* Client callbacks. */
+ GSASL_SAML20_AUTHENTICATE_IN_BROWSER = 250,
+ GSASL_OPENID20_AUTHENTICATE_IN_BROWSER = 251,
+ /* Server validation callback properties. */
+ GSASL_VALIDATE_SIMPLE = 500,
+ GSASL_VALIDATE_EXTERNAL = 501,
+ GSASL_VALIDATE_ANONYMOUS = 502,
+ GSASL_VALIDATE_GSSAPI = 503,
+ GSASL_VALIDATE_SECURID = 504,
+ GSASL_VALIDATE_SAML20 = 505,
+ GSASL_VALIDATE_OPENID20 = 506
+ }
+}
View
144 MongoDB.Driver/Communication/Security/Mechanisms/Gsasl/GsaslSession.cs
@@ -0,0 +1,144 @@
+/* Copyright 2010-2013 10gen Inc.
+*
+* Licensed 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.Runtime.InteropServices;
+
+namespace MongoDB.Driver.Communication.Security.Mechanisms.Gsasl
+{
+ /// <summary>
+ /// A handle to a gsasl session.
+ /// </summary>
+ internal class GsaslSession : SafeHandle
+ {
+ // private fields
+ private bool _isComplete;
+
+ // constructors
+ /// <summary>
+ /// Initializes a new instance of the <see cref="GsaslSession" /> class.
+ /// </summary>
+ public GsaslSession()
+ : base(IntPtr.Zero, true)
+ {
+ }
+
+ // public properties
+ /// <summary>
+ /// Gets a value indicating whether this instance is complete.
+ /// </summary>
+ /// <value>
+ /// <c>true</c> if this instance is complete; otherwise, <c>false</c>.
+ /// </value>
+ public bool IsComplete
+ {
+ get { return _isComplete; }
+ }
+
+ /// <summary>
+ /// When overridden in a derived class, gets a value indicating whether the handle value is invalid.
+ /// </summary>
+ /// <returns>true if the handle value is invalid; otherwise, false.</returns>
+ /// <PermissionSet>
+ /// <IPermission class="System.Security.Permissions.SecurityPermission, mscorlib, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Flags="UnmanagedCode" />
+ /// </PermissionSet>
+ public override bool IsInvalid
+ {
+ get { return base.IsClosed || handle == IntPtr.Zero; }
+ }
+
+ // public methods
+ /// <summary>
+ /// Sets the property.
+ /// </summary>
+ /// <param name="name">The name.</param>
+ /// <param name="value">The value.</param>
+ /// <exception cref="MongoSecurityException"></exception>
+ public void SetProperty(string name, string value)
+ {
+ if(!name.StartsWith("GSASL_", StringComparison.InvariantCultureIgnoreCase))
+ {
+ name = "GSASL_" + name;
+ }
+
+ GsaslProperty prop;
+ try
+ {
+ prop = (GsaslProperty)Enum.Parse(typeof(GsaslProperty), name, true);
+ }
+ catch (ArgumentException)
+ {
+ throw new MongoSecurityException(string.Format("The name {0} is not a valid name.", name));
+ }
+
+ Gsasl.gsasl_property_set(this, prop, value);
+ }
+
+ /// <summary>
+ /// Steps the specified input.
+ /// </summary>
+ /// <param name="input">The input.</param>
+ /// <returns>The output bytes to be sent to the server.</returns>
+ /// <exception cref="System.Exception"></exception>
+ public byte[] Step(byte[] input)
+ {
+ IntPtr inputPtr = IntPtr.Zero;
+ IntPtr outputPtr = IntPtr.Zero;
+ int outputLength;
+ try
+ {
+ inputPtr = Marshal.AllocHGlobal(input.Length);
+ Marshal.Copy(input, 0, inputPtr, input.Length);
+
+ var rc = Gsasl.gsasl_step(this, inputPtr, input.Length, out outputPtr, out outputLength);
+ if (rc != Gsasl.GSASL_OK && rc != Gsasl.GSASL_NEEDS_MORE)
+ {
+ var message = Gsasl.GetError(rc);
+ throw new GsaslException(rc, message);
+ }
+
+ _isComplete = rc == Gsasl.GSASL_OK;
+
+ var output = new byte[outputLength];
+ Marshal.Copy(outputPtr, output, 0, output.Length);
+ return output;
+ }
+ finally
+ {
+ if (inputPtr != IntPtr.Zero)
+ {
+ Marshal.FreeHGlobal(inputPtr);
+ }
+ if (outputPtr != IntPtr.Zero)
+ {
+ Gsasl.gsasl_free(outputPtr);
+ }
+ }
+ }
+
+ // protected methods
+ /// <summary>
+ /// When overridden in a derived class, executes the code required to free the handle.
+ /// </summary>
+ /// <returns>
+ /// true if the handle is released successfully; otherwise, in the event of a catastrophic failure, false. In this case, it generates a releaseHandleFailed MDA Managed Debugging Assistant.
+ /// </returns>
+ protected override bool ReleaseHandle()
+ {
+ Gsasl.gsasl_finish(handle);
+ return true;
+ }
+ }
+}
View
67 MongoDB.Driver/Communication/Security/Mechanisms/GsaslCramMD5Implementation.cs
@@ -0,0 +1,67 @@
+/* Copyright 2010-2013 10gen Inc.
+*
+* Licensed 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.Generic;
+using System.Linq;
+using System.Security.Cryptography;
+using System.Text;
+
+namespace MongoDB.Driver.Communication.Security.Mechanisms
+{
+ /// <summary>
+ /// A mechanism implementing the CRAM-MD5 sasl specification.
+ /// </summary>
+ internal class GsaslCramMD5Implementation : GsaslImplementationBase
+ {
+ // private fields
+ private readonly string _username;
+ private readonly PasswordEvidence _password;
+
+ // constructors
+ /// <summary>
+ /// Initializes a new instance of the <see cref="GsaslCramMD5Implementation" /> class.
+ /// </summary>
+ /// <param name="username">The username.</param>
+ /// <param name="password">The password.</param>
+ public GsaslCramMD5Implementation(string username, PasswordEvidence password)
+ : base("CRAM-MD5", new byte[0])
+ {
+ _username = username;
+ _password = password;
+ }
+
+ // protected methods
+ /// <summary>
+ /// Gets the properties that should be used in the specified mechanism.
+ /// </summary>
+ /// <returns>The properties.</returns>
+ protected override IEnumerable<KeyValuePair<string, string>> GetProperties()
+ {
+ yield return new KeyValuePair<string, string>("AUTHID", _username);
+ yield return new KeyValuePair<string, string>("PASSWORD", CreatePassword());
+ }
+
+ // private methods
+ private string CreatePassword()
+ {
+ using(var md5 = MD5.Create())
+ {
+ var bytes = GetMongoPassword(md5, Encoding.UTF8, _username, _password.Password);
+ return ToHexString(bytes);
+ }
+ }
+ }
+}
View
67 MongoDB.Driver/Communication/Security/Mechanisms/GsaslGssapiImplementation.cs
@@ -0,0 +1,67 @@
+/* Copyright 2010-2013 10gen Inc.
+*
+* Licensed 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.Collections.Generic;
+
+namespace MongoDB.Driver.Communication.Security.Mechanisms
+{
+ /// <summary>
+ /// Implements the GssApi specification using the Gsasl library.
+ /// </summary>
+ internal class GsaslGssapiImplementation : GsaslImplementationBase
+ {
+ // private fields
+ private readonly string _authorizationId;
+ private readonly MongoIdentityEvidence _evidence;
+ private readonly string _servicePrincipalName;
+
+ // constructors
+ /// <summary>
+ /// Initializes a new instance of the <see cref="GsaslGssapiImplementation" /> class.
+ /// </summary>
+ /// <param name="serverName">Name of the server.</param>
+ /// <param name="username">The username.</param>
+ /// <param name="evidence">The evidence.</param>
+ public GsaslGssapiImplementation(string serverName, string username, MongoIdentityEvidence evidence)
+ : base("GSSAPI", new byte[0])
+ {
+ _authorizationId = username;
+ _evidence = evidence;
+ _servicePrincipalName = "mongodb/" + serverName;
+ }
+
+ // protected methods
+ /// <summary>
+ /// Gets the properties that should be used in the specified mechanism.
+ /// </summary>
+ /// <returns>The properties.</returns>
+ protected override IEnumerable<KeyValuePair<string, string>> GetProperties()
+ {
+ yield return new KeyValuePair<string, string>("AUTHZID", _authorizationId);
+ yield return new KeyValuePair<string, string>("AUTHID", _authorizationId);
+ if (_evidence is PasswordEvidence)
+ {
+ yield return new KeyValuePair<string, string>("PASSWORD", ((PasswordEvidence)_evidence).Password); // TODO: fix this to be secure
+ }
+ var atIndex = _authorizationId.LastIndexOf("@");
+ if (atIndex != -1 && atIndex != _authorizationId.Length - 1)
+ {
+ var realm = _authorizationId.Substring(atIndex + 1);
+ yield return new KeyValuePair<string, string>("REALM", realm);
+ }
+ yield return new KeyValuePair<string, string>("SERVICE", _servicePrincipalName);
+ }
+ }
+}
View
144 MongoDB.Driver/Communication/Security/Mechanisms/GsaslImplementationBase.cs
@@ -0,0 +1,144 @@
+/* Copyright 2010-2013 10gen Inc.
+*
+* Licensed 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.Collections.Generic;
+using MongoDB.Driver.Communication.Security.Mechanisms.Gsasl;
+
+namespace MongoDB.Driver.Communication.Security.Mechanisms
+{
+ /// <summary>
+ /// A base class for implementing a mechanism using Libgsasl.
+ /// </summary>
+ internal abstract class GsaslImplementationBase : SaslImplementationBase, ISaslStep
+ {
+ // private fields
+ private readonly string _name;
+ private readonly byte[] _output;
+
+ // constructors
+ /// <summary>
+ /// Initializes a new instance of the <see cref="GsaslImplementationBase" /> class.
+ /// </summary>
+ /// <param name="name">The name.</param>
+ /// <param name="output">The output.</param>
+ protected GsaslImplementationBase(string name, byte[] output)
+ {
+ _name = name;
+ _output = output;
+ }
+
+ // public properties
+ /// <summary>
+ /// The bytes that should be sent to ther server before calling Transition.
+ /// </summary>
+ public byte[] BytesToSendToServer
+ {
+ get { return _output; }
+ }
+
+ // public methods
+ /// <summary>
+ /// Transitions to the next step in the conversation.
+ /// </summary>
+ /// <param name="conversation">The conversation.</param>
+ /// <param name="input">The input.</param>
+ /// <returns>An ISaslStep.</returns>
+ /// <exception cref="MongoSecurityException">Unable to initialize context.</exception>
+ public ISaslStep Transition(SaslConversation conversation, byte[] input)
+ {
+ GsaslContext context;
+ try
+ {
+ context = GsaslContext.Initialize();
+ conversation.RegisterItemForDisposal(context);
+ }
+ catch (GsaslException ex)
+ {
+ throw new MongoSecurityException("Unable to initialize context.", ex);
+ }
+
+ GsaslSession session;
+ try
+ {
+ session = context.BeginSession(_name);
+ conversation.RegisterItemForDisposal(session);
+ }
+ catch (GsaslException ex)
+ {
+ throw new MongoSecurityException("Unable to start a session.", ex);
+ }
+
+ foreach (var property in GetProperties())
+ {
+ session.SetProperty(property.Key, property.Value);
+ }
+
+ return new GsaslAuthenticateStep(session, null)
+ .Transition(conversation, input);
+ }
+
+ // protected methods
+ /// <summary>
+ /// Gets the properties that should be used in the specified mechanism.
+ /// </summary>
+ /// <returns>The properties.</returns>
+ protected abstract IEnumerable<KeyValuePair<string, string>> GetProperties();
+
+ // nested classes
+ private class GsaslAuthenticateStep : ISaslStep
+ {
+ // private fields
+ private readonly byte[] _bytesToSendToServer;
+ private GsaslSession _session;
+
+ // constructors
+ public GsaslAuthenticateStep(GsaslSession session, byte[] bytesToSendToServer)
+ {
+ _session = session;
+ _bytesToSendToServer = bytesToSendToServer;
+ }
+
+ // public properties
+ public byte[] BytesToSendToServer
+ {
+ get { return _bytesToSendToServer; }
+ }
+
+ public bool IsComplete
+ {
+ get { return false; }
+ }
+
+ // public methods
+ public ISaslStep Transition(SaslConversation conversation, byte[] bytesReceivedFromServer)
+ {
+ try
+ {
+ var bytesToSendToServer = _session.Step(bytesReceivedFromServer);
+ if (_session.IsComplete)
+ {
+ return new SaslCompletionStep(bytesToSendToServer);
+ }
+
+ return new GsaslAuthenticateStep(_session, bytesToSendToServer);
+ }
+ catch (GsaslException ex)
+ {
+ throw new MongoSecurityException("Unable to authenticate.", ex);
+ }
+ }
+ }
+ }
+}
View
78 MongoDB.Driver/Communication/Security/Mechanisms/GssapiMechanism.cs
@@ -0,0 +1,78 @@
+/* Copyright 2010-2013 10gen Inc.
+*
+* Licensed 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.Generic;
+using System.Linq;
+using System.Text;
+using MongoDB.Driver.Internal;
+
+namespace MongoDB.Driver.Communication.Security.Mechanisms
+{
+ /// <summary>
+ /// A mechanism implementing the GSS API specification.
+ /// </summary>
+ internal class GssapiMechanism : ISaslMechanism
+ {
+ // public properties
+ /// <summary>
+ /// Gets the name of the mechanism.
+ /// </summary>
+ public string Name
+ {
+ get { return "GSSAPI"; }
+ }
+
+ // public methods
+ /// <summary>
+ /// Determines whether this instance can authenticate with the specified credentials.
+ /// </summary>
+ /// <param name="credentials">The credentials.</param>
+ /// <returns>
+ /// <c>true</c> if this instance can authenticate with the specified credentials; otherwise, <c>false</c>.
+ /// </returns>
+ /// <exception cref="System.NotImplementedException"></exception>
+ public bool CanUse(MongoCredentials credentials)
+ {
+ return credentials.AuthenticationProtocol == MongoAuthenticationProtocol.Gssapi &&
+ credentials.Identity is MongoExternalIdentity;
+ }
+
+ /// <summary>
+ /// Initializes the mechanism.
+ /// </summary>
+ /// <param name="connection">The connection.</param>
+ /// <param name="credentials">The credentials.</param>
+ /// <returns>The initial step.</returns>
+ public ISaslStep Initialize(MongoConnection connection, MongoCredentials credentials)
+ {
+ // TODO: provide an override to force the use of gsasl?
+ bool useGsasl = !Environment.OSVersion.Platform.ToString().Contains("Win");
+ if (useGsasl)
+ {
+ throw new NotImplementedException("Gssapi Support on Non-Windows Machinse is Not Implemented.");
+ //return new GsaslGssapiImplementation(
+ // connection.ServerInstance.Address.Host,
+ // credentials.Username,
+ // credentials.Evidence);
+ }
+
+ return new WindowsGssapiImplementation(
+ connection.ServerInstance.Address.Host,
+ credentials.Username,
+ credentials.Evidence);
+ }
+ }
+}
View
79 MongoDB.Driver/Communication/Security/Mechanisms/ManagedCramMD5Implementation.cs
@@ -0,0 +1,79 @@
+/* Copyright 2010-2013 10gen Inc.
+*
+* Licensed 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.Generic;
+using System.Linq;
+using System.Security.Cryptography;
+using System.Text;
+
+namespace MongoDB.Driver.Communication.Security.Mechanisms
+{
+ /// <summary>
+ /// Managed implementation of the CRAM-MD5 sasl spec (http://tools.ietf.org/html/draft-ietf-sasl-crammd5-10).
+ /// </summary>
+ internal class ManagedCramMD5Implementation : SaslImplementationBase, ISaslStep
+ {
+ // private fields
+ private readonly string _username;
+ private readonly string _password;
+
+ // constructors
+ /// <summary>
+ /// Initializes a new instance of the <see cref="ManagedCramMD5Implementation" /> class.
+ /// </summary>
+ /// <param name="username">The username.</param>
+ /// <param name="password">The password.</param>
+ public ManagedCramMD5Implementation(string username, string password)
+ {
+ _username = username;
+ _password = password;
+ }
+
+ // public methods
+ /// <summary>
+ /// The bytes that should be sent to ther server before calling Transition.
+ /// </summary>
+ public byte[] BytesToSendToServer
+ {
+ get { return new byte[0]; }
+ }
+
+ // public methods
+ public ISaslStep Transition(SaslConversation conversation, byte[] bytesReceivedFromServer)
+ {
+ var encoding = Encoding.UTF8;
+ var mongoPassword = _username + ":mongo:" + _password;
+ byte[] password;
+ using (var md5 = MD5.Create())
+ {
+ password = GetMongoPassword(md5, encoding, _username, _password);
+ var temp = ToHexString(password);
+ password = encoding.GetBytes(temp);
+ }
+
+ byte[] digest;
+ using (var hmacMd5 = new HMACMD5(password))
+ {
+ digest = hmacMd5.ComputeHash(bytesReceivedFromServer);
+ }
+
+ var response = _username + " " + ToHexString(digest);
+ var bytesToSendToServer = encoding.GetBytes(response);
+
+ return new SaslCompletionStep(bytesToSendToServer);
+ }
+ }
+}
View
334 MongoDB.Driver/Communication/Security/Mechanisms/ManagedDigestMD5Implementation.cs
@@ -0,0 +1,334 @@
+/* Copyright 2010-2013 10gen Inc.
+*
+* Licensed 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.Generic;
+using System.Linq;
+using System.Security.Cryptography;
+using System.Text;
+
+namespace MongoDB.Driver.Communication.Security.Mechanisms
+{
+ /// <summary>
+ /// A managed implementation of the DIGEST-MD5 sasl spec (http://www.ietf.org/rfc/rfc2831.txt).
+ /// </summary>
+ internal class ManagedDigestMD5Implementation : SaslImplementationBase, ISaslStep
+ {
+ // private fields
+ private readonly byte[] _cnonce;
+ private readonly string _digestUri;
+ private readonly string _nonceCount;
+ private readonly string _qop;
+ private readonly string _password;
+ private readonly string _username;
+
+ // constructors
+ /// <summary>
+ /// Initializes a new instance of the <see cref="ManagedDigestMD5Implementation" /> class.
+ /// </summary>
+ /// <param name="serverName">Name of the server.</param>
+ /// <param name="username">The username.</param>
+ /// <param name="password">The password.</param>
+ public ManagedDigestMD5Implementation(string serverName, string username, string password)
+ {
+ _cnonce = CreateClientNonce();
+ _digestUri = "mongodb/" + serverName;
+ _nonceCount = "00000001";
+ _qop = "auth";
+ _password = password;
+ _username = username;
+ }
+
+ // public properties
+ /// <summary>
+ /// The bytes that should be sent to ther server before calling Transition.
+ /// </summary>
+ public byte[] BytesToSendToServer
+ {
+ get { return new byte[0]; }
+ }
+
+ // public methods
+ /// <summary>
+ /// Transitions to the next step in the conversation.
+ /// </summary>
+ /// <param name="conversation">The conversation.</param>
+ /// <param name="bytesReceivedFromServer">The bytes received from the server.</param>
+ /// <returns>An ISaslStep.</returns>
+ public ISaslStep Transition(SaslConversation conversation, byte[] bytesReceivedFromServer)
+ {
+ var directives = DirectiveParser.Parse(bytesReceivedFromServer);
+ var encoding = Encoding.UTF8;
+
+ var sb = new StringBuilder();
+ sb.AppendFormat("username=\"{0}\"", _username);
+ sb.AppendFormat(",nonce=\"{0}\"", encoding.GetString(directives["nonce"]).Replace("\"", "\\\""));
+ sb.AppendFormat(",cnonce=\"{0}\"", encoding.GetString(_cnonce).Replace("\"", "\\\""));
+ sb.AppendFormat(",nc={0}", _nonceCount);
+ sb.AppendFormat(",qop={0}", _qop);
+ sb.AppendFormat(",digest-uri=\"{0}\"", _digestUri);
+ sb.AppendFormat(",response={0}", ComputeResponse(encoding, directives["nonce"]));
+ sb.Append(",charset=\"utf-8\"");
+
+ return new ManagedDigestMD5FinalStep(encoding.GetBytes(sb.ToString()));
+ }
+
+ // private methods
+ private string ComputeResponse(Encoding encoding, byte[] nonce)
+ {
+ using (var md5 = MD5.Create())
+ {
+ var a1 = ComputeA1(encoding, md5, nonce);
+ var a2 = ComputeA2(encoding);
+
+ var a1Hash = md5.ComputeHash(a1);
+ var a2Hash = md5.ComputeHash(a2);
+
+ var a1Hex = ToHexString(a1Hash);
+ var a2Hex = ToHexString(a2Hash);
+
+ var kd = new List<byte>();
+ kd.AddRange(encoding.GetBytes(a1Hex));
+ kd.Add((byte)':');
+ kd.AddRange(nonce);
+ kd.Add((byte)':');
+ kd.AddRange(encoding.GetBytes(_nonceCount));
+ kd.Add((byte)':');
+ kd.AddRange(_cnonce);
+ kd.Add((byte)':');
+ kd.AddRange(encoding.GetBytes(_qop));
+ kd.Add((byte)':');
+ kd.AddRange(encoding.GetBytes(a2Hex));
+
+ var kdHash = md5.ComputeHash(kd.ToArray());
+ return ToHexString(kdHash);
+ }
+ }
+
+ private byte[] ComputeA1(Encoding encoding, MD5 md5, byte[] nonce)
+ {
+ // User Token
+ var userToken = new List<byte>();
+ userToken.AddRange(encoding.GetBytes(_username));
+ userToken.Add((byte)':');
+ userToken.Add((byte)':');
+ var passwordBytes = GetMongoPassword(md5, encoding, _username, _password);
+ var passwordHex = ToHexString(passwordBytes);
+ userToken.AddRange(encoding.GetBytes(passwordHex));
+ var userTokenBytes = md5.ComputeHash(userToken.ToArray());
+
+ var nonceBytes = new List<byte>();
+ nonceBytes.Add((byte)':');
+ nonceBytes.AddRange(nonce);
+ nonceBytes.Add((byte)':');
+ nonceBytes.AddRange(_cnonce);
+
+ var result = new byte[userTokenBytes.Length + nonceBytes.Count];
+ userTokenBytes.CopyTo(result, 0);
+ nonceBytes.CopyTo(result, userTokenBytes.Length);
+
+ return result;
+ }
+
+ private byte[] ComputeA2(Encoding encoding)
+ {
+ return encoding.GetBytes("AUTHENTICATE:" + _digestUri);
+ }
+
+ private byte[] CreateClientNonce()
+ {
+ return Encoding.UTF8.GetBytes(new Random().Next(1234000, 99999999).ToString());
+ }
+
+ // nested classes
+ private class ManagedDigestMD5FinalStep : ISaslStep
+ {
+ private readonly byte[] _bytesToSendToServer;
+
+ public ManagedDigestMD5FinalStep(byte[] bytesToSendToServer)
+ {
+ _bytesToSendToServer = bytesToSendToServer;
+ }
+
+ public byte[] BytesToSendToServer
+ {
+ get { return _bytesToSendToServer; }
+ }
+
+ public ISaslStep Transition(SaslConversation conversation, byte[] bytesReceivedFromServer)
+ {
+ return new SaslCompletionStep(new byte[0]);
+ }
+ }
+
+ private static class DirectiveParser
+ {
+ public static Dictionary<string, byte[]> Parse(byte[] bytes)
+ {
+ var s = Encoding.UTF8.GetString(bytes);
+
+ var parsed = new Dictionary<string, byte[]>();
+ var index = 0;
+ while (index < bytes.Length)
+ {
+ var key = ParseKey(bytes, ref index);
+ SkipWhitespace(bytes, ref index);
+ if (bytes[index] != '=')
+ {
+ throw new MongoSecurityException(string.Format("Expected a '=' after a key \"{0}\"."));
+ }
+ else if (key.Length == 0)
+ {
+ throw new MongoSecurityException("Empty directive key.");
+ }
+ index++; // skip =
+
+ var value = ParseValue(key, bytes, ref index);
+ parsed.Add(key, value);
+ if (index >= bytes.Length)
+ {
+ break;
+ }
+ else if (bytes[index] != ',')
+ {
+ throw new MongoSecurityException(string.Format("Expected a ',' after directive \"{0}\".", key));
+ }
+ else
+ {
+ index++;
+ SkipWhitespace(bytes, ref index);
+ }
+ }
+
+ return parsed;
+ }
+
+ private static string ParseKey(byte[] bytes, ref int index)
+ {
+ var key = new StringBuilder();
+ while (index < bytes.Length)
+ {
+ var b = bytes[index];
+ if (b == ',')
+ {
+ if (key.Length == 0)
+ {
+ // there were some extra commas, so we skip over them
+ // and try to find the next key
+ index++;
+ SkipWhitespace(bytes, ref index);
+ }
+ else
+ {
+ throw new MongoSecurityException(string.Format("Directive key \"{0}\" contains a ','.", key.ToString()));
+ }
+ }
+ else if (b == '=')
+ {
+ break;
+ }
+ else if (IsWhiteSpace(b))
+ {
+ index++;
+ break;
+ }
+ else
+ {
+ index++;
+ key.Append((char)b);
+ }
+ }
+
+ return key.ToString();
+ }
+
+ private static byte[] ParseValue(string key, byte[] bytes, ref int index)
+ {
+ List<byte> value = new List<byte>();
+ bool isQuoted = false;
+ while (index < bytes.Length)
+ {
+ var b = bytes[index];
+ if (b == '\\')
+ {
+ index++; // skip escape
+ if (index < bytes.Length)
+ {
+ value.Add(b);
+ index++;
+ }
+ else
+ {
+ throw new MongoSecurityException(string.Format("Unmatched quote found in value of directive key \"{0}\".", key));
+ }
+ }
+ else if (b == '"')
+ {
+ index++;
+ if (isQuoted)
+ {
+ // we have closed the quote...
+ break;
+ }
+ else if (value.Count == 0)
+ {
+ isQuoted = true;
+ }
+ else
+ {
+ // quote in the middle of an unquoted string
+ value.Add(b);
+ }
+ }
+ else
+ {
+ if (b == ',' && !isQuoted)
+ {
+ break;
+ }
+ value.Add(b);
+ index++;
+ }
+ }
+
+ return value.ToArray();
+ }
+
+ private static bool IsWhiteSpace(byte b)
+ {
+ switch (b)
+ {
+ case 13: // US-ASCII CR, carriage return
+ case 10: // US-ASCII LF, linefeed
+ case 32: // US-ASCII SP, space
+ case 9: // US-ASCII HT, horizontal-tab
+ return true;
+ }
+ return false;
+ }
+
+ private static void SkipWhitespace(byte[] bytes, ref int index)
+ {
+ for (; index < bytes.Length; index++)
+ {
+ if (!IsWhiteSpace(bytes[index]))
+ {
+ return;
+ }
+ }
+ }
+ }
+ }
+}
View
55 MongoDB.Driver/Communication/Security/Mechanisms/SaslImplementationBase.cs
@@ -0,0 +1,55 @@
+/* Copyright 2010-2013 10gen Inc.
+*
+* Licensed 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.Generic;
+using System.Linq;
+using System.Security.Cryptography;
+using System.Text;
+using MongoDB.Bson;
+
+namespace MongoDB.Driver.Communication.Security.Mechanisms
+{
+ /// <summary>
+ /// Base implementation for a sasl step to provide some common methods.
+ /// </summary>
+ internal abstract class SaslImplementationBase
+ {
+ // protected methods
+ /// <summary>
+ /// Gets the mongo password.
+ /// </summary>
+ /// <param name="md5">The MD5.</param>
+ /// <param name="encoding">The encoding.</param>
+ /// <param name="username">The username.</param>
+ /// <param name="password">The password.</param>
+ /// <returns></returns>
+ protected byte[] GetMongoPassword(MD5 md5, Encoding encoding, string username, string password)
+ {
+ var mongoPassword = username + ":mongo:" + password;
+ return md5.ComputeHash(encoding.GetBytes(mongoPassword));
+ }
+
+ /// <summary>
+ /// To the hex string.
+ /// </summary>
+ /// <param name="bytes">The bytes.</param>
+ /// <returns></returns>
+ protected string ToHexString(byte[] bytes)
+ {
+ return BsonUtils.ToHexString(bytes);
+ }
+ }
+}
View
95 MongoDB.Driver/Communication/Security/Mechanisms/Sspi/AuthIdentity.cs
<
@@ -0,0 +1,95 @@
+/* Copyright 2010-2013 10gen Inc.
+*
+* Licensed 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.Runtime.InteropServices;
+using System.Security;
+
+namespace MongoDB.Driver.Communication.Security.Mechanisms.Sspi
+{
+ /// <summary>
+ /// SEC_WINNT_AUTH_IDENTITY
+ /// </summary>
+ [StructLayout(LayoutKind.Sequential)]
+ internal sealed class AuthIdentity : IDisposable
+ {
+ // public fields
+ [MarshalAs(UnmanagedType.LPWStr)]
+ public string Username;
+ public int UsernameLength;
+ [MarshalAs(UnmanagedType.LPWStr)]
+ public string Domain;
+ public int DomainLength;
+ public IntPtr Password;
+ public int PasswordLength;
+ public AuthIdentityFlag Flags;
+
+ // constructors
+ /// <summary>
+ /// Initializes a new instance of the <see cref="AuthIdentity" /> struct.
+ /// </summary>
+ /// <param name="username">The username.</param>
+ /// <param name="password">The password.</param>
+ public AuthIdentity(string username, SecureString password)
+ {
+ Username = null;
+ UsernameLength = 0;
+ if (!string.IsNullOrEmpty(username))
+ {
+ Username = username;
+ UsernameLength = username.Length;
+ }
+
+ Password = IntPtr.Zero;
+ PasswordLength = 0;
+
+ if (password != null && password.Length > 0)
+ {
+ Password = Marshal.SecureStringToGlobalAllocUnicode(password);
+ PasswordLength = password.Length;
+ }