Skip to content

Commit

Permalink
Added keyhook code, reworked things to use it.
Browse files Browse the repository at this point in the history
  • Loading branch information
icculus committed Dec 24, 2013
1 parent c88cba1 commit d1bd522
Show file tree
Hide file tree
Showing 5 changed files with 238 additions and 50 deletions.
22 changes: 22 additions & 0 deletions 1pass.c
Expand Up @@ -9,6 +9,7 @@
#include "aes.h"
#include "base64.h"
#include "md5.h"
#include "keyhook.h"
#include <gtk/gtk.h>

#define STATICARRAYLEN(x) ( (sizeof ((x))) / (sizeof ((x)[0])) )
Expand Down Expand Up @@ -287,8 +288,29 @@ static int popupGuiMenu(lua_State *L)
} // popupGuiMenu


static void keyhookPressed(void)
{
lua_getglobal(luaState, "keyhookPressed");
lua_call(luaState, 0, 0);
} // keyhookPressed


static gboolean keyhookPumper(void *arg)
{
if (pumpKeyHook())
keyhookPressed();
return TRUE; // keep firing timer
} // keyhookPumper


static int giveControlToGui(lua_State *L)
{
if (initKeyHook())
{
atexit(deinitKeyHook);
g_timeout_add(200, (GSourceFunc) keyhookPumper, (gpointer) NULL);
} // if

gtk_main();
return 0;
} // giveControlToGui
Expand Down
119 changes: 69 additions & 50 deletions 1pass.lua
@@ -1,6 +1,11 @@
JSON = (loadfile "JSON.lua")()
dofile("dumptable.lua")

local basedir = "1Password/1Password.agilekeychain/data/default" -- !!! FIXME
local password = argv[2]
local items = nil
local keyhookRunning = false

