aboutsummaryrefslogtreecommitdiff
path: root/gkeys
diff options
context:
space:
mode:
authorBrian Dolbec <dolsen@gentoo.org>2015-07-23 09:13:30 -0700
committerBrian Dolbec <dolsen@gentoo.org>2015-08-08 08:57:12 -0700
commit85357c2afe680b288bc277d61b497957d150221c (patch)
tree13529e8e4887cc3febd9748118e716533fe1755a /gkeys
parentgkeys/lib.py: Creation of the code for the verify_text() (diff)
downloadgentoo-keys-85357c2afe680b288bc277d61b497957d150221c.tar.gz
gentoo-keys-85357c2afe680b288bc277d61b497957d150221c.tar.bz2
gentoo-keys-85357c2afe680b288bc277d61b497957d150221c.zip
gkeysgpg: Initial commit of the gkeys-gpg command
Working skeleton of the cli using the gkeys cli code base gkeysgpg: Get the initail cli operations working with stubbed out Actions Parse the stdin data for the user name, nick, search for and set the correct keydir. Add in remaining args options needed. Change the return info from True/False to 0/1 to prevent confusion.
Diffstat (limited to 'gkeys')
-rwxr-xr-xgkeys/bin/gkeys-gpg56
-rw-r--r--gkeys/gkeysgpg/__init__.py1
-rw-r--r--gkeys/gkeysgpg/actions.py179
-rw-r--r--gkeys/gkeysgpg/cli.py140
4 files changed, 376 insertions, 0 deletions
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/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)