Доступ до спільного файлу (UNC) з віддаленого довіреного домену з обліковими даними


151

Ми зіткнулися з цікавою ситуацією, яка потребує вирішення, і мої пошуки виявилися нікчемними. Тому я звертаюся до громади ЗП за допомогою.

Проблема полягає в тому, що у нас є необхідність програмного доступу до спільного файлу, який відсутній у нашому домені та не знаходиться в довіреному зовнішньому домені через віддалений обмін файлами / UNC. Природно, нам потрібно надати облікові дані віддаленій машині.

Зазвичай цю проблему вирішують одним із двох способів:

  1. Зобразіть спільний доступ до файлів як дисковод та надайте облікові дані на той час. Зазвичай це робиться за допомогою NET USEкоманди або функцій Win32, які дублюються NET USE.
  2. Отримайте доступ до файлу за допомогою шляху UNC так, ніби віддалений комп'ютер був у домені та переконайтесь, що обліковий запис, під яким працює програма, дублюється (включаючи пароль) на віддаленій машині як локальний користувач. В основному, використовуйте той факт, що Windows автоматично надасть облікові дані поточного користувача, коли користувач намагається отримати доступ до спільного файлу.
  3. Не використовуйте віддалений обмін файлами. Використовуйте FTP (або якийсь інший спосіб), щоб перенести файл, працювати над ним локально, а потім перенести його назад.

З різних та різних причин наші архітектори з безпеки / мережі відхилили перші два підходи. Другий підхід, очевидно, є безпечним отвором; якщо віддалений комп'ютер порушений, локальний комп'ютер зараз під загрозою. Перший підхід незадовільний, оскільки щойно змонтований накопичувач - це спільний ресурс, доступний для інших програм на локальному комп'ютері під час доступу до файлів програмою. Незважаючи на те, що зробити це тимчасово цілком можливо, це все-таки є діркою їхньої думки.

Вони відкриті для третього варіанту, але віддалений адміністратор мережі наполягає на SFTP, а не на FTPS, і FtpWebRequest підтримує лише FTPS. SFTP є більш зручним для брандмауера варіантом, і є кілька бібліотек, які я міг би використовувати для такого підходу, але я вважаю за краще зменшити свої залежності, якщо зможу.

Я шукав MSDN як керованого, так і win32 способу використання віддаленого спільного використання файлів, але мені не вдалося придумати нічого корисного.

І тому я запитую: чи є інший шлях? Чи пропустив я суперсекретну функцію win32, яка робить те, що я хочу? Або я повинен переслідувати якийсь варіант варіанту 3?


Я вирішив це за допомогою підходу під себе, але це між двома машинами поза доменом. Я не знаю, чи не було б проблеми з розмовою від домену до комп'ютера поза доменом. stackoverflow.com/questions/17221476 / ...
Wolf5

Відповіді:


174

Спосіб вирішення вашої проблеми полягає у використанні Win32 API під назвою WNetUseConnection .
Використовуйте цю функцію для підключення до шляху UNC з аутентифікацією, а не для відображення диска .

Це дозволить вам підключитися до віддаленої машини, навіть якщо він не знаходиться в одному домені, і навіть якщо він має інше ім’я користувача та пароль.

Після використання WNetUseConnection ви зможете отримати доступ до файлу через шлях UNC, як якщо б ви були на одному домені. Найкращий спосіб - це, мабуть, адміністративний вбудований в акції.
Приклад: \\ ім'я комп'ютера \ c $ \ програмні файли \ папка \ file.txt

Ось приклад C # коду, який використовує WNetUseConnection.
Зауважте, що для NetResource вам слід пропустити null для lpLocalName та lpProvider. Тип dwType повинен бути RESOURCETYPE_DISK. LpRemoteName має бути \\ ComputerName.

using System;
using System.Runtime.InteropServices ;
using System.Threading;

namespace ExtremeMirror
{
    public class PinvokeWindowsNetworking
    {
        #region Consts
        const int RESOURCE_CONNECTED = 0x00000001;
        const int RESOURCE_GLOBALNET = 0x00000002;
        const int RESOURCE_REMEMBERED = 0x00000003;

        const int RESOURCETYPE_ANY = 0x00000000;
        const int RESOURCETYPE_DISK = 0x00000001;
        const int RESOURCETYPE_PRINT = 0x00000002;

