c# networkconnection - Как указать имя пользователя и пароль при подключении к сетевому ресурсу




wnetaddconnection2 to (10)

При подключении к сетевому ресурсу, для которого текущий пользователь (в моем случае, пользователь службы с включенной сетью) не имеет прав, имя и пароль должны быть предоставлены.

Я знаю, как это сделать с помощью функций Win32 (семейство WNet* из mpr.dll ), но хотелось бы сделать это с помощью функции .Net (2.0).

Какие варианты доступны?

Возможно, дополнительная информация помогает:

  • Вариант использования - это служба Windows, а не приложение Asp.Net.
  • Служба работает под учетной записью, которая не имеет прав на эту долю.
  • Учетная запись пользователя, необходимая для этого ресурса, неизвестна на стороне клиента.
  • Клиент и сервер не являются членами одного домена.

Answers

Я искал множество методов, и я сделал это по-своему. Вы должны открыть соединение между двумя машинами с помощью командной строки NET USE, и после завершения вашей работы очистите соединение с помощью командной строки NET USE «myconnection» / delete.

Вы должны использовать процесс командной строки из кода следующим образом:

var savePath = @"\\servername\foldername\myfilename.jpg";
var filePath = @"C:\\temp\myfileTosave.jpg";

Использование прост:

SaveACopyfileToServer(filePath, savePath);

Вот функции:

using System.IO
using System.Diagnostics;


public static void SaveACopyfileToServer(string filePath, string savePath)
    {
        var directory = Path.GetDirectoryName(savePath).Trim();
        var username = "loginusername";
        var password = "loginpassword";
        var filenameToSave = Path.GetFileName(savePath);

        if (!directory.EndsWith("\\"))
            filenameToSave = "\\" + filenameToSave;

        var command = "NET USE " + directory + " /delete";
        ExecuteCommand(command, 5000);

        command = "NET USE " + directory + " /user:" + username + " " + password;
        ExecuteCommand(command, 5000);

        command = " copy \"" + filePath + "\"  \"" + directory + filenameToSave + "\"";

        ExecuteCommand(command, 5000);


        command = "NET USE " + directory + " /delete";
        ExecuteCommand(command, 5000);
    }

А также функция ExecuteCommand:

public static int ExecuteCommand(string command, int timeout)
    {
        var processInfo = new ProcessStartInfo("cmd.exe", "/C " + command)
                              {
                                  CreateNoWindow = true, 
                                  UseShellExecute = false, 
                                  WorkingDirectory = "C:\\",
                              };

        var process = Process.Start(processInfo);
        process.WaitForExit(timeout);
        var exitCode = process.ExitCode;
        process.Close();
        return exitCode;
    } 

Эти функции работали очень быстро и стабильно для меня.


Один из вариантов, который может работать, - это использовать WindowsIdentity.Impersonate (и изменить главный поток), чтобы стать желаемым пользователем. Вернемся к p / invoke, хотя, боюсь ...

Еще один нахальный (и в равной степени далекий от идеального) вариант может состоять в том, чтобы создать процесс для выполнения работы ... ProcessStartInfo принимает имя .UserName , .Password и .Domain .

Наконец - возможно, запустите службу в отдельной учетной записи, имеющей доступ? (удалены, поскольку вы уточнили, что это не вариант).


Сегодня 7 лет спустя я столкнулся с той же проблемой, и мне хотелось бы поделиться своей версией решения.

Готово копировать и вставлять :-) Вот он:

Шаг 1

В вашем коде (когда вам нужно что-то делать с разрешениями)

ImpersonationHelper.Impersonate(domain, userName, userPassword, delegate
                            {
                                //Your code here 
                                //Let's say file copy:
                                if (!File.Exists(to))
                                {
                                    File.Copy(from, to);
                                }
                            });

Шаг 2

Файл помощника, который делает магию

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


namespace BlaBla
{
    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);
        }
    }

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

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

        [PermissionSet(SecurityAction.Demand, Name = "FullTrust")]
        public static void Impersonate(string domainName, string userName, string userPassword, Action actionToExecute)
        {
            SafeTokenHandle safeTokenHandle;
            try
            {

                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(userName, domainName, userPassword,
                    LOGON32_LOGON_INTERACTIVE, LOGON32_PROVIDER_DEFAULT,
                    out safeTokenHandle);
                //Facade.Instance.Trace("LogonUser called.");

                if (returnValue == false)
                {
                    int ret = Marshal.GetLastWin32Error();
                    //Facade.Instance.Trace($"LogonUser failed with error code : {ret}");

                    throw new System.ComponentModel.Win32Exception(ret);
                }

                using (safeTokenHandle)
                {
                    //Facade.Instance.Trace($"Value of Windows NT token: {safeTokenHandle}");
                    //Facade.Instance.Trace($"Before impersonation: {WindowsIdentity.GetCurrent().Name}");

                    // Use the token handle returned by LogonUser.
                    using (WindowsIdentity newId = new WindowsIdentity(safeTokenHandle.DangerousGetHandle()))
                    {
                        using (WindowsImpersonationContext impersonatedUser = newId.Impersonate())
                        {
                            //Facade.Instance.Trace($"After impersonation: {WindowsIdentity.GetCurrent().Name}");
                            //Facade.Instance.Trace("Start executing an action");

                            actionToExecute();

                            //Facade.Instance.Trace("Finished executing an action");
                        }
                    }
                    //Facade.Instance.Trace($"After closing the context: {WindowsIdentity.GetCurrent().Name}");
                }

            }
            catch (Exception ex)
            {
                //Facade.Instance.Trace("Oh no! Impersonate method failed.");
                //ex.HandleException();
                //On purpose: we want to notify a caller about the issue /Pavel Kovalev 9/16/2016 2:15:23 PM)/
                throw;
            }
        }
    }
}

