8570 zone installer needs support for ssl certs & keys in111b os200906
authorDan Price <dp@eng.sun.com>
Thu, 07 May 2009 18:45:52 -0700
changeset 1111 26c3e2407c53
parent 1110 26613c457379
child 1112 711e22d74883
8570 zone installer needs support for ssl certs & keys
src/brand/attach
src/brand/common.ksh
src/brand/config.xml
src/brand/pkgcreatezone
src/client.py
src/modules/client/image.py
src/modules/client/imageconfig.py
src/tests/api/t_imageconfig.py
--- a/src/brand/attach	Thu May 07 16:50:17 2009 -0700
+++ b/src/brand/attach	Thu May 07 18:45:52 2009 -0700
@@ -58,6 +58,9 @@
 f_reset_pub=$(gettext "Failed to reset publisher to %s %s")
 f_no_pref_publisher=$(gettext "Unable to get preferred publisher information for zone '%s'.")
 f_pkg_list=$(gettext "Unable to get zone package list.")
+f_get_secinfo=$(gettext "Failed to get key/cert information for publisher %s")
+f_nosuch_key=$(gettext "Failed to find key %s for global zone publisher")
+f_nosuch_cert=$(gettext "Failed to find cert %s for global zone publisher")
 
 
 #set -o xtrace
@@ -68,7 +71,17 @@
 TEXTDOMAIN="SUNW_OST_OSCMD"
 export TEXTDOMAIN
 
-PKG="LC_ALL=C /usr/bin/pkg"
+PKG="/usr/bin/pkg"
+KEYDIR=/var/pkg/ssl
+
+#
+# Resetting GZ_IMAGE to something besides slash allows for simplified
+# debugging of various global zone image configurations-- simply make
+# an image somewhere with the appropriate interesting parameters.
+#
+GZ_IMAGE=${GZ_IMAGE:-/}
+PKG_IMAGE=$GZ_IMAGE
+export PKG_IMAGE
 
 aarg=0
 darg=0
@@ -412,6 +425,7 @@
 #
 trap "/usr/sbin/umount $zoneroot > /dev/null 2>&1" EXIT
 
+
 #
 # Look for the 'entire' incorporation's FMRI in the current image; due to users
 # doing weird machinations with their publishers, we strip off the publisher
@@ -428,8 +442,21 @@
 [[ -z $gz_publisher ]] && fail_usage "$f_no_pref_publisher" "global"
 [[ -z $gz_publisher_url ]] && fail_usage "$f_no_pref_publisher" "global"
 
+get_pub_secinfo $gz_publisher | read gzkeyfile gzcertfile
+if [[ $? -ne 0 ]]; then
+	fail_usage "$f_get_secinfo" $gz_publisher
+fi
+[[ $gzkeyfile != "None" && ! -f $gzkeyfile ]] && \
+    fail_usage "$f_nosuch_key" $gzkeyfile
+[[ $gzcertfile != "None" && ! -f $gzcertfile ]] && \
+    fail_usage "$f_nosuch_cert" $gzcertfile
+
+
+#
+# We're done with the global zone: switch images to the non-global
+# zone.
+#
 PKG_IMAGE="$zoneroot"
-export PKG_IMAGE
 
 #
 # Get publisher information for non global zone.
@@ -477,7 +504,7 @@
 # See if the zone knows about the gz entire fmri in question.  If yes,
 # we'll try using that.
 #
-$PKG list --no-refresh -a $gz_entire_fmri > /dev/null 2>&1
+LC_ALL=C $PKG list --no-refresh -a $gz_entire_fmri > /dev/null 2>&1
 
 #
 # If this doesn't exist, then we reset the preferred pub for
@@ -488,9 +515,37 @@
 	printf "$m_resetpub2\n" $gz_entire_fmri
 	printf "$m_resetpub3\n"
 
