0

Issue

I have a method, which I can call but it is never executed.

The line which calls 'CreateHomeDrive' looks as follows:

FileInterface.HomeDriveCreation.CreateHomeDrive("my_domain","admin_user","admin_pwd", document.UserName);

The line is successfully reached and the corresponding break pointer halts

The following Break pointer should be reached but is never reached.
There is only 1 method referring to this function:

public class HomeDriveCreation
{
    public static void CreateHomeDrive(string domainName, string adminUser, string adminPassword, string userNameToCreate)
        {
            {/*Break pint here is never reached!*/ }
            // method logic here...
        }
}

Any idea on why this code is never reached? The break pointer is in the first line of the function so this break marker should alsways be reached from my understanding.

There is indeed an error thrown, which does not help me:

One or more errors occurred. 
(Could not load type 'System.Security.Principal.WindowsImpersonationContext' 
from assembly 'mscorlib, Version=4.0.0.0, Culture=neutral, 
PublicKeyToken=/*private?*/'.)

Project Structure

The Project structure looks like the following:
enter image description here

FileInterface is a .net Framework class library,
JiraBackgroundTasks which calls FileInterface.HomeDriveCreation is a .net core 3.5 class library.

Reference to System.Security is added enter image description here

Specific class which should be called

the Code to be called is derived from the Microsoft reference for impersonation https://learn.microsoft.com/en-us/dotnet/api/system.security.principal.windowsidentity.impersonate?redirectedfrom=MSDN&view=netframework-4.8#System_Security_Principal_WindowsIdentity_Impersonate_System_IntPtr_ This is the full class:

using System;
using System.Runtime.InteropServices;
using System.Security.Principal;
using System.Security.Permissions;
using Microsoft.Win32.SafeHandles;
using System.Runtime.ConstrainedExecution;
using System.Security;
using System.IO;
using System.Security.AccessControl;

namespace FileInterface
{
    public class HomeDriveCreation
    {
        [DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
        public static extern bool LogonUser(String lpszUsername, String lpszDomain, String lpszPassword,
            int dwLogonType, int dwLogonProvider, out SafeTokenHandle phToken);

        [DllImport("kernel32.dll", CharSet = CharSet.Auto)]
        public extern static bool CloseHandle(IntPtr handle);

        // Test harness.
        // If you incorporate this code into a DLL, be sure to demand FullTrust.
        [PermissionSetAttribute(SecurityAction.Demand, Name = "FullTrust")]
        public static void CreateHomeDrive(string domainName, string adminUser, string adminPassword, string userNameToCreate)
        {
            { }
            SafeTokenHandle safeTokenHandle;
            const int LOGON32_PROVIDER_DEFAULT = 0;
            //This parameter causes LogonUser to create a primary token.
            const int LOGON32_LOGON_INTERACTIVE = 2;

            // Call LogonUser to obtain a handle to an access token.
            bool returnValue = LogonUser(adminUser, domainName, adminPassword,
                LOGON32_LOGON_INTERACTIVE, LOGON32_PROVIDER_DEFAULT,
                out safeTokenHandle);

            if (false == returnValue)
            {
                int ret = Marshal.GetLastWin32Error();
                throw new System.ComponentModel.Win32Exception(ret);
            }
            using (safeTokenHandle)
            {
                // Use the token handle returned by LogonUser.
                using (WindowsImpersonationContext impersonatedUser = WindowsIdentity.Impersonate(safeTokenHandle.DangerousGetHandle()))
                {
                    string path = $@"\\server\Data\Home\{userNameToCreate}";
                    if (!Directory.Exists(path))
                    {
                        Directory.CreateDirectory(path);
                        AddDirectorySecurity(path, $@"{domainName}\{userNameToCreate}", FileSystemRights.ReadAndExecute, AccessControlType.Allow);
                        AddDirectorySecurity(path, $@"{domainName}\{userNameToCreate}", FileSystemRights.Write, AccessControlType.Allow);
                        AddDirectorySecurity(path, $@"{domainName}\{userNameToCreate}", FileSystemRights.DeleteSubdirectoriesAndFiles, AccessControlType.Allow);
                        AddDirectorySecurity(path, $@"{domainName}\{userNameToCreate}", FileSystemRights.FullControl, AccessControlType.Allow);
                    }
                }
                // Releasing the context object stops the impersonation
                // Check the identity.
            }
        }
        // Adds an ACL entry on the specified directory for the specified account.
        public static void AddDirectorySecurity(string FileName, string Account, FileSystemRights Rights, AccessControlType ControlType)
        {
            // Create a new DirectoryInfo object.
            DirectoryInfo dInfo = new DirectoryInfo(FileName);

            // Get a DirectorySecurity object that represents the
            // current security settings.
            DirectorySecurity dSecurity = dInfo.GetAccessControl();

            // Add the FileSystemAccessRule to the security settings.
            dSecurity.AddAccessRule(new FileSystemAccessRule(Account,
                                                            Rights,
                                                            ControlType));

            // Set the new access settings.
            dInfo.SetAccessControl(dSecurity);
        }
    }
    public sealed class SafeTokenHandle : SafeHandleZeroOrMinusOneIsInvalid
    {
        private SafeTokenHandle()
            : base(true)
        {
        }

        [DllImport("kernel32.dll")]
        [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
        [SuppressUnmanagedCodeSecurity]
        [return: MarshalAs(UnmanagedType.Bool)]
        private static extern bool CloseHandle(IntPtr handle);

        protected override bool ReleaseHandle()
        {
            return CloseHandle(handle);
        }
    }
}
julian bechtold
  • 1,875
  • 2
  • 19
  • 49
  • Maybe the reference is missing? The reference to System.Security is not a standard reference and has to be added manually to the project. Although the code snippet you have given does not reference System.Security.Principal.WindowsImpersonationContext. More of your source code might help in understanding the problem. – ChronosMOT Jul 23 '21 at 13:48
  • there is just this single class in the project. which additional info might help? – julian bechtold Jul 23 '21 at 13:54
  • it is a project (.net Framework class library in vs); apparently mscorlib.dll is not included and i cant add it as a reference since visual studio says its added per default – julian bechtold Jul 23 '21 at 14:07
  • You can add references to your project in vs by rightclicking the references item in the solution explorer and then "Add reference ..." in the context menu. There you have a list of the System Assemblies. Look for System.Security it should have a checkmark in the first column. – ChronosMOT Jul 23 '21 at 14:16
  • In reguard to the additional information. The error you are getting implies that your code references WindowsImpersonationContext in some way. That reference is missing from the source code you posted, which makes it hard to understand how this error happens and how it might cause your problem. My guess is, that their might be a static field of that type in your HomeDriveCreation. Then some static + library voodoo happens. I believe static fields are only initiated when the first reference to the class is reached. Then at your method invocation the static constructor is executed and crashes. – ChronosMOT Jul 23 '21 at 14:22
  • I added the Project structure and reference fore more clarity. – julian bechtold Jul 23 '21 at 14:47
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/235234/discussion-between-chronosmot-and-julian-bechtold). – ChronosMOT Jul 23 '21 at 15:34
  • FYI it's called a breakpoint (one word) not a break pointer and not a break pint. (A break *pint* is something you drink on Friday afternoons.) – John Wu Jul 23 '21 at 16:19
  • Did my answer solve your problem? If so please mark it as accepted answer and if not please tell me what issue remains. – ChronosMOT Aug 01 '21 at 12:30

2 Answers2

0

WindowsImpersonationContext is specific to .Net Framework. Note the "applies to" section in the article you posted.

For .Net Core you can use WindowsIdentity.RunImpersonated .

See also SO: WindowsImpersonationContext & Impersonate() not found in ASP.Core

ChronosMOT
  • 341
  • 5
  • 17
0

The issue in this case is that impersonation is handled differently in .net core

Eventhough the project is .net framework, Visual Studio has trouble matching requirements of multi framework targeting solutions.

More Information on how to impersonate in .net core can be found here: https://learn.microsoft.com/en-us/dotnet/api/system.security.principal.windowsidentity.runimpersonated?view=net-5.0

As for the specific task of impersonating Folder access permissions, a great answer has been found here: How to provide user name and password when connecting to a network share (answer from Luke Quinane)

Keep in mind, that this will not work when the network location is mapped under a different user already in Windows. In this case the program will map the network drive internally and dispose it after use.

implement his class and then use it like:

using (new NetworkConnection(@"\\server\Data\Home", cred))
{
    string path = $@"\\server\Data\Home\testuserfolder";
    string domainName = "domain.com";
    string userNameToCreate = "testuser";
    Directory.CreateDirectory(path);
    SetFullPermission(path, userNameToCreate);
}
julian bechtold
  • 1,875
  • 2
  • 19
  • 49