16273726 https support for svc:/application/pkg/depot
authorXiaobo Shen <xiaobo.shen@oracle.com>
Tue, 08 Apr 2014 09:09:52 -0700
changeset 3056 be6ea75e4be4
parent 3054 37c6fdddacac
child 3057 6fea2db6d754
16273726 https support for svc:/application/pkg/depot
src/depot-config.py
src/man/pkg.depot-config.1m
src/modules/smf.py
src/svc/pkg-depot.xml
src/svc/svc-pkg-depot
src/tests/cli/t_depot_config.py
src/tests/pkg5unittest.py
src/util/apache2/depot/depot_httpd.conf.mako
src/util/misc/user_attr.d/package:pkg
--- a/src/depot-config.py	Mon Apr 07 09:53:51 2014 -0700
+++ b/src/depot-config.py	Tue Apr 08 09:09:52 2014 -0700
@@ -21,7 +21,7 @@
 #
 
 #
-# Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 2013, 2014, Oracle and/or its affiliates. All rights reserved.
 #
 
 import errno
@@ -40,6 +40,7 @@
 
 from mako.template import Template
 from mako.lookup import TemplateLookup
+from OpenSSL.crypto import *
 
 import pkg
 import pkg.client.api_errors as apx
@@ -142,7 +143,12 @@
         pkg.depot-config ( -d repository_dir | -S ) -r runtime_dir
                 [-c cache_dir] [-s cache_size] [-p port] [-h hostname]
                 [-l logs_dir] [-T template_dir] [-A]
-                [-t server_type] ( [-F] [-P server_prefix] )
+                [-t server_type] ( ( [-F] [-P server_prefix] ) | [--https
+                ( ( --cert server_cert_file --key server_key_file
+                [--cert-chain ssl_cert_chain_file] ) |
+                --cert-key-dir cert_key_directory ) [ (--ca-cert ca_cert_file
+                --ca-key ca_key_file ) ]
+                [--smf-fmri smf_pkg_depot_fmri] ] )
 """))
         sys.exit(retcode)
 
@@ -184,7 +190,8 @@
 
 def _write_httpd_conf(pubs, default_pubs, runtime_dir, log_dir, template_dir,
         cache_dir, cache_size, host, port, sroot,
-        fragment=False, allow_refresh=False):
+        fragment=False, allow_refresh=False, ssl_cert_file="",
+        ssl_key_file="", ssl_cert_chain_file=""):
         """Writes the webserver configuration for the depot.
 
         pubs            repository and publisher information, a list in the form
@@ -219,6 +226,13 @@
 
         'repo_prefix' exists so that we can disambiguate between multiple
         repositories that provide the same publisher.
