Skip to content

Commit aa52702

Browse files
committed
CSHARP-1727: Refactor SecureString related code to not use BSTR and to be compatible with .NET Core.
1 parent 3fb9b82 commit aa52702

File tree

10 files changed

+142
-202
lines changed

10 files changed

+142
-202
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,7 @@ Please see our [guidelines](CONTRIBUTING.md) for contributing to the driver.
105105
* Sridhar Nanjundeswaran https://github.com/sridharn
106106
* Andrew Rondeau [email protected]
107107
108+
* Alexey Skalozub [email protected]
108109
* Pete Smith [email protected]
109110
* staywellandy https://github.com/staywellandy
110111

src/MongoDB.Driver.Core.Dotnet/project.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
"System.Net.Security": "4.0.0",
1010
"System.Security.Cryptography.Algorithms": "4.2.0",
1111
"System.Security.Cryptography.X509Certificates": "4.1.0",
12+
"System.Security.SecureString": "4.0.0",
1213
"System.Threading": "4.0.11"
1314
},
1415

src/MongoDB.Driver.Core/Core/Authentication/AuthenticationHelper.cs

Lines changed: 49 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -60,77 +60,81 @@ public static async Task AuthenticateAsync(IConnection connection, ConnectionDes
6060
}
6161
}
6262

63-
#if NET45
6463
public static string MongoPasswordDigest(string username, SecureString password)
6564
{
66-
IntPtr unmanagedPassword = IntPtr.Zero;
67-
try
65+
if (password.Length == 0)
66+
{
67+
return MongoPasswordDigest(username, new byte[0]);
68+
}
69+
else
6870
{
69-
unmanagedPassword = Marshal.SecureStringToBSTR(password);
70-
var passwordChars = new char[password.Length];
71-
GCHandle passwordCharsHandle = new GCHandle();
71+
#if NETSTANDARD1_6
72+
var passwordIntPtr = SecureStringMarshal.SecureStringToGlobalAllocUnicode(password);
73+
#else
74+
var passwordIntPtr = Marshal.SecureStringToGlobalAllocUnicode(password);
75+
#endif
7276
try
7377
{
74-
passwordCharsHandle = GCHandle.Alloc(passwordChars, GCHandleType.Pinned);
75-
Marshal.Copy(unmanagedPassword, passwordChars, 0, passwordChars.Length);
76-
77-
var byteCount = Utf8Encodings.Strict.GetByteCount(passwordChars);
78-
var passwordBytes = new byte[byteCount];
79-
GCHandle passwordBytesHandle = new GCHandle();
78+
var passwordChars = new char[password.Length];
79+
var passwordCharsHandle = GCHandle.Alloc(passwordChars, GCHandleType.Pinned);
8080
try
8181
{
82-
passwordBytesHandle = GCHandle.Alloc(passwordBytesHandle, GCHandleType.Pinned);
83-
Utf8Encodings.Strict.GetBytes(passwordChars, 0, passwordChars.Length, passwordBytes, 0);
82+
Marshal.Copy(passwordIntPtr, passwordChars, 0, password.Length);
8483

85-
return MongoPasswordDigest(username, passwordBytes);
84+
return MongoPasswordDigest(username, passwordChars);
8685
}
8786
finally
8887
{
89-
Array.Clear(passwordBytes, 0, passwordBytes.Length);
90-
91-
if (passwordBytesHandle.IsAllocated)
92-
{
93-
passwordBytesHandle.Free();
94-
}
88+
Array.Clear(passwordChars, 0, passwordChars.Length);
89+
passwordCharsHandle.Free();
9590
}
9691
}
9792
finally
9893
{
99-
Array.Clear(passwordChars, 0, passwordChars.Length);
100-
101-
if (passwordCharsHandle.IsAllocated)
102-
{
103-
passwordCharsHandle.Free();
104-
}
94+
Marshal.ZeroFreeGlobalAllocUnicode(passwordIntPtr);
10595
}
10696
}
97+
}
98+
99+
private static string MongoPasswordDigest(string username, char[] passwordChars)
100+
{
101+
var passwordBytes = new byte[Utf8Encodings.Strict.GetByteCount(passwordChars)];
102+
var passwordBytesHandle = GCHandle.Alloc(passwordBytes, GCHandleType.Pinned);
103+
try
104+
{
105+
Utf8Encodings.Strict.GetBytes(passwordChars, 0, passwordChars.Length, passwordBytes, 0);
106+
107+
return MongoPasswordDigest(username, passwordBytes);
108+
}
107109
finally
108110
{
109-
if (unmanagedPassword != IntPtr.Zero)
110-
{
111-
Marshal.ZeroFreeBSTR(unmanagedPassword);
112-
}
111+
Array.Clear(passwordBytes, 0, passwordBytes.Length);
112+
passwordBytesHandle.Free();
113113
}
114114
}
115-
#endif
116115

