// Created by Windward Studios - no copyright is claimed. This code can be used in // any manner by anyone for any reason. There is no copyright of any kind on it. You may // use it in commercial products. You may change it without sharing those changes. // We ask that you keep the "created by Windward Studios" in a comment at the top. using System; using System.IO; using System.Net; using System.Runtime.InteropServices; using System.Security.Principal; using System.Xml; namespace net.windward.utils.xml { /// /// Provides the capability to open and test an xml file regardless of it's filename/url and the /// security mechanism. This includes most combinations where the calling program is on a domain /// or workgroup and the xml file is on a domain (same or different), workgroup, http, ftp, or a /// system with no uname/password required. /// It does not handle every combination because Windows does not provide the capabilities to do so /// in an API (even though "net use" can do so). /// bugbug - need to add schema support for open and test; return an XPathDocument also. /// public class XmlFileOpen { [DllImport("advapi32.dll", SetLastError = true)] private static extern bool LogonUser(String lpszUsername, String lpszDomain, String lpszPassword, int dwLogonType, int dwLogonProvider, ref IntPtr phToken); [DllImport("kernel32.dll", CharSet = CharSet.Auto)] private extern static bool CloseHandle(IntPtr handle); /* [DllImport("NetApi32.dll", SetLastError = true, CharSet = CharSet.Unicode)] private static extern UInt32 NetUseAdd(string UncServerName, int Level, ref USE_INFO_2 Buf, out uint ParmError); [DllImport("NetApi32.dll", SetLastError = true, CharSet = CharSet.Unicode)] private static extern uint NetUseDel(string UncServerName, string UseName, uint ForceCond); */ [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] private struct USE_INFO_2 { public string ui2_local; public string ui2_remote; public string ui2_password; public uint ui2_status; public uint ui2_asg_type; public uint ui2_refcount; public uint ui2_usecount; public string ui2_username; public string ui2_domainname; } const int LOGON32_PROVIDER_DEFAULT = 0; const int LOGON32_LOGON_NEW_CREDENTIALS = 9; /// /// Open the XML document using the passed in credentials. /// /// The name of the file. Can be http(s) or ftp as well as a share or plain old filename. /// Username to open under. If null then anonymous user. /// Password to open under. /// the loaded XML document. public static XmlDocument OpenXmlDocument(string filename, string username, string password) { // handle http(s) and ftp using credentials string lcName = filename.ToLower(); if (lcName.StartsWith("http:") || lcName.StartsWith("https:") || lcName.StartsWith("ftp:")) { XmlDocument xmlDoc = new XmlDocument(); if (username != null) { XmlUrlResolver resolver = new XmlUrlResolver(); resolver.Credentials = new NetworkCredential(username, password); xmlDoc.XmlResolver = resolver; } xmlDoc.Load(filename); return xmlDoc; } // if anon - try it if (username == null) { // can't do this -- return OpenWithIdentity(filename, WindowsIdentity.GetAnonymous()); XmlDocument xmlDoc = new XmlDocument(); xmlDoc.Load(filename); return xmlDoc; } // get the username & domain string domain, user; int pos = username.IndexOf('\\'); if (pos == -1) pos = username.IndexOf('/'); bool onlyDomain; if (pos != -1) { onlyDomain = true; domain = username.Substring(0, pos); user = username.Substring(pos + 1); } else { onlyDomain = false; WindowsIdentity ident = WindowsIdentity.GetCurrent(); if (ident == null) domain = null; else { domain = ident.Name; pos = domain.IndexOf('\\'); if (pos != -1) domain = domain.Substring(0, pos); } user = username; } // first we try a domain set of credentials if (domain != null) { try { XmlDocument doc = OpenAsDomainUser(filename, domain, user, password); if (doc != null) return doc; } catch (Exception) { if (onlyDomain) throw; } } if (onlyDomain) throw new IOException("Could not open: " + filename + " as user: " + domain + "\\" + user); // get the share name (may not be one) - we don't handle z:\file.xml using local user on Z: system /* only needed if we have remote local user access string share = null; if (filename.StartsWith("\\\\")) { pos = filename.IndexOf('\\', 3); if (pos != -1) { pos = filename.IndexOf('\\', pos + 2); if (pos != -1) share = filename.Substring(0, pos); } } */ // second we try as a local user on this system return OpenAsDomainUser(filename, ".", user, password); /* No good way to open as a local user on a remote machine try { XmlDocument doc = OpenAsDomainUser(filename, ".", user, password); if (doc != null) return doc; } catch (Exception) { // if not a share - we're done if (share == null) throw; } // if no share, we're done if (share == null) throw new ApplicationException("bugbug"); // third try local user on remote system return OpenAsRemoteUser(share, filename, username, password); */ } /// /// Open the XML document using the passed in credentials. /// /// The name of the file. Can be http(s) or ftp as well as a share or plain old filename. /// Username to open under. If null then anonymous user. /// Password to open under. public static void TestXmlFile(string filename, string username, string password) { // handle http(s) and ftp using credentials string lcName = filename.ToLower(); if (lcName.StartsWith("http:") || lcName.StartsWith("https:") || lcName.StartsWith("ftp:")) { XmlReaderSettings readerSettings = new XmlReaderSettings(); readerSettings.ProhibitDtd = false; readerSettings.ValidationType = ValidationType.DTD; if (username != null) { XmlUrlResolver resolver = new XmlUrlResolver(); resolver.Credentials = new NetworkCredential(username, password); readerSettings.XmlResolver = resolver; } XmlReader xmlReader = XmlReader.Create(filename, readerSettings); while (xmlReader.Read()) // nothing - just make sure can read all ; return; } // if anon - try it if (username == null) { XmlReaderSettings readerSettings = new XmlReaderSettings(); readerSettings.ProhibitDtd = false; readerSettings.ValidationType = ValidationType.DTD; XmlReader xmlReader = XmlReader.Create(filename, readerSettings); while (xmlReader.Read()) // nothing - just make sure can read all ; return; } // get the username & domain string domain, user; int pos = username.IndexOf('\\'); if (pos == -1) pos = username.IndexOf('/'); bool onlyDomain; if (pos != -1) { onlyDomain = true; domain = username.Substring(0, pos); user = username.Substring(pos + 1); } else { onlyDomain = false; WindowsIdentity ident = WindowsIdentity.GetCurrent(); Trap.trap(ident == null); if (ident == null) domain = null; else { domain = ident.Name; pos = domain.IndexOf('\\'); if (pos != -1) domain = domain.Substring(0, pos); } user = username; } // first we try a domain set of credentials if (domain != null) { try { TestAsDomainUser(filename, domain, user, password); return; } catch (Exception) { if (onlyDomain) throw; } } if (onlyDomain) throw new IOException("Could not test: " + filename + " as user: " + domain + "\\" + user); // second we try as a local user on this system TestAsDomainUser(filename, ".", user, password); } private static XmlDocument OpenWithIdentity(string filename, WindowsIdentity identity) { WindowsImpersonationContext impersonationContext = identity.Impersonate(); try { XmlDocument xmlDoc = new XmlDocument(); xmlDoc.Load(filename); return xmlDoc; } finally { impersonationContext.Undo(); impersonationContext.Dispose(); } } private static XmlDocument OpenAsDomainUser(string filename, string domain, string username, string password) { IntPtr tokenHandle = new IntPtr(0); bool returnValue = LogonUser(username, domain, password, LOGON32_LOGON_NEW_CREDENTIALS, LOGON32_PROVIDER_DEFAULT, ref tokenHandle); if (returnValue == false) return null; try { WindowsIdentity identity = new WindowsIdentity(tokenHandle); return OpenWithIdentity(filename, identity); } finally { CloseHandle(tokenHandle); } } private static void TestAsDomainUser(string filename, string domain, string username, string password) { IntPtr tokenHandle = new IntPtr(0); bool returnValue = LogonUser(username, domain, password, LOGON32_LOGON_NEW_CREDENTIALS, LOGON32_PROVIDER_DEFAULT, ref tokenHandle); if (returnValue == false) throw new IOException("Could not login as user: " + domain + "\\" + username); try { WindowsIdentity identity = new WindowsIdentity(tokenHandle); WindowsImpersonationContext impersonationContext = identity.Impersonate(); try { XmlReaderSettings readerSettings = new XmlReaderSettings(); readerSettings.ProhibitDtd = false; readerSettings.ValidationType = ValidationType.DTD; XmlReader xmlReader = XmlReader.Create(filename, readerSettings); while (xmlReader.Read()) // nothing - just make sure can read all ; } finally { impersonationContext.Undo(); impersonationContext.Dispose(); } } finally { CloseHandle(tokenHandle); } } /* private static XmlDocument OpenAsRemoteUser(string share, string filename, string username, string password) { USE_INFO_2 useInfo = new USE_INFO_2(); useInfo.ui2_remote = share; useInfo.ui2_password = password; useInfo.ui2_asg_type = 0; //disk drive useInfo.ui2_usecount = 1; useInfo.ui2_username = username; useInfo.ui2_domainname = "."; uint paramErrorIndex; uint returnCode = NetUseAdd(String.Empty, 2, ref useInfo, out paramErrorIndex); if (returnCode != 0) throw new Win32Exception((int)returnCode); XmlDocument xmlDoc = new XmlDocument(); xmlDoc.Load(filename); // NetUseDel() return xmlDoc; } */ } }