Хорошо ... я могу разглядеть ..

Отказ от ответственности: у меня был только 18-часовой рабочий день (опять). Я стар и забываю .. Я не могу писать. У меня короткий охват внимания, поэтому я лучше реагирую быстро. :-)

Вопрос:

Можно ли изменить принцип потока для пользователя без учетной записи на локальном компьютере?

Ответ:

Да, вы можете изменить принцип потока, даже если учетные данные, которые вы используете, не определены локально или находятся за пределами «леса».

Я просто столкнулся с этой проблемой при попытке подключиться к серверу SQL с использованием аутентификации NTLM из службы. Этот вызов использует учетные данные, связанные с процессом, что означает, что вам нужно либо локальную учетную запись, либо учетную запись домена для аутентификации, прежде чем вы сможете выдавать себя за нее. Бла, бла ...

Но...

Вызов LogonUser (..) с атрибутом ???? NEW_CREDENTIALS вернет маркер безопасности, не пытаясь аутентифицировать учетные данные. Kewl .. Не нужно определять учетную запись в «лесу». После того, как у вас есть токен, вам может потребоваться вызвать DuplicateToken () с возможностью включения олицетворения, приводящего к новому токену. Теперь вызовите SetThreadToken (NULL, токен); (Это может быть и токен?). Вызов ImpersonateLoggedonUser (токен); может потребоваться, но я так не думаю. Поищи это..

Сделайте то, что вам нужно сделать.

Вызовите RevertToSelf (), если вы вызвали ImpersonateLoggedonUser (), затем SetThreadToken (NULL, NULL); (Я думаю ... посмотрю), а затем CloseHandle () на созданных ручках.

Никаких обещаний, но это сработало для меня ... Это от головы (как у моих волос), и я не могу заклинать !!!


Для VB.lovers эквивалент VB.NET кода Люка Куинане (спасибо Люку!)

Imports System
Imports System.Net
Imports System.Runtime.InteropServices
Imports System.ComponentModel

Public Class NetworkConnection
    Implements IDisposable

    Private _networkName As String

    Public Sub New(networkName As String, credentials As NetworkCredential)
        _networkName = networkName

        Dim netResource = New NetResource() With {
             .Scope = ResourceScope.GlobalNetwork,
             .ResourceType = ResourceType.Disk,
             .DisplayType = ResourceDisplaytype.Share,
             .RemoteName = networkName
        }

        Dim userName = If(String.IsNullOrEmpty(credentials.Domain), credentials.UserName, String.Format("{0}\{1}", credentials.Domain, credentials.UserName))

        Dim result = WNetAddConnection2(NetResource, credentials.Password, userName, 0)

        If result <> 0 Then
            Throw New Win32Exception(result, "Error connecting to remote share")
        End If
    End Sub

    Protected Overrides Sub Finalize()
        Try
            Dispose (False)
        Finally
            MyBase.Finalize()
        End Try
    End Sub

    Public Sub Dispose() Implements IDisposable.Dispose
        Dispose (True)
        GC.SuppressFinalize (Me)
    End Sub

    Protected Overridable Sub Dispose(disposing As Boolean)
        WNetCancelConnection2(_networkName, 0, True)
    End Sub

    <DllImport("mpr.dll")> _
    Private Shared Function WNetAddConnection2(netResource As NetResource, password As String, username As String, flags As Integer) As Integer
    End Function

    <DllImport("mpr.dll")> _
    Private Shared Function WNetCancelConnection2(name As String, flags As Integer, force As Boolean) As Integer
    End Function

End Class

<StructLayout(LayoutKind.Sequential)> _
Public Class NetResource
    Public Scope As ResourceScope
    Public ResourceType As ResourceType
    Public DisplayType As ResourceDisplaytype
    Public Usage As Integer
    Public LocalName As String
    Public RemoteName As String
    Public Comment As String
    Public Provider As String
End Class

