#! /usr/bin/env python3
""" Imports a keyring into the database """
# Copyright (C) 2007 Anthony Towns <aj@erisian.com.au>
# Copyright (C) 2009 Mark Hymers <mhy@debian.org>
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
################################################################################
import sys
import apt_pkg
from daklib.config import Config
from daklib.dbconn import *
# Globals
Options = None
################################################################################
[docs]def get_uid_info(session):
byname = {}
byid = {}
q = session.execute("SELECT id, uid, name FROM uid")
for (keyid, uid, name) in q.fetchall():
byname[uid] = (keyid, name)
byid[keyid] = (uid, name)
return (byname, byid)
[docs]def get_fingerprint_info(session):
fins = {}
q = session.execute("SELECT f.fingerprint, f.id, f.uid, f.keyring FROM fingerprint f")
for (fingerprint, fingerprint_id, uid, keyring) in q.fetchall():
fins[fingerprint] = (uid, fingerprint_id, keyring)
return fins
[docs]def list_uids(session, pattern):
sql_pattern = f"%{pattern}%"
message = "List UIDs matching pattern %s" % sql_pattern
message += "\n" + ("=" * len(message))
print(message)
uid_query = session.query(Uid).filter(Uid.uid.ilike(sql_pattern))
for uid in uid_query.all():
print("\nuid %s" % uid.uid)
for fp in uid.fingerprint:
print(" fingerprint %s" % fp.fingerprint)
keyring = "unknown"
if fp.keyring:
keyring = fp.keyring.keyring_name
print(" keyring %s" % keyring)
################################################################################
[docs]def usage(exit_code=0):
print("""Usage: dak import-keyring [OPTION]... [KEYRING]
-h, --help show this help and exit.
-L, --import-ldap-users generate uid entries for keyring from LDAP
-U, --generate-users FMT generate uid entries from keyring as FMT
-l, --list-uids STRING list all uids matching *STRING*
-n, --no-action don't change database""")
sys.exit(exit_code)
################################################################################
[docs]def main():
global Options
cnf = Config()
Arguments = [('h', "help", "Import-Keyring::Options::Help"),
('L', "import-ldap-users", "Import-Keyring::Options::Import-Ldap-Users"),
('U', "generate-users", "Import-Keyring::Options::Generate-Users", "HasArg"),
('l', "list-uids", "Import-Keyring::Options::List-UIDs", "HasArg"),
('n', "no-action", "Import-Keyring::Options::No-Action"),
]
for i in ["help", "report-changes", "generate-users",
"import-ldap-users", "list-uids", "no-action"]:
key = "Import-Keyring::Options::%s" % i
if key not in cnf:
cnf[key] = ""
keyring_names = apt_pkg.parse_commandline(cnf.Cnf, Arguments, sys.argv)
### Parse options
Options = cnf.subtree("Import-Keyring::Options")
if Options["Help"]:
usage()
### Initialise
session = DBConn().session()
if Options["List-UIDs"]:
list_uids(session, Options["List-UIDs"])
sys.exit(0)
if len(keyring_names) != 1:
usage(1)
### Keep track of changes made
changes = [] # (uid, changes strings)
### Cache all the existing fingerprint entries
db_fin_info = get_fingerprint_info(session)
### Parse the keyring
keyringname = keyring_names[0]
keyring = get_keyring(keyringname, session)
if not keyring:
print("E: Can't load keyring %s from database" % keyringname)
sys.exit(1)
keyring.load_keys(keyringname)
### Generate new uid entries if they're needed (from LDAP or the keyring)
if Options["Generate-Users"]:
format = Options["Generate-Users"]
(desuid_byname, desuid_byid) = keyring.generate_users_from_keyring(Options["Generate-Users"], session)
elif Options["Import-Ldap-Users"]:
(desuid_byname, desuid_byid) = keyring.import_users_from_ldap(session)
else:
(desuid_byname, desuid_byid) = ({}, {})
### Cache all the existing uid entries
(db_uid_byname, db_uid_byid) = get_uid_info(session)
### Update full names of applicable users
for keyid in desuid_byid.keys():
uid = (keyid, desuid_byid[keyid][0])
name = desuid_byid[keyid][1]
oname = db_uid_byid[keyid][1]
if name and oname != name:
changes.append((uid[1], "Full name: %s" % (name)))
session.execute("UPDATE uid SET name = :name WHERE id = :keyid",
{'name': name, 'keyid': keyid})
# The fingerprint table (fpr) points to a uid and a keyring.
# If the uid is being decided here (ldap/generate) we set it to it.
# Otherwise, if the fingerprint table already has a uid (which we've
# cached earlier), we preserve it.
# Otherwise we leave it as None
fpr = {}
for z in keyring.keys.keys():
keyid = db_uid_byname.get(keyring.keys[z].get("uid", None), [None])[0]
if keyid is None:
keyid = db_fin_info.get(keyring.keys[z]["fingerprints"][0], [None])[0]
for y in keyring.keys[z]["fingerprints"]:
fpr[y] = (keyid, keyring.keyring_id)
# For any keys that used to be in this keyring, disassociate them.
# We don't change the uid, leaving that for historical info; if
# the id should change, it'll be set when importing another keyring.
for f, (u, fid, kr) in db_fin_info.items():
if kr != keyring.keyring_id:
continue
if f in fpr:
continue
changes.append((db_uid_byid.get(u, [None])[0], "Removed key: %s" % (f)))
session.execute("""UPDATE fingerprint
SET keyring = NULL
WHERE id = :fprid""", {'fprid': fid})
# For the keys in this keyring, add/update any fingerprints that've
# changed.
for f in fpr:
newuid = fpr[f][0]
newuiduid = db_uid_byid.get(newuid, [None])[0]
(olduid, oldfid, oldkid) = db_fin_info.get(f, [-1, -1, -1])
if olduid is None:
olduid = -1
if oldkid is None:
oldkid = -1
if oldfid == -1:
changes.append((newuiduid, "Added key: %s" % (f)))
fp = Fingerprint()
fp.fingerprint = f
fp.keyring_id = keyring.keyring_id
if newuid:
fp.uid_id = newuid
session.add(fp)
session.flush()
else:
if newuid and olduid != newuid and olduid == -1:
changes.append((newuiduid, "Linked key: %s" % f))
changes.append((newuiduid, " (formerly unowned)"))
session.execute("UPDATE fingerprint SET uid = :uid WHERE id = :fpr",
{'uid': newuid, 'fpr': oldfid})
# Don't move a key from a keyring with a higher priority to a lower one
if oldkid != keyring.keyring_id:
movekey = False
if oldkid == -1:
movekey = True
else:
try:
oldkeyring = session.query(Keyring).filter_by(keyring_id=oldkid).one()
except NotFoundError:
print("ERROR: Cannot find old keyring with id %s" % oldkid)
sys.exit(1)
if oldkeyring.priority < keyring.priority:
movekey = True
# Only change the keyring if it won't result in a loss of permissions
if movekey:
session.execute("""UPDATE fingerprint
SET keyring = :keyring
WHERE id = :fpr""",
{'keyring': keyring.keyring_id,
'fpr': oldfid})
session.flush()
else:
print("Key %s exists in both %s and %s keyrings. Not demoting." % (f,
oldkeyring.keyring_name,
keyring.keyring_name))
# All done!
if Options["No-Action"]:
session.rollback()
else:
session.commit()
# Print a summary
changesd = {}
for (k, v) in changes:
if k not in changesd:
changesd[k] = ""
changesd[k] += " %s\n" % (v)
for k in sorted(changesd):
print("%s\n%s\n" % (k, changesd[k]))
################################################################################
if __name__ == '__main__':
main()