# Credentials are signed XML files that assign a subject gid privileges to an object gid
##
-import os
+import os, os.path
+import subprocess
from types import StringTypes
import datetime
from StringIO import StringIO
return subject
# sounds like this should be __repr__ instead ??
- def get_summary_tostring(self):
+ def pretty_cred(self):
if not self.gidObject:
self.decode()
obj = self.gidObject.get_printable_subject()
caller = self.gidCaller.get_printable_subject()
exp = self.get_expiration()
# Summarize the rights too? The issuer?
- return "[ Grant %s rights on %s until %s ]" % (caller, obj, exp)
+ return "[ Grant {caller} rights on {obj} until {exp} ]".format(**locals())
def get_signature(self):
if not self.signature:
xmlschema = etree.XMLSchema(schema_doc)
if not xmlschema.validate(tree):
error = xmlschema.error_log.last_error
- message = "%s: %s (line %s)" % (self.get_summary_tostring(), error.message, error.line)
+ message = "%s: %s (line %s)" % (self.pretty_cred(), error.message, error.line)
raise CredentialNotVerifiable(message)
if trusted_certs_required and trusted_certs is None:
# make sure it is not expired
if self.get_expiration() < datetime.datetime.utcnow():
raise CredentialNotVerifiable("Credential %s expired at %s" % \
- (self.get_summary_tostring(),
+ (self.pretty_cred(),
self.expiration.strftime(SFATIME_FORMAT)))
# Verify the signatures
filename = self.save_to_random_tmp_file()
- if trusted_certs is not None:
- cert_args = " ".join(['--trusted-pem %s' % x for x in trusted_certs])
# If caller explicitly passed in None that means skip cert chain validation.
# - Strange and not typical
if trusted_certs is None:
break
- command = '{} --verify --node-id "{}" {} {} 2>&1'.\
- format(self.xmlsec_path, ref, cert_args, filename)
- logger.debug("Running '{}'".format(command))
- verified = os.popen(command).read()
- logger.debug("xmlsec command returned {}".format(verified))
- if not verified.strip().startswith("OK"):
+ # Thierry - jan 2015
+ # up to fedora20 we used os.popen and checked that the output begins with OK
+ # turns out, with fedora21, there is extra input before this 'OK' thing
+ # looks like we're better off just using the exit code - that's what it is made for
+ #cert_args = " ".join(['--trusted-pem %s' % x for x in trusted_certs])
+ #command = '{} --verify --node-id "{}" {} {} 2>&1'.\
+ # format(self.xmlsec_path, ref, cert_args, filename)
+ command = [ self.xmlsec_path, '--verify', '--node-id', ref ]
+ for trusted in trusted_certs:
+ command += ["--trusted-pem", trusted ]
+ command += [ filename ]
+ logger.debug("Running " + " ".join(command))
+ try:
+ verified = subprocess.check_output(command, stderr=subprocess.STDOUT)
+ logger.debug("xmlsec command returned {}".format(verified))
+ if "OK\n" not in verified:
+ logger.warning("WARNING: xmlsec1 seemed to return fine but without a OK in its output")
+ except subprocess.CalledProcessError as e:
+ verified = e.output
# xmlsec errors have a msg= which is the interesting bit.
mstart = verified.find("msg=")
msg = ""
mstart = mstart + 4
mend = verified.find('\\', mstart)
msg = verified[mstart:mend]
- raise CredentialNotVerifiable("xmlsec1 error verifying cred %s"
- "using Signature ID %s: %s %s" % \
- (self.get_summary_tostring(),
- ref, msg, verified.strip()))
+ logger.warning("Credential.verify - failed - xmlsec1 returned {}".format(verified.strip()))
+ raise CredentialNotVerifiable("xmlsec1 error verifying cred %s using Signature ID %s: %s" % \
+ (self.pretty_cred(), ref, msg))
os.remove(filename)
# Verify the parents (delegation)
# make sure my expiry time is <= my parent's
if not parent_cred.get_expiration() >= self.get_expiration():
raise CredentialNotVerifiable("Delegated credential %s expires after parent %s" % \
- (self.get_summary_tostring(), parent_cred.get_summary_tostring()))
+ (self.pretty_cred(), parent_cred.pretty_cred()))
# make sure my signer is the parent's caller
if not parent_cred.get_gid_caller().save_to_string(False) == \
self.get_signature().get_issuer_gid().save_to_string(False):
raise CredentialNotVerifiable("Delegated credential %s not signed by parent %s's caller" % \
- (self.get_summary_tostring(), parent_cred.get_summary_tostring()))
+ (self.pretty_cred(), parent_cred.pretty_cred()))
# Recurse
if parent_cred.parent:
# else this looks like a delegated credential, and the real caller is the issuer
else:
actual_caller_hrn=issuer_hrn
- logger.info("actual_caller_hrn: caller_hrn=%s, issuer_hrn=%s, returning %s"%(caller_hrn,issuer_hrn,actual_caller_hrn))
+ logger.info("actual_caller_hrn: caller_hrn=%s, issuer_hrn=%s, returning %s"
+ %(caller_hrn,issuer_hrn,actual_caller_hrn))
return actual_caller_hrn
##