        const int RESOURCEDISPLAYTYPE_GENERIC = 0x00000000;
        const int RESOURCEDISPLAYTYPE_DOMAIN = 0x00000001;
        const int RESOURCEDISPLAYTYPE_SERVER = 0x00000002;
        const int RESOURCEDISPLAYTYPE_SHARE = 0x00000003;
        const int RESOURCEDISPLAYTYPE_FILE = 0x00000004;
        const int RESOURCEDISPLAYTYPE_GROUP = 0x00000005;

        const int RESOURCEUSAGE_CONNECTABLE = 0x00000001;
        const int RESOURCEUSAGE_CONTAINER = 0x00000002;


        const int CONNECT_INTERACTIVE = 0x00000008;
        const int CONNECT_PROMPT = 0x00000010;
        const int CONNECT_REDIRECT = 0x00000080;
        const int CONNECT_UPDATE_PROFILE = 0x00000001;
        const int CONNECT_COMMANDLINE = 0x00000800;
        const int CONNECT_CMD_SAVECRED = 0x00001000;

        const int CONNECT_LOCALDRIVE = 0x00000100;
        #endregion

        #region Errors
        const int NO_ERROR = 0;

        const int ERROR_ACCESS_DENIED = 5;
        const int ERROR_ALREADY_ASSIGNED = 85;
        const int ERROR_BAD_DEVICE = 1200;
        const int ERROR_BAD_NET_NAME = 67;
        const int ERROR_BAD_PROVIDER = 1204;
        const int ERROR_CANCELLED = 1223;
        const int ERROR_EXTENDED_ERROR = 1208;
        const int ERROR_INVALID_ADDRESS = 487;
        const int ERROR_INVALID_PARAMETER = 87;
        const int ERROR_INVALID_PASSWORD = 1216;
        const int ERROR_MORE_DATA = 234;
        const int ERROR_NO_MORE_ITEMS = 259;
        const int ERROR_NO_NET_OR_BAD_PATH = 1203;
        const int ERROR_NO_NETWORK = 1222;

        const int ERROR_BAD_PROFILE = 1206;
        const int ERROR_CANNOT_OPEN_PROFILE = 1205;
        const int ERROR_DEVICE_IN_USE = 2404;
        const int ERROR_NOT_CONNECTED = 2250;
        const int ERROR_OPEN_FILES  = 2401;

        private struct ErrorClass 
        {
            public int num;
            public string message;
            public ErrorClass(int num, string message) 
            {
                this.num = num;
                this.message = message;
            }
        }


        // Created with excel formula:
        // ="new ErrorClass("&A1&", """&PROPER(SUBSTITUTE(MID(A1,7,LEN(A1)-6), "_", " "))&"""), "
        private static ErrorClass[] ERROR_LIST = new ErrorClass[] {
            new ErrorClass(ERROR_ACCESS_DENIED, "Error: Access Denied"), 
            new ErrorClass(ERROR_ALREADY_ASSIGNED, "Error: Already Assigned"), 
            new ErrorClass(ERROR_BAD_DEVICE, "Error: Bad Device"), 
            new ErrorClass(ERROR_BAD_NET_NAME, "Error: Bad Net Name"), 
            new ErrorClass(ERROR_BAD_PROVIDER, "Error: Bad Provider"), 
            new ErrorClass(ERROR_CANCELLED, "Error: Cancelled"), 
            new ErrorClass(ERROR_EXTENDED_ERROR, "Error: Extended Error"), 
            new ErrorClass(ERROR_INVALID_ADDRESS, "Error: Invalid Address"), 
            new ErrorClass(ERROR_INVALID_PARAMETER, "Error: Invalid Parameter"), 
            new ErrorClass(ERROR_INVALID_PASSWORD, "Error: Invalid Password"), 
            new ErrorClass(ERROR_MORE_DATA, "Error: More Data"), 
            new ErrorClass(ERROR_NO_MORE_ITEMS, "Error: No More Items"), 
            new ErrorClass(ERROR_NO_NET_OR_BAD_PATH, "Error: No Net Or Bad Path"), 
            new ErrorClass(ERROR_NO_NETWORK, "Error: No Network"), 
            new ErrorClass(ERROR_BAD_PROFILE, "Error: Bad Profile"), 
            new ErrorClass(ERROR_CANNOT_OPEN_PROFILE, "Error: Cannot Open Profile"), 
            new ErrorClass(ERROR_DEVICE_IN_USE, "Error: Device In Use"), 
            new ErrorClass(ERROR_EXTENDED_ERROR, "Error: Extended Error"), 
            new ErrorClass(ERROR_NOT_CONNECTED, "Error: Not Connected"), 
            new ErrorClass(ERROR_OPEN_FILES, "Error: Open Files"), 
        };

