Skip to content
This repository has been archived by the owner on Nov 4, 2021. It is now read-only.

Commit

Permalink
Merge branch 'release-0.2'
Browse files Browse the repository at this point in the history
  • Loading branch information
leonnnn committed Sep 9, 2013
2 parents dce8dd0 + 77d490a commit 9e1bb08
Show file tree
Hide file tree
Showing 14 changed files with 1,077 additions and 169 deletions.
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
*.py[cod]
*.pickle

# C extensions
*.so
Expand Down Expand Up @@ -31,3 +32,6 @@ nosetests.xml
.mr.developer.cfg
.project
.pydevproject

# Unix editor backup files
*~
10 changes: 10 additions & 0 deletions CHANGELOG
Original file line number Diff line number Diff line change
@@ -1,3 +1,13 @@
Release 0.2 :
• Security: Check correctly for the result of the
xcb_grab_{pointer,keyboard} commands
• Security: Limit length of buffered password to prevent memory exhaustion
(this is a real concern when attacked with custom hardware which
simulates most rapid keystrokes)
• Security: Fix several memory leaks
• Enhancement: Report missing libraries when loading via ctypes.
• Enhancement: Provide ability and tools to use custom lock images as cursor

Release 0.1:
• [#8] Security: Fixed a typo that could in some circumstances lead to a
crash after multiple failed authentication attempts. Thanks,
Expand Down
4 changes: 3 additions & 1 deletion MANIFEST.in
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
include COPYING README.md
include COPYING CHANGELOG README.md
include tools/*.py tools/README
include make_default_lock.py
44 changes: 34 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,26 +53,50 @@ we recommend the ``xautolock`` tool. Just add something like

xautolock -locker pyxtrlock -time 5

to your X autostart file to lock the screen with ``pyxtrlock`` after
5 minutes idle time. ``xautolock`` has many other useful features, see
to your X autostart file to lock the screen with ``pyxtrlock`` after 5
minutes idle time. ``xautolock`` has many other useful features, see
its documentation. Most distributions provide an ``xautolock`` package
with a man page.
with a man page. An alternative to ``xautolock`` is the use of
[autolockd](https://github.com/zombofant/autolockd) which also
monitors for lid close and suspend events.

Bugs
----
Bugs & Limitations
------------------
Additional input devices other than the keyboard and mouse are not disabled.

Although this is not a bug, please note that pyxtrlock does not prevent a
user from switching to a virtual terminal, so be advised to always leave your
terminals locked.
Although this is not a bug, please note that pyxtrlock does not
prevent a user from switching to a virtual terminal, so be advised to
always log out from your terminals.

Please report any new bugs you may find to our [Github issue tracker](https://github.com/leonnnn/pyxtrlock/issues).
The lenght of the password is limited to 100 KiB to prevent memory
exhaustion attacks. This limit can only be adapted in the source code.

Please report any new bugs you may find to our
[Github issue tracker](https://github.com/leonnnn/pyxtrlock/issues).

Configuration
-------------
The padlock icon can be changed. It is stored as a
[pickle](http://docs.python.org/3/library/pickle.html) of a
dictionary, and the ``tools`` directory contains a tool for generating
cursors from image files.

The default cursor file is placed at
``PREFIX/share/pyxtrlock/lock.pickle`` while the cursor file at
``~/.config/pyxtrlock/lock.pickle`` takes precedence if present.

*PLEASE NOTE:* The ``pickle`` file format is not designed to be
resistant against maliciously crafted files. Therfore do not open
``pickle`` files from untrusted sources as they may compromise your
system. The default padlock file is created on install (by
``make_default_lock.py``).

Requirements
------------
* [python3-simplepam](https://github.com/leonnnn/python3-simplepam)
* Python ≥ 3.0
* libxcb
* libxcb-image
* libX11 ≥ 1.4, or libX11 ≥ 1.2 compiled with XCB backend

These requirements are met at least on
Expand All @@ -83,7 +107,7 @@ These requirements are met at least on
Authors
-------
* Leon Weber <[email protected]>
* Sebastian Riese <sebastian.riese[email protected]>
* Sebastian Riese <s.riese@zombofant.net>

pyxtrlock has been inspired by
[Ian Jacksons](http://www.chiark.greenend.org.uk/~ijackson/)'s brilliant
Expand Down
27 changes: 24 additions & 3 deletions lib/X.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
from ctypes import *
from ctypes.util import find_library
from pyxtrlock.utils import check_and_load_library

import pyxtrlock.xcb as xcb

libx_xcb = cdll.LoadLibrary(find_library('X11-xcb'))
libx = cdll.LoadLibrary(find_library('X11'))
libx_xcb = check_and_load_library('X11-xcb')
libx = check_and_load_library('X11')


class Display(Structure):
Expand Down Expand Up @@ -35,6 +35,27 @@ class KeyEvent(Structure):
("same_screen", Bool)
]

@classmethod
def from_xcb_event(cls, display, xcb_key_press_event):
x_key_press_event = cls()
x_key_press_event.type = xcb_key_press_event.response_type
x_key_press_event.serial = xcb_key_press_event.sequence
x_key_press_event.send_event = 0
x_key_press_event.display = display
x_key_press_event.window = xcb_key_press_event.event
x_key_press_event.root = xcb_key_press_event.root
x_key_press_event.subwindow = xcb_key_press_event.child
x_key_press_event.time = xcb_key_press_event.time
x_key_press_event.x = xcb_key_press_event.event_x
x_key_press_event.y = xcb_key_press_event.event_y
x_key_press_event.y_root = xcb_key_press_event.root_y
x_key_press_event.state = xcb_key_press_event.state
x_key_press_event.same_screen = xcb_key_press_event.same_screen
x_key_press_event.keycode = xcb_key_press_event.detail

return x_key_press_event


Keysym = c_ulong
Status = c_int

Expand Down
10 changes: 10 additions & 0 deletions lib/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@

import sys
import os

data_dir = os.path.join(sys.prefix, "share/pyxtrlock")

def panic(message, exit_code=1):
"""Print an error message to stderr and exit"""
print(message, file=sys.stderr)
sys.exit(exit_code)
9 changes: 9 additions & 0 deletions lib/utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
from ctypes import cdll
from ctypes.util import find_library

def check_and_load_library(libname):
handle = find_library(libname)
if handle is None:
raise ImportError("unable to find system library: {}".format(
libname))
return cdll.LoadLibrary(handle)
122 changes: 106 additions & 16 deletions lib/xcb.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
from ctypes import *
from ctypes.util import find_library

from pyxtrlock.utils import check_and_load_library

class XCBError(Exception):
"""
Expand Down Expand Up @@ -81,6 +80,7 @@ class Cookie(Structure):

VoidCookie = Cookie
AllocNamedColorCookie = Cookie
AllocColorCookie = Cookie
GrabKeyboardCookie = Cookie
GrabPointerCookie = Cookie

Expand Down Expand Up @@ -109,6 +109,19 @@ class AllocNamedColorReply(Structure):
("visual_blue", c_uint16)
]

class AllocColorReply(Structure):
_fields_ = [
("response_type", c_uint8),
("pad0", c_uint8),
("sequence", c_uint16),
("length", c_uint32),
("red", c_uint16),
("green", c_uint16),
("blue", c_uint16),
("pad1", c_uint8 * 2),
("pixel", c_uint32),
]


class GenericError(Structure):
_fields_ = [
Expand Down Expand Up @@ -200,8 +213,9 @@ class KeyPressEvent(Structure):

KEY_PRESS = 2

libxcb = cdll.LoadLibrary(find_library('xcb'))
libxcb_image = cdll.LoadLibrary(find_library('xcb-image'))
libxcb = check_and_load_library('xcb')
libxcb_image = check_and_load_library('xcb-image')
libc = check_and_load_library('c')

connect = libxcb.xcb_connect
connect.argtypes = [c_char_p, POINTER(c_int)]
Expand Down Expand Up @@ -250,6 +264,16 @@ class KeyPressEvent(Structure):
]
alloc_named_color.restype = AllocNamedColorCookie

alloc_color = libxcb.xcb_alloc_color
alloc_color.argtypes = [
POINTER(Connection), # connection
Colormap, # cmap
c_uint16, # r
c_uint16, # g
c_uint16 # b
]
alloc_color.restype = AllocColorCookie

alloc_named_color_reply = libxcb.xcb_alloc_named_color_reply
alloc_named_color_reply.argtypes = [
POINTER(Connection), # connection
Expand All @@ -258,6 +282,14 @@ class KeyPressEvent(Structure):
]
alloc_named_color_reply.restype = POINTER(AllocNamedColorReply)

alloc_color_reply = libxcb.xcb_alloc_color_reply
alloc_color_reply.argtypes = [
POINTER(Connection), # connection
AllocColorCookie, # cookie
POINTER(POINTER(GenericError)) # e
]
alloc_color_reply.restype = POINTER(AllocColorReply)


def alloc_named_color_sync(conn, colormap, color_string):
"""Synchronously allocate a named color
Expand All @@ -276,7 +308,38 @@ def alloc_named_color_sync(conn, colormap, color_string):
if error_p:
raise XCBError(error_p.contents)

return res
ret = (res.contents.visual_red, res.contents.visual_green,
res.contents.visual_blue)
free(res)
return ret

def alloc_color_sync(conn, colormap, r, g, b):
"""Synchronously allocate a color
Wrapper function for xcb_alloc_color and alloc_color_reply.
The (r, g, b) triple is in the range 0 to 255 (as opposed to
the X protocol using the 0 to 2^16-1 range).
Raises ``XCBError`` on xcb errors and value errors for invalid
values of r, g, b.
"""
if r < 0 or b < 0 or g < 0:
raise ValueError
if r > 255 or b > 255 or g > 255:
raise ValueError

r <<= 8; g <<= 8; b <<= 8

cookie = alloc_color(conn, colormap, r, g, b)
error_p = POINTER(GenericError)()
res = alloc_color_reply(conn, cookie, byref(error_p))
if error_p:
raise XCBERror(error_p.contents)

ret = (res.contents.red, res.contents.blue, res.contents.green)
free(res)
return ret

request_check = libxcb.xcb_request_check
request_check.argtypes = [POINTER(Connection), VoidCookie]
Expand Down Expand Up @@ -308,10 +371,8 @@ def create_cursor_sync(conn, source, mask, fg, bg, x, y):
"""
cursor = generate_id(conn)
cookie = create_cursor_checked(conn, cursor, source, mask,
fg.visual_red, fg.visual_green,
fg.visual_blue, bg.visual_red,
bg.visual_green, bg.visual_blue,
x, y)
fg[0], fg[1], fg[2], bg[0],
bg[1], bg[2], x, y)
error = request_check(conn, cookie)
if error:
raise XCBError(error.contents)
Expand Down Expand Up @@ -351,8 +412,8 @@ def grab_keyboard_sync(conn, owner_events, grab_window, time, ptr_mode,
"""
Synchronously grab the keyboard.
Wrapper function for grab_pointer and grab_pointer_reply.
Raises ``XCBError`` on error, otherwise returns ``GrabKeyboardReply``.
Wrapper function for grab_pointer and grab_pointer_reply. Returns
the status field from the reply. Raises ``XCBError`` on error.
"""
owner_events = 1 if owner_events else 0

Expand All @@ -363,7 +424,9 @@ def grab_keyboard_sync(conn, owner_events, grab_window, time, ptr_mode,

if error_p:
raise XCBError(error_p.contents)
return kbd_grab
status = kbd_grab.contents.status
free(kbd_grab)
return status


grab_pointer = libxcb.xcb_grab_pointer
Expand All @@ -388,6 +451,12 @@ def grab_keyboard_sync(conn, owner_events, grab_window, time, ptr_mode,
]
grab_pointer_reply.restype = POINTER(GrabPointerReply)

# constants to interpret grab results
GrabSuccess = 0
AlreadyGrabbed = 1
GrabInvalidTime = 2
GrabNotViewable = 3
GrabFrozen = 4

def grab_pointer_sync(conn, owner_events, window, event_mask, ptr_mode,
kbd_mode, confine_to, cursor, timestamp):
Expand All @@ -405,11 +474,32 @@ def grab_pointer_sync(conn, owner_events, window, event_mask, ptr_mode,
ptr_grab = grab_pointer_reply(conn, cookie, byref(error_p))
if error_p:
raise XCBError(error_p.contents)
return ptr_grab
status = ptr_grab.contents.status
free(ptr_grab)
return status

wait_for_event_ = libxcb.xcb_wait_for_event
wait_for_event_.argtypes = [POINTER(Connection)]
wait_for_event_.restype = POINTER(GenericEvent)

free = libc.free
free.argtypes = [c_void_p]
free.restype = None

class FreeWrapper(object):

def __init__(self, pointer):
self.pointer = pointer

def __enter__(self):
return self.pointer

def __exit__(self, etype, evalue, traceback):
free(self.pointer)


wait_for_event = libxcb.xcb_wait_for_event
wait_for_event.argtypes = [POINTER(Connection)]
wait_for_event.restype = POINTER(GenericEvent)
def wait_for_event(conn):
return FreeWrapper(wait_for_event_(conn))

# xcb_image
image_create_pixmap_from_bitmap_data = \
Expand Down
Loading

0 comments on commit 9e1bb08

Please sign in to comment.