+
+        'ssl_cert_file' the location of the server certificate file.
+
+        'ssl_key_file' the location of the server key file.
+
+        'ssl_cert_chain_file' the location of the certificate chain file if the
+            the server certificate is not signed by the top level CA.
         """
 
         try:
@@ -298,7 +312,10 @@
                     host=host,
                     port=port,
                     sroot=sroot,
-                    allow_refresh=allow_refresh
+                    allow_refresh=allow_refresh,
+                    ssl_cert_file=ssl_cert_file,
+                    ssl_key_file=ssl_key_file,
+                    ssl_cert_chain_file=ssl_cert_chain_file
                 )
 
                 with file(conf_path, "wb") as conf_file:
@@ -373,6 +390,127 @@
                 raise DepotException(
                     _("Unable to write status response: %s") % err)
 
+def _createCertificateKey(serial, CN, starttime, endtime,
+    dump_cert_path, dump_key_path, issuerCert=None, issuerKey=None,
+    key_type=TYPE_RSA, key_bits=1024, digest="sha256"):
+        """Generate a certificate given a certificate request.
+
+        'serial' is the serial number for the certificate
+
+        'CN' is the subject common name of the certificate.
+
+        'starttime' is the timestamp when the certificate starts
+                          being valid. 0 means now.
+
+        'endtime' is the timestamp when the certificate stops being
+                        valid
+
+        'dump_cert_path' is the file the generated certificate gets dumped.
+
+        'dump_key_path' is the file the generated key gets dumped.
+
+        'issuerCert' is the certificate object of the issuer.
+
+        'issuerKey' is the key object of the issuer.
+
+        'key_type' is the key type. allowed value: TYPE_RSA and TYPE_DSA.
+
+        'key_bits' is number of bits to use in the key.
+
+        'digest' is the digestion method to use for signing.
+        """
+
+        key = PKey()
+        key.generate_key(key_type, key_bits)
+
+        cert = X509()
+        cert.set_serial_number(serial)
+        cert.gmtime_adj_notBefore(starttime)
+        cert.gmtime_adj_notAfter(endtime)
+
+        cert.get_subject().C = "US"
+        cert.get_subject().ST = "California"
+        cert.get_subject().L = "Santa Clara"
+        cert.get_subject().O = "pkg5"
+
+        cert.set_pubkey(key)
+        # If a issuer is specified, set the issuer. otherwise set cert
+        # itself as a issuer.
+        if issuerCert:
+                cert.get_subject().CN = CN
+                cert.set_issuer(issuerCert.get_subject())
+        else:
+                cert.get_subject().CN = "Depot Test CA"
+                cert.set_issuer(cert.get_subject())
+
+        # If there is a issuer key, sign with that key. Otherwise,
+        # create a self-signed cert.
+        if issuerKey:
+                cert.add_extensions([X509Extension("basicConstraints", True,
+                    "CA:FALSE")])
+                cert.sign(issuerKey, digest)
+        else:
+                cert.add_extensions([X509Extension("basicConstraints", True,
+                    "CA:TRUE")])
+                cert.sign(key, digest)
+        with open(dump_cert_path, "w") as f:
+                f.write(dump_certificate(FILETYPE_PEM, cert))
+        with open(dump_key_path, "w") as f:
+                f.write(dump_privatekey(FILETYPE_PEM, key))
+        return (cert, key)
+
+def _generate_server_cert_key(host, port, ca_cert_file="", ca_key_file="",
+    output_dir="/tmp"):
+        """ Generate certificate and key files for https service."""
+        if os.path.exists(output_dir):
+                if not os.path.isdir(output_dir):
+                        raise DepotException(
+                            _("%s is not a directory") % output_dir)
+        else:
+                misc.makedirs(output_dir)
+        server_id = "%s_%s" % (host, port)
+
+        cs_prefix = "server_%s" % server_id
+        server_cert_file = os.path.join(output_dir, "%s_cert.pem" % cs_prefix)
+        server_key_file = os.path.join(output_dir, "%s_key.pem" % cs_prefix)
+
+        # If the cert and key files do not exist, then generate one.
+        if not os.path.exists(server_cert_file) or not os.path.exists(
+            server_key_file):
+                # Used as a factor to easily specify a year.
+                year_factor = 60 * 60 * 24 * 365
+
+                # If user specifies ca_cert_file and ca_key_file, just load
+                # the files. Otherwise, generate new ca_cert and ca_key.
+                if not ca_cert_file or not ca_key_file:
+                        ca_cert_file = os.path.join(output_dir,
+                            "ca_%s_cert.pem" % server_id)
+                        ca_key_file = os.path.join(output_dir,
+                            "ca_%s_key.pem" % server_id)
+                        ca_cert, ca_key = _createCertificateKey(1, host,
+                            0, year_factor * 10, ca_cert_file, ca_key_file)
+                else:
+                        if not os.path.exists(ca_cert_file):
+                                raise DepotException(_("Cannot find user "
+                                    "provided CA certificate file: %s")
+                                    % ca_cert_file)
+                        if not os.path.exists(ca_key_file):
+                                raise DepotException(_("Cannot find user "
+                                    "provided CA key file: %s")
+                                    % ca_key_file)
+                        with open(ca_cert_file, "r") as fr:
+                                ca_cert = load_certificate(FILETYPE_PEM,
+                                    fr.read())
+                        with open(ca_key_file, "r") as fr:
+                                ca_key = load_privatekey(FILETYPE_PEM,
+                                    fr.read())
+
+                _createCertificateKey(2, host, 0, year_factor * 10,
+                    server_cert_file, server_key_file, issuerCert=ca_cert,
+                    issuerKey=ca_key)
+
+        return (ca_cert_file, ca_key_file, server_cert_file, server_key_file)
+
 def cleanup_htdocs(htdocs_dir):
         """Destroy any existing "htdocs" directory."""
         try:
@@ -384,7 +522,8 @@
 
 def refresh_conf(repo_info, log_dir, host, port, runtime_dir,
             template_dir, cache_dir, cache_size, sroot, fragment=False,
-            allow_refresh=False):
+            allow_refresh=False, ssl_cert_file="", ssl_key_file="",
+            ssl_cert_chain_file=""):
         """Creates a new configuration for the depot."""
         try:
                 ret = EXIT_OK
@@ -439,7 +578,9 @@
 
                 _write_httpd_conf(pubs, default_pubs, runtime_dir, log_dir,
                     template_dir, cache_dir, cache_size, host, port, sroot,
-                    fragment=fragment, allow_refresh=allow_refresh)
+                    fragment=fragment, allow_refresh=allow_refresh,
+                    ssl_cert_file=ssl_cert_file, ssl_key_file=ssl_key_file,
+                    ssl_cert_chain_file=ssl_cert_chain_file)
                 _write_versions_response(htdocs_path, fragment=fragment)
                 # If we're writing a configuration fragment, then the web server
                 # is probably not running as DEPOT_USER:DEPOT_GROUP
@@ -518,6 +659,19 @@
                 raise DepotException(_("%s is not a valid prefix"))
         return "%s/" % val
 
+def _update_smf_props(smf_fmri, prop_list, orig, dest):
+        """Update the smf props after the new prop values are generated."""
+
+        smf_instances = smf.check_fmris(None, smf_fmri)
+        for fmri in smf_instances:
+                refresh = False
+                for i in range(len(prop_list)):
+                        if orig[i] != dest[i]:
+                                smf.set_prop(fmri, prop_list[i], dest[i])
+                                refresh = True
+                if refresh:
+                        smf.refresh(fmri)
+
 def main_func():
 
         # some sensible defaults
@@ -532,6 +686,23 @@
         cache_size = 0
         # whether we're writing a full httpd.conf, or just a fragment
         fragment = False
+        # Whether we support https service.
+        https = False
+        # The location of server certificate file.
+        ssl_cert_file = ""
+        # The location of server key file.
+        ssl_key_file = ""
+        # The location of the server ca certificate file.
+        ssl_ca_cert_file = ""
+        # The location of the server ca key file.
+        ssl_ca_key_file = ""
+        # Directory for storing generated certificates and keys
+        cert_key_dir = ""
+        # SSL certificate chain file path if the server certificate is not
+        # signed by the top level CA.
+        ssl_cert_chain_file = ""
+        # The pkg/depot smf instance fmri.
+        smf_fmri = ""
         # an optional url-prefix, used to separate pkg5 services from the rest
         # of the webserver url namespace, only used when running in fragment
         # mode, otherwise we assume we're the only service running on this
@@ -552,10 +723,11 @@
         server_type = "apache2"
 
         writable_root_set = False
-
         try:
                 opts, pargs = getopt.getopt(sys.argv[1:],
-                    "Ac:d:Fh:l:P:p:r:Ss:t:T:?", ["help", "debug="])
+                    "Ac:d:Fh:l:P:p:r:Ss:t:T:?", ["help", "debug=", "https",
+                    "cert=", "key=", "ca-cert=", "ca-key=", "cert-chain=",
+                    "cert-key-dir=", "smf-fmri="])
                 for opt, arg in opts:
                         if opt == "--help":
                                 usage()
@@ -597,6 +769,22 @@
                                 use_smf_instances = True
                         elif opt == "-A":
                                 allow_refresh = True
+                        elif opt == "--https":
+                                https = True
+                        elif opt == "--cert":
+                                ssl_cert_file = arg
+                        elif opt == "--key":
+                                ssl_key_file = arg
+                        elif opt == "--ca-cert":
+                                ssl_ca_cert_file = arg
+                        elif opt == "--ca-key":
+                                ssl_ca_key_file = arg
+                        elif opt == "--cert-chain":
+                                ssl_cert_chain_file = arg
+                        elif opt == "--cert-key-dir":
+                                cert_key_dir = arg
+                        elif opt == "--smf-fmri":
+                                smf_fmri = arg
                         elif opt == "--debug":
                                 try:
                                         key, value = arg.split("=", 1)
@@ -629,6 +817,86 @@
         if repo_info and use_smf_instances:
                 usage(_("cannot use -d and -S together."))
 
+        if https:
+                if fragment:
+                        usage(_("https configuration is not supported in "
+                            "fragment mode."))
+                if bool(ssl_cert_file) != bool(ssl_key_file):
+                        usage(_("certificate and key files must be presented "
+                            "at the same time."))
+                elif not ssl_cert_file and not ssl_key_file:
+                        if not cert_key_dir:
+                                usage(_("cert-key-dir option is require to "
+                                    "store the generated certificates and keys"))
+                        if ssl_cert_chain_file:
+                                usage(_("Cannot use --cert-chain without "
+                                    "--cert and --key"))
+                        if bool(ssl_ca_cert_file) != bool(ssl_ca_key_file):
+                                usage(_("server CA certificate and key files "
+                                    "must be presented at the same time."))
+                        # If fmri is specifed for pkg/depot instance, we need
+                        # record the proporty values for updating.
+                        if smf_fmri:
+                                orig = (ssl_ca_cert_file, ssl_ca_key_file,
+                                    ssl_cert_file, ssl_key_file)
+                        try:
+                                ssl_ca_cert_file, ssl_ca_key_file, ssl_cert_file, \
+                                    ssl_key_file = \
+                                    _generate_server_cert_key(host, port,
+                                    ca_cert_file=ssl_ca_cert_file,
+                                    ca_key_file=ssl_ca_key_file,
+                                    output_dir=cert_key_dir)
+                                if ssl_ca_cert_file:
+                                        msg(_("Server CA certificate is "
+                                            "located at %s. Please deploy it "
+                                            "into /etc/certs/CA directory of "
+                                            "each client.")
+                                            % ssl_ca_cert_file)
+                        except (DepotException, EnvironmentError), e:
+                                    error(e)
+                                    return EXIT_OOPS
+
+                        # Update the pkg/depot instance smf properties if
+                        # anything changes.
+                        if smf_fmri:
+                                dest = (ssl_ca_cert_file, ssl_ca_key_file,
+                                    ssl_cert_file, ssl_key_file)
+                                if orig != dest:
+                                        prop_list = ["config/ssl_ca_cert_file",
+                                            "config/ssl_ca_key_file",
+                                            "config/ssl_cert_file",
+                                            "config/ssl_key_file"]
+                                        try:
+                                                _update_smf_props(smf_fmri, prop_list,
+                                                    orig, dest)
+                                        except (smf.NonzeroExitException,
+                                            RuntimeError), e:
+                                                error(e)
+                                                return EXIT_OOPS
+                else:
+                        if not os.path.exists(ssl_cert_file):
+                                error(_("User provided server certificate "
+                                    "file %s does not exist.") % ssl_cert_file)
+                                return EXIT_OOPS
+                        if not os.path.exists(ssl_key_file):
+                                error(_("User provided server key file %s "
+                                    "does not exist.") % ssl_key_file)
+                                return EXIT_OOPS
+                        if ssl_cert_chain_file and not os.path.exists(
+                            ssl_cert_chain_file):
+                                error(_("User provided certificate chain file "
+                                    "%s does not exist.") %
+                                    ssl_cert_chain_file)
+                                return EXIT_OOPS
+        else:
+                if ssl_cert_file or ssl_key_file or ssl_ca_cert_file \
+                    or ssl_ca_key_file or ssl_cert_chain_file:
+                        usage(_("certificate or key files are given before "
+                            "https service is turned on. Use --https to turn "
+                            "on the service."))
+                if smf_fmri:
+                        usage(_("cannot use --smf-fmri without --https."))
+
         # We can't support httpd.conf fragments with writable root, because
         # we don't have the mod_wsgi app that can build the index or serve
         # search requests everywhere the fragments might be used. (eg. on
@@ -666,7 +934,8 @@
 
         ret = refresh_conf(repo_info, log_dir, host, port, runtime_dir,
             template_dir, cache_dir, cache_size, sroot, fragment=fragment,
-            allow_refresh=allow_refresh)
+            allow_refresh=allow_refresh, ssl_cert_file=ssl_cert_file,
+            ssl_key_file=ssl_key_file, ssl_cert_chain_file=ssl_cert_chain_file)
         return ret
 
 #
--- a/src/man/pkg.depot-config.1m	Mon Apr 07 09:53:51 2014 -0700
+++ b/src/man/pkg.depot-config.1m	Tue Apr 08 09:09:52 2014 -0700
@@ -1,6 +1,6 @@
 '\" te
-.\" Copyright (c) 2007, 2013, Oracle and/or its affiliates. All rights reserved.
-.TH pkg.depot-config 1M "02 Oct 2013" "SunOS 5.12" "System Administration Commands"
+.\" Copyright (c) 2007, 2014, Oracle and/or its affiliates. All rights reserved.
+.TH pkg.depot-config 1M "31 Mar 2014" "SunOS 5.12" "System Administration Commands"
 .SH NAME
 pkg.depot-config \- Image Packaging System HTTP depot configuration generator
 .SH SYNOPSIS
@@ -9,7 +9,12 @@
 /usr/lib/pkg.depot-config ( -d \fIrepository_dir\fR | -S )
     -r \fIruntime_dir\fR [-c \fIcache_dir\fR] [-s \fIcache_size\fR] [-p \fIport\fR]
     [-h \fIhostname\fR] [-l \fIlogs_dir\fR] [-T \fItemplate_dir\fR]
-    [-A] [-t \fIserver_type\fR] ( [-F] [-P \fIserver_prefix\fR] )
+    [-A] [-t \fIserver_type\fR] ( ([-F] [-P \fIserver_prefix\fR] ) | [--https
+    ( ( --cert \fIserver_cert_file\fR --key \fIserver_key_file\fR
+    [--cert-chain \fIssl_cert_chain_file\fR] ) |
+    --cert-key-dir \fIcert_key_directory\fR )
+    [ (--ca-cert \fIca_cert_file\fR --ca-key \fIca_key_file\fR ) ]
+    [--smf-fmri \fIsmf_pkg_depot_fmri\fR] ] )
 .fi
 
 .SH DESCRIPTION
@@ -214,6 +219,94 @@
 Specify the prefix used to map the depot into the web server namespace. The \fB-P\fR option is intended to be used with the \fB-F\fR option.
 .RE
 
+.sp
+.ne 2
+.mk
+.na
+\fB\fB--https\fR
+.ad
+.sp .6
+.RS 4n
+Enable the HTTPS service. This option cannot be used with the \fB-F\fR or \fB-P\fR options.
+.RE
+
+.sp
+.ne 2
+.mk
+.na
+\fB\fB--cert\fR \fIserver_cert_file\fR\fR
+.ad
+.sp .6
+.RS 4n
+Specify the location of the server certificate file. This option can only be used with the \fB--https\fR option. Either both the \fB--cert\fR and \fB--key\fR options or the \fB--cert-key-dir\fR option must be used with the \fB--https\fR option.
+.RE
+
+.sp
+.ne 2
+.mk
+.na
+\fB\fB--key\fR \fIserver_key_file\fR\fR
+.ad
+.sp .6
+.RS 4n
+Specify the location of the server key file. This option can only be used with the \fB--https\fR option. Either both the \fB--cert\fR and \fB--key\fR options or the \fB--cert-key-dir\fR option must be used with the \fB--https\fR option.
+.RE
+
+.sp
+.ne 2
+.mk
+.na
+\fB\fB--cert-key-dir\fR \fIcert_key_directory\fR\fR
+.ad
+.sp .6
+.RS 4n
+Specify the directory where the automatically generated certificates and keys should be stored if options \fB--cert\fR and \fB--key\fR are omitted. This option can only be used with the \fB--https\fR option. Either both the \fB--cert\fR and \fB--key\fR options or the \fB--cert-key-dir\fR option must be used with the \fB--https\fR option.
+.RE
+
+.sp
+.ne 2
+.mk
+.na
+\fB\fB--ca-cert\fR \fIssl_ca_cert_file\fR\fR
+.ad
+.sp .6
+.RS 4n
+Specify the location of the top CA certificate file. This option can only be used with the \fB--https\fR option and must be used with \fB--ca-key\fR option together. The option is only used for automatically generating the server certificate based on this CA certificate and the key specified by \fB--ca-key\fR option.
+.RE
+
+.sp
+.ne 2
+.mk
+.na
+\fB\fB--ca-key\fR \fIssl_ca_key_file\fR\fR
+.ad
+.sp .6
+.RS 4n
+Specify the location of the top CA key file. This option can only be used with the \fB--https\fR option and must be used with \fB--ca-cert\fR option together. The option is only used for automatically generating the server certificate based on this key and the CA certificate specified by \fB--ca-cert\fR option.
+.RE
+
+.sp
+.ne 2
+.mk
+.na
+\fB\fB--cert-chain\fR \fIssl_cert_chain_file\fR\fR
+.ad
+.sp .6
+.RS 4n
+This option can only be used with the \fB--https\fR option. This option is required if the server certificate is not signed by the top level CA directly but is signed by an intermediate authority.
+.RE
+
+.sp
+.ne 2
+.mk
+.na
+\fB\fB--smf-fmri\fR \fIsmf_pkg_depot_fmri\fR\fR
+.ad
+.sp .6
+.RS 4n
+Specify the FMRI of the pkg/depot service instance. This option is used to update the corresponding SMF properties of that instance if any certificates or keys are automatically generated for that instance. This option can only be used with the \fB--https\fR option.
+.RE
+
 .SH PROVIDING ADDITIONAL SERVER CONFIGURATION
 .sp
 .LP
--- a/src/modules/smf.py	Mon Apr 07 09:53:51 2014 -0700
+++ b/src/modules/smf.py	Tue Apr 08 09:09:52 2014 -0700
@@ -52,6 +52,7 @@
 
 svcprop_path = "/usr/bin/svcprop"
 svcadm_path  = "/usr/sbin/svcadm"
+svccfg_path = "/usr/sbin/svccfg"
 svcs_path = "/usr/bin/svcs"
 zlogin_path = "/usr/sbin/zlogin"
 
@@ -175,6 +176,10 @@
             for l in buf
         ])
 
+def set_prop(fmri, prop, value, zone=None):
+        args = (svccfg_path, "-s", fmri, "setprop", "%s=%s" % (prop, value))
+        __call(args, zone=zone)
+
 def get_prop(fmri, prop, zone=None):
         args = (svcprop_path, "-c", "-p", prop, fmri)
         buf = __call(args, zone=zone)
--- a/src/svc/pkg-depot.xml	Mon Apr 07 09:53:51 2014 -0700
+++ b/src/svc/pkg-depot.xml	Tue Apr 08 09:09:52 2014 -0700
@@ -19,7 +19,7 @@
 
 	CDDL HEADER END
 
-	Copyright (c) 2013, Oracle and/or its affiliates.  All rights reserved.
+	Copyright (c) 2013, 2014, Oracle and/or its affiliates.  All rights reserved.
 
 	NOTE:  This service manifest is not editable; its contents will
 	be overwritten by package or patch operations, including
@@ -152,8 +152,22 @@
                         <propval name='allow_refresh' type='boolean' value='false' />
 
                         <propval name='value_authorization' type='astring'
-                                value='solaris.smf.value.pkg-depot' />
+                                value='solaris.smf.value.pkg-depot-config' />
 
+                        <propval name='https' type='boolean'
+                                value='false' />
+                        <propval name='ssl_cert_file' type='astring'
+                                value='' />
+                        <propval name='ssl_key_file' type='astring'
+                                value='' />
+                        <propval name='ssl_ca_cert_file' type='astring'
+                                value='' />
+                        <propval name='ssl_ca_key_file' type='astring'
+                                value='' />
+                        <propval name='ssl_cert_key_dir' type='astring'
+                                value='/var/cache/pkg/depot/cert_key_dir' />
+                        <propval name='ssl_cert_chain_file' type='astring'
+                                value='' />
 
                 </property_group>
 
--- a/src/svc/svc-pkg-depot	Mon Apr 07 09:53:51 2014 -0700
+++ b/src/svc/svc-pkg-depot	Tue Apr 08 09:09:52 2014 -0700
@@ -20,7 +20,7 @@
 # CDDL HEADER END
 #
 #
-# Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 2013, 2014, Oracle and/or its affiliates. All rights reserved.
 #
 
 . /lib/svc/share/smf_include.sh
@@ -111,20 +111,48 @@
 }
 
 function run_depot {
-
+        if [ "${depot_https}" == "true" ]; then
+                https_cmd="--https"
+                smf_fmri_cmd="--smf-fmri ${SMF_FMRI}"
+                if ! [ -z "${depot_ssl_cert_file}" ]; then
+                        ssl_cert_file_cmd="--cert ${depot_ssl_cert_file}"
+                fi
+                if ! [ -z "${depot_ssl_key_file}" ]; then
+                        ssl_key_file_cmd="--key ${depot_ssl_key_file}"
+                fi
+                if ! [ -z "${depot_ssl_ca_cert_file}" ]; then
+                        ssl_ca_cert_file_cmd="--ca-cert ${depot_ssl_ca_cert_file}"
+                fi
+                if ! [ -z "${depot_ssl_ca_key_file}" ]; then
+                        ssl_ca_key_file_cmd="--ca-key ${depot_ssl_ca_key_file}"
+                fi
+                if ! [ -z "${depot_ssl_cert_key_dir}" ]; then
+                        ssl_cert_key_dir_cmd="--cert-key-dir ${depot_ssl_cert_key_dir}"
+                fi
+                if ! [ -z "${depot_ssl_cert_chain_file}" ]; then
+                        ssl_cert_chain_file_cmd="--cert-chain ${depot_ssl_cert_chain_file}"
+                fi
+        fi
         /usr/lib/pkg.depot-config \
-               -S \
-               -c ${depot_cache_dir} \
-               -h ${depot_host} \
-               -l ${depot_log_dir} \
-               -p ${depot_port} \
-               -r ${depot_runtime_dir} \
-               -s ${depot_cache_max} \
-               -T ${depot_template_dir} \
-               -t apache2 \
-               ${depot_allow_refresh}
+            -S \
+            -c ${depot_cache_dir} \
+            -h ${depot_host} \
+            -l ${depot_log_dir} \
+            -p ${depot_port} \
+            -r ${depot_runtime_dir} \
+            -s ${depot_cache_max} \
+            -T ${depot_template_dir} \
+            -t apache2 ${https_cmd} \
+            ${smf_fmri_cmd} \
+            ${ssl_cert_file_cmd} \
+            ${ssl_key_file_cmd} \
+            ${ssl_ca_cert_file_cmd} \
+            ${ssl_ca_key_file_cmd} \
+            ${ssl_cert_key_dir_cmd} \
+            ${ssl_cert_chain_file_cmd} \
+            ${depot_allow_refresh}
 	failure=$?
-	if [ $? -ne 0 ] ; then
+	if [ ${failure} -ne 0 ] ; then
 		# make sure we leave nothing behind
 		kill_apache
 		kill_htcacheclean
@@ -207,10 +235,11 @@
 check_prop ${depot_host} config/host
 check_prop ${depot_port} config/port
 check_prop ${depot_log_dir} config/log_dir
-check_prop ${deport_template_dir} config/template_dir
+check_prop ${depot_template_dir} config/template_dir
 check_prop ${depot_runtime_dir} config/runtime_dir
 check_prop ${depot_cache_dir} config/cache_dir
 check_prop ${depot_cache_max} config/cache_max
+check_prop ${depot_https} config/https
 check_prop ${depot_allow_refresh} config/allow_refresh
 if [ "${depot_allow_refresh}" == "true" ] ; then
 	depot_allow_refresh="-A"
@@ -221,6 +250,7 @@
 FAILED_TO_RUN="Server failed to %s. Check the SMF service log or the\
  error log at ${depot_log_dir}/error_log for more information, if any."
 
+
 case "$1" in
 "start")
 	cmd="start"
@@ -251,8 +281,18 @@
 	cmd="stop"
 	kill_htcacheclean
         emsg=$(/usr/bin/printf ${FAILED_TO_RUN} stop)
-	${HTTPD} -f ${depot_runtime_dir}/depot_httpd.conf \
-            ${STARTUP_OPTIONS} -k ${cmd} 2>&1
+        # If https service is on and user blindly deleted the certificate dir,
+        # then the stop method will cause error due to not find certificate
+        # and key files. Instead of causing this error, we kill the apache
+        # instance manually.
+        if [[ "${depot_https}" == "true" && \
+            ! ( -f "${depot_ssl_cert_file}" && \
+            -f "${depot_ssl_key_file}" ) ]]; then
+                kill_apache
+        else
+	        ${HTTPD} -f ${depot_runtime_dir}/depot_httpd.conf \
+                    ${STARTUP_OPTIONS} -k ${cmd} 2>&1
+        fi
 	check_apache_failure $? $emsg
 	;;
 *)
--- a/src/tests/cli/t_depot_config.py	Mon Apr 07 09:53:51 2014 -0700
+++ b/src/tests/cli/t_depot_config.py	Tue Apr 08 09:09:52 2014 -0700
@@ -22,7 +22,7 @@
 #
 
 #
-# Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 2013, 2014, Oracle and/or its affiliates. All rights reserved.
 #
 
 import testutils
@@ -36,24 +36,16 @@
 import time
 import unittest
 import urllib2
+import certgenerator
+import shutil
 
+from pkg.client.debugvalues import DebugValues
 import pkg.fmri
 
 HTTPDEPOT_USER = "pkg5srv"
 
-class TestHttpDepot(pkg5unittest.ApacheDepotTestCase):
-        """Tests that exercise the pkg.depot-config CLI as well as checking the
-        functionality of the depot-config itself. This test class will
-        fail if not run as root, since many of the tests use 'pkg.depot-config -a'
-        which will attempt to chown a directory to pkg5srv:pkg5srv.
 
