diff options
author | Brian Dolbec <brian.dolbec@gmail.com> | 2015-08-08 10:54:33 -0700 |
---|---|---|
committer | Brian Dolbec <brian.dolbec@gmail.com> | 2015-08-08 10:54:33 -0700 |
commit | 9de30985ddbd5716a64b103193c2fa6aa1ace8ad (patch) | |
tree | 13529e8e4887cc3febd9748118e716533fe1755a | |
parent | gkeys/gkeysinterface.py: Initial commit of an api consumer interface (diff) | |
parent | gkeysgpg: Initial commit of the gkeys-gpg command (diff) | |
download | gentoo-keys-9de30985ddbd5716a64b103193c2fa6aa1ace8ad.tar.gz gentoo-keys-9de30985ddbd5716a64b103193c2fa6aa1ace8ad.tar.bz2 gentoo-keys-9de30985ddbd5716a64b103193c2fa6aa1ace8ad.zip |
Merge pull request #38 from gentoo/gkeys-gpg
Gkeys-gpg
-rw-r--r-- | gkeys-gen/gkeygen/cli.py | 1 | ||||
-rw-r--r-- | gkeys-ldap/gkeyldap/cli.py | 1 | ||||
-rwxr-xr-x | gkeys/bin/gkeys-gpg | 56 | ||||
-rw-r--r-- | gkeys/gkeys/actionbase.py | 90 | ||||
-rw-r--r-- | gkeys/gkeys/actions.py | 171 | ||||
-rw-r--r-- | gkeys/gkeys/base.py | 58 | ||||
-rw-r--r-- | gkeys/gkeys/checks.py | 3 | ||||
-rw-r--r-- | gkeys/gkeys/cli.py | 3 | ||||
-rw-r--r-- | gkeys/gkeys/config.py | 10 | ||||
-rw-r--r-- | gkeys/gkeys/keyhandler.py | 110 | ||||
-rw-r--r-- | gkeys/gkeys/lib.py | 48 | ||||
-rw-r--r-- | gkeys/gkeys/seed.py | 8 | ||||
-rw-r--r-- | gkeys/gkeys/seedhandler.py | 8 | ||||
-rw-r--r-- | gkeys/gkeysgpg/__init__.py | 1 | ||||
-rw-r--r-- | gkeys/gkeysgpg/actions.py | 179 | ||||
-rw-r--r-- | gkeys/gkeysgpg/cli.py | 140 |
16 files changed, 708 insertions, 179 deletions
diff --git a/gkeys-gen/gkeygen/cli.py b/gkeys-gen/gkeygen/cli.py index f522814..c0beb3b 100644 --- a/gkeys-gen/gkeygen/cli.py +++ b/gkeys-gen/gkeygen/cli.py @@ -32,6 +32,7 @@ class Main(CliBase): 'Actions': Actions, 'Available_Actions': Available_Actions, 'Action_Map': Action_Map, + 'Base_Options': [], 'prog': 'gkeys-gen', 'description': 'Gentoo Keys GPG key generator program', 'epilog': '''CAUTION: adding UNTRUSTED keys can be HAZARDOUS to your system!''' diff --git a/gkeys-ldap/gkeyldap/cli.py b/gkeys-ldap/gkeyldap/cli.py index f18b6de..4d38485 100644 --- a/gkeys-ldap/gkeyldap/cli.py +++ b/gkeys-ldap/gkeyldap/cli.py @@ -34,6 +34,7 @@ class Main(CliBase): 'Actions': Actions, 'Available_Actions': Available_Actions, 'Action_Map': Action_Map, + 'Base_Options': [], 'prog': 'gkeys-ldap', 'description': 'Gentoo-keys LDAP interface and seed file generator program', 'epilog': '''CAUTION: adding UNTRUSTED keys can be HAZARDOUS to your system!''' diff --git a/gkeys/bin/gkeys-gpg b/gkeys/bin/gkeys-gpg new file mode 100755 index 0000000..3bed18f --- /dev/null +++ b/gkeys/bin/gkeys-gpg @@ -0,0 +1,56 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +'''Gentoo-keys is a gpg key manager for managing + gentoo's gpg-signing keys. It is these keys that are + used to verify and validate release media, etc.. + + This gkeys-gpg command is a wrapper to gnupg's gpg command + which uses the gentoo-keys keyring system to control the + keydirs and keyrings visible to gpg + + Distributed under the terms of the GNU General Public License v2 + + Copyright: + (c) 2011 Brian Dolbec + Distributed under the terms of the GNU General Public License v2 + + Author(s): + Brian Dolbec <dolsen@gentoo.org> + +''' + +from __future__ import print_function + +from gkeysgpg.cli import Main + +import os +import sys + + +# This block ensures that ^C interrupts are handled quietly. +try: + import signal + + def exithandler(signum,frame): + signal.signal(signal.SIGINT, signal.SIG_IGN) + signal.signal(signal.SIGTERM, signal.SIG_IGN) + print() + sys.exit(1) + + signal.signal(signal.SIGINT, exithandler) + signal.signal(signal.SIGTERM, exithandler) + signal.signal(signal.SIGPIPE, signal.SIG_DFL) + +except KeyboardInterrupt: + print() + sys.exit(1) + +root = None +if 'ROOT' in os.environ: + root = os.environ['ROOT'] + +main = Main(root=root) +returncode = main() + +sys.exit(returncode) diff --git a/gkeys/gkeys/actionbase.py b/gkeys/gkeys/actionbase.py new file mode 100644 index 0000000..77748c5 --- /dev/null +++ b/gkeys/gkeys/actionbase.py @@ -0,0 +1,90 @@ +# +#-*- coding:utf-8 -*- + +""" + Gentoo-keys - actionbase.py + + Base api interface module + + @copyright: 2012-2015 by Brian Dolbec <dol-sen@gentoo.org> + @license: GNU GPL2, see COPYING for details. +""" + +from __future__ import print_function + +import os +import sys + +if sys.version_info[0] >= 3: + _unicode = str +else: + _unicode = unicode + + +from snakeoil.demandload import demandload + +demandload( + "json:load", + "gkeys.lib:GkeysGPG", + "gkeys.keyhandler:KeyHandler", +) + + + +class ActionBase(object): + '''Base actions class holding comon functions and init''' + + def __init__(self, config, output=None, logger=None): + self.config = config + self.output = output + self.logger = logger + self.seeds = None + self._seedhandler = None + self._keyhandler = None + self._gpg = None + self.category = None + + + @property + def gpg(self): + '''Holds the classwide GkeysGPG instance''' + if not self._gpg: + self._gpg = GkeysGPG(self.config, + self._set_category(self.category), self.logger) + else: + self._gpg.basedir = self._set_category(self.category) + return self._gpg + + + @property + def keyhandler(self): + '''Holds the classwide KeyHandler instance''' + if not self._keyhandler: + self._init_keyhandler() + return self._keyhandler + + + def _init_keyhandler(self): + self._keyhandler = KeyHandler(self.config, self.logger) + self._seedhandler = self._keyhandler.seedhandler + + + @property + def seedhandler(self): + '''Holds the classwide SeedHandler instance + which is a convienience variable for the keyhandler's instance of it''' + if not self._seedhandler: + self._init_keyhandler() + return self._seedhandler + + + def _set_category(self, cat): + keyring = self.config.get_key('keyring') + if "foo-bar'd" in keyring: + raise + self.category = cat + catdir = os.path.join(keyring, cat) + self.logger.debug(_unicode("ACTIONS: _set_category; catdir = %s") % catdir) + return catdir + + diff --git a/gkeys/gkeys/actions.py b/gkeys/gkeys/actions.py index 6610480..69d05b6 100644 --- a/gkeys/gkeys/actions.py +++ b/gkeys/gkeys/actions.py @@ -6,7 +6,8 @@ Primary api interface module - @copyright: 2012 by Brian Dolbec <dol-sen@gentoo.org> + @copyright: 2012-2015 by Brian Dolbec <dol-sen@gentoo.org> + @copyright: 2014-2015 by Pavlos Ratis <dastergon@gentoo.org> @license: GNU GPL2, see COPYING for details. """ @@ -25,29 +26,25 @@ else: from collections import defaultdict -from snakeoil.demandload import demandload - +from gkeys.actionbase import ActionBase from gkeys.gkey import GKEY from gkeys.checks import SPECCHECK_SUMMARY, convert_pf, convert_yn +from snakeoil.demandload import demandload + demandload( + "gkeys.base:Args", "json:load", - "gkeys.lib:GkeysGPG", - "gkeys.seedhandler:SeedHandler", ) - EXTENSIONS = ['.sig', '.asc', '.gpg','.gpgsig'] -class Actions(object): +class Actions(ActionBase): '''Primary API actions''' def __init__(self, config, output=None, logger=None): - self.config = config - self.output = output - self.logger = logger - self.seeds = None + ActionBase.__init__(self, config, output, logger) @staticmethod @@ -67,16 +64,14 @@ class Actions(object): '''-----< general actions >------''' pass - def listseed(self, args): '''Pretty-print the selected seed file''' - handler = SeedHandler(self.logger, self.config) - kwargs = handler.build_gkeydict(args) + kwargs = self.seedhandler.build_gkeydict(args) self.logger.debug(_unicode("ACTIONS: listseed; kwargs: %s") % _unicode(kwargs)) if not self.seeds: try: - self.seeds = handler.load_seeds(args.category, args.nick) + self.seeds = self.seedhandler.load_seeds(args.category, args.nick) except ValueError: return (False, ['', "Failed to load seed file. Consider fetching seedfiles."]) if self.seeds: @@ -92,8 +87,10 @@ class Actions(object): % _unicode(args)) if not args.category: return (False, ["Please specify seeds category."]) - handler = SeedHandler(self.logger, self.config) - success, messages = handler.fetch_seeds(args.category, args, self.verify) + self._set_category(self.config.get_key('verify-keyring')) + verifyargs = Args() + verifyargs.category=args.category + success, messages = self.seedhandler.fetch_seeds(args.category, verifyargs, self.verify) messages.append("") messages.append("Fetch operation completed") return (False not in success, messages) @@ -101,7 +98,6 @@ class Actions(object): def addseed(self, args): '''Add or replace a key in the selected seed file''' - handler = SeedHandler(self.logger, self.config) success, data = self.listseed(args) gkeys = data[1] if not args.nick or not args.name or not args.keys or not args.keydir: @@ -110,7 +106,7 @@ class Actions(object): args.fingerprint = args.keys if args.uid is None: args.uid = [] - gkey = handler.new(args, checkgkey=True) + gkey = self.seedhandler.new(args, checkgkey=True) if not gkey: return (False, ["Failed to create a valid GKEY instance.", "Check for invalid data entries"]) @@ -153,13 +149,12 @@ class Actions(object): def moveseed(self, args): '''Move keys between seed files''' - handler = SeedHandler(self.logger, self.config) - searchkey = handler.new(args, checkgkey=False) + searchkey = self.seedhandler.new(args, checkgkey=False) self.logger.debug(_unicode("ACTIONS: moveseed; gkey: %s") % _unicode(searchkey)) if not self.seeds: self.seeds = self.load_seeds(args.category) - kwargs = handler.build_gkeydict(args) + kwargs = self.seedhandler.build_gkeydict(args) sourcekeys = self.seeds.list(**kwargs) dest = self.load_seeds(args.destination) destkeys = dest.list(**kwargs) @@ -202,21 +197,17 @@ class Actions(object): # fill in code here if not args.category: args.category = 'gentoo' - keyring = self.config.get_key('keyring') - catdir = os.path.join(keyring, args.category) - self.logger.debug(_unicode("ACTIONS: listkey; catdir = %s") % catdir) - self.gpg = GkeysGPG(self.config, catdir, self.logger) - handler = SeedHandler(self.logger, self.config) + self.category = args.category if args.keydir: self.gpg.set_keydir(args.keydir, "list-keys") self.gpg.set_keyseedfile() seeds = self.gpg.seedfile else: - seeds = handler.load_category(args.category) + seeds = self.seedhandler.load_category(args.category) results = {} success = [] messages = [] - kwargs = handler.build_gkeydict(args) + kwargs = self.seedhandler.build_gkeydict(args) keyresults = seeds.list(**kwargs) for key in sorted(keyresults): if args.fingerprint: @@ -275,11 +266,7 @@ class Actions(object): return (False, ["Search failed for search term"]) # get confirmation # fill in code here - keyring = self.config.get_key('keyring') - catdir = os.path.join(keyring, args.category) - self.logger.debug(_unicode("ACTIONS: installkey; catdir = %s") - % catdir) - self.gpg = GkeysGPG(self.config, catdir, self.logger) + self.category = args.category for gkey in gkeys: self.gpg.set_keydir(gkey.keydir, "recv-keys") self.gpg.set_keyseedfile() @@ -336,15 +323,11 @@ class Actions(object): if not args.category: return (False, [_unicode("Please specify seeds category.")]) self.logger.debug(_unicode("ACTIONS: checkkey; args: %s") % _unicode(args)) - handler = SeedHandler(self.logger, self.config) - seeds = handler.load_category(args.category) - keyring = self.config.get_key('keyring') - catdir = os.path.join(keyring, args.category) - self.logger.debug(_unicode("ACTIONS: checkkey; catdir = %s") % catdir) - self.gpg = GkeysGPG(self.config, catdir, self.logger) + seeds = self.seedhandler.load_category(args.category) + self.category = args.category results = {} failed = defaultdict(list) - kwargs = handler.build_gkeydict(args) + kwargs = self.seedhandler.build_gkeydict(args) keyresults = seeds.list(**kwargs) self.output('', '\n Checking keys...') for gkey in sorted(keyresults): @@ -391,16 +374,11 @@ class Actions(object): return (False, ["Please specify seeds category."]) self.logger.debug(_unicode("ACTIONS: speccheck; args: %s") % _unicode(args)) - handler = SeedHandler(self.logger, self.config) - seeds = handler.load_category(args.category) - keyring = self.config.get_key('keyring') - catdir = os.path.join(keyring, args.category) + self.category = args.category + catdir, keyresults = self.keyhandler.determine_keys(args) self.logger.debug(_unicode("ACTIONS: speccheck; catdir = %s") % catdir) - self.gpg = GkeysGPG(self.config, catdir, self.logger) results = {} failed = defaultdict(list) - kwargs = handler.build_gkeydict(args) - keyresults = seeds.list(**kwargs) self.output('', '\n Checking keys...') for gkey in sorted(keyresults): self.logger.info(_unicode("Checking key %s, %s") @@ -530,11 +508,10 @@ class Actions(object): '''Remove an installed key''' if not args.nick: return (False, ["Please provide a nickname or -n *"]) - handler = SeedHandler(self.logger, self.config) - kwargs = handler.build_gkeydict(args) + kwargs = self.seedhandler.build_gkeydict(args) self.logger.debug(_unicode("ACTIONS: removekey; kwargs: %s") % _unicode(kwargs)) - seeds = handler.load_category(args.category) + seeds = self.seedhandler.load_category(args.category) messages = [] if args.nick == '*': self.output([''],_unicode('Remove All keys in category: %s') @@ -568,11 +545,6 @@ class Actions(object): if ans in ["no", "n"]: messages.append("Key removal aborted... Nothing to be done.") else: - keyring = self.config.get_key('keyring') - catdir = os.path.join(keyring, args.category) - self.logger.debug(_unicode("ACTIONS: removekey; catdir = %s") - % catdir) - self.gpg = GkeysGPG(self.config, catdir, self.logger) if len(gkey.keys) == 1 or args.keys == gkey.keys: success, msgs = self.gpg.del_keydir(gkey) messages.extend(msgs) @@ -595,11 +567,7 @@ class Actions(object): def importkey(self, args): '''Add a specified key to a specified keyring''' if args.category: - keyring = self.config.get_key('keyring') - catdir = os.path.join(keyring, args.category) - self.logger.debug(_unicode("ACTIONS: importkey; catdir = %s") - % catdir) - self.gpg = GkeysGPG(self.config, catdir, self.logger) + catdir = self._set_category(args.category) success, data = self.listseed(args) gkeys = data[1] results = {} @@ -696,11 +664,10 @@ class Actions(object): self.logger.debug(_unicode( "ACTIONS: verify; keyring category not specified, using default: %s") % args.category) - handler = SeedHandler(self.logger, self.config) - keys = handler.load_category(args.category) + keys = self.seedhandler.load_category(args.category) if not keys: return (False, ['No installed keys found, try installkey action.']) - key = handler.seeds.nick_search(args.nick) + key = self.seedhandler.seeds.nick_search(args.nick) if not key: if args.nick: messages.append(_unicode( @@ -712,14 +679,10 @@ class Actions(object): % (args.category, args.nick)) return self.verify(args, messages) - keyrings = self.config.get_key('keyring') - catdir = os.path.join(keyrings, args.category) - self.logger.debug(_unicode("ACTIONS: verify; catdir = %s") % catdir) - self.gpg = GkeysGPG(self.config, catdir, self.logger) - return self._verify(args, handler, key, messages) + return self._verify(args, key, messages) - def _verify(self, args, handler, key, messages=None): + def _verify(self, args, key, messages=None): if messages == None: messages = [] filepath, signature = args.filename, args.signature @@ -779,7 +742,7 @@ class Actions(object): _unicode("ACTIONS: verify; local file %s") % filepath) success = os.path.isfile(filepath) if (not signature - and '.' + filepath.rsplit('.', 1)[1] not in EXTENSIONS): + and '.' + filepath.rsplit('.', 1)[-1] not in EXTENSIONS): success_fetch = False for ext in EXTENSIONS: sig_path = filepath + ext @@ -810,25 +773,10 @@ class Actions(object): [_unicode("Verification failed....: %s") % (filepath), _unicode("Key info...............: %s <%s>, %s") % ( key.name, key.nick, keyid)]) - has_no_pubkey, s_keyid = results.no_pubkey - if has_no_pubkey: - messages.append( - _unicode("Auto-searching for key.: 0x%s") % s_keyid) - # reset all but keyid and pass thru data - args.keyid = s_keyid - args.keydir = None - args.fingerprint = None - args.exact = False - args.category = None - args.nick = None - args.name = None - args.all = False - keys = self.key_search(args, data_only=True) - if keys: - args.category = list(keys)[0] - args.nick = keys[args.category][0].nick - return self.verify(args, messages) - messages.append(_unicode("Failed to find gpg key.: 0x%s") % s_keyid) + found, args, new_msgs = self.keyhandler.autosearch_key(args, results) + messages.extend(new_msgs) + if found: + return self.verify(args, messages) return (verified, messages) @@ -859,11 +807,9 @@ class Actions(object): else: nicks = args.nick # load our installed signing keys db - handler = SeedHandler(self.logger, self.config) - self.seeds = handler.load_category('sign', nicks) + self.seeds = self.seedhandler.load_category('sign', nicks) if not self.seeds.seeds: return (False, ['No installed keys, try installkey action.', '']) - basedir = self.config.get_key("sign-keydir") keydir = self.config.get_key("sign", "keydir") task = self.config.get_key("sign", "type") keyring = self.config.get_key("sign", "keyring") @@ -872,7 +818,6 @@ class Actions(object): self.logger.debug(_unicode("ACTIONS: sign; keydir = %s") % keydir) - self.gpg = GkeysGPG(self.config, basedir, self.logger) self.gpg.set_keydir(keydir, task) if keyring not in ['', None]: self.gpg.set_keyring(keyring, task) @@ -905,14 +850,10 @@ class Actions(object): return (False, ["Please specify seeds type."]) self.logger.debug(_unicode("ACTIONS: refreshkey; args: %s") % _unicode(args)) - handler = SeedHandler(self.logger, self.config) - seeds = handler.load_category(args.category, refresh=True) - keyring = self.config.get_key('keyring') - catdir = os.path.join(keyring, args.category) - self.logger.debug(_unicode("ACTIONS: refreshkey; catdir = %s") % catdir) - self.gpg = GkeysGPG(self.config, catdir, self.logger) + seeds = self.seedhandler.load_category(args.category, refresh=True) + self.category = args.category results = {} - kwargs = handler.build_gkeydict(args) + kwargs = self.seedhandler.build_gkeydict(args) keyresults = seeds.list(**kwargs) self.output('', '\n Refreshig keys...') for gkey in sorted(keyresults): @@ -929,37 +870,13 @@ class Actions(object): def key_search(self, args, data_only=False): '''Search for a key's seed in the installed keys db''' - handler = SeedHandler(self.logger, self.config) - results = {} - search_args = [x for x in - ['nick', 'name', 'keydir', 'fingerprint', 'keyid', 'uid'] - if getattr(args, x)] - if args.category: - handler.load_category(args.category) - results[args.category] = handler.key_search(args, search_args) - else: - for cat in list(self.config.get_key('seeds')): - handler.load_category(cat) - found = handler.key_search(args, search_args) - if found: - if cat in results: - results[cat].extend(found) - else: - results[cat] = found - keys = {} - for cat in results: - keys[cat] = [] - for result in results[cat]: - if result and result.nick not in keys[cat]: - if isinstance(result, GKEY): - keys[cat].append(result) + keys = self.keyhandler.key_search(args) if data_only: - del found, cat, handler return keys msgs = [] for cat in list(keys): msgs.append(_unicode("Category.....: %s") % cat) msgs.append(keys[cat]) - del keys, cat + del keys return (True, msgs) diff --git a/gkeys/gkeys/base.py b/gkeys/gkeys/base.py index b422caf..dbe6642 100644 --- a/gkeys/gkeys/base.py +++ b/gkeys/gkeys/base.py @@ -7,7 +7,7 @@ Command line interface argsparse options module and common functions - @copyright: 2012 by Brian Dolbec <dol-sen@gentoo.org> + @copyright: 2012-2015 by Brian Dolbec <dol-sen@gentoo.org> @license: GNU GPL2, see COPYING for details. """ @@ -34,7 +34,7 @@ class Args(object): def __init__(self): - self.status = False + self.action = None self.all = False self.category = None self.cleankey = False @@ -50,6 +50,8 @@ class Args(object): self.keydir = None self.seedfile = None self.signature = None + self.status = False + self.timestamp = None self.uid = None @@ -62,6 +64,7 @@ class CliBase(object): 'Actions': None, 'Available_Actions': [], 'Action_Map': {}, + 'Base_Options': [], 'prog': 'gkeys', 'description': 'Gentoo-keys manager program', 'epilog': '''Caution: adding UNTRUSTED keys can be HAZARDOUS to your system!''' @@ -72,6 +75,7 @@ class CliBase(object): self.actions = None self.logger = None self.version = None + self.need_Action = True @staticmethod @@ -240,29 +244,32 @@ class CliBase(object): parser.add_argument('-V', '--version', action = 'version', version = self.version) + # Add any additional options to the command base + self._add_options(parser, self.cli_config['Base_Options']) - subparsers = parser.add_subparsers( - title='Subcommands', - description='Valid subcommands', - help='Additional help') - for name in self.cli_config['Available_Actions']: - actiondoc = self.cli_config['Action_Map'][name]['desc'] - try: - text = actiondoc.splitlines()[0] - except AttributeError: - text = "" - action_parser = subparsers.add_parser( - name, - help=text, - description=actiondoc, - formatter_class=argparse.RawDescriptionHelpFormatter) - action_parser.set_defaults(action=name) - options = self.cli_config['Action_Map'][name]['options'] - self._add_options(action_parser, options) + if self.cli_config['Available_Actions']: + subparsers = parser.add_subparsers( + title='Subcommands', + description='Valid subcommands', + help='Additional help') + for name in self.cli_config['Available_Actions']: + actiondoc = self.cli_config['Action_Map'][name]['desc'] + try: + text = actiondoc.splitlines()[0] + except AttributeError: + text = "" + action_parser = subparsers.add_parser( + name, + help=text, + description=actiondoc, + formatter_class=argparse.RawDescriptionHelpFormatter) + action_parser.set_defaults(action=name) + options = self.cli_config['Action_Map'][name]['options'] + self._add_options(action_parser, options) parsed_args = parser.parse_args(argv) action = getattr(parsed_args, 'action', None) - if not action: + if self.need_Action and not action: parser.print_usage() sys.exit(1) elif action in ['---general---', '----keys-----', '----seeds----']: @@ -297,8 +304,8 @@ class CliBase(object): if not self.config.defaults['homedir']: self.config.defaults['homedir'] = os.path.expanduser('~') if not os.access(self.config['logdir'], os.W_OK): - self.config['logdir'] = ensure_dirs( - os.path.join(self.config['user-dir'], 'logs')) + self.config.options['logdir'] = os.path.join(self.config['userconfigdir'], 'logs') + ensure_dirs(self.config.options['logdir']) # establish our logger and update it in the imported files self.logger = set_logger(self.cli_config['prog'], self.config['logdir'], args.debug, dirmode=int(self.config.get_key('permissions', 'directories'),0), @@ -312,6 +319,7 @@ class CliBase(object): if args.config: self.logger.debug("Main: run; Found alternate config request: %s" % args.config) + self.logger.debug("Main: run; Using config: %s" % self.config['config']) # check if a -C, --category was input # if it was, check if the category is listed in the [seeds] @@ -324,6 +332,10 @@ class CliBase(object): def run(self, args): + '''Run the action selected + + @param args: list of argumanets to parse + ''' # establish our actions instance self.actions = self.cli_config['Actions'](self.config, self.output_results, self.logger) diff --git a/gkeys/gkeys/checks.py b/gkeys/gkeys/checks.py index 7e40720..7610d70 100644 --- a/gkeys/gkeys/checks.py +++ b/gkeys/gkeys/checks.py @@ -5,7 +5,8 @@ Gentoo-Keys - gkeygen/checks.py Primary key checks module - @copyright: 2014 by Brian Dolbec <dolsen@gentoo.org> + @copyright: 2014-2015 by Brian Dolbec <dolsen@gentoo.org> + @copyright: 2014-2015 by Pavlos Ratis <dastergon@gentoo.org> @license: GNU GPL2, see COPYING for details """ diff --git a/gkeys/gkeys/cli.py b/gkeys/gkeys/cli.py index 194afd2..8ce00b3 100644 --- a/gkeys/gkeys/cli.py +++ b/gkeys/gkeys/cli.py @@ -6,7 +6,7 @@ Command line interface module - @copyright: 2012 by Brian Dolbec <dol-sen@gentoo.org> + @copyright: 2012-2015 by Brian Dolbec <dol-sen@gentoo.org> @license: GNU GPL2, see COPYING for details. """ @@ -41,6 +41,7 @@ class Main(CliBase): 'Actions': Actions, 'Available_Actions': Available_Actions, 'Action_Map': Action_Map, + 'Base_Options': [], 'prog': 'gkeys', 'description': 'Gentoo-keys manager program', 'epilog': '''CAUTION: adding UNTRUSTED keys can be HAZARDOUS to your system!''' diff --git a/gkeys/gkeys/config.py b/gkeys/gkeys/config.py index 7cd856a..592aad6 100644 --- a/gkeys/gkeys/config.py +++ b/gkeys/gkeys/config.py @@ -54,10 +54,11 @@ class GKeysConfig(GPGConfig): def _set_default_config(self): self.defaults['homedir'] = os.path.expanduser('~') - self.defaults['configdir'] = os.path.join( - self.defaults['homedir'], '.gkeys') + self.defaults['userconfigdir'] = os.path.join( + self.defaults['homedir'], '.config', 'gkeys') + self.defaults['configdir'] = self.defaults['userconfigdir'] self.defaults['config']= os.path.join( - self.defaults['configdir'], 'gkeys.conf') + self.defaults['userconfigdir'], 'gkeys.conf') if not os.path.exists(self.defaults['config']): self.defaults['configdir'] = path([self.root, EPREFIX, '/etc/gkeys']) self.defaults['config'] = '%(configdir)s/gkeys.conf' @@ -101,6 +102,9 @@ class GKeysConfig(GPGConfig): defaults[key] = self.defaults[key] if filename == None: filename = self.defaults['config'] + if "foo-bar'd" in filename: + print("Config: read_config(); Configuration ERROR: filename: %s, access: %s" + % (filename, os.access(filename, os.R_OK))) self.configparser = SaneConfigParser(defaults) self.configparser.read(filename) if self.configparser.has_section('base'): diff --git a/gkeys/gkeys/keyhandler.py b/gkeys/gkeys/keyhandler.py new file mode 100644 index 0000000..9043fcd --- /dev/null +++ b/gkeys/gkeys/keyhandler.py @@ -0,0 +1,110 @@ +# +#-*- coding:utf-8 -*- + +""" + Gentoo-keys - keyhandler.py + + GKEY handling interface module + + @copyright: 2015 by Brian Dolbec <dol-sen@gentoo.org> + @license: GNU GPL2, see COPYING for details. +""" +import os +import sys + +from snakeoil.demandload import demandload + +if sys.version_info[0] >= 3: + _unicode = str +else: + _unicode = unicode + +from gkeys.gkey import GKEY + + +demandload( + "gkeys.seedhandler:SeedHandler", +) + +KEY_OPTIONS = ['nick', 'name', 'keydir', 'fingerprint', 'keyid', 'uid'] + + +class KeyHandler(object): + '''Class to hold various key operations''' + + + def __init__(self, config, logger): + self.config = config + self.logger = logger + self._seedhandler = None + + + @property + def seedhandler(self): + if not self._seedhandler: + self._seedhandler = SeedHandler(self.logger, self.config) + return self._seedhandler + + + def autosearch_key(self, args, results): + '''Search for the correct keyid from the GPGResult''' + messages = [] + has_no_pubkey, s_keyid = results.no_pubkey + if has_no_pubkey: + messages.append( + _unicode("Auto-searching for key.: 0x%s") % s_keyid) + # reset all but keyid and pass thru data + args.keyid = s_keyid + args.keydir = None + args.fingerprint = None + args.exact = False + args.category = None + args.nick = None + args.name = None + args.all = False + keys = self.key_search(args) + if keys: + args.category = list(keys)[0] + args.nick = keys[args.category][0].nick + return (True, args, messages) + messages.append(_unicode("Failed to find gpg key.: 0x%s") % s_keyid) + return (False, args, messages) + + + def determine_keys(self, args, default_cat=None): + seeds = self.seedhandler.load_category(args.category or default_cat) + keyring = self.config.get_key('keyring') + catdir = os.path.join(keyring, args.category) + self.logger.debug(_unicode("KeyHandler: determine_keys; catdir = %s") % catdir) + kwargs = self.seedhandler.build_gkeydict(args) + return (catdir, seeds.list(**kwargs)) + + + def key_search(self, args, first_match=False): + '''Search for a key's seed in the installed keys db''' + results = {} + search_args = [x for x in KEY_OPTIONS if getattr(args, x)] + if args.category: + self.seedhandler.load_category(args.category) + results[args.category] = self.seedhandler.key_search(args, search_args) + else: + for cat in sorted(self.config.get_key('seeds')): + self.seedhandler.load_category(cat) + found = self.seedhandler.key_search(args, search_args) + if found: + if cat in results: + results[cat].extend(found) + else: + results[cat] = found + if first_match: + break + keys = {} + for cat in results: + keys[cat] = [] + for result in results[cat]: + if result and result.nick not in keys[cat]: + if isinstance(result, GKEY): + keys[cat].append(result) + + self.logger.debug(_unicode("KeyHandler: key_search; keys = %s") % str(keys)) + return keys diff --git a/gkeys/gkeys/lib.py b/gkeys/gkeys/lib.py index bce4e55..5ad1aab 100644 --- a/gkeys/gkeys/lib.py +++ b/gkeys/gkeys/lib.py @@ -8,11 +8,12 @@ with gentoo-keys specific convienience functions. Distributed under the terms of the GNU General Public License v2 Copyright: - (c) 2011 Brian Dolbec - Distributed under the terms of the GNU General Public License v2 + @copyright: 2011-2015 by Brian Dolbec <dol-sen@gentoo.org> + @copyright: 2014-2015 by Pavlos Ratis <dastergon@gentoo.org> + Distributed under the terms of the GNU General Public License v2 Author(s): - Brian Dolbec <dolsen@gentoo.org> + Brian Dolbec <dolsen@gentoo.org> ''' @@ -20,6 +21,7 @@ with gentoo-keys specific convienience functions. from __future__ import print_function import os + from os.path import abspath, pardir from os.path import join as pjoin from shutil import rmtree @@ -29,6 +31,7 @@ from gkeys.checks import KeyChecks from gkeys.fileops import ensure_dirs from gkeys.seed import Seeds + class GkeysGPG(GPG): '''Gentoo-keys primary gpg class''' @@ -315,10 +318,19 @@ class GkeysGPG(GPG): pass - def verify_text(self, text): + def verify_text(self, gkey, text, filepath=None): '''Verify a text block in memory + + @param gkey: GKEY instance of the gpg key used to verify it + @param text: string of the of the text to verify + @param filepath: optional string with the path or url of the signed file ''' - pass + self.set_keydir(gkey.keydir, 'verify', fingerprint=False, reset=True) + self.logger.debug("** Calling runGPG with Running 'gpg %s --verify %s'" + % (' '.join(self.config['tasks']['verify']), filepath)) + results = self.runGPG(task='verify', inputfile=filepath, inputtxt=text) + self._log_result('verification', gkey, results) + return results def verify_file(self, gkey, signature, filepath): @@ -338,14 +350,7 @@ class GkeysGPG(GPG): self.logger.debug("** Calling runGPG with Running 'gpg %s --decrypt %s'" % (' '.join(self.config['tasks']['decrypt']), filepath)) results = self.runGPG(task='decrypt', inputfile=filepath) - keyid = gkey.keyid[0] - if results.verified[0]: - self.logger.info("GPG verification succeeded. Name: %s / Key: %s" % (gkey.name, keyid)) - self.logger.info("\tSignature result:" + str(results.verified)) - else: - self.logger.debug("GPG verification failed. Name: %s / Key: %s" % (gkey.name, keyid)) - self.logger.debug("\t Signature result:"+ str(results.verified)) - self.logger.debug("LIB: verify; stderr_out:" + str(results.stderr_out)) + self._log_result('verification', gkey, results) return results @@ -364,17 +369,24 @@ class GkeysGPG(GPG): @param fingerprint: string of the fingerprint to sign with @param filepath: string with the path of the file to sign ''' - keyid = gkey.keyid[0] self.set_keydir(gkey.keydir, mode, reset=True) self.logger.debug("** Calling runGPG with Running 'gpg %s --%s %s %s'" % (' '.join(self.config['tasks'][mode]), mode, fingerprint, filepath)) results = self.runGPG(task=mode, inputfile=filepath) + self._log_result('signing', gkey, results) + return results + + def _log_result(self, mode, gkey, results): if results.verified[0]: - self.logger.info("GPG signing succeeded. Name: %s / Key: %s" % (str(gkey.name), str(keyid))) + self.logger.info("GPG %s succeeded. Name: %s / Key: %s" + % (mode, gkey.name, gkey.keyid[0])) self.logger.info("\tSignature result:" + str(results.verified)) else: - self.logger.debug("GPG signing failed. Name: %s / Key: %s" % (str(gkey.name), str(keyid))) + self.logger.debug("GPG %s failed. Name: %s / Key: %s" + % (mode, gkey.name, gkey.keyid[0])) self.logger.debug("\t Signature result:"+ str(results.verified)) - self.logger.debug("LIB: sign; stderr_out:" + str(results.stderr_out)) - return results + self.logger.debug("LIB: verify; stderr_out:" + + str(results.stderr_out)) + + diff --git a/gkeys/gkeys/seed.py b/gkeys/gkeys/seed.py index 01ca5a5..f1bf7db 100644 --- a/gkeys/gkeys/seed.py +++ b/gkeys/gkeys/seed.py @@ -8,11 +8,13 @@ with gentoo-keys specific convienience functions. Distributed under the terms of the GNU General Public License v2 Copyright: - (c) 2011 Brian Dolbec - Distributed under the terms of the GNU General Public License v2 + @copyright: 2011-2015 by Brian Dolbec <dol-sen@gentoo.org> + @copyright: 2014-2015 by Pavlos Ratis <dastergon@gentoo.org> + Distributed under the terms of the GNU General Public License v2 Author(s): - Brian Dolbec <dolsen@gentoo.org> + Brian Dolbec <dolsen@gentoo.org> + Pavlos Ratis <dastergon@gentoo.org> ''' diff --git a/gkeys/gkeys/seedhandler.py b/gkeys/gkeys/seedhandler.py index 18725b9..3e51bb2 100644 --- a/gkeys/gkeys/seedhandler.py +++ b/gkeys/gkeys/seedhandler.py @@ -6,7 +6,8 @@ Seed handling interface module - @copyright: 2012 by Brian Dolbec <dol-sen@gentoo.org> + @copyright: 2012-2015 by Brian Dolbec <dol-sen@gentoo.org> + @copyright: 2014-2015 by Pavlos Ratis <dastergon@gentoo.org> @license: GNU GPL2, see COPYING for details. """ @@ -223,10 +224,11 @@ class SeedHandler(object): def key_search(self, args, search_args): '''Performs a search for all listed args in the seeds''' - results = [] + search_args.sort() self.logger.debug("SeedHandler.key_search() search_args: %s" % str(search_args)) + self.logger.debug("SeedHandler.key_search() search_args values: %s" % str(args)) + results = [] found = {} - search_args.sort() if isinstance(args, dict): exact = args.get('exact', False) _all = args.get('all', False) diff --git a/gkeys/gkeysgpg/__init__.py b/gkeys/gkeysgpg/__init__.py new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/gkeys/gkeysgpg/__init__.py @@ -0,0 +1 @@ + diff --git a/gkeys/gkeysgpg/actions.py b/gkeys/gkeysgpg/actions.py new file mode 100644 index 0000000..a25e1e4 --- /dev/null +++ b/gkeys/gkeysgpg/actions.py @@ -0,0 +1,179 @@ +# +#-*- coding:utf-8 -*- + +""" + Gentoo-keys - gkeys-gpg/actions.py + + Primary api interface module + + @copyright: 2012 by Brian Dolbec <dol-sen@gentoo.org> + @license: GNU GPL2, see COPYING for details. +""" + +from __future__ import print_function + +import sys + +if sys.version_info[0] >= 3: + _unicode = str +else: + _unicode = unicode + + +from collections import OrderedDict + +from snakeoil.demandload import demandload + +from gkeys.actions import Actions as gkeyActions +from gkeys.actionbase import ActionBase +from gkeys.base import Args + +demandload( + "json:load", + "gkeys.gkey:GKEY", + "re", +) + + +Action_Map = OrderedDict([ + ('sign', { + 'func': 'sign', + 'options': ['nick', 'name', 'fingerprint', ], + 'desc': '''Sign a file''', + 'long_desc': '''Sign a file with the designated gpg key. + The default sign settings can be set in gpg.conf. These settings can be + overridden on the command line using the 'nick', 'name', 'fingerprint' options''', + 'example': '''gkeys-gpg --sign foo''', + }), + ('verify', { + 'func': 'verify', + 'options': [], + 'desc': '''File automatic download and/or verification action.''', + 'long_desc': '''File automatic download and/or verification action. + Note: If the specified key/keyring to verify against does not contain + the key used to sign the file. It will Auto-search for the correct key + in the installed keys db. And verify against the matching key. + It will report the success/failure along with the key information used for + the verification''', + 'example': '''$ gkeys-gpg --verify foo''' + }), +]) + +Available_Actions = ['sign', 'verify'] + + +class Actions(ActionBase): + '''Primary API actions''' + + def __init__(self, config, output=None, logger=None): + ActionBase.__init__(self, config, output, logger) + + + def verify(self, args, argv): + '''File verification action. + Note: If the specified key/keyring to verify against does not contain + the key used to sign the file. It will Auto-search for the correct key + in the installed keys db. And verify against the matching key.''' + + ''' + @param args: argparse.parse_args instance + ''' + key = None + if args.dash: # stdin arg + # data is the data that is signed and needs to be verified + data = sys.stdin.read() + self.logger.info("data to verify:") + self.logger.info("stdin:\n%s\n" % data) + if not args.nick: + (args.name, args.nick) = self._committer_search(data.split('\n')) + keys = self.keyhandler.key_search(args, first_match=True) + self.logger.debug("key_search results: %s" % str(keys)) + args.category = list(keys)[0] + catdir = self._set_category(args.category) + self.logger.debug("Category found from key_search: %s" + % args.category) + key = keys[args.category][0] + + if not args.category: + args.category = self.config.get_key('verify_keyring') + self.logger.debug(_unicode( + "ACTIONS: verify; keyring category not specified, using default: %s") + % args.category) + catdir = self._set_category(args.category) + if not key: + self.logger.debug(_unicode("ACTIONS: verify; key not defined: (1)")) + keys = self.seedhandler.load_category(args.category) + if not keys: + return (False, ['No installed keys found, try installkey action.']) + key = self.seedhandler.seeds.nick_search(args.nick) + if not key: + self.logger.debug(_unicode("ACTIONS: verify; key not defined: (2)")) + if args.nick: + self.logger.info(_unicode( + "Failed to find.........: %s in category: %s") + % (args.category, args.nick)) + args.category = self.config.get_key('verify-keyring') + args.nick = self.config.get_key('verify-nick') + self.logger.info(_unicode("Using config defaults..: %s %s") + % (args.category, args.nick)) + catdir = self._set_category(args.category) + return self.verify(args) + + self.logger.debug(_unicode("ACTIONS: verify; catdir = %s") % catdir) + if args.statusfd: + self.config.defaults['gpg_defaults'] = [ + '--display-charset', 'utf-8', + '--status-fd', args.statusfd] + self.config.defaults['gpg_defaults'].extend(["--trust-model", "always"]) + self.logger.info("Verifying file...") + results = self.gpg.verify_text(key, data.encode('utf-8'), args.verify) + keyid = key.keyid[0] + (valid, trust) = results.verified + # TODO verify that the key it is signed with is listed as a current + # gpg key for that dev, not an old one still in the keyring. + # Add a setting to trigger allowing old gpg keys to validate against + if valid: + self.logger.info(_unicode("Verification succeeded.: %s") + % (args.verify)) + self.logger.info(_unicode("Key info...............: %s <%s>, %s") + % ( key.name, key.nick, keyid)) + self.logger.info(_unicode(" category, nick.....: %s %s") + % (args.category, args.nick)) + else: + self.logger.info( + _unicode("Verification failed....: %s") % (args.verify)) + self.logger.info(_unicode("Key info...............: %s <%s>, %s") + % ( key.name, key.nick, keyid)) + found, args, new_msgs = self.keyhandler.autosearch_key(args, results) + if found: + return self.verify(args) + sys.stdout.write(results.output) + sys.stderr.write('\n'.join(results.stderr_out)) + self.logger.debug("gpg stdout results: \n%s\n" %str(results.output)) + self.logger.debug("gpg returncode: \n%s\n" %str(results.returncode)) + self.logger.debug("gpg stderr results: \n%s\n" %str(results.stderr_out)) + return (results.returncode, results) + + + def sign(self, args): + '''Sign a file''' + print("Made it to the --sign option :)") + gkeyargs = Args() + gkeyargs.filename = args.sign + gkeys = gkeyActions(self.config, self.output, self.logger) + return gkeys.sign(gkeyargs) + + + def _committer_search(self, data): + username = None + nick = None + for line in data: + self.logger.debug("_committer_search: line: %s" % line) + matches = re.match("committer (.*) <(.*)@.*>", line) + if matches is not None: + username = matches.group(1) + nick = matches.group(2) + self.logger.debug("_committer_search: " + "Commiter username, nick: %s, %s" % (username, nick)) + break + return (username, nick) diff --git a/gkeys/gkeysgpg/cli.py b/gkeys/gkeysgpg/cli.py new file mode 100644 index 0000000..70a898d --- /dev/null +++ b/gkeys/gkeysgpg/cli.py @@ -0,0 +1,140 @@ +# +#-*- coding:utf-8 -*- + +""" + Gentoo-keys - cli.py + + Command line interface module + + @copyright: 2012 by Brian Dolbec <dol-sen@gentoo.org> + @license: GNU GPL2, see COPYING for details. +""" + +from __future__ import print_function + + +import os +import sys + +from gkeys import __version__ +from gkeys.base import CliBase +from gkeys.config import GKeysConfig +from gkeys.keyhandler import KEY_OPTIONS +from gkeysgpg.actions import Actions, Available_Actions, Action_Map + + +class Main(CliBase): + '''Main command line interface class''' + + + def __init__(self, root=None, config=None, print_results=True): + """ Main class init function. + + @param root: string, root path to use + """ + CliBase.__init__(self) + self.root = root or "/" + self.config = config or GKeysConfig(root=root) + self.config.options['print_results'] = print_results + self.cli_config = { + 'Actions': Actions, + 'Available_Actions': Available_Actions, + 'Action_Map': Action_Map, + 'Base_Options': Available_Actions.copy(), + 'prog': 'gkeys-gpg', + 'description': 'Gentoo-keys gpg command wrapper', + 'epilog': '''CAUTION: adding UNTRUSTED keys can be HAZARDOUS to your system!''' + } + self.cli_config['Base_Options'].extend(["dash", "statusfd"]) + self.cli_config['Base_Options'].extend(KEY_OPTIONS) + self.cli_config['Base_Options'].extend(["category"]) + self.version = __version__ + self.need_Action = False + + + def __call__(self, args=None): + """Main class call function + + @param args: Optional list of argumanets to parse and action to run + Defaults to sys.argv[1:] + """ + if args: + ok = self.setup(args, []) + else: + args = self.parse_args(sys.argv[1:]) + ok = self.setup(args, os.path.join(self.config['configdir'],'gkeys.conf')) + if ok: + return self.run(args) + return 1 + + + def run(self, args): + '''Run the gpg command option + + @param args: list of argumanets to parse + ''' + # establish our actions instance + self.actions = self.cli_config['Actions'](self.config, self.output_results, self.logger) + + for action in self.cli_config['Available_Actions']: + if getattr(args, action): + break + + # run the action + func = getattr(self.actions, '%s' + % self.cli_config['Action_Map'][action]['func']) + self.logger.debug('Main: run; Found action: %s' % action) + returncode, results = func(args, sys.argv[1:]) + if not results: + print("No results found. Check your configuration and that the", + "seed file exists.") + return 1 + self.logger.debug("gpg results output:") + self.logger.debug(results) + self.logger.debug("Return code: %s, %s" %(str(returncode), type(returncode))) + return returncode + + + @staticmethod + def _option_blank(parser=None): + parser.add_argument('-', '--', dest='blank', nargs='', default=None, + help='fill me in') + + @staticmethod + def _option_clearsign(parser=None): + parser.add_argument('--clearsign', dest='clearsign', default=None, + help='make a clear text signature') + + @staticmethod + def _option_detachsign(parser=None): + parser.add_argument('-b', '--detach-sign', dest='detachsign', default=None, + help='make a detached signature') + + @staticmethod + def _option_sign(parser=None): + parser.add_argument('-s', '--sign', dest='sign', default=None, + help='make a signature') + + @staticmethod + def _option_verify(parser=None): + parser.add_argument('--verify', dest='verify', default=None, + help='verify a signature') + +### These are for gpg command compatibilty only + @staticmethod + def _option_statusfd(parser=None): + parser.add_argument('--status-fd', dest='statusfd', default=None, + help='Write special status strings to the file descriptor n.') + + @staticmethod + def _option_dash(parser=None): + parser.add_argument('-', dest='dash', action='store_true', default=False, + help='read input from stdin.') + + + def output_results(self, args, results): + print(results[1].encode('utf-8'), file=sys.stderr) + if args.statusfd == '1': + print(results[0].encode('utf-8')) + elif args.statusfd == '2': + print(results, file=sys.stderr) |