Здравствуйте, Sinix, Вы писали:
S>>Например, я почти уверен, что в коде дырка: не блокируются удалённые соединения. В понедельник буду на работе, проверю.
S>Проверил. Дырка. Гугль молчит
Попробуйте в PipeSecurity добавить явный запрет для Network users.
S>с WCF граблей не меньше. Но удалённые соединения допускает.
Пайпы-то работают. Я имел в виду, что это искусственно зарезано в NamedPipeBinding
"Нормальные герои всегда идут в обход!"
Re[3]: [.Net 4, cross-process singleton] Покритикуйте код
Здравствуйте, Sinix, Вы писали:
S>Здравствуйте, Jolly Roger, Вы писали:
S>*истерически хихикая и нецензурно бормоча.
Спокойствие, только спокойствие!(c)
S>Как выяснилось, реализация на WCF "работает" благодаря двум ньюансам в реализации.
Ну первое — вроде ничего страшного, нет?
S>Во-вторых: S>
S>If my WCF server process using a Named Pipe-based endpoint doesn't have privileges to create a Global kernel object it silently fails and creates a local one which will not be visible to processes outside of its session.
S>Кратко: без прав админа WCF не найдёт Named Pipe, запущенный в 2й сессии.
Хм. Возможно, я отстал от жизни, но с каких пор пайпы стали делиться на локальные и глобальные?! По жизни, пайп, созданный в любой сессии, виден как во всех сессиях данной машины, так и в сети. Для открытия может не хватить прав, да, но виден-то будет всем
Пайпы именуются так: "\\.\pipe\PipeName", ни о каких Local-Global и речи нет.
Во-вторых, с каких пор для создания пайпа стала нужна SeCreateGlobal, про которую русским языком по-английски написано:
"Required to create named file mapping objects in the global namespace during Terminal Services sessions. "
, да ещё и в описании namespaces добавлено:
... must have SeCreateGlobalPrivilege enabled in order to create a file-mapping object in the global namespace successfully. The privilege check is limited to the creation of file-mapping objects, and does not apply to opening existing ones.
а для всяких там пайпрв и прочих мьютексов она нафиг не сдалась Тот товарисЧ чуть-чуть напутал с интерпритацией результатов, он без SeCreateGlobal shared section создать не может, а чистые пайпы работать будут.
S>На закуску: S>
S>The ACL set up when WCF creates the named pipe looks like this..
S>- Deny Full Access to NETWORK USERS — that is: deny the access rights specified by the access mask GENERIC_ALL, to any security context having membership of the group with well-known SID S-1-5-2
S>- Allow the access rights specified by the access mask 0x0012019f, to EVERYONE (the well-known SID S-1-1-0)
S>- Allow the access rights specified by the access mask 0x0012019f, to the well-known SID S-1-3-0 (CREATOR OWNER)
S>Вариант с WCF ещё беззащитней моего.
Да. "Доктор, а может всё-таки COM? Чё сразу в морг-то?"
PS Код пока не смотрел.
"Нормальные герои всегда идут в обход!"
Re: [.Net 4, cross-process singleton] Покритикуйте код
Здравствуйте, Sinix, Вы писали:
S>// не должен позволять соединения вне текущей сессии, в т.ч. удалённые
В другой сессии может быть запущен процесс от имени юзера текущей сессии. И даже на другой машине, если это доменная учётка.
S>PipeTransmissionMode.Message, PipeOptions.WriteThrough, // Не уверен. Может, всё-таки использовать буфер?
Ага Through применим только для byte-mode, и только если клиент и сервер находятся на разных машинах. А в Вашем случае этот флаг будет просто проигнорирован как не имеющий смысла.
"Нормальные герои всегда идут в обход!"
Re[4]: [.Net 4, cross-process singleton] Покритикуйте код
Здравствуйте, Jolly Roger, Вы писали:
JR>На WCF пайпы как раз работают только локально (если только в 4.0 не поменяли), но вот фильтровать по сессиям вряд-ли удастся просто, если вообще.
[нецензурно-унылое выражение разочарования в несовершенстве мира].
Здравствуйте, Sinix, Вы писали:
S>Увы да, нужен именно logon session sid. Сейчас осталась единственная дырка: локально заходим одновременно под двумя пользователями, под первым запускаем сервер, под вторым (от имени первого) — клиента.
Ну Вам конечно виднее, но всё-же по-моему Вам таки нужен Terminal Service Session ID.
S>Если его добыть — даём права только сессии — вуаля S>Сейчас осталась единственная дырка: локально заходим одновременно под двумя пользователями, под первым запускаем сервер, под вторым (от имени первого) — клиента.
Так если ориентироваться на Logon Session, то я, воспользовавшись LsaLogonUser, задам токену тот LUID, который был присвоен токену сессии сервера и — вуаля — Ваш сервер примет такое подключение, хотя клиент будет находиться в другой терминальной сессии.
Logon session ID и Terminal Service Session ID — это вещи из параллельных миров. Logon session формируется при логоне юзера. Даже внутри одной пользовательской(терминальной) сессии могут быть процессы с токенами, у которых разные logon session. А это:
локально заходим одновременно под двумя пользователями
как раз таки создание двух терминальных сессий. На XP Fast User Switching реализуется именно через Terminal Service. С Висты оно уже есть всегда — сервисы работают в нулевой терминальной сессии, для вошедшего юзера создаётся терминальная сессия с ID = 1.
"Нормальные герои всегда идут в обход!"
Re[3]: [.Net 4, cross-process singleton] Покритикуйте код
Здравствуйте, Sinix, Вы писали:
S>Здравствуйте, hardcase, Вы писали:
H>>А это на COM+ нельзя разве решить? S>Оверкилл. Возни с ним много (регистрация та же) а преимуществ 0. Да ещё использоваться будет в софте с "установкой копированием".
Вообще-то в случае COM без регистрации вполне можно обойтись, вот здесь
я описывал, как. На шарпе, думаю, это тоже реализуемо. Для захвата приоритета можно использовать стандартную схему с мьютексом, а интерфейс сервера раздавать через Running Object Table (ROT). На пайпах требование по поводу чужих сессий реализовать, по-моему, намного сложнее.
"Нормальные герои всегда идут в обход!"
Re[9]: [.Net 4, cross-process singleton] Покритикуйте код
Кое-чего можно посмотреть. Вот здесь утилиты от Руссиновича (раньше было Sysinternals, с некоторых пор Windows Sysinternals). Они все полезные, но в данном случае нас интересует logonsessions.exe. Ей можно посмотреть список всех Logon Sessions, существующих в данный момент, кто логинился, как, когда, в какой терминальной сессии. Можно, например, войти под пользователем X, потом через контекстное меню Run As запустить какой-нибудь блокнот от имени того-же пользователя X, и сравнить список логон сессий до и после, просто для информации
"Нормальные герои всегда идут в обход!"
Re[5]: [.Net 4, cross-process singleton] Покритикуйте код
Здравствуйте, Sinix, Вы писали:
S>Что ж делать-то?
Ну дык каждый ищет там, где ему светлее Я-бы мьютекс и COM использовал — для работы без регистрации код один раз пишется, а ROT позволяет исключить подключения из чужих сессий, а удалённым он вообще недоступен.
"Нормальные герои всегда идут в обход!"
Re[6]: [.Net 4, cross-process singleton] Покритикуйте код
Здравствуйте, Jolly Roger, Вы писали:
JR>Ну дык каждый ищет там, где ему светлее
C COMом знаком только заочно, связываться сейчас ради решения "один раз сделал — всю жизнь мучаешься" не рискну. Ну, и смысл откатываться с устаревшего, но работающего remoting'а на ещё более легаси, но, возможно, работающий ещё лучше COM+ —
Пойду копаться в the new shining code, шоб его...
Спасибо!
Re[9]: [.Net 4, cross-process singleton] Покритикуйте код
Здравствуйте, Jolly Roger, Вы писали:
JR>Ну первое — вроде ничего страшного, нет?
Да. Но в результате на ровном месте — ничего не запрещало использовать обычные имена — мы получили грабли, приведшие к п.2. Очевидно, у обычных пайпов был фатальный недостаток.
JR>Хм. Возможно, я отстал от жизни, но с каких пор пайпы стали делиться на локальные и глобальные?!
Не пайпы, а shared memory section, используемая для получения метаданных. Вы же сами цитату нашли
... must have SeCreateGlobalPrivilege enabled in order to create a file-mapping object in the global namespace successfully. The privilege check is limited to the creation of file-mapping objects, and does not apply to opening existing ones.
Кстати, теперь понял (см выделенное) почему новые клиенты коннектятся к серверу, запущенному от админа.
JR>а для всяких там пайпрв и прочих мьютексов она нафиг не сдалась Тот товарисЧ чуть-чуть напутал с интерпритацией результатов, он без SeCreateGlobal shared section создать не может, а чистые пайпы работать будут.
Чистые — да. NetNamedPipeBinding — нет.
JR>Да. "Доктор, а может всё-таки COM? Чё сразу в морг-то?"
+100
Re[10]: [.Net 4, cross-process singleton] Покритикуйте код
Здравствуйте, Sinix, Вы писали:
S>Здравствуйте, Jolly Roger, Вы писали:
JR>>Ну первое — вроде ничего страшного, нет? S>Да. Но в результате на ровном месте — ничего не запрещало использовать обычные имена — мы получили грабли, приведшие к п.2. Очевидно, у обычных пайпов был фатальный недостаток.
S>Не пайпы, а shared memory section, используемая для получения метаданных. Вы же сами цитату нашли
Ну я её не то чтобы нашёл, просто знал, где лежит Да, товарисч прав, я просто бегло и не до конца просмотрел ссылку, виноват
JR>>Да. "Доктор, а может всё-таки COM? Чё сразу в морг-то?" S>+100
"Нормальные герои всегда идут в обход!"
Re[9]: [.Net 4, cross-process singleton] Покритикуйте код
Вчерновую накидал cross-process singleton на named pipes, плиз потыкайте носом
Ясен пень, что до юзабельного состояния пилить и пилить: вынести в отдельный класс, добавить разные имена для разных сессий, не забыть про обработку ошибок, запуск в фоновом потоке, прерывание работы и т.д. и т.п.
Меня сейчас интересуют в первую очередь упущенные нюансы/неверно указанные параметры/безопасность. Например, я почти уверен, что в коде дырка: не блокируются удалённые соединения. В понедельник буду на работе, проверю.
код
using System;
using System.IO;
using System.IO.Pipes;
using System.Security.AccessControl;
using System.Security.Principal;
namespace ProcessSingleInstance
{
class Program
{
static void Main(string[] args)
{
string[] data = new[] { "a", "aaaaa", "qweqeqweqeqweqwert" };
NamedPipeServerStream server = null;
try
{
server = CreateServerStream();
}
catch (UnauthorizedAccessException)
{
}
if (server != null)
{
RunServer(server);
}
else
{
RunClient(data);
}
}
// критиковать здесь.
// Сервер должен _только_ читать данные;
// не должен позволять соединения вне текущей сессии, в т.ч. удалённыеprivate static NamedPipeServerStream CreateServerStream()
{
PipeSecurity security = new PipeSecurity();
security.AddAccessRule(
new PipeAccessRule(
WindowsIdentity.GetCurrent().User,
PipeAccessRights.Synchronize
| PipeAccessRights.ReadAttributes // DONTTOUCH: Required. I don't know why.
| PipeAccessRights.Write,
AccessControlType.Allow));
NamedPipeServerStream server = new NamedPipeServerStream(
"server",
PipeDirection.In, 1,
PipeTransmissionMode.Message, PipeOptions.WriteThrough, // Не уверен. Может, всё-таки использовать буфер?
4096, 4096,
security, HandleInheritability.None);
return server;
}
// и здесьprivate static NamedPipeClientStream CreateClientStream()
{
NamedPipeClientStream client = new NamedPipeClientStream(
".", "server",
PipeDirection.Out, PipeOptions.WriteThrough, TokenImpersonationLevel.None, HandleInheritability.None);
return client;
}
private static void RunServer(NamedPipeServerStream server)
{
while (true)
{
Console.WriteLine("Wait...");
BinaryReader r = new BinaryReader(server);
server.WaitForConnection();
try
{
int count = r.ReadInt32();
for (int i = 0; i < count; i++)
{
Console.WriteLine(r.ReadString());
}
}
catch (IOException)
{ }
server.Disconnect();
}
}
private static void RunClient(string[] data)
{
using (NamedPipeClientStream client = CreateClientStream())
{
client.Connect();
using (BinaryWriter w = new BinaryWriter(client))
{
try
{
w.Write(data.Length);
foreach (var item in data)
{
w.Write(item);
}
client.WaitForPipeDrain();
}
catch (IOException)
{ }
}
}
}
}
}
Re: [.Net 4, cross-process singleton] Покритикуйте код
Здравствуйте, hardcase, Вы писали:
H>А это на COM+ нельзя разве решить?
Оверкилл. Возни с ним много (регистрация та же) а преимуществ 0. Да ещё использоваться будет в софте с "установкой копированием".
Re[2]: [.Net 4, cross-process singleton] Покритикуйте код
JR>В другой сессии может быть запущен процесс от имени юзера текущей сессии. И даже на другой машине, если это доменная учётка.
Спс. Я быстренько покопался во внутренностях: кажется, что фильтрацию по IP/данным к NamedPipeServerStream клиента не прикрутить. Либо переходить на WCF (а там это возможно будет?) либо попробовать что-то ещё (что? в голову ничего разумного не приходит), либо забить и вернуться к реализации на глюкодырявом ремотинге
JR>Ага Through применим только для byte-mode, и только если клиент и сервер находятся на разных машинах. А в Вашем случае этот флаг будет просто проигнорирован как не имеющий смысла.
От оно чё...
Re[3]: [.Net 4, cross-process singleton] Покритикуйте код
Здравствуйте, Sinix, Вы писали:
S> Либо переходить на WCF (а там это возможно будет?)
На WCF пайпы как раз работают только локально (если только в 4.0 не поменяли), но вот фильтровать по сессиям вряд-ли удастся просто, если вообще.
S> вернуться к реализации на глюкодырявом ремотинге
Но-но Когда этот "ремонтник" появился, создатели его позиционировали как супер-пупер киллера всех остальных технологий удалённого взаимодействия. Ну прям как WCF сейчас
"Нормальные герои всегда идут в обход!"
Re[5]: [.Net 4, cross-process singleton] Покритикуйте код
Здравствуйте, Sinix, Вы писали:
S>Меня сейчас интересуют в первую очередь упущенные нюансы/неверно указанные параметры/безопасность. Например, я почти уверен, что в коде дырка: не блокируются удалённые соединения. В понедельник буду на работе, проверю.
Проверил. Дырка. Гугль молчит
Re[2]: [.Net 4, cross-process singleton] Покритикуйте код
По поводу локальных чужих сессий — в принципе, можно попробовать имперсонировать клиента, вытащить из маркера его SessionId (GetTokenInformation + TokenSessionId) и сравнить с текущей сессией.
"Нормальные герои всегда идут в обход!"
Re[3]: [.Net 4, cross-process singleton] Покритикуйте код
Здравствуйте, Jolly Roger, Вы писали:
JR>По поводу локальных чужих сессий — в принципе, можно попробовать имперсонировать клиента, вытащить из маркера его SessionId (GetTokenInformation + TokenSessionId) и сравнить с текущей сессией.
1) Да не, это уже оверкилл будет. И так хватит.
2) По наводке: http://msdn.microsoft.com/en-us/library/aa379626.aspx
TokenSessionId
...
In a non-Terminal Services environment, the session identifier is zero.
Огромное спасибо!
Re[3]: [.Net 4, cross-process singleton] Покритикуйте код
Здравствуйте, Sinix, Вы писали:
S>UPD. Вроде бы, нужно использовать AuthenticationId из TOKEN_STATISTICS.
Да нет, вероятно. AuthenticationId — это идентификатор Logon Session, это не то, его, к примеру, можно задать, используя для логона LsaLogonUser.
S>In a non-Terminal Services environment, the session identifier is zero
Ну да, если у Вас нет Terminal Services environment, то и пользовательская сессия одна-единственная => вопрос о сессиях снимается автоматически.
S>Аж стыдно, что не додумался до такого простого решения
Да ладно, мне оно тоже не сразу в голову пришло Мало иметь информацию в мозгах, нужно ещё чтобы она в нужный момент всплыла на поверхность из всего прочего хлама
"Нормальные герои всегда идут в обход!"
Re[5]: [.Net 4, cross-process singleton] Покритикуйте код
JR>Да нет, вероятно. AuthenticationId — это идентификатор Logon Session, это не то, его, к примеру, можно задать, используя для логона LsaLogonUser.
Увы да, нужен именно logon session sid. Если его добыть — даём права только сессии — вуаля Сейчас осталась единственная дырка: локально заходим одновременно под двумя пользователями, под первым запускаем сервер, под вторым (от имени первого) — клиента.
Попробую вытащить ч/з WMI и забью. Тем более что, как я понимаю, в WCF будет абсолютно та же уязвимость.
Re[7]: [.Net 4, cross-process singleton] Покритикуйте код
Здравствуйте, Jolly Roger, Вы писали:
JR>как раз таки создание двух терминальных сессий. На XP Fast User Switching реализуется именно через Terminal Service. С Висты оно уже есть всегда — сервисы работают в нулевой терминальной сессии, для вошедшего юзера создаётся терминальная сессия с ID = 1.
О как. Забил, коллега реализовал вариант на WCF. Спасибо
Re[7]: [.Net 4, cross-process singleton] Покритикуйте код
*истерически хихикая и нецензурно бормоча.
Как выяснилось, реализация на WCF "работает" благодаря двум ньюансам в реализации. Во-первых:
What I see instead is a handle to a pipe named something like...
\\.\pipe\197ad019-6e5f-48cb-8f88-02ae11dfd8c0
...
So in order to locate the correct pipe, a client of a WCF NetNamedPipe service endpoint has to:
— know that the special metadata mechanism exists
— know how to derive from the endpoint URI the name of the file mapping object through which the metadata is published
— located the file mapping object and use it to open a view on the shared memory
— know how to interpret the metadata stored in the shared memory, and translate it into the name of the pipe currently being used by the endpoint
If my WCF server process using a Named Pipe-based endpoint doesn't have privileges to create a Global kernel object it silently fails and creates a local one which will not be visible to processes outside of its session.
Кратко: без прав админа WCF не найдёт Named Pipe, запущенный в 2й сессии.
В результате, запустив сампл от имени администратора и без, мы получим _два_ сервера. Коллеге как-то удалось сделать так, чтобы все дальнейшие запуски обращаются к серверу, запущенному с правами администратора
The ACL set up when WCF creates the named pipe looks like this..
— Deny Full Access to NETWORK USERS — that is: deny the access rights specified by the access mask GENERIC_ALL, to any security context having membership of the group with well-known SID S-1-5-2
— Allow the access rights specified by the access mask 0x0012019f, to EVERYONE (the well-known SID S-1-1-0)
— Allow the access rights specified by the access mask 0x0012019f, to the well-known SID S-1-3-0 (CREATOR OWNER)
Вариант с WCF ещё беззащитней моего.
Код от коллеги, чуть сокращён
using System;
using System.ServiceModel;
namespace WCFSample
{
[ServiceContract]
public interface IServiceContract
{
[OperationContract]
void StartOnServer(string[] args);
}
public class Service: IServiceContract
{
public void StartOnServer(string[] args)
{
Console.WriteLine(string.Join(",", args));
}
}
class Program
{
static void Main(string[] args)
{
try
{
ServerStart(@"net.pipe://127.0.0.1/wtfService");
}
catch (AddressAlreadyInUseException)
{
try
{
ClientStart(@"net.pipe://127.0.0.1/wtfService");
}
catch (Exception ex)
{
Console.WriteLine(ex);
Console.ReadKey();
}
}
}
private static void ServerStart(string channelUri)
{
ServiceHost host = new ServiceHost(typeof(Service), new Uri(channelUri));
try
{
host.AddServiceEndpoint(typeof(IServiceContract),
new NetNamedPipeBinding(NetNamedPipeSecurityMode.Transport),
new Uri(channelUri));
host.Open();
Console.WriteLine("Service is ready, press any key to terminate.");
Console.ReadKey();
// ((Service) as Service).LaucnhStartUpWindow();
}
catch (CommunicationException ex)
{
// Console.WriteLine(ex);
Console.WriteLine("Client starting...");
host.Abort();
throw;
}
}
private static void ClientStart(string channelUri)
{
ChannelFactory<IServiceContract> factory =
new ChannelFactory<IServiceContract>
(new NetNamedPipeBinding(), new EndpointAddress(channelUri));
IServiceContract proxy = factory.CreateChannel();
try
{
//signal server that a new instance is selected
proxy.StartOnServer(new[] { "Hello", "world" });
}
catch (EndpointNotFoundException ex)
{
Console.WriteLine(ex);
//return false to indicate this instance is first instance
// return false;
}
}
}
}
Re[7]: [.Net 4, cross-process singleton] Покритикуйте код
От:
Аноним
Дата:
05.10.10 14:31
Оценка:
Здравствуйте, Sinix, Вы писали:
S>Здравствуйте, Jolly Roger, Вы писали:
JR>>Ну дык каждый ищет там, где ему светлее
S>C COMом знаком только заочно, связываться сейчас ради решения "один раз сделал — всю жизнь мучаешься" не рискну. Ну, и смысл откатываться с устаревшего, но работающего remoting'а на ещё более легаси, но, возможно, работающий ещё лучше COM+ —
Как вариант остается типичный Windows Service — будет честный синглтон, но да, его нужно регистрировать.
Re[7]: [.Net 4, cross-process singleton] Покритикуйте код
Здравствуйте, Sinix, Вы писали:
S>C COMом знаком только заочно, связываться сейчас ради решения "один раз сделал — всю жизнь мучаешься" не рискну. Ну, и смысл откатываться с устаревшего, но работающего remoting'а на ещё более легаси, но, возможно, работающий ещё лучше COM+ —
Я тут чего подумал В принципе, если ограничить клиентов работой через диспинтерфейс, то вроде ничего сложного-то в реализации и нет. ID-binding, он конечно помедленней, чем early, но не так уж и сильно, зато работает без всякой регистрации и дополнительных телодвижений, и даже библиотека типов не нужна. А в NET есть аттрибут ClassInterface с типом, кроме прочих, AutoDispatch — похоже, оно самое. Если хотите, завтра выберу время и попробую пример набросать.
"Нормальные герои всегда идут в обход!"
Re[8]: [.Net 4, cross-process singleton] Покритикуйте код
Здравствуйте, Константин Л., Вы писали:
КЛ>Здравствуйте, Jolly Roger, Вы писали:
КЛ>[]
JR>>Да. "Доктор, а может всё-таки COM? Чё сразу в морг-то?"
КЛ>писать на .net outproc com-сервера та еще задача. да еще и singleton. там большая засада с MTA и вообще.
КЛ>я бы написал com-обвязку для этого на с++, а на с++\cli интероп с c# кодом. не советую писать всё на .net
что тебя так развеселило? Есть положительный опыт написания com local server полностью на .net? Так покажи нам