This file is indexed.

/usr/bin/openpgpkey is in hash-slinger 2.6-1.

This file is owned by root:root, with mode 0o755.

The actual contents of the file can be viewed below.

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
#!/usr/bin/python

# openpgpkey: create OPENPGPKEY DNS record from a key in your keychain.

# Copyright 2012 - 2014 Paul Wouters <paul@cypherpunks.ca>
#
# License: GNU GENERAL PUBLIC LICENSE Version 2 or later

VERSION="2.6"
OPENPGPKEY=61

import sys
import os
import gnupg
import unbound
from hashlib import sha224
import tempfile
import shutil

global ctx

def asctohex(s):
    empty = '' # I use this construct because I find ''.join() too dense
    return empty.join(['%02x' % ord(c) for c in s]) # the %02 pads when needed

def getOPENPGPKEY(email,insecure_ok):
	"""This function queries for an OPENPGPKEY DNS Resource Record and compares it with the local gnupg keyring"""

	global ctx

	try:
		username, domainname = email.split("@")
	except:
		sys.exit("Invalid email syntax")

	keyname = "%s._openpgpkey.%s"%(sha224(username).hexdigest() ,domainname)

	status, result = ctx.resolve(keyname, OPENPGPKEY)
	if status == 0 and result.havedata:
		if not result.secure:
			if not insecure_ok:
				# The data is insecure and a secure lookup was requested
				sys.exit("Error: query data is not secured by DNSSEC - use --insecure to override")
			else:
				print >> sys.stderr, 'Warning: query data was not secured by DNSSEC.'
		# If we are here the data was either secure or insecure data is accepted
		return result.data.raw
	else:
		sys.exit('Unsuccesful lookup or no data returned for OPENPGPKEY (rrtype 61)')

