#!/bin/env python

# Copyright/License GPLv2+ by Sascha Thomas Spreitzer <sspreitzer@fedoraproject.org>
# requires openssl, nss-tools

import os, os.path, sys, getopt
import re
import glob
import subprocess

certdir = "/etc/pki/tls/certs"

def popen(args):
    try:
        p = subprocess.Popen(args.split(" "), stdout=subprocess.PIPE)
        out = p.communicate()[0]
        rc = p.wait()
        return (out, rc)
    except StandardError, e:
        print e
        return ("", -1)

def get_hash(path, index=True):
    i = 0
    
    ret = popen("/usr/bin/openssl x509 -hash -noout -in " + path)
    h = ret[0].split("\n")[0]
    
    if ret[1] != 0:
        sys.exit(4)
    
    if index:
        while os.access(certdir + "/" + h + "." + str(i), os.F_OK):
            if os.path.realpath(certdir + "/" + h + "." + str(i)) == path:
                return ""
            i += 1
        return h + "." + str(i)
    else:
        return h
    
    
def rm_brlinks():
    for i in os.listdir(certdir):
        path = certdir + "/" + i
        if os.path.islink(path):
            if not os.access(path, os.F_OK):
                print "INF: Removing broken link " + path
                os.remove(path)    


def add_link(cert):
    hash = get_hash(cert)
    if hash != "":
        if not os.path.islink(certdir + "/" + hash):
            print "INF: Creating link from " + certdir + "/" + hash + " to " + cert
            os.symlink(cert, certdir + "/" + hash)


def add_nss(cert):
    os.environ['NSS_DEFAULT_DB_TYPE'] = "sql"
    print "INF: Adding certificate from " + cert + " to shared system NSS"
    ret = popen("/usr/bin/certutil -A -d /etc/pki/nssdb -n " + cert + " -t C,C,C -i "+ cert)
    if ret[1] == 0:
        return True
    else:
        return False


def del_link(cert):
    for i in glob.glob(certdir + "/" + get_hash(cert, False) + ".*"):
        if os.path.realpath(i) == cert:
            print "INF: Removing certificate link " + cert
            os.remove(i)


def del_nss(cert):
    os.environ['NSS_DEFAULT_DB_TYPE'] = "sql"
    print "INF: Removing certificate " + cert + " from shared system NSS"
    ret = popen("/usr/bin/certutil -D -d /etc/pki/nssdb -n " + cert)
    if ret[1] == 0:
        return True
    else:
        return False    
    

def usage():
    print "Usage: " + sys.argv[0] + " [options]"
    print
    print "Options:"
    print " -a, --add\t\tadds a CA certficate to the systems CA store"
    print " -d, --delete\t\tdeletes broken links of CA certificates"
    print " -n, --nss\t\talso use shared NSS store"
    print " -i myCA.crt, --inputcert=myCA.crt\n\t\t\tCA certficate input file"
    print 
    print "Examples:"
    print " " + sys.argv[0] + " -a -n -i /etc/pki/tls/certs/myCA.crt"
    print "\tAdds myCA.crt to OpenSSL and NSS shared system store"
    print
    print " " + sys.argv[0] + " -d -n -i /etc/pki/tls/certs/myCA.crt"
    print " rm /etc/pki/tls/certs/myCA.crt"
    print "\tRemoves the certificate from the OpenSSL and NSS shared system storage"
    print "\tCertificate file is untouched, you need to remove it manually"
    print


def main():
    try:
        nss = False
        mode = ""
        cert = ""
        opts, args = getopt.getopt(sys.argv[1:], "adi:n", ["add", "delete", "nss", "inputcert="])
        for o, a in opts:
            if o == "-a" or o == "--add":
                mode = "add"
            elif o == "-d" or o == "--delete":
                mode = "delete"
            elif o == "-i" or o == "--inputcert":
                cert = a
            elif o == "-n" or o == "--nss":
                nss = True
    except getopt.GetoptError, e:
        print e
        usage()
        sys.exit(3)
    
    if os.geteuid() != 0:
        print "INF: Must be run as root"
        sys.exit(1)    
    
    if cert == "":
        print "ERR: Provide a cert with -i or --inputcert= please"
        usage()
        sys.exit(4)
    
    cert = os.path.abspath(cert)
    if not os.access(cert, os.R_OK|os.F_OK):
        print "ERR: Cannot open or read " + cert
        sys.exit(4)
        
    if mode == "add":
        try:
            rm_brlinks()
            add_link(cert)        
            if nss:
                add_nss(cert)
        except StandardError, e:
            print e
            sys.exit(4)
            
        return
    
    if mode == "delete":
        try:
            del_link(cert)
            if nss:
                del_nss(cert)
            rm_brlinks()
        except StandardError, e:
            print e
            sys.exit(4)
            
        return
           
            
if __name__=="__main__":
    main()
