Newer
Older
"""Gestion centralisée des mots de passe avec chiffrement GPG
Copyright (C) 2010-2020 Cr@ns <roots@crans.org>
Authors : Daniel Stan <daniel.stan@crans.org>
Vincent Le Gallic <legallic@crans.org>
"""
Vincent Le gallic
committed
# Import builtins
Vincent Le gallic
committed
import time
Vincent Le gallic
committed
from paramiko.client import SSHClient
from paramiko.ssh_exception import SSHException
# On n'a pas encore accès à la config donc on devine le nom
bootstrap_cmd_name = os.path.split(sys.argv[0])[1]
default_config_path = os.path.expanduser("~/.config/" + bootstrap_cmd_name)
config_path = os.getenv(
"CRANSPASSWORDS_CLIENT_CONFIG_DIR", default_config_path)
config = ConfigParser()
if not config.read(config_path + "/clientconfig.ini"):
# If config could not be imported, display an error if required
ducktape_display_error = sys.stderr.isatty() and \
not any([opt in sys.argv for opt in ["-q", "--quiet"]]) and \
__name__ == '__main__'
if ducktape_display_error:
# Do not use logger as it has not been initialized yet
print("%s/clientconfig.ini could not be read. Please read README." %
config_path)
exit(1)
# Logger local
log = logging.getLogger(bootstrap_cmd_name)
Vincent Le gallic
committed
#: Pattern utilisé pour détecter la ligne contenant le mot de passe dans les fichiers
pass_regexp = re.compile('[\t ]*pass(?:word)?[\t ]*:[\t ]*(.*)\r?\n?$',
Vincent Le gallic
committed
#: Path du binaire gpg
Vincent Le gallic
committed
#: Paramètres à fournir à gpg en fonction de l'action désirée
'decrypt': ['-d'],
'encrypt': ['--armor', '-es'],
'receive-keys': ['--recv-keys'],
'list-keys': ['--list-keys', '--with-colons', '--fixed-list-mode',
'--with-fingerprint', '--with-fingerprint'], # Ce n'est pas une erreur. Il faut 2 --with-fingerprint pour avoir les fingerprints des subkeys.
}
Vincent Le gallic
committed
#: Mapping (lettre de trustlevel) -> (signification, faut-il faire confiance à la clé)
"-": ("inconnue (pas de valeur assignée)", False),
"o": ("inconnue (nouvelle clé)", False),
"i": ("invalide (self-signature manquante ?)", False),
"n": ("nulle (il ne faut pas faire confiance à cette clé)", False),
"m": ("marginale (pas assez de lien de confiance vers cette clé)", False),
"f": ("entière (clé dans le réseau de confiance)", True),
"u": ("ultime (c'est probablement ta clé)", True),
"r": ("révoquée", False),
"e": ("expirée", False),
"q": ("non définie", False),
}
def gpg(options, command, args=None):
Vincent Le gallic
committed
donnés. Renvoie son entrée standard et sa sortie standard."""
full_command = [GPG]
full_command.extend(GPG_ARGS[command])
if args:
full_command.extend(args)
Vincent Le gallic
committed
if options.verbose:
stderr = sys.stderr
stderr = subprocess.PIPE
# Run gpg
log.info("Running `%s`" % " ".join(full_command))
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
stderr=stderr,
close_fds=True)
Vincent Le gallic
committed
if not options.verbose:
Vincent Le gallic
committed
def _parse_timestamp(string, canbenone=False):
"""Interprète ``string`` comme un timestamp depuis l'Epoch."""
Vincent Le gallic
committed
return None
return datetime.datetime(*time.localtime(int(string))[:7])
Vincent Le gallic
committed
def _parse_pub(data):
"""Interprète une ligne ``pub:``"""
d = {
'trustletter': data[1],
'length': int(data[2]),
'algorithm': int(data[3]),
'longid': data[4],
'signdate': _parse_timestamp(data[5]),
'expiredate': _parse_timestamp(data[6], canbenone=True),
'ownertrustletter': data[8],
'capabilities': data[11],
Vincent Le gallic
committed
return d
Vincent Le gallic
committed
def _parse_uid(data):
"""Interprète une ligne ``uid:``"""
d = {
'trustletter': data[1],
'signdate': _parse_timestamp(data[5], canbenone=True),
'hash': data[7],
'uid': data[9],
Vincent Le gallic
committed
return d
Vincent Le gallic
committed
def _parse_fpr(data):
"""Interprète une ligne ``fpr:``"""
d = {
Vincent Le gallic
committed
return d
Vincent Le gallic
committed
def _parse_sub(data):
"""Interprète une ligne ``sub:``"""
d = {
'trustletter': data[1],
'length': int(data[2]),
'algorithm': int(data[3]),
'longid': data[4],
'signdate': _parse_timestamp(data[5]),
'expiredate': _parse_timestamp(data[6], canbenone=True),
'capabilities': data[11],
Vincent Le gallic
committed
return d
Vincent Le gallic
committed
#: Functions to parse the recognized fields
GPG_PARSERS = {
'pub': _parse_pub,
'uid': _parse_uid,
'fpr': _parse_fpr,
'sub': _parse_sub,
Vincent Le gallic
committed
def parse_keys(gpgout, debug=False):
"""Parse l'output d'un listing de clés gpg."""
ring = {}
# Valeur utilisée pour dire "cet objet n'a pas encore été rencontré pendant le parsing"
init_value = "initialize"
Vincent Le gallic
committed
current_pub = init_value
current_sub = init_value
for line in iter(gpgout.readline, ''):
# La doc dit que l'output est en UTF-8 «regardless of any --display-charset setting»
try:
line = line.decode("utf-8")
except UnicodeDecodeError:
try:
line = line.decode("iso8859-1")
except UnicodeDecodeError:
line = line.decode("iso8859-1", "ignore")
log.warning(
"gpg is telling shit, it is neither ISO-8859-1 nor UTF-8. Dropping!")
Vincent Le gallic
committed
line = line.split(":")
field = line[0]
if field in GPG_PARSERS.keys():
log.debug("begin loop, met %s :" % (field))
log.debug("current_pub : %r" % current_pub)
log.debug("current_sub : %r" % current_sub)
Vincent Le gallic
committed
try:
content = GPG_PARSERS[field](line)
Vincent Le gallic
committed
raise
Loading
Loading full blame...