This file is indexed.

/usr/bin/openpgpkey is in hash-slinger 2.5-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
#!/usr/bin/python

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

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

VERSION="2.5"

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

global openpgpkey_rrtype
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
	global openpgpkey_rrtype

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

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

	status, result = ctx.resolve(keyname, rrtype=int(openpgpkey_rrtype))
	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 rrtype %s.' %openpgpkey_rrtype)

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('--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='/var/lib/unbound/root.anchor', help='Location of the unbound compatible DNSSEC root.anchor (default: /var/lib/unbound/root.anchor)')
	parser.add_argument('--rrtype', metavar='rrtype', action='store', default=65280, help='Location of the unbound compatible DNSSEC root.anchor (default: /var/lib/unbound/root.anchor)')
	parser.add_argument('email', metavar="email")

	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')

	parser.add_argument('--output', '-o', action='store', default='generic', choices=['generic','rfc','both'], help='The type of output. Generic (RFC 3597, TYPE65280), RFC (OPENPGPKEY) or both (default: %(default)s).')

	args = parser.parse_args()

	global openpgpkey_rrtype

	# unbound setup
	global ctx
	ctx = unbound.ub_ctx()
	resolvconf = "/etc/resolv.conf"
	rootanchor = "/var/lib/unbound/root.anchor"
	dlvkey = "/etc/unbound/dlv.isc.org.key"

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

	if os.path.isfile(dlvkey):
		ctx.set_option("dlv-anchor-file:", dlvkey)

	if args.rootanchor:
		if os.path.isfile(args.rootanchor):
			resolvconf = args.rootanchor
		else:
			print >> sys.stdout, '%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:
		ctx.add_ta_file(rootanchor)
	else:
		# fallback to a root key if we can find it
		if os.path.isfile("/etc/unbound/root.key"):
			rootkey = "/etc/unbound/root.key"
		elif os.path.isfile("/var/lib/unbound/root.key"):
			rootkey = "/var/lib/unbound/root.key"
		else:
			rootkey = None
		if rootkey:
			unbound.ub_ctx_trustedkeys(ctx,rootkey)

	# unbound setup completed

	openpgpkey_rrtype = int(args.rrtype)

	if args.verify:
		openpgpkeys = getOPENPGPKEY(args.email, args.insecure)
		if len(openpgpkeys) == 0:
			print 'Received nothing?'
			sys.exit(1)
		fdir = tempfile.mkdtemp(".gpg","openpgpkey-","/tmp/")
		gpgnet = gnupg.GPG(gnupghome=fdir)
		gpgdisk = gnupg.GPG()
		for openpgpkey in openpgpkeys:
			import_result = gpgnet.import_keys(openpgpkey)
		received_keys = gpgnet.list_keys() 
		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()
		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 = base64.b64encode(ekey)
					user,domain = args.email.split("@")
					euser = sha224(user).hexdigest()

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