-        The default_svcs_conf having an instance name of 'usr' is not a
-        coincidence: we use it there so that we catch RewriteRules that
-        mistakenly try to serve content from the root filesystem ('/') rather
-        than from beneath our DocumentRoot (assuming that test systems always
-        have a /usr directory)
-        """
-
+class _Apache(object):
         # An array that can be used to build our svcs(1) wrapper.
         default_svcs_conf = [
             # FMRI                                   STATE
@@ -111,10 +103,159 @@
 
         misc_files = ["tmp/sample", "tmp/updated", "tmp/another", "tmp/new"]
 
+        _svcs_template = \
+"""#!/usr/bin/ksh93
+#
+# This script produces false svcs(1) output, using
+# a list of space separated strings, with each string
+# of the format <fmri>%%<state>
+#
+# Since the string here is generated from a Python program, we have to escape
+# all 'percent' characters.
+#
+# eg.
+# SERVICE_STATUS="svc:/application/pkg/server:foo%%online svc:/application/pkg/server:default%%offline svc:/application/pkg/server:usr%%online"
+# We expect to be called with 'svcs -H -o fmri <fmri>' but completely ignore
+# the <fmri> argument.
+#
+SERVICE_STATUS="%s"
+
+set -- `getopt o:H $*`
+for i in $* ; do
+    case $i in
+        -H)    minus_h=$i; shift;;
+        -o)    minus_o=$2; shift;;
+        *)     break;;
+    esac
+done
+
+if [ "${minus_o}" ]; then
+    if [ -z "${minus_h}" ]; then
+        echo "FMRI"
+    fi
+    for service in $SERVICE_STATUS ; do
+        echo $service | sed -e 's/%%/ /' | read fmri state
+        echo $fmri
+    done
+    exit 0
+fi
+
+if [ -z "${minus_h}" ]; then
+    printf "%%-14s%%6s    %%s\n" STATE STIME FMRI
+fi
+for service in $SERVICE_STATUS ; do
+    echo $service | sed -e 's/%%/ /' | read fmri state
+    printf "%%-14s%%9s %%s\n" $state 00:00:00 $fmri
+done
+"""
+
+        _svcprop_template = \
+"""#!/usr/bin/ksh93
+#
+# This script produces false svcprop(1) output, using
+# a list of space separated strings, with each string
+# of the format <fmri>%%<state>%%<inst_root>%%<readonly>%%<standalone>%%<writable_root>
+#
+# eg.
+# SERVICE_PROPS="svc:/application/pkg/server:foo%%online%%/space/repo%%true%%false%%/space/writable_root"
+#
+# we expect to be called as "svcprop -c -p <property> <fmri>"
+# which is enough svcprop(1) functionalty for these tests. Any other
+# command line options will cause us to return nonsense.
+#
+
+typeset -A prop_state
+typeset -A prop_readonly
+typeset -A prop_inst_root
+typeset -A prop_standalone
+typeset -A prop_writable_root
+
+SERVICE_PROPS="%s"
+for service in $SERVICE_PROPS ; do
+        echo $service | sed -e 's/%%/ /g' | \
+            read fmri state inst_root readonly standalone writable_root
+        # create a hashable version of the FMRI
+        fmri=$(echo $fmri | sed -e 's/\///g' -e 's/://g')
+        prop_state[$fmri]=$state
+        prop_inst_root[$fmri]=$inst_root
+        prop_readonly[$fmri]=$readonly
+        prop_standalone[$fmri]=$standalone
+        prop_writable_root[$fmri]=$writable_root
+done
+
+
+FMRI=$(echo $4 | sed -e 's/\///g' -e 's/://g')
+case $3 in
+        "pkg/inst_root")
+                echo ${prop_inst_root[$FMRI]}
+                ;;
+        "pkg/readonly")
+                echo ${prop_readonly[$FMRI]}
+                ;;
+        "pkg/standalone")
+                echo ${prop_standalone[$FMRI]}
+                ;;
+        "pkg/writable_root")
+                echo ${prop_writable_root[$FMRI]}
+                ;;
+        "restarter/state")
+                echo ${prop_state[$FMRI]}
+                ;;
+        *)
+                echo "Completely bogus svcprop output. Sorry."
+esac
+"""
+
+        # A very minimal httpd.conf, which contains an Include directive
+        # that we will use to reference our pkg5 depot-config.conf file. We leave
+        # an Alias pointing to /server-status to make this server distinctive
+        # for this test case.
+        _default_httpd_conf = \
+"""ServerRoot "/usr/apache2/2.2"
+PidFile "%(runtime_dir)s/default_httpd.pid"
+Listen %(port)s
+<IfDefine 64bit>
+Include /etc/apache2/2.2/conf.d/modules-64.load
+</IfDefine>
+<IfDefine !64bit>
+Include /etc/apache2/2.2/conf.d/modules-32.load
+</IfDefine>
+
+User webservd
+Group webservd
+ServerAdmin [email protected]
+ServerName 127.0.0.1
+DocumentRoot "/var/apache2/2.2/htdocs"
+<Directory "/var/apache2/2.2/htdocs">
+    Options Indexes FollowSymLinks
+    AllowOverride None
+    Order allow,deny
+    Allow from all
+</Directory>
+<IfModule dir_module>
+    DirectoryIndex index.html
+</IfModule>
+LogFormat \"%%h %%l %%u %%t \\\"%%r\\\" %%>s %%b\" common
+ErrorLog "%(runtime_dir)s/error_log"
+CustomLog "%(runtime_dir)s/access_log" common
+LogLevel debug
+DefaultType text/plain
+# Reference the depot.conf file generated by pkg.depot-config, which makes this
+# web server into something that can serve pkg(5) repositories.
+Include %(depot_conf)s
+SSLRandomSeed startup builtin
+SSLRandomSeed connect builtin
+# We enable server-status here, using /pkg5test-server-status to it to make the
+# URI distinctive.
+<Location /pkg5test-server-status>
+    SetHandler server-status
+</Location>
+"""
+
         def setUp(self):
                 self.sc = None
-                pkg5unittest.ApacheDepotTestCase.setUp(self, ["test1", "test2",
-                    "test3"])
+                pkg5unittest.ApacheDepotTestCase.setUp(self, ["test1",
+                    "test2", "test3"])
                 self.rdir1 = self.dcs[1].get_repodir()
                 self.rdir2 = self.dcs[2].get_repodir()
                 self.rdir3 = self.dcs[3].get_repodir()
@@ -131,9 +272,9 @@
                 self.depot_port = self.next_free_port
                 self.next_free_port += 1
                 self.make_misc_files(self.misc_files)
-                self.__set_smf_state()
+                self._set_smf_state()
 
-        def __set_smf_state(self, svcs_conf=default_svcs_conf,
+        def _set_smf_state(self, svcs_conf=default_svcs_conf,
             svcprop_conf=default_svcprop_conf):
                 """Create wrapper scripts for svcprop and svcs based on the
                 arrays of arrays passed in as arguments. By default, the
