Permalink
Browse files

- Moved files to src directory

- Added first draft of a CLI tool
- Added a sample psafe
  • Loading branch information...
1 parent 0aa419f commit 208c6abc8fda22bd8222d82b33051d6b76cc7dd7 @gpmidi committed Jul 23, 2011
View
@@ -0,0 +1,3 @@
+/.project
+/.pydevproject
+/.settings
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<launchConfiguration type="org.python.pydev.debug.regularLaunchConfigurationType">
+<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_PATHS">
+<listEntry value="/PyPWSafe/src/bin/psafedump.py"/>
+</listAttribute>
+<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_TYPES">
+<listEntry value="1"/>
+</listAttribute>
+<listAttribute key="org.eclipse.debug.ui.favoriteGroups">
+<listEntry value="org.eclipse.debug.ui.launchGroup.run"/>
+</listAttribute>
+<stringAttribute key="org.eclipse.ui.externaltools.ATTR_LOCATION" value="${workspace_loc:PyPWSafe/src/bin/psafedump.py}"/>
+<stringAttribute key="org.eclipse.ui.externaltools.ATTR_OTHER_WORKING_DIRECTORY" value=""/>
+<stringAttribute key="org.eclipse.ui.externaltools.ATTR_TOOL_ARGUMENTS" value="--debug --file &quot;${project_loc}\test safes\simple.psafe3&quot; --password &quot;bogus12345&quot; --readonly --display"/>
+<stringAttribute key="org.python.pydev.debug.ATTR_INTERPRETER" value="__default"/>
+<stringAttribute key="org.python.pydev.debug.ATTR_PROJECT" value="PyPWSafe"/>
+</launchConfiguration>
View
@@ -0,0 +1,243 @@
+#!/usr/bin/python
+''' A CLI interface for PyPWSafe.
+
+Allow users to view Password Safe files.
+
+Created on Jul 22, 2011
+
+@author: paulson mcintyre <paul@gpmidi.net>
+'''
+import logging, logging.config
+logger = logging.getLogger("psafebin.dump")
+log = logger.log
+logging.basicConfig(
+ level = logging.INFO,
+ format = '%(asctime)s %(levelname)s %(message)s',
+ )
+log(10, 'initing')
+import os, sys, os.path
+# FIXME: Add in tests to make sure we can import all
+# required libraries and generate non-coder errors
+# as to what is missing.
+#try:
+import pypwsafe as pws
+#except ImportError:
+# log(50, "Can't find the pypwsafe library")
+# sys.exit(1)
+
+from optparse import OptionParser
+
+# TODO: Find a simpler way of doing this
+def show_entry(entry, opts):
+ """ Return true if an entry should be displayed """
+ m = (
+ ('Group', opts.filter_group),
+ ('UUID', opts.filter_uuid),
+ ('Title', opts.filter_title),
+ ('Username', opts.filter_username),
+ )
+ def match(e_name, lst):
+ """ Return true if the given entry properity,
+ e_name, is in lst or lst is empty.
+ """
+ if len(lst) > 0:
+ if entry[e_name] in lst:
+ return True
+ else:
+ return False
+ else:
+ return True
+
+ for e_name, lst in m:
+ if not match(e_name, lst):
+ return False
+
+ return True
+
+def display_xml(entries, opts):
+ raise NotImplementedError
+
+def display_csv(entries, opts):
+ raise NotImplementedError
+
+def display_display(entries, opts):
+ ret = ''
+ by_groups = {}
+ for entry in entries:
+ if not by_groups.has_key(entry['Group']):
+ by_groups[entry['Group']] = []
+ by_groups[entry['Group']].append(entry)
+ groups = by_groups.keys()
+ # In place!
+ groups.sort()
+ for group in groups:
+ ret += "= %r =\n" % group
+ by_groups[group].sort(lambda a, b: cmp(a['Title'] + a['Username'], b['Title'] + b['Username']))
+ for entry in by_groups[group]:
+ ret += """ == %(Title)r ==
+ Username: %(Username)r
+ Password: %(Password)r
+""" % entry
+ return ret
+
+
+if __name__ == "__main__":
+ # Setup option parser to parse out args
+ parser = OptionParser(
+ usage = "%prog",
+ version = "%prog v0.1",
+ prog = "psafedump",
+ description = """
+A CLI interface for viewing Password Safe files.
+ """,
+ )
+ # TODO: Support multiple files
+ parser.add_option(
+ "-f",
+ "--file",
+ action = "store",
+ type = "string",
+ dest = "filename",
+ default = None,
+ help = "Password Safe file to create, edit, or view. ",
+ )
+ parser.add_option(
+ "-r",
+ "--readonly",
+ action = "store_true",
+ dest = "readonly",
+ default = False,
+ help = "Open the Password Safe in read-only mode. [Default: %default]",
+ )
+ parser.add_option(
+ "-p",
+ "--password",
+ action = "append",
+ dest = "password",
+ default = [],
+ help = "The password to use when opening the file. If given multiple times each password given will be tried. If not given, a password will be prompted for via STDIN and STDOUT. Note: Specifying the password via an argument is INSECURE. ",
+ )
+ parser.add_option(
+ "-d",
+ "--debug",
+ action = "store_true",
+ dest = "debug",
+ default = False,
+ help = "Run in debug mode. Running in this mode WILL EXPOSE YOUR PASSWORDS and other sensitive information. [Default: %default]",
+ )
+ # Output format
+ parser.add_option(
+ "--xml",
+ action = "store_true",
+ default = False,
+ dest = "xml",
+ help = "Display results in XML",
+ )
+ parser.add_option(
+ "--csv",
+ action = "store_true",
+ default = False,
+ dest = "csv",
+ help = "Display results in CSV",
+ )
+ parser.add_option(
+ "--display",
+ action = "store_true",
+ default = False,
+ dest = "display",
+ help = "Display results in a human readable format",
+ )
+
+ # Display Filters
+ parser.add_option(
+ "--uuid",
+ action = "append",
+ dest = "filter_uuid",
+ default = [],
+ help = "Limit display to entries with this UUID. If given multiple times then displayed entries must match at least one of the UUIDs. ",
+ )
+ parser.add_option(
+ "--title",
+ action = "append",
+ dest = "filter_title",
+ default = [],
+ help = "Limit display to entries with this title. If given multiple times then displayed entries must match at least one of the titles. ",
+ )
+ parser.add_option(
+ "--group",
+ action = "append",
+ dest = "filter_group",
+ default = [],
+ help = "Limit display to entries with this group. If given multiple times then displayed entries must match at least one of the groups. ",
+ )
+ parser.add_option(
+ "--username",
+ action = "append",
+ dest = "filter_username",
+ default = [],
+ help = "Limit display to entries with this username. If given multiple times then displayed entries must match at least one of the usernames. ",
+ )
+ log(10, "Parsing args")
+ (opts, args) = parser.parse_args()
+
+ if opts.debug:
+ logger.setLevel(logging.DEBUG)
+
+ # Handle filename as first arg
+ if not opts.filename:
+ if len(args) == 1:
+ opts.filename = args[0]
+ else:
+ log(50, "No filename was given")
+ parser.error("No filename specified. ")
+ sys.exit(2)
+
+ # FIXME: Add tests to make sure that
+ # - File exists (reading)
+ # - File doesn't exist but can create it (creating)
+ # - Can write the file if not readonly
+
+ if not (opts.xml or opts.csv or opts.display):
+ opts.display = True
+ log(10, "No output format given. Defaulting to display.")
+
+ if len(opts.password) == 0:
+ # FIXME: Add in support for prompting for a password.
+ raise NotImplementedError
+
+ if opts.readonly:
+ mode = "RO"
+ else:
+ mode = "RW"
+ log(10, "Set mode to %r", mode)
+
+ psafe = None
+ for passwd in opts.password:
+ try:
+ log(10, "Trying password %r", passwd)
+ psafe = pws.PWSafe3(
+ filename = opts.filename,
+ password = passwd,
+ mode = mode,
+ )
+ log(10, "Got psafe object %r", psafe)
+ break
+ except pws.PasswordError:
+ log(10, "Password error - Trying next password if any")
+ continue
+
+ if not psafe:
+ log(50, "No valid password was found. ")
+
+ to_display = []
+ for entry in psafe.records:
+ if show_entry(entry, opts):
+ to_display.append(entry)
+
+ if opts.xml:
+ print display_xml(to_display, opts)
+ elif opts.csv:
+ print display_csv(to_display, opts)
+ elif opts.display:
+ print display_display(to_display, opts)
+
File renamed without changes.
File renamed without changes.
View
@@ -1,6 +1,6 @@
#!/usr/bin/env python
#===============================================================================
-# SYMANTEC: Copyright © 2009-2011 Symantec Corporation. All rights reserved.
+# SYMANTEC: Copyright (C) 2009-2011 Symantec Corporation. All rights reserved.
#
# This file is part of PyPWSafe.
#
@@ -23,11 +23,11 @@
"""
# Lets this lib work from both 2.4 and above
try:
- from hashlib import sha256_func #@UnresolvedImport
- from hashlib import sha256_mod #@UnresolvedImport
+ from hashlib import sha256_func #@UnresolvedImport
+ from hashlib import sha256_mod #@UnresolvedImport
except:
- import Crypto.Hash.SHA256 as sha256_mod #@UnresolvedImport
- from Crypto.Hash.SHA256 import new as sha256_func #@UnresolvedImport
+ import Crypto.Hash.SHA256 as sha256_mod #@UnresolvedImport @Reimport
+ from Crypto.Hash.SHA256 import new as sha256_func #@UnresolvedImport @Reimport
from mcrypt import MCRYPT #@UnresolvedImport
from hmac import new as HMAC
from PWSafeV3Headers import *
@@ -83,7 +83,7 @@ class PWSafe3(object):
prefs
"""
- def __init__(self, filename, password, mode="RW"):
+ def __init__(self, filename, password, mode = "RW"):
log.debug('Creating psafe %s' % repr(filename))
psafe_exists = os.access(filename, os.F_OK)
psafe_canwrite = os.access(filename, os.W_OK)
@@ -291,7 +291,7 @@ def load(self):
req = Record(self._fetch_block)
self.records.append(req)
- if self.current_hmac(cached=True) != self.hmac:
+ if self.current_hmac(cached = True) != self.hmac:
log.error('Invalid HMAC Calculated: %s File: %s' % (repr(self.current_hmac()), repr(self.hmac)))
#raise InvalidHMACError, "Calculated: %s File: %s"%(repr(self.current_hmac()),repr(self.hmac))
@@ -301,7 +301,7 @@ def __str__(self):
ret += str(i) + "\n\n"
return ret
- def _fetch_block(self, num_blocks=1):
+ def _fetch_block(self, num_blocks = 1):
"""Returns one or more 16-byte block of data. Raises EOFError when there is no more data. """
assert num_blocks > 0
bytes = num_blocks * 16
@@ -333,7 +333,7 @@ def encrypt_data(self):
tw.init(self.enckey, self.iv)
self.cryptdata = tw.encrypt(self.fulldata)
- def current_hmac(self, cached=False):
+ def current_hmac(self, cached = False):
"""Returns the current hmac of self.fulldata"""
data = ''
for i in self.headers:
@@ -391,7 +391,7 @@ def nrwrapper(name):
, nrwrapper('Notes')
)
- def getpass(self, uuid=None):
+ def getpass(self, uuid = None):
"""Returns the password of the item with the given UUID"""
for record in self.records:
if record['UUID'] == uuid:
View
File renamed without changes.
View
File renamed without changes.
View
@@ -0,0 +1 @@
+/simple.ibak
View
Binary file not shown.

1 comment on commit 208c6ab

ronys commented on 208c6ab Jul 23, 2011

You might want to add a copyright (left) notice to psafedump.py.

Rony

Please sign in to comment.