I'm having an issue with a COM based client-server setup. The COM server is written in C# (.NET 4.0) and runs as a (registered) local server.
Depending on which application connects to the server, other clients will receive a Server execution failed (Exception from HRESULT: 0x80080005 (CO_E_SERVER_EXEC_FAILURE)
The underlying issue is explained here (in the section COM is integrity aware). The way I understand it, it is being caused by the fact that an elevated application creates the server with a higher integrity level. When another non-elevated application then connects, it is not allowed to connect to the same instance. The same happens when a non-elevated application creates the process, followed an elevated application connecting.
I've tried to implement the solution described on the page: modifying the registry to set a security descriptor that should allow all clients to connect. There is a code sample in C++, but this does effectively the same thing in .NET:
// Security Descriptor with NO_EXECUTE_UP
var sd = new RawSecurityDescriptor("O:BAG:BAD:(A;;0xb;;;WD)S:(ML;;NX;;;LW)");
byte[] securityDescriptor = new Byte[sd.BinaryLength];
sd.GetBinaryForm(securityDescriptor, 0);
RegistryKey key = Registry.ClassesRoot.OpenSubKey("AppID\\{APP-ID-GUID}", true);
if (key == null)
{
key = Registry.ClassesRoot.CreateSubKey("AppID\\{APP-ID-GUID}");
}
using (key)
{
key.SetValue("LaunchPermission", securityDescriptor, RegistryValueKind.Binary);
}
However, this does not have the desired effect. When the second client tries to create an instance of the object in question, Windows tries to launch a separate instance of my COM Server, but the server prevents two instances from running as the same user. Given the permissions I've set, I would not expect a second instance to launch in the first place.
Since one of the client applications is running in Medium IL and the other in High IL, I also experimented with variants on the mandatory label, like:
O:BAG:BAD:(A;;0xb;;;WD)S:(ML;;NX;;;ME)
O:BAG:BAD:(A;;0xb;;;WD)S:(ML;;NX;;;LW)(ML;;NX;;;ME)(ML;;NX;;;HI)
I've also tried setting the ROTFlags registry key to 0x1 (ROTFLAGS_ALLOWANYCLIENT) as suggested on the page, still no change in behavior.
I've established that the LaunchPermission registry value is being used in some way. I cannot discover where it's being read using Process Monitor, but when I use the dcomcnfg.exe tool to set the same key, I can force the server to fail loading by denying launch permissions.
I would like to point out that my server process does not need elevation. How do I make both elevated and non-elevated processes capable of connecting to a single server instance?
According to Windows Vista Security Model Analysis you will need to use shared objects such as a named pipe to go between the different IL. Also, the shared object should have an IL equivalent to your lowest IL being used.
you have to Set Debug option to Any cpu in VS.
Related
I have a web service that uses SSH.NET to call a shell script on a Unix box.
If I run the script normally, it works fine, does its work correctly on the Informix DB.
Just some background:
I call a script that executes a .4gl (cant show this as its business knowledge).
The g4l is giving the following error back in a log, when I execute it with SSH.NET:
fglgo: error while loading shared libraries: libiffgisql.so: cannot open shared object file: No such file or directory
file_load ended: 2017-09-21 15:37:01
C# code to execute SSH.NET script
sshclients = new SshClient(p, 22, username, password);
sshclients.Connect();
sshclients.KeepAliveInterval = new TimeSpan(0, 0, 1);
sshclients.RunCommand("sh " + Script_dir);
I added the KeepAliveInterval, to see, if it helps.
My question is the error I am getting from Unix/4gl.
Why is this happening and who can I get the script to execute correctly?
The SshClient.RunCommand uses SSH "exec" channel internally. It, by default, (rightfully) does not allocate a pseudo terminal (PTY) for the session. As a consequence a different set of startup scripts is (might be) sourced. And/or different branches in the scripts are taken, based on absence/presence of the TERM environment variable. So the environment might differ from the interactive session, you use with your SSH client.
So, in your case, the PATH is probably set differently; and consequently the shared object cannot be found.
To verify that this is the root cause, disable the pseudo terminal allocation in your SSH client. For example in PuTTY, it's Connection > SSH > TTY > Don't allocate a pseudo terminal. Then, go to Connection > SSH > Remote command and enter your g4l command. Check Session > Close window on exit > Never and open the session. You should get the same "No such file or directory" error.
Ways to fix this, in preference order:
Fix the scripts not to rely on a specific environment.
Fix your startup scripts to set the PATH the same for both interactive and non-interactive sessions.
If the command itself relies on a specific environment setup and you cannot fix the startup scripts, you can change the environment in the command itself. Syntax for that depends on the remote system and/or the shell. In common *nix systems, this works:
sshclients.RunCommand("PATH=\"$PATH;/path/to/g4l\" && sh ...");
Another (not recommended) approach is to force the pseudo terminal allocation for the "exec" channel.
Though SSH.NET does not support this. You would have to modify its code issue SendPseudoTerminalRequest request in .RunCommand implementation (I didn't test this).
You can also try to use "shell" channel using .CreateShell method. For it, SSH.NET does support pseudo terminal allocation.
Though, using the pseudo terminal to automate a command execution can bring you nasty side effects. See for example Is there a simple way to get rid of junk values that come when you SSH using Python's Paramiko library and fetch output from CLI of a remote machine?
For a similar issues, see
Renci SSH.NET - no result string returned for opmnctl
Certain Unix commands fail with "... not found", when executed through Java using JSch
Commands executed using JSch behaves differently than in SSH terminal (bypasses confirm prompt message of "yes/"no")
JSch: Is there a way to expose user environment variables to "exec" channel?
Have seen similar questions asked by Informix-4gl developers as they transition to FourJs Genero and using its Web Services functionality. The question I'll put to them is "who owns the fglgo/fglrun process that the Genero Application Server has launched, where is it running from, and what is its environment". If needed, I'll illustrate with a simple program that does something like ...
MAIN
RUN "env > /tmp/myname.txt"
RUN "who >> /tmp/myname.txt"
RUN "pwd >> /tmp/myname.txt"
END MAIN
... and say compare with when program is running from command line. It is normally a case like in the earlier answer of configuring so that the environment is set correctly before the 4gl program is executed.
I am taking what was traditionally a single-instance-only application and updating it so multiple instances can execute on the same PC. Each instance will require exclusive access to a USB device. The first application instance opens a USB device with the following command (some Kernel32 wrappers for C# are in place) and gets a valid handle, as expected:
this.handle = Kernel32.CreateFile(
pathToUsbDevice,
Convert.ToUInt32(FileRead.Read | FileRead.Write),
Convert.ToUInt32(FileShare.None),
IntPtr.Zero,
Convert.ToUInt32(CreateDisposition.OpenExisting),
Convert.ToUInt32(FileFlags.Overlapped),
IntPtr.Zero);
The problem is that I then open the second instance of the application and it is able to also get a valid handle to the same device (while the first instance still has its handle). This seems incorrect to me, as my understanding of CreateFile is that it should return an invalid file handle if that device is already opened exclusively (sharing set to 0, or None).
Am I doing something wrong in CreateFile? Perhaps my assumption that it will return an invalid handle if the device is already exclusively opened is incorrect? How can I exclusively open the device for a single application instance so that other application instances cannot open it?
In case it matters, the development & test PC is Windows 7 Professional, 64-bit, building the app via Visual Studio Express 2013.
Looks like it is a device driver issue. I've found a similar discussion:
It's up to the driver to manage shared/non-shared, not the IO manager. So you
have two choices:
Mark your device exclusive in the INF (see the Exclusive registry value under
http://msdn.microsoft.com/en-us/library/windows/hardware/ff546320(v=3Dvs.85).aspx
)
Add file create/close handlers in your driver and manage the count of clients
yourself
You might want to use some other approach to ensure only one instance of your app is accessing device (like using a named global mutex to guard device access).
I'm developing an open source .NET assembly (WinSCP .NET assembly) that spawns a native (C++) application and communicates with it via events and file mapping objects.
The assembly spawns the application using the Process class, with no special settings. The assembly creates few events (using the EventWaitHandle) and file mapping (using the PInvoked CreateFileMapping) and the application "opens" these using the OpenEvent and the OpenFileMapping.
It works fine in most cases. But now I'm having a user that uses the assembly from an ASPX application on Windows Server 2008 R2 64 bit.
In his case both the OpenEvent and the OpenFileMapping return NULL and the GetLastError returns the ERROR_ACCESS_DENIED.
I have tried to improve the assembly code by explicitly granting the current user necessary permissions to the event objects and the application code to require only the really needed access rights (instead of original EVENT_ALL_ACCESS) as per Microsoft Docs example. It didn't help. So I did not even bother to try the same for the file mapping object.
The C# code that creates the event is:
EventWaitHandleSecurity security = new EventWaitHandleSecurity();
string user = Environment.UserDomainName + "\\" + Environment.UserName;
EventWaitHandleAccessRule rule;
rule =
new EventWaitHandleAccessRule(
user, EventWaitHandleRights.Synchronize | EventWaitHandleRights.Modify,
AccessControlType.Allow);
security.AddAccessRule(rule);
rule =
new EventWaitHandleAccessRule(
user, EventWaitHandleRights.ChangePermissions, AccessControlType.Deny);
security.AddAccessRule(rule);
new EventWaitHandle(
false, EventResetMode.AutoReset, name, out createdNew, security);
The C++ code that "opens" the events is:
OpenEvent(EVENT_MODIFY_STATE, false, name);
(For other events the access level is SYNCHRONIZE, depending on needs).
I have also tried to add Global\ prefix to the object names. As expected this didn't solve the problem.
Does anyone have any idea what causes the "access denied" error in OpenEvent (or CreateFileMapping)?
My guess is that the event is created by either the anonymous user or the logged in user depending on how the website is setup. But the sub-process is being launched with the base process user. This can be checked by using process monitor and looking at the acl for the event handle to see who the creator is. Then look at the sub process to see who it is running as.
If this is the case then you can update the acl on the event to include the base process. In addition to this, you may still need to prefix with "global" to make sure that the event can be used across user boundaries.
I have a COM server running in its own .exe that provides an optional interface. If the user hasn't started the program, I don't want to start it for them. Unfortunately it does start automatically if I use the following code:
Type typ = Type.GetTypeFromProgID(prog_id);
server = (IMyServer)Activator.CreateInstance(typ);
I'm already using Process.GetProcessesByName to see if the executable is running, but there are cases where the COM connection can't be made even if the process is detected. Is there a more direct method to prevent it from starting the server?
I'm working on a Mono application that will run on Linux, Mac, and Windows, and need the ability for apps (on a single os) to send simple string messages to each other.
Specifically, I want a Single Instance Application. If a second instance is attempted to be started, it will instead send a message to the single instance already running.
DBus is out, as I don't want to have that be an additional requirement.
Socket communication seems to be hard, as windows seems to not allow permission to connect.
Memory Mapped Files seems not to be supported in Mono.
Named Pipes appears not to be supported in Mono.
IPC seems not to be supported on Mono.
So, is there a simple method to send string messages on a single machine to a server app that works on each os, without requiring permissions, or additional dependencies?
On my ubuntu (10.10 mono version: 2.6.7) I've tried using WCF for interprocess communication with BasicHttpBinding, NetTcpBinding and NetNamedPipeBinding. First 2 worked fine, for NetNamedPipeBinding I got an error:
Channel type IDuplexSessionChannel is
not supported
when calling ChannelFactory.CreateChannel() method.
I've also tried using Remoting (which is a legacy technology since WCF came out) with IpcChannel; example from this msdn page started and worked without problems on my machine.
I suppose you shouldn't have problems using WCF or Remoting on Windows either, not sure about Mac though, don't have any of those around to test. Let me know if you need any code examples.
hope this helps, regards
I wrote about this on the mono-dev mailing list. Several general-purpose inter-process messaging systems were considered, including DBus, System.Threading.Mutex class, WCF, Remoting, Named Pipes... The conclusions were basically mono doesn't support Mutex class (works for inter-thread, not for inter-process) and there's nothing platform agnostic available.
I have only been able to imagine three possible solutions. All have their drawbacks. Maybe there's a better solution available, or maybe just better solutions for specific purposes, or maybe there exist some cross-platform 3rd party libraries you could include in your app (I don't know.) But these are the best solutions I've been able to find so far:
Open or create a file in a known location, with exclusive lock. (FileShare.None). Each application tries to open the file, do its work, and close the file. If failing to open, Thread.Sleep(1) and try again. This is kind of ghetto, but it works cross-platform to provide inter-process mutex.
Sockets. First application listens on localhost, some high numbered port. Second application attempts to listen on that port, fails to open (because some other process already has it) so second process sends a message to the first process, which is already listening on that port.
If you have access to a transactional database, or message passing system (sqs, rabbitmq, etc) use it.
Of course, you could detect which platform you're on, and then use whatever works on that platform.
Solved my problem with two techniques: a named mutex (so that the app can be run on the same machine by different users), and a watcher on a message file. The file is opened and written to for communication. Here is a basic solution, written in IronPython 2.6:
(mutex, locked) = System.Threading.Mutex(True, "MyApp/%s" % System.Environment.UserName, None)
if locked:
watcher = System.IO.FileSystemWatcher()
watcher.Path = path_to_user_dir
watcher.Filter = "messages"
watcher.NotifyFilter = System.IO.NotifyFilters.LastWrite
watcher.Changed += handleMessages
watcher.EnableRaisingEvents = True
else:
messages = os.path.join(path_to_user_dir, "messages")
fp = file(messages, "a")
fp.write(command)
fp.close()
sys.exit(0)
For your simple reason for needing IPC, I'd look for another solution.
This code is confirmed to work on Linux and Windows. Should work on Mac as well:
public static IList Processes()
{
IList<Process> processes = new List<Process>();
foreach (System.Diagnostics.Process process in System.Diagnostics.Process.GetProcesses())
{
Process p = new Process();
p.Pid = process.Id;
p.Name = process.ProcessName;
processes.Add(p);
}
return processes;
}
Just iterate through the list and look for your own ProcessName.
To send a message to your application, just use MyProcess.StandardInput to write to the applications standard input. This only works assuming your application is a GUI application though.
If you have problems with that, then you could maybe use a specialized "lock" file. Using the FileSystemWatcher class you can check when it changes. This way the second instance could write a message in the file and then the first instance notice that it changes and can read in the contents of the file to get a message.