@@ -176,8 +317,8 @@
                     for item in _svcprop_conf])
 
                 self.smf_cmds = {
-                    "usr/bin/svcs": self.__svcs_template % _svcs_conf,
-                    "usr/bin/svcprop": self.__svcprop_template % _svcprop_conf
+                    "usr/bin/svcs": self._svcs_template % _svcs_conf,
+                    "usr/bin/svcprop": self._svcprop_template % _svcprop_conf
                 }
                 self.make_misc_files(self.smf_cmds, "smf_cmds", mode=0755)
 
@@ -192,6 +333,21 @@
                         u = urllib2.urlopen(
                             "%s/depot/depot-wait-refresh" % hc.url).close()
 
+
+class TestHttpDepot(_Apache, pkg5unittest.ApacheDepotTestCase):
+        """Tests that exercise the pkg.depot-config CLI as well as checking the
+        functionality of the depot-config itself for configuring http service.
+        This test class will fail if not run as root, since many of the tests
+        use 'pkg.depot-config -a' which will attempt to chown a directory to
+        pkg5srv:pkg5srv.
+
+        The default_svcs_conf having an instance name of 'usr' is not a
+        coincidence: we use it there so that we catch RewriteRules that
+        mistakenly try to serve content from the root filesystem ('/') rather
+        than from beneath our DocumentRoot (assuming that test systems always
+        have a /usr directory)
+        """
+
         def test_0_htdepot(self):
                 """A basic test to see that we can start the depot,
                 as part of this, by starting the depot, ApacheController will
@@ -244,7 +400,7 @@
                 # ensure we also catch invalid SMF inst_roots
                 svcs_conf = [["svc:/application/pkg/server:default", "online" ]]
                 svcprop_conf = [["/tmp", "true", "false"]]
-                self.__set_smf_state(svcs_conf, svcprop_conf)
+                self._set_smf_state(svcs_conf, svcprop_conf)
                 ret, output, err = self.depotconfig("", out=True, stderr=True,
                     exit=1)
                 self.assert_("/tmp" in err, "error message did not contain "
@@ -331,15 +487,6 @@
                         self.assert_(invalid_tmp in err, "error message "
                             "did not contain %s: %s" % (invalid_tmp, err))
 
-        def test_9_invalid_httemplates_dir(self):
-                """We return an error given an invalid templates_dir"""
-
-                for invalid_tmp in ["/dev/null", "/etc/passwd", "/proc"]:
-                        ret, output, err = self.depotconfig("-T %s" % invalid_tmp,
-                            out=True, stderr=True, exit=1)
-                        self.assert_(invalid_tmp in err, "error message "
-                            "did not contain %s: %s" % (invalid_tmp, err))
-
         def test_10_httype(self):
                 """We return an error given an invalid type option."""
 
@@ -725,7 +872,6 @@
                     "-P testpkg5" %
                     (self.default_depot_runtime, self.rdir1, self.rdir2,
                     self.index_dir), exit=2)
-
                 self.depotconfig("-l %s -F -d usr=%s -d spaghetti=%s "
                     "-P testpkg5" %
                     (self.default_depot_runtime, self.rdir1, self.rdir2))
@@ -733,7 +879,7 @@
                 default_httpd_conf_path = os.path.join(self.test_root,
                     "default_httpd.conf")
                 httpd_conf = open(default_httpd_conf_path, "w")
-                httpd_conf.write(self.__default_httpd_conf %
+                httpd_conf.write(self._default_httpd_conf %
                     {"port": self.depot_port,
                     "depot_conf": self.depot_conf_fragment,
                     "runtime_dir": self.default_depot_runtime})
@@ -768,154 +914,331 @@
                 self.pkgrepo("-s %s/testpkg5/usr refresh" %
                     self.ac.url, exit=1)
 
-        __svcs_template = \
-"""#!/usr/bin/ksh93
-#
-# This script produces false svcs(1) output, using
-# a list of space separated strings, with each string
-# of the format <fmri>%%<state>
-#
-# Since the string here is generated from a Python program, we have to escape
-# all 'percent' characters.
-#
-# eg.
-# SERVICE_STATUS="svc:/application/pkg/server:foo%%online svc:/application/pkg/server:default%%offline svc:/application/pkg/server:usr%%online"
-# We expect to be called with 'svcs -H -o fmri <fmri>' but completely ignore
-# the <fmri> argument.
-#
-SERVICE_STATUS="%s"
+
+class TestHttpsDepot(_Apache, pkg5unittest.HTTPSTestClass):
+        """Tests that exercise the pkg.depot-config CLI as well as checking the
+        functionality of the depot-config itself for configuring https service.
+        This test class will fail if not run as root, since many of the tests
+        use 'pkg.depot-config -a' which will attempt to chown a directory to
+        pkg5srv:pkg5srv.
+        """
+
+        def test_0_invalid_option_combo(self):
+                """We return an error given an invalid option combo."""
+
+                cert = os.path.join(self.test_root, "tmp",
+                    "ido_exist_cert")
+                key = os.path.join(self.test_root, "tmp",
+                    "ido_exist_key")
+                self.make_misc_files(["tmp/ido_exist_cert",
+                    "tmp/ido_exist_key"])
+
+                # Test that without --https, providing certs or keys will fail.
+                dummy_ret, dummy_output, err = self.depotconfig(
+                    "--cert %s --key %s" % (cert, key),
+                    out=True, stderr=True, exit=2)
+                self.assert_(len(err), "error message: Without --https, "
+                            "providing cert or key should fail but succeeded "
+                            "instead.")
+
+                dummy_ret, dummy_output, err = self.depotconfig(
+                    "--ca-cert %s --ca-key %s" % (cert, key),
+                    out=True, stderr=True, exit=2)
+                self.assert_(len(err), "error message: Without --https, "
+                            "providing cert or key should fail but succeeded "
+                            "instead.")
 
