From: Neale Ferguson Date: Fri, 19 Sep 2014 15:42:55 +0000 (-0400) Subject: Add SqlCredential support - X-Git-Url: http://wien.tomnetworks.com/gitweb/?a=commitdiff_plain;h=a83e9cf4aaaef69d3eabbca2de74005ed207374f;p=mono.git Add SqlCredential support - - Tds.cs: Use SecureString objects for passwords; Add method to retrieve string from SecureString - Tds42.cs: Use SecureString for passwords - Tds50.cs: Use SecureString for passwords - Tds70.cs: Use SecureString for passwords - TdsConnectionParameters.cs: Use SecureString for passwords; Initialize Password parameter as cleared string; Add indicator for when password is set - SqlConnection.cs: Use SecureString for passwords; Add SqlConnect method that accepts an SqlCredential along with the Connection string; Perform checking of parameters to ensure user/password not specified in connection string if credentials have been specified or using credentials when domain login is specified. - SqlCredential.cs: Add new class with support for credentials - System.Data.dll.sources: Add SqlCredential.cs to the build list --- diff --git a/mcs/class/Mono.Data.Tds/Mono.Data.Tds.Protocol/Tds.cs b/mcs/class/Mono.Data.Tds/Mono.Data.Tds.Protocol/Tds.cs index 6865df9adbe..f06932c8026 100644 --- a/mcs/class/Mono.Data.Tds/Mono.Data.Tds.Protocol/Tds.cs +++ b/mcs/class/Mono.Data.Tds/Mono.Data.Tds.Protocol/Tds.cs @@ -41,7 +41,9 @@ using System.ComponentModel; using System.Diagnostics; using System.Net.Sockets; using System.Globalization; +using System.Security; using System.Text; +using System.Runtime.InteropServices; namespace Mono.Data.Tds.Protocol { @@ -1468,7 +1470,7 @@ namespace Mono.Data.Tds.Protocol t3.Domain = this.connectionParms.DefaultDomain; t3.Host = this.connectionParms.Hostname; t3.Username = this.connectionParms.User; - t3.Password = this.connectionParms.Password; + t3.Password = GetPlainPassword(this.connectionParms.Password); Comm.StartPacket (TdsPacketType.SspAuth); // 0x11 Comm.Append (t3.GetBytes ()); @@ -1919,6 +1921,20 @@ namespace Mono.Data.Tds.Protocol comm.Skip(4); } + public static string GetPlainPassword(SecureString secPass) + { + IntPtr plainString = IntPtr.Zero; + try + { + plainString = Marshal.SecureStringToGlobalAllocUnicode(secPass); + return Marshal.PtrToStringUni(plainString); + } + finally + { + Marshal.ZeroFreeGlobalAllocUnicode(plainString); + } + } + #endregion // Private Methods #if NET_2_0 diff --git a/mcs/class/Mono.Data.Tds/Mono.Data.Tds.Protocol/Tds42.cs b/mcs/class/Mono.Data.Tds/Mono.Data.Tds.Protocol/Tds42.cs index fdca83087b1..fb517d013d5 100644 --- a/mcs/class/Mono.Data.Tds/Mono.Data.Tds.Protocol/Tds42.cs +++ b/mcs/class/Mono.Data.Tds/Mono.Data.Tds.Protocol/Tds42.cs @@ -29,6 +29,7 @@ // using System; +using System.Security; namespace Mono.Data.Tds.Protocol { public sealed class Tds42 : Tds @@ -77,7 +78,7 @@ namespace Mono.Data.Tds.Protocol { Comm.Append ((byte) (tmp.Length < 30 ? tmp.Length : 30)); // password (offset 62 0x3e) - tmp = Comm.Append (connectionParameters.Password, 30, pad); + tmp = Comm.Append (GetPlainPassword(connectionParameters.Password), 30, pad); Comm.Append ((byte) (tmp.Length < 30 ? tmp.Length : 30)); // hostproc (offset 93 0x5d) @@ -145,7 +146,7 @@ namespace Mono.Data.Tds.Protocol { // remote passwords Comm.Append (empty, 2, pad); - tmp = Comm.Append (connectionParameters.Password, 253, pad); + tmp = Comm.Append (GetPlainPassword(connectionParameters.Password), 253, pad); Comm.Append ((byte) (tmp.Length < 253 ? tmp.Length + 2 : 253 + 2)); // tds version diff --git a/mcs/class/Mono.Data.Tds/Mono.Data.Tds.Protocol/Tds50.cs b/mcs/class/Mono.Data.Tds/Mono.Data.Tds.Protocol/Tds50.cs index 0d0e86c023d..88219125df5 100644 --- a/mcs/class/Mono.Data.Tds/Mono.Data.Tds.Protocol/Tds50.cs +++ b/mcs/class/Mono.Data.Tds/Mono.Data.Tds.Protocol/Tds50.cs @@ -31,6 +31,7 @@ using Mono.Data.Tds; using System; using System.Text; +using System.Security; namespace Mono.Data.Tds.Protocol { @@ -118,7 +119,7 @@ namespace Mono.Data.Tds.Protocol // password (offset 62 0x3e) // 62-92 - tmp = Comm.Append (connectionParameters.Password, 30, pad); + tmp = Comm.Append (GetPlainPassword(connectionParameters.Password), 30, pad); Comm.Append ((byte) (tmp.Length < 30 ? tmp.Length : 30)); // hostproc (offset 93 0x5d) @@ -187,7 +188,7 @@ namespace Mono.Data.Tds.Protocol // remote passwords // 202-457 Comm.Append (empty, 2, pad); - tmp = Comm.Append (connectionParameters.Password, 253, pad); + tmp = Comm.Append (GetPlainPassword(connectionParameters.Password), 253, pad); Comm.Append ((byte) (tmp.Length < 253 ? tmp.Length + 2 : 253 + 2)); // tds version diff --git a/mcs/class/Mono.Data.Tds/Mono.Data.Tds.Protocol/Tds70.cs b/mcs/class/Mono.Data.Tds/Mono.Data.Tds.Protocol/Tds70.cs index c67b6e8c3bb..991727bbc00 100644 --- a/mcs/class/Mono.Data.Tds/Mono.Data.Tds.Protocol/Tds70.cs +++ b/mcs/class/Mono.Data.Tds/Mono.Data.Tds.Protocol/Tds70.cs @@ -37,6 +37,7 @@ using System; using System.Globalization; using System.Text; +using System.Security; using Mono.Security.Protocol.Ntlm; @@ -392,11 +393,12 @@ namespace Mono.Data.Tds.Protocol return IsConnected; } - private static string EncryptPassword (string pass) + private static string EncryptPassword (SecureString secPass) { int xormask = 0x5a5a; - int len = pass.Length; + int len = secPass.Length; char[] chars = new char[len]; + string pass = GetPlainPassword(secPass); for (int i = 0; i < len; ++i) { int c = ((int) (pass[i])) ^ xormask; diff --git a/mcs/class/Mono.Data.Tds/Mono.Data.Tds.Protocol/TdsConnectionParameters.cs b/mcs/class/Mono.Data.Tds/Mono.Data.Tds.Protocol/TdsConnectionParameters.cs index 19f676077d7..e3b83aa257c 100644 --- a/mcs/class/Mono.Data.Tds/Mono.Data.Tds.Protocol/TdsConnectionParameters.cs +++ b/mcs/class/Mono.Data.Tds/Mono.Data.Tds.Protocol/TdsConnectionParameters.cs @@ -31,6 +31,7 @@ // using System; +using System.Security; namespace Mono.Data.Tds.Protocol { @@ -42,7 +43,8 @@ namespace Mono.Data.Tds.Protocol public string Hostname; public string Language; public string LibraryName; - public string Password; + public SecureString Password; + public bool PasswordSet; public string ProgName; public string User; public bool DomainLogin; @@ -62,7 +64,8 @@ namespace Mono.Data.Tds.Protocol Hostname = System.Net.Dns.GetHostName(); Language = String.Empty; LibraryName = "Mono"; - Password = String.Empty; + Password = new SecureString(); + PasswordSet = false; ProgName = "Mono"; User = String.Empty; DomainLogin = false; diff --git a/mcs/class/System.Data/System.Data.SqlClient/SqlConnection.cs b/mcs/class/System.Data/System.Data.SqlClient/SqlConnection.cs index 8712eca6cf9..e1fa878c4ca 100644 --- a/mcs/class/System.Data/System.Data.SqlClient/SqlConnection.cs +++ b/mcs/class/System.Data/System.Data.SqlClient/SqlConnection.cs @@ -56,6 +56,7 @@ using System.Xml; #if NET_2_0 using System.Collections.Generic; #endif +using System.Security; namespace System.Data.SqlClient { @@ -93,6 +94,9 @@ namespace System.Data.SqlClient // The connection string that identifies this connection string connectionString; + // The connection credentials + SqlCredential credentials; + // The transaction object for the current transaction SqlTransaction transaction; @@ -133,6 +137,12 @@ namespace System.Data.SqlClient ConnectionString = connectionString; } + public SqlConnection (string connectionString, SqlCredential cred) + { + ConnectionString = connectionString; + Credentials = cred; + } + #endregion // Constructors #region Properties @@ -155,6 +165,15 @@ namespace System.Data.SqlClient } } + public SqlCredential Credentials { + get { + return credentials; + } + set { + credentials = value; + } + } + #if !NET_2_0 [DataSysDescription ("Current connection timeout value, 'Connect Timeout=X' in the ConnectionString.")] #endif @@ -563,6 +582,16 @@ namespace System.Data.SqlClient if (!tds.IsConnected) { try { + if (Credentials != null) { + if (parms.User != String.Empty) + throw new ArgumentException("UserID already specified"); + if (parms.PasswordSet) + throw new ArgumentException("Password already specified"); + if (parms.DomainLogin != false) + throw new ArgumentException("Cannot use credentials with DomainLogin"); + parms.User = Credentials.UserId; + parms.Password = Credentials.Password; + } tds.Connect (parms); } catch { if (pooling) @@ -879,7 +908,10 @@ namespace System.Data.SqlClient break; case "password" : case "pwd" : - parms.Password = value; + parms.Password = new SecureString(); + foreach (char c in value) + parms.Password.AppendChar(c); + parms.PasswordSet = true; break; case "persistsecurityinfo" : case "persist security info" : diff --git a/mcs/class/System.Data/System.Data.SqlClient/SqlCredential.cs b/mcs/class/System.Data/System.Data.SqlClient/SqlCredential.cs new file mode 100644 index 00000000000..5b15b48cb66 --- /dev/null +++ b/mcs/class/System.Data/System.Data.SqlClient/SqlCredential.cs @@ -0,0 +1,76 @@ +// +// System.Data.SqlClient.SqlCredential.cs +// +// Author: +// Neale Ferguson (neale@sinenomine.net) +// +// Copyright (C) Neale Ferguson, 2014 +// + +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// + +using System; +using System.Data; +using System.Runtime.InteropServices; +using System.Security; + +namespace System.Data.SqlClient { + /// + /// Describes an error from a SQL database. + /// + [Serializable] + public sealed class SqlCredential + { + #region Fields + + string uid = ""; + SecureString pwd = null; + + #endregion // Fields + + #region Constructors + + public SqlCredential (string user, SecureString password) + { + if (user == null) + throw new ArgumentNullException("UserID"); + if (password == null) + throw new ArgumentNullException("Password"); + this.uid = user; + this.pwd = password; + } + + #endregion // Constructors + + #region Properties + + public string UserId { + get { return uid; } + } + + public SecureString Password { + get { return pwd; } + } + + #endregion + } +} diff --git a/mcs/class/System.Data/System.Data.dll.sources b/mcs/class/System.Data/System.Data.dll.sources index aeae903dbc9..5ec88803ea6 100644 --- a/mcs/class/System.Data/System.Data.dll.sources +++ b/mcs/class/System.Data/System.Data.dll.sources @@ -289,6 +289,7 @@ System.Data.SqlClient/SqlCommand.cs System.Data.SqlClient/SqlCommandBuilder.cs System.Data.SqlClient/SqlConnection.cs System.Data.SqlClient/SqlConnectionStringBuilder.cs +System.Data.SqlClient/SqlCredential.cs System.Data.SqlClient/SqlDataAdapter.cs System.Data.SqlClient/SqlDataReader.cs System.Data.SqlClient/SqlDataSourceConverter.cs