none
Weird behavior of LogonUser - Why do I get "Access denied" on the 2nd File.AppendAllText() ? RRS feed

  • Question

  • using System;
    using System.Data;
    using System.Data.SqlClient ;
    using System.Threading;
    using System.Collections.Generic;
    using System.Runtime.InteropServices;
    using System.Security;
    using System.Security.Permissions;
    using System.Security.Principal;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    using System.Globalization;
    using System.ComponentModel;
    using System.IO;
    using System.Xml;
    using System.Collections;
    using System.Diagnostics;
    using System.Web;
    using System.Security.Policy;
    
    
    namespace Z
    {
        public class SimulateUser2 : IDisposable
        {
            const int LOGON32_PROVIDER_DEFAULT = 0;
            const int LOGON32_LOGON_INTERACTIVE = 2;
            const int LOGON32_LOGON_NETWORK = 3;
    
            WindowsImpersonationContext _wic = null;
    
            public string _loginID = "";
            public string _domain = "";
            public string _password = "";
    
            [DllImport("advapi32.dll", SetLastError = true)]
            private static extern bool LogonUser(string lpszloginID,
                string lpszDomain, string lpszPassword, int dwLogonType,
                int dwLogonProvider, ref IntPtr phToken);
    
            [DllImport("kernel32.dll", CharSet = CharSet.Auto)]
            private static extern bool CloseHandle(IntPtr handle);
    
            public SimulateUser2(string loginID, string domain, string password)
            {
                this._loginID = loginID;
                this._domain = domain;
                this._password = password;
            }
    
            public void Impersonate()
            {
                _wic = this.Logon().Impersonate();
            }
    
            public void Revert()
            {
                if (_wic != null)
                    _wic.Undo();
                _wic = null;
            }
    
            public WindowsIdentity Logon()
            {
                // Initialize pointer.
                IntPtr windowsIdentityHandle = new IntPtr(0);
                windowsIdentityHandle = IntPtr.Zero;
    
                bool success = LogonUser(this._loginID, this._domain,
                    this._password, LOGON32_LOGON_INTERACTIVE, LOGON32_PROVIDER_DEFAULT,
                    ref windowsIdentityHandle);
    
                // Throw an exception if the logon fails.
                if (success == false)
                {
                    int error = Marshal.GetLastWin32Error();
                    throw new Exception("LogonUser failed. Error: " + error);
                }
    
                WindowsIdentity windowsIdentity =
                    new WindowsIdentity(windowsIdentityHandle, "NTLM", System.Security.Principal.WindowsAccountType.Normal, true);
                CloseHandle(windowsIdentityHandle);
                return (windowsIdentity);
            }
    
            void IDisposable.Dispose()
            {
                Revert();
            }
        }
    
    
        class Z
        {
            static void Main(string[] args)
            {
                AppDomain.CurrentDomain.SetPrincipalPolicy(PrincipalPolicy.WindowsPrincipal);
                WindowsIdentity id = WindowsIdentity.GetCurrent();
                WindowsPrincipal wPrincipal = new WindowsPrincipal(id);
                bool st = wPrincipal.IsInRole(@"TEST\YYY Setup Group");
                bool sta = wPrincipal.IsInRole(@"BUILTIN\Administrators");
                bool staa = wPrincipal.IsInRole(WindowsBuiltInRole.Administrator);
                var un2 = WindowsIdentity.GetCurrent().Name;
                var s2 = Path.GetTempPath();
                string _filename2 = Path.Combine(Path.GetTempPath(), "YYYInstall.log");
                File.AppendAllText(_filename2, "Impersonating user - BEFORE " + un2);
    
                var username = @"YYYSetup";
                var domain = @"test";
                var pwd = "1";
                using (var su = new SimulateUser2(username, domain, pwd))
                {
                    su.Impersonate();
    
                    var un = WindowsIdentity.GetCurrent().Name;
    
                    var s = Path.GetTempPath();
                    string _filename = Path.Combine(Path.GetTempPath(), "YYYInstall.log");
                    File.AppendAllText(_filename, "Impersonating user - IN " + un);
                }
    
    .NET 4.5. The user "YYYSetup" is a member of TEST\YYY Setup Group and local admins group (set explicitly via Local Users and Groups MMC). However, sta and staa come with FALSE value. And moreover, I get "Access denied" on the 2nd attempt of File.AppendAllText(). I wonder why and how to make sure that all attempts to write to the same file under impersonated account will work.
    Wednesday, December 4, 2013 7:49 AM

Answers

All replies

  • I would wager that when the file is initially created it is done using the current user's identity.  That means the file is owned by the current user.  When you impersonate the other user they don't have (NTFS) permissions to the original file so you get an access denied.  You can confirm this by checking the NTFS permissions on the actual file.  If you want to create a file that is writable by multiple users then you'll need to modify the file security using SetAccessControl after you create the original file and give everyone (or at least the simulated account) write permissions.

    Michael Taylor
    http://msmvps.com/blogs/p3net

    Wednesday, December 4, 2013 3:09 PM
    Moderator
  • Ok, but what about the glitch with IsInRole() ?
    Thursday, December 5, 2013 11:17 AM
  • I don't think there is a glitch.  Please confirm that the user identity that you're getting back in both cases is the same user.  If it is then we can discuss why you're getting different results.  At this point though we need to know where the implementation of WindowsPrincipal.current is coming from and whether it is returning the same results as the standard .NET approach.  I don't believe it is.
    Thursday, December 5, 2013 2:45 PM
    Moderator
  • What do you mean under "bot cases"? The user outside of "using" clause and inside?
    Thursday, December 5, 2013 3:18 PM
  • Sorry, I'm mixing up this thread with the other thread about the IsInRole issue that you posted.  Please do not mix up the discussions of these threads.  This thread you started has a subject about access denied so it should be limited to that specific issue.  It is not impacted by IsInRole or anything like that most likely but rather a simple NTFS permissions issue.  The other thread you started about the odd behavior of IsInRole can handle the problem with IsInRole you're having. 

    Did you check the NTFS permissions and confirm that you were giving write access to the user you are impersonating?  By default only the create user will have permissions to write to the file.  Also be aware that Path.GetTempPath is not what you want to call.  The returned path is going to be inside the original user's directory and therefore will not be accessible by other users.  You need to store the file in a user-neutral location (the system temp directory might be a good place).  Once you fix the path and modify the write permissions your error should go away.

    Thursday, December 5, 2013 3:47 PM
    Moderator
  • According to NTFS permissions I saw in the file properties local admins had full control over the file. Obviously, the user used for impersonation is not considered as a member of  BUILTIN\Administrators and gets this exception. But why he's not a member of the group despite of the fact I put him there explicitly I don't know.

    Thursday, December 5, 2013 6:58 PM
  • If you're running Vista+ then the user would need to be running elevated otherwise their token isn't in the group.  If you google for LogonUser you'll get some additional information about the restricted token being returned.
    Thursday, December 5, 2013 7:51 PM
    Moderator
  • I changed LogonUser(LOGON32_LOGON_INTERACTIVE) for LogonUser(LOGON32_LOGON_NETWORK) and it started recognizing my second user as a local admin! Its' working like a charm now. I'd like to know why it's so different in terms of behavior because the MSDN article is pretty vague about this parameter and how it affect the requested token.
    Tuesday, December 10, 2013 6:35 PM
  • The Compatibility Team posted a blog entry about the basics of the behavior.  It ultimately seems to boil down to whether the user is authenticated via the network or locally (interactive).  UAC tends to put unexpected wrinkles in a lot of things.

    Michael Taylor
    http://msmvps.com/blogs/p3net

    Tuesday, December 10, 2013 8:13 PM
    Moderator