-set -- `getopt o:H $*`
-for i in $* ; do
-    case $i in
-        -H)    minus_h=$i; shift;;
-        -o)    minus_o=$2; shift;;
-        *)     break;;
-    esac
-done
+                dummy_ret, dummy_output, err = self.depotconfig(
+                    "--cert-chain %s" % cert, out=True, stderr=True, exit=2)
+                self.assert_(len(err), "error message: Without --https, "
+                            "providing cert or key should fail but succeeded "
+                            "instead.")
+
+                # Checking that HTTPS is not supported in fragment mode.
+                dummy_ret, dummy_output, err = self.depotconfig(
+                    "--https -F", out=True, stderr=True, exit=2)
+                self.assert_(len(err), "error message: Without --https, "
+                            "providing cert or key should fail but succeeded "
+                            "instead.")
+
+        def test_1_missing_combo_options(self):
+                """We return errors if the option in a combo is not specified
+                at the same time."""
+
+                cert = os.path.join(self.test_root, "tmp",
+                    "ido_exist_cert")
+                key = os.path.join(self.test_root, "tmp",
+                    "ido_exist_key")
+                self.make_misc_files(["tmp/ido_exist_cert",
+                    "tmp/ido_exist_key"])
+
+                self.depotconfig("--https --cert %s" % cert, exit=2)
+                self.depotconfig("--https --key %s" % key, exit=2)
+                self.depotconfig("--https --ca-cert %s" % cert, exit=2)
+                self.depotconfig("--https --ca-key %s" % key, exit=2)
+                self.depotconfig("--https --cert-chain %s" % cert, exit=2)
+
+        def test_2_invalid_cert_key_dir(self):
+                """We return an error given an invalid cer_key_dir."""
+
+                for invalid_certkey_dir in ["/dev/null", "/etc/passwd"]:
+                        ret, output, err = self.depotconfig("--https "
+                            "--cert-key-dir %s" %
+                            invalid_certkey_dir, out=True, stderr=True, exit=1)
+                        self.assert_(invalid_certkey_dir in err, "error message "
+                           "did not contain %s: %s" % (invalid_certkey_dir, err))
+
+        def test_3_non_exist_cert_key(self):
+                """We return an error given an non-exist cert or key."""
 
