Скопировать файл по сети в пункт назначения за пределами домена

#c# #cmd #copy #psexec

#c# #cmd #Копировать #psexec

Вопрос:

Я хочу скопировать файл с компьютера A (с учетной записью MyAccount@mydomain) на компьютер B (UserB@ComputerB) по сети, используя c #. Я попробовал стандартный

 File.Copy(source,destination)
  

и попытался запустить процесс cmd (с компьютера A) и вызвать метод копирования

 System.Diagnostics.Process process = new System.Diagnostics.Process();
System.Diagnostics.ProcessStartInfo startInfo = new System.Diagnostics.ProcessStartInfo();
startInfo.UseShellExecute = false;
startInfo.Domain = "computerB"; //ofcourse it wont work since its outside the local domain of A
startInfo.FileName = "cmd.exe";
startInfo.Arguments = @"/C COPY \computerAPathFile1.txt \computerBPath$ ";
process.StartInfo = startInfo;
process.Start();
process.WaitForExit();
//It will exit the user name or password is incorrect
  

Я пытался также использовать PsExec для олицетворения ComputerB :

  System.Diagnostics.Process process = new System.Diagnostics.Process();
 System.Diagnostics.ProcessStartInfo startInfo = new 
 System.Diagnostics.ProcessStartInfo();
 startInfo.UseShellExecute = false;
 startInfo.FileName = "cmd.exe";
 startInfo.Arguments = @"psexec \computerB -u computerBuserB -p userBPassword cmd /c COPY \computerAPathFile1.txt \computerBPath$";
 process.StartInfo = startInfo;
 process.Start();
 process.WaitForExit();
//it will exit that the source file is unknown
  

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

Есть ли обходной путь для этой проблемы?

Комментарии:

1. «Есть ли обходной путь для этой проблемы?» — Многие. Некоторые из них: 1. Создайте общедоступный и анонимный общий ресурс на B (не такой безопасный). 1b. Создайте выделенного пользователя на B , который имеет права на назначение. 2. Переключитесь с «push» на «pull»: что-то вроде http-сервера на A и B запросите документ / файл. 3. Запустите общедоступный FTP-сервер B 4. Используйте SSH / SCP…

2. Исключите C # из уравнения. Вам нужно заставить это работать без C #, затем включить C # в проблему (поскольку C # не может волшебным образом чего-либо достичь, если целевая машина недоступна).

3. @Fildor спасибо, как бы я мог выполнить это ваше второе предложение «Переключиться с «push» на «pull»

4. @MhamadTabikh Я не знаю вашего системного ландшафта и требований. Вполне может быть, что это невозможно, поскольку B пришлось бы «знать», что извлекать, и если и когда вообще.

5. @Fildor Если бы я подключил диск компьютера A к компьютеру A и запустил .copy(), это сработало бы?

Ответ №1:

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

Решение состоит в том, чтобы открыть аутентифицированное соединение с целевым устройством с помощью WNetUseConnection API, выполнить свои файловые операции, а затем закрыть соединение с помощью вызова WNetCancelConnection2 .

Вот некоторый код, который я использовал в прошлом для этого:

 class ConnectSMB : IDisposable
{
    public string URI { get; private set; }
    public bool Connected { get; private set; } = false;

    public ConnectSMB(string uri, string username, string encPassword)
    {
        string pass = StringEncryption.Decode(encPassword);
        string connResult = Native.ConnectToRemote(uri, username, pass);
        if (connResult != null)
            throw new Exception(connResult);
            
        URI = uri;
        Connected = true;
    }

    public void Dispose()
    {
        Close();
    }

    public void Close()
    {
        if (Connected)
        {
            Native.DisconnectRemote(URI);
            URI = null;
            Connected = false;
        }
    }
}

public class Native
{
    #region Consts
    const int RESOURCETYPE_DISK = 1;
    const int CONNECT_UPDATE_PROFILE = 0x00000001;
    #endregion

    #region Errors
    public enum ENetUseError
    {
        NoError = 0,
        AccessDenied = 5,
        AlreadyAssigned = 85,
        BadDevice = 1200,
        BadNetName = 67,
        BadProvider = 1204,
        Cancelled = 1223,
        ExtendedError = 1208,
        InvalidAddress = 487,
        InvalidParameter = 87,
        InvalidPassword = 1216,
        MoreData = 234,
        NoMoreItems = 259,
        NoNetOrBadPath = 1203,
        NoNetwork = 1222,
        BadProfile = 1206,
        CannotOpenProfile = 1205,
        DeviceInUse = 2404,
        NotConnected = 2250,
        OpenFiles = 2401
    }
    #endregion

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

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

    [StructLayout(LayoutKind.Sequential)]
    private class NETRESOURCE
    {
        // Not used
        public int dwScope = 0;
        // Resource Type - disk or printer
        public int dwType = RESOURCETYPE_DISK;
        // Not used
        public int dwDisplayType = 0;
        // Not used
        public int dwUsage = 0;
        // Local Name - name of local device (optional, not used here)
        public string lpLocalName = "";
        // Remote Name - full path to remote share
        public string lpRemoteName = "";
        // Not used
        public string lpComment = "";
        // Not used
        public string lpProvider = "";
    }
    #endregion

    public static string ConnectToRemote(string remoteUNC, string username, string password)
    {
        NETRESOURCE nr = new NETRESOURCE
        {
            lpRemoteName = remoteUNC
        };

        ENetUseError ret = WNetUseConnection(IntPtr.Zero, nr, password, username, 0, null, null, null);
        if (ret == ENetUseError.NoError) return null;
        return ret.ToString();
    }

    public static string DisconnectRemote(string remoteUNC)
    {
        ENetUseError ret = WNetCancelConnection2(remoteUNC, CONNECT_UPDATE_PROFILE, false);
        if (ret == ENetUseError.NoError) return null;
        return ret.ToString();
    }
}
  

Создайте экземпляр ConnectSMB класса перед выполнением операций с удаленными файлами, затем удалите (или закройте) его, когда вы закончите с подключением.

 using (var smb = new ConnectSMB(@"\computerBPath", "userB", "userBPassword"))
{
    File.Copy(source, destination);
}
  

Комментарии:

1. спасибо, но у меня есть вопрос. Если бы я установил это соединение и после копирования файла, не выдало бы ли это исключение, поскольку UserB, похоже, не может найти исходный путь?

2. @MhamadTabikh По сути, это то же самое, что происходит, когда вы переходите к удаленному пути в проводнике Windows и получаете приглашение для входа на удаленный компьютер. Это не изменяет текущего пользователя, это добавляет токен аутентификации к текущему пользователю, который позволяет им получать доступ к ресурсам на удаленном компьютере.