+	safe_dir $zoneroot/var
+	safe_dir $zoneroot/var/pkg
+
+	# Copy credentials from global zone.
+	secinfo=""
+	if [[ $gzkeyfile != "None" || $gzcertfile != "None" ]]; then
+		if [[ -e $zoneroot/$KEYDIR ]]; then
+			safe_dir $zoneroot/$KEYDIR
+		else
+			mkdir -m 755 $zoneroot/$KEYDIR
+		fi
+	fi
+
+	if [[ $gzkeyfile != None ]]; then
+		newlocation="$KEYDIR/attach_$(basename $gzkeyfile)"
+		safe_copy $gzkeyfile $zoneroot/$newlocation
+		chmod 644 $zoneroot/$newkeylocation
+		chown -h root:root $zoneroot/$newkeylocation
+		secinfo="$secinfo -k $newlocation"
+	fi
+	if [[ $gzcertfile != None ]]; then
+		newlocation="$KEYDIR/attach_$(basename $gzcertfile)"
+		safe_copy $gzcertfile $zoneroot/$newlocation
+		chmod 644 $zoneroot/$newkeylocation
+		chown -h root:root $zoneroot/$newkeylocation
+		secinfo="$secinfo -c $newlocation"
+	fi
+
 	# Note that we do cause a refresh here-- at some point we need the
 	# catalog to be updated.
-	pkg -R $zoneroot set-publisher -P -O $gz_publisher_url $gz_publisher
+	$PKG set-publisher -P -O $gz_publisher_url $secinfo $gz_publisher
 	if [[ $? -ne 0 ]]; then
 		fail_fatal "$f_reset_pub"
 	fi
@@ -527,7 +582,7 @@
 # First, list all the packages for the preferred publisher, then
 # do an 'install' on those.
 #
