ipc-win: restrict read access to the IPC pipe

The default security descriptor for named pipes in Windows allows the
pipe to be opened for read access by the Everyone group and Anonymous
account, as well as low-integrity processes (like web browser renderer
processes.) This does not allow commands to be ran, but it does allow
events to be received.

I don't think any sensitive data is exposed by events, but that may not
always be the case and Lua plugins might change this, since they can
broadcast their own events with script-message. To be safe, this commit
sets a custom security descriptor on the named pipe which only allows
access from processes running under the same user account with an
integrity level greater than or equal to the one used by mpv.
This commit is contained in:
James Ross-Gowan 2016-03-25 21:06:30 +11:00
parent a76f3e8e46
commit ef625a78a4
1 changed files with 96 additions and 2 deletions

View File

@ -16,6 +16,7 @@
*/ */
#include <windows.h> #include <windows.h>
#include <sddl.h>
#include "config.h" #include "config.h"
@ -50,6 +51,88 @@ struct client_arg {
OVERLAPPED write_ol; OVERLAPPED write_ol;
}; };
// Get a string SID representing the current user. Must be freed by LocalFree.
static char *get_user_sid(void)
{
char *ssid = NULL;
TOKEN_USER *info = NULL;
HANDLE t;
if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &t))
goto done;
DWORD info_len;
if (!GetTokenInformation(t, TokenUser, NULL, 0, &info_len) &&
GetLastError() != ERROR_INSUFFICIENT_BUFFER)
goto done;
info = talloc_size(NULL, info_len);
if (!GetTokenInformation(t, TokenUser, info, info_len, &info_len))
goto done;
if (!info->User.Sid)
goto done;
ConvertSidToStringSidA(info->User.Sid, &ssid);
done:
if (t)
CloseHandle(t);
talloc_free(info);
return ssid;
}
// Get a string SID for the process integrity level. Must be freed by LocalFree.
static char *get_integrity_sid(void)
{
char *ssid = NULL;
TOKEN_MANDATORY_LABEL *info = NULL;
HANDLE t;
if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &t))
goto done;
DWORD info_len;
if (!GetTokenInformation(t, TokenIntegrityLevel, NULL, 0, &info_len) &&
GetLastError() != ERROR_INSUFFICIENT_BUFFER)
goto done;
info = talloc_size(NULL, info_len);
if (!GetTokenInformation(t, TokenIntegrityLevel, info, info_len, &info_len))
goto done;
if (!info->Label.Sid)
goto done;
ConvertSidToStringSidA(info->Label.Sid, &ssid);
done:
if (t)
CloseHandle(t);
talloc_free(info);
return ssid;
}
// Create a security descriptor that only grants access to processes running
// under the current user at the current integrity level or higher
static PSECURITY_DESCRIPTOR create_restricted_sd(void)
{
char *user_sid = get_user_sid();
char *integrity_sid = get_integrity_sid();
if (!user_sid || !integrity_sid)
return NULL;
char *sddl = talloc_asprintf(NULL,
"O:%s" // Set the owner to user_sid
"D:(A;;GRGW;;;%s)" // Grant GENERIC_{READ,WRITE} access to user_sid
"S:(ML;;NRNWNX;;;%s)", // Disallow read, write and execute permissions
// to integrity levels below integrity_sid
user_sid, user_sid, integrity_sid);
LocalFree(user_sid);
LocalFree(integrity_sid);
PSECURITY_DESCRIPTOR sd = NULL;
ConvertStringSecurityDescriptorToSecurityDescriptorA(sddl, SDDL_REVISION_1,
&sd, NULL);
talloc_free(sddl);
return sd;
}
static void wakeup_cb(void *d) static void wakeup_cb(void *d)
{ {
HANDLE event = d; HANDLE event = d;
@ -275,6 +358,15 @@ static void *ipc_thread(void *p)
mpthread_set_name("ipc named pipe listener"); mpthread_set_name("ipc named pipe listener");
MP_VERBOSE(arg, "Starting IPC master\n"); MP_VERBOSE(arg, "Starting IPC master\n");
SECURITY_ATTRIBUTES sa = {
.nLength = sizeof sa,
.lpSecurityDescriptor = create_restricted_sd(),
};
if (!sa.lpSecurityDescriptor) {
MP_ERR(arg, "Couldn't create security descriptor");
goto done;
}
OVERLAPPED ol = { .hEvent = CreateEventW(NULL, TRUE, TRUE, NULL) }; OVERLAPPED ol = { .hEvent = CreateEventW(NULL, TRUE, TRUE, NULL) };
if (!ol.hEvent) { if (!ol.hEvent) {
MP_ERR(arg, "Couldn't create event"); MP_ERR(arg, "Couldn't create event");
@ -282,7 +374,7 @@ static void *ipc_thread(void *p)
} }
server = CreateNamedPipeW(arg->path, mode | FILE_FLAG_FIRST_PIPE_INSTANCE, server = CreateNamedPipeW(arg->path, mode | FILE_FLAG_FIRST_PIPE_INSTANCE,
state, PIPE_UNLIMITED_INSTANCES, bufsiz, bufsiz, 0, NULL); state, PIPE_UNLIMITED_INSTANCES, bufsiz, bufsiz, 0, &sa);
if (server == INVALID_HANDLE_VALUE) { if (server == INVALID_HANDLE_VALUE) {
MP_ERR(arg, "Couldn't create first pipe instance: %s\n", MP_ERR(arg, "Couldn't create first pipe instance: %s\n",
mp_LastError_to_str()); mp_LastError_to_str());
@ -333,7 +425,7 @@ static void *ipc_thread(void *p)
// closes the handle and there are no active instances of the pipe // closes the handle and there are no active instances of the pipe
client = server; client = server;
server = CreateNamedPipeW(arg->path, mode, state, server = CreateNamedPipeW(arg->path, mode, state,
PIPE_UNLIMITED_INSTANCES, bufsiz, bufsiz, 0, NULL); PIPE_UNLIMITED_INSTANCES, bufsiz, bufsiz, 0, &sa);
if (server == INVALID_HANDLE_VALUE) { if (server == INVALID_HANDLE_VALUE) {
MP_ERR(arg, "Couldn't create additional pipe instance: %s\n", MP_ERR(arg, "Couldn't create additional pipe instance: %s\n",
mp_LastError_to_str()); mp_LastError_to_str());
@ -345,6 +437,8 @@ static void *ipc_thread(void *p)
} }
done: done:
if (sa.lpSecurityDescriptor)
LocalFree(sa.lpSecurityDescriptor);
if (client != INVALID_HANDLE_VALUE) if (client != INVALID_HANDLE_VALUE)
CloseHandle(client); CloseHandle(client);
if (server != INVALID_HANDLE_VALUE) if (server != INVALID_HANDLE_VALUE)