        private static string getErrorForNumber(int errNum) 
        {
            foreach (ErrorClass er in ERROR_LIST) 
            {
                if (er.num == errNum) return er.message;
            }
            return "Error: Unknown, " + errNum;
        }
        #endregion

        [DllImport("Mpr.dll")] private static extern int WNetUseConnection(
            IntPtr hwndOwner,
            NETRESOURCE lpNetResource,
            string lpPassword,
            string lpUserID,
            int dwFlags,
            string lpAccessName,
            string lpBufferSize,
            string lpResult
        );

        [DllImport("Mpr.dll")] private static extern int WNetCancelConnection2(
            string lpName,
            int dwFlags,
            bool fForce
        );

        [StructLayout(LayoutKind.Sequential)] private class NETRESOURCE
        { 
            public int dwScope = 0;
            public int dwType = 0;
            public int dwDisplayType = 0;
            public int dwUsage = 0;
            public string lpLocalName = "";
            public string lpRemoteName = "";
            public string lpComment = "";
            public string lpProvider = "";
        }


        public static string connectToRemote(string remoteUNC, string username, string password) 
        {
            return connectToRemote(remoteUNC, username, password, false);
        }

        public static string connectToRemote(string remoteUNC, string username, string password, bool promptUser) 
        {
            NETRESOURCE nr = new NETRESOURCE();
            nr.dwType = RESOURCETYPE_DISK;
            nr.lpRemoteName = remoteUNC;
            //          nr.lpLocalName = "F:";

            int ret;
            if (promptUser) 
                ret = WNetUseConnection(IntPtr.Zero, nr, "", "", CONNECT_INTERACTIVE | CONNECT_PROMPT, null, null, null);
            else 
                ret = WNetUseConnection(IntPtr.Zero, nr, password, username, 0, null, null, null);

            if (ret == NO_ERROR) return null;
            return getErrorForNumber(ret);
        }

        public static string disconnectRemote(string remoteUNC) 
        {
            int ret = WNetCancelConnection2(remoteUNC, CONNECT_UPDATE_PROFILE, false);
            if (ret == NO_ERROR) return null;
            return getErrorForNumber(ret);
        }
    }
}

Чи є можливість використовувати такі функції, як явно відкривати / закривати з'єднання з мережевою машиною за допомогою поточних облікових даних, тобто без надання імені користувача та пароля? Мені спеціально цікаво закрити з'єднання після доступу до спільного доступу до файлів.
flipdoubt

Не для підключення, якщо на самому комп’ютері немає імені користувача або пароля. Для відключення впевненого, що можна. Ви можете це зробити навіть через командний рядок.
Брайан Р. Бонді

1
Привіт Брайан. Документи, на які ви посилаєтесь, говорять, що ви можете передати NULL для імені користувача та пароля, щоб використовувати поточні облікові дані. Я зроблю тестування, щоб побачити, чи працює це.
flipdoubt

Пропуск нуля для імені користувача / пароля дозволяє мені підключитися, але як я можу довести, що я відключився? Щось на сервері я можу подивитися? На сервері 2003 я можу дивитись сеанси, але список поточних сесій оновлюється так само швидко, коли моя програма не використовує ці API.
flipdoubt

Чи WNetUseConnectionслід закрити з'єднання вручну, зателефонувавши WNetCancelConnection2? Або є час очікування в режимі очікування (або якийсь інший механізм), і нам не потрібно турбуватися?
w128

123

Для людей, які шукають швидке рішення, ви можете скористатися NetworkShareAccesserнаписаним нами нещодавно (на основі цієї відповіді (велике спасибі!)):

Використання:

using (NetworkShareAccesser.Access(REMOTE_COMPUTER_NAME, DOMAIN, USER_NAME, PASSWORD))
{
    File.Copy(@"C:\Some\File\To\copy.txt", @"\\REMOTE-COMPUTER\My\Shared\Target\file.txt");
}

УВАГА: Будь ласка , абсолютно впевнений, що Disposeз NetworkShareAccesserназивається (навіть якщо додаток падає!), В іншому випадку відкрите з'єднання буде залишатися на Windows. Ви можете побачити всі відкриті з'єднання, відкривши cmdпідказку та ввівши net use.

Код:

/// <summary>
/// Provides access to a network share.
/// </summary>
public class NetworkShareAccesser : IDisposable
{
    private string _remoteUncName;
    private string _remoteComputerName;