-zone_pkgs=$($PKG list --no-refresh -H "pkg://$zone_publisher/*" | \
+zone_pkgs=$(LC_ALL=C $PKG list --no-refresh -H "pkg://$zone_publisher/*" | \
     awk '{print $1}' | egrep -v '^entire$')
 if [[ $? -ne 0 ]]; then
 	fail_fatal "$f_pkg_list"
--- a/src/brand/common.ksh	Thu May 07 16:50:17 2009 -0700
+++ b/src/brand/common.ksh	Thu May 07 18:45:52 2009 -0700
@@ -40,6 +40,10 @@
 f_no_active_ds=$(gettext "Error: no active dataset.")
 f_zfs_mount=$(gettext "Unable to mount the zone's ZFS dataset.")
 
+f_safedir=$(gettext "Expected %s to be a directory.")
+f_cp=$(gettext "Failed to cp %s %s.")
+f_cp_unsafe=$(gettext "Failed to safely copy %s to %s.")
+
 fail_incomplete() {
 	printf "ERROR: " 1>&2
 	printf "$@" 1>&2
@@ -151,6 +155,43 @@
 # Emits to stdout the preferred publisher and its URL.
 #
 get_preferred_publisher() {
-	$PKG publisher -PH | nawk '$2 == "origin" && $3 == "online" \
+	LC_ALL=C $PKG publisher -PH | nawk '$2 == "origin" && $3 == "online" \
 	    {printf "%s %s\n", $1, $4; exit 0;}'
 }
+
+#
+# Emit to stdout the key and cert associated with the publisher
+# name provided.  Returns 'None' if no information is present.
+#
+get_pub_secinfo() {
+	key=$( LC_ALL=C $PKG publisher $1 | egrep '^ *SSL Key:' |
+	    awk '{print $3}' )
+	[[ $? -ne 0 ]] && return 1
+	cert=$( LC_ALL=C $PKG publisher $1 | egrep '^ *SSL Cert:' |
+	    awk '{print $3}' )
+	[[ $? -ne 0 ]] && return 1
+	print $key $cert
+}
+
+# Validate that the directory is safe.
+# n.b.: this is diverged from the shared/common.ksh version.
+safe_dir()
+{
+	typeset dir="$1"
+
+	[[ -h $dir || ! -d $dir ]] && fail_fatal "$f_safedir"
+}
+
+# Make a copy even if the destination already exists.
+# n.b.: this is diverged from the shared/common.ksh version.
+safe_copy()
+{
+	typeset src="$1"
+	typeset dst="$2"
+
+	if [[ ! -h $src && ! -h $dst && ! -d $dst ]]; then
+		/usr/bin/cp -p $src $dst || fail_fatal "$f_cp" "$src" "$dst"
+	else
+		fail_fatal "$f_cp_unsafe" "$src" "$dst"
+	fi
+}
--- a/src/brand/config.xml	Thu May 07 16:50:17 2009 -0700
+++ b/src/brand/config.xml	Thu May 07 18:45:52 2009 -0700
@@ -38,7 +38,7 @@
 
 	<!-- We may not be able to do the create in pkg(1) proper. -->
 	<install>/usr/lib/brand/ipkg/pkgcreatezone -z %z -R %R</install>
-	<installopts>a:p:e:h</installopts>
+	<installopts>a:P:e:c:k:h</installopts>
 	<boot></boot>
 	<halt></halt>
 	<verify_cfg></verify_cfg>
--- a/src/brand/pkgcreatezone	Thu May 07 16:50:17 2009 -0700
+++ b/src/brand/pkgcreatezone	Thu May 07 18:45:52 2009 -0700
@@ -24,6 +24,15 @@
 # Use is subject to license terms.
 #
 
+#
+# Resetting GZ_IMAGE to something besides slash allows for simplified
+# debugging of various global zone image configurations-- simply make
+# an image somewhere with the appropriate interesting parameters.
+#
+GZ_IMAGE=${GZ_IMAGE:-/}
+PKG_IMAGE=$GZ_IMAGE
+export PKG_IMAGE
+
 . /usr/lib/brand/ipkg/common.ksh
 
 f_pkg5_missing=$(gettext "pkg(5) does not seem to be present on this system.\n")
@@ -34,12 +43,18 @@
 f_bad_publisher=$(gettext "Syntax error in publisher information.\n")
 f_no_entire=$(gettext "Unable to find 'entire' incorporation in the global zone image.\n")
 f_no_entire_in_pref=$(gettext "Unable to locate the incorporation '%s' in the preferred publisher '%s'.\nUse -P to supply a publisher which contains this package.\n")
-
+f_key_prop=$(gettext "Unable to propagate key %s to %s")
+f_cert_prop=$(gettext "Unable to propagate cert %s to %s")
+f_get_secinfo=$(gettext "Failed to get key/cert information for publisher %s")
+f_nosuch_key=$(gettext "Failed to find key %s")
+f_nosuch_cert=$(gettext "Failed to find cert %s")
 
 m_publisher=$(gettext   "   Publisher: Using %s (%s).")
 m_cache=$(gettext       "       Cache: Using %s.")
 m_image=$(gettext       "       Image: Preparing at %s.")
 m_incorp=$(gettext      "Sanity Check: Looking for 'entire' incorporation.\n")
+m_key_prop=$(gettext    " Credentials: Propagating %s\n")
+m_cert_prop=$(gettext   " Credentials: Propagating %s\n")
 m_core=$(gettext        "  Installing: Core System (output follows)\n")
 m_more=$(gettext        "  Installing: Additional Packages (output follows)\n")
 m_smf=$(gettext	        " Postinstall: Copying SMF seed repository ...")
@@ -77,13 +92,17 @@
 TEXTDOMAIN="SUNW_OST_OSCMD"
 export TEXTDOMAIN
 
+KEYDIR=/var/pkg/ssl
 PKG=/usr/bin/pkg
+
 #
 # Just in case.  This should probably be removed later.
 #
 [[ ! -x $PKG ]] && fail_incomplete "$f_pkg5_missing"
 
-while getopts "a:P:z:R:h:e:" opt; do
+certfile="None"
+keyfile="None"
+while getopts "a:P:z:R:h:e:c:k:" opt; do
 	case $opt in
 		h)	fail_usage "$m_usage";;
 		R)	zonepath="$OPTARG" ;;