-if [ "${minus_o}" ]; then
-    if [ -z "${minus_h}" ]; then
-        echo "FMRI"
-    fi
-    for service in $SERVICE_STATUS ; do
-        echo $service | sed -e 's/%%/ /' | read fmri state
-        echo $fmri
-    done
-    exit 0
-fi
+                non_exist_cert = os.path.join(self.test_root,
+                    "idonot_exist_cert")
+                non_exist_key = os.path.join(self.test_root,
+                    "idonot_exist_key")
+                exist_cert = os.path.join(self.test_root, "tmp",
+                    "ido_exist_cert")
+                exist_key = os.path.join(self.test_root, "tmp",
+                    "ido_exist_key")
+                self.make_misc_files(["tmp/ido_exist_cert",
+                    "tmp/ido_exist_key"])
+
+                # Test checking user provided server cert works.
+                dummy_ret, dummy_output, err = self.depotconfig("--https "
+                    "--cert %s --key %s" % (non_exist_cert, exist_key),
+                    out=True, stderr=True, exit=1)
+                self.assert_(non_exist_cert in err, "error message "
+                    "did not contain %s: %s" % (non_exist_cert, err))
 
-if [ -z "${minus_h}" ]; then
-    printf "%%-14s%%6s    %%s\n" STATE STIME FMRI
-fi
-for service in $SERVICE_STATUS ; do
-    echo $service | sed -e 's/%%/ /' | read fmri state
-    printf "%%-14s%%9s %%s\n" $state 00:00:00 $fmri
-done
-"""
+                # Test checking user provided server key works.
+                dummy_ret, dummy_output, err = self.depotconfig("--https "
+                    "--cert %s --key %s" % (exist_cert, non_exist_key),
+                    out=True, stderr=True, exit=1)
+                self.assert_(non_exist_key in err, "error message "
+                    "did not contain %s: %s" % (non_exist_key, err))
+
+                # Test checking user provided cert chain file works.
+                dummy_ret, dummy_output, err = self.depotconfig("--https "
+                    "--cert %s --key %s --cert-chain %s" %
+                    (exist_cert, exist_key, non_exist_cert),
+                    out=True, stderr=True, exit=1)
+                self.assert_(non_exist_cert in err, "error message "
+                    "did not contain %s: %s" % (non_exist_cert, err))
+
+                # Test checking user provided CA cert file works.
+                tmp_dir = os.path.join(self.test_root, "tmp")
+                dummy_ret, dummy_output, err = self.depotconfig("--https "
+                    "--ca-cert %s --ca-key %s --cert-key-dir %s" %
+                    (non_exist_cert, exist_key, tmp_dir),
+                    out=True, stderr=True, exit=1)
+                self.assert_(non_exist_cert in err, "error message "
+                    "did not contain %s: %s" % (non_exist_cert, err))
 
-        __svcprop_template = \
-"""#!/usr/bin/ksh93
-#
-# This script produces false svcprop(1) output, using
-# a list of space separated strings, with each string
-# of the format <fmri>%%<state>%%<inst_root>%%<readonly>%%<standalone>%%<writable_root>
-#
-# eg.
-# SERVICE_PROPS="svc:/application/pkg/server:foo%%online%%/space/repo%%true%%false%%/space/writable_root"
-#
-# we expect to be called as "svcprop -c -p <property> <fmri>"
-# which is enough svcprop(1) functionalty for these tests. Any other
-# command line options will cause us to return nonsense.
-#
+                # Test checking user provided CA key file works.
+                dummy_ret, dummy_output, err = self.depotconfig("--https "
+                    "--ca-cert %s --ca-key %s --cert-key-dir %s" %
+                    (exist_cert, non_exist_key, tmp_dir),
+                    out=True, stderr=True, exit=1)
+                self.assert_(non_exist_key in err, "error message "
+                    "did not contain %s: %s" % (non_exist_key, err))
+
+        def test_4_invalid_smf_fmri(self):
+                """We return an error given an invalid pkg/depot smf fmri."""
 