    public string RemoteComputerName
    {
        get
        {
            return this._remoteComputerName;
        }
        set
        {
            this._remoteComputerName = value;
            this._remoteUncName = @"\\" + this._remoteComputerName;
        }
    }

    public string UserName
    {
        get;
        set;
    }
    public string Password
    {
        get;
        set;
    }

    #region Consts

    private const int RESOURCE_CONNECTED = 0x00000001;
    private const int RESOURCE_GLOBALNET = 0x00000002;
    private const int RESOURCE_REMEMBERED = 0x00000003;

    private const int RESOURCETYPE_ANY = 0x00000000;
    private const int RESOURCETYPE_DISK = 0x00000001;
    private const int RESOURCETYPE_PRINT = 0x00000002;

    private const int RESOURCEDISPLAYTYPE_GENERIC = 0x00000000;
    private const int RESOURCEDISPLAYTYPE_DOMAIN = 0x00000001;
    private const int RESOURCEDISPLAYTYPE_SERVER = 0x00000002;
    private const int RESOURCEDISPLAYTYPE_SHARE = 0x00000003;
    private const int RESOURCEDISPLAYTYPE_FILE = 0x00000004;
    private const int RESOURCEDISPLAYTYPE_GROUP = 0x00000005;

    private const int RESOURCEUSAGE_CONNECTABLE = 0x00000001;
    private const int RESOURCEUSAGE_CONTAINER = 0x00000002;


    private const int CONNECT_INTERACTIVE = 0x00000008;
    private const int CONNECT_PROMPT = 0x00000010;
    private const int CONNECT_REDIRECT = 0x00000080;
    private const int CONNECT_UPDATE_PROFILE = 0x00000001;
    private const int CONNECT_COMMANDLINE = 0x00000800;
    private const int CONNECT_CMD_SAVECRED = 0x00001000;

    private const int CONNECT_LOCALDRIVE = 0x00000100;

    #endregion

    #region Errors

    private const int NO_ERROR = 0;

    private const int ERROR_ACCESS_DENIED = 5;
    private const int ERROR_ALREADY_ASSIGNED = 85;
    private const int ERROR_BAD_DEVICE = 1200;
    private const int ERROR_BAD_NET_NAME = 67;
    private const int ERROR_BAD_PROVIDER = 1204;
    private const int ERROR_CANCELLED = 1223;
    private const int ERROR_EXTENDED_ERROR = 1208;
    private const int ERROR_INVALID_ADDRESS = 487;
    private const int ERROR_INVALID_PARAMETER = 87;
    private const int ERROR_INVALID_PASSWORD = 1216;
    private const int ERROR_MORE_DATA = 234;
    private const int ERROR_NO_MORE_ITEMS = 259;
    private const int ERROR_NO_NET_OR_BAD_PATH = 1203;
    private const int ERROR_NO_NETWORK = 1222;

    private const int ERROR_BAD_PROFILE = 1206;
    private const int ERROR_CANNOT_OPEN_PROFILE = 1205;
    private const int ERROR_DEVICE_IN_USE = 2404;
    private const int ERROR_NOT_CONNECTED = 2250;
    private const int ERROR_OPEN_FILES = 2401;

    #endregion

    #region PInvoke Signatures

    [DllImport("Mpr.dll")]
    private static extern int WNetUseConnection(
        IntPtr hwndOwner,
        NETRESOURCE lpNetResource,
        string lpPassword,
        string lpUserID,
        int dwFlags,
        string lpAccessName,
        string lpBufferSize,
        string lpResult
        );

    [DllImport("Mpr.dll")]
    private static extern int WNetCancelConnection2(
        string lpName,
        int dwFlags,
        bool fForce
        );

    [StructLayout(LayoutKind.Sequential)]
    private class NETRESOURCE
    {
        public int dwScope = 0;
        public int dwType = 0;
        public int dwDisplayType = 0;
        public int dwUsage = 0;
        public string lpLocalName = "";
        public string lpRemoteName = "";
        public string lpComment = "";
        public string lpProvider = "";
    }

    #endregion

    /// <summary>
    /// Creates a NetworkShareAccesser for the given computer name. The user will be promted to enter credentials
    /// </summary>
    /// <param name="remoteComputerName"></param>
    /// <returns></returns>
    public static NetworkShareAccesser Access(string remoteComputerName)
    {
        return new NetworkShareAccesser(remoteComputerName);
    }

