mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-08 18:54:09 -04:00
Use authentication for the named pipe in the windows file dialogs
This commit is contained in:
parent
55aca490a4
commit
d8e131ad08
@ -17,6 +17,7 @@
|
|||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
|
|
||||||
#define PRINTERR(x) fprintf(stderr, "%s", x); fflush(stderr);
|
#define PRINTERR(x) fprintf(stderr, "%s", x); fflush(stderr);
|
||||||
|
#define SECRET_SIZE 32
|
||||||
|
|
||||||
bool write_bytes(HANDLE pipe, DWORD sz, const char* buf) {
|
bool write_bytes(HANDLE pipe, DWORD sz, const char* buf) {
|
||||||
DWORD written = 0;
|
DWORD written = 0;
|
||||||
@ -102,7 +103,7 @@ static void print_com_error(HRESULT hr, const char *msg) {
|
|||||||
#define REPORTERR(hr, x) { print_com_error(hr, x); ret = 1; goto error; }
|
#define REPORTERR(hr, x) { print_com_error(hr, x); ret = 1; goto error; }
|
||||||
#define CALLCOM(x, err) hr = x; if(FAILED(hr)) REPORTERR(hr, err)
|
#define CALLCOM(x, err) hr = x; if(FAILED(hr)) REPORTERR(hr, err)
|
||||||
|
|
||||||
int show_dialog(HANDLE pipe, HWND parent, bool save_dialog, LPWSTR title, LPWSTR folder, LPWSTR filename, LPWSTR save_path, bool multiselect, bool confirm_overwrite, bool only_dirs, bool no_symlinks, COMDLG_FILTERSPEC *file_types, UINT num_file_types) {
|
int show_dialog(HANDLE pipe, char *secret, HWND parent, bool save_dialog, LPWSTR title, LPWSTR folder, LPWSTR filename, LPWSTR save_path, bool multiselect, bool confirm_overwrite, bool only_dirs, bool no_symlinks, COMDLG_FILTERSPEC *file_types, UINT num_file_types) {
|
||||||
int ret = 0, name_sz = 0;
|
int ret = 0, name_sz = 0;
|
||||||
IFileDialog *pfd = NULL;
|
IFileDialog *pfd = NULL;
|
||||||
IShellItemArray *items = NULL;
|
IShellItemArray *items = NULL;
|
||||||
@ -155,11 +156,13 @@ int show_dialog(HANDLE pipe, HWND parent, bool save_dialog, LPWSTR title, LPWSTR
|
|||||||
path = to_utf8(name, &name_sz);
|
path = to_utf8(name, &name_sz);
|
||||||
CoTaskMemFree(name); name = NULL;
|
CoTaskMemFree(name); name = NULL;
|
||||||
if (path == NULL) return 1;
|
if (path == NULL) return 1;
|
||||||
|
if (!write_bytes(pipe, SECRET_SIZE+1, secret)) return 1;
|
||||||
if (!write_bytes(pipe, name_sz, path)) return 1;
|
if (!write_bytes(pipe, name_sz, path)) return 1;
|
||||||
} else {
|
} else {
|
||||||
CALLCOM(((IFileOpenDialog*)pfd)->GetResults(&items), "Failed to get dialog results");
|
CALLCOM(((IFileOpenDialog*)pfd)->GetResults(&items), "Failed to get dialog results");
|
||||||
CALLCOM(items->GetCount(&item_count), "Failed to get count of results");
|
CALLCOM(items->GetCount(&item_count), "Failed to get count of results");
|
||||||
if (item_count > 0) {
|
if (item_count > 0) {
|
||||||
|
if (!write_bytes(pipe, SECRET_SIZE+1, secret)) return 1;
|
||||||
for (DWORD i = 0; i < item_count; i++) {
|
for (DWORD i = 0; i < item_count; i++) {
|
||||||
CALLCOM(items->GetItemAt(i, &item), "Failed to get result item");
|
CALLCOM(items->GetItemAt(i, &item), "Failed to get result item");
|
||||||
if (SUCCEEDED(item->GetDisplayName(SIGDN_FILESYSPATH, &name))) {
|
if (SUCCEEDED(item->GetDisplayName(SIGDN_FILESYSPATH, &name))) {
|
||||||
@ -199,7 +202,7 @@ HANDLE open_named_pipe(LPWSTR pipename) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PWSTR pCmdLine, int nCmdShow) {
|
int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PWSTR pCmdLine, int nCmdShow) {
|
||||||
char buf[257] = {0};
|
char buf[257] = {0}, secret[SECRET_SIZE + 1] = {0};
|
||||||
size_t key_size = 0;
|
size_t key_size = 0;
|
||||||
HWND parent = NULL;
|
HWND parent = NULL;
|
||||||
bool save_dialog = false, multiselect = false, confirm_overwrite = false, only_dirs = false, no_symlinks = false;
|
bool save_dialog = false, multiselect = false, confirm_overwrite = false, only_dirs = false, no_symlinks = false;
|
||||||
@ -230,6 +233,8 @@ int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PWSTR pCmdLine
|
|||||||
|
|
||||||
else if CHECK_KEY("PIPENAME") { READSTR(pipename); pipe = open_named_pipe(pipename); if (pipe == INVALID_HANDLE_VALUE) return 1; }
|
else if CHECK_KEY("PIPENAME") { READSTR(pipename); pipe = open_named_pipe(pipename); if (pipe == INVALID_HANDLE_VALUE) return 1; }
|
||||||
|
|
||||||
|
else if CHECK_KEY("SECRET") { if(!read_bytes(SECRET_SIZE, secret)) return 1; }
|
||||||
|
|
||||||
else if CHECK_KEY("TITLE") { READSTR(title) }
|
else if CHECK_KEY("TITLE") { READSTR(title) }
|
||||||
|
|
||||||
else if CHECK_KEY("FOLDER") { READSTR(folder) }
|
else if CHECK_KEY("FOLDER") { READSTR(folder) }
|
||||||
@ -259,12 +264,14 @@ int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PWSTR pCmdLine
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (pipe == INVALID_HANDLE_VALUE) { PRINTERR("No pipename received"); return 1; }
|
if (pipe == INVALID_HANDLE_VALUE) { PRINTERR("No pipename received"); return 1; }
|
||||||
|
if (secret == NULL) { PRINTERR("No secret received"); return 1; }
|
||||||
|
|
||||||
if (echo != NULL) {
|
if (echo != NULL) {
|
||||||
int echo_sz = 0;
|
int echo_sz = 0;
|
||||||
char *echo_buf = to_utf8(echo, &echo_sz);
|
char *echo_buf = to_utf8(echo, &echo_sz);
|
||||||
|
if (!write_bytes(pipe, SECRET_SIZE+1, secret)) return 1;
|
||||||
return write_bytes(pipe, echo_sz, echo_buf) ? 0 : 1;
|
return write_bytes(pipe, echo_sz, echo_buf) ? 0 : 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
return show_dialog(pipe, parent, save_dialog, title, folder, filename, save_path, multiselect, confirm_overwrite, only_dirs, no_symlinks, file_types, num_file_types);
|
return show_dialog(pipe, secret, parent, save_dialog, title, folder, filename, save_path, multiselect, confirm_overwrite, only_dirs, no_symlinks, file_types, num_file_types);
|
||||||
}
|
}
|
||||||
|
@ -39,6 +39,9 @@ def serialize_hwnd(hwnd):
|
|||||||
return b''
|
return b''
|
||||||
return struct.pack(b'=B4s' + (b'Q' if is64bit else b'I'), 4, b'HWND', int(hwnd))
|
return struct.pack(b'=B4s' + (b'Q' if is64bit else b'I'), 4, b'HWND', int(hwnd))
|
||||||
|
|
||||||
|
def serialize_secret(secret):
|
||||||
|
return struct.pack(b'=B6s32s', 6, b'SECRET', secret)
|
||||||
|
|
||||||
def serialize_binary(key, val):
|
def serialize_binary(key, val):
|
||||||
key = key.encode('ascii') if not isinstance(key, bytes) else key
|
key = key.encode('ascii') if not isinstance(key, bytes) else key
|
||||||
return struct.pack(b'=B%ssB' % len(key), len(key), key, int(val))
|
return struct.pack(b'=B%ssB' % len(key), len(key), key, int(val))
|
||||||
@ -110,8 +113,9 @@ def run_file_dialog(
|
|||||||
from calibre.gui2 import sanitize_env_vars
|
from calibre.gui2 import sanitize_env_vars
|
||||||
with sanitize_env_vars():
|
with sanitize_env_vars():
|
||||||
env = os.environ.copy()
|
env = os.environ.copy()
|
||||||
|
secret = os.urandom(32)
|
||||||
pipename = '\\\\.\\pipe\\%s' % uuid4()
|
pipename = '\\\\.\\pipe\\%s' % uuid4()
|
||||||
data = [serialize_string('PIPENAME', pipename)]
|
data = [serialize_string('PIPENAME', pipename), serialize_secret(secret)]
|
||||||
parent = parent or None
|
parent = parent or None
|
||||||
if parent is not None:
|
if parent is not None:
|
||||||
data.append(serialize_hwnd(get_hwnd(parent)))
|
data.append(serialize_hwnd(get_hwnd(parent)))
|
||||||
@ -165,9 +169,11 @@ def run_file_dialog(
|
|||||||
except Exception:
|
except Exception:
|
||||||
x = repr(x)
|
x = repr(x)
|
||||||
return x
|
return x
|
||||||
|
def get_errors():
|
||||||
|
return decode(h.stdoutdata) + ' ' + decode(h.stderrdata)
|
||||||
|
|
||||||
if h.rc != 0:
|
if h.rc != 0:
|
||||||
raise Exception('File dialog failed: ' + decode(h.stdoutdata) + ' ' + decode(h.stderrdata))
|
raise Exception('File dialog failed: ' + get_errors())
|
||||||
server.join(2)
|
server.join(2)
|
||||||
if server.is_alive():
|
if server.is_alive():
|
||||||
raise Exception('Timed out waiting for read from pipe to complete')
|
raise Exception('Timed out waiting for read from pipe to complete')
|
||||||
@ -175,7 +181,12 @@ def run_file_dialog(
|
|||||||
raise Exception(server.err_msg)
|
raise Exception(server.err_msg)
|
||||||
if not server.data:
|
if not server.data:
|
||||||
return ()
|
return ()
|
||||||
ans = tuple((os.path.abspath(x.decode('utf-8')) for x in server.data.split(b'\0') if x))
|
parts = list(filter(None, server.data.split(b'\0')))
|
||||||
|
if len(parts) < 2:
|
||||||
|
return ()
|
||||||
|
if parts[0] != secret:
|
||||||
|
raise Exception('File dialog failed, incorrect secret received: ' + get_errors())
|
||||||
|
ans = tuple((os.path.abspath(x.decode('utf-8')) for x in parts[1:]))
|
||||||
return ans
|
return ans
|
||||||
|
|
||||||
def get_initial_folder(name, title, default_dir='~', no_save_dir=False):
|
def get_initial_folder(name, title, default_dir='~', no_save_dir=False):
|
||||||
@ -289,16 +300,20 @@ class PipeServer(Thread):
|
|||||||
def test(helper=HELPER):
|
def test(helper=HELPER):
|
||||||
pipename = '\\\\.\\pipe\\%s' % uuid4()
|
pipename = '\\\\.\\pipe\\%s' % uuid4()
|
||||||
echo = '\U0001f431 Hello world!'
|
echo = '\U0001f431 Hello world!'
|
||||||
data = serialize_string('PIPENAME', pipename) + serialize_string('ECHO', echo)
|
secret = os.urandom(32)
|
||||||
|
data = serialize_string('PIPENAME', pipename) + serialize_string('ECHO', echo) + serialize_secret(secret)
|
||||||
server = PipeServer(pipename)
|
server = PipeServer(pipename)
|
||||||
p = subprocess.Popen([helper], stdout=subprocess.PIPE, stdin=subprocess.PIPE, stderr=subprocess.PIPE)
|
p = subprocess.Popen([helper], stdout=subprocess.PIPE, stdin=subprocess.PIPE, stderr=subprocess.PIPE)
|
||||||
stdout, stderr = p.communicate(data)
|
stdout, stderr = p.communicate(data)
|
||||||
if p.wait() != 0:
|
if p.wait() != 0:
|
||||||
raise Exception('File dialog failed: ' + stderr.decode('utf-8'))
|
raise Exception('File dialog failed: ' + stdout.decode('utf-8') + ' ' + stderr.decode('utf-8'))
|
||||||
if server.err_msg is not None:
|
if server.err_msg is not None:
|
||||||
raise RuntimeError(server.err_msg)
|
raise RuntimeError(server.err_msg)
|
||||||
server.join(2)
|
server.join(2)
|
||||||
q = server.data[:-1].decode('utf-8')
|
parts = filter(None, server.data.split(b'\0'))
|
||||||
|
if parts[0] != secret:
|
||||||
|
raise RuntimeError('Did not get back secret: %r != %r' % (secret, parts[0]))
|
||||||
|
q = parts[1].decode('utf-8')
|
||||||
if q != echo:
|
if q != echo:
|
||||||
raise RuntimeError('Unexpected response: %r' % server.data)
|
raise RuntimeError('Unexpected response: %r' % server.data)
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user