-typeset -A prop_state
-typeset -A prop_readonly
-typeset -A prop_inst_root
-typeset -A prop_standalone
-typeset -A prop_writable_root
+                some_fake_file = os.path.join(self.test_root, "tmp",
+                    "some_fake_file")
+                self.make_misc_files(["tmp/some_fake_file"])
+                tmp_dir = os.path.join(self.test_root, "tmp")
+                # Test with invalid fmri.
+                for invalid_fmri in ["svc:", "svc://notexist", some_fake_file]:
+                        dummy_ret, dummy_output, err = self.depotconfig(
+                            "--https --cert-key-dir %s --smf-fmri %s" %
+                            (tmp_dir, invalid_fmri), out=True, stderr=True)
+                        self.assert_(len(err), "error message: SMF FMRI "
+                            "setting should fail but succeeded instead.")
+
+                # Test with wrong fmri.
+                wrong_fmri = "pkg/server:default"
+                dummy_ret, dummy_output, err = self.depotconfig(
+                    "--https --cert-key-dir %s --smf-fmri %s" %
+                    (tmp_dir, wrong_fmri), out=True, stderr=True, exit=1)
+                self.assert_(len(err), "error message: SMF FMRI "
+                    "setting should fail but succeeded instead.")
+
+        def test_5_https_gen_cert(self):
+                """Test that https functionality works as expected."""
+
+                self.pkgsend_bulk(self.dcs[1].get_repo_url(),
+                    self.sample_pkg)
+                self.pkgrepo("-s %s add-publisher carrots" %
+                    self.dcs[1].get_repo_url())
+                self.pkgsend_bulk(self.dcs[1].get_repo_url(),
+                    self.carrots_pkg)
+                self.pkgsend_bulk(self.dcs[2].get_repo_url(),
+                    self.new_pkg)
 
-SERVICE_PROPS="%s"
-for service in $SERVICE_PROPS ; do
-        echo $service | sed -e 's/%%/ /g' | \
-            read fmri state inst_root readonly standalone writable_root
-        # create a hashable version of the FMRI
-        fmri=$(echo $fmri | sed -e 's/\///g' -e 's/://g')
-        prop_state[$fmri]=$state
-        prop_inst_root[$fmri]=$inst_root
-        prop_readonly[$fmri]=$readonly
-        prop_standalone[$fmri]=$standalone
-        prop_writable_root[$fmri]=$writable_root
-done
+                cert_key_dir = os.path.join(self.default_depot_runtime,
+                    "cert_key")
+                if os.path.isdir(cert_key_dir):
+                        shutil.rmtree(cert_key_dir)
 
+                cache_dir = os.path.join(self.test_root, "cache_test_dir")
+                self.depotconfig("-l %s -r %s -c %s -d usr=%s -d spa=%s -p %s "
+                    "--https -T %s -h localhost --cert-key-dir %s" %
+                    (self.default_depot_runtime, self.default_depot_runtime,
+                    cache_dir, self.rdir1, self.rdir2, self.depot_port,
+                    self.depot_template_dir, cert_key_dir))
+                server_id = "localhost_%s" % self.depot_port
+                ca_cert_file = os.path.join(cert_key_dir, "ca_%s_cert.pem" %
+                    server_id)
+                DebugValues["ssl_ca_file"] = ca_cert_file
+
+                # Start an Apache instance
+                self.default_depot_conf = os.path.join(
+                    self.default_depot_runtime, "depot_httpd.conf")
+                ac = pkg5unittest.HttpDepotController(self.default_depot_conf,
+                    self.depot_port, self.default_depot_runtime, testcase=self,
+                    https=True)
+                self.register_apache_controller("depot", ac)
+                ac.start()
+                self.image_create()
 
-FMRI=$(echo $4 | sed -e 's/\///g' -e 's/://g')
-case $3 in
-        "pkg/inst_root")
-                echo ${prop_inst_root[$FMRI]}
-                ;;
-        "pkg/readonly")
-                echo ${prop_readonly[$FMRI]}
-                ;;
-        "pkg/standalone")
-                echo ${prop_standalone[$FMRI]}
-                ;;
-        "pkg/writable_root")
-                echo ${prop_writable_root[$FMRI]}
-                ;;
-        "restarter/state")
-                echo ${prop_state[$FMRI]}
-                ;;
-        *)
-                echo "Completely bogus svcprop output. Sorry."
-esac
-"""
+                # add publishers for the two repositories being served by this
+                # Apache instance.
+                self.pkg("set-publisher -p %s/usr" % self.ac.url)
+                self.pkg("set-publisher -p %s/spa" % self.ac.url)
+                # install packages from the two different publishers in the
+                # first repository
+                self.pkg("install sample")
+                self.pkg("install carrots")
+                # install a package from the second repository
+                self.pkg("install new")
+                # we can't perform remote search or admin operations, since
+                # we've no supporting mod_wsgi process.
+                self.pkg("search -r new", exit=1)
+                self.pkgrepo("-s %s/testpkg5/usr refresh" %
+                    self.ac.url, exit=1)
+
+        def test_6_https_cert_chain(self):
+                """Test that https functionality with cert chain works as
+                expected."""
+
+                self.pkgsend_bulk(self.dcs[1].get_repo_url(),
+                    self.sample_pkg)
+                self.pkgrepo("-s %s add-publisher carrots" %
+                    self.dcs[1].get_repo_url())
+                self.pkgsend_bulk(self.dcs[1].get_repo_url(),
+                    self.carrots_pkg)
+                self.pkgsend_bulk(self.dcs[2].get_repo_url(),
+                    self.new_pkg)
+
+                cert_key_dir = os.path.join(self.default_depot_runtime,
+                    "cert_key_dir")
+                if os.path.isdir(cert_key_dir):
+                        shutil.rmtree(cert_key_dir)
+                os.makedirs(cert_key_dir)
+                cg = certgenerator.CertGenerator(base_dir=cert_key_dir)
+                cg.make_trust_anchor("ta", https=True)
+                cg.make_ca_cert("ca_ta", "ta", https=True)
+                cg.make_cs_cert("cs_ta", "ca_ta", parent_loc="chain_certs",
+                    https=True)
+
+                ta_cert_file = os.path.join(cg.raw_trust_anchor_dir,
+                    "ta_cert.pem")
+                ca_cert_file = os.path.join(cg.chain_certs_dir,
+                    "ca_ta_cert.pem")
+                cs_cert_file = os.path.join(cg.cs_dir, "cs_ta_cert.pem")
+                cs_key_file = os.path.join(cg.keys_dir, "cs_ta_key.pem")
+
+                cache_dir = os.path.join(self.test_root, "cache_test_dir")
+                self.depotconfig("-l %s -r %s -c %s -d usr=%s -d spa=%s -p %s "
+                    "--https -T %s -h localhost --cert %s --key %s "
+                    "--cert-chain %s" %
+                    (self.default_depot_runtime, self.default_depot_runtime,
+                    cache_dir, self.rdir1, self.rdir2, self.depot_port,
+                    self.depot_template_dir, cs_cert_file, cs_key_file,
+                    ca_cert_file))
 
-        # A very minimal httpd.conf, which contains an Include directive
-        # that we will use to reference our pkg5 depot-config.conf file. We leave
-        # an Alias pointing to /server-status to make this server distinctive
-        # for this test case.
-        __default_httpd_conf = \
-"""ServerRoot "/usr/apache2/2.2"
-PidFile "%(runtime_dir)s/default_httpd.pid"
-Listen %(port)s
-<IfDefine 64bit>
-Include /etc/apache2/2.2/conf.d/modules-64.load
-</IfDefine>
-<IfDefine !64bit>
-Include /etc/apache2/2.2/conf.d/modules-32.load
-</IfDefine>
+                DebugValues["ssl_ca_file"] = ta_cert_file
+
+                # Start an Apache instance
+                self.default_depot_conf = os.path.join(
+                    self.default_depot_runtime, "depot_httpd.conf")
+                ac = pkg5unittest.HttpDepotController(self.default_depot_conf,
+                    self.depot_port, self.default_depot_runtime, testcase=self,
+                    https=True)
+                self.register_apache_controller("depot", ac)
+                ac.start()
+                self.image_create()
+
+                # add publishers for the two repositories being served by this
+                # Apache instance.
+                self.pkg("set-publisher -p %s/usr" % self.ac.url)
+                self.pkg("set-publisher -p %s/spa" % self.ac.url)
+                # install packages from the two different publishers in the
+                # first repository
+                self.pkg("install sample")
+                self.pkg("install carrots")
+                # install a package from the second repository
+                self.pkg("install new")
+
+        def test_7_https_provided_ca(self):
+                """Test that pkg.depot-config functionality with provided
+                ca certificate and key works as expected."""
+
+                self.pkgsend_bulk(self.dcs[1].get_repo_url(),
+                    self.sample_pkg)
+                self.pkgrepo("-s %s add-publisher carrots" %
+                    self.dcs[1].get_repo_url())
+                self.pkgsend_bulk(self.dcs[1].get_repo_url(),
+                    self.carrots_pkg)
+                self.pkgsend_bulk(self.dcs[2].get_repo_url(),
+                    self.new_pkg)
 
