#!/usr/bin/env python
# **********************************************************************
#
# Copyright (c) 2003-2006 ZeroC, Inc. All rights reserved.
#
# This copy of Ice is licensed to you under the terms described in the
# ICE_LICENSE file included in this distribution.
#
# **********************************************************************

import os
import sys
import getopt
import shutil
import socket
import tempfile

#
# dirname handles finding the .py under Windows since it will contain
# the location of the script. ICE_HOME handles the case where the
# install is in a non-standard location. "." handles
# development. "/usr/bin" handles RPM installs and "/opt/..." handles
# standard tarball installs.
#
for bindir in [os.path.dirname(sys.argv[0]), os.path.join(os.getenv("ICE_HOME"), "bin"), ".", "/usr/bin", \
	       "/opt/Ice-3.1.0/bin"]:
    bindir = os.path.normpath(bindir)
    if os.path.exists(os.path.join(bindir, "cautil.py")):
        break
else:
    raise "can't locate simple CA package"

sys.path.append(os.path.join(bindir))

import cautil

def usage():
    print "usage: " + sys.argv[0] + " [--no-password] [--overwrite]"
    sys.exit(1)

try:
    opts, args = getopt.getopt(sys.argv[1:], "", [ "no-password", "overwrite"])
except getopt.GetoptError:
    usage()

if args:
    usage()

home = cautil.findHome()
caroot = os.path.join(home, "ca")
cadb = os.path.join(caroot, "db")

print "This script will initialize your organization's Certificate Authority (CA)."
print "The CA database will be created in " + caroot

nopassphrase = False
for o, a in opts:
    if o == "--no-password":
        nopassphrase = True
    if o == "--overwrite":
	# If the CA exists then destroy it.
	if os.path.exists(os.path.join(home, caroot)):
	    print "Warning: running this command will destroy your existing CA setup!"
	    choice = raw_input("Do you want to continue? (y/n)")
	    if choice != 'y' and choice != 'Y':
		sys.exit(1)
	    shutil.rmtree(os.path.join(home, caroot))

#
# Check that the caroot isn't already populated.
#
if os.path.exists(os.path.join(cadb, "ca_key.pem")):
    print cadb + ": CA has already been initialized."
    print "Use the --overwrite option to re-initialize the database."
    sys.exit(1)

try:
    os.makedirs(cadb)
except OSError:
    pass

#
# Initialize the CA serial and index.
#
serial = open(os.path.join(cadb, "serial"), "w" )
serial.write("01\n")
serial.close()

index = open(os.path.join(cadb, "index.txt"), "w")
index.close()
    
# Construct the DN for the CA certificate.
DNelements = { \
    'C':['countryName', "Country name", "", 'match'], \
    'ST':['stateOfProviceName', "State or province name", "", 'match'], \
    'L':['localityName', "Locality", "", 'match'], \
    'O':['organizationName', "Organization name", "GridCA-" + socket.gethostname(), 'match'], \
    'OU':['organizationUnitName', "Organization unit name", "", 'optional'], \
    'CN':['commonName', "Common name", "Grid CA", 'supplied'] \
}

while True:
    print "The subject name for your CA will be "

    first = True
    for k,v in DNelements.iteritems():
    	if len(v[2]) > 0:
    	    if not first:
		print ", ",
	    print k + "=" + v[2],
    	    first = False
    print

    input = raw_input("Do you want to keep this as the CA subject name? (y/n) [y]")
    if input == 'n':
	for v in DNelements.values():
	    v[2] = raw_input(v[1] + ":")
    else:
	break

while True:
    DNelements['emailAddress'] = ['emailAddress', '', raw_input("Enter the email address of the CA: "), 'optional']
    if DNelements['emailAddress'][2] and len(DNelements['emailAddress'][2]) > 0:
	break