    /// <summary>
    /// Creates a NetworkShareAccesser for the given computer name using the given domain/computer name, username and password
    /// </summary>
    /// <param name="remoteComputerName"></param>
    /// <param name="domainOrComuterName"></param>
    /// <param name="userName"></param>
    /// <param name="password"></param>
    public static NetworkShareAccesser Access(string remoteComputerName, string domainOrComuterName, string userName, string password)
    {
        return new NetworkShareAccesser(remoteComputerName,
                                        domainOrComuterName + @"\" + userName,
                                        password);
    }

    /// <summary>
    /// Creates a NetworkShareAccesser for the given computer name using the given username (format: domainOrComputername\Username) and password
    /// </summary>
    /// <param name="remoteComputerName"></param>
    /// <param name="userName"></param>
    /// <param name="password"></param>
    public static NetworkShareAccesser Access(string remoteComputerName, string userName, string password)
    {
        return new NetworkShareAccesser(remoteComputerName, 
                                        userName,
                                        password);
    }

    private NetworkShareAccesser(string remoteComputerName)
    {
        RemoteComputerName = remoteComputerName;               

        this.ConnectToShare(this._remoteUncName, null, null, true);
    }

    private NetworkShareAccesser(string remoteComputerName, string userName, string password)
    {
        RemoteComputerName = remoteComputerName;
        UserName = userName;
        Password = password;

        this.ConnectToShare(this._remoteUncName, this.UserName, this.Password, false);
    }

    private void ConnectToShare(string remoteUnc, string username, string password, bool promptUser)
    {
        NETRESOURCE nr = new NETRESOURCE
        {
            dwType = RESOURCETYPE_DISK,
            lpRemoteName = remoteUnc
        };

        int result;
        if (promptUser)
        {
            result = WNetUseConnection(IntPtr.Zero, nr, "", "", CONNECT_INTERACTIVE | CONNECT_PROMPT, null, null, null);
        }
        else
        {
            result = WNetUseConnection(IntPtr.Zero, nr, password, username, 0, null, null, null);
        }

        if (result != NO_ERROR)
        {
            throw new Win32Exception(result);
        }
    }

    private void DisconnectFromShare(string remoteUnc)
    {
        int result = WNetCancelConnection2(remoteUnc, CONNECT_UPDATE_PROFILE, false);
        if (result != NO_ERROR)
        {
            throw new Win32Exception(result);
        }
    }

    /// <summary>
    /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
    /// </summary>
    /// <filterpriority>2</filterpriority>
    public void Dispose()
    {
        this.DisconnectFromShare(this._remoteUncName);
    }
}

2
вам також потрібно using System.Runtime.InteropServices;і using System.ComponentModel;для DllImportіWin32Exception
Kᴀτᴢ

Це рішення зупинило мій тривалий день пошуку. Дякую!!! Діє досить добре, як потрібно.
Венкат

1
Я намагаюся використовувати ваше рішення з локальним обліковим записом користувача на віддаленій машині, але мені все одно не вдається отримати помилку в доступі. Чи буде ваше рішення працювати лише для мережевих облікових записів?
M3NTA7

1
Обліковий запис існує на віддаленій машині, але це не мережевий обліковий запис. Це місцевий рахунок машини. Я спробував встановити домен на ім'я машини. Я також надав повні дозволи на обліковий запис місцевого користувача у спільній папці, але мені доступ заборонено. Будь-які ідеї, чому це може статися? Дякую.
M3NTA7

2
Примітка: розміщення об’єкта, здається, не стирає облікові дані системи (Windows 10); Я можу отримати доступ до файлів на віддаленому комп'ютері після того, як з'єднання було "скасовано". Повторна реєстрація в обліковому записі користувача або перезавантаження комп'ютера очищає цей внутрішній кеш.
Тім Купер

16

AFAIK, вам не потрібно відобразити шлях UNC до букви диска, щоб встановити облікові дані для сервера. Я регулярно використовував пакетні сценарії, такі як:

net use \\myserver /user:username password

:: do something with \\myserver\the\file\i\want.xml

net use /delete \\my.server.com

Однак будь-яка програма, що працює на тому ж акаунті, що і ваша програма, все одно матиме доступ до всього, до чого username:passwordмає доступ. Можливим рішенням може бути ізоляція вашої програми у власному обліковому записі локального користувача (доступ до UNC є локальним для того, хто викликається NET USE).

Примітка: Використання SMB в усіх доменах - це не дуже вдале використання технології, IMO. Якщо безпека є такою важливою, той факт, що SMB бракує шифрування, є дещо заслінкою.


