diff options
Diffstat (limited to 'main/src/addins/MacPlatform')
-rw-r--r-- | main/src/addins/MacPlatform/MacInterop/Keychain.cs | 296 |
1 files changed, 263 insertions, 33 deletions
diff --git a/main/src/addins/MacPlatform/MacInterop/Keychain.cs b/main/src/addins/MacPlatform/MacInterop/Keychain.cs index b444165470..a371610e4c 100644 --- a/main/src/addins/MacPlatform/MacInterop/Keychain.cs +++ b/main/src/addins/MacPlatform/MacInterop/Keychain.cs @@ -26,17 +26,17 @@ using System; using System.Linq; -using System.Runtime.InteropServices; +using System.Text; using System.Collections.Generic; -using MonoDevelop.Core; -using MonoDevelop.Core.Serialization; +using System.Runtime.InteropServices; using System.Security.Cryptography.X509Certificates; +using MonoDevelop.Core; + namespace MonoDevelop.MacInterop { public static class Keychain { - #region P/Invoke signatures const string SecurityLib = "/System/Library/Frameworks/Security.framework/Security"; @@ -55,24 +55,29 @@ namespace MonoDevelop.MacInterop static extern OSStatus SecKeychainItemFreeContent (IntPtr attrList, IntPtr data); [DllImport (SecurityLib)] - static extern OSStatus SecKeychainAddGenericPassword (IntPtr keychain, uint serviceNameLength, string serviceName, - uint accountNameLength, string accountName, uint passwordLength, + static extern OSStatus SecKeychainAddGenericPassword (IntPtr keychain, uint serviceNameLength, byte[] serviceName, + uint accountNameLength, byte[] accountName, uint passwordLength, byte[] passwordData, ref IntPtr itemRef); [DllImport (SecurityLib)] - static extern OSStatus SecKeychainFindGenericPassword (IntPtr keychain, uint serviceNameLength, string serviceName, - uint accountNameLength, string accountName, out uint passwordLength, - out IntPtr passwordData, ref IntPtr itemRef); + static extern OSStatus SecKeychainFindGenericPassword (IntPtr keychain, uint serviceNameLength, byte[] serviceName, + uint accountNameLength, byte[] accountName, out uint passwordLength, + out IntPtr passwordData, ref IntPtr itemRef); [DllImport (SecurityLib)] - static extern OSStatus SecKeychainAddInternetPassword (IntPtr keychain, uint serverNameLength, string serverName, uint securityDomainLength, - string securityDomain, uint accountNameLength, string accountName, uint pathLength, - string path, ushort port, int protocol, int authenticationType, - uint passwordLength, byte[] passwordData, ref IntPtr itemRef); + static extern OSStatus SecKeychainAddInternetPassword (IntPtr keychain, uint serverNameLength, byte[] serverName, uint securityDomainLength, + byte[] securityDomain, uint accountNameLength, byte[] accountName, uint pathLength, + byte[] path, ushort port, SecProtocolType protocol, SecAuthenticationType authType, + uint passwordLength, byte[] passwordData, ref IntPtr itemRef); + [DllImport (SecurityLib)] + static extern OSStatus SecKeychainFindInternetPassword (IntPtr keychain, uint serverNameLength, byte[] serverName, uint securityDomainLength, + byte[] securityDomain, uint accountNameLength, byte[] accountName, uint pathLength, + byte[] path, ushort port, SecProtocolType protocol, SecAuthenticationType authType, + out uint passwordLength, out IntPtr passwordData, ref IntPtr itemRef); + [DllImport (SecurityLib)] - static extern OSStatus SecKeychainFindInternetPassword (IntPtr keychain, uint serverNameLength, string serverName, uint securityDomainLength, - string securityDomain, uint accountNameLength, string accountName, uint pathLength, - string path, ushort port, int protocol, int authenticationType, - out uint passwordLength, out IntPtr passwordData, ref IntPtr itemRef); + static extern unsafe OSStatus SecKeychainItemCreateFromContent (SecItemClass itemClass, SecKeychainAttributeList *attrList, + uint passwordLength, byte[] password, IntPtr keychain, + IntPtr initialAccess, ref IntPtr itemRef); [DllImport (SecurityLib)] static extern OSStatus SecKeychainItemModifyAttributesAndData (IntPtr itemRef, IntPtr attrList, uint length, byte [] data); @@ -111,6 +116,35 @@ namespace MonoDevelop.MacInterop [DllImport (SecurityLib)] static extern OSStatus SecCertificateGetData (IntPtr certificate, out CssmData data); + + + [StructLayout (LayoutKind.Sequential)] + struct SecKeychainAttributeList + { + public int Count; + public IntPtr Attrs; + + public SecKeychainAttributeList (int count, IntPtr attrs) + { + Count = count; + Attrs = attrs; + } + } + + [StructLayout (LayoutKind.Sequential)] + struct SecKeychainAttribute + { + public SecItemAttr Tag; + public uint Length; + public IntPtr Data; + + public SecKeychainAttribute (SecItemAttr tag, uint length, IntPtr data) + { + Tag = tag; + Length = length; + Data = data; + } + } struct CssmData { @@ -254,7 +288,7 @@ namespace MonoDevelop.MacInterop { return GetAllSigningCertificates ().Where (x => { var y = GetCertificateCommonName (x); - return y != null && y.Length > 0 && nameCheck (y); + return !string.IsNullOrEmpty (y) && nameCheck (y); }); } @@ -343,25 +377,122 @@ namespace MonoDevelop.MacInterop return cert.GetNameInfo (X509NameType.SimpleName, false); } - public static void AddInternetPassword (Uri uri, string password) + static SecAuthenticationType GetSecAuthenticationType (string query) { - IntPtr itemRef = IntPtr.Zero; + if (string.IsNullOrEmpty (query)) + return SecAuthenticationType.Any; + + string auth = "default"; + foreach (var pair in query.Substring (1).Split (new char[] { '&' })) { + var kvp = pair.ToLowerInvariant ().Split (new char[] { '=' }); + if (kvp[0] == "auth" && kvp.Length == 2) { + auth = kvp[1]; + break; + } + } + + switch (auth.ToLowerInvariant ()) { + case "ntlm": return SecAuthenticationType.NTLM; + case "msn": return SecAuthenticationType.MSN; + case "dpa": return SecAuthenticationType.DPA; + case "rpa": return SecAuthenticationType.RPA; + case "httpbasic": case "basic": return SecAuthenticationType.HTTPBasic; + case "httpdigest": case "digest": return SecAuthenticationType.HTTPDigest; + case "htmlform": case "form": return SecAuthenticationType.HTMLForm; + case "default": return SecAuthenticationType.Default; + default: return SecAuthenticationType.Any; + } + } + + static SecProtocolType GetSecProtocolType (string protocol) + { + switch (protocol.ToLowerInvariant ()) { + case "ftp": return SecProtocolType.FTP; + case "ftpaccount": return SecProtocolType.FTPAccount; + case "http": return SecProtocolType.HTTP; + case "irc": return SecProtocolType.IRC; + case "nntp": return SecProtocolType.NNTP; + case "pop3": return SecProtocolType.POP3; + case "smtp": return SecProtocolType.SMTP; + case "socks": return SecProtocolType.SOCKS; + case "imap": return SecProtocolType.IMAP; + case "ldap": return SecProtocolType.LDAP; + case "appletalk": return SecProtocolType.AppleTalk; + case "afp": return SecProtocolType.AFP; + case "telnet": return SecProtocolType.Telnet; + case "ssh": return SecProtocolType.SSH; + case "ftps": return SecProtocolType.FTPS; + case "httpproxy": return SecProtocolType.HTTPProxy; + case "httpsproxy": return SecProtocolType.HTTPSProxy; + case "ftpproxy": return SecProtocolType.FTPProxy; + case "cifs": return SecProtocolType.CIFS; + case "smb": return SecProtocolType.SMB; + case "rtsp": return SecProtocolType.RTSP; + case "rtspproxy": return SecProtocolType.RTSPProxy; + case "daap": return SecProtocolType.DAAP; + case "eppc": return SecProtocolType.EPPC; + case "ipp": return SecProtocolType.IPP; + case "nntps": return SecProtocolType.NNTPS; + case "ldaps": return SecProtocolType.LDAPS; + case "telnets": return SecProtocolType.TelnetS; + case "imaps": return SecProtocolType.IMAPS; + case "ircs": return SecProtocolType.IRCS; + case "pop3s": return SecProtocolType.POP3S; + case "cvspserver": return SecProtocolType.CVSpserver; + case "svn": return SecProtocolType.SVN; + default: return SecProtocolType.Any; + } + } + + public static unsafe void AddInternetPassword (Uri uri, string password) + { + byte[] path = Encoding.UTF8.GetBytes (string.Join (string.Empty, uri.Segments).Substring (1)); // don't include the leading '/' + byte[] user = Encoding.UTF8.GetBytes (Uri.UnescapeDataString (uri.UserInfo)); + byte[] passwd = Encoding.UTF8.GetBytes (password); + byte[] host = Encoding.UTF8.GetBytes (uri.Host); + var auth = GetSecAuthenticationType (uri.Query); + var protocol = GetSecProtocolType (uri.Scheme); IntPtr passwordPtr = IntPtr.Zero; + IntPtr itemRef = IntPtr.Zero; uint passwordLength = 0; - var passwordBytes = System.Text.Encoding.UTF8.GetBytes (password); + int port = uri.Port; // See if there is already a password there for this uri - var result = SecKeychainFindInternetPassword (IntPtr.Zero, (uint) uri.Host.Length, uri.Host, 0, null, - (uint) uri.UserInfo.Length, uri.UserInfo, (uint) uri.PathAndQuery.Length, uri.PathAndQuery, - (ushort) uri.Port, 0, 0, out passwordLength, out passwordPtr, ref itemRef); + var result = SecKeychainFindInternetPassword (IntPtr.Zero, (uint) host.Length, host, 0, null, + (uint) user.Length, user, (uint) path.Length, path, (ushort) port, + protocol, auth, out passwordLength, out passwordPtr, ref itemRef); + if (result == OSStatus.Ok) { // If there is, replace it with the new one - result = SecKeychainItemModifyAttributesAndData (itemRef, IntPtr.Zero, (uint) passwordBytes.Length, passwordBytes); + result = SecKeychainItemModifyAttributesAndData (itemRef, IntPtr.Zero, (uint) passwd.Length, passwd); + CFRelease (itemRef); } else { // Otherwise add a new entry with the password - result = SecKeychainAddInternetPassword (IntPtr.Zero, (uint) uri.Host.Length, uri.Host, 0, null, - (uint) uri.UserInfo.Length, uri.UserInfo, (uint) uri.PathAndQuery.Length, uri.PathAndQuery, - (ushort) uri.Port, 0, 0, (uint) passwordBytes.Length, passwordBytes, ref itemRef); +// result = SecKeychainAddInternetPassword (IntPtr.Zero, (uint) uri.Host.Length, uri.Host, 0, null, +// (uint) user.Length, user, (uint) path.Length, path, (ushort) uri.Port, +// (int) protocol, (int) auth, (uint) passwordBytes.Length, passwordBytes, ref itemRef); + + var label = Encoding.UTF8.GetBytes (string.Format ("{0} ({1})", uri.Host, Uri.UnescapeDataString (uri.UserInfo))); + fixed (byte* labelPtr = label, userPtr = user, hostPtr = host, pathPtr = path) { + SecKeychainAttribute* attrs = stackalloc SecKeychainAttribute [7]; + int* protoPtr = (int*) &protocol; + int* authPtr = (int*) &auth; + int* portPtr = &port; + + attrs[0] = new SecKeychainAttribute (SecItemAttr.Label, (uint) label.Length, (IntPtr) labelPtr); + attrs[1] = new SecKeychainAttribute (SecItemAttr.Account, (uint) user.Length, (IntPtr) userPtr); + attrs[2] = new SecKeychainAttribute (SecItemAttr.Protocol, (uint) 4, (IntPtr) protoPtr); + attrs[3] = new SecKeychainAttribute (SecItemAttr.AuthType, (uint) 4, (IntPtr) authPtr); + attrs[4] = new SecKeychainAttribute (SecItemAttr.Server, (uint) host.Length, (IntPtr) hostPtr); + attrs[5] = new SecKeychainAttribute (SecItemAttr.Port, (uint) 4, (IntPtr) portPtr); + attrs[6] = new SecKeychainAttribute (SecItemAttr.Path, (uint) path.Length, (IntPtr) pathPtr); + + SecKeychainAttributeList attrList = new SecKeychainAttributeList (6, (IntPtr) attrs); + + itemRef = IntPtr.Zero; + result = SecKeychainItemCreateFromContent (SecItemClass.InternetPassword, &attrList, (uint) passwd.Length, passwd, IntPtr.Zero, IntPtr.Zero, ref itemRef); + CFRelease (itemRef); + } } if (result != OSStatus.Ok) @@ -370,19 +501,35 @@ namespace MonoDevelop.MacInterop public static string FindInternetPassword (Uri uri) { + byte[] path = Encoding.UTF8.GetBytes (string.Join (string.Empty, uri.Segments).Substring (1)); // don't include the leading '/' + byte[] user = Encoding.UTF8.GetBytes (Uri.UnescapeDataString (uri.UserInfo)); + byte[] host = Encoding.UTF8.GetBytes (uri.Host); + var auth = GetSecAuthenticationType (uri.Query); + var protocol = GetSecProtocolType (uri.Scheme); + IntPtr passwordPtr = IntPtr.Zero; IntPtr itemRef = IntPtr.Zero; - IntPtr password = IntPtr.Zero; uint passwordLength = 0; - var result = SecKeychainFindInternetPassword (IntPtr.Zero, (uint) uri.Host.Length, uri.Host, 0, null, - (uint) uri.UserInfo.Length, uri.UserInfo, (uint) uri.PathAndQuery.Length, uri.PathAndQuery, - (ushort) uri.Port, 0, 0, out passwordLength, out password, ref itemRef); + + // Look for an internet password for the given protocol and auth mechanism + var result = SecKeychainFindInternetPassword (IntPtr.Zero, (uint) host.Length, host, 0, null, + (uint) user.Length, user, (uint) path.Length, path, (ushort) uri.Port, + protocol, auth, out passwordLength, out passwordPtr, ref itemRef); + + // Fall back to looking for a password for SecProtocolType.Any && SecAuthenticationType.Any + if (result == OSStatus.ItemNotFound && protocol != SecProtocolType.Any) + result = SecKeychainFindInternetPassword (IntPtr.Zero, (uint) host.Length, host, 0, null, + (uint) user.Length, user, (uint) path.Length, path, (ushort) uri.Port, + 0, auth, out passwordLength, out passwordPtr, ref itemRef); + + CFRelease (itemRef); + if (result == OSStatus.ItemNotFound) return null; if (result != OSStatus.Ok) throw new Exception ("Could not find internet password: " + GetError (result)); - return Marshal.PtrToStringAuto (password, (int) passwordLength); + return Marshal.PtrToStringAuto (passwordPtr, (int) passwordLength); } enum SecItemClass : uint @@ -395,6 +542,38 @@ namespace MonoDevelop.MacInterop PrivateKey = 0x0000000A + 6, SymmetricKey = 0x0000000A + 7 } + + enum SecItemAttr : int + { + CreationDate = 1667522932, + ModDate = 1835295092, + Description = 1684370275, + Comment = 1768123764, + Creator = 1668445298, + Type = 1954115685, + ScriptCode = 1935897200, + Label = 1818321516, + Invisible = 1768846953, + Negative = 1852139361, + CustomIcon = 1668641641, + Account = 1633903476, + Service = 1937138533, + Generic = 1734700641, + SecurityDomain = 1935961454, + Server = 1936881266, + AuthType = 1635023216, + Port = 1886351988, + Path = 1885434984, + Volume = 1986817381, + Address = 1633969266, + Signature = 1936943463, + Protocol = 1886675820, + CertificateType = 1668577648, + CertificateEncoding = 1667591779, + CrlType = 1668445296, + CrlEncoding = 1668443747, + Alias = 1634494835, + } enum OSStatus { @@ -432,6 +611,57 @@ namespace MonoDevelop.MacInterop Wrap = 25, Unwrap = 26, } + + enum SecAuthenticationType : int + { + NTLM = 1835824238, + MSN = 1634628461, + DPA = 1633775716, + RPA = 1633775730, + HTTPBasic = 1886680168, + HTTPDigest = 1685353576, + HTMLForm = 1836216166, + Default = 1953261156, + Any = 0 + } + + enum SecProtocolType : int + { + FTP = 1718906912, + FTPAccount = 1718906977, + HTTP = 1752462448, + IRC = 1769104160, + NNTP = 1852732528, + POP3 = 1886351411, + SMTP = 1936553072, + SOCKS = 1936685088, + IMAP = 1768776048, + LDAP = 1818517872, + AppleTalk = 1635019883, + AFP = 1634103328, + Telnet = 1952803950, + SSH = 1936943136, + FTPS = 1718906995, + HTTPProxy = 1752461432, + HTTPSProxy = 1752462200, + FTPProxy = 1718907000, + CIFS = 1667851891, + SMB = 1936548384, + RTSP = 1920234352, + RTSPProxy = 1920234360, + DAAP = 1684103536, + EPPC = 1701867619, + IPP = 1768976416, + NNTPS = 1853124723, + LDAPS = 1818521715, + TelnetS = 1952803955, + IMAPS = 1768779891, + IRCS = 1769104243, + POP3S = 1886351475, + CVSpserver = 1668707184, + SVN = 1937141280, + Any = 0 + } [Flags] enum CssmKeyUse : uint |