if __name__ == '__main__':
	import argparse
	# create the parser
	parser = argparse.ArgumentParser(description='Create and verify OPENPGPKEY records.', epilog='For bugs. see paul@nohats.ca')

	parser.add_argument('--verify','-v', action='store_true', help='Verify an OPENPGPKEY record, exit 0 when all records are matched, exit 1 on error.')
	parser.add_argument('--fetch','-f', action='store_true', help='Fetch an OPENPGPKEY record, and show it in ascii armor on stdout')

	parser.add_argument('--create','-c', action='store_true', help='Create an OPENPGKEY record')
	parser.add_argument('--version', action='version', version='openpgpkey version: %s'%VERSION, help='show version and exit')

	parser.add_argument('--insecure', action='store_true', default=False, help='Allow use of non-dnssec secured answers')
	parser.add_argument('--resolvconf', action='store', default='', help='Use a recursive resolver listed in a resolv.conf file (default: /etc/resolv.conf)')
	parser.add_argument('--rootanchor', action='store', default='/usr/share/dns/root.key', help='Location of the unbound compatible DNSSEC root key (default: /usr/share/dns/root.key)')
	parser.add_argument('email', metavar="email")

	parser.add_argument('--uid', action='store', default='', help='override the uid (email address) within the key')

	parser.add_argument('--debug', '-d', action='store_true', help='Print details plus the result of the validation')
	parser.add_argument('--quiet', '-q', action='store_true', help='Ignored for backwards compatibility')

	# default for now to generic - when more tools support OPENPGPKEY, switch the default to rfc
	parser.add_argument('--output', '-o', action='store', default='generic', choices=['generic','rfc','both'], help='The type of output. Generic (RFC 3597, TYPE61), RFC (OPENPGPKEY) or both (default: %(default)s).')

	args = parser.parse_args()

	if args.verify and args.create:
		sys.exit("openpgpkey: must specify --create or --verify")

	if args.verify or args.fetch:
		# unbound setup, only for verify
		global ctx
		ctx = unbound.ub_ctx()
		resolvconf = "/etc/resolv.conf"
		rootanchor = "/usr/share/dns/root.key"

		if args.resolvconf:
			if os.path.isfile(args.resolvconf):
				resolvconf = args.resolvconf
			else:
				print >> sys.stdout, 'openpgpkey: %s is not a file. Unable to use it as resolv.conf' % args.resolvconf
				sys.exit(1)
		ctx.resolvconf(resolvconf)

		if args.rootanchor:
			if os.path.isfile(args.rootanchor):
				rootanchor = args.rootanchor
			else:
				print >> sys.stdout, 'openpgpkey: %s is not a file. Unable to use it as rootanchor' % args.rootanchor
				sys.exit(1)
		else:
			if not os.path.isfile(rootanchor):
				rootanchor = None
		if rootanchor:
			try:
				ctx.add_ta_file(rootanchor)
                        except:
				unbound.ub_ctx_trustedkeys(ctx,rootanchor)
		else:
			# fallback to a root key if we can find it
			if os.path.isfile("/usr/share/dns/root.key"):
				rootkey = "/usr/share/dns/root.key"
			else:
				rootkey = None
			if rootkey:
				unbound.ub_ctx_trustedkeys(ctx,rootkey)
		# unbound setup completed

		openpgpkeys = getOPENPGPKEY(args.email, args.insecure)
		if len(openpgpkeys) == 0:
			print >> sys.stderr, 'openpgpkey: Received nothing?'
			sys.exit(1)
		fdir = tempfile.mkdtemp(".gpg","openpgpkey-","/tmp/")
		gpgnet = gnupg.GPG(gnupghome=fdir)
		gpgnet.decode_errors = 'ignore'

		for openpgpkey in openpgpkeys:
			import_result = gpgnet.import_keys(openpgpkey)
			if args.fetch:
				if args.uid:
					pubkey = gpgnet.export_keys(args.uid, minimal=True)
					if not pubkey:
						print >> sys.stderr, 'openpgpkey: Requested uid not present in received OpenPGP data'
						for id in  gpgnet.list_keys()[0]['uids']:
							print >> sys.stderr, "# %s"%id
						sys.exit(1)
				else:
					pubkey = gpgnet.export_keys(args.email, minimal=True)
				if not pubkey:
					print >> sys.stderr, 'openpgpkey: Received OpenPGP data does not contain a key with keyid %s'%args.email
					print >> sys.stderr, '(add --uid <uid> to override with any of the below received uids)'
					for id in  gpgnet.list_keys()[0]['uids']:
						print >> sys.stderr, "# %s"%id
					sys.exit(1)

				pubkey = pubkey.replace("Version:","Comment: %s key obtained from DNS\nVersion:"%args.email)
				if args.insecure:
					pubkey = pubkey.replace("Version:","Comment: NOT VALIDATED BY DNSSEC\nVersion:")
				else:
					pubkey = pubkey.replace("Version:","Comment: key transfer was protected by DNSSEC\nVersion:")
				print pubkey

		if args.fetch:
			sys.exit(0)

		received_keys = gpgnet.list_keys() 
		gpgdisk = gnupg.GPG()
		gpgdisk.decode_errors = 'ignore'
		disk_keys = gpgdisk.list_keys()
		for pkey in received_keys:
			if args.debug:
				print >> sys.stderr, "Received from DNS: Key-ID:%s Fingerprint:%s"%(pkey["keyid"], pkey["fingerprint"])
			found = False
			for dkey in disk_keys:
				if args.debug:
					print >> sys.stderr, "Local disk: Key-ID:%s Fingerprint:%s"%(dkey["keyid"], dkey["fingerprint"])
				if pkey["keyid"] == dkey["keyid"] and pkey["fingerprint"] == dkey["fingerprint"]:
					found = True
			if found == False:
				shutil.rmtree(fdir)
				sys.exit("Received key with keyid %s was not found"%pkey["keyid"])
			else:
				if args.debug:
					print >> sys.stderr, "Received key with keyid %s was found"%pkey["keyid"]
		print "All OPENPGPKEY records matched with content from the local keyring"
		shutil.rmtree(fdir)
		sys.exit(0)

	else: # we want to create
		gpgdisk = gnupg.GPG()
		gpgdisk.decode_errors = 'ignore'
		disk_keys = gpgdisk.list_keys()
		found = False
		for pgpkey in disk_keys:
			for uid in pgpkey["uids"]:
				if "<%s>"%args.email in uid:
					found = True
					if args.debug:
						print >> sys.stderr,  "Found matching KeyID: %s (%s) for %s"%(pgpkey["keyid"], pgpkey["fingerprint"], uid)
					ekey = gpgdisk.export_keys(pgpkey["keyid"],minimal=True, secret=False, armor=False)
					ekey64 = "".join(gpgdisk.export_keys(pgpkey["keyid"],minimal=True, secret=False, armor=True).split("\n")[2:-3])
					user,domain = args.email.split("@")
					euser = sha224(user).hexdigest()

					if args.output == "rfc":
						print "%s._openpgpkey.%s. IN OPENPGPKEY %s"%(euser,domain,ekey64)
					if args.output ==  "generic":
						if args.debug:
							print "Length for generic record is %s"%len(ekey)
						print "%s._openpgpkey.%s. IN TYPE61 \# %s %s"%(euser,domain,len(ekey),asctohex(ekey))
		if not found:
			sys.exit("No key found containing the email address '%s' in the keyid(s)"%args.email)