Якщо ви неправі щодо того, що доступ до UNC доступний лише для облікового запису, який дзвонив NET USE, це може бути прийнятним підходом. Ви впевнені, що нам потрібно використовувати локальний рахунок? Чи не буде NET USEвиклик локальним для машини, на якій він був викликаний? Ви дали мені хороший дослідницький шлях
Рандольфо,

AFAIK, і я можу помилятися, доступ до UNC буде доступний лише для конкретного директора безпеки (облікового запису SAM, незалежно від того), під яким було здійснено дзвінок до NET USE. Ви можете перевірити це, використовуючи RunAs, щоб скласти карту, а потім спробувати отримати доступ до нього з іншого облікового запису.
Яків

у моєму випадку мені довелося використовувати чисте використання \\ myserver / user: ім'я користувача @ пароль домену, оскільки користувач перебуває на іншому домені.
StarCub

4

Замість WNetUseConnection я б рекомендував NetUseAdd . WNetUseConnection - це застаріла функція, яку витіснили WNetUseConnection2 та WNetUseConnection3, але всі ці функції створюють мережевий пристрій, який відображається в Windows Explorer. NetUseAdd - еквівалент виклику використання мережі в запиті DOS для аутентифікації на віддаленому комп'ютері.

Якщо ви зателефонували в NetUseAdd, наступні спроби отримати доступ до каталогу повинні мати успіх.


1
@ Адам Робінсон: Це неправда. Немає таких WNetUseConnection2, ні WNetUseConnection3. Я думаю, що ви неясні щодо того, що WNetAddConnection витіснили WNetAddConnection2 та WnetAddConnection3. Також інформація, яку ви дали про це, не відповідає дійсності.
Брайан Р. Бонді

WNetUseConnection схожий на WNetAddConnection3, але він також має необов'язкові можливості для створення відображеного локального диска. Яким не потрібно користуватися.
Брайан Р. Бонді

@ BrianR.Bondy Вони дійсно існують, просто не реалізовані як C #. Джерело: docs.microsoft.com/da-dk/windows/win32/api/lmuse/… Цитата: "Ви також можете використовувати функції WNetAddConnection2 та WNetAddConnection3 для перенаправлення локального пристрою на мережевий ресурс."
Томас Вільямс

4

Хоча я не знаю себе, я, безумовно, сподіваюся, що №2 є невірним ... Я хотів би подумати, що Windows не збирається АВТОМАТИЧНО видавати мою інформацію про вхід (щонайменше, мій пароль!) На будь-яку машину , не кажучи вже про той, який не є частиною моєї довіри.

Незалежно від того, чи досліджували ви архітектуру видання? Ваш код буде виглядати приблизно так:

using (System.Security.Principal.WindowsImpersonationContext context = System.Security.Principal.WindowsIdentity.Impersonate(token))
{
    // Do network operations here

    context.Undo();
}

У цьому випадку tokenзмінна - це IntPtr. Для того, щоб отримати значення для цієї змінної, вам доведеться викликати некеровану функцію API API LogonUser Windows. Швидка поїздка на pinvoke.net дає нам такий підпис:

[System.Runtime.InteropServices.DllImport("advapi32.dll", SetLastError = true)]
public static extern bool LogonUser(
    string lpszUsername,
    string lpszDomain,
    string lpszPassword,
    int dwLogonType,
    int dwLogonProvider,
    out IntPtr phToken
);

Ім'я користувача, домен та пароль повинні здаватися досить очевидними. Подивіться на різні значення, які можна передавати dwLogonType та dwLogonProvider, щоб визначити те, що найкраще відповідає вашим потребам.

Цей код не перевірений, тому що у мене немає другого домену, де я можу перевірити, але це, сподіваємось, повинно поставити вас на правильний шлях.


7
Подання себе не працюватиме, коли ви намагаєтесь використати ідентифікатор входу з ненадійного домену. Ідентифікатор користувача повинен бути в змозі ввійти локально.
Moose

Так, ми спробували цей маршрут, і він закінчився так, як говорить @Moose: Домен не довіряють, і тому введення в життя не працюватиме.
Рандольфо