117-
public static string MongoPasswordDigest(string username, string password)
116+
private static string MongoPasswordDigest(string username, byte[] passwordBytes)
118117
{
119-
var passwordBytes = Utf8Encodings.Strict.GetBytes(password);
120-
return MongoPasswordDigest(username, passwordBytes);
121-
}
118+
var prefixString = username + ":mongo:";
119+
var prefixBytes = Utf8Encodings.Strict.GetBytes(prefixString);
122120

123-
public static string MongoPasswordDigest(string username, byte[] passwordBytes)
124-
{
125-
using (var md5 = MD5.Create())
121+
var buffer = new byte[prefixBytes.Length + passwordBytes.Length];
122+
var bufferHandle = GCHandle.Alloc(buffer, GCHandleType.Pinned);
123+
try
126124
{
127-
var bytes = Utf8Encodings.Strict.GetBytes(username + ":mongo:");
128-
129-
var buffer = new byte[bytes.Length + passwordBytes.Length];
130-
Buffer.BlockCopy(bytes, 0, buffer, 0, bytes.Length);
131-
Buffer.BlockCopy(passwordBytes, 0, buffer, bytes.Length, passwordBytes.Length);
125+
Buffer.BlockCopy(prefixBytes, 0, buffer, 0, prefixBytes.Length);
126+
Buffer.BlockCopy(passwordBytes, 0, buffer, prefixBytes.Length, passwordBytes.Length);
132127

133-
return BsonUtils.ToHexString(md5.ComputeHash(buffer));
128+
using (var md5 = MD5.Create())
129+
{
130+
var hash = md5.ComputeHash(buffer);
131+
return BsonUtils.ToHexString(hash);
132+
}
133+
}
134+
finally
135+
{
136+
Array.Clear(buffer, 0, buffer.Length);
137+
bufferHandle.Free();
134138
}
135139
}
136140
}

src/MongoDB.Driver.Core/Core/Authentication/MongoDBCRAuthenticator.cs

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -142,18 +142,14 @@ private CommandWireProtocol<BsonDocument> CreateGetNonceProtocol()
142142
return protocol;
143143
}
144144

145-
#if NETSTANDARD1_6
146-
private string CreateKey(string username, string password, string nonce)
147-
#else
148145
private string CreateKey(string username, SecureString password, string nonce)
149-
#endif
150146
{
151147
var passwordDigest = AuthenticationHelper.MongoPasswordDigest(username, password);
152148
using (var md5 = MD5.Create())
153149
{
154150
var bytes = Utf8Encodings.Strict.GetBytes(nonce + username + passwordDigest);
155-
bytes = md5.ComputeHash(bytes);
156-
return BsonUtils.ToHexString(bytes);
151+
var hash = md5.ComputeHash(bytes);
152+
return BsonUtils.ToHexString(hash);
157153
}
158154
}
159155
}

src/MongoDB.Driver.Core/Core/Authentication/UsernamePasswordCredential.cs

Lines changed: 14 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -27,28 +27,10 @@ public sealed class UsernamePasswordCredential
2727
{
2828
// fields
2929
private string _source;
30-
#if NETSTANDARD1_6
31-
private string _password;
32-
#else
3330
private SecureString _password;
34-
#endif
3531
private string _username;
3632

3733
// constructors
38-
#if NETSTANDARD1_6
39-
/// <summary>
40-
/// Initializes a new instance of the <see cref="UsernamePasswordCredential"/> class.
41-
/// </summary>
42-
/// <param name="source">The source.</param>
43-
/// <param name="username">The username.</param>
44-
/// <param name="password">The password.</param>
45-
public UsernamePasswordCredential(string source, string username, string password)
46-
{
47-
_source = Ensure.IsNotNullOrEmpty(source, nameof(source));
48-
_username = Ensure.IsNotNullOrEmpty(username, nameof(username));
49-
_password = Ensure.IsNotNull(password, nameof(password));
50-
}
51-
#else
5234
/// <summary>
5335
/// Initializes a new instance of the <see cref="UsernamePasswordCredential"/> class.
5436
/// </summary>
@@ -72,7 +54,6 @@ public UsernamePasswordCredential(string source, string username, SecureString p
7254
_username = Ensure.IsNotNullOrEmpty(username, nameof(username));
7355
_password = Ensure.IsNotNull(password, nameof(password));
7456
}
75-
#endif
7657

7758
// properties
7859
/// <summary>
@@ -81,11 +62,7 @@ public UsernamePasswordCredential(string source, string username, SecureString p
8162
/// <value>
8263
/// The password.
8364
/// </value>
84-
#if NETSTANDARD1_6
85-
public string Password
86-
#else
8765
public SecureString Password
88-
#endif
8966
{
9067
get { return _password; }
9168
}
@@ -119,26 +96,28 @@ public string Username
11996
/// <returns>The password.</returns>
12097
public string GetInsecurePassword()
12198
{
122-
#if NETSTANDARD1_6
123-
return _password;
124-
#else
125-
IntPtr unmanagedPassword = IntPtr.Zero;
126-
try
99+
if (_password.Length == 0)
127100
{
128-
unmanagedPassword = Marshal.SecureStringToGlobalAllocUnicode(_password);
129-
return Marshal.PtrToStringUni(unmanagedPassword);
101+
return "";
130102
}
131-
finally
103+
else
132104
{
133-
if (unmanagedPassword != IntPtr.Zero)
105+
#if NETSTANDARD1_6
106+
var passwordIntPtr = SecureStringMarshal.SecureStringToGlobalAllocUnicode(_password);
107+
#else
108+
var passwordIntPtr = Marshal.SecureStringToGlobalAllocUnicode(_password);
109+
#endif
110+
try
134111
{
135-
Marshal.ZeroFreeGlobalAllocUnicode(unmanagedPassword);
112+
return Marshal.PtrToStringUni(passwordIntPtr, _password.Length);
113+
}
114+
finally
115+
{
116+
Marshal.ZeroFreeGlobalAllocUnicode(passwordIntPtr);
136117
}
137118
}
138-
#endif
139119
}
140120

141-
#if NET45
142121
private static SecureString ConvertPasswordToSecureString(string password)
143122
{
144123
var secureString = new SecureString();
@@ -149,6 +128,5 @@ private static SecureString ConvertPasswordToSecureString(string password)
149128
secureString.MakeReadOnly();
150129
return secureString;
151130
}
152-
#endif
153131
}
154132
}

