OSDN Git Service

commited.
authorTaizo ITO <taizo@karesansui-project.info>
Tue, 1 Dec 2009 04:55:49 +0000 (13:55 +0900)
committerTaizo ITO <taizo@karesansui-project.info>
Tue, 1 Dec 2009 04:55:49 +0000 (13:55 +0900)
installer/installer/const.py
installer/installer/install.py
installer/installer/snack_ui.py
installer/installer/utils.py

index 84f42d4..02f5c88 100644 (file)
@@ -81,10 +81,24 @@ DEFAULT_PYTHONPATH="%s:%s:%s:%s" % (SCRIPT_DIR,VENDOR_PYTHONPATH,SILHOUETTE_PYTH
 LIGHTY_SSL_CONF  = "%s/lighttpd/conf.d/karesansui/ssl.conf"  % VENDOR_SYSCONFDIR
 LIGHTY_PORT_CONF = "%s/lighttpd/conf.d/karesansui/port.conf" % VENDOR_SYSCONFDIR
 
+PKI_DATA_PREFIX        = VENDOR_SYSCONFDIR + "/pki"
+PKI_DATA_PREFIX        = "/tmp/pki"
+PKI_CA_DIR             = PKI_DATA_PREFIX + "/CA"
+PKI_CA_INFO            = PKI_CA_DIR + "/ca.info"
+PKI_CA_PRIVATE_KEY     = PKI_CA_DIR + "/cakey.pem"
+PKI_CA_SELFSIGNED_CRT  = PKI_CA_DIR + "/cacert.pem"
+PKI_SERVER_INFO        = PKI_DATA_PREFIX + "/libvirt/server.info"
+PKI_SERVER_PRIVATE_KEY = PKI_DATA_PREFIX + "/libvirt/private/serverkey.pem"
+PKI_SERVER_CRT         = PKI_DATA_PREFIX + "/libvirt/servercert.pem"
+PKI_CLIENT_INFO        = PKI_DATA_PREFIX + "/libvirt/client.info"
+PKI_CLIENT_PRIVATE_KEY = PKI_DATA_PREFIX + "/libvirt/private/clientkey.pem"
+PKI_CLIENT_CRT         = PKI_DATA_PREFIX + "/libvirt/clientcert.pem"
+
 COMMAND_CHKCONFIG = "/sbin/chkconfig"
 COMMAND_SERVICE   = "/sbin/service"
 COMMAND_IPTABLES  = "/sbin/iptables"
 COMMAND_IPTABLES_SAVE = "/sbin/iptables-save"
+COMMAND_CERTTOOL      = "certtool"
 
 REQUIRE_SERVICES = [
     "hde-libvirtd",
index c59bae7..058fba9 100644 (file)
@@ -235,6 +235,57 @@ def process_install_package(opts,p_callback=None):
     write_log(_("Leaving '%s'") % sys._getframe(0).f_code.co_name)
     return True
 
+def process_init_certs(opts,p_callback=None):
+    write_log(_("Entering '%s'") % sys._getframe(0).f_code.co_name)
+
+    global logfile
+    from installer.utils import r_chmod, r_chgrp, generate_privkey, generate_ca_info, generate_ca_cert, generate_cert, generate_server_info, generate_client_info
+
+    title = _("Generating SSL Certificates...")
+
+    cert_opts = {
+      "country"      :opts.country,
+      "state"        :opts.state,
+      "locality"     :opts.locality,
+      "organization" :opts.organization,
+      "cn"           :opts.cn,
+    }
+
+    if p_callback: p_callback.start(text=title+"\n"+_("Creating a private key for your CA...")+"\n", size=100)
+    if p_callback: p_callback.update(10)
+    generate_privkey(PKI_CA_PRIVATE_KEY)
+    if p_callback: p_callback.update(20)
+    generate_ca_info(PKI_CA_INFO,cert_opts)
+    if p_callback: p_callback.update(30)
+    if os.path.exists(PKI_CA_PRIVATE_KEY) and os.path.exists(PKI_CA_INFO):
+        generate_ca_cert(PKI_CA_SELFSIGNED_CRT,PKI_CA_PRIVATE_KEY,PKI_CA_INFO)
+
+
+    if p_callback: p_callback.start(text=title+"\n"+_("Issuing server certificates...")+"\n", size=100)
+    if p_callback: p_callback.update(40)
+    generate_privkey(PKI_SERVER_PRIVATE_KEY)
+    if p_callback: p_callback.update(50)
+    generate_server_info(PKI_SERVER_INFO,cert_opts)
+    if p_callback: p_callback.update(60)
+    if os.path.exists(PKI_CA_SELFSIGNED_CRT) and os.path.exists(PKI_SERVER_PRIVATE_KEY) and os.path.exists(PKI_SERVER_INFO):
+        generate_cert(PKI_SERVER_CRT,PKI_SERVER_PRIVATE_KEY,PKI_SERVER_INFO,PKI_CA_SELFSIGNED_CRT,PKI_CA_PRIVATE_KEY)
+
+
+    if p_callback: p_callback.start(text=title+"\n"+_("Issuing client certificates...")+"\n", size=100)
+    if p_callback: p_callback.update(70)
+    generate_privkey(PKI_CLIENT_PRIVATE_KEY)
+    if p_callback: p_callback.update(80)
+    generate_client_info(PKI_CLIENT_INFO,cert_opts)
+    if p_callback: p_callback.update(90)
+    if os.path.exists(PKI_CA_SELFSIGNED_CRT) and os.path.exists(PKI_CLIENT_PRIVATE_KEY) and os.path.exists(PKI_CLIENT_INFO):
+        generate_cert(PKI_CLIENT_CRT,PKI_CLIENT_PRIVATE_KEY,PKI_CLIENT_INFO,PKI_CA_SELFSIGNED_CRT,PKI_CA_PRIVATE_KEY)
+
+    if p_callback: p_callback.end(100)
+
+    time.sleep(1)
+    write_log(_("Leaving '%s'") % sys._getframe(0).f_code.co_name)
+    return True
+
 def process_create_admin(opts,p_callback=None):
     write_log(_("Entering '%s'") % sys._getframe(0).f_code.co_name)
 
@@ -345,7 +396,8 @@ def process_write_config(opts,p_callback=None):
     if p_callback: p_callback.update(cnt)
     cnt = cnt + 1
 
-    opts.dbinit = 0
+    opts.certinit = 0
+    opts.dbinit   = 0
     sh_config_write(opts.ini,opts)
 
     if p_callback: p_callback.update(cnt)
@@ -454,7 +506,11 @@ def process_start_service(opts,p_callback=None):
 
 def run(opts, **kwargs):
     p_callback = kwargs.get('progress_callback', None)
-    dbinit = opts.dbinit
+    certinit = opts.certinit
+    dbinit   = opts.dbinit
+
+    if certinit is True:
+        process_init_certs(opts,p_callback)
 
     if opts.action & FLAG_RPM_REBUILD:
         process_rebuild_package(opts,p_callback)
index b2731e9..7b04417 100644 (file)
@@ -37,6 +37,7 @@ class SnackUI(object):
         self.wins = [
                      WelcomeWin,
                      AdminSettingWin,
+                     TLSCertificatesSettingWin,
                      DatabaseSettingWin,
                      ConfirmWin,
                      FinishWin,
@@ -112,7 +113,7 @@ class ProgressWin:
         if size is None:
             size = 1
 
-        width = 55
+        width = 65
         if (len(text) > width):
             width = min(len(text), self.width)
 
@@ -287,6 +288,167 @@ class AdminSettingWin(BaseWin):
 
         return button
 
+class TLSCertificatesSettingWin(BaseWin):
+    def __init__(self, *kwargs):
+        BaseWin.__init__(self, *kwargs)
+        self.opts.fqdn = getattr(self.opts, 'fqdn', socket.gethostname())
+        self.opts.lang = getattr(self.opts, 'lang', os.environ["LANG"][0:5])
+
+        #import pdb; pdb.set_trace()
+        default_certinit = 0
+        infos = {}
+        if os.path.exists(PKI_CLIENT_INFO):
+            from installer.utils import sh_config_read
+            infos = sh_config_read(PKI_CLIENT_INFO," = ")
+        else:
+            default_certinit = 1
+
+        default_infos = {}
+        for opt_name in ["country","state","locality","organization","cn"]:
+            try:
+                default_infos[opt_name] = infos[opt_name]
+            except:
+                default_infos[opt_name] = ""
+                if opt_name == "country":
+                    default_infos[opt_name] = "JP"
+                if opt_name == "organization":
+                    default_infos[opt_name] = "Karesansui Project"
+
+        for opt_name in ["country","state","locality","organization","cn"]:
+            exec("self.opts.%s = getattr(self.opts, '%s', '%s')" % (opt_name, opt_name, default_infos[opt_name]))
+
+        self.opts.certinit = getattr(self.opts, 'certinit', default_certinit)
+
+    def __call__(self):
+        current_page = "[%d/%d]" % (self.ui.page+1, len(self.ui.wins))
+        width = 25
+
+        label_country = snack.Label(_("Country Name (ISO 2 letter code)"))
+        country = snack.Entry(3, text=self.opts.country, hidden=False)
+
+        label_state = snack.Label(_("State or Province Name (full name)"))
+        state = snack.Entry(width, text=self.opts.state, hidden=False)
+
+        label_locality = snack.Label(_("Locality Name (e.g. city)"))
+        locality = snack.Entry(width, text=self.opts.locality, hidden=False)
+
+        label_organization = snack.Label(_("Organization Name (eg, company)"))
+        organization = snack.Entry(width, text=self.opts.organization, hidden=False)
+
+        label_cn = snack.Label(_("Common Name (fully qualified domain name)"))
+        cn = snack.Entry(width, text=self.opts.fqdn, hidden=False)
+
+        cb = snack.Checkbox(_("Initialize SSL certificates settings?"), self.opts.certinit)
+
+        sub_g = snack.Grid(2, 5)
+        sub_g.setField(label_country,  0, 0, anchorLeft=1)
+        sub_g.setField(country,        1, 0, anchorLeft=1, padding=(1, 0, 0, 0))
+        sub_g.setField(label_state,    0, 1, anchorLeft=1, padding=(0, 0, 0, 0))
+        sub_g.setField(state,          1, 1, anchorLeft=1, padding=(1, 0, 0, 0))
+        sub_g.setField(label_locality, 0, 2, anchorLeft=1, padding=(0, 0, 0, 0))
+        sub_g.setField(locality,       1, 2, anchorLeft=1, padding=(1, 0, 0, 0))
+        sub_g.setField(label_organization, 0, 3, anchorLeft=1, padding=(0, 0, 0, 0))
+        sub_g.setField(organization,   1, 3, anchorLeft=1, padding=(1, 0, 0, 0))
+        sub_g.setField(label_cn,       0, 4, anchorLeft=1, padding=(0, 1, 0, 0))
+        sub_g.setField(cn,             1, 4, anchorLeft=1, padding=(1, 1, 0, 0))
+
+        buttons = snack.ButtonBar(self.screen, [OK_BUTTON, BACK_BUTTON, CANCEL_BUTTON], compact=BUTTON_COMPACT)
+
+        g = snack.GridFormHelp(self.screen, _("TLS certificates setting") + current_page, None, 1, 3)
+        g.add(sub_g,      0, 0, growx=1)
+        g.add(cb,         0, 1, growx=1, padding = (0, 1, 0, 0))
+        g.add(buttons,    0, 2, growx=1, padding = (0, 1, 0, 0))
+
+        button = buttons.buttonPressed(g.runOnce())
+
+        if button is None:
+            button = OK_VALUE
+
+        if button is OK_VALUE:
+
+            """ Check country code """
+            input_country = country.value()
+            self.opts.country = input_country
+
+            if input_country == '':
+                ret = snack.ButtonChoiceWindow(self.screen, _("ERROR"), _("%s is empty") % _('Country Name (ISO 2 letter code)'), [OK_BUTTON], self.ui.width)
+                return -1
+
+            regex = "^[A-Z]{2}$"
+            m = re.compile(regex).search(input_country)
+            if not m:
+                ret = snack.ButtonChoiceWindow(self.screen, _("ERROR"), _("%s is in an invalid format.") % _('Country Name (ISO 2 letter code)'), [OK_BUTTON], self.ui.width)
+                return -1
+
+            """ Check state name """
+            input_state = state.value()
+            self.opts.state = input_state
+
+            if input_state == '':
+                ret = snack.ButtonChoiceWindow(self.screen, _("ERROR"), _("%s is empty") % _('State or Province Name (full name)'), [OK_BUTTON], self.ui.width)
+                return -1
+
+            regex = "^[-a-zA-Z0-9 ]{1,128}$"
+            m = re.compile(regex).search(input_state)
+            if not m:
+                ret = snack.ButtonChoiceWindow(self.screen, _("ERROR"), _("%s is in an invalid format.") % _('State or Province Name (full name)'), [OK_BUTTON], self.ui.width)
+                return -1
+
+            """ Check locality name """
+            input_locality = locality.value()
+            self.opts.locality = input_locality
+
+            if input_locality == '':
+                ret = snack.ButtonChoiceWindow(self.screen, _("ERROR"), _("%s is empty") % _('Locality Name (e.g. city)'), [OK_BUTTON], self.ui.width)
+                return -1
+
+            regex = "^[-a-zA-Z0-9 ]{1,128}$"
+            m = re.compile(regex).search(input_locality)
+            if not m:
+                ret = snack.ButtonChoiceWindow(self.screen, _("ERROR"), _("%s is in an invalid format.") % _('Locality Name (e.g. city)'), [OK_BUTTON], self.ui.width)
+                return -1
+
+            """ Check organization name """
+            input_organization = organization.value()
+            self.opts.organization = input_organization
+
+            if input_organization == '':
+                ret = snack.ButtonChoiceWindow(self.screen, _("ERROR"), _("%s is empty") % _('Organization Name (eg, company)'), [OK_BUTTON], self.ui.width)
+                return -1
+
+            regex = "^[-a-zA-Z0-9 .,\(\)]{1,64}$"
+            m = re.compile(regex).search(input_locality)
+            if not m:
+                ret = snack.ButtonChoiceWindow(self.screen, _("ERROR"), _("%s is in an invalid format.") % _('Organization Name (eg, company)'), [OK_BUTTON], self.ui.width)
+                return -1
+
+            """ Check common name """
+            input_cn = cn.value()
+            self.opts.cn = input_cn
+
+            if input_cn == '':
+                ret = snack.ButtonChoiceWindow(self.screen, _("ERROR"), _("%s is empty") % _('Common Name (fully qualified domain name)'), [OK_BUTTON], self.ui.width)
+                return -1
+
+            input_cn_error_msg = ""
+            if input_cn != "localhost" and not re.compile(r'.*\.').match(input_cn):
+                input_cn_error_msg = _('%s must include at least one dot . character.') % (_('Common Name (fully qualified domain name)'),)
+
+            if re.compile(r'(^\.|.*\.$|.*\.\.|.*-\.|.*\.-|^-|.*-$)', re.VERBOSE).match(input_cn):
+                input_cn_error_msg = _('%s must be specified like %s.') % (_('Common Name (fully qualified domain name)'), "host.example.com",)
+
+            invalid_input_cn_regex = "[^-a-zA-Z0-9\.]"
+            m = re.compile(invalid_input_cn_regex).search(input_cn)
+            if m:
+                input_cn_error_msg = _('%s\nAvailable characters are %s') % (_('Common Name (fully qualified domain name)'),_('a-z A-Z 0-9 . -'))
+            if input_cn_error_msg != "":
+                ret = snack.ButtonChoiceWindow(self.screen, _("ERROR"), input_cn_error_msg, [OK_BUTTON], self.ui.width)
+                return -1
+
+            self.opts.certinit = cb.selected()
+
+        return button
+
 class DatabaseSettingWin(BaseWin):
     def __init__(self, *kwargs):
         BaseWin.__init__(self, *kwargs)
index 0c99223..b27cf90 100644 (file)
@@ -302,7 +302,7 @@ def r_chmod(path,perm):
 
     return True
 
-def sh_config_read(filename):
+def sh_config_read(filename,separator='='):
     ret = {}
     try:
         fp = open(filename,"r")
@@ -311,8 +311,18 @@ def sh_config_read(filename):
             line = line.strip()
             if len(line) <= 0 or line[0] == "#":
                 continue
-            key, value = line.split('=',1)
-            ret[key] = value
+            try:
+                key, value = line.split(separator,1)
+                try:
+                    ret[key]
+                    if type(ret[key]) is not list:
+                        old = ret[key]
+                        ret[key] = [old]
+                    ret[key].append(value)
+                except:
+                    ret[key] = value
+            except:
+                pass
         fcntl.lockf(fp.fileno(), fcntl.LOCK_UN)
         fp.close()
     except:
@@ -320,7 +330,7 @@ def sh_config_read(filename):
 
     return ret
 
-def sh_config_write(filename,opts):
+def sh_config_write(filename,opts,separator='='):
     ret = True
 
     res = {}
@@ -335,10 +345,16 @@ def sh_config_write(filename,opts):
         fcntl.lockf(fp.fileno(), fcntl.LOCK_EX)
         for k,v in res.iteritems():
             if type(v) == str and k[0:2] != "__" and k[0:4] != "pass":
-                fp.write("%s=%s\n" % (k, v,))
+                fp.write("%s%s%s\n" % (k, separator, v,))
+            if type(v) == list:
+                for v2 in v:
+                    fp.write("%s%s%s\n" % (k, separator, v2,))
         fcntl.lockf(fp.fileno(), fcntl.LOCK_UN)
         fp.close()
-    except:
+    except TypeError, e:
+        pass
+    except IOError, e:
+        raise "%s" % e
         ret = False
 
     return ret
@@ -690,3 +706,173 @@ def file_contents_replace(filename, new_filename, pattern=None, replace=None, mo
 
     return True
 
+def append_random_entropy(count=3000):
+    try:
+        import installer.extensions.random_entropy
+        return installer.extensions.random_entropy.appendEntropy(count)
+    except:
+        import extensions.random_entropy
+        return extensions.random_entropy.appendEntropy(count)
+
+import threading
+class Append_Random_Entropy_Child_t(threading.Thread):
+    def __init__(self):
+        threading.Thread.__init__(self)
+    def run(self):
+        import time
+        time.sleep(3)
+        append_random_entropy(3000)
+
+def execute_certtool(opts,outfile="/dev/stdout"):
+    retval = True
+
+    outdir = os.path.dirname(outfile)
+    if os.path.exists(outdir) is False:
+        os.makedirs(outdir)
+
+    child_t = Append_Random_Entropy_Child_t()
+    child_t.start()
+
+    command_args = opts
+    command_args.insert(0,COMMAND_CERTTOOL)
+    (ret,res) = execute_command(command_args)
+
+    child_t.join()
+
+    if ret == 0:
+        try:
+            fp = open(outfile,"w")
+            fcntl.lockf(fp.fileno(), fcntl.LOCK_EX)
+            for line in res:
+                fp.write(line+"\n")
+            fcntl.lockf(fp.fileno(), fcntl.LOCK_UN)
+            fp.close()
+        except:
+            retval = False
+    else:
+        retval = False
+
+    return retval
+
+def generate_privkey(outfile="/dev/stdout"):
+    opts = [ "--generate-privkey" ]
+    execute_certtool(opts,outfile)
+    return os.path.exists(outfile)
+
+def generate_ca_info(outfile,opts):
+    retval = True
+
+    outdir = os.path.dirname(outfile)
+    if os.path.exists(outdir) is False:
+        os.makedirs(outdir)
+
+    try:
+        fp = open(outfile,"w")
+        fcntl.lockf(fp.fileno(), fcntl.LOCK_EX)
+        fp.write("""
+cn = %s
+ca
+cert_signing_key
+""" % (opts["cn"],));
+        fcntl.lockf(fp.fileno(), fcntl.LOCK_UN)
+        fp.close()
+    except:
+        retval = False
+
+    return retval
+
+def generate_ca_cert(outfile,privkey,info):
+    opts = [ "--generate-self-signed",
+             "--load-privkey",
+             privkey,
+             "--template",
+             info,
+           ]
+    execute_certtool(opts,outfile)
+    return os.path.exists(outfile)
+
+def generate_server_info(outfile,opts):
+    retval = True
+
+    outdir = os.path.dirname(outfile)
+    if os.path.exists(outdir) is False:
+        os.makedirs(outdir)
+
+    try:
+        fp = open(outfile,"w")
+        fcntl.lockf(fp.fileno(), fcntl.LOCK_EX)
+        fp.write("""
+organization = %s
+cn = %s
+tls_www_server
+encryption_key
+signing_key
+""" % (opts["organization"], opts["cn"],));
+        fcntl.lockf(fp.fileno(), fcntl.LOCK_UN)
+        fp.close()
+    except:
+        retval = False
+
+    return retval
+
+def generate_cert(outfile,privkey,info,ca_cert,ca_privkey):
+    opts = [ "--generate-certificate",
+             "--load-privkey",
+             privkey,
+             "--load-ca-certificate",
+             ca_cert,
+             "--load-ca-privkey",
+             ca_privkey,
+             "--template",
+             info,
+           ]
+    execute_certtool(opts,outfile)
+    return os.path.exists(outfile)
+
+def generate_client_info(outfile,opts):
+    retval = True
+
+    outdir = os.path.dirname(outfile)
+    if os.path.exists(outdir) is False:
+        os.makedirs(outdir)
+
+    try:
+        fp = open(outfile,"w")
+        fcntl.lockf(fp.fileno(), fcntl.LOCK_EX)
+        fp.write("""
+country = %s
+state = %s
+locality = %s
+organization = %s
+cn = %s
+tls_www_client
+encryption_key
+signing_key
+""" % (opts["country"],opts["state"],opts["locality"],opts["organization"],opts["cn"],));
+        fcntl.lockf(fp.fileno(), fcntl.LOCK_UN)
+        fp.close()
+    except:
+        retval = False
+
+    return retval
+
+def generate_ssl_certificates(opts):
+
+    # Setting up a Certificate Authority (CA)
+    generate_privkey(PKI_CA_PRIVATE_KEY)
+    generate_ca_info(PKI_CA_INFO,opts)
+    if os.path.exists(PKI_CA_PRIVATE_KEY) and os.path.exists(PKI_CA_INFO):
+        generate_ca_cert(PKI_CA_SELFSIGNED_CRT,PKI_CA_PRIVATE_KEY,PKI_CA_INFO)
+
+    # Issuing server certificates
+    generate_privkey(PKI_SERVER_PRIVATE_KEY)
+    generate_server_info(PKI_SERVER_INFO,opts)
+    if os.path.exists(PKI_CA_SELFSIGNED_CRT) and os.path.exists(PKI_SERVER_PRIVATE_KEY) and os.path.exists(PKI_SERVER_INFO):
+        generate_cert(PKI_SERVER_CRT,PKI_SERVER_PRIVATE_KEY,PKI_SERVER_INFO,PKI_CA_SELFSIGNED_CRT,PKI_CA_PRIVATE_KEY)
+
+    # Issuing client certificates
+    generate_privkey(PKI_CLIENT_PRIVATE_KEY)
+    generate_client_info(PKI_CLIENT_INFO,opts)
+    if os.path.exists(PKI_CA_SELFSIGNED_CRT) and os.path.exists(PKI_CLIENT_PRIVATE_KEY) and os.path.exists(PKI_CLIENT_INFO):
+        generate_cert(PKI_CLIENT_CRT,PKI_CLIENT_PRIVATE_KEY,PKI_CLIENT_INFO,PKI_CA_SELFSIGNED_CRT,PKI_CA_PRIVATE_KEY)
+