Public Enum ResourceScope As Integer
    Connected = 1
    GlobalNetwork
    Remembered
    Recent
    Context
End Enum

Public Enum ResourceType As Integer
    Any = 0
    Disk = 1
    Print = 2
    Reserved = 8
End Enum

Public Enum ResourceDisplaytype As Integer
    Generic = &H0
    Domain = &H1
    Server = &H2
    Share = &H3
    File = &H4
    Group = &H5
    Network = &H6
    Root = &H7
    Shareadmin = &H8
    Directory = &H9
    Tree = &HA
    Ndscontainer = &HB
End Enum

Вы можете либо изменить идентификатор потока, либо P / Invoke WNetAddConnection2. Я предпочитаю последнее, поскольку иногда мне нужно поддерживать несколько учетных данных для разных местоположений. Я переношу его в IDisposable и вызываю WNetCancelConnection2 для удаления учетных записей впоследствии (избегая ошибки нескольких пользователей):

using (new NetworkConnection(@"\\server\read", readCredentials))
using (new NetworkConnection(@"\\server2\write", writeCredentials)) {
   File.Copy(@"\\server\read\file", @"\\server2\write\file");
}

Если вы не можете создать локально допустимый токен безопасности, похоже, что вы выбрали все панели параметров Win32 API и WNetAddConnection *.

Тонны информации о MSDN о WNet-PInvoke и пример кода, который подключается к UNC-пути здесь:

http://www.pinvoke.net/default.aspx/mpr/WNetAddConnection2.html#

MSDN Ссылка здесь:

http://msdn.microsoft.com/en-us/library/aa385391(VS.85).aspx


Вы должны выглядеть так:

<identity impersonate="true" userName="domain\user" password="****" />

В ваш web.config.

Больше информации.


Мне очень понравился ответ что я сделал свою собственную быструю реализацию. Вот, если кто-то еще нуждается в этом в спешке:

public class NetworkConnection : IDisposable
{
    string _networkName;

    public NetworkConnection(string networkName, 
        NetworkCredential credentials)
    {
        _networkName = networkName;

        var netResource = new NetResource()
        {
            Scope = ResourceScope.GlobalNetwork,
            ResourceType = ResourceType.Disk,
            DisplayType = ResourceDisplaytype.Share,
            RemoteName = networkName
        };

        var userName = string.IsNullOrEmpty(credentials.Domain)
            ? credentials.UserName
            : string.Format(@"{0}\{1}", credentials.Domain, credentials.UserName);

        var result = WNetAddConnection2(
            netResource, 
            credentials.Password,
            userName,
            0);

        if (result != 0)
        {
            throw new Win32Exception(result, "Error connecting to remote share");
        }   
    }

    ~NetworkConnection()
    {
        Dispose(false);
    }

    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }

    protected virtual void Dispose(bool disposing)
    {
        WNetCancelConnection2(_networkName, 0, true);
    }

    [DllImport("mpr.dll")]
    private static extern int WNetAddConnection2(NetResource netResource, 
        string password, string username, int flags);

    [DllImport("mpr.dll")]
    private static extern int WNetCancelConnection2(string name, int flags,
        bool force);
}

[StructLayout(LayoutKind.Sequential)]
public class NetResource
{
    public ResourceScope Scope;
    public ResourceType ResourceType;
    public ResourceDisplaytype DisplayType;
    public int Usage;
    public string LocalName;
    public string RemoteName;
    public string Comment;
    public string Provider;
}

public enum ResourceScope : int
{
    Connected = 1,
    GlobalNetwork,
    Remembered,
    Recent,
    Context
};

public enum ResourceType : int
{
    Any = 0,
    Disk = 1,
    Print = 2,
    Reserved = 8,
}

public enum ResourceDisplaytype : int
{
    Generic = 0x0,
    Domain = 0x01,
    Server = 0x02,
    Share = 0x03,
    File = 0x04,
    Group = 0x05,
    Network = 0x06,
    Root = 0x07,
    Shareadmin = 0x08,
    Directory = 0x09,
    Tree = 0x0a,
    Ndscontainer = 0x0b
}

// For formatting string and parameters define a function
// The shortcut function wl, kind of write line
public void wl( string format, params object[] parms){
    Console.WriteLine(format, parms);
}

// Just for strings we can use Action delegate
Action<string> ws = Console.WriteLine;

// examples:
ws("String with no formatting parameters");

wl("String without formatting parameters");
wl("String with {0} parameters {1}", 2, "included");
wl("several parameters {0} {1} {2} repeated {0}", 1234, 5678, 6543);

или используя метод расширения: formatString.wl (аргументы ...)

public static class ConsoleWriteExtensions
{
    public static void wl(this string format, params object[] parms){
        Console.WriteLine(format, parms);
    }
}

"{0} -> {1}".wl("Mili",123.45); // prints Mili -> 123.45




c# .net winapi networking passwords