Так, одного разу я побачив цей коментар, тому я опублікував відповідь за допомогою NetUseAdd (головна різниця між нею та функціями WNetUseConnection та WNetAddConnection полягає в тому, що NetUseAdd не робить з'єднання видимим у Windows Explorer).
Адам Робінсон

Подання себе не працює на тому ж домені, на моїх тестах він постійно реагує на мене, коли Access Denied намагається прочитати файл у спільній папці з обліковим записом адміністратора (адміністратор на обох машинах). Отже, я думаю, що це не правильний підхід.
lidermin

4

Тут мінімальний клас POC w / уся сукупність видалена

using System;
using System.ComponentModel;
using System.Runtime.InteropServices;

public class UncShareWithCredentials : IDisposable
{
    private string _uncShare;

    public UncShareWithCredentials(string uncShare, string userName, string password)
    {
        var nr = new Native.NETRESOURCE
        {
            dwType = Native.RESOURCETYPE_DISK,
            lpRemoteName = uncShare
        };

        int result = Native.WNetUseConnection(IntPtr.Zero, nr, password, userName, 0, null, null, null);
        if (result != Native.NO_ERROR)
        {
            throw new Win32Exception(result);
        }
        _uncShare = uncShare;
    }

    public void Dispose()
    {
        if (!string.IsNullOrEmpty(_uncShare))
        {
            Native.WNetCancelConnection2(_uncShare, Native.CONNECT_UPDATE_PROFILE, false);
            _uncShare = null;
        }
    }

    private class Native
    {
        public const int RESOURCETYPE_DISK = 0x00000001;
        public const int CONNECT_UPDATE_PROFILE = 0x00000001;
        public const int NO_ERROR = 0;

        [DllImport("mpr.dll")]
        public static extern int WNetUseConnection(IntPtr hwndOwner, NETRESOURCE lpNetResource, string lpPassword, string lpUserID,
            int dwFlags, string lpAccessName, string lpBufferSize, string lpResult);

        [DllImport("mpr.dll")]
        public static extern int WNetCancelConnection2(string lpName, int dwFlags, bool fForce);

        [StructLayout(LayoutKind.Sequential)]
        public class NETRESOURCE
        {
            public int dwScope;
            public int dwType;
            public int dwDisplayType;
            public int dwUsage;
            public string lpLocalName;
            public string lpRemoteName;
            public string lpComment;
            public string lpProvider;
        }
    }
}

Ви можете безпосередньо використовувати \\server\share\folderw / WNetUseConnection, не потрібно \\serverзаздалегідь знімати його на частину.


2

Більшість серверів SFTP також підтримують SCP, що може бути набагато простіше знайти бібліотеки. Ви навіть можете просто зателефонувати існуючому клієнту зі свого коду, наприклад, pscp, що входить до програми PuTTY .

Якщо тип файлу, з яким ви працюєте, є чимось простим, як текстовий або XML-файл, ви можете навіть зайти так далеко, щоб написати власну реалізацію клієнта / сервера, щоб маніпулювати файлом, використовуючи щось на зразок .NET Remoting або веб-служб.



1

я додаю свій код vb.net на основі довідок Брайана

Imports System.ComponentModel

Imports System.Runtime.InteropServices

Public Class PinvokeWindowsNetworking

Const NO_ERROR As Integer = 0



Private Structure ErrorClass

    Public num As Integer

    Public message As String



    Public Sub New(ByVal num As Integer, ByVal message As String)

        Me.num = num

        Me.message = message

    End Sub

End Structure



Private Shared ERROR_LIST As ErrorClass() = New ErrorClass() {

    New ErrorClass(5, "Error: Access Denied"),

    New ErrorClass(85, "Error: Already Assigned"),

    New ErrorClass(1200, "Error: Bad Device"),

    New ErrorClass(67, "Error: Bad Net Name"),

    New ErrorClass(1204, "Error: Bad Provider"),

    New ErrorClass(1223, "Error: Cancelled"),

    New ErrorClass(1208, "Error: Extended Error"),

    New ErrorClass(487, "Error: Invalid Address"),

    New ErrorClass(87, "Error: Invalid Parameter"),

    New ErrorClass(1216, "Error: Invalid Password"),

    New ErrorClass(234, "Error: More Data"),

    New ErrorClass(259, "Error: No More Items"),

    New ErrorClass(1203, "Error: No Net Or Bad Path"),

    New ErrorClass(1222, "Error: No Network"),

    New ErrorClass(1206, "Error: Bad Profile"),

    New ErrorClass(1205, "Error: Cannot Open Profile"),

    New ErrorClass(2404, "Error: Device In Use"),

    New ErrorClass(2250, "Error: Not Connected"),

    New ErrorClass(2401, "Error: Open Files")}



Private Shared Function getErrorForNumber(ByVal errNum As Integer) As String

    For Each er As ErrorClass In ERROR_LIST

        If er.num = errNum Then Return er.message

    Next



    Try

        Throw New Win32Exception(errNum)

    Catch ex As Exception

        Return "Error: Unknown, " & errNum & " " & ex.Message

    End Try



    Return "Error: Unknown, " & errNum

End Function



<DllImport("Mpr.dll")>

Private Shared Function WNetUseConnection(ByVal hwndOwner As IntPtr, ByVal lpNetResource As NETRESOURCE, ByVal lpPassword As String, ByVal lpUserID As String, ByVal dwFlags As Integer, ByVal lpAccessName As String, ByVal lpBufferSize As String, ByVal lpResult As String) As Integer

End Function



<DllImport("Mpr.dll")>

Private Shared Function WNetCancelConnection2(ByVal lpName As String, ByVal dwFlags As Integer, ByVal fForce As Boolean) As Integer

End Function



<StructLayout(LayoutKind.Sequential)>

Private Class NETRESOURCE

    Public dwScope As Integer = 0

    Public dwType As Integer = 0

    Public dwDisplayType As Integer = 0

    Public dwUsage As Integer = 0

    Public lpLocalName As String = ""

    Public lpRemoteName As String = ""

    Public lpComment As String = ""

    Public lpProvider As String = ""

End Class



Public Shared Function connectToRemote(ByVal remoteUNC As String, ByVal username As String, ByVal password As String) As String

    Return connectToRemote(remoteUNC, username, password, False)

End Function



Public Shared Function connectToRemote(ByVal remoteUNC As String, ByVal username As String, ByVal password As String, ByVal promptUser As Boolean) As String

    Dim nr As NETRESOURCE = New NETRESOURCE()

    nr.dwType = ResourceTypes.Disk

    nr.lpRemoteName = remoteUNC

    Dim ret As Integer



    If promptUser Then

        ret = WNetUseConnection(IntPtr.Zero, nr, "", "", Connects.Interactive Or Connects.Prompt, Nothing, Nothing, Nothing)

    Else

        ret = WNetUseConnection(IntPtr.Zero, nr, password, username, 0, Nothing, Nothing, Nothing)

    End If



    If ret = NO_ERROR Then Return Nothing

    Return getErrorForNumber(ret)

End Function



Public Shared Function disconnectRemote(ByVal remoteUNC As String) As String

    Dim ret As Integer = WNetCancelConnection2(remoteUNC, Connects.UpdateProfile, False)

    If ret = NO_ERROR Then Return Nothing

    Return getErrorForNumber(ret)

End Function


Enum Resources As Integer

    Connected = &H1

    GlobalNet = &H2

    Remembered = &H3

End Enum


Enum ResourceTypes As Integer

    Any = &H0

    Disk = &H1

    Print = &H2

End Enum


Enum ResourceDisplayTypes As Integer

    Generic = &H0

    Domain = &H1

    Server = &H2

    Share = &H3

    File = &H4

    Group = &H5

End Enum


Enum ResourceUsages As Integer

    Connectable = &H1

    Container = &H2

End Enum


Enum Connects As Integer

    Interactive = &H8

    Prompt = &H10

    Redirect = &H80

    UpdateProfile = &H1

    CommandLine = &H800

    CmdSaveCred = &H1000

    LocalDrive = &H100

End Enum


End Class

як ним користуватися

Dim login = PinvokeWindowsNetworking.connectToRemote("\\ComputerName", "ComputerName\UserName", "Password")

    If IsNothing(login) Then



        'do your thing on the shared folder



       PinvokeWindowsNetworking.disconnectRemote("\\ComputerName")

    End If

-1

Я звернувся до МС, щоб знайти відповіді. Перше рішення передбачає, що обліковий запис користувача, який виконує процес подання заявки, має доступ до спільної папки або диска (той самий домен). Переконайтеся, що ваш DNS вирішено або спробуйте використовувати IP-адресу. Просто виконайте наступне:

 DirectoryInfo di = new DirectoryInfo(PATH);
 var files = di.EnumerateFiles("*.*", SearchOption.AllDirectories);

Якщо ви хочете в різних доменах .NET 2.0 з обліковими записами, дотримуйтесь цієї моделі:

WebRequest req = FileWebRequest.Create(new Uri(@"\\<server Name>\Dir\test.txt"));

        req.Credentials = new NetworkCredential(@"<Domain>\<User>", "<Password>");
        req.PreAuthenticate = true;

        WebResponse d = req.GetResponse();
        FileStream fs = File.Create("test.txt");

        // here you can check that the cast was successful if you want. 
        fs = d.GetResponseStream() as FileStream;
        fs.Close();

виглядає цікаво
DeerSpotter
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.