Search code examples
c#functionmethodscallbreakpoints

Function cannot be called, no breakpoint hit, - could not load assembly from mscorlib


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);
        }
    }
}

Solution

  • 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);
    }