Add SqlCredential support -
authorNeale Ferguson <neale@sinenomine.net>
Fri, 19 Sep 2014 15:42:55 +0000 (11:42 -0400)
committerNeale Ferguson <neale@sinenomine.net>
Fri, 19 Sep 2014 15:52:52 +0000 (11:52 -0400)
- 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

mcs/class/Mono.Data.Tds/Mono.Data.Tds.Protocol/Tds.cs
mcs/class/Mono.Data.Tds/Mono.Data.Tds.Protocol/Tds42.cs
mcs/class/Mono.Data.Tds/Mono.Data.Tds.Protocol/Tds50.cs
mcs/class/Mono.Data.Tds/Mono.Data.Tds.Protocol/Tds70.cs
mcs/class/Mono.Data.Tds/Mono.Data.Tds.Protocol/TdsConnectionParameters.cs
mcs/class/System.Data/System.Data.SqlClient/SqlConnection.cs
mcs/class/System.Data/System.Data.SqlClient/SqlCredential.cs [new file with mode: 0644]
mcs/class/System.Data/System.Data.dll.sources

index 6865df9adbed27a38fcc7d789b427bc682938cb4..f06932c802620d1fed8f3fcb74fb9d5e0fb1ee26 100644 (file)
@@ -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
index fdca83087b1cc65b930f7218a481968462430fa5..fb517d013d55841d99f8602154b2826e0e793fc9 100644 (file)
@@ -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
index 0d0e86c023dfa673bf24f67787908c105ffc978e..88219125df5f3d081963aed376c790a6cacc208f 100644 (file)
@@ -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
index c67b6e8c3bb3930186f872afae041c4eacdc8a15..991727bbc00779b3441c2c7020c03f405aa85f76 100644 (file)
@@ -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;
index 19f676077d760f1394248c67f583b748a1044f20..e3b83aa257c9441108183f949910aee8a4c72302 100644 (file)
@@ -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; 
index 8712eca6cf9300024b0ccf802a5738eb96df6324..e1fa878c4caa3045bcd95254ba1b2c015ce3370b 100644 (file)
@@ -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 (file)
index 0000000..5b15b48
--- /dev/null
@@ -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 {
+       /// <summary>
+       /// Describes an error from a SQL database.
+       /// </summary>
+       [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
+       }
+}
index aeae903dbc907c5a880348a0459e045d06b1adfb..5ec88803ea643775ca3c14ac6048d7c1e259440e 100644 (file)
@@ -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