Доступ к общему файлу (UNC) из удаленного ненадежного домена с учетными данными

152

Мы столкнулись с интересной ситуацией, которая требует разрешения, и мои поиски закончились ничем. Поэтому я обращаюсь к сообществу SO за помощью.

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

Как правило, эту проблему решают одним из двух способов:

  1. Сопоставьте общий файловый ресурс как диск и укажите учетные данные. Обычно это делается с помощью NET USEкоманды или дублирующих функций Win32 NET USE.
  2. Получите доступ к файлу с UNC-путем, как если бы удаленный компьютер находился в домене, и убедитесь, что учетная запись, под которой запускается программа, дублируется (включая пароль) на удаленном компьютере как локальный пользователь. В основном используется тот факт, что Windows автоматически предоставит учетные данные текущего пользователя, когда пользователь попытается получить доступ к общему файлу.
  3. Не используйте удаленный обмен файлами. Используйте FTP (или другие средства) для передачи файла, работайте с ним локально, а затем передайте его обратно.

По разным и разным причинам наши архитекторы безопасности / сети отвергли первые два подхода. Второй подход, очевидно, является дырой в безопасности; если удаленный компьютер скомпрометирован, локальный компьютер теперь находится в опасности. Первый подход неудовлетворителен, поскольку вновь смонтированный диск является общим ресурсом, доступным для других программ на локальном компьютере во время доступа программы к файлам. Хотя это вполне возможно сделать временным, это все еще дыра в их мнении.

Они открыты для третьего варианта, но администраторы удаленных сетей настаивают на SFTP, а не на FTPS, а FtpWebRequest поддерживает только FTPS. SFTP является более дружественным к брандмауэру вариантом, и есть несколько библиотек, которые я мог бы использовать для этого подхода, но я бы предпочел уменьшить свои зависимости, если смогу.

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

И поэтому я спрашиваю: есть ли другой путь? Я пропустил сверхсекретную функцию win32, которая делает то, что я хочу? Или я должен использовать какой-то вариант 3?

Randolpho
источник
Я решил это с помощью олицетворения, но это между 2 машинами вне домена. Я не знаю, возникнет ли проблема при разговоре с домена на компьютер за пределами домена. stackoverflow.com/questions/17221476/…
Wolf5

Ответы:

174

Способ решения вашей проблемы - использовать Win32 API с именем WNetUseConnection .
Используйте эту функцию для подключения к пути UNC с аутентификацией, а НЕ для сопоставления диска .

Это позволит вам подключиться к удаленному компьютеру, даже если он не находится в одном домене, и даже если у него другое имя пользователя и пароль.

После использования WNetUseConnection вы сможете получить доступ к файлу по UNC-пути, как если бы вы находились в том же домене. Лучший способ, вероятно, через административные встроенные акции.
Пример: \\ имя_компьютера \ c $ \ program files \ Folder \ 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
Если для имени пользователя / пароля указано значение null, я могу подключиться, но как я могу доказать, что я отключился? Есть ли что-то на сервере, на которое я могу посмотреть? На Server 2003 я могу наблюдать за сессиями, но список текущих сессий обновляется так же быстро, когда мое приложение не использует эти API.
легкое сомнение
Должны ли соединения, открытые с помощью, 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);
    }
}
GameScripting
источник
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 для сопоставления пути и затем пытаясь получить к нему доступ из другой учетной записи.
Джейкоб
в моем случае мне пришлось использовать net use \\ myserver / user: username @ domain password, поскольку пользователь находится в другом домене.
StarCub,
4

Вместо WNetUseConnection я бы порекомендовал NetUseAdd . WNetUseConnection - это устаревшая функция, которая была заменена WNetUseConnection2 и WNetUseConnection3, но все эти функции создают сетевое устройство, которое отображается в проводнике Windows. NetUseAdd является эквивалентом вызова net use в командной строке 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. Чтобы получить значение для этой переменной, вам нужно вызвать неуправляемую функцию Windows API LogonUser. Быстрая поездка на 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).
Адам Робинсон
Олицетворение не работает ни в одном и том же домене, в моих тестах он продолжает отвечать мне Отказано в доступе, пытаясь прочитать файл в общей папке с учетной записью администратора (администратор на обеих машинах). Так что я думаю, что это неправильный подход.
Лидермин
4

Здесь минимальный класс POC со всеми удаленными кортами

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части только заранее.

wqw
источник
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
roy.d
источник
-1

Я посмотрел на MS, чтобы найти ответы. Первое решение предполагает, что учетная запись пользователя, на котором запущен процесс приложения, имеет доступ к общей папке или диску (тот же домен). Убедитесь, что ваш 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();
Kentonbmax
источник
выглядит интересно
DeerSpotter