src/MongoDB.Driver.Legacy.Dotnet/project.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
{
1+
{
22
"version": "1.0.0-*",
33

44
"dependencies": {

src/MongoDB.Driver/MongoCredential.cs

Lines changed: 0 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -97,11 +97,7 @@ public string Password
9797
var passwordEvidence = _evidence as PasswordEvidence;
9898
if (passwordEvidence != null)
9999
{
100-
#if NETSTANDARD1_6
101-
return passwordEvidence.Password;
102-
#else
103100
return MongoUtils.ToInsecureString(passwordEvidence.SecurePassword);
104-
#endif
105101
}
106102

107103
return null;
@@ -163,7 +159,6 @@ public static MongoCredential CreateCredential(string databaseName, string usern
163159
new PasswordEvidence(password));
164160
}
165161

166-
#if NET45
167162
/// <summary>
168163
/// Creates a default credential.
169164
/// </summary>
@@ -178,7 +173,6 @@ public static MongoCredential CreateCredential(string databaseName, string usern
178173
username,
179174
new PasswordEvidence(password));
180175
}
181-
#endif
182176

183177
/// <summary>
184178
/// Creates a GSSAPI credential.
@@ -208,7 +202,6 @@ public static MongoCredential CreateGssapiCredential(string username, string pas
208202
new PasswordEvidence(password));
209203
}
210204

211-
#if NET45
212205
/// <summary>
213206
/// Creates a GSSAPI credential.
214207
/// </summary>
@@ -222,7 +215,6 @@ public static MongoCredential CreateGssapiCredential(string username, SecureStri
222215
username,
223216
new PasswordEvidence(password));
224217
}
225-
#endif
226218

227219
/// <summary>
228220
/// Creates a credential used with MONGODB-CR.
@@ -239,7 +231,6 @@ public static MongoCredential CreateMongoCRCredential(string databaseName, strin
239231
new PasswordEvidence(password));
240232
}
241233

242-
#if NET45
243234
/// <summary>
244235
/// Creates a credential used with MONGODB-CR.
245236
/// </summary>
@@ -254,7 +245,6 @@ public static MongoCredential CreateMongoCRCredential(string databaseName, strin
254245
username,
255246
new PasswordEvidence(password));
256247
}
257-
#endif
258248

259249
/// <summary>
260250
/// Creates a credential used with MONGODB-CR.
@@ -284,7 +274,6 @@ public static MongoCredential CreatePlainCredential(string databaseName, string
284274
new PasswordEvidence(password));
285275
}
286276

287-
#if NET45
288277
/// <summary>
289278
/// Creates a PLAIN credential.
290279
/// </summary>
@@ -299,7 +288,6 @@ public static MongoCredential CreatePlainCredential(string databaseName, string
299288
username,
300289
new PasswordEvidence(password));
301290
}
302-
#endif
303291

304292
// public methods
305293
/// <summary>
@@ -391,11 +379,7 @@ internal IAuthenticator ToAuthenticator()
391379
var passwordEvidence = _evidence as PasswordEvidence;
392380
if (passwordEvidence != null)
393381
{
394-
#if NETSTANDARD1_6
395-
var insecurePassword = passwordEvidence.Password;
396-
#else
397382
var insecurePassword = MongoUtils.ToInsecureString(passwordEvidence.SecurePassword);
398-
#endif
399383
var credential = new UsernamePasswordCredential(
400384
_identity.Source,
401385
_identity.Username,

0 commit comments

Comments
 (0)