Named Pipe Server выдает UnauthorizedAccessException при создании экземпляра seccond, если PipeSecurity установлен
Я пытаюсь написать (повышенную привилегию) службу, которая будет разговаривать с не-привилегированным приложением winforms. Я смог иметь два консольных приложения (один поднятый не один), говорить без проблем, но у меня проблема с сервисом и приложением winforms.
Первый экземпляр трубы работает отлично. Однако после того, как мой клиент подключится, и я попытаюсь создать новый экземпляр, чтобы он был готов, если подключен второй клиент, но конструктор для NamedPipeServerStream генерирует исключение
System.UnauthorizedAccessException was unhandled
Message=Access to the path is denied.
Source=System.Core
StackTrace:
at System.IO.__Error.WinIOError(Int32 errorCode, String maybeFullPath)
at System.IO.Pipes.NamedPipeServerStream.Create(String fullPipeName, PipeDirection direction, Int32 maxNumberOfServerInstances, PipeTransmissionMode transmissionMode, PipeOptions options, Int32 inBufferSize, Int32 outBufferSize, PipeAccessRights rights, SECURITY_ATTRIBUTES secAttrs)
at System.IO.Pipes.NamedPipeServerStream..ctor(String pipeName, PipeDirection direction, Int32 maxNumberOfServerInstances, PipeTransmissionMode transmissionMode, PipeOptions options, Int32 inBufferSize, Int32 outBufferSize, PipeSecurity pipeSecurity, HandleInheritability inheritability, PipeAccessRights additionalAccessRights)
at System.IO.Pipes.NamedPipeServerStream..ctor(String pipeName, PipeDirection direction, Int32 maxNumberOfServerInstances, PipeTransmissionMode transmissionMode, PipeOptions options, Int32 inBufferSize, Int32 outBufferSize, PipeSecurity pipeSecurity)
at PipeServer.Server.Client..ctor(String pipeName, List`1 container) in E:\Visual Studio 2010\Projects\Sandbox Service\PipeServer.cs:line 27
at PipeServer.Server.ListenForClients() in E:\Visual Studio 2010\Projects\Sandbox Service\PipeServer.cs:line 148
at System.Threading.ThreadHelper.ThreadStart_Context(Object state)
at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean ignoreSyncCtx)
at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
at System.Threading.ThreadHelper.ThreadStart()
InnerException:
Пример удален, см. пример внизу для более простого тестового примера
Первая итерация работает отлично. это когда клиент подключается, а client = new Client
вызывается второй раз, что в свою очередь вызывает Pipe = new NamedPipeServerStream
, и это вызовет исключение.
Может ли кто-нибудь увидеть, какую ошибку я делаю?
Немного больше информации. Из любопытства я вернулся в консольное приложение. Чтобы проверить несколько экземпляров, я просто запускал exe несколько раз. Когда я помещаю два new NamedPipeServerStream
в один и тот же исполняемый файл, я получаю ту же ошибку... Так почему это нормально, если у вас есть отдельный exe, действующий как сервер, указывающий на тот же адрес именованного канала, но запрещается делать это внутри того же самого ехе?
static void Main()
{
PipeAccessRule pr = new PipeAccessRule("Users", PipeAccessRights.ReadWrite, System.Security.AccessControl.AccessControlType.Allow);
PipeSecurity ps = new PipeSecurity();
ps.AddAccessRule(pr);
using (NamedPipeServerStream pipeServer =
new NamedPipeServerStream("testpipe",PipeDirection.InOut,10,
PipeTransmissionMode.Message,
PipeOptions.WriteThrough,4028,4028,ps))
using (NamedPipeServerStream pipeServer2 = //v-- Throws the execption, but if you comment this out and run the EXE twice it works fine. creating a new instance of ps and pr does not change anything.
new NamedPipeServerStream("testpipe", PipeDirection.InOut, 10,
PipeTransmissionMode.Message,
PipeOptions.WriteThrough, 4028, 4028, ps))
{
Дополнительная информация:
Если я не устанавливаю PipeSecurity
, он не генерирует исключение, но когда я устанавливаю безопасность, он будет. Не имеет значения, пропускаю ли я оба экземпляра один и тот же PipeSecurity
или два экземпляра с теми же настройками, которые он по-прежнему вызывает исключение.
Ответы
Ответ 1
Есть две вещи, которые могут вызвать создание второго или последующего
NamedPipeServerStream на том же самом канале:
- Аргумент maxNumberOfServerInstances ctor должен быть установлен более чем на 1, когда был создан первый экземпляр сервера трубопроводов. Если нет, второй вызов завершится неудачно, если первый экземпляр не был полностью закрыт.
- процесс, вызывающий ctor, должен иметь право доступа, представленное PipeAccessRights.CreateNewInstance. Это мощное право, которое сервер труб должен защищать ревниво, поскольку он позволяет его обладателю способности действовать как сервер каналов.
Сервисный процесс должен установить безопасность канала таким образом:
PipeSecurity ps = new PipeSecurity();
ps.AddAccessRule(new PipeAccessRule(myPipeUsersGroup, PipeAccessRights.ReadWrite, AccessControlType.Allow));
ps.AddAccessRule(new PipeAccessRule(myPipeServerIdentity, PipeAccessRights.FullControl, AccessControlType.Allow));
где:
-
myPipeUsersGroup
является заполнителем для группы, которая содержит все потенциальные идентификаторы клиентов, которые будут подключаться к трубе. В зависимости от ваших требований/вариантов использования это может быть конкретный идентификатор клиента, пользовательская группа или встроенная группа, такая как "Пользователи" или "Администраторы".
-
myPipeServerIdentity
является заполнителем идентификатора службы. Это может быть установлено, например, на WindowsIdentity.GetCurrent().Owner
. Когда сервер трубопроводов размещен в службе Windows, тогда еще лучше (но гораздо сложнее реализовать) будет идентификатор идентификатора входа в систему для процесса обслуживания - это гарантирует, что только конкретный сервисный процесс может создавать экземпляры канала.
Если вы хотите, чтобы доступ к каналу ограничивался только для пользователей, которые вошли в систему локально, т.е. для предотвращения удаленного доступа по сети, вы также можете добавить запрещенный ACE для сетевых пользователей в ACL безопасности ACL.
Ответ 2
Я понял это.
static void Main()
{
PipeSecurity ps = new PipeSecurity();
ps.AddAccessRule(new PipeAccessRule("Users", PipeAccessRights.ReadWrite | PipeAccessRights.CreateNewInstance, AccessControlType.Allow));
ps.AddAccessRule(new PipeAccessRule("CREATOR OWNER", PipeAccessRights.FullControl, AccessControlType.Allow));
ps.AddAccessRule(new PipeAccessRule("SYSTEM", PipeAccessRights.FullControl, AccessControlType.Allow));
ps.AddAccessRule(pa);
using (NamedPipeServerStream pipeServer =
new NamedPipeServerStream("testpipe",PipeDirection.InOut,10,
PipeTransmissionMode.Message, PipeOptions.WriteThrough, 1024,1024,ps))
using (NamedPipeServerStream pipeServer2 =
new NamedPipeServerStream("testpipe", PipeDirection.InOut, 10,
PipeTransmissionMode.Message, PipeOptions.WriteThrough,1024,1024,ps))
{
Добавив права PipeAccessRights.CreateNewInstance
, теперь он отлично работает.
Я ударил еще одну загвоздку, но я решил ее, но хотел опубликовать ее, если другие люди обнаружат это через Google. предоставляя свой собственный объект безопасности Pipe, он удаляет стандартную версию, поэтому, если вам это нужно, вам нужно повторно добавить группу System, чтобы она могла разговаривать с трубой, если вы пишете сервис. Я обновил свой выше код до того, что я использовал, чтобы получить повышенную службу и приложение с неограниченными winforms, чтобы разговаривать друг с другом (владелец Creator, вероятно, не нужен)
Ответ 3
"Создатель владельца" может быть заменен на System.Security.Principal.WindowsIdentity.GetCurrent(). Имя и удалить PipeAccessRights.CreateNewInstance для любых пользователей.
static void Main()
{
PipeSecurity ps = new PipeSecurity();
ps.AddAccessRule(new PipeAccessRule("Users", PipeAccessRights.ReadWrite, AccessControlType.Allow));
ps.AddAccessRule(new PipeAccessRule(System.Security.Principal.WindowsIdentity.GetCurrent().Name, PipeAccessRights.FullControl, AccessControlType.Allow));
ps.AddAccessRule(new PipeAccessRule("SYSTEM", PipeAccessRights.FullControl, AccessControlType.Allow));
ps.AddAccessRule(pa);
Ответ 4
Решение, которое работает с локализованными версиями Windows:
pipeSecurity.AddAccessRule(new PipeAccessRule(new SecurityIdentifier(WellKnownSidType.BuiltinUsersSid, null),
accessRights, PipeAccessRights.ReadWrite | PipeAccessRights.CreateNewInstance, AccessControlType.Allow));
pipeSecurity.AddAccessRule(new PipeAccessRule(new SecurityIdentifier(WellKnownSidType.CreatorOwnerSid, null),
PipeAccessRights.FullControl, AccessControlType.Allow));
pipeSecurity.AddAccessRule(new PipeAccessRule(new SecurityIdentifier(WellKnownSidType.LocalSystemSid, null),
PipeAccessRights.FullControl, AccessControlType.Allow));
Ответ 5
Это сделает сервер доступным для всех для чтения и записи, он также будет работать на ОС со всеми языками, поскольку имя пользователя не является жестко закодированным
public NamedPipeServerInstance(string pipeName, int maxNumberOfServerInstances)
{
var ps = new PipeSecurity();
var sid = new SecurityIdentifier(WellKnownSidType.AuthenticatedUserSid, null);
var everyone = sid.Translate(typeof(NTAccount));
ps.SetAccessRule(new PipeAccessRule(everyone,
PipeAccessRights.ReadWrite, AccessControlType.Allow));
server = new NamedPipeServerStream(pipeName,
PipeDirection.InOut,
maxNumberOfServerInstances,
PipeTransmissionMode.Message,
PipeOptions.Asynchronous,
4028, 4028, ps);
var asyncResult = server.BeginWaitForConnection(OnConnected, null);
}
client = new NamedPipeClientStream(".", pipeName, PipeDirection.Out, PipeOptions.Asynchronous);
Ответ 6
Я знаю, что есть много ответов на этот вопрос, но я исправил аналогичную проблему, установив уровень requestExecutionLevel на requireAdministrator в файле app.manifest проекта где находится мой код namedpipe.
И это зафиксировало для меня UnauthorizedAccessException