#
# Static configuration file data. This avoids locating template files
# and such.
#
config = {"ca.cnf":"\
# **********************************************************************\n\
#\n\
# Copyright (c) 2003-2006 ZeroC, Inc. All rights reserved.\n\
#\n\
# This copy of Ice is licensed to you under the terms described in the\n\
# ICE_LICENSE file included in this distribution.\n\
#\n\
# **********************************************************************\n\
\n\
# Configuration file for the CA. This file is generated by initca.py. DO NOT EDIT!\n\
\n\
###############################################################################\n\
###  Self Signed Root Certificate\n\
###############################################################################\n\
\n\
[ ca ]\n\
default_ca = ice\n\
\n\
[ ice ]\n\
default_days     = 1825    # How long certs are valid.\n\
default_md       = md5     # The Message Digest type.\n\
preserve         = no      # Keep passed DN ordering?\n\
\n\
[ req ]\n\
default_bits        = 2048\n\
default_keyfile     = $ENV::ICE_CA_HOME/ca/db/ca_key.pem\n\
default_md          = md5\n\
prompt              = no\n\
distinguished_name  = dn\n\
x509_extensions     = extensions\n\
\n\
[ extensions ]\n\
basicConstraints = CA:true\n\
\n\
# PKIX recommendation.\n\
subjectKeyIdentifier = hash\n\
authorityKeyIdentifier = keyid:always,issuer:always\n\
\n\
[dn]\n\
",\
"sign.cnf":"\
# **********************************************************************\n\
#\n\
# Copyright (c) 2003-2006 ZeroC, Inc. All rights reserved.\n\
#\n\
# This copy of Ice is licensed to you under the terms described in the\n\
# ICE_LICENSE file included in this distribution.\n\
#\n\
# **********************************************************************\n\
\n\
# Configuration file to sign a certificate. This file is generated by initca.py.\n\
# DO NOT EDIT!!\n\
\n\
[ ca ]\n\
default_ca = ice\n\
\n\
[ ice ]\n\
dir              = $ENV::ICE_CA_HOME/ca/db  # Where everything is kept.\n\
private_key      = $dir/ca_key.pem   # The CA Private Key.\n\
certificate      = $dir/ca_cert.pem  # The CA Certificate.\n\
database         = $dir/index.txt           # Database index file.\n\
new_certs_dir    = $dir                     # Default loc for new certs.\n\
serial           = $dir/serial              # The current serial number.\n\
certs            = $dir                     # Where issued certs are kept.\n\
RANDFILE         = $dir/.rand               # Private random number file.\n\
default_days     = 1825                     # How long certs are valid.\n\
default_md       = md5                      # The Message Digest type.\n\
preserve         = yes                      # Keep passed DN ordering?\n\
\n\
policy           = ca_policy\n\
x509_extensions  = certificate_extensions\n\
\n\
[ certificate_extensions ]\n\
basicConstraints = CA:false\n\
\n\
# PKIX recommendation.\n\
subjectKeyIdentifier = hash\n\
authorityKeyIdentifier = keyid:always,issuer:always\n\
\n\
# ca_policy is generated by the initca script.\n\
",\
"req.cnf":"\
# **********************************************************************\n\
#\n\
# Copyright (c) 2003-2006 ZeroC, Inc. All rights reserved.\n\
#\n\
# This copy of Ice is licensed to you under the terms described in the\n\
# ICE_LICENSE file included in this distribution.\n\
#\n\
# **********************************************************************\n\
\n\
# Configuration file to request a node, registry or service\n\
# certificate. This file is generated by initca.py. DO NOT EDIT!\n\
\n\
[ req ]\n\
default_bits        = 1024\n\
default_md          = md5\n\
prompt              = no\n\
distinguished_name  = dn\n\
x509_extensions     = extensions\n\
\n\
[ extensions ]\n\
basicConstraints = CA:false\n\
\n\
# PKIX recommendation.\n\
subjectKeyIdentifier = hash\n\
authorityKeyIdentifier = keyid:always,issuer:always\n\
keyUsage = nonRepudiation, digitalSignature, keyEncipherment\n\
\n\
# The dn section is added by the initca script.\n\
\n\
[dn]\n\
"\
}

#
# It is necessary to generate configuration files because the openssl
# configuration files do not permit empty values.
#
print "Generating configuration files... ",

print "ca.cnf",
temp, cacnfname = tempfile.mkstemp(".cnf", "ca")
os.write(temp, config["ca.cnf"])
for k,v in DNelements.iteritems():
    if len(v[2]) > 0:
	os.write(temp, v[0] + "=" + v[2] + "\n")
os.close(temp)

file = 'sign.cnf'
print " " + file,
cnf = open(os.path.join(caroot, file), "w")
cnf.write(config[file])
cnf.write("[ ca_policy ]\n");
for k,v in DNelements.iteritems():
    if len(v[2]) > 0:
	cnf.write(v[0] + "=" + v[3] + "\n")
cnf.close()

# Don't want these RDNs in req.cnf
del DNelements['emailAddress']
del DNelements['CN']

file = "req.cnf"
print file,
cnf = open(os.path.join(home, file), "w")
cnf.write(config[file])
for k,v in DNelements.iteritems():
    if len(v[2]) > 0:
	cnf.write(v[0] + "=" + v[2] + "\n")
cnf.close()

print "ok"

cmd = "openssl req -config " + cacnfname + " -x509 -days 1825 -newkey rsa:2048 -out " + \
	os.path.join(cadb, "ca_cert.pem") + " -outform PEM"

if nopassphrase:
    cmd += " -nodes"

#print cmd
status = os.system(cmd)
os.remove(cacnfname)
if status != 0:
    print "openssl command failed"
    sys.exit(1)

# Copy in the new ca certificate and private key.
shutil.copy(os.path.join(cadb, "ca_cert.pem"), os.path.join(home))

print
print "The CA is initialized."
print
print "You need to distribute the following files to all machines that can"
print "request certificates:"
print
print os.path.join(home, "req.cnf")
print os.path.join(home, "ca_cert.pem")
print
print "These files should be placed in the user's home directory in"
print "~/.iceca. On Windows, place these files in <ice-install>/config."