@@ -92,6 +111,8 @@
 			print -u2 \
 			    "WARNING: -a is deprecated.  Use -P instead." ;;
 		P)	pub_and_url="$OPTARG" ;;
+		c)	certfile="$OPTARG" ;;
+		k)	keyfile="$OPTARG" ;;
 		e)	extra_packages="$extra_packages $OPTARG" ;;
 		*)	fail_usage "$m_usage";;
 	esac
@@ -106,20 +127,31 @@
 is_system_labeled
 
 zoneroot=$zonepath/root
+secinfo=""
 
 #
 # If the user didn't give us a publisher, and there's a preferred publisher set
 # for the system, set that as the default.
 #
+propagate_secinfo=
 if [[ -z $pub_and_url ]]; then
+	if [[ $keyfile != "None" ]]; then
+		fail_usage "Key file not allowed without -P"
+	fi
+	if [[ $certfile != "None" ]]; then
+		fail_usage "Cert file not allowed without -P"
+	fi
 	#
 	# We look for a preferred online origin.
 	#
-	tpub_and_url=`LC_ALL=C $PKG -R / publisher -PH | \
+	tpub_and_url=`LC_ALL=C $PKG publisher -PH | \
 	    awk '$2 == "origin" && $3 == "online" \
 	    {printf "%s=%s\n", $1, $4; exit 0;}'`
 
 	[[ $? -eq 0 && -n $tpub_and_url ]] && pub_and_url="$tpub_and_url"
+
+	# Note that later we need to propagate key & cert.
+	propagate_secinfo=1
 fi
 
 #
@@ -137,6 +169,24 @@
 	fail_usage "$f_bad_publisher"
 fi
 
+if [[ -n $propagate_secinfo ]]; then
+	#
+	# Get the global zone's cert and key (if any) so that we can propagate
+	# them into the new image.
+	#
+	get_pub_secinfo $publisher | read keyfile certfile
+	if [[ $? -ne 0 ]]; then
+		fail_usage "$f_get_secinfo" $publisher
+	fi
+fi
+#
+# Do some sanity checks on key and cert.
+#
+[[ $keyfile != "None" && ! -f $keyfile ]] && \
+    fail_usage "$f_nosuch_key" $keyfile
+[[ $certfile != "None" && ! -f $certfile ]] && \
+    fail_usage "$f_nosuch_cert" $certfile
+
 #
 # Look for the 'entire' incorporation's FMRI in the current image; due to users
 # doing weird machinations with their publishers, we strip off the publisher
@@ -210,9 +260,40 @@
 #
 printf "$m_publisher" $publisher $publisherurl
 printf "\n$m_image\n" $zoneroot
-$PKG image-create --zone --full -p "$pub_and_url" $zoneroot || \
-	fail_incomplete "$f_img"
 
+#
+# We copy the credentials from the global zone into the new image
+# we're about to create.
+#
+if [[ $keyfile != "None" ]]; then
+	newkeylocation="$KEYDIR/$(basename $keyfile)"
+	secinfo="$secinfo -k $newkeylocation"
+	printf "$m_key_prop\n" $(basename $keyfile)
+	mkdir -p -m 755 $zoneroot/$KEYDIR || fail_fatal "$f_key_prop"
+	cp $keyfile $zoneroot/$newkeylocation || fail_fatal "$f_key_prop"
+	chmod 644 $zoneroot/$newkeylocation
+	chown -h root:root $zoneroot/$newkeylocation
+fi
+if [[ $certfile != "None" ]]; then
+	newcertlocation="$KEYDIR/$(basename $certfile)"
+	secinfo="$secinfo -c $newcertlocation"
+	printf "$m_cert_prop\n" $(basename $certfile)
+	mkdir -p -m 755 $zoneroot/$KEYDIR || fail_fatal "$f_cert_prop"
+	cp $certfile $zoneroot/$newcertlocation || fail_fatal "$f_cert_prop"
+	chmod 644 $zoneroot/$newkeylocation
+	chown -h root:root $zoneroot/$newkeylocation
+fi
+
+#
+# Regrettably, since we already copied the key information into place,
+# we must pass the -f (force) option to image-create, since it thinks that
+# something must be wrong, as the image exists.
+#
+# XXX in the future, we can create the image --no-refresh, then
+# set the publisher.  But image-create --no-refresh is broken right now.
+#
+$PKG image-create -f --zone --full -p "$pub_and_url" $secinfo $zoneroot || \
+    fail_incomplete "$f_img"
 
 PKG_IMAGE="$zoneroot"
 export PKG_IMAGE
--- a/src/client.py	Thu May 07 16:50:17 2009 -0700
+++ b/src/client.py	Thu May 07 18:45:52 2009 -0700
@@ -1662,7 +1662,7 @@
         else:
                 return 0
 
-def publisher_set(img_dir, args):
+def publisher_set(img, img_dir, args):
         """pkg set-publisher [-Ped] [-k ssl_key] [-c ssl_cert] [--reset-uuid]
             [-O origin_url] [-m mirror to add] [-M mirror to remove]
             [--enable] [--disable] [--no-refresh] publisher"""
@@ -1786,6 +1786,18 @@
         # None is checked for here so that a client can unset a ssl_cert or
         # ssl_key by using -k "" or -c "".
         if ssl_cert is not None or ssl_key is not None:
+                #
+                # In the case of zones, the ssl cert given is assumed to
+                # be relative to the root of the image, not truly absolute.
+                #
+                if img.is_zone():
+                        if ssl_cert is not None:
+                                ssl_cert = os.path.abspath(
+                                    img.get_root() + os.sep + ssl_cert)
+                        if ssl_key is not None:
+                                ssl_key = os.path.abspath(
+                                    img.get_root() + os.sep + ssl_key)
+
                 # Assume the user wanted to update the ssl_cert or ssl_key
                 # information for *all* of the currently selected
                 # repository's origins and mirrors.
@@ -2184,16 +2196,27 @@
 
         if len(pargs) != 1:
                 usage(_("image-create requires a single image directory path"))
+        image_dir = pargs[0]
 
         if ssl_key:
-                ssl_key = os.path.abspath(ssl_key)
+                # When creating zones, the path is image-root-relative.
+                if is_zone:
+                        ssl_key = os.path.normpath(image_dir + os.sep + \
+                            ssl_key)
+                else:
+                        ssl_key = os.path.abspath(ssl_key)
                 if not os.path.exists(ssl_key):
                         msg(_("pkg: set-publisher: SSL key file '%s' does " \
                             "not exist") % ssl_key)
                         return 1
 
         if ssl_cert:
-                ssl_cert = os.path.abspath(ssl_cert)
+                # When creating zones, the path is image-root-relative.
+                if is_zone:
+                        ssl_cert = os.path.normpath(image_dir + os.sep + \
+                            ssl_cert)
+                else:
+                        ssl_cert = os.path.abspath(ssl_cert)
                 if not os.path.exists(ssl_cert):
                         msg(_("pkg: set-publisher: SSL key cert '%s' does " \
                             "not exist") % ssl_cert)
@@ -2216,8 +2239,6 @@
                     "characters"))
                 return 1
 
-        image_dir = pargs[0]
-
         # Bail if there is already an image there
         if img.image_type(image_dir) != None and not force:
                 error(_("there is already an image at: %s") % image_dir)
@@ -2535,7 +2556,7 @@
                 elif subcommand == "verify":
                         return verify_image(img, pargs)
                 elif subcommand in ("set-authority", "set-publisher"):
-                        return publisher_set(mydir, pargs)
+                        return publisher_set(img, mydir, pargs)
                 elif subcommand in ("unset-authority", "unset-publisher"):
                         return publisher_unset(mydir, pargs)
                 elif subcommand in ("authority", "publisher"):
--- a/src/modules/client/image.py	Thu May 07 16:50:17 2009 -0700
+++ b/src/modules/client/image.py	Thu May 07 18:45:52 2009 -0700
@@ -265,35 +265,45 @@
                 if self.root == None:
                         raise RuntimeError, "self.root must be set"
 
-                ic = imageconfig.ImageConfig()
+                ic = imageconfig.ImageConfig(self.root)
                 ic.read(self.imgdir)
 
-                self.cfg_cache = ic
-
                 # make sure we define architecture variant; upgrade config
                 # file if possible.
-                if "variant.arch" not in self.cfg_cache.variants:
-                        self.cfg_cache.variants["variant.arch"] = \
-                            platform.processor()
+                changed = False
+                if "variant.arch" not in ic.variants:
+                        ic.variants["variant.arch"] = platform.processor()
                         try:
-                                self.save_config()
+                                ic.write(self.imgdir)
+                                changed = True
                         except api_errors.PermissionsException:
                                 pass
                 # make sure we define zone variant; upgrade config if possible
-                if "variant.opensolaris.zone" not in self.cfg_cache.variants:
-                        zone = self.cfg_cache.filters.get("opensolaris.zone",
-                            "")
+                if "variant.opensolaris.zone" not in ic.variants:
+                        zone = ic.filters.get("opensolaris.zone", "")
                         if zone == "nonglobal":
-                                self.cfg_cache.variants[
+                                ic.variants[
                                     "variant.opensolaris.zone"] = "nonglobal"
                         else:
-                                self.cfg_cache.variants[
+                                ic.variants[
                                     "variant.opensolaris.zone"] = "global"
                         try:
-                                self.save_config()
+                                ic.write(self.imgdir)
+                                changed = True
                         except api_errors.PermissionsException:
                                 pass
 
+                #
+                # If we made changes to the configuration, reload it;
+                # this lets any processing which is a side-effect of
+                # these changes take place.
+                #
+                if changed:
+                        ic = imageconfig.ImageConfig(self.root)
+                        ic.read(self.imgdir)
+
+                self.cfg_cache = ic
+
         def save_config(self):
                 self.cfg_cache.write(self.imgdir)
 
@@ -347,7 +357,7 @@
                     repositories=[repo])
 
                 # Initialize and store the configuration object.
-                self.cfg_cache = imageconfig.ImageConfig()
+                self.cfg_cache = imageconfig.ImageConfig(self.root)
 
                 # ...so that if creation of the Publisher object fails, an
                 # empty, useless image won't be left behind.
--- a/src/modules/client/imageconfig.py	Thu May 07 16:50:17 2009 -0700
+++ b/src/modules/client/imageconfig.py	Thu May 07 18:45:52 2009 -0700
@@ -107,7 +107,8 @@
         # XXX Use of ConfigParser is convenient and at most speculative--and
         # definitely not interface.
 
-        def __init__(self):
+        def __init__(self, imgroot):
+                self.__imgroot = imgroot
                 self.publishers = {}
                 self.publisher_status = {}
                 self.mirror_status = {}
@@ -151,6 +152,14 @@
                 # The root directory for publisher metadata.
                 pmroot = os.path.join(path, PUB_META_DIR)
 
+                #
+                # Must load variants first, since in the case of zones,
+                # the variant can impact the processing of publishers.
+                #
+                if cp.has_section("variant"):
+                        for o in cp.options("variant"):
+                                self.variants[o] = cp.get("variant", o)
+
                 for s in cp.sections():
                         if re.match("authority_.*", s):
                                 k, a = self.read_publisher(pmroot, cp, s)
@@ -183,10 +192,6 @@
                         for o in cp.options("filter"):
                                 self.filters[o] = cp.get("filter", o)
 
-                if cp.has_section("variant"):
-                        for o in cp.options("variant"):
-                                self.variants[o] = cp.get("variant", o)
-
                 try:
                         self.preferred_publisher = \
                             self.properties["preferred-publisher"]
@@ -265,9 +270,27 @@
                         c.set(section, "mirrors",
                             str([u.uri for u in repo.mirrors]))
 
+                        #
+                        # For zones, where the reachability of an absolute path
+                        # changes depending on whether you're in the zone or
+                        # not.  So we have a different policy: ssl_key and
+                        # ssl_cert are treated as zone root relative.
+                        #
+                        ngz = self.variants.get("variant.opensolaris.zone",
+                            "global") == "nonglobal"
+                        p = str(pub["ssl_key"])
+                        if ngz and self.__imgroot != os.sep and p != "None":
+                                # Trim the imageroot from the path.
+                                if p.startswith(self.__imgroot):
+                                        p = p[len(self.__imgroot):]
                         # XXX this should be per origin or mirror
-                        c.set(section, "ssl_key", str(pub["ssl_key"]))
-                        c.set(section, "ssl_cert", str(pub["ssl_cert"]))
+                        c.set(section, "ssl_key", p)
+                        p = str(pub["ssl_cert"])
+                        if ngz and self.__imgroot != os.sep and p != "None":
+                                if p.startswith(self.__imgroot):
+                                        p = p[len(self.__imgroot):]
+                        # XXX this should be per origin or mirror
+                        c.set(section, "ssl_cert", p)
 
                         # XXX this should really be client_uuid, but is being
                         # left with this name for compatibility with older
@@ -434,8 +457,21 @@
                         ssl_key = None
                         ssl_cert = None
 
+                #
+                # For zones, where the reachability of an absolute path
+                # changes depending on whether you're in the zone or not.  So
+                # we have a different policy: ssl_key and ssl_cert are treated
+                # as zone root relative.
+                #
+                ngz = self.variants.get("variant.opensolaris.zone",
+                    "global") == "nonglobal"
+
                 if ssl_key:
-                        ssl_key = os.path.abspath(ssl_key)
+                        if ngz:
+                                ssl_key = os.path.normpath(self.__imgroot +
+                                    os.sep + ssl_key)
+                        else:
+                                ssl_key = os.path.abspath(ssl_key)
                         if not os.path.exists(ssl_key):
                                 # XXX need client messaging framework
                                 emsg(api_errors.NoSuchCertificate(ssl_key,
@@ -443,7 +479,11 @@
                                 ssl_key = None
 
                 if ssl_cert:
-                        ssl_cert = os.path.abspath(ssl_cert)
+                        if ngz:
+                                ssl_cert = os.path.normpath(self.__imgroot +
+                                    os.sep + ssl_cert)
+                        else:
+                                ssl_cert = os.path.abspath(ssl_cert)
                         if not os.path.exists(ssl_cert):
                                 # XXX need client messaging framework
                                 emsg(api_errors.NoSuchCertificate(ssl_cert,
--- a/src/tests/api/t_imageconfig.py	Thu May 07 16:50:17 2009 -0700
+++ b/src/tests/api/t_imageconfig.py	Thu May 07 18:45:52 2009 -0700
@@ -20,7 +20,7 @@
 # CDDL HEADER END
 #
 
-# Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
+# Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
 # Use is subject to license terms.
 
 import unittest
@@ -66,7 +66,7 @@
 sort_policy: priority
 """)
                 f.close()
-                self.ic = imageconfig.ImageConfig()
+                self.ic = imageconfig.ImageConfig(self.sample_dir)
 
         def tearDown(self):
                 try:
@@ -104,7 +104,7 @@
                 self.ic.properties['name'] = ustr
                 newdir = tempfile.mkdtemp()
                 self.ic.write(newdir)
-                ic2 = imageconfig.ImageConfig()
+                ic2 = imageconfig.ImageConfig(newdir)
                 ic2.read(newdir)
                 ustr2 = ic2.properties['name']
                 shutil.rmtree(newdir)