-User webservd
-Group webservd
-ServerAdmin [email protected]
-ServerName 127.0.0.1
-DocumentRoot "/var/apache2/2.2/htdocs"
-<Directory "/var/apache2/2.2/htdocs">
-    Options Indexes FollowSymLinks
-    AllowOverride None
-    Order allow,deny
-    Allow from all
-</Directory>
-<IfModule dir_module>
-    DirectoryIndex index.html
-</IfModule>
-LogFormat \"%%h %%l %%u %%t \\\"%%r\\\" %%>s %%b\" common
-ErrorLog "%(runtime_dir)s/error_log"
-CustomLog "%(runtime_dir)s/access_log" common
-LogLevel debug
-DefaultType text/plain
-# Reference the depot.conf file generated by pkg.depot-config, which makes this
-# web server into something that can serve pkg(5) repositories.
-Include %(depot_conf)s
-SSLRandomSeed startup builtin
-SSLRandomSeed connect builtin
-# We enable server-status here, using /pkg5test-server-status to it to make the
-# URI distinctive.
-<Location /pkg5test-server-status>
-    SetHandler server-status
-</Location>
-"""
+                cert_key_dir = os.path.join(self.default_depot_runtime,
+                    "cert_key_dir")
+                if os.path.isdir(cert_key_dir):
+                        shutil.rmtree(cert_key_dir)
+                os.makedirs(cert_key_dir)
+
+                cg = certgenerator.CertGenerator(base_dir=cert_key_dir)
+                cg.make_trust_anchor("ta", https=True)
+
+                ta_cert_file = os.path.join(cg.raw_trust_anchor_dir,
+                    "ta_cert.pem")
+                ta_key_file = os.path.join(cg.keys_dir, "ta_key.pem")
+
+                cache_dir = os.path.join(self.test_root, "cache_test_dir")
+                self.depotconfig("-l %s -r %s -c %s -d usr=%s -d spa=%s -p %s "
+                    "--https -T %s -h localhost --ca-cert %s --ca-key %s "
+                    "--cert-key-dir %s" %
+                    (self.default_depot_runtime, self.default_depot_runtime,
+                    cache_dir, self.rdir1, self.rdir2, self.depot_port,
+                    self.depot_template_dir, ta_cert_file, ta_key_file,
+                    cert_key_dir))
+
+                DebugValues["ssl_ca_file"] = ta_cert_file
+
+                # Start an Apache instance
+                self.default_depot_conf = os.path.join(
+                    self.default_depot_runtime, "depot_httpd.conf")
+                ac = pkg5unittest.HttpDepotController(self.default_depot_conf,
+                    self.depot_port, self.default_depot_runtime, testcase=self,
+                    https=True)
+                self.register_apache_controller("depot", ac)
+                ac.start()
+                self.image_create()
+
+                # add publishers for the two repositories being served by this
+                # Apache instance.
+                self.pkg("set-publisher -p %s/usr" % self.ac.url)
+                self.pkg("set-publisher -p %s/spa" % self.ac.url)
+                # install packages from the two different publishers in the
+                # first repository
+                self.pkg("install sample")
+                self.pkg("install carrots")
+                # install a package from the second repository
+                self.pkg("install new")
+
 
 if __name__ == "__main__":
         unittest.main()
--- a/src/tests/pkg5unittest.py	Mon Apr 07 09:53:51 2014 -0700
+++ b/src/tests/pkg5unittest.py	Tue Apr 08 09:09:52 2014 -0700
@@ -2839,9 +2839,9 @@
                 arguments to point to template, logs, cache and proto areas
                 within our test root."""
 
-                if "-S" not in args and "-d" not in args and fill_missing_args:
+                if "-S" not in args and "-d " not in args and fill_missing_args:
                         args += " -S "
-                if "-c" not in args and fill_missing_args:
+                if "-c " not in args and fill_missing_args:
                         args += " -c %s" % os.path.join(self.test_root,
                             "depot_cache")
                 if "-l" not in args:
@@ -3609,6 +3609,7 @@
                 return "ta%d" % ta
 
         def setUp(self, publishers, start_depots=True):
+
                 # We only have 5 usable CA certs and there are not many usecases
                 # for setting up more than 5 different SSL-secured depots.
                 assert len(publishers) < 6
@@ -4453,7 +4454,7 @@
 
         def __init__(self, conf, port, work_dir, testcase=None, https=False):
                 ApacheController.__init__(self, conf, port, work_dir,
-                    testcase=testcase, https=False)
+                    testcase=testcase, https=https)
                 self.apachectl = "/usr/apache2/2.2/bin/64/httpd.worker"
 
         def _network_ping(self):
@@ -4472,7 +4473,7 @@
 
         def __init__(self, conf, port, work_dir, testcase=None, https=False):
                 ApacheController.__init__(self, conf, port, work_dir,
-                    testcase=testcase, https=False)
+                    testcase=testcase, https=https)
                 self.apachectl = "/usr/apache2/2.2/bin/64/httpd.worker"
 
         def _network_ping(self):
--- a/src/util/apache2/depot/depot_httpd.conf.mako	Mon Apr 07 09:53:51 2014 -0700
+++ b/src/util/apache2/depot/depot_httpd.conf.mako	Tue Apr 08 09:09:52 2014 -0700
@@ -19,7 +19,7 @@
 #
 # CDDL HEADER END
 #
-# Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 2013, 2014, Oracle and/or its affiliates. All rights reserved.
 #
 
 #
@@ -335,6 +335,34 @@
 CacheMaxFileSize 45690876
 % endif
 
+<%
+        ssl_cert_file_path = context.get("ssl_cert_file", "")
+        ssl_key_file_path = context.get("ssl_key_file", "")
+        if ssl_cert_file_path and ssl_key_file_path:
+                context.write("""
+# DNS domain name of the server
+ServerName %s
+# enable SSL
+SSLEngine On
+# Location of the server certificate and key.
+""" % context.get("host", "localhost"))
+                context.write("SSLCertificateFile %s\n" % ssl_cert_file_path)
+                context.write("SSLCertificateKeyFile %s\n" % ssl_key_file_path)
+                context.write("""
+# Intermediate CA certificate file. Required if your server certificate
+# is not signed by a top-level CA directly but an intermediate authority.
+# Comment out this section if you don't need one or if you are using a
+# test certificate
+""")
+                ssl_cert_chain_file_path = context.get("ssl_cert_chain_file",
+                    "")
+                if ssl_cert_chain_file_path:
+                        context.write("SSLCertificateChainFile %s\n" %
+                            ssl_cert_chain_file_path)
+                else:
+                        context.write("# SSLCertificateChainFile /cert_path\n")
+%>
+
 # Rules to serve static content directly from the file-repositories.
 <%include file="/depot.conf.mako"/>
 # with no URL-path, we show an index of the available repositories.
--- a/src/util/misc/user_attr.d/package:pkg	Mon Apr 07 09:53:51 2014 -0700
+++ b/src/util/misc/user_attr.d/package:pkg	Tue Apr 08 09:09:52 2014 -0700
@@ -1,1 +1,1 @@
-pkg5srv::RO::auths=solaris.smf.manage.pkg-mirror,solaris.smf.value.pkg-mirror
+pkg5srv::RO::auths=solaris.smf.manage.pkg-mirror,solaris.smf.value.pkg-mirror,solaris.smf.value.pkg-depot-config,solaris.smf.manage.pkg-depot