local passwordTypeNameMap = {
["webforms.WebForm"] = "Logins",
["wallet.financial.CreditCard"] = "Credit cards",
Expand Down Expand Up @@ -42,7 +47,7 @@ end


local keys = {}
function loadKey(basedir, level, password)
local function loadKey(level, password)
if keys[level] ~= nil then
return keys[level]
end
Expand Down Expand Up @@ -78,7 +83,7 @@ function loadKey(basedir, level, password)
return nil
end

local function getHint(basedir)
local function getHint()
local f = io.open(basedir .. "/.password.hint", "r")
if (f == nil) then
return
Expand All @@ -91,7 +96,7 @@ local function getHint(basedir)
end


function loadContents(basedir)
local function loadContents()
return load_json(basedir .. "/contents.js")
end

Expand All @@ -109,6 +114,7 @@ local function build_secret_menuitem(menu, type, str, hidden)
local callback = function()
copyToClipboard(str)
--print("Copied data [" .. str .. "] to clipboard.")
keyhookRunning = false
end
return appendGuiMenuItem(menu, text, callback)
end
Expand Down Expand Up @@ -208,13 +214,13 @@ end
secret_menuitem_builders["wallet.financial.CreditCard"] = build_secret_menuitem_creditcard


local function build_secret_menuitems(basedir, info, menu, password)
local function build_secret_menuitems(info, menu)
local metadata = load_json(basedir .. "/" .. info.uuid .. ".1password")
if metadata == nil then
return
end

local plaintext = decryptBase64UsingKey(metadata.encrypted, loadKey(basedir, metadata.securityLevel, password))
local plaintext = decryptBase64UsingKey(metadata.encrypted, loadKey(metadata.securityLevel, password))
if plaintext == nil then
return
end
Expand All @@ -238,60 +244,73 @@ local function build_secret_menuitems(basedir, info, menu, password)
setGuiMenuItemSubmenu(menuitem, submenu)
end


-- Mainline!

--for i,v in ipairs(argv) do
-- print("argv[" .. i .. "] = " .. v)
--end

local basedir = "1Password/1Password.agilekeychain/data/default" -- !!! FIXME

local password = argv[2]
while password == nil do
password = runGuiPasswordPrompt(getHint(basedir))
if password == nil then
os.exit(1)
end
if loadKey(basedir, "SL5", password) == nil then
password = nil -- wrong password
local start = os.time() -- cook the CPU for three seconds.
local now = start
while os.difftime(now, start) < 3 do
now = os.time()
local function prepItems()
items = {}
local contents = loadContents()
for i,v in ipairs(contents) do
local t = v[2]
if items[t] == nil then
items[t] = {}
end
local bucket = items[t]
bucket[#bucket+1] = { uuid=v[1], type=t, name=v[3], url=v[4] } -- !!! FIXME: there are more fields, don't know what they mean yet.
end
end

local contents = loadContents(basedir)
local items = {}
for i,v in ipairs(contents) do
local t = v[2]
if items[t] == nil then
items[t] = {}
function keyhookPressed() -- not local! Called from C!
if keyhookRunning then
return
end
local bucket = items[t]
bucket[#bucket+1] = { uuid=v[1], type=t, name=v[3], url=v[4] } -- !!! FIXME: there are more fields, don't know what they mean yet.
end
contents = nil

local topmenu = makeGuiMenu()
for orderi,type in ipairs(passwordTypeOrdering) do
local bucket = items[type]
local realname = passwordTypeNameMap[type]
if realname == nil then
realname = type

keyhookRunning = true

while password == nil do
password = runGuiPasswordPrompt(getHint())
if password == nil then
keyhookRunning = false
return
end
if loadKey("SL5", password) == nil then
password = nil -- wrong password
local start = os.time() -- cook the CPU for three seconds.
local now = start
while os.difftime(now, start) < 3 do
now = os.time()
end
end
end
local menuitem = appendGuiMenuItem(topmenu, realname)
local submenu = makeGuiMenu()
table.sort(bucket, function(a, b) return a.name < b.name end)
for i,v in pairs(bucket) do
build_secret_menuitems(basedir, v, submenu, password)

prepItems()

local topmenu = makeGuiMenu()
for orderi,type in ipairs(passwordTypeOrdering) do
local bucket = items[type]
local realname = passwordTypeNameMap[type]
if realname == nil then
realname = type
end
local menuitem = appendGuiMenuItem(topmenu, realname)
local submenu = makeGuiMenu()
table.sort(bucket, function(a, b) return a.name < b.name end)
for i,v in pairs(bucket) do
build_secret_menuitems(v, submenu)
end
setGuiMenuItemSubmenu(menuitem, submenu)
end
setGuiMenuItemSubmenu(menuitem, submenu)

popupGuiMenu(topmenu)
end

popupGuiMenu(topmenu)

-- Mainline!

--for i,v in ipairs(argv) do
-- print("argv[" .. i .. "] = " .. v)
--end

-- !!! FIXME: message box, exit if basedir is wack.
-- !!! FIXME: this can probably happen in C now (the Lua mainline is basically gone now).
--print("Now waiting for keyhook.")
giveControlToGui()

-- end of 1pass.lua ...
Expand Down
2 changes: 2 additions & 0 deletions CMakeLists.txt
Expand Up @@ -75,6 +75,7 @@ include_directories("lua")

add_executable(1pass
1pass.c
keyhook.c
pkcs5_pbkdf2.c
aes.c
md5.c
Expand Down Expand Up @@ -121,6 +122,7 @@ target_link_libraries(1pass ${PKG_GTKPLUS2_LIBRARIES})
if(LINUX)
set_target_properties(1pass PROPERTIES LINK_FLAGS "-Wl,-rpath,$ORIGIN")
target_link_libraries(1pass "m")
target_link_libraries(1pass "Xtst")
endif()

# end of CMakeLists.txt ...
Expand Down
134 changes: 134 additions & 0 deletions keyhook.c
@@ -0,0 +1,134 @@
// !!! FIXME: this is X11 specific. :(

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
#include <unistd.h>

#include <X11/Xlib.h>
#include <X11/Xlibint.h>
#include <X11/extensions/record.h>

#include "keyhook.h"

static volatile int keyPressFlags = 0;
static volatile int sawKeyCombo = 0;
static void keyhookCallback(XPointer priv, XRecordInterceptData *data)
{
const xEvent *xev = (const xEvent *) data->data;
if (data->category == XRecordFromServer)
{
const BYTE keycode = xev->u.u.detail;
if (xev->u.u.type == KeyPress)
{
// !!! FIXME: don't hardcode these keycodes.
if ((keycode == 64) && (keyPressFlags == 0))
keyPressFlags++;
else if ((keycode == 133) && (keyPressFlags == 1))
keyPressFlags++;
else if ((keycode == 51) && (keyPressFlags == 2))
{
sawKeyCombo = 1;
keyPressFlags = 0;
} // else if
else
keyPressFlags = 0;
} // if
else if (xev->u.u.type == KeyRelease)
{
keyPressFlags = 0;
} // else if
} // if

XRecordFreeData(data);
} // keyhookCallback


// every example I've seen needs two Display connections...one for the
// keyhook, and one to control it.
static Display *ctrldpy = NULL;
static Display *datadpy = NULL;
static XRecordContext xrc = 0;

int initKeyHook(void)
{
int major = 0;
int minor = 0;
XRecordRange *xrr = NULL;
XRecordClientSpec xrcs = XRecordAllClients;

if (ctrldpy)
return 0; // already initialized.

ctrldpy = XOpenDisplay(NULL);
if (!ctrldpy)
goto failed;

XSynchronize(ctrldpy, True);

datadpy = XOpenDisplay(NULL);
if (!datadpy)
goto failed;
else if (!XRecordQueryVersion(ctrldpy, &major, &minor))
goto failed;
else if ((xrr = XRecordAllocRange()) == NULL)
goto failed;

memset(xrr, '\0', sizeof (*xrr));
xrr->device_events.first = KeyPress;
xrr->device_events.last = KeyPress;

if ((xrc = XRecordCreateContext(ctrldpy, 0, &xrcs, 1, &xrr, 1)) == 0)
goto failed;
else if (!XRecordEnableContextAsync(datadpy, xrc, keyhookCallback, NULL))
goto failed;

XFree(xrr);
xrr = NULL;

return 1;

failed:
deinitKeyHook();
if (xrr) XFree(xrr);

return 0;
} // initKeyHook


void deinitKeyHook(void)
{
if (ctrldpy)
{
if (xrc)
{
XRecordDisableContext(ctrldpy, xrc);
XRecordFreeContext(ctrldpy, xrc);
} // if
XCloseDisplay(ctrldpy);
} // if

if (datadpy)
XCloseDisplay(datadpy);

ctrldpy = NULL;
datadpy = NULL;
xrc = 0;
sawKeyCombo = 0;
keyPressFlags = 0;
} // deinitKeyHook


int pumpKeyHook(void)
{
if (!datadpy)
return 0;

sawKeyCombo = 0;
XRecordProcessReplies(datadpy);
return sawKeyCombo;
} // pumpKeyHook

// end of keyhook.c ...

11 changes: 11 additions & 0 deletions keyhook.h
@@ -0,0 +1,11 @@
#ifndef _INCL_KEYHOOK_H_
#define _INCL_KEYHOOK_H_

int pumpKeyHook(void);
void deinitKeyHook(void);
int initKeyHook(void);

#endif

// end of keyhook.h ...

0 comments on commit d1bd522

Please sign in to comment.