summaryrefslogtreecommitdiff
path: root/usr/src/lib/libpkg/common
diff options
context:
space:
mode:
Diffstat (limited to 'usr/src/lib/libpkg/common')
-rw-r--r--usr/src/lib/libpkg/common/canonize.c97
-rw-r--r--usr/src/lib/libpkg/common/cfext.h83
-rw-r--r--usr/src/lib/libpkg/common/ckparam.c196
-rw-r--r--usr/src/lib/libpkg/common/ckvolseq.c109
-rw-r--r--usr/src/lib/libpkg/common/cvtpath.c58
-rw-r--r--usr/src/lib/libpkg/common/dbtables.h210
-rw-r--r--usr/src/lib/libpkg/common/devtype.c115
-rw-r--r--usr/src/lib/libpkg/common/dstream.c1036
-rw-r--r--usr/src/lib/libpkg/common/fmkdir.c65
-rw-r--r--usr/src/lib/libpkg/common/gpkglist.c359
-rw-r--r--usr/src/lib/libpkg/common/gpkgmap.c1275
-rw-r--r--usr/src/lib/libpkg/common/handlelocalfs.c150
-rw-r--r--usr/src/lib/libpkg/common/isdir.c391
-rw-r--r--usr/src/lib/libpkg/common/keystore.c2474
-rw-r--r--usr/src/lib/libpkg/common/keystore.h145
-rw-r--r--usr/src/lib/libpkg/common/llib-lpkg36
-rw-r--r--usr/src/lib/libpkg/common/logerr.c72
-rw-r--r--usr/src/lib/libpkg/common/mapfile-vers223
-rw-r--r--usr/src/lib/libpkg/common/mappath.c236
-rw-r--r--usr/src/lib/libpkg/common/ncgrpw.c740
-rw-r--r--usr/src/lib/libpkg/common/nhash.c182
-rw-r--r--usr/src/lib/libpkg/common/nhash.h75
-rw-r--r--usr/src/lib/libpkg/common/p12lib.c2786
-rw-r--r--usr/src/lib/libpkg/common/p12lib.h245
-rw-r--r--usr/src/lib/libpkg/common/pkgerr.c135
-rw-r--r--usr/src/lib/libpkg/common/pkgerr.h104
-rw-r--r--usr/src/lib/libpkg/common/pkgexecl.c80
-rw-r--r--usr/src/lib/libpkg/common/pkgexecv.c445
-rw-r--r--usr/src/lib/libpkg/common/pkglib.h622
-rw-r--r--usr/src/lib/libpkg/common/pkglibmsgs.h431
-rw-r--r--usr/src/lib/libpkg/common/pkglocale.h51
-rw-r--r--usr/src/lib/libpkg/common/pkgmount.c167
-rw-r--r--usr/src/lib/libpkg/common/pkgstr.c1132
-rw-r--r--usr/src/lib/libpkg/common/pkgtrans.c1973
-rw-r--r--usr/src/lib/libpkg/common/pkgweb.c3238
-rw-r--r--usr/src/lib/libpkg/common/pkgweb.h130
-rw-r--r--usr/src/lib/libpkg/common/pkgxpand.c118
-rw-r--r--usr/src/lib/libpkg/common/ppkgmap.c139
-rw-r--r--usr/src/lib/libpkg/common/progerr.c201
-rw-r--r--usr/src/lib/libpkg/common/putcfile.c376
-rw-r--r--usr/src/lib/libpkg/common/rrmdir.c83
-rw-r--r--usr/src/lib/libpkg/common/runcmd.c808
-rw-r--r--usr/src/lib/libpkg/common/security.c282
-rw-r--r--usr/src/lib/libpkg/common/srchcfile.c1278
-rw-r--r--usr/src/lib/libpkg/common/tputcfent.c191
-rw-r--r--usr/src/lib/libpkg/common/verify.c989
-rw-r--r--usr/src/lib/libpkg/common/vfpops.c1283
47 files changed, 25614 insertions, 0 deletions
diff --git a/usr/src/lib/libpkg/common/canonize.c b/usr/src/lib/libpkg/common/canonize.c
new file mode 100644
index 0000000000..ff34c40507
--- /dev/null
+++ b/usr/src/lib/libpkg/common/canonize.c
@@ -0,0 +1,97 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
+/* All Rights Reserved */
+
+
+#include <string.h>
+
+#define isdot(x) ((x[0] == '.') && (!x[1] || (x[1] == '/')))
+#define isdotdot(x) ((x[0] == '.') && (x[1] == '.') && \
+ (!x[2] || (x[2] == '/')))
+
+void
+canonize(char *file)
+{
+ char *pt, *last;
+ int level;
+
+ /* remove references such as "./" and "../" and "//" */
+ for (pt = file; *pt; /* void */) {
+ if (isdot(pt))
+ (void) strcpy(pt, pt[1] ? pt+2 : pt+1);
+ else if (isdotdot(pt)) {
+ level = 0;
+ last = pt;
+ do {
+ level++;
+ last += 2;
+ if (*last)
+ last++;
+ } while (isdotdot(last));
+ --pt; /* point to previous '/' */
+ while (level--) {
+ if (pt <= file)
+ return;
+ while ((*--pt != '/') && (pt > file))
+ ;
+ }
+ if (*pt == '/')
+ pt++;
+ (void) strcpy(pt, last);
+ } else {
+ while (*pt && (*pt != '/'))
+ pt++;
+ if (*pt == '/') {
+ while (pt[1] == '/')
+ (void) strcpy(pt, pt+1);
+ pt++;
+ }
+ }
+ }
+ if ((--pt > file) && (*pt == '/'))
+ *pt = '\0';
+}
+
+void
+canonize_slashes(char *file)
+{
+ char *pt;
+
+ /* remove references such as "//" */
+ for (pt = file; *pt; /* void */) {
+ while (*pt && (*pt != '/'))
+ pt++;
+ if (*pt == '/') {
+ while (pt[1] == '/')
+ (void) strcpy(pt, pt+1);
+ pt++;
+ }
+ }
+ if ((--pt > file) && (*pt == '/'))
+ *pt = '\0';
+}
diff --git a/usr/src/lib/libpkg/common/cfext.h b/usr/src/lib/libpkg/common/cfext.h
new file mode 100644
index 0000000000..14ec1fce2a
--- /dev/null
+++ b/usr/src/lib/libpkg/common/cfext.h
@@ -0,0 +1,83 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _CFEXT_H
+#define _CFEXT_H
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <pkgstrct.h>
+
+struct mergstat {
+ unsigned setuid:1; /* pkgmap entry has setuid */
+ unsigned setgid:1; /* ... and/or setgid bit set */
+ unsigned contchg:1; /* contents of the files different */
+ unsigned attrchg:1; /* attributes are different */
+ unsigned shared:1; /* > 1 pkg associated with this */
+ unsigned osetuid:1; /* installed set[ug]id process ... */
+ unsigned osetgid:1; /* ... being overwritten by pkg. */
+ unsigned rogue:1; /* conflicting file not owned by a package */
+ unsigned dir2nondir:1; /* was a directory & now a non-directory */
+ unsigned replace:1; /* merge makes no sense for this object pair */
+ unsigned denied:1; /* for some reason this was not allowed in */
+ unsigned preloaded:1; /* already checked in a prior pkg op */
+ unsigned processed:1; /* already installed or removed */
+ unsigned parentsyml2dir:1;
+ /* parent directory changed from symlink to a directory */
+};
+
+/*
+ * This is information required by pkgadd for fast operation. A
+ * cfextra struct is tagged to each cfent structure requiring
+ * processing. This is how we avoid some unneeded repetition. The
+ * entries incorporating the word 'local' refer to the path that
+ * gets us to the delivered package file. In other words, to install
+ * a file we usually copy from 'local' to 'path' below. In the case
+ * of a link, where no actual copying takes place, local is the source
+ * of the link. Note that environment variables are not evaluated in
+ * the locals unless they are links since the literal path is how
+ * pkgadd finds the entry under the reloc directory.
+ */
+struct cfextra {
+ struct cfent cf_ent; /* basic contents file entry */
+ struct mergstat mstat; /* merge status for installs */
+ short fsys_value; /* fstab[] entry index */
+ short fsys_base; /* actual base filesystem in fs_tab[] */
+ char *client_path; /* the client-relative path */
+ char *server_path; /* the server-relative path */
+ char *map_path; /* as read from the pkgmap */
+ char *client_local; /* client_relative local */
+ char *server_local; /* server relative local */
+};
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _CFEXT_H */
diff --git a/usr/src/lib/libpkg/common/ckparam.c b/usr/src/lib/libpkg/common/ckparam.c
new file mode 100644
index 0000000000..68284e6012
--- /dev/null
+++ b/usr/src/lib/libpkg/common/ckparam.c
@@ -0,0 +1,196 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
+/* All Rights Reserved */
+
+
+
+#include <ctype.h>
+#include <string.h>
+#include <sys/types.h>
+#include "pkglib.h"
+#include "pkglibmsgs.h"
+#include "pkglocale.h"
+
+#define MAXLEN 256
+#define TOKLEN 16
+
+static int proc_name(char *param, char *value);
+static int proc_arch(char *param, char *value);
+static int proc_version(char *param, char *value);
+static int proc_category(char *param, char *value);
+static int bad_first_char(char *param, char *value);
+static int not_alnum(char *param, char *pt);
+static int not_ascii(char *param, char *pt);
+static int too_long(char *param, char *pt, int len);
+static int isnull(char *param, char *pt);
+
+int
+ckparam(char *param, char *val)
+{
+ char *value = strdup(val);
+ int ret_val = 0; /* return value */
+
+ if (strcmp(param, "NAME") == 0)
+ ret_val = proc_name(param, value);
+
+ else if (strcmp(param, "ARCH") == 0)
+ ret_val = proc_arch(param, value);
+
+ else if (strcmp(param, "VERSION") == 0)
+ ret_val = proc_version(param, value);
+
+ else if (strcmp(param, "CATEGORY") == 0)
+ ret_val = proc_category(param, value);
+
+ /* param does not match existing parameters */
+ free(value);
+ return (ret_val);
+}
+
+static int
+proc_name(char *param, char *value)
+{
+ int ret_val;
+
+ if (!(ret_val = isnull(param, value))) {
+ ret_val += too_long(param, value, MAXLEN);
+ ret_val += not_ascii(param, value);
+ }
+
+ return (ret_val);
+}
+
+static int
+proc_arch(char *param, char *value)
+{
+ int ret_val;
+ char *token;
+
+ if (!(ret_val = isnull(param, value))) {
+ token = strtok(value, ", ");
+
+ while (token) {
+ ret_val += too_long(param, token, TOKLEN);
+ ret_val += not_ascii(param, token);
+ token = strtok(NULL, ", ");
+ }
+ }
+
+ return (ret_val);
+}
+
+static int
+proc_version(char *param, char *value)
+{
+ int ret_val;
+
+ if (!(ret_val = isnull(param, value))) {
+ ret_val += bad_first_char(param, value);
+ ret_val += too_long(param, value, MAXLEN);
+ ret_val += not_ascii(param, value);
+ }
+
+ return (ret_val);
+}
+
+static int
+proc_category(char *param, char *value)
+{
+ int ret_val;
+ char *token;
+
+ if (!(ret_val = isnull(param, value))) {
+ token = strtok(value, ", ");
+
+ while (token) {
+ ret_val += too_long(param, token, TOKLEN);
+ ret_val += not_alnum(param, token);
+ token = strtok(NULL, ", ");
+ }
+ }
+
+ return (ret_val);
+}
+
+static int
+bad_first_char(char *param, char *value)
+{
+ if (*value == '(') {
+ progerr(pkg_gt(ERR_CHAR), param);
+ return (1);
+ }
+
+ return (0);
+}
+
+static int
+isnull(char *param, char *pt)
+{
+ if (!pt || *pt == '\0') {
+ progerr(pkg_gt(ERR_UNDEF), param);
+ return (1);
+ }
+ return (0);
+}
+
+static int
+too_long(char *param, char *pt, int len)
+{
+ if (strlen(pt) > (size_t)len) {
+ progerr(pkg_gt(ERR_LEN), pt);
+ return (1);
+ }
+ return (0);
+}
+
+static int
+not_ascii(char *param, char *pt)
+{
+ while (*pt) {
+ if (!(isascii(*pt))) {
+ progerr(pkg_gt(ERR_ASCII), param);
+ return (1);
+ }
+ pt++;
+ }
+ return (0);
+}
+
+static int
+not_alnum(char *param, char *pt)
+{
+ while (*pt) {
+ if (!(isalnum(*pt))) {
+ progerr(pkg_gt(ERR_ALNUM), param);
+ return (1);
+ }
+ pt++;
+ }
+
+ return (0);
+}
diff --git a/usr/src/lib/libpkg/common/ckvolseq.c b/usr/src/lib/libpkg/common/ckvolseq.c
new file mode 100644
index 0000000000..13015ea898
--- /dev/null
+++ b/usr/src/lib/libpkg/common/ckvolseq.c
@@ -0,0 +1,109 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
+/* All Rights Reserved */
+
+
+
+#include <stdio.h>
+#include <limits.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include "pkgstrct.h"
+#include "pkglib.h"
+#include "pkglibmsgs.h"
+#include "pkglocale.h"
+
+#define PKGMAP "pkgmap"
+#define PKGINFO "pkginfo"
+
+int
+ckvolseq(char *dir, int part, int nparts)
+{
+ static struct cinfo cinfo;
+ char ftype, path[PATH_MAX];
+
+ if (part > 0) {
+ ftype = 'f';
+ if (part == 1) {
+ /*
+ * save stats about content information of pkginfo
+ * file in order to verify multi-volume packages
+ */
+ cinfo.cksum = cinfo.size = cinfo.modtime = (-1L);
+ (void) snprintf(path, sizeof (path), "%s/pkginfo", dir);
+ if (cverify(0, &ftype, path, &cinfo, 1)) {
+ logerr(pkg_gt(ERR_BADPKGINFO), path);
+ logerr(getErrbufAddr());
+ return (1);
+ }
+ (void) snprintf(path, sizeof (path), "%s/pkgmap", dir);
+ if (access(path, 0)) {
+ logerr(pkg_gt(ERR_NOPKGMAP), path);
+ return (2);
+ }
+ } else {
+ /* temp fix due to summit problem */
+ cinfo.modtime = (-1);
+
+ /* pkginfo file doesn't match first floppy */
+ (void) snprintf(path, sizeof (path), "%s/pkginfo", dir);
+ if (cverify(0, &ftype, path, &cinfo, 1)) {
+ logerr(pkg_gt(MSG_CORRUPT));
+ logerr(getErrbufAddr());
+ return (1);
+ }
+ }
+ } else
+ part = (-part);
+
+ /*
+ * each volume in a multi-volume package must
+ * contain either the root.n or reloc.n directories
+ */
+ if (nparts != 1) {
+ /* look for multi-volume specification */
+ (void) snprintf(path, sizeof (path), "%s/root.%d", dir, part);
+ if (access(path, 0) == 0)
+ return (0);
+ (void) snprintf(path, sizeof (path), "%s/reloc.%d", dir, part);
+ if (access(path, 0) == 0)
+ return (0);
+ if (part == 1) {
+ (void) snprintf(path, sizeof (path), "%s/install",
+ dir, part);
+ if (access(path, 0) == 0)
+ return (0);
+ }
+ if (nparts) {
+ logerr(pkg_gt(MSG_SEQ));
+ return (2);
+ }
+ }
+ return (0);
+}
diff --git a/usr/src/lib/libpkg/common/cvtpath.c b/usr/src/lib/libpkg/common/cvtpath.c
new file mode 100644
index 0000000000..1912e24523
--- /dev/null
+++ b/usr/src/lib/libpkg/common/cvtpath.c
@@ -0,0 +1,58 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
+/* All Rights Reserved */
+
+
+
+#include <string.h>
+
+extern char *root, *basedir; /* WHERE? */
+
+void
+cvtpath(char *path, char *copy)
+{
+ *copy++ = '/';
+ if (root || (basedir && (*path != '/'))) {
+ if (root && ((basedir == NULL) || (path[0] == '/') ||
+ (basedir[0] != '/'))) {
+ /* look in root */
+ (void) strcpy(copy, root + (*root == '/' ? 1 : 0));
+ copy += strlen(copy);
+ if (copy[-1] != '/')
+ *copy++ = '/';
+ }
+ if (basedir && (*path != '/')) {
+ (void) strcpy(copy,
+ basedir + (*basedir == '/' ? 1 : 0));
+ copy += strlen(copy);
+ if (copy[-1] != '/')
+ *copy++ = '/';
+ }
+ }
+ (void) strcpy(copy, path + (*path == '/' ? 1 : 0));
+}
diff --git a/usr/src/lib/libpkg/common/dbtables.h b/usr/src/lib/libpkg/common/dbtables.h
new file mode 100644
index 0000000000..67b3b0fd20
--- /dev/null
+++ b/usr/src/lib/libpkg/common/dbtables.h
@@ -0,0 +1,210 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _DBTABLES_H
+#define _DBTABLES_H
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+#define TBL_HDR "create table"
+#define TBL_TRL "on conflict replace"
+
+/*
+ * The patch contents table. This table is used for storing patch package
+ * related file object information and is similar to the pkg contents table.
+ * This table is for future use in allowing FS object checking instead or
+ * in lieu of package level chacking.
+ *
+ * The pkgs column is not normalized. This is due to trying to represent the
+ * data the same way as the legacy patch system does. The column should be
+ * normalized when the Patch Architecture is re-designed.
+ *
+ * Note that path is given the maximum length of /usr/include/limits.h
+ * defined PATH_MAX.
+ */
+
+const char *pcPatchContents = {
+ "CREATE TABLE patch_contents_table (" \
+ "patch CHAR(16) NOT NULL," \
+ "path VARCHAR(1024) NOT NULL," \
+ "ftype CHAR(1) NOT NULL," \
+ "class CHAR(32) NOT NULL," \
+ "mode CHAR(5) NOT NULL," \
+ "owner CHAR(32) NOT NULL," \
+ "grp CHAR(32) NOT NULL," \
+ "major CHAR(32) NOT NULL," \
+ "minor CHAR(32) NOT NULL," \
+ "sz CHAR(32) NOT NULL," \
+ "sum CHAR(32) NOT NULL," \
+ "modtime CHAR(32) NOT NULL," \
+ "pkgstatus CHAR(1) NOT NULL," \
+ "pkgs VARCHAR(4096) NOT NULL," \
+ "PRIMARY KEY(patch)" \
+ "ON CONFLICT REPLACE);"};
+
+/*
+ * The patch table. This table represents all patch meta data needed
+ * to handle dependency checking during the installation and removal of
+ * a patch.
+ */
+
+const char *pcPatchPkg = {
+ "CREATE TABLE patch_pkg_table (" \
+ "patch CHAR(16) NOT NULL," \
+ "pkg VARCHAR(64) NOT NULL," \
+ "PRIMARY KEY(patch, pkg)" \
+ "ON CONFLICT REPLACE);"};
+
+/* This table represents the informatin associated with an installed patch */
+
+const char *pcPatch = {
+ "CREATE TABLE patch_table (" \
+ "patch CHAR(16) NOT NULL," \
+ "rev CHAR(8) NOT NULL," \
+ "bcode CHAR(12) NOT NULL," \
+ "sep CHAR(1) NOT NULL," \
+ "obs VARCHAR(1024) NULL," \
+ "reqs VARCHAR(1024) NULL," \
+ "incs VARCHAR(1024) NULL," \
+ "backout VARCHAR(256) NULL," \
+ "time TIMESTAMP(64) NOT NULL," \
+ "pkgs VARCHAR(1024) NOT NULL," \
+ "PRIMARY KEY(patch)" \
+ "ON CONFLICT REPLACE);"};
+
+/* This table represents the patchinfo file in its entirety */
+
+const char *pcPatchinfo = {
+ "CREATE TABLE patchinfo_table (" \
+ "patch CHAR(16) NOT NULL," \
+ "key CHAR(256) NOT NULL," \
+ "value CHAR(256) NOT NULL," \
+ "PRIMARY KEY(patch)" \
+ "ON CONFLICT REPLACE);"};
+
+/*
+ * Since it is read in as a whole - column by column
+ * and processed by legacy code which expects all of
+ * the data, all the time, it is not useful to separate
+ * the columns into separate tables via normalization.
+ * In effect, each column is logically one data unit,
+ * even if it comprises several distinct values (ie. pkgs).
+ *
+ * Note that path is given the maximum length of /usr/include/limits.h
+ * defined PATH_MAX.
+ */
+
+const char *pcContents =
+ "CREATE TABLE pkg_table (" \
+ "path VARCHAR(1024) NOT NULL," \
+ "ftype CHAR(1) NOT NULL," \
+ "class CHAR(32) NOT NULL," \
+ "mode CHAR(5) DEFAULT '-1'," \
+ "owner CHAR(32) DEFAULT '?'," \
+ "grp CHAR(32) DEFAULT '?'," \
+ "major CHAR(32) DEFAULT '-1'," \
+ "minor CHAR(32) DEFAULT '-1'," \
+ "sz CHAR(32) DEFAULT '-1'," \
+ "sum CHAR(32) DEFAULT '-1'," \
+ "modtime CHAR(32) DEFAULT '-1'," \
+ "pkgstatus CHAR(1) DEFAULT '0'," \
+ "pkgs VARCHAR(4096) NOT NULL," \
+ "PRIMARY KEY(path)" \
+ " ON CONFLICT REPLACE);";
+
+/*
+ * It is necessary to save off the dependency comments so that
+ * they can be regenerated in the depend output. This is due
+ * to there being copyright information in the depend files
+ * which cannot be tossed. This is a logically distinct table
+ * to the depend data since it includes none of the other data
+ * used in that table.
+ */
+
+const char *pcDepComments =
+ "CREATE TABLE depcomment_table (" \
+ "pkg VARCHAR(64) NOT NULL, " \
+ "comment VARCHAR(800) NOT NULL, " \
+ "seqno INT NOT NULL, " \
+ "PRIMARY KEY(pkg) " \
+ "ON CONFLICT REPLACE);";
+
+/* This is the table which contains the pkginfo data. */
+
+const char *pcSQL_pkginfo =
+ "CREATE TABLE pkginfo_table (" \
+ "pkg VARCHAR(80) NOT NULL, " \
+ "param VARCHAR(128) NOT NULL, " \
+ "value VARCHAR(128) NOT NULL, "\
+ "seqno INT NOT NULL, " \
+ "PRIMARY KEY(pkg, param) " \
+ "ON CONFLICT REPLACE);";
+
+/*
+ * This is the table which contains the depenency data.
+ * Note that the 'name' data is optional. Further, note
+ * that platform information (version & architecture)
+ * have been separated off into a separate table.
+ */
+
+const char *pcSQL_depend =
+ "CREATE TABLE depend_table (" \
+ "pkg VARCHAR(64) NOT NULL, " \
+ "pkgdep VARCHAR(64) NOT NULL, " \
+ "type CHAR(1) NOT NULL, "\
+ "name VARCHAR(128) NULL, " \
+ "seqno INT NOT NULL, " \
+ "PRIMARY KEY(pkg, pkgdep) " \
+ "ON CONFLICT REPLACE);";
+
+/*
+ * This table includes data which is specifically related to
+ * an individual dependency. The version and arch info are
+ * related to a specific pkg/pkgdep pair. It is possible to
+ * list more than one version/arch pair for a pkg/pkgdep
+ * pair using the definition below.
+ */
+
+const char *pcSQL_platform =
+ "CREATE TABLE depplatform_table ( " \
+ "pkg VARCHAR(64) NOT NULL " \
+ " REFERENCES depend_table(pkg), " \
+ "pkgdep VARCHAR(64) NOT NULL " \
+ " REFERENCES depend_table(pkgdep), " \
+ "version VARCHAR(32) NOT NULL, " \
+ "arch VARCHAR(32) NOT NULL, " \
+ "seqno INT NOT NULL, " \
+ "PRIMARY KEY (pkg, pkgdep, version, arch) " \
+ "ON CONFLICT REPLACE);";
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* _DBTABLES_H */
diff --git a/usr/src/lib/libpkg/common/devtype.c b/usr/src/lib/libpkg/common/devtype.c
new file mode 100644
index 0000000000..e087966f86
--- /dev/null
+++ b/usr/src/lib/libpkg/common/devtype.c
@@ -0,0 +1,115 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
+/* All Rights Reserved */
+
+
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include "pkgdev.h"
+#include "pkglib.h"
+
+extern char *devattr(char *device, char *attribute); /* libadm.a */
+
+int
+devtype(char *alias, struct pkgdev *devp)
+{
+ char *name;
+ devp->mntflg = 0;
+ devp->name = alias;
+ devp->dirname = devp->pathname = devp->mount = NULL;
+ devp->fstyp = devp->cdevice = devp->bdevice = devp->norewind = NULL;
+ devp->rdonly = 0;
+ devp->capacity = 0;
+
+ /* see if alias represents an existing file */
+ if (alias[0] == '/') {
+ if (!isdir(alias)) {
+ devp->dirname = devp->name;
+ return (0);
+ }
+ }
+
+ /* see if alias represents a mountable device (e.g., a floppy) */
+ if ((devp->mount = devattr(alias, "mountpt")) != NULL &&
+ devp->mount[0] != NULL) {
+ devp->bdevice = devattr(alias, "bdevice");
+ if (!devp->bdevice || !devp->bdevice[0]) {
+ if (devp->bdevice) {
+ free(devp->bdevice);
+ devp->bdevice = NULL;
+ }
+ return (-1);
+ }
+ devp->dirname = devp->mount;
+ } else if (devp->mount) {
+ free(devp->mount);
+ devp->mount = NULL;
+ }
+
+ devp->cdevice = devattr(alias, "cdevice");
+ if (devp->cdevice && devp->cdevice[0]) {
+ /* check for capacity */
+ if (name = devattr(alias, "capacity")) {
+ if (name[0])
+ devp->capacity = atoll(name);
+ free(name);
+ }
+ /* check for norewind device */
+ devp->norewind = devattr(alias, "norewind");
+ if (devp->norewind && !devp->norewind[0]) {
+ free(devp->norewind);
+ devp->norewind = NULL;
+ }
+
+ /* mountable devices will always have associated raw device */
+ return (0);
+ }
+ if (devp->cdevice) {
+ free(devp->cdevice);
+ devp->cdevice = NULL;
+ }
+ /*
+ * if it is not a raw device, it must be a directory or a regular file
+ */
+ name = devattr(alias, "pathname");
+ if (!name || !name[0]) {
+ /* Assume a regular file */
+ if (name)
+ free(name);
+ devp->pathname = alias;
+ return (0);
+ }
+ if (!isdir(name))
+ devp->dirname = name;
+ else
+ devp->pathname = name;
+ return (0);
+}
diff --git a/usr/src/lib/libpkg/common/dstream.c b/usr/src/lib/libpkg/common/dstream.c
new file mode 100644
index 0000000000..68ff540393
--- /dev/null
+++ b/usr/src/lib/libpkg/common/dstream.c
@@ -0,0 +1,1036 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
+/* All Rights Reserved */
+
+
+
+#include <stdio.h>
+#include <string.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/sysmacros.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/statvfs.h>
+#include <fcntl.h>
+#ifdef u3b2
+#include <sys/sys3b.h>
+#endif /* u3b2 */
+#include <openssl/err.h>
+#include "pkglib.h"
+#include "pkglibmsgs.h"
+#include "pkglocale.h"
+#ifdef u3b2
+static
+struct stat orig_st_buf; /* Stat structure of original file (3B2/CTC) */
+static char ds_ctcflg;
+#endif /* u3b2 */
+
+/* libadm.a */
+extern char *devattr(char *device, char *attribute);
+extern int pkgnmchk(register char *pkg, register char *spec,
+ int presvr4flg);
+extern int getvol(char *device, char *label, int options, char *prompt);
+
+#define CMDSIZ 512
+#define LSIZE 128
+#define DDPROC "/usr/bin/dd"
+#define CPIOPROC "/usr/bin/cpio"
+
+/* device types */
+
+#define G_TM_TAPE 1 /* Tapemaster controller */
+#define G_XY_DISK 3 /* xy disks */
+#define G_SD_DISK 7 /* scsi sd disk */
+#define G_XT_TAPE 8 /* xt tapes */
+#define G_SF_FLOPPY 9 /* sf floppy */
+#define G_XD_DISK 10 /* xd disks */
+#define G_ST_TAPE 11 /* scsi tape */
+#define G_NS 12 /* noswap pseudo-dev */
+#define G_RAM 13 /* ram pseudo-dev */
+#define G_FT 14 /* tftp */
+#define G_HD 15 /* 386 network disk */
+#define G_FD 16 /* 386 AT disk */
+#define G_FILE 28 /* file, not a device */
+#define G_NO_DEV 29 /* device does not require special treatment */
+#define G_DEV_MAX 30 /* last valid device type */
+
+struct dstoc {
+ int cnt;
+ char pkg[NON_ABI_NAMELNGTH];
+ int nparts;
+ long maxsiz;
+ char volnos[128];
+ struct dstoc *next;
+} *ds_head, *ds_toc;
+
+#define ds_nparts ds_toc->nparts
+#define ds_maxsiz ds_toc->maxsiz
+
+int ds_totread; /* total number of parts read */
+int ds_fd = -1;
+int ds_curpartcnt = -1;
+
+int ds_next(char *device, char *instdir);
+int ds_ginit(char *device);
+int ds_close(int pkgendflg);
+
+static FILE *ds_pp;
+static int ds_realfd = -1; /* file descriptor for real device */
+static int ds_read; /* number of parts read for current package */
+static int ds_volno; /* volume number of current volume */
+static int ds_volcnt; /* total number of volumes */
+static char ds_volnos[128]; /* parts/volume info */
+static char *ds_device;
+static int ds_volpart; /* number of parts read in current volume, */
+ /* including skipped parts */
+static int ds_bufsize;
+static int ds_skippart; /* number of parts skipped in current volume */
+
+static int ds_getnextvol(char *device);
+static int ds_skip(char *device, int nskip);
+
+void
+ds_order(char *list[])
+{
+ struct dstoc *toc_pt;
+ register int j, n;
+ char *pt;
+
+ toc_pt = ds_head;
+ n = 0;
+ while (toc_pt) {
+ for (j = n; list[j]; j++) {
+ if (strcmp(list[j], toc_pt->pkg) == 0) {
+ /* just swap places in the array */
+ pt = list[n];
+ list[n++] = list[j];
+ list[j] = pt;
+ }
+ }
+ toc_pt = toc_pt->next;
+ }
+}
+
+static char *pds_header;
+static char *ds_header;
+static char *ds_header_raw;
+static int ds_headsize;
+
+static char *
+ds_gets(char *buf, int size)
+{
+ int length;
+ char *nextp;
+
+ nextp = strchr(pds_header, '\n');
+ if (nextp == NULL) {
+ length = strlen(pds_header);
+ if (length > size)
+ return (0);
+ if ((ds_header = (char *)realloc(ds_header,
+ ds_headsize + BLK_SIZE)) == NULL)
+ return (0);
+ if (read(ds_fd, ds_header + ds_headsize, BLK_SIZE) < BLK_SIZE)
+ return (0);
+ ds_headsize += BLK_SIZE;
+ nextp = strchr(pds_header, '\n');
+ if (nextp == NULL)
+ return (0);
+ *nextp = '\0';
+ if (length + (int)strlen(pds_header) > size)
+ return (0);
+ (void) strncpy(buf + length, pds_header, strlen(pds_header));
+ buf[length + strlen(pds_header)] = '\0';
+ pds_header = nextp + 1;
+ return (buf);
+ }
+ *nextp = '\0';
+ if ((int)strlen(pds_header) > size)
+ return (0);
+ (void) strncpy(buf, pds_header, strlen(pds_header));
+ buf[strlen(pds_header)] = '\0';
+ pds_header = nextp + 1;
+ return (buf);
+}
+
+/*
+ * function to determine if media is datastream or mounted
+ * floppy
+ */
+int
+ds_readbuf(char *device)
+{
+ char buf[BLK_SIZE];
+
+ if (ds_fd >= 0)
+ (void) close(ds_fd);
+ if ((ds_fd = open(device, O_RDONLY)) >= 0 &&
+ read(ds_fd, buf, BLK_SIZE) == BLK_SIZE &&
+ strncmp(buf, HDR_PREFIX, 20) == 0) {
+ if ((ds_header = (char *)calloc(BLK_SIZE, 1)) == NULL) {
+ progerr(pkg_gt(ERR_UNPACK));
+ logerr(pkg_gt(MSG_MEM));
+ (void) ds_close(0);
+ return (0);
+ }
+ memcpy(ds_header, buf, BLK_SIZE);
+ ds_headsize = BLK_SIZE;
+
+ if (ds_ginit(device) < 0) {
+ progerr(pkg_gt(ERR_UNPACK));
+ logerr(pkg_gt(MSG_OPEN), device, errno);
+ (void) ds_close(0);
+ return (0);
+ }
+ return (1);
+ } else if (ds_fd >= 0) {
+ (void) close(ds_fd);
+ ds_fd = -1;
+ }
+ return (0);
+}
+
+/*
+ * Determine how many additional volumes are needed for current package.
+ * Note: a 0 will occur as first volume number when the package begins
+ * on the next volume.
+ */
+static int
+ds_volsum(struct dstoc *toc)
+{
+ int curpartcnt, volcnt;
+ char volnos[128], tmpvol[128];
+ if (toc->volnos[0]) {
+ int index, sum;
+ sscanf(toc->volnos, "%d %[ 0-9]", &curpartcnt, volnos);
+ volcnt = 0;
+ sum = curpartcnt;
+ while (sum < toc->nparts && sscanf(volnos, "%d %[ 0-9]",
+ &index, tmpvol) >= 1) {
+ (void) strcpy(volnos, tmpvol);
+ volcnt++;
+ sum += index;
+ }
+ /* side effect - set number of parts read on current volume */
+ ds_volpart = index;
+ return (volcnt);
+ }
+ ds_volpart += toc->nparts;
+ return (0);
+}
+
+/* initialize ds_curpartcnt and ds_volnos */
+static void
+ds_pkginit(void)
+{
+ if (ds_toc->volnos[0])
+ sscanf(ds_toc->volnos, "%d %[ 0-9]", &ds_curpartcnt, ds_volnos);
+ else
+ ds_curpartcnt = -1;
+}
+
+/*
+ * functions to pass current package info to exec'ed program
+ */
+void
+ds_putinfo(char *buf)
+{
+ (void) sprintf(buf, "%d %d %d %d %d %d %d %d %d %d %s",
+ ds_fd, ds_realfd, ds_volcnt, ds_volno, ds_totread, ds_volpart,
+ ds_skippart, ds_bufsize, ds_toc->nparts, ds_toc->maxsiz,
+ ds_toc->volnos);
+}
+
+int
+ds_getinfo(char *string)
+{
+ ds_toc = (struct dstoc *)calloc(1, sizeof (struct dstoc));
+ (void) sscanf(string, "%d %d %d %d %d %d %d %d %d %d %[ 0-9]",
+ &ds_fd, &ds_realfd, &ds_volcnt, &ds_volno, &ds_totread,
+ &ds_volpart, &ds_skippart, &ds_bufsize, &ds_toc->nparts,
+ &ds_toc->maxsiz, ds_toc->volnos);
+ ds_pkginit();
+ return (ds_toc->nparts);
+}
+
+/*
+ * Return true if the file descriptor (ds_fd) is open on the package stream.
+ */
+boolean_t
+ds_fd_open(void)
+{
+ return (ds_fd >= 0 ? B_TRUE : B_FALSE);
+}
+
+/*
+ * Read the source device. Acquire the header data and check it for validity.
+ */
+int
+ds_init(char *device, char **pkg, char *norewind)
+{
+ struct dstoc *tail, *toc_pt;
+ char *ret;
+ char cmd[CMDSIZ];
+ char line[LSIZE+1];
+ int i, n, count = 0, header_size = BLK_SIZE;
+
+ if (!ds_header) { /* If the header hasn't been read yet */
+ if (ds_fd >= 0)
+ (void) ds_close(0);
+
+ /* always start with rewind device */
+ if ((ds_fd = open(device, O_RDONLY)) < 0) {
+ progerr(pkg_gt(ERR_UNPACK));
+ logerr(pkg_gt(MSG_OPEN), device, errno);
+ return (-1);
+ }
+
+ /* allocate room for the header equivalent to a block */
+ if ((ds_header = (char *)calloc(BLK_SIZE, 1)) == NULL) {
+ progerr(pkg_gt(ERR_UNPACK));
+ logerr(pkg_gt(MSG_MEM));
+ return (-1);
+ }
+
+ /* initialize the device */
+ if (ds_ginit(device) < 0) {
+ (void) ds_close(0);
+ progerr(pkg_gt(ERR_UNPACK));
+ logerr(pkg_gt(MSG_OPEN), device, errno);
+ return (-1);
+ }
+
+ /* read a logical block from the source device */
+ if (read(ds_fd, ds_header, BLK_SIZE) != BLK_SIZE) {
+ rpterr();
+ progerr(pkg_gt(ERR_UNPACK));
+ logerr(pkg_gt(MSG_TOC));
+ (void) ds_close(0);
+ return (-1);
+ }
+
+ /*
+ * This loop scans the medium for the start of the header.
+ * If the above read worked, we skip this. If it did't, this
+ * loop will retry the read ten times looking for the header
+ * marker string.
+ */
+ while (strncmp(ds_header, HDR_PREFIX, 20) != 0) {
+ /* only ten tries iff the device rewinds */
+ if (!norewind || count++ > 10) {
+ progerr(pkg_gt(ERR_UNPACK));
+ logerr(pkg_gt(MSG_TOC));
+ (void) ds_close(0);
+ return (-1);
+ }
+
+ /* read through to the last block */
+ if (count > 1)
+ while (read(ds_fd, ds_header, BLK_SIZE) > 0)
+ ;
+
+ /* then close the device */
+ (void) ds_close(0);
+
+ /* and reopen it */
+ if ((ds_fd = open(norewind, O_RDONLY)) < 0) {
+ progerr(pkg_gt(ERR_UNPACK));
+ logerr(pkg_gt(MSG_OPEN), device, errno);
+ (void) free(ds_header);
+ return (-1);
+ }
+
+ /* initialize the device */
+ if (ds_ginit(device) < 0) {
+ (void) ds_close(0);
+ progerr(pkg_gt(ERR_UNPACK));
+ logerr(pkg_gt(MSG_OPEN), device, errno);
+ return (-1);
+ }
+
+ /* read the block again */
+ if (read(ds_fd, ds_header, BLK_SIZE) != BLK_SIZE) {
+ rpterr();
+ progerr(pkg_gt(ERR_UNPACK));
+ logerr(pkg_gt(MSG_TOC));
+ (void) ds_close(0);
+ return (-1);
+ }
+ }
+
+ /* Now keep scanning until the whole header is in place. */
+ while (strstr(ds_header, HDR_SUFFIX) == NULL) {
+ /* We need a bigger buffer */
+ if ((ds_header = (char *)realloc(ds_header,
+ header_size + BLK_SIZE)) == NULL) {
+ progerr(pkg_gt(ERR_UNPACK));
+ logerr(pkg_gt(MSG_MEM));
+ (void) ds_close(0);
+ return (1);
+ }
+
+ /* clear the new memory */
+ (void) memset(ds_header + header_size, '\0',
+ BLK_SIZE);
+
+
+ /* read a logical block from the source device */
+ if (read(ds_fd, ds_header + header_size, BLK_SIZE) !=
+ BLK_SIZE) {
+ rpterr();
+ progerr(pkg_gt(ERR_UNPACK));
+ logerr(pkg_gt(MSG_TOC));
+ (void) ds_close(0);
+ return (-1);
+ } else
+ header_size += BLK_SIZE; /* new size */
+ }
+
+ /*
+ * remember rewind device for ds_close to rewind at
+ * close
+ */
+ if (count >= 1)
+ ds_device = device;
+ ds_headsize = header_size;
+
+ }
+
+ pds_header = ds_header;
+
+ /* save raw copy of header for later use in BIO_dump_header */
+ if ((ds_header_raw = (char *)malloc(header_size)) == NULL) {
+ progerr(pkg_gt(ERR_UNPACK));
+ logerr(pkg_gt(MSG_MEM));
+ (void) ds_close(0);
+ return (1);
+ }
+ memcpy(ds_header_raw, ds_header, header_size);
+
+ /* read datastream table of contents */
+ ds_head = tail = (struct dstoc *)0;
+ ds_volcnt = 1;
+
+ while (ret = ds_gets(line, LSIZE)) {
+ if (strcmp(line, HDR_SUFFIX) == 0)
+ break;
+ if (!line[0] || line[0] == '#')
+ continue;
+ toc_pt = (struct dstoc *)calloc(1, sizeof (struct dstoc));
+ if (!toc_pt) {
+ progerr(pkg_gt(ERR_UNPACK));
+ logerr(pkg_gt(MSG_MEM));
+ ecleanup();
+ (void) free(ds_header);
+ return (-1);
+ }
+ if (sscanf(line, "%s %d %d %[ 0-9]", toc_pt->pkg,
+ &toc_pt->nparts, &toc_pt->maxsiz, toc_pt->volnos) < 3) {
+ progerr(pkg_gt(ERR_UNPACK));
+ logerr(pkg_gt(MSG_TOC));
+ free(toc_pt);
+ (void) free(ds_header);
+ ecleanup();
+ return (-1);
+ }
+ if (tail) {
+ tail->next = toc_pt;
+ tail = toc_pt;
+ } else
+ ds_head = tail = toc_pt;
+ ds_volcnt += ds_volsum(toc_pt);
+ }
+ if (!ret) {
+ progerr(pkg_gt(ERR_UNPACK));
+ logerr(pkg_gt(MSG_TOC));
+ (void) free(ds_header);
+ return (-1);
+ }
+ sighold(SIGINT);
+ sigrelse(SIGINT);
+ if (!ds_head) {
+ progerr(pkg_gt(ERR_UNPACK));
+ logerr(pkg_gt(MSG_EMPTY));
+ (void) free(ds_header);
+ return (-1);
+ }
+ /* this could break, thanks to cpio command limit */
+#ifndef SUNOS41
+ (void) sprintf(cmd, "%s -icdumD -C %d", CPIOPROC, (int)BLK_SIZE);
+#else
+ (void) sprintf(cmd, "%s -icdum -C %d", CPIOPROC, (int)BLK_SIZE);
+#endif
+ n = 0;
+ for (i = 0; pkg[i]; i++) {
+ if (strcmp(pkg[i], "all") == 0)
+ continue;
+ if (n == 0) {
+ strcat(cmd, " ");
+ n = 1;
+ }
+ strlcat(cmd, pkg[i], CMDSIZ);
+ strlcat(cmd, "'/*' ", CMDSIZ);
+
+ /* extract signature too, if present. */
+ strlcat(cmd, SIGNATURE_FILENAME, CMDSIZ);
+ strlcat(cmd, " ", CMDSIZ);
+ }
+
+ /*
+ * if we are extracting all packages (pkgs == NULL),
+ * signature will automatically be extracted
+ */
+ if (n = esystem(cmd, ds_fd, -1)) {
+ rpterr();
+ progerr(pkg_gt(ERR_UNPACK));
+ logerr(pkg_gt(MSG_CMDFAIL), cmd, n);
+ (void) free(ds_header);
+ return (-1);
+ }
+
+ ds_toc = ds_head;
+ ds_totread = 0;
+ ds_volno = 1;
+ return (0);
+}
+
+int
+ds_findpkg(char *device, char *pkg)
+{
+ char *pkglist[2];
+ int nskip, ods_volpart;
+
+ if (ds_head == NULL) {
+ pkglist[0] = pkg;
+ pkglist[1] = NULL;
+ if (ds_init(device, pkglist, NULL))
+ return (-1);
+ }
+
+ if (!pkg || pkgnmchk(pkg, "all", 0)) {
+ progerr(pkg_gt(ERR_UNPACK));
+ logerr(pkg_gt(MSG_PKGNAME));
+ return (-1);
+ }
+
+ nskip = 0;
+ ds_volno = 1;
+ ds_volpart = 0;
+ ds_toc = ds_head;
+ while (ds_toc) {
+ if (strcmp(ds_toc->pkg, pkg) == 0)
+ break;
+ nskip += ds_toc->nparts;
+ ds_volno += ds_volsum(ds_toc);
+ ds_toc = ds_toc->next;
+ }
+ if (!ds_toc) {
+ progerr(pkg_gt(ERR_UNPACK));
+ logerr(pkg_gt(MSG_NOPKG), pkg);
+ return (-1);
+ }
+
+ ds_pkginit();
+ ds_skippart = 0;
+ if (ds_curpartcnt > 0) {
+ ods_volpart = ds_volpart;
+ /*
+ * skip past archives belonging to last package on current
+ * volume
+ */
+ if (ds_volpart > 0 && ds_getnextvol(device))
+ return (-1);
+ ds_totread = nskip - ods_volpart;
+ if (ds_skip(device, ods_volpart))
+ return (-1);
+ } else if (ds_curpartcnt < 0) {
+ if (ds_skip(device, nskip - ds_totread))
+ return (-1);
+ } else
+ ds_totread = nskip;
+ ds_read = 0;
+ return (ds_nparts);
+}
+
+/*
+ * Get datastream part
+ * Call for first part should be preceded by
+ * call to ds_findpkg
+ */
+
+int
+ds_getpkg(char *device, int n, char *dstdir)
+{
+ struct statvfs64 svfsb;
+ u_longlong_t free_blocks;
+
+ if (ds_read >= ds_nparts)
+ return (2);
+
+ if (ds_read == n)
+ return (0);
+ else if ((ds_read > n) || (n > ds_nparts))
+ return (2);
+
+ if (ds_maxsiz > 0) {
+ if (statvfs64(".", &svfsb)) {
+ progerr(pkg_gt(ERR_UNPACK));
+ logerr(pkg_gt(MSG_STATFS), errno);
+ return (-1);
+ }
+#ifdef SUNOS41
+ free_blocks = svfsb.f_bfree * howmany(svfsb.f_bsize, DEV_BSIZE);
+#else /* !SUNOS41 */
+ free_blocks = (((long)svfsb.f_frsize > 0) ?
+ howmany(svfsb.f_frsize, DEV_BSIZE) :
+ howmany(svfsb.f_bsize, DEV_BSIZE)) * svfsb.f_bfree;
+#endif /* SUNOS41 */
+ if ((ds_maxsiz + 50) > free_blocks) {
+ progerr(pkg_gt(ERR_UNPACK));
+ logerr(pkg_gt(MSG_NOSPACE), ds_maxsiz+50, free_blocks);
+ return (-1);
+ }
+ }
+ return (ds_next(device, dstdir));
+}
+
+static int
+ds_getnextvol(char *device)
+{
+ char prompt[128];
+ int n;
+
+ if (ds_close(0))
+ return (-1);
+ (void) sprintf(prompt,
+ pkg_gt("Insert %%v %d of %d into %%p"),
+ ds_volno, ds_volcnt);
+ if (n = getvol(device, NULL, NULL, prompt))
+ return (n);
+ if ((ds_fd = open(device, O_RDONLY)) < 0)
+ return (-1);
+ if (ds_ginit(device) < 0) {
+ (void) ds_close(0);
+ return (-1);
+ }
+ ds_volpart = 0;
+ return (0);
+}
+
+/*
+ * called by ds_findpkg to skip past archives for unwanted packages
+ * in current volume
+ */
+static int
+ds_skip(char *device, int nskip)
+{
+ char cmd[CMDSIZ];
+ int n, onskip = nskip;
+
+ while (nskip--) {
+ /* skip this one */
+#ifndef SUNOS41
+ (void) sprintf(cmd, "%s -ictD -C %d > /dev/null",
+#else
+ (void) sprintf(cmd, "%s -ict -C %d > /dev/null",
+#endif
+ CPIOPROC, (int)BLK_SIZE);
+ if (n = esystem(cmd, ds_fd, -1)) {
+ rpterr();
+ progerr(pkg_gt(ERR_UNPACK));
+ logerr(pkg_gt(MSG_CMDFAIL), cmd, n);
+ nskip = onskip;
+ if (ds_volno == 1 || ds_volpart > 0)
+ return (n);
+ if (n = ds_getnextvol(device))
+ return (n);
+ }
+ }
+ ds_totread += onskip;
+ ds_volpart = onskip;
+ ds_skippart = onskip;
+ return (0);
+}
+
+/* skip to end of package if necessary */
+void
+ds_skiptoend(char *device)
+{
+ if (ds_read < ds_nparts && ds_curpartcnt < 0)
+ (void) ds_skip(device, ds_nparts - ds_read);
+}
+
+int
+ds_next(char *device, char *instdir)
+{
+ char cmd[CMDSIZ], tmpvol[128];
+ int nparts, n, index;
+
+ /*CONSTCOND*/
+ while (1) {
+ if (ds_read + 1 > ds_curpartcnt && ds_curpartcnt >= 0) {
+ ds_volno++;
+ if (n = ds_getnextvol(device))
+ return (n);
+ (void) sscanf(ds_volnos, "%d %[ 0-9]", &index, tmpvol);
+ (void) strcpy(ds_volnos, tmpvol);
+ ds_curpartcnt += index;
+ }
+#ifndef SUNOS41
+ (void) sprintf(cmd, "%s -icdumD -C %d",
+#else
+ (void) sprintf(cmd, "%s -icdum -C %d",
+#endif
+ CPIOPROC, (int)BLK_SIZE);
+ if (n = esystem(cmd, ds_fd, -1)) {
+ rpterr();
+ progerr(pkg_gt(ERR_UNPACK));
+ logerr(pkg_gt(MSG_CMDFAIL), cmd, n);
+ }
+ if (ds_read == 0)
+ nparts = 0;
+ else
+ nparts = ds_toc->nparts;
+ if (n || (n = ckvolseq(instdir, ds_read + 1, nparts))) {
+ if (ds_volno == 1 || ds_volpart > ds_skippart)
+ return (-1);
+
+ if (n = ds_getnextvol(device))
+ return (n);
+ continue;
+ }
+ ds_read++;
+ ds_totread++;
+ ds_volpart++;
+
+ return (0);
+ }
+ /*NOTREACHED*/
+}
+
+/*
+ * Name: BIO_ds_dump
+ * Description: Dumps all data from the static 'ds_fd' file handle into
+ * the supplied BIO.
+ *
+ * Arguments: err - where to record any errors.
+ * device - Description of device being dumped into,
+ * for error reporting
+ * bio - BIO object to dump data into
+ *
+ * Returns : zero - successfully dumped all data to EOF
+ * non-zero - some failure occurred.
+ */
+int
+BIO_ds_dump(PKG_ERR *err, char *device, BIO *bio)
+{
+ int amtread;
+ char readbuf[BLK_SIZE];
+
+ /*
+ * note this will read to the end of the device, so it won't
+ * work for character devices since we don't know when the
+ * end of the CPIO archive is
+ */
+ while ((amtread = read(ds_fd, readbuf, BLK_SIZE)) != 0) {
+ if (BIO_write(bio, readbuf, amtread) != amtread) {
+ pkgerr_add(err, PKGERR_WRITE, ERR_WRITE, device,
+ ERR_error_string(ERR_get_error(), NULL));
+ return (1);
+ }
+ }
+
+ return (0);
+ /*NOTREACHED*/
+}
+
+
+/*
+ * Name: BIO_ds_dump_header
+ * Description: Dumps all ds_headsize bytes from the
+ * static 'ds_header_raw' character array
+ * to the supplied BIO.
+ *
+ * Arguments: err - where to record any errors.
+ * bio - BIO object to dump data into
+ *
+ * Returns : zero - successfully dumped all raw
+ * header characters
+ * non-zero - some failure occurred.
+ */
+int
+BIO_ds_dump_header(PKG_ERR *err, BIO *bio)
+{
+
+ char zeros[BLK_SIZE];
+
+ memset(zeros, 0, BLK_SIZE);
+
+ if (BIO_write(bio, ds_header_raw, ds_headsize) != ds_headsize) {
+ pkgerr_add(err, PKGERR_WRITE, ERR_WRITE, "bio",
+ ERR_error_string(ERR_get_error(), NULL));
+ return (1);
+ }
+
+ return (0);
+}
+
+/*
+ * ds_ginit: Determine the device being accessed, set the buffer size,
+ * and perform any device specific initialization. For the 3B2,
+ * a device with major number of 17 (0x11) is an internal hard disk,
+ * unless the minor number is 128 (0x80) in which case it is an internal
+ * floppy disk. Otherwise, get the system configuration
+ * table and check it by comparing slot numbers to major numbers.
+ * For the special case of the 3B2 CTC several unusual things must be done.
+ * To enable
+ * streaming mode on the CTC, the file descriptor must be closed, re-opened
+ * (with O_RDWR and O_CTSPECIAL flags set), the STREAMON ioctl(2) command
+ * issued, and the file descriptor re-re-opened either read-only or write_only.
+ */
+
+int
+ds_ginit(char *device)
+{
+#ifdef u3b2
+ major_t maj;
+ minor_t min;
+ int nflag, i, count, size;
+ struct s3bconf *buffer;
+ struct s3bc *table;
+ struct stat st_buf;
+ int devtype;
+ char buf[BLK_SIZE];
+ int fd2, fd;
+#endif /* u3b2 */
+ int oflag;
+ char *pbufsize, cmd[CMDSIZ];
+ int fd2, fd;
+
+ if ((pbufsize = devattr(device, "bufsize")) != NULL) {
+ ds_bufsize = atoi(pbufsize);
+ (void) free(pbufsize);
+ } else
+ ds_bufsize = BLK_SIZE;
+ oflag = fcntl(ds_fd, F_GETFL, 0);
+#ifdef u3b2
+ devtype = G_NO_DEV;
+ if (fstat(ds_fd, &st_buf) == -1)
+ return (-1);
+ if (!S_ISCHR(st_buf.st_mode) && !S_ISBLK(st_buf.st_mode))
+ goto lab;
+
+ /*
+ * We'll have to add a remote attribute to stat but this should
+ * work for now.
+ */
+ else if (st_buf.st_dev & 0x8000) /* if remote rdev */
+ goto lab;
+
+ maj = major(st_buf.st_rdev);
+ min = minor(st_buf.st_rdev);
+ if (maj == 0x11) { /* internal hard or floppy disk */
+ if (min & 0x80)
+ devtype = G_3B2_FD; /* internal floppy disk */
+ else
+ devtype = G_3B2_HD; /* internal hard disk */
+ } else {
+ if (sys3b(S3BCONF, (struct s3bconf *)&count, sizeof (count)) ==
+ -1)
+ return (-1);
+ size = sizeof (int) + (count * sizeof (struct s3bconf));
+ buffer = (struct s3bconf *)malloc((unsigned)size);
+ if (sys3b(S3BCONF, buffer, size) == -1)
+ return (-1);
+ table = (struct s3bc *)((char *)buffer + sizeof (int));
+ for (i = 0; i < count; i++) {
+ if (maj == (int)table->board) {
+ if (strncmp(table->name, "CTC", 3) == 0) {
+ devtype = G_3B2_CTC;
+ break;
+ } else if (strncmp(table->name, "TAPE", 4)
+ == 0) {
+ devtype = G_TAPE;
+ break;
+ }
+ /* other possible devices can go here */
+ }
+ table++;
+ }
+ }
+ switch (devtype) {
+ case G_3B2_CTC: /* do special CTC initialization */
+ ds_bufsize = pbufsize ? ds_bufsize : 15872;
+ if (fstat(ds_fd, &orig_st_buf) < 0) {
+ ds_bufsize = -1;
+ break;
+ }
+ nflag = (O_RDWR | O_CTSPECIAL);
+ (void) close(ds_fd);
+ if ((ds_fd = open(device, nflag, 0666)) != -1) {
+ if (ioctl(ds_fd, STREAMON) != -1) {
+ (void) close(ds_fd);
+ nflag = (oflag == O_WRONLY) ?
+ O_WRONLY : O_RDONLY;
+ if ((ds_fd =
+ open(device, nflag, 0666)) == -1) {
+ rpterr();
+ progerr(
+ pkg_gt(ERR_TRANSFER));
+ logerr(pkg_gt(MSG_OPEN),
+ device, errno);
+ return (-1);
+ }
+ ds_bufsize = 15872;
+ }
+ } else
+ ds_bufsize = -1;
+ if (oflag == O_RDONLY && ds_header && ds_totread == 0)
+ /* Have already read in first block of header */
+ read(ds_fd, buf, BLK_SIZE);
+ ds_ctcflg = 1;
+
+ break;
+ case G_NO_DEV:
+ case G_3B2_HD:
+ case G_3B2_FD:
+ case G_TAPE:
+ case G_SCSI_HD: /* not developed yet */
+ case G_SCSI_FD:
+ case G_SCSI_9T:
+ case G_SCSI_Q24:
+ case G_SCSI_Q120:
+ case G_386_HD:
+ case G_386_FD:
+ case G_386_Q24:
+ ds_bufsize = pbufsize ? ds_bufsize : BLK_SIZE;
+ break;
+ default:
+ ds_bufsize = -1;
+ errno = ENODEV;
+ } /* devtype */
+lab:
+#endif /* u3b2 */
+ if (ds_bufsize > BLK_SIZE) {
+ if (oflag & O_WRONLY)
+ fd = 1;
+ else
+ fd = 0;
+ fd2 = fcntl(fd, F_DUPFD, fd);
+ (void) close(fd);
+ fcntl(ds_fd, F_DUPFD, fd);
+ if (fd)
+ sprintf(cmd, "%s obs=%d 2>/dev/null", DDPROC,
+ ds_bufsize);
+ else
+ sprintf(cmd, "%s ibs=%d 2>/dev/null", DDPROC,
+ ds_bufsize);
+ if ((ds_pp = popen(cmd, fd ? "w" : "r")) == NULL) {
+ progerr(pkg_gt(ERR_TRANSFER));
+ logerr(pkg_gt(MSG_POPEN), cmd, errno);
+ return (-1);
+ }
+ (void) close(fd);
+ fcntl(fd2, F_DUPFD, fd);
+ (void) close(fd2);
+ ds_realfd = ds_fd;
+ ds_fd = fileno(ds_pp);
+ }
+ return (ds_bufsize);
+}
+
+int
+ds_close(int pkgendflg)
+{
+#ifdef u3b2
+ int cnt, mode;
+ char *ptr;
+ struct stat statbuf;
+#endif /* u3b2 */
+ int n, ret = 0;
+
+#ifdef u3b2
+ if (ds_pp && ds_ctcflg) {
+ ds_ctcflg = 0;
+ if ((mode = fcntl(ds_realfd, F_GETFL, 0)) < 0) {
+ ret = -1;
+ } else if (mode & O_WRONLY) {
+ /*
+ * pipe to dd write process,
+ * make sure one more buffer
+ * gets written out
+ */
+ if ((ptr = calloc(BLK_SIZE, 1)) == NULL) {
+ ret = -1;
+ /* pad to bufsize */
+ } else {
+ cnt = ds_bufsize;
+ while (cnt > 0) {
+ if ((n = write(ds_fd, ptr,
+ BLK_SIZE)) < 0) {
+ ret = -1;
+ break;
+ }
+ cnt -= n;
+ }
+ (void) free(ptr);
+ }
+ }
+ }
+#endif
+ if (pkgendflg) {
+ if (ds_header)
+ (void) free(ds_header);
+ ds_header = (char *)NULL;
+ ds_totread = 0;
+ }
+
+ if (ds_pp) {
+ (void) pclose(ds_pp);
+ ds_pp = 0;
+ (void) close(ds_realfd);
+ ds_realfd = -1;
+ ds_fd = -1;
+ } else if (ds_fd >= 0) {
+ (void) close(ds_fd);
+ ds_fd = -1;
+ }
+
+ if (ds_device) {
+ /* rewind device */
+ if ((n = open(ds_device, 0)) >= 0)
+ (void) close(n);
+ ds_device = NULL;
+ }
+ return (ret);
+}
diff --git a/usr/src/lib/libpkg/common/fmkdir.c b/usr/src/lib/libpkg/common/fmkdir.c
new file mode 100644
index 0000000000..a56933bc3d
--- /dev/null
+++ b/usr/src/lib/libpkg/common/fmkdir.c
@@ -0,0 +1,65 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include "pkglib.h"
+
+/*
+ * Name: fmkdir
+ * Description: force the creation of a directory, even if the current
+ * node exists and is not a directory
+ * Arguments: a_path - pointer to string representing the path to the
+ * directory to create
+ * a_mode - mode(2) bits to set the path to if created
+ * returns: 0 - directory created
+ * 1 - could not remove existing non-directory node
+ * 2 - could not create specified new directory
+ */
+int
+fmkdir(char *a_path, int a_mode)
+{
+ if (access(a_path, F_OK) == 0) {
+ if (rrmdir(a_path) != 0) {
+ return (1);
+ }
+ }
+
+ if (mkdir(a_path, a_mode) != 0) {
+ return (2);
+ }
+
+ return (0);
+}
diff --git a/usr/src/lib/libpkg/common/gpkglist.c b/usr/src/lib/libpkg/common/gpkglist.c
new file mode 100644
index 0000000000..9257c6afcb
--- /dev/null
+++ b/usr/src/lib/libpkg/common/gpkglist.c
@@ -0,0 +1,359 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
+/* All Rights Reserved */
+
+
+
+#include <stdio.h>
+#include <ctype.h>
+#include <string.h>
+#include <signal.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <valtools.h>
+#include "pkginfo.h"
+#include "pkglib.h"
+#include "pkglibmsgs.h"
+#include "pkgstrct.h"
+#include "pkglocale.h"
+
+extern char *pkgdir; /* WHERE? */
+
+/* libadm.a */
+extern CKMENU *allocmenu(char *label, int attr);
+extern int ckitem(CKMENU *menup, char *item[], short max, char *defstr,
+ char *error, char *help, char *prompt);
+extern int pkgnmchk(register char *pkg, register char *spec,
+ int presvr4flg);
+extern int fpkginfo(struct pkginfo *info, char *pkginst);
+extern char *fpkginst(char *pkg, ...);
+extern int setinvis(CKMENU *menup, char *choice);
+extern int setitem(CKMENU *menup, char *choice);
+
+#define CMDSIZ 512
+#define LSIZE 256
+#define MAXSIZE 128
+#define MALLOCSIZ 128
+#define MAX_CAT_ARGS 64
+#define MAX_CAT_LEN 16
+
+static int cont_in_list = 0; /* live continuation */
+static char cont_keyword[PKGSIZ+1]; /* the continuation keyword */
+
+/*
+ * Allocate memory for the next package name. This function attempts the
+ * allocation and if that succeeds, returns a pointer to the new memory
+ * location and increments "n". Otherwise, it returens NULL and n is
+ * unchanged.
+ */
+static char **
+next_n(int *n, char **nwpkg)
+{
+ int loc_n = *n;
+
+ if ((++loc_n % MALLOCSIZ) == 0) {
+ nwpkg = (char **)realloc(nwpkg,
+ (loc_n+MALLOCSIZ) * sizeof (char **));
+ if (nwpkg == NULL) {
+ progerr(pkg_gt(ERR_MEMORY), errno);
+ errno = ENOMEM;
+ return (NULL);
+ }
+ }
+
+ *n = loc_n;
+ return (nwpkg);
+}
+
+/*
+ * This informs gpkglist() to put a keyword at the head of the pkglist. This
+ * was originally intended for live continue, but it may have other
+ * applications as well.
+ */
+void
+pkglist_cont(char *keyword)
+{
+ cont_in_list = 1;
+ (void) strncpy(cont_keyword, keyword, PKGSIZ);
+}
+
+/*
+ * This function constructs the list of packages that the user wants managed.
+ * It may be a list on the command line, it may be some or all of the
+ * packages in a directory or it may be a continuation from a previous
+ * dryrun. It may also be a list of pkgs gathered from the CATEGORY parameter
+ * in a spooled or installed pkginfo file.
+ */
+char **
+gpkglist(char *dir, char **pkg, char **catg)
+{
+ struct _choice_ *chp;
+ struct pkginfo info;
+ char *inst;
+ CKMENU *menup;
+ char temp[LSIZE];
+ char *savedir, **nwpkg;
+ int i, n;
+
+ savedir = pkgdir;
+ pkgdir = dir;
+
+ info.pkginst = NULL; /* initialize for memory handling */
+ if (pkginfo(&info, "all", NULL, NULL)) {
+ errno = ENOPKG; /* contains no valid packages */
+ pkgdir = savedir;
+ return (NULL);
+ }
+
+ /*
+ * If no explicit list was provided and this is not a continuation
+ * (implying a certain level of direction on the caller's part)
+ * present a menu of available packages for installation.
+ */
+ if (pkg[0] == NULL && !cont_in_list) {
+ menup = allocmenu(pkg_gt(HEADER), CKALPHA);
+ if (setinvis(menup, "all")) {
+ errno = EFAULT;
+ return (NULL);
+ }
+ do {
+ /* bug id 1087404 */
+ if (!info.pkginst || !info.name || !info.arch ||
+ !info.version)
+ continue;
+ (void) sprintf(temp, "%s %s\n(%s) %s", info.pkginst,
+ info.name, info.arch, info.version);
+ if (setitem(menup, temp)) {
+ errno = EFAULT;
+ return (NULL);
+ }
+ } while (pkginfo(&info, "all", NULL, NULL) == 0);
+ /* clear memory usage by pkginfo */
+ (void) pkginfo(&info, NULL, NULL, NULL);
+ pkgdir = savedir; /* restore pkgdir to orig value */
+
+ nwpkg = (char **)calloc(MALLOCSIZ, sizeof (char **));
+ n = ckitem(menup, nwpkg, MALLOCSIZ, "all", NULL,
+ pkg_gt(HELP), pkg_gt(PROMPT));
+ if (n) {
+ free(nwpkg);
+ errno = ((n == 3) ? EINTR : EFAULT);
+ pkgdir = savedir;
+ return (NULL);
+ }
+ if (strcmp(nwpkg[0], "all") == 0) {
+ chp = menup->choice;
+ for (n = 0; chp; /* void */) {
+ nwpkg[n] = strdup(chp->token);
+ nwpkg = next_n(&n, nwpkg);
+ chp = chp->next;
+ nwpkg[n] = NULL;
+ }
+ } else {
+ for (n = 0; nwpkg[n]; n++)
+ nwpkg[n] = strdup(nwpkg[n]);
+ }
+ (void) setitem(menup, NULL); /* free resources */
+ free(menup);
+ pkgdir = savedir;
+ return (nwpkg);
+ }
+
+ /* clear memory usage by pkginfo */
+ (void) pkginfo(&info, NULL, NULL, NULL);
+
+ nwpkg = (char **)calloc(MALLOCSIZ, sizeof (char **));
+
+ /*
+ * pkg array contains the instance identifiers to
+ * be selected, or possibly wildcard definitions
+ */
+ i = n = 0;
+ do {
+ if (cont_in_list) { /* This is a live continuation. */
+ nwpkg[n] = strdup(cont_keyword);
+ nwpkg = next_n(&n, nwpkg);
+ nwpkg[n] = NULL;
+ cont_in_list = 0; /* handled */
+
+ if (pkg[0] == NULL) { /* It's just a continuation. */
+ break;
+ }
+ } else if (pkgnmchk(pkg[i], "all", 1)) {
+ /* wildcard specification */
+ (void) fpkginst(NULL);
+ inst = fpkginst(pkg[i], NULL, NULL);
+ if (inst == NULL) {
+ progerr(pkg_gt(ERR_NOPKG), pkg[i]);
+ free(nwpkg);
+ nwpkg = NULL;
+ errno = ESRCH;
+ break;
+ }
+ do {
+ if (catg != NULL) {
+ pkginfo(&info, inst, NULL, NULL);
+ if (!is_same_CATEGORY(catg,
+ info.catg))
+ continue;
+ }
+ nwpkg[n] = strdup(inst);
+ nwpkg = next_n(&n, nwpkg);
+ nwpkg[n] = NULL;
+ } while (inst = fpkginst(pkg[i], NULL, NULL));
+ } else {
+ if (fpkginfo(&info, pkg[i])) {
+ progerr(pkg_gt(ERR_NOPKG), pkg[i]);
+ free(nwpkg);
+ nwpkg = NULL;
+ errno = ESRCH;
+ break;
+ }
+ nwpkg[n] = strdup(pkg[i]);
+ nwpkg = next_n(&n, nwpkg);
+ nwpkg[n] = NULL;
+ }
+ } while (pkg[++i]);
+
+ (void) fpkginst(NULL);
+ (void) fpkginfo(&info, NULL);
+ pkgdir = savedir; /* restore pkgdir to orig value */
+
+ if (catg != NULL) {
+ if (nwpkg[0] == NULL) {
+
+ /*
+ * No pkgs in the spooled directory matched the
+ * category specified by the user.
+ */
+
+ free(nwpkg);
+ return (NULL);
+ }
+ }
+ return (nwpkg);
+}
+
+/*
+ * Check category passed in on the command line to see if it is valid.
+ *
+ * returns 0 if the category is valid
+ * returns 1 if the category is invalid
+ */
+
+int
+is_not_valid_category(char **category, char *progname)
+{
+ if (strcasecmp(progname, "pkgrm") == 0) {
+ if (is_same_CATEGORY(category, "system"))
+ return (1);
+ }
+
+ return (0);
+}
+
+/*
+ * Check category length
+ *
+ * returns 0 if the category length is valid
+ * returns 1 if a category has length > 16 chars as defined by the SVr4 ABI
+ */
+
+int
+is_not_valid_length(char **category)
+{
+ int i;
+
+ for (i = 0; category[i] != NULL; i++) {
+ if (strlen(category[i]) > MAX_CAT_LEN)
+ return (1);
+ }
+
+ return (0);
+}
+
+/*
+ * Check category passed in on the command line against the CATEGORY in the
+ * spooled or installed packages pkginfo file.
+ *
+ * returns 0 if categories match
+ * returns 1 if categories don't match
+ */
+
+int
+is_same_CATEGORY(char **category, char *persistent_category)
+{
+ int i, j, n = 0;
+ char *pers_catg, **pers_catgs;
+
+ pers_catg = strdup(persistent_category);
+
+ pers_catgs = (char **)calloc(MAX_CAT_LEN, sizeof (char **));
+
+ pers_catgs[n++] = strtok(pers_catg, " \t\n, ");
+ while (pers_catgs[n] = strtok(NULL, " \t\n, "))
+ n++;
+
+ for (i = 0; category[i] != NULL; i++) {
+ for (j = 0; j < n; j++) {
+ if (strcasecmp(category[i], pers_catgs[j]) == 0) {
+ return (1);
+ }
+ }
+ }
+
+ return (0);
+}
+
+/*
+ * Given a string of categories, construct a null-terminated array of
+ * categories.
+ *
+ * returns the array of categories or NULL
+ */
+
+char **
+get_categories(char *catg_arg)
+{
+ int n = 0;
+ char *tmp_catg;
+ char **catgs;
+
+ tmp_catg = strdup(catg_arg);
+
+ catgs = (char **)calloc(MAX_CAT_LEN, sizeof (char **));
+
+ catgs[n++] = strtok(tmp_catg, " \t\n, ");
+ while (catgs[n] = strtok(NULL, " \t\n, "))
+ n++;
+
+ if (*catgs == NULL)
+ return (NULL);
+ else
+ return (catgs);
+}
diff --git a/usr/src/lib/libpkg/common/gpkgmap.c b/usr/src/lib/libpkg/common/gpkgmap.c
new file mode 100644
index 0000000000..d94b16c4d1
--- /dev/null
+++ b/usr/src/lib/libpkg/common/gpkgmap.c
@@ -0,0 +1,1275 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
+/* All Rights Reserved */
+
+
+
+#include <stdio.h>
+#include <limits.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <ctype.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <errno.h>
+#include "pkgstrct.h"
+#include "pkglib.h"
+#include "pkglibmsgs.h"
+#include "pkglocale.h"
+
+#define ERR_CANT_READ_LCLPATH "unable to read local pathname"
+#define ERR_BAD_VOLUME_NUMBER "bad volume number"
+#define ERR_CANNOT_READ_PATHNAME_FIELD "unable to read pathname field"
+#define ERR_CANNOT_READ_CONTENT_INFO "unable to read content info"
+#define ERR_EXTRA_TOKENS_PRESENT "extra tokens on input line"
+#define ERR_CANNOT_READ_CLASS_TOKEN "unable to read class token"
+#define ERR_BAD_LINK_SPEC "missing or invalid link specification"
+#define ERR_UNKNOWN_FTYPE "unknown ftype"
+#define ERR_NO_LINKSOURCE "no link source specified"
+#define ERR_CANNOT_READ_MM_DEVNUMS "unable to read major/minor "\
+ "device numbers"
+static int eatwhite(FILE *fp);
+static int getend(FILE *fp);
+static int getstr(FILE *fp, char *sep, int n, char *str);
+static int getnum(FILE *fp, int base, long *d, long bad);
+static int getlnum(FILE *fp, int base, fsblkcnt_t *d, long bad);
+static int getvalmode(FILE *fp, mode_t *d, long bad, int map);
+
+static int getendvfp(char **cp);
+static void findendvfp(char **cp);
+static int getstrvfp(char **cp, char *sep, int n, char *str);
+static int getvalmodevfp(char **cp, mode_t *d, long bad, int map);
+int getnumvfp(char **cp, int base, long *d, long bad);
+int getlnumvfp(char **cp, int base, fsblkcnt_t *d, long bad);
+
+static char mypath[PATH_MAX];
+static char mylocal[PATH_MAX];
+static int mapmode = MAPNONE;
+static char *maptype = "";
+static mode_t d_mode = BADMODE;
+static char *d_owner = BADOWNER;
+static char *d_group = BADGROUP;
+
+/*
+ * These determine how gpkgmap() deals with mode, owner and group defaults.
+ * It is assumed that the owner and group arguments represent static fields
+ * which will persist until attrdefault() is called.
+ */
+void
+attrpreset(int mode, char *owner, char *group)
+{
+ d_mode = mode;
+ d_owner = owner;
+ d_group = group;
+}
+
+void
+attrdefault()
+{
+ d_mode = NOMODE;
+ d_owner = NOOWNER;
+ d_group = NOGROUP;
+}
+
+/*
+ * This determines how gpkgmap() deals with environment variables in the
+ * mode, owner and group. Path is evaluated at a higher level based upon
+ * other circumstances.
+ */
+void
+setmapmode(int mode)
+{
+ if (mode >= 0 || mode <= 3) {
+ mapmode = mode;
+ if (mode == MAPBUILD)
+ maptype = " build";
+ else if (mode == MAPINSTALL)
+ maptype = " install";
+ else
+ maptype = "";
+ }
+}
+
+/* This is the external query interface for mapmode. */
+int
+getmapmode(void)
+{
+ return (mapmode);
+}
+
+/*
+ * Unpack the pkgmap or the contents file or whatever file is in that format.
+ * Based upon mapmode, environment parameters will be resolved for mode,
+ * owner and group.
+ */
+
+int
+gpkgmap(struct cfent *ept, FILE *fp)
+{
+ int c;
+ boolean_t first_char = B_TRUE;
+
+ setErrstr(NULL);
+ ept->volno = 0;
+ ept->ftype = BADFTYPE;
+ (void) strcpy(ept->pkg_class, BADCLASS);
+ ept->pkg_class_idx = -1;
+ ept->path = NULL;
+ ept->ainfo.local = NULL;
+ /* default attributes were supplied, so don't reset */
+ ept->ainfo.mode = d_mode;
+ (void) strcpy(ept->ainfo.owner, d_owner);
+ (void) strcpy(ept->ainfo.group, d_group);
+#ifdef SUNOS41
+ ept->ainfo.xmajor = BADMAJOR;
+ ept->ainfo.xminor = BADMINOR;
+#else
+ ept->ainfo.major = BADMAJOR;
+ ept->ainfo.minor = BADMINOR;
+#endif
+ ept->cinfo.cksum = ept->cinfo.modtime = ept->cinfo.size = (-1L);
+
+ ept->npkgs = 0;
+
+ if (!fp)
+ return (-1);
+readline:
+ c = eatwhite(fp);
+
+ /*
+ * If the first character is not a digit, we assume that the
+ * volume number is 1.
+ */
+ if (first_char && !isdigit(c)) {
+ ept->volno = 1;
+ }
+ first_char = B_FALSE;
+
+ switch (c) {
+ case EOF:
+ return (0);
+
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ if (ept->volno) {
+ setErrstr(pkg_gt(ERR_BAD_VOLUME_NUMBER));
+ goto error;
+ }
+ do {
+ ept->volno = (ept->volno*10)+c-'0';
+ c = getc(fp);
+ } while (isdigit(c));
+ if (ept->volno == 0)
+ ept->volno = 1;
+
+ goto readline;
+
+ case ':':
+ case '#':
+ (void) getend(fp);
+ /*FALLTHRU*/
+ case '\n':
+ /*
+ * Since we are going to scan the next line,
+ * we need to reset volume number and first_char.
+ */
+ ept->volno = 0;
+ first_char = B_TRUE;
+ goto readline;
+
+ case 'i':
+ ept->ftype = (char)c;
+ c = eatwhite(fp);
+ /*FALLTHRU*/
+ case '.':
+ case '/':
+ (void) ungetc(c, fp);
+
+ if (getstr(fp, "=", PATH_MAX, mypath)) {
+ setErrstr(pkg_gt(ERR_CANNOT_READ_PATHNAME_FIELD));
+ goto error;
+ }
+ ept->path = mypath;
+ c = getc(fp);
+ if (c == '=') {
+ if (getstr(fp, NULL, PATH_MAX, mylocal)) {
+ setErrstr(pkg_gt(ERR_CANT_READ_LCLPATH));
+ goto error;
+ }
+ ept->ainfo.local = mylocal;
+ } else
+ (void) ungetc(c, fp);
+
+ if (ept->ftype == 'i') {
+ /* content info might exist */
+ if (!getlnum(fp, 10, (fsblkcnt_t *)&ept->cinfo.size,
+ BADCONT) &&
+ (getnum(fp, 10, (long *)&ept->cinfo.cksum,
+ BADCONT) ||
+ getnum(fp, 10, (long *)&ept->cinfo.modtime,
+ BADCONT))) {
+ setErrstr(pkg_gt(ERR_CANNOT_READ_CONTENT_INFO));
+ goto error;
+ }
+ }
+ if (getend(fp)) {
+ setErrstr(pkg_gt(ERR_EXTRA_TOKENS_PRESENT));
+ return (-1);
+ }
+ return (1);
+
+ case '?':
+ case 'f':
+ case 'v':
+ case 'e':
+ case 'l':
+ case 's':
+ case 'p':
+ case 'c':
+ case 'b':
+ case 'd':
+ case 'x':
+ ept->ftype = (char)c;
+ if (getstr(fp, NULL, CLSSIZ, ept->pkg_class)) {
+ setErrstr(pkg_gt(ERR_CANNOT_READ_CLASS_TOKEN));
+ goto error;
+ }
+ if (getstr(fp, "=", PATH_MAX, mypath)) {
+ setErrstr(pkg_gt(ERR_CANNOT_READ_PATHNAME_FIELD));
+ goto error;
+ }
+ ept->path = mypath;
+
+ c = getc(fp);
+ if (c == '=') {
+ /* local path */
+ if (getstr(fp, NULL, PATH_MAX, mylocal)) {
+ if (ept->ftype == 's' || ept->ftype == 'l') {
+ setErrstr(pkg_gt(ERR_READLINK));
+ } else {
+ setErrstr(
+ pkg_gt(ERR_CANT_READ_LCLPATH));
+ }
+ goto error;
+ }
+ ept->ainfo.local = mylocal;
+ } else if (strchr("sl", ept->ftype)) {
+ if ((c != EOF) && (c != '\n'))
+ (void) getend(fp);
+ setErrstr(pkg_gt(ERR_BAD_LINK_SPEC));
+ return (-1);
+ } else
+ (void) ungetc(c, fp);
+ break;
+
+ default:
+ setErrstr(pkg_gt(ERR_UNKNOWN_FTYPE));
+error:
+ (void) getend(fp);
+ return (-1);
+ }
+
+ if (strchr("sl", ept->ftype) && (ept->ainfo.local == NULL)) {
+ setErrstr(pkg_gt(ERR_NO_LINKSOURCE));
+ goto error;
+ }
+
+ if (strchr("cb", ept->ftype)) {
+#ifdef SUNOS41
+ ept->ainfo.xmajor = BADMAJOR;
+ ept->ainfo.xminor = BADMINOR;
+ if (getnum(fp, 10, (long *)&ept->ainfo.xmajor, BADMAJOR) ||
+ getnum(fp, 10, (long *)&ept->ainfo.xminor, BADMINOR))
+#else
+ ept->ainfo.major = BADMAJOR;
+ ept->ainfo.minor = BADMINOR;
+ if (getnum(fp, 10, (long *)&ept->ainfo.major, BADMAJOR) ||
+ getnum(fp, 10, (long *)&ept->ainfo.minor, BADMINOR))
+#endif
+ {
+ setErrstr(pkg_gt(ERR_CANNOT_READ_MM_DEVNUMS));
+ goto error;
+ }
+ }
+
+ /*
+ * Links and information files don't have attributes associated with
+ * them. The following either resolves potential variables or passes
+ * them through. Mode is tested for validity to some degree. BAD???
+ * is returned to indicate that no meaningful mode was provided. A
+ * higher authority will decide if that's OK or not. CUR??? means that
+ * the prototype file specifically requires a wildcard ('?') for
+ * that entry. We issue an error if attributes were entered wrong.
+ * We just return BAD??? if there was no entry at all.
+ */
+ if (strchr("cbdxpfve", ept->ftype)) {
+ int retval;
+
+ if ((retval = getvalmode(fp, &(ept->ainfo.mode), CURMODE,
+ (mapmode != MAPNONE))) == 1)
+ goto end; /* nothing else on the line */
+ else if (retval == 2)
+ goto error; /* mode is too no good */
+
+ /* owner & group should be here */
+ if ((retval = getstr(fp, NULL, ATRSIZ,
+ ept->ainfo.owner)) == 1)
+ goto end; /* no owner or group - warning */
+ if (retval == -1) {
+ setErrstr(pkg_gt(ERR_OWNTOOLONG));
+ goto error;
+ }
+
+ if ((retval = getstr(fp, NULL, ATRSIZ,
+ ept->ainfo.group)) == 1)
+ goto end; /* no group - warning */
+ if (retval == -1) {
+ setErrstr(pkg_gt(ERR_GRPTOOLONG));
+ goto error;
+ }
+
+ /* Resolve the parameters if required. */
+ if (mapmode != MAPNONE) {
+ if (mapvar(mapmode, ept->ainfo.owner)) {
+ (void) snprintf(getErrbufAddr(),
+ getErrbufSize(),
+ pkg_gt(ERR_NOVAR),
+ maptype, ept->ainfo.owner);
+ setErrstr(getErrbufAddr());
+ goto error;
+ }
+ if (mapvar(mapmode, ept->ainfo.group)) {
+ (void) snprintf(getErrbufAddr(),
+ getErrbufSize(), pkg_gt(ERR_NOVAR),
+ maptype, ept->ainfo.group);
+ setErrstr(getErrbufAddr());
+ goto error;
+ }
+ }
+ }
+
+ if (strchr("ifve", ept->ftype)) {
+ /* look for content description */
+ if (!getlnum(fp, 10, (fsblkcnt_t *)&ept->cinfo.size, BADCONT) &&
+ (getnum(fp, 10, (long *)&ept->cinfo.cksum, BADCONT) ||
+ getnum(fp, 10, (long *)&ept->cinfo.modtime, BADCONT))) {
+ setErrstr(pkg_gt(ERR_CANNOT_READ_CONTENT_INFO));
+ goto error;
+ }
+ }
+
+ if (ept->ftype == 'i')
+ goto end;
+
+end:
+ if (getend(fp) && ept->pinfo) {
+ setErrstr(pkg_gt(ERR_EXTRA_TOKENS_PRESENT));
+ return (-1);
+ }
+
+done:
+ return (1);
+}
+
+/*
+ * Get and validate the mode attribute. This returns an error if
+ * 1. the mode string is too long
+ * 2. the mode string includes alpha characters
+ * 3. the mode string is not octal
+ * 4. mode string is an install parameter
+ * 5. mode is an unresolved build parameter and MAPBUILD is
+ * in effect.
+ * If the mode is a build parameter, it is
+ * 1. returned as is if MAPNONE is in effect
+ * 2. evaluated if MAPBUILD is in effect
+ *
+ * NOTE : We use "mapmode!=MAPBUILD" to gather that it is install
+ * time. At install time we just fix a mode with bad bits set by
+ * setting it to CURMODE. This should be an error in a few releases
+ * (2.8 maybe) but faulty modes are so common in existing packages
+ * that this is a reasonable exception. -- JST 1994-11-9
+ *
+ * RETURNS
+ * 0 if mode is being returned as a valid value
+ * 1 if no attributes are present on the line
+ * 2 if there was a fundamental error
+ */
+static int
+getvalmode(FILE *fp, mode_t *d, long bad, int map)
+{
+ char tempmode[20];
+ mode_t tempmode_t;
+ int retval;
+
+ if ((retval = getstr(fp, NULL, ATRSIZ, tempmode)) == 1)
+ return (1);
+ else if (retval == -1) {
+ setErrstr(pkg_gt(ERR_MODELONG));
+ return (2);
+ } else {
+ /*
+ * If it isn't a '?' (meaning go with whatever mode is
+ * there), validate the mode and convert it to a mode_t. The
+ * "bad" variable here is a misnomer. It doesn't necessarily
+ * mean bad.
+ */
+ if (tempmode[0] == '?') {
+ *d = WILDCARD;
+ } else {
+ /*
+ * Mode may not be an install parameter or a
+ * non-build parameter.
+ */
+ if (tempmode[0] == '$' &&
+ (isupper(tempmode[1]) || !islower(tempmode[1]))) {
+ setErrstr(pkg_gt(ERR_IMODE));
+ return (2);
+ }
+
+ if ((map) && (mapvar(mapmode, tempmode))) {
+ (void) snprintf(getErrbufAddr(),
+ getErrbufSize(),
+ pkg_gt(ERR_NOVAR),
+ maptype, tempmode);
+ setErrstr(getErrbufAddr());
+ return (2);
+ }
+
+
+ if (tempmode[0] == '$') {
+ *d = BADMODE; /* may be a problem */
+ } else {
+ /*
+ * At this point it's supposed to be
+ * something we can convert to a number.
+ */
+ int n = 0;
+
+ /*
+ * We reject it if it contains nonnumbers or
+ * it's not octal.
+ */
+ while (tempmode[n] && !isspace(tempmode[n])) {
+ if (!isdigit(tempmode[n])) {
+ setErrstr(
+ pkg_gt(ERR_MODEALPHA));
+ return (2);
+ }
+
+ if (strchr("89abcdefABCDEF",
+ tempmode[n])) {
+ setErrstr(
+ pkg_gt(ERR_BASEINVAL));
+ return (2);
+ }
+ n++;
+ }
+
+ tempmode_t = strtol(tempmode, NULL, 8);
+
+ /*
+ * We reject it if it contains inappropriate
+ * bits.
+ */
+ if (tempmode_t & ~(S_IAMB |
+ S_ISUID | S_ISGID | S_ISVTX)) {
+ if (mapmode != MAPBUILD) {
+ tempmode_t = bad;
+ } else {
+ setErrstr(pkg_gt(ERR_MODEBITS));
+ return (2);
+ }
+ }
+ *d = tempmode_t;
+ }
+ }
+ return (0);
+ }
+}
+
+static int
+getnum(FILE *fp, int base, long *d, long bad)
+{
+ int c, b;
+
+ /* leading white space ignored */
+ c = eatwhite(fp);
+ if (c == '?') {
+ *d = bad;
+ return (0);
+ }
+
+ if ((c == EOF) || (c == '\n') || !isdigit(c)) {
+ (void) ungetc(c, fp);
+ return (1);
+ }
+
+ *d = 0;
+ while (isdigit(c)) {
+ b = (c & 017);
+ if (b >= base)
+ return (2);
+ *d = (*d * base) + b;
+ c = getc(fp);
+ }
+ (void) ungetc(c, fp);
+ return (0);
+}
+
+static int
+getlnum(FILE *fp, int base, fsblkcnt_t *d, long bad)
+{
+ int c, b;
+
+ /* leading white space ignored */
+ c = eatwhite(fp);
+ if (c == '?') {
+ *d = bad;
+ return (0);
+ }
+
+ if ((c == EOF) || (c == '\n') || !isdigit(c)) {
+ (void) ungetc(c, fp);
+ return (1);
+ }
+
+ *d = 0;
+ while (isdigit(c)) {
+ b = (c & 017);
+ if (b >= base)
+ return (2);
+ *d = (*d * base) + b;
+ c = getc(fp);
+ }
+ (void) ungetc(c, fp);
+ return (0);
+}
+
+/*
+ * Get a string from the file. Returns
+ * 0 if all OK
+ * 1 if nothing there
+ * -1 if string is too long
+ */
+static int
+getstr(FILE *fp, char *sep, int n, char *str)
+{
+ int c;
+
+ /* leading white space ignored */
+ c = eatwhite(fp);
+ if ((c == EOF) || (c == '\n')) {
+ (void) ungetc(c, fp);
+ return (1); /* nothing there */
+ }
+
+ /* fill up string until space, tab, or separator */
+ while (!strchr(" \t", c) && (!sep || !strchr(sep, c))) {
+ if (n-- < 1) {
+ *str = '\0';
+ return (-1); /* too long */
+ }
+ *str++ = (char)c;
+ c = getc(fp);
+ if ((c == EOF) || (c == '\n'))
+ break; /* no more on this line */
+ }
+ *str = '\0';
+ (void) ungetc(c, fp);
+
+ return (0);
+}
+
+static int
+getend(FILE *fp)
+{
+ int c;
+ int n;
+
+ n = 0;
+ do {
+ if ((c = getc(fp)) == EOF)
+ return (n);
+ if (!isspace(c))
+ n++;
+ } while (c != '\n');
+ return (n);
+}
+
+static int
+eatwhite(FILE *fp)
+{
+ int c;
+
+ /* this test works around a side effect of getc() */
+ if (feof(fp))
+ return (EOF);
+ do
+ c = getc(fp);
+ while ((c == ' ') || (c == '\t'));
+ return (c);
+}
+
+int
+gpkgmapvfp(struct cfent *ept, VFP_T *vfp)
+{
+ int c;
+ boolean_t first_char = B_TRUE;
+ (void) strlcpy(ept->pkg_class, BADCLASS, sizeof (ept->pkg_class));
+ (void) strlcpy(ept->ainfo.owner, d_owner, sizeof (ept->ainfo.owner));
+ (void) strlcpy(ept->ainfo.group, d_group, sizeof (ept->ainfo.group));
+
+ setErrstr(NULL);
+ ept->volno = 0;
+ ept->ftype = BADFTYPE;
+ ept->pkg_class_idx = -1;
+ ept->path = NULL;
+ ept->ainfo.local = NULL;
+ ept->ainfo.mode = d_mode;
+ ept->ainfo.major = BADMAJOR;
+ ept->ainfo.minor = BADMINOR;
+ ept->cinfo.cksum = (-1L);
+ ept->cinfo.modtime = (-1L);
+ ept->cinfo.size = (-1L);
+
+ ept->npkgs = 0;
+
+ /* return error if no vfp specified */
+
+ if (vfp == (VFP_T *)NULL) {
+ return (-1);
+ }
+
+readline:
+ while (((c = vfpGetcNoInc(vfp)) != '\0') && (isspace(vfpGetc(vfp))))
+ ;
+
+ /*
+ * If the first character is not a digit, we assume that the
+ * volume number is 1.
+ */
+ if (first_char && !isdigit(c)) {
+ ept->volno = 1;
+ }
+ first_char = B_FALSE;
+
+ /*
+ * In case of hsfs the zero-padding of partial pages
+ * returned by mmap is not done properly. A separate bug has been filed
+ * on this.
+ */
+
+ if (vfp->_vfpCurr && (vfp->_vfpCurr > vfp->_vfpEnd)) {
+ return (0);
+ }
+
+ switch (c) {
+ case '\0':
+ return (0);
+
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ if (ept->volno) {
+ setErrstr(pkg_gt(ERR_BAD_VOLUME_NUMBER));
+ goto error;
+ }
+ do {
+ ept->volno = (ept->volno*10)+c-'0';
+ c = vfpGetc(vfp);
+ } while (isdigit(c));
+ if (ept->volno == 0) {
+ ept->volno = 1;
+ }
+
+ goto readline;
+
+ case ':':
+ case '#':
+ (void) findendvfp(&vfpGetCurrCharPtr(vfp));
+ /*FALLTHRU*/
+ case '\n':
+ /*
+ * Since we are going to scan the next line,
+ * we need to reset volume number and first_char.
+ */
+ ept->volno = 0;
+ first_char = B_TRUE;
+ goto readline;
+
+ case 'i':
+ ept->ftype = (char)c;
+ while (((c = vfpGetcNoInc(vfp)) != '\0') &&
+ (isspace(vfpGetc(vfp))))
+ ;
+ /*FALLTHRU*/
+ case '.':
+ case '/':
+ vfpDecCurrPtr(vfp);
+
+ if (getstrvfp(&vfpGetCurrCharPtr(vfp), "=", PATH_MAX, mypath)) {
+ setErrstr(pkg_gt(ERR_CANNOT_READ_PATHNAME_FIELD));
+ goto error;
+ }
+ ept->path = mypath;
+ c = vfpGetc(vfp);
+ if (c == '=') {
+ if (getstrvfp(&vfpGetCurrCharPtr(vfp), NULL, PATH_MAX,
+ mylocal)) {
+ setErrstr(pkg_gt(ERR_CANT_READ_LCLPATH));
+ goto error;
+ }
+ ept->ainfo.local = mylocal;
+ } else {
+ vfpDecCurrPtr(vfp);
+ }
+
+ if (ept->ftype == 'i') {
+ /* content info might exist */
+ if (!getlnumvfp(&vfpGetCurrCharPtr(vfp), 10,
+ (fsblkcnt_t *)&ept->cinfo.size, BADCONT) &&
+ (getnumvfp(&vfpGetCurrCharPtr(vfp), 10,
+ (long *)&ept->cinfo.cksum, BADCONT) ||
+ getnumvfp(&vfpGetCurrCharPtr(vfp), 10,
+ (long *)&ept->cinfo.modtime, BADCONT))) {
+ setErrstr(pkg_gt(ERR_CANNOT_READ_CONTENT_INFO));
+ goto error;
+ }
+ }
+
+ if (getendvfp(&vfpGetCurrCharPtr(vfp))) {
+ setErrstr(pkg_gt(ERR_EXTRA_TOKENS_PRESENT));
+ return (-1);
+ }
+ return (1);
+
+ case '?':
+ case 'f':
+ case 'v':
+ case 'e':
+ case 'l':
+ case 's':
+ case 'p':
+ case 'c':
+ case 'b':
+ case 'd':
+ case 'x':
+ ept->ftype = (char)c;
+ if (getstrvfp(&vfpGetCurrCharPtr(vfp), NULL,
+ CLSSIZ, ept->pkg_class)) {
+ setErrstr(pkg_gt(ERR_CANNOT_READ_CLASS_TOKEN));
+ goto error;
+ }
+ if (getstrvfp(&vfpGetCurrCharPtr(vfp), "=", PATH_MAX, mypath)) {
+ setErrstr(pkg_gt(ERR_CANNOT_READ_PATHNAME_FIELD));
+ goto error;
+ }
+ ept->path = mypath;
+
+ c = vfpGetc(vfp);
+ if (c == '=') {
+ /* local path */
+ if (getstrvfp(&vfpGetCurrCharPtr(vfp), NULL,
+ PATH_MAX, mylocal)) {
+ if (ept->ftype == 's' || ept->ftype == 'l') {
+ setErrstr(pkg_gt(ERR_READLINK));
+ } else {
+ setErrstr(
+ pkg_gt(ERR_CANT_READ_LCLPATH));
+ }
+ goto error;
+ }
+ ept->ainfo.local = mylocal;
+ } else if ((ept->ftype == 's') || (ept->ftype == 'l')) {
+ if ((c != '\0') && (c != '\n'))
+ (void) findendvfp(&vfpGetCurrCharPtr(vfp));
+ setErrstr(pkg_gt(ERR_BAD_LINK_SPEC));
+ return (-1);
+ } else {
+ vfpDecCurrPtr(vfp);
+ }
+ break;
+
+ default:
+ setErrstr(pkg_gt(ERR_UNKNOWN_FTYPE));
+error:
+ (void) findendvfp(&vfpGetCurrCharPtr(vfp));
+ return (-1);
+ }
+
+ if (((ept->ftype == 's') || (ept->ftype == 'l')) &&
+ (ept->ainfo.local == NULL)) {
+ setErrstr(pkg_gt(ERR_NO_LINKSOURCE));
+ goto error;
+ }
+
+ if (((ept->ftype == 'c') || (ept->ftype == 'b'))) {
+ ept->ainfo.major = BADMAJOR;
+ ept->ainfo.minor = BADMINOR;
+
+ if (getnumvfp(&vfpGetCurrCharPtr(vfp), 10,
+ (long *)&ept->ainfo.major, BADMAJOR) ||
+ getnumvfp(&vfpGetCurrCharPtr(vfp), 10,
+ (long *)&ept->ainfo.minor, BADMINOR)) {
+ setErrstr(pkg_gt(ERR_CANNOT_READ_MM_DEVNUMS));
+ goto error;
+ }
+ }
+
+ /*
+ * Links and information files don't have attributes associated with
+ * them. The following either resolves potential variables or passes
+ * them through. Mode is tested for validity to some degree. BAD???
+ * is returned to indicate that no meaningful mode was provided. A
+ * higher authority will decide if that's OK or not. CUR??? means that
+ * the prototype file specifically requires a wildcard ('?') for
+ * that entry. We issue an error if attributes were entered wrong.
+ * We just return BAD??? if there was no entry at all.
+ */
+ if ((ept->ftype == 'd') || (ept->ftype == 'x') || (ept->ftype == 'c') ||
+ (ept->ftype == 'b') || (ept->ftype == 'p') ||
+ (ept->ftype == 'f') || (ept->ftype == 'v') ||
+ (ept->ftype == 'e')) {
+ int retval;
+
+ retval = getvalmodevfp(&vfpGetCurrCharPtr(vfp),
+ &(ept->ainfo.mode),
+ CURMODE, (mapmode != MAPNONE));
+
+ if (retval == 1) {
+ goto end; /* nothing else on the line */
+ } else if (retval == 2) {
+ goto error; /* mode is too no good */
+ }
+
+ /* owner & group should be here */
+ if ((retval = getstrvfp(&vfpGetCurrCharPtr(vfp), NULL, ATRSIZ,
+ ept->ainfo.owner)) == 1)
+ goto end; /* no owner or group - warning */
+ if (retval == -1) {
+ setErrstr(pkg_gt(ERR_OWNTOOLONG));
+ goto error;
+ }
+
+ if ((retval = getstrvfp(&vfpGetCurrCharPtr(vfp), NULL, ATRSIZ,
+ ept->ainfo.group)) == 1)
+ goto end; /* no group - warning */
+ if (retval == -1) {
+ setErrstr(pkg_gt(ERR_GRPTOOLONG));
+ goto error;
+ }
+
+ /* Resolve the parameters if required. */
+ if (mapmode != MAPNONE) {
+ if (mapvar(mapmode, ept->ainfo.owner)) {
+ (void) snprintf(getErrbufAddr(),
+ getErrbufSize(), pkg_gt(ERR_NOVAR),
+ maptype, ept->ainfo.owner);
+ setErrstr(getErrbufAddr());
+ goto error;
+ }
+ if (mapvar(mapmode, ept->ainfo.group)) {
+ (void) snprintf(getErrbufAddr(),
+ getErrbufSize(), pkg_gt(ERR_NOVAR),
+ maptype, ept->ainfo.group);
+ setErrstr(getErrbufAddr());
+ goto error;
+ }
+ }
+ }
+
+ if ((ept->ftype == 'i') || (ept->ftype == 'f') ||
+ (ept->ftype == 'v') || (ept->ftype == 'e')) {
+ /* look for content description */
+ if (!getlnumvfp(&vfpGetCurrCharPtr(vfp), 10,
+ (fsblkcnt_t *)&ept->cinfo.size, BADCONT) &&
+ (getnumvfp(&vfpGetCurrCharPtr(vfp), 10,
+ (long *)&ept->cinfo.cksum, BADCONT) ||
+ getnumvfp(&vfpGetCurrCharPtr(vfp), 10,
+ (long *)&ept->cinfo.modtime, BADCONT))) {
+ setErrstr(pkg_gt(ERR_CANNOT_READ_CONTENT_INFO));
+ goto error;
+ }
+ }
+
+ if (ept->ftype == 'i')
+ goto end;
+
+end:
+ if (getendvfp(&vfpGetCurrCharPtr(vfp)) && ept->pinfo) {
+ setErrstr(pkg_gt(ERR_EXTRA_TOKENS_PRESENT));
+ return (-1);
+ }
+
+done:
+ return (1);
+}
+
+/*
+ * Get and validate the mode attribute. This returns an error if
+ * 1. the mode string is too long
+ * 2. the mode string includes alpha characters
+ * 3. the mode string is not octal
+ * 4. mode string is an install parameter
+ * 5. mode is an unresolved build parameter and MAPBUILD is
+ * in effect.
+ * If the mode is a build parameter, it is
+ * 1. returned as is if MAPNONE is in effect
+ * 2. evaluated if MAPBUILD is in effect
+ *
+ * NOTE : We use "mapmode!=MAPBUILD" to gather that it is install
+ * time. At install time we just fix a mode with bad bits set by
+ * setting it to CURMODE. This should be an error in a few releases
+ * (2.8 maybe) but faulty modes are so common in existing packages
+ * that this is a reasonable exception. -- JST 1994-11-9
+ *
+ * RETURNS
+ * 0 if mode is being returned as a valid value
+ * 1 if no attributes are present on the line
+ * 2 if there was a fundamental error
+ */
+static int
+getvalmodevfp(char **cp, mode_t *d, long bad, int map)
+{
+ char tempmode[ATRSIZ+1];
+ mode_t tempmode_t;
+ int retval;
+ int n;
+
+ if ((retval = getstrvfp(cp, NULL, sizeof (tempmode), tempmode)) == 1) {
+ return (1);
+ } else if (retval == -1) {
+ setErrstr(pkg_gt(ERR_MODELONG));
+ return (2);
+ }
+
+ /*
+ * If it isn't a '?' (meaning go with whatever mode is
+ * there), validate the mode and convert it to a mode_t. The
+ * "bad" variable here is a misnomer. It doesn't necessarily
+ * mean bad.
+ */
+ if (tempmode[0] == '?') {
+ *d = WILDCARD;
+ return (0);
+ }
+
+ /*
+ * Mode may not be an install parameter or a
+ * non-build parameter.
+ */
+
+ if (tempmode[0] == '$' &&
+ (isupper(tempmode[1]) || !islower(tempmode[1]))) {
+ setErrstr(pkg_gt(ERR_IMODE));
+ return (2);
+ }
+
+ if ((map) && (mapvar(mapmode, tempmode))) {
+ (void) snprintf(getErrbufAddr(), getErrbufSize(),
+ pkg_gt(ERR_NOVAR), maptype, tempmode);
+ setErrstr(getErrbufAddr());
+ return (2);
+ }
+
+ if (tempmode[0] == '$') {
+ *d = BADMODE; /* may be a problem */
+ return (0);
+ }
+
+ /* it's supposed to be something we can convert to a number */
+
+ n = 0;
+
+ /* reject it if it contains nonnumbers or it's not octal */
+
+ while (tempmode[n] && !isspace(tempmode[n])) {
+ if (!isdigit(tempmode[n])) {
+ setErrstr(pkg_gt(ERR_MODEALPHA));
+ return (2);
+ }
+
+ if (strchr("89abcdefABCDEF", tempmode[n])) {
+ setErrstr(pkg_gt(ERR_BASEINVAL));
+ return (2);
+ }
+ n++;
+ }
+
+ tempmode_t = strtol(tempmode, NULL, 8);
+
+ /*
+ * We reject it if it contains inappropriate
+ * bits.
+ */
+ if (tempmode_t & (~(S_IAMB | S_ISUID | S_ISGID | S_ISVTX))) {
+ if (mapmode == MAPBUILD) {
+ setErrstr(pkg_gt(ERR_MODEBITS));
+ return (2);
+ }
+ tempmode_t = bad;
+ }
+
+ *d = tempmode_t;
+
+ return (0);
+}
+
+int
+getnumvfp(char **cp, int base, long *d, long bad)
+{
+ int c;
+ char *p = *cp;
+
+ if (*p == '\0') {
+ return (0);
+ }
+
+ /* leading white space ignored */
+ while (((c = *p) != '\0') && (isspace(*p++)))
+ ;
+ if (c == '?') {
+ *d = bad;
+ *cp = p;
+ return (0);
+ }
+
+ if ((c == '\0') || (c == '\n') || !isdigit(c)) {
+ p--;
+ *cp = p;
+ return (1);
+ }
+
+ *d = 0;
+ while (isdigit(c)) {
+ *d = (*d * base) + (c & 017);
+ c = *p++;
+ }
+ p--;
+ *cp = p;
+ return (0);
+}
+
+int
+getlnumvfp(char **cp, int base, fsblkcnt_t *d, long bad)
+{
+ int c;
+ char *p = *cp;
+
+ if (*p == '\0') {
+ return (0);
+ }
+
+ /* leading white space ignored */
+ while (((c = *p) != '\0') && (isspace(*p++)))
+ ;
+ if (c == '?') {
+ *d = bad;
+ *cp = p;
+ return (0);
+ }
+
+ if ((c == '\0') || (c == '\n') || !isdigit(c)) {
+ p--;
+ *cp = p;
+ return (1);
+ }
+
+ *d = 0;
+ while (isdigit(c)) {
+ *d = (*d * base) + (c & 017);
+ c = *p++;
+ }
+ p--;
+ *cp = p;
+ return (0);
+}
+
+static int
+getstrvfp(char **cp, char *sep, int n, char *str)
+{
+ char delims[256];
+ int c;
+ char *p = *cp;
+ char *p1;
+ size_t len;
+
+ if (*p == '\0') {
+ return (1);
+ }
+
+ /* leading white space ignored */
+
+ while (((c = *p) != '\0') && (isspace(*p++)))
+ ;
+ if ((c == '\0') || (c == '\n')) {
+ p--;
+ *cp = p;
+ return (1); /* nothing there */
+ }
+
+ p--;
+
+ /* generate complete list of delimiters to scan for */
+
+ (void) strlcpy(delims, " \t\n", sizeof (delims));
+ if ((sep != (char *)NULL) && (*sep != '\0')) {
+ (void) strlcat(delims, sep, sizeof (delims));
+ }
+
+ /* compute length based on delimiter found or not */
+
+ p1 = strpbrk(p, delims);
+ if (p1 == (char *)NULL) {
+ len = strlen(p);
+ } else {
+ len = (ptrdiff_t)p1 - (ptrdiff_t)p;
+ }
+
+ /* if string will fit in result buffer copy string and return success */
+
+ if (len < n) {
+ (void) memcpy(str, p, len);
+ str[len] = '\0';
+ p += len;
+ *cp = p;
+ return (0);
+ }
+
+ /* result buffer too small; copy partial string, return error */
+ (void) memcpy(str, p, n-1);
+ str[n-1] = '\0';
+ p += n;
+ *cp = p;
+ return (-1);
+}
+
+/*
+ * Name: getendvfp
+ * Description: Locate the end of the current line given a pointer into a buffer
+ * containing characters that is null terminated.
+ * Arguments: char **cp - pointer to pointer to null-terminated string buffer
+ * Returns: int == 0 -- no non-space characters preceeded the newline
+ * != 0 -- one or more non-space characters preceeded newline
+ * Effects: cp is updated to point to the first character PAST the first new
+ * line character found. If no newline character is found, cp is
+ * updated to point to the '\0' at the end of the buffer.
+ */
+
+static int
+getendvfp(char **cp)
+{
+ int n;
+ char *p = *cp;
+
+ n = 0;
+
+ /* if at end of buffer return no more characters left */
+
+ if (*p == '\0') {
+ return (0);
+ }
+
+ /* find the first null or end of line character */
+
+ while ((*p != '\0') && (*p != '\n')) {
+ if (n == 0) {
+ if (!isspace(*p)) {
+ n++;
+ }
+ }
+ p++;
+ }
+
+ /* if at newline, increment pointer to first character past newline */
+
+ if (*p == '\n') {
+ p++;
+ }
+
+ /* set return pointer to null or first character past newline */
+
+ *cp = p;
+
+ /* return space/nospace indicator */
+
+ return (n);
+}
+
+/*
+ * Name: findendvfp
+ * Description: Locate the end of the current line given a pointer into a buffer
+ * containing characters that is null terminated.
+ * Arguments: char **cp - pointer to pointer to null-terminated string buffer
+ * Returns: none
+ * Effects: cp is updated to point to the first character PAST the first new
+ * line character found. If no newline character is found, cp is
+ * updated to point to the '\0' at the end of the buffer.
+ */
+
+static void
+findendvfp(char **cp)
+{
+ char *p1;
+ char *p = *cp;
+
+ /* if at end of buffer return no more characters left */
+
+ if (*p == '\0') {
+ return;
+ }
+
+ /* find the end of the line */
+
+ p1 = strchr(p, '\n');
+ if (p1 != (char *)NULL) {
+ *cp = ++p1;
+ return;
+ }
+
+ /* no newline found - point to null terminator */
+
+ *cp = strchr(p, '\0');
+}
diff --git a/usr/src/lib/libpkg/common/handlelocalfs.c b/usr/src/lib/libpkg/common/handlelocalfs.c
new file mode 100644
index 0000000000..9130cb1417
--- /dev/null
+++ b/usr/src/lib/libpkg/common/handlelocalfs.c
@@ -0,0 +1,150 @@
+/*
+ *
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ *
+ */
+
+/*
+ * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ *
+*/
+
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <strings.h>
+#include <libscf.h>
+
+#define MAX_TRY 15
+static boolean_t fs_temporarily_enabled = B_FALSE;
+char svm_core_svcs[] = "system/filesystem/local:default";
+
+/*
+ * Name: enable_local_fs
+ * Description: If the SMF service system/filesystem/local:default is not
+ * enabled, then this function enables the service, so that,
+ * all the local filesystems are mounted.
+ * Arguments: None
+ * Returns: B_TRUE on success; B_FALSE on error.
+ */
+boolean_t
+enable_local_fs(void)
+{
+ char *cur_smf_state;
+ int i;
+ boolean_t fs_enabled_here = B_FALSE;
+
+ if (fs_temporarily_enabled) {
+ return (B_TRUE);
+ }
+
+ if ((cur_smf_state = smf_get_state(svm_core_svcs)) != NULL) {
+ if (strcmp(cur_smf_state, SCF_STATE_STRING_DISABLED) == 0) {
+ if (smf_enable_instance(svm_core_svcs, SMF_TEMPORARY)
+ != 0) {
+ free(cur_smf_state);
+ return (B_FALSE);
+ }
+
+ fs_enabled_here = B_TRUE;
+
+ } else if (strcmp(cur_smf_state, SCF_STATE_STRING_ONLINE)
+ == 0) {
+ free(cur_smf_state);
+ return (B_TRUE);
+ } else if (strcmp(cur_smf_state, SCF_STATE_STRING_OFFLINE)
+ != 0) {
+ free(cur_smf_state);
+ return (B_FALSE);
+ }
+
+ free(cur_smf_state);
+
+ } else {
+ return (B_FALSE);
+ }
+
+ for (i = 0; i < MAX_TRY; i++) {
+ if ((cur_smf_state = smf_get_state(svm_core_svcs)) != NULL) {
+ if (strcmp(cur_smf_state, SCF_STATE_STRING_ONLINE)
+ == 0) {
+ free(cur_smf_state);
+ if (fs_enabled_here) {
+ fs_temporarily_enabled = B_TRUE;
+ }
+ return (B_TRUE);
+ } else if ((strcmp(cur_smf_state,
+ SCF_STATE_STRING_OFFLINE) == 0) ||
+ (strcmp(cur_smf_state, SCF_STATE_STRING_DISABLED) == 0)) {
+ (void) sleep(1);
+ free(cur_smf_state);
+ } else {
+ free(cur_smf_state);
+ return (B_FALSE);
+ }
+ } else {
+ return (B_FALSE);
+ }
+ }
+
+ return (B_FALSE);
+}
+
+/*
+ * Name: restore_local_fs
+ * Description: If the SMF service system/filesystem/local:default was
+ * enabled using enable_local_fs(), then this function disables
+ * the service.
+ * Arguments: None
+ * Returns: B_TRUE on success; B_FALSE on error.
+ */
+boolean_t
+restore_local_fs(void)
+{
+ int i;
+ char *cur_smf_state;
+
+ if (!fs_temporarily_enabled) {
+ return (B_TRUE);
+ }
+
+ if (smf_disable_instance(svm_core_svcs, SMF_TEMPORARY) != 0) {
+ return (B_FALSE);
+ }
+
+ for (i = 0; i < MAX_TRY; i++) {
+ if ((cur_smf_state = smf_get_state(svm_core_svcs)) != NULL) {
+ if (strcmp(cur_smf_state, SCF_STATE_STRING_DISABLED)
+ == 0) {
+ fs_temporarily_enabled = B_FALSE;
+ free(cur_smf_state);
+ break;
+ }
+ (void) sleep(1);
+
+ free(cur_smf_state);
+ } else {
+ return (B_FALSE);
+ }
+ }
+
+ return (!fs_temporarily_enabled);
+}
diff --git a/usr/src/lib/libpkg/common/isdir.c b/usr/src/lib/libpkg/common/isdir.c
new file mode 100644
index 0000000000..55b59d5e67
--- /dev/null
+++ b/usr/src/lib/libpkg/common/isdir.c
@@ -0,0 +1,391 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
+/* All Rights Reserved */
+
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <archives.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include "pkglocale.h"
+#include "pkglibmsgs.h"
+
+/*
+ * Defines for cpio/compression checks.
+ */
+#define BIT_MASK 0x1f
+#define BLOCK_MASK 0x80
+
+#define MASK_CK(x, y) (((x) & (y)) == (y))
+#define ISCOMPCPIO ((unsigned char) cm.c_mag[0] == m_h[0] && \
+ (unsigned char) cm.c_mag[1] == m_h[1] && \
+ (MASK_CK((unsigned char) cm.c_mag[2], BLOCK_MASK) || \
+ MASK_CK((unsigned char) cm.c_mag[2], BIT_MASK)))
+
+#define ISCPIO (cm.b_mag != CMN_BIN && \
+ (strcmp(cm.c_mag, CMS_ASC) == 0) && \
+ (strcmp(cm.c_mag, CMS_CHR) == 0) && \
+ (strcmp(cm.c_mag, CMS_CRC) == 0))
+
+/* location of distributed file system types database */
+
+#define REMOTE_FS_DBFILE "/etc/dfs/fstypes"
+
+/* character array used to hold dfs types database contents */
+
+static long numRemoteFstypes = -1;
+static char **remoteFstypes = (char **)NULL;
+
+/* forward declarations */
+
+static void _InitRemoteFstypes(void);
+
+int isFdRemote(int a_fd);
+int isPathRemote(char *a_path);
+int isFstypeRemote(char *a_fstype);
+int isdir(char *path);
+int isfile(char *dir, char *file);
+int iscpio(char *path, int *iscomp);
+
+/*
+ * Name: isdir
+ * Description: determine if specified path exists and is a directory
+ * Arguments: path - pointer to string representing the path to verify
+ * returns: 0 - directory exists
+ * 1 - directory does not exist or is not a directory
+ * NOTE: errno is set appropriately
+ */
+
+int
+isdir(char *path)
+{
+ struct stat statbuf;
+
+ /* return error if path does not exist */
+
+ if (stat(path, &statbuf) != 0) {
+ return (1);
+ }
+
+ /* return error if path is not a directory */
+
+ if ((statbuf.st_mode & S_IFMT) != S_IFDIR) {
+ errno = ENOTDIR;
+ return (1);
+ }
+
+ return (0);
+}
+
+/*
+ * Name: isfile
+ * Description: determine if specified path exists and is a directory
+ * Arguments: dir - pointer to string representing the directory where
+ * the file is located
+ * == NULL - use "file" argument only
+ * file - pointer to string representing the file to verify
+ * Returns: 0 - success - file exists
+ * 1 - failure - file does not exist OR is not a file
+ * NOTE: errno is set appropriately
+ */
+
+int
+isfile(char *dir, char *file)
+{
+ struct stat statbuf;
+ char path[PATH_MAX];
+
+ /* construct full path if directory specified */
+
+ if (dir) {
+ (void) snprintf(path, sizeof (path), "%s/%s", dir, file);
+ file = path;
+ }
+
+ /* return error if path does not exist */
+
+ if (stat(file, &statbuf) != 0) {
+ return (1);
+ }
+
+ /* return error if path is a directory */
+
+ if ((statbuf.st_mode & S_IFMT) == S_IFDIR) {
+ errno = EISDIR;
+ return (1);
+ }
+
+ /* return error if path is not a file */
+
+ if ((statbuf.st_mode & S_IFMT) != S_IFREG) {
+ errno = EINVAL;
+ return (1);
+ }
+
+ return (0);
+}
+
+int
+iscpio(char *path, int *iscomp)
+{
+ /*
+ * Compressed File Header.
+ */
+ unsigned char m_h[] = { "\037\235" }; /* 1F 9D */
+
+ static union {
+ short int b_mag;
+ char c_mag[CMS_LEN];
+ } cm;
+
+ struct stat statb;
+ int fd;
+
+
+ *iscomp = 0;
+
+ if ((fd = open(path, O_RDONLY, 0)) == -1) {
+ if (errno != ENOENT) {
+ perror("");
+ (void) fprintf(stderr, pkg_gt(ERR_ISCPIO_OPEN), path);
+ }
+ return (0);
+ } else {
+ if (fstat(fd, &statb) == -1) {
+ perror("");
+ (void) fprintf(stderr, pkg_gt(ERR_ISCPIO_FSTAT), path);
+ (void) close(fd);
+ return (0);
+ } else {
+ if (S_ISREG(statb.st_mode)) { /* Must be a file */
+ if (read(fd, cm.c_mag, sizeof (cm.c_mag)) !=
+ sizeof (cm.c_mag)) {
+ perror("");
+ (void) fprintf(stderr,
+ pkg_gt(ERR_ISCPIO_READ), path);
+ (void) close(fd);
+ return (0);
+ }
+ /*
+ * Try to determine if the file is a compressed
+ * file, if that fails, try to determine if it
+ * is a cpio archive, if that fails, then we
+ * fail!
+ */
+ if (ISCOMPCPIO) {
+ *iscomp = 1;
+ (void) close(fd);
+ return (1);
+ } else if (ISCPIO) {
+ (void) fprintf(stderr,
+ pkg_gt(ERR_ISCPIO_NOCPIO),
+ path);
+ (void) close(fd);
+ return (0);
+ }
+ (void) close(fd);
+ return (1);
+ } else {
+ (void) close(fd);
+ return (0);
+ }
+ }
+ }
+}
+
+/*
+ * Name: isPathRemote
+ * Description: determine if a path object is local or remote
+ * Arguments: a_path - [RO, *RO] - (char *)
+ * Pointer to string representing the path to check
+ * Returns: int
+ * 1 - the path is remote
+ * 0 - the path is local to this system
+ * -1 - cannot determine if path is remote or local
+ */
+
+int
+isPathRemote(char *a_path)
+{
+ int r;
+ struct stat statbuf;
+
+ r = lstat(a_path, &statbuf);
+ if (r < 0) {
+ return (-1);
+ }
+
+ return (isFstypeRemote(statbuf.st_fstype));
+}
+
+/*
+ * Name: isFdRemote
+ * Description: determine if an open file is local or remote
+ * Arguments: a_fd - [RO, *RO] - (int)
+ * Integer representing open file to check
+ * Returns: int
+ * 1 - the path is remote
+ * 0 - the path is local to this system
+ * -1 - cannot determine if path is remote or local
+ */
+
+int
+isFdRemote(int a_fd)
+{
+ int r;
+ struct stat statbuf;
+
+ r = fstat(a_fd, &statbuf);
+ if (r < 0) {
+ return (-1);
+ }
+
+ return (isFstypeRemote(statbuf.st_fstype));
+}
+
+/*
+ * Name: isFstypeRemote
+ * Description: determine if a file system type is remote (distributed)
+ * Arguments: a_fstype - [RO, *RO] - (char *)
+ * Pointer to string representing the file system type
+ * to check
+ * Returns: int
+ * 1 - the file system type is remote
+ * 0 - the file system type is local to this system
+ */
+
+int
+isFstypeRemote(char *a_fstype)
+{
+ int i;
+
+ /* initialize the list if it is not yet initialized */
+
+ _InitRemoteFstypes();
+
+ /* scan the list looking for the specified type */
+
+ for (i = 0; i < numRemoteFstypes; i++) {
+ if (strcmp(remoteFstypes[i], a_fstype) == 0) {
+ return (1);
+ }
+ }
+
+ /* type not found in remote file system type list - is not remote */
+
+ return (0);
+}
+
+/*
+ * Name: _InitRemoteFstypes
+ * Description: initialize table of remote file system type names
+ * Arguments: none
+ * Returns: none
+ * Side Effects:
+ * - The global array "(char **)remoteFstypes" is set to the
+ * address of an array of string pointers, each of which represents
+ * a single remote file system type
+ * - The global variable "(long) numRemoteFstypes" is set to the total
+ * number of remote file system type strings (names) that are
+ * contained in the "remoteFstypes" global array.
+ * - numRemoteFstypes is initialized to "-1" before any attempt has been
+ * made to read the remote file system type name database.
+ */
+static void
+_InitRemoteFstypes(void)
+{
+ FILE *fp;
+ char line_buf[LINE_MAX];
+
+ /* return if already initialized */
+
+ if (numRemoteFstypes > 0) {
+ return;
+ }
+
+ /* if list is uninitialized, start with zero */
+
+ if (numRemoteFstypes == -1) {
+ numRemoteFstypes = 0;
+ }
+
+ /* open the remote file system type database file */
+
+ if ((fp = fopen(REMOTE_FS_DBFILE, "r")) == NULL) {
+ /* no remote type database: use predefined remote types */
+ remoteFstypes = (char **)realloc(remoteFstypes,
+ sizeof (char *) * (numRemoteFstypes+3));
+ remoteFstypes[numRemoteFstypes++] = "nfs"; /* +1 */
+ remoteFstypes[numRemoteFstypes++] = "autofs"; /* +2 */
+ remoteFstypes[numRemoteFstypes++] = "cachefs"; /* +3 */
+ return;
+ }
+
+ /*
+ * Read the remote file system type database; from fstypes(4):
+ *
+ * fstypes resides in directory /etc/dfs and lists distributed file
+ * system utilities packages installed on the system. For each installed
+ * distributed file system type, there is a line that begins with the
+ * file system type name (for example, ``nfs''), followed by white space
+ * and descriptive text.
+ *
+ * Lines will look at lot like this:
+ *
+ * nfs NFS Utilities
+ * autofs AUTOFS Utilities
+ * cachefs CACHEFS Utilities
+ */
+
+ while (fgets(line_buf, sizeof (line_buf), fp) != NULL) {
+ char buf[LINE_MAX];
+ static char format[128] = {'\0'};
+
+ if (format[0] == '\0') {
+ /* create bounded format: %ns */
+ (void) snprintf(format, sizeof (format),
+ "%%%ds", sizeof (buf)-1);
+ }
+
+ (void) sscanf(line_buf, format, buf);
+
+ remoteFstypes = realloc(remoteFstypes,
+ sizeof (char *) * (numRemoteFstypes+1));
+ remoteFstypes[numRemoteFstypes++] = strdup(buf);
+ }
+
+ /* close database file and return */
+
+ (void) fclose(fp);
+}
diff --git a/usr/src/lib/libpkg/common/keystore.c b/usr/src/lib/libpkg/common/keystore.c
new file mode 100644
index 0000000000..ad94b96c2d
--- /dev/null
+++ b/usr/src/lib/libpkg/common/keystore.c
@@ -0,0 +1,2474 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/*
+ * Module: keystore.c
+ * Description: This module contains the structure definitions for processing
+ * package keystore files.
+ */
+
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <strings.h>
+#include <libintl.h>
+#include <time.h>
+#include <ctype.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <openssl/evp.h>
+#include <openssl/x509.h>
+#include <openssl/pkcs12.h>
+#include <openssl/asn1.h>
+#include <openssl/pem.h>
+#include <openssl/err.h>
+#include <openssl/safestack.h>
+#include <openssl/stack.h>
+#include "p12lib.h"
+#include "pkgerr.h"
+#include "keystore.h"
+#include "pkglib.h"
+#include "pkglibmsgs.h"
+
+typedef struct keystore_t {
+ boolean_t dirty;
+ boolean_t new;
+ char *path;
+ char *passphrase;
+ /* truststore handles */
+ int cafd;
+ STACK_OF(X509) *cacerts;
+ char *capath;
+
+ /* user certificate handles */
+ STACK_OF(X509) *clcerts;
+ char *clpath;
+
+ /* private key handles */
+ STACK_OF(EVP_PKEY) *pkeys;
+ char *keypath;
+} keystore_t;
+
+/* local routines */
+static keystore_t *new_keystore(void);
+static void free_keystore(keystore_t *);
+static boolean_t verify_keystore_integrity(PKG_ERR *, keystore_t *);
+static boolean_t check_password(PKCS12 *, char *);
+static boolean_t resolve_paths(PKG_ERR *, char *, char *,
+ long, keystore_t *);
+static boolean_t lock_keystore(PKG_ERR *, long, keystore_t *);
+
+static boolean_t unlock_keystore(PKG_ERR *, keystore_t *);
+static boolean_t read_keystore(PKG_ERR *, keystore_t *,
+ keystore_passphrase_cb);
+static boolean_t write_keystore(PKG_ERR *, keystore_t *,
+ keystore_passphrase_cb);
+static boolean_t write_keystore_file(PKG_ERR *, char *, PKCS12 *);
+static boolean_t clear_keystore_file(PKG_ERR *, char *);
+static PKCS12 *read_keystore_file(PKG_ERR *, char *);
+static char *get_time_string(ASN1_TIME *);
+
+/* locking routines */
+static boolean_t restore_keystore_file(PKG_ERR *, char *);
+static int file_lock(int, int, int);
+static int file_unlock(int);
+static boolean_t file_lock_test(int, int);
+static boolean_t file_empty(char *);
+static boolean_t get_keystore_passwd(PKG_ERR *err, PKCS12 *p12,
+ keystore_passphrase_cb cb, keystore_t *keystore);
+static boolean_t wait_restore(int, char *, char *, char *);
+
+#define KEYSTORE_PERMS (S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)
+
+/* wait on other keystore access for 1 minute before giving up */
+#define LOCK_TIMEOUT 60
+
+/*
+ * print_certs - prints certificates out of a keystore, to a file.
+ *
+ * Arguments:
+ * err - Error object to append errors to
+ * keystore - Keystore on which to operate
+ * alias - Name of certificate to print, NULL means print all
+ * format - Format in which to print certificates
+ * outfile - Where to print certificates
+ *
+ * Returns:
+ * 0 - Success
+ * non-zero - Failure, errors added to err
+ */
+int
+print_certs(PKG_ERR *err, keystore_handle_t keystore_h, char *alias,
+ keystore_encoding_format_t format, FILE *outfile)
+{
+ int i;
+ X509 *cert;
+ char *fname = NULL;
+ boolean_t found = B_FALSE;
+ keystore_t *keystore = keystore_h;
+
+ if (keystore->clcerts != NULL) {
+ /* print out each client cert */
+ for (i = 0; i < sk_X509_num(keystore->clcerts); i++) {
+ cert = sk_X509_value(keystore->clcerts, i);
+ (void) sunw_get_cert_fname(GETDO_COPY, cert,
+ &fname);
+
+ if (fname == NULL) {
+ /* no name recorded, keystore is corrupt */
+ pkgerr_add(err, PKGERR_CORRUPT,
+ gettext(ERR_KEYSTORE_NO_ALIAS),
+ get_subject_display_name(cert));
+ return (1);
+ }
+
+ if ((alias != NULL) && (!streq(alias, fname))) {
+ /* name does not match, skip it */
+ (void) OPENSSL_free(fname);
+ fname = NULL;
+ continue;
+ } else {
+ found = B_TRUE;
+ (void) print_cert(err, cert, format,
+ fname, B_FALSE, outfile);
+ (void) OPENSSL_free(fname);
+ fname = NULL;
+ }
+ }
+ }
+
+ if (fname != NULL) {
+ (void) OPENSSL_free(fname);
+ fname = NULL;
+ }
+
+ if (keystore->cacerts != NULL) {
+ /* print out each trusted cert */
+ for (i = 0; i < sk_X509_num(keystore->cacerts); i++) {
+ cert = sk_X509_value(keystore->cacerts, i);
+ (void) sunw_get_cert_fname(GETDO_COPY,
+ cert, &fname);
+
+ if (fname == NULL) {
+ /* no name recorded, keystore is corrupt */
+ pkgerr_add(err, PKGERR_CORRUPT,
+ gettext(ERR_KEYSTORE_NO_ALIAS),
+ get_subject_display_name(cert));
+ return (1);
+ }
+
+ if ((alias != NULL) && (!streq(alias, fname))) {
+ /* name does not match, skip it */
+ (void) OPENSSL_free(fname);
+ fname = NULL;
+ continue;
+ } else {
+ found = B_TRUE;
+ (void) print_cert(err, cert, format,
+ fname, B_TRUE, outfile);
+ (void) OPENSSL_free(fname);
+ fname = NULL;
+ }
+ }
+ }
+
+ if (fname != NULL) {
+ (void) OPENSSL_free(fname);
+ fname = NULL;
+ }
+
+ if (found) {
+ return (0);
+ } else {
+ /* no certs printed */
+ if (alias != NULL) {
+ pkgerr_add(err, PKGERR_NOALIASMATCH,
+ gettext(ERR_KEYSTORE_NOCERT),
+ alias, keystore->path);
+ } else {
+ pkgerr_add(err, PKGERR_NOPUBKEY,
+ gettext(ERR_KEYSTORE_NOPUBCERTS),
+ keystore->path);
+ pkgerr_add(err, PKGERR_NOCACERT,
+ gettext(ERR_KEYSTORE_NOCACERTS),
+ keystore->path);
+ }
+ return (1);
+ }
+}
+
+/*
+ * print_cert - prints a single certificate, to a file
+ *
+ * Arguments:
+ * err - Error object to append errors to
+ * x - The certificate to print
+ * alias - Name of certificate to print
+ * format - Format in which to print certificate
+ * outfile - Where to print certificate
+ *
+ * Returns:
+ * 0 - Success
+ * non-zero - Failure, errors added to err
+ */
+int print_cert(PKG_ERR *err, X509 *x,
+ keystore_encoding_format_t format, char *alias, boolean_t is_trusted,
+ FILE *outfile)
+{
+
+ char *vdb_str;
+ char *vda_str;
+ char vd_str[ATTR_MAX];
+ int ret = 0;
+ char *cn_str, *icn_str, *typ_str;
+ char *tmp;
+ char *md5_fp;
+ char *sha1_fp;
+ int len;
+
+ /* need to localize the word "Fingerprint", hence these pointers */
+ char md5_label[ATTR_MAX];
+ char sha1_label[ATTR_MAX];
+
+ if (is_trusted) {
+ typ_str = gettext(MSG_KEYSTORE_TRUSTED);
+ } else {
+ typ_str = gettext(MSG_KEYSTORE_UNTRUSTED);
+ }
+
+ if ((cn_str = get_subject_display_name(x)) == NULL) {
+ cn_str = gettext(MSG_KEYSTORE_UNKNOWN);
+ }
+
+ if ((icn_str = get_issuer_display_name(x)) == NULL) {
+ icn_str = gettext(MSG_KEYSTORE_UNKNOWN);
+ }
+
+ vdb_str = xstrdup(get_time_string(X509_get_notBefore(x)));
+ vda_str = xstrdup(get_time_string(X509_get_notAfter(x)));
+ if (((len = snprintf(vd_str, ATTR_MAX, "<%s> - <%s>",
+ vdb_str, vda_str)) < 0) || (len >= ATTR_MAX)) {
+ pkgerr_add(err, PKGERR_WEB, gettext(ERR_LEN), vdb_str);
+ ret = 1;
+ goto cleanup;
+ }
+
+ if ((tmp = get_fingerprint(x, EVP_md5())) == NULL) {
+ md5_fp = gettext(MSG_KEYSTORE_UNKNOWN);
+ } else {
+ /*
+ * make a copy, otherwise the next call to get_fingerprint
+ * will overwrite this one
+ */
+ md5_fp = xstrdup(tmp);
+ }
+
+ if ((tmp = get_fingerprint(x, EVP_sha1())) == NULL) {
+ sha1_fp = gettext(MSG_KEYSTORE_UNKNOWN);
+ } else {
+ sha1_fp = xstrdup(tmp);
+ }
+
+ (void) snprintf(md5_label, ATTR_MAX, "%s %s",
+ OBJ_nid2sn(EVP_MD_type(EVP_md5())),
+ /* i18n: 14 characters max */
+ gettext(MSG_KEYSTORE_FP));
+
+ (void) snprintf(sha1_label, ATTR_MAX, "%s %s",
+ OBJ_nid2sn(EVP_MD_type(EVP_sha1())),
+ /* i18n: 14 characters max */
+ gettext(MSG_KEYSTORE_FP));
+
+ switch (format) {
+ case KEYSTORE_FORMAT_PEM:
+ (void) PEM_write_X509(outfile, x);
+ break;
+ case KEYSTORE_FORMAT_DER:
+ (void) i2d_X509_fp(outfile, x);
+ break;
+ case KEYSTORE_FORMAT_TEXT:
+ (void) fprintf(outfile, "%18s: %s\n",
+ /* i18n: 18 characters max */
+ gettext(MSG_KEYSTORE_AL), alias);
+ (void) fprintf(outfile, "%18s: %s\n",
+ /* i18n: 18 characters max */
+ gettext(MSG_KEYSTORE_CN), cn_str);
+ (void) fprintf(outfile, "%18s: %s\n",
+ /* i18n: 18 characters max */
+ gettext(MSG_KEYSTORE_TY), typ_str);
+ (void) fprintf(outfile, "%18s: %s\n",
+ /* i18n: 18 characters max */
+ gettext(MSG_KEYSTORE_IN), icn_str);
+ (void) fprintf(outfile, "%18s: %s\n",
+ /* i18n: 18 characters max */
+ gettext(MSG_KEYSTORE_VD), vd_str);
+ (void) fprintf(outfile, "%18s: %s\n", md5_label, md5_fp);
+ (void) fprintf(outfile, "%18s: %s\n", sha1_label, sha1_fp);
+ (void) fprintf(outfile, "\n");
+ break;
+ default:
+ pkgerr_add(err, PKGERR_INTERNAL,
+ gettext(ERR_KEYSTORE_INTERNAL),
+ __FILE__, __LINE__);
+ ret = 1;
+ goto cleanup;
+ }
+
+cleanup:
+ if (md5_fp != NULL)
+ free(md5_fp);
+ if (sha1_fp != NULL)
+ free(sha1_fp);
+ if (vda_str != NULL)
+ free(vda_str);
+ if (vdb_str != NULL)
+ free(vdb_str);
+ return (ret);
+}
+
+/*
+ * open_keystore - Initialize new keystore object for
+ * impending access.
+ *
+ * Arguments:
+ * err - Error object to append errors to
+ * keystore_file - Base filename or directory of keystore
+ * app - Application making request
+ * passwd - Password used to decrypt keystore
+ * flags - Control flags used to control access mode and behavior
+ * result - Resulting keystore object stored here on success
+ *
+ * Returns:
+ * 0 - Success - result contains a pointer to the opened keystore
+ * non-zero - Failure, errors added to err
+ */
+int
+open_keystore(PKG_ERR *err, char *keystore_file, char *app,
+ keystore_passphrase_cb cb, long flags, keystore_handle_t *result)
+{
+ int ret = 0;
+ keystore_t *tmpstore;
+
+ tmpstore = new_keystore();
+
+ tmpstore->dirty = B_FALSE;
+ tmpstore->new = B_FALSE;
+ tmpstore->path = xstrdup(keystore_file);
+
+ if (!resolve_paths(err, keystore_file, app, flags, tmpstore)) {
+ /* unable to determine keystore paths */
+ pkgerr_add(err, PKGERR_CORRUPT, gettext(ERR_KEYSTORE_REPAIR),
+ keystore_file);
+ ret = 1;
+ goto cleanup;
+ }
+
+ if (!verify_keystore_integrity(err, tmpstore)) {
+ /* unable to repair keystore */
+ pkgerr_add(err, PKGERR_CORRUPT, gettext(ERR_KEYSTORE_REPAIR),
+ keystore_file);
+ ret = 1;
+ goto cleanup;
+ }
+
+ if (!lock_keystore(err, flags, tmpstore)) {
+ pkgerr_add(err, PKGERR_LOCKED, gettext(ERR_KEYSTORE_LOCKED),
+ keystore_file);
+ ret = 1;
+ goto cleanup;
+ }
+
+ /* now that we have locked the keystore, go ahead and read it */
+ if (!read_keystore(err, tmpstore, cb)) {
+ pkgerr_add(err, PKGERR_READ, gettext(ERR_PARSE),
+ keystore_file);
+ ret = 1;
+ goto cleanup;
+ }
+
+ *result = tmpstore;
+ tmpstore = NULL;
+
+cleanup:
+ if (tmpstore != NULL)
+ free_keystore(tmpstore);
+ return (ret);
+}
+
+/*
+ * new_keystore - Allocates and initializes a Keystore object
+ *
+ * Arguments:
+ * NONE
+ *
+ * Returns:
+ * NULL - out of memory
+ * otherwise, returns a pointer to the newly allocated object,
+ * which should be freed with free_keystore() when no longer
+ * needed.
+ */
+static keystore_t
+*new_keystore(void)
+{
+ keystore_t *tmpstore;
+
+ if ((tmpstore = (keystore_t *)malloc(sizeof (keystore_t))) == NULL) {
+ return (NULL);
+ }
+ tmpstore->dirty = B_FALSE;
+ tmpstore->new = B_FALSE;
+ tmpstore->path = NULL;
+ tmpstore->passphrase = NULL;
+ tmpstore->cafd = -1;
+ tmpstore->cacerts = NULL;
+ tmpstore->capath = NULL;
+ tmpstore->clcerts = NULL;
+ tmpstore->clpath = NULL;
+ tmpstore->pkeys = NULL;
+ tmpstore->keypath = NULL;
+
+ return (tmpstore);
+}
+
+/*
+ * free_keystore - Deallocates a Keystore object
+ *
+ * Arguments:
+ * keystore - The keystore to deallocate
+ *
+ * Returns:
+ * NONE
+ */
+static void
+free_keystore(keystore_t *keystore)
+{
+ if (keystore->path != NULL)
+ free(keystore->path);
+ if (keystore->capath != NULL)
+ free(keystore->capath);
+ if (keystore->passphrase != NULL)
+ free(keystore->passphrase);
+ if (keystore->clpath != NULL)
+ free(keystore->clpath);
+ if (keystore->keypath != NULL)
+ free(keystore->keypath);
+
+ if (keystore->pkeys != NULL) {
+ sk_EVP_PKEY_pop_free(keystore->pkeys,
+ sunw_evp_pkey_free);
+ }
+ if (keystore->clcerts != NULL)
+ sk_X509_free(keystore->clcerts);
+ if (keystore->cacerts != NULL)
+ sk_X509_free(keystore->cacerts);
+ free(keystore);
+}
+
+/*
+ * close_keystore - Writes keystore to disk if needed, then
+ * unlocks and closes keystore.
+ *
+ * Arguments:
+ * err - Error object to append errors to
+ * keystore - Keystore which should be closed
+ * passwd - Password used to encrypt keystore
+ *
+ * Returns:
+ * 0 - Success - keystore is committed to disk, and unlocked
+ * non-zero - Failure, errors added to err
+ */
+int
+close_keystore(PKG_ERR *err, keystore_handle_t keystore_h,
+ keystore_passphrase_cb cb)
+{
+ int ret = 0;
+ keystore_t *keystore = keystore_h;
+
+ if (keystore->dirty) {
+ /* write out the keystore first */
+ if (!write_keystore(err, keystore, cb)) {
+ pkgerr_add(err, PKGERR_WRITE,
+ gettext(ERR_KEYSTORE_WRITE),
+ keystore->path);
+ ret = 1;
+ goto cleanup;
+ }
+ }
+
+ if (!unlock_keystore(err, keystore)) {
+ pkgerr_add(err, PKGERR_UNLOCK, gettext(ERR_KEYSTORE_UNLOCK),
+ keystore->path);
+ ret = 1;
+ goto cleanup;
+ }
+
+ free_keystore(keystore);
+cleanup:
+ return (ret);
+}
+
+/*
+ * merge_ca_cert - Adds a trusted certificate (trust anchor) to a keystore.
+ * certificate checked for validity dates and non-duplicity.
+ *
+ * Arguments:
+ * err - Error object to add errors to
+ * cacert - Certificate which to merge into keystore
+ * keystore - The keystore into which the certificate is merged
+ *
+ * Returns:
+ * 0 - Success - Certificate passes validity, and
+ * is merged into keystore
+ * non-zero - Failure, errors recorded in err
+ */
+int
+merge_ca_cert(PKG_ERR *err, X509 *cacert, keystore_handle_t keystore_h)
+{
+
+ int ret = 0;
+ X509 *existing = NULL;
+ char *fname;
+ keystore_t *keystore = keystore_h;
+
+ /* check validity dates */
+ if (check_cert(err, cacert) != 0) {
+ ret = 1;
+ goto cleanup;
+ }
+
+ /* create the certificate's friendlyName */
+ fname = get_subject_display_name(cacert);
+
+ if (sunw_set_fname(fname, NULL, cacert) != 0) {
+ pkgerr_add(err, PKGERR_NOMEM, gettext(ERR_MEM));
+ ret = 1;
+ goto cleanup;
+ }
+
+ /* merge certificate into the keystore */
+ if (keystore->cacerts == NULL) {
+ /* no existing truststore, so make a new one */
+ if ((keystore->cacerts = sk_X509_new_null()) == NULL) {
+ pkgerr_add(err, PKGERR_NOMEM, gettext(ERR_MEM));
+ ret = 1;
+ goto cleanup;
+ }
+ } else {
+ /* existing truststore, make sure there's no duplicate */
+ if (sunw_find_fname(fname, NULL, keystore->cacerts,
+ NULL, &existing) < 0) {
+ pkgerr_add(err, PKGERR_INTERNAL,
+ gettext(ERR_KEYSTORE_INTERNAL),
+ __FILE__, __LINE__);
+ ERR_print_errors_fp(stderr);
+ ret = 1;
+ goto cleanup;
+ /* could not search properly! */
+ }
+ if (existing != NULL) {
+ /* whoops, found one already */
+ pkgerr_add(err, PKGERR_DUPLICATE,
+ gettext(ERR_KEYSTORE_DUPLICATECERT), fname);
+ ret = 1;
+ goto cleanup;
+ }
+ }
+
+ (void) sk_X509_push(keystore->cacerts, cacert);
+ keystore->dirty = B_TRUE;
+cleanup:
+ if (existing != NULL)
+ X509_free(existing);
+ return (ret);
+}
+
+/*
+ * find_key_cert_pair - Searches a keystore for a matching
+ * public key certificate and private key, given an alias.
+ *
+ * Arguments:
+ * err - Error object to add errors to
+ * ks - Keystore to search
+ * alias - Name to used to match certificate's alias
+ * key - Resulting key is placed here
+ * cert - Resulting cert is placed here
+ *
+ * Returns:
+ * 0 - Success - Matching cert/key pair placed in key and cert.
+ * non-zero - Failure, errors recorded in err
+ */
+int
+find_key_cert_pair(PKG_ERR *err, keystore_handle_t ks_h, char *alias,
+ EVP_PKEY **key, X509 **cert)
+{
+ X509 *tmpcert = NULL;
+ EVP_PKEY *tmpkey = NULL;
+ int ret = 0;
+ int items_found;
+ keystore_t *ks = ks_h;
+
+ if (key == NULL || cert == NULL) {
+ pkgerr_add(err, PKGERR_NOPUBKEY,
+ gettext(ERR_KEYSTORE_NOPUBCERTS), ks->path);
+ ret = 1;
+ goto cleanup;
+ }
+
+ if (ks->clcerts == NULL) {
+ /* no public certs */
+ pkgerr_add(err, PKGERR_NOPUBKEY,
+ gettext(ERR_KEYSTORE_NOCERTS), ks->path);
+ ret = 1;
+ goto cleanup;
+ }
+ if (ks->pkeys == NULL) {
+ /* no private keys */
+ pkgerr_add(err, PKGERR_NOPRIVKEY,
+ gettext(ERR_KEYSTORE_NOKEYS), ks->path);
+ ret = 1;
+ goto cleanup;
+ }
+
+ /* try the easy case first */
+ if ((sk_EVP_PKEY_num(ks->pkeys) == 1) &&
+ (sk_X509_num(ks->clcerts) == 1)) {
+ tmpkey = sk_EVP_PKEY_value(ks->pkeys, 0);
+ tmpcert = sk_X509_value(ks->clcerts, 0);
+ if (sunw_check_keys(tmpcert, tmpkey)) {
+ /*
+ * only one private key and public key cert, and they
+ * match, so use them
+ */
+ *key = tmpkey;
+ tmpkey = NULL;
+ *cert = tmpcert;
+ tmpcert = NULL;
+ goto cleanup;
+ }
+ }
+
+ /* Attempt to find the right pair given the alias */
+ items_found = sunw_find_fname(alias, ks->pkeys, ks->clcerts,
+ &tmpkey, &tmpcert);
+
+ if ((items_found < 0) ||
+ (items_found & (FOUND_PKEY | FOUND_CERT)) == 0) {
+ /* no key/cert pair found. bail. */
+ pkgerr_add(err, PKGERR_BADALIAS,
+ gettext(ERR_KEYSTORE_NOMATCH), alias);
+ ret = 1;
+ goto cleanup;
+ }
+
+ /* success */
+ *key = tmpkey;
+ tmpkey = NULL;
+ *cert = tmpcert;
+ tmpcert = NULL;
+
+cleanup:
+
+ if (tmpcert != NULL)
+ (void) X509_free(tmpcert);
+
+ if (tmpkey != NULL)
+ sunw_evp_pkey_free(tmpkey);
+
+ return (ret);
+}
+
+/*
+ * find_ca_certs - Searches a keystore for trusted certificates
+ *
+ * Arguments:
+ * err - Error object to add errors to
+ * ks - Keystore to search
+ * cacerts - resulting set of trusted certs are placed here
+ *
+ * Returns:
+ * 0 - Success - trusted cert list returned in cacerts
+ * non-zero - Failure, errors recorded in err
+ */
+int
+find_ca_certs(PKG_ERR *err, keystore_handle_t ks_h, STACK_OF(X509) **cacerts)
+{
+
+ keystore_t *ks = ks_h;
+
+ /* easy */
+ if (cacerts == NULL) {
+ pkgerr_add(err, PKGERR_INTERNAL,
+ gettext(ERR_KEYSTORE_INTERNAL), __FILE__, __LINE__);
+ return (1);
+ }
+
+ *cacerts = ks->cacerts;
+ return (0);
+}
+
+/*
+ * find_cl_certs - Searches a keystore for user certificates
+ *
+ * Arguments:
+ * err - Error object to add errors to
+ * ks - Keystore to search
+ * cacerts - resulting set of user certs are placed here
+ *
+ * No matching of any kind is performed.
+ * Returns:
+ * 0 - Success - trusted cert list returned in cacerts
+ * non-zero - Failure, errors recorded in err
+ */
+/* ARGSUSED */
+int
+find_cl_certs(PKG_ERR *err, keystore_handle_t ks_h, STACK_OF(X509) **clcerts)
+{
+ keystore_t *ks = ks_h;
+
+ /* easy */
+ *clcerts = ks->clcerts;
+ return (0);
+}
+
+
+/*
+ * merge_cert_and_key - Adds a user certificate and matching
+ * private key to a keystore.
+ * certificate checked for validity dates and non-duplicity.
+ *
+ * Arguments:
+ * err - Error object to add errors to
+ * cert - Certificate which to merge into keystore
+ * key - matching private key to 'cert'
+ * alias - Name which to store the cert and key under
+ * keystore - The keystore into which the certificate is merged
+ *
+ * Returns:
+ * 0 - Success - Certificate passes validity, and
+ * is merged into keystore, along with key
+ * non-zero - Failure, errors recorded in err
+ */
+int
+merge_cert_and_key(PKG_ERR *err, X509 *cert, EVP_PKEY *key, char *alias,
+ keystore_handle_t keystore_h)
+{
+ X509 *existingcert = NULL;
+ EVP_PKEY *existingkey = NULL;
+ int ret = 0;
+ keystore_t *keystore = keystore_h;
+
+ /* check validity dates */
+ if (check_cert(err, cert) != 0) {
+ ret = 1;
+ goto cleanup;
+ }
+
+ /* set the friendlyName of the key and cert to the supplied alias */
+ if (sunw_set_fname(alias, key, cert) != 0) {
+ pkgerr_add(err, PKGERR_NOMEM, gettext(ERR_MEM));
+ ret = 1;
+ goto cleanup;
+ }
+
+ /* merge certificate and key into the keystore */
+ if (keystore->clcerts == NULL) {
+ /* no existing truststore, so make a new one */
+ if ((keystore->clcerts = sk_X509_new_null()) == NULL) {
+ pkgerr_add(err, PKGERR_NOMEM, gettext(ERR_MEM));
+ ret = 1;
+ goto cleanup;
+ }
+ } else {
+ /* existing certstore, make sure there's no duplicate */
+ if (sunw_find_fname(alias, NULL, keystore->clcerts,
+ NULL, &existingcert) < 0) {
+ pkgerr_add(err, PKGERR_INTERNAL,
+ gettext(ERR_KEYSTORE_INTERNAL),
+ __FILE__, __LINE__);
+ ERR_print_errors_fp(stderr);
+ ret = 1;
+ goto cleanup;
+ /* could not search properly! */
+ }
+ if (existingcert != NULL) {
+ /* whoops, found one already */
+ pkgerr_add(err, PKGERR_DUPLICATE,
+ gettext(ERR_KEYSTORE_DUPLICATECERT), alias);
+ ret = 1;
+ goto cleanup;
+ }
+ }
+
+ if (keystore->pkeys == NULL) {
+ /* no existing keystore, so make a new one */
+ if ((keystore->pkeys = sk_EVP_PKEY_new_null()) == NULL) {
+ pkgerr_add(err, PKGERR_NOMEM, gettext(ERR_MEM));
+ ret = 1;
+ goto cleanup;
+ }
+ } else {
+ /* existing keystore, so make sure there's no duplicate entry */
+ if (sunw_find_fname(alias, keystore->pkeys, NULL,
+ &existingkey, NULL) < 0) {
+ pkgerr_add(err, PKGERR_INTERNAL,
+ gettext(ERR_KEYSTORE_INTERNAL),
+ __FILE__, __LINE__);
+ ERR_print_errors_fp(stderr);
+ ret = 1;
+ goto cleanup;
+ /* could not search properly! */
+ }
+ if (existingkey != NULL) {
+ /* whoops, found one already */
+ pkgerr_add(err, PKGERR_DUPLICATE,
+ gettext(ERR_KEYSTORE_DUPLICATEKEY), alias);
+ ret = 1;
+ goto cleanup;
+ }
+ }
+
+ (void) sk_X509_push(keystore->clcerts, cert);
+ (void) sk_EVP_PKEY_push(keystore->pkeys, key);
+ keystore->dirty = B_TRUE;
+cleanup:
+ if (existingcert != NULL)
+ (void) X509_free(existingcert);
+ if (existingkey != NULL)
+ (void) sunw_evp_pkey_free(existingkey);
+ return (ret);
+}
+
+/*
+ * delete_cert_and_keys - Deletes one or more certificates
+ * and matching private keys from a keystore.
+ *
+ * Arguments:
+ * err - Error object to add errors to
+ * ks - The keystore from which certs and keys are deleted
+ * alias - Name which to search for certificates and keys
+ * to delete
+ *
+ * Returns:
+ * 0 - Success - All trusted certs which match 'alias'
+ * are deleted. All user certificates
+ * which match 'alias' are deleted, along
+ * with the matching private key.
+ * non-zero - Failure, errors recorded in err
+ */
+int
+delete_cert_and_keys(PKG_ERR *err, keystore_handle_t ks_h, char *alias)
+{
+ X509 *existingcert;
+ EVP_PKEY *existingkey;
+ int i;
+ char *fname = NULL;
+ boolean_t found = B_FALSE;
+ keystore_t *ks = ks_h;
+
+ /* delete any and all client certs with the supplied name */
+ if (ks->clcerts != NULL) {
+ for (i = 0; i < sk_X509_num(ks->clcerts); i++) {
+ existingcert = sk_X509_value(ks->clcerts, i);
+ if (sunw_get_cert_fname(GETDO_COPY,
+ existingcert, &fname) >= 0) {
+ if (streq(fname, alias)) {
+ /* match, so nuke it */
+ existingcert =
+ sk_X509_delete(ks->clcerts, i);
+ X509_free(existingcert);
+ existingcert = NULL;
+ found = B_TRUE;
+ }
+ (void) OPENSSL_free(fname);
+ fname = NULL;
+ }
+ }
+ if (sk_X509_num(ks->clcerts) <= 0) {
+ /* we deleted all the client certs */
+ sk_X509_free(ks->clcerts);
+ ks->clcerts = NULL;
+ }
+ }
+
+ /* and now the private keys */
+ if (ks->pkeys != NULL) {
+ for (i = 0; i < sk_EVP_PKEY_num(ks->pkeys); i++) {
+ existingkey = sk_EVP_PKEY_value(ks->pkeys, i);
+ if (sunw_get_pkey_fname(GETDO_COPY,
+ existingkey, &fname) >= 0) {
+ if (streq(fname, alias)) {
+ /* match, so nuke it */
+ existingkey =
+ sk_EVP_PKEY_delete(ks->pkeys, i);
+ sunw_evp_pkey_free(existingkey);
+ existingkey = NULL;
+ found = B_TRUE;
+ }
+ (void) OPENSSL_free(fname);
+ fname = NULL;
+ }
+ }
+ if (sk_EVP_PKEY_num(ks->pkeys) <= 0) {
+ /* we deleted all the private keys */
+ sk_EVP_PKEY_free(ks->pkeys);
+ ks->pkeys = NULL;
+ }
+ }
+
+ /* finally, remove any trust anchors that match */
+
+ if (ks->cacerts != NULL) {
+ for (i = 0; i < sk_X509_num(ks->cacerts); i++) {
+ existingcert = sk_X509_value(ks->cacerts, i);
+ if (sunw_get_cert_fname(GETDO_COPY,
+ existingcert, &fname) >= 0) {
+ if (streq(fname, alias)) {
+ /* match, so nuke it */
+ existingcert =
+ sk_X509_delete(ks->cacerts, i);
+ X509_free(existingcert);
+ existingcert = NULL;
+ found = B_TRUE;
+ }
+ (void) OPENSSL_free(fname);
+ fname = NULL;
+ }
+ }
+ if (sk_X509_num(ks->cacerts) <= 0) {
+ /* we deleted all the CA certs */
+ sk_X509_free(ks->cacerts);
+ ks->cacerts = NULL;
+ }
+ }
+
+ if (found) {
+ ks->dirty = B_TRUE;
+ return (0);
+ } else {
+ /* no certs or keys deleted */
+ pkgerr_add(err, PKGERR_NOALIASMATCH,
+ gettext(ERR_KEYSTORE_NOCERTKEY),
+ alias, ks->path);
+ return (1);
+ }
+}
+
+/*
+ * check_cert - Checks certificate validity. This routine
+ * checks that the current time falls within the period
+ * of validity for the cert.
+ *
+ * Arguments:
+ * err - Error object to add errors to
+ * cert - The certificate to check
+ *
+ * Returns:
+ * 0 - Success - Certificate checks out
+ * non-zero - Failure, errors and reasons recorded in err
+ */
+int
+check_cert(PKG_ERR *err, X509 *cert)
+{
+ char currtimestr[ATTR_MAX];
+ time_t currtime;
+ char *r, *before_str, *after_str;
+ /* get current time */
+ if ((currtime = time(NULL)) == (time_t)-1) {
+ pkgerr_add(err, PKGERR_TIME, gettext(ERR_CURR_TIME));
+ return (1);
+ }
+
+ (void) strlcpy(currtimestr, ctime(&currtime), ATTR_MAX);
+
+ /* trim whitespace from end of time string */
+ for (r = (currtimestr + strlen(currtimestr) - 1); isspace(*r); r--) {
+ *r = '\0';
+ }
+ /* check validity of cert */
+ switch (sunw_check_cert_times(CHK_BOTH, cert)) {
+ case CHKERR_TIME_OK:
+ /* Current time meets requested checks */
+ break;
+ case CHKERR_TIME_BEFORE_BAD:
+ /* 'not before' field is invalid */
+ case CHKERR_TIME_AFTER_BAD:
+ /* 'not after' field is invalid */
+ pkgerr_add(err, PKGERR_TIME, gettext(ERR_CERT_TIME_BAD));
+ return (1);
+ case CHKERR_TIME_IS_BEFORE:
+ /* Current time is before 'not before' */
+ case CHKERR_TIME_HAS_EXPIRED:
+ /*
+ * Ignore expiration time since the trust cert used to
+ * verify the certs used to sign Sun patches is already
+ * expired. Once the patches get resigned with the new
+ * cert we will check expiration against the time the
+ * patch was signed and not the time it is installed.
+ */
+ return (0);
+ default:
+ pkgerr_add(err, PKGERR_INTERNAL,
+ gettext(ERR_KEYSTORE_INTERNAL),
+ __FILE__, __LINE__);
+ return (1);
+ }
+
+ /* all checks ok */
+ return (0);
+}
+
+/*
+ * check_cert - Checks certificate validity. This routine
+ * checks everything that check_cert checks, and additionally
+ * verifies that the private key and corresponding public
+ * key are indeed a pair.
+ *
+ * Arguments:
+ * err - Error object to add errors to
+ * cert - The certificate to check
+ * key - the key to check
+ * Returns:
+ * 0 - Success - Certificate checks out
+ * non-zero - Failure, errors and reasons recorded in err
+ */
+int
+check_cert_and_key(PKG_ERR *err, X509 *cert, EVP_PKEY *key)
+{
+
+ /* check validity dates */
+ if (check_cert(err, cert) != 0) {
+ return (1);
+ }
+
+ /* check key pair match */
+ if (sunw_check_keys(cert, key) == 0) {
+ pkgerr_add(err, PKGERR_VERIFY, gettext(ERR_MISMATCHED_KEYS),
+ get_subject_display_name(cert));
+ return (1);
+ }
+
+ /* all checks OK */
+ return (0);
+}
+
+/* ------------------ private functions ---------------------- */
+
+/*
+ * verify_keystore_integrity - Searches for the remnants
+ * of a failed or aborted keystore modification, and
+ * cleans up the files, retstores the keystore to a known
+ * state.
+ *
+ * Arguments:
+ * err - Error object to add errors to
+ * keystore_file - Base directory or filename of keystore
+ * app - Application making request
+ *
+ * Returns:
+ * 0 - Success - Keystore is restored, or untouched in the
+ * case that cleanup was unnecessary
+ * non-zero - Failure, errors and reasons recorded in err
+ */
+static boolean_t
+verify_keystore_integrity(PKG_ERR *err, keystore_t *keystore)
+{
+ if (keystore->capath != NULL) {
+ if (!restore_keystore_file(err, keystore->capath)) {
+ return (B_FALSE);
+ }
+ }
+ if (keystore->clpath != NULL) {
+ if (!restore_keystore_file(err, keystore->clpath)) {
+ return (B_FALSE);
+ }
+ }
+ if (keystore->keypath != NULL) {
+ if (!restore_keystore_file(err, keystore->keypath)) {
+ return (B_FALSE);
+ }
+ }
+ return (B_TRUE);
+}
+
+/*
+ * restore_keystore_file - restores a keystore file to
+ * a known state.
+ *
+ * Keystore files can possibly be corrupted by a variety
+ * of error conditions during reading/writing. This
+ * routine, along with write_keystore_file, tries to
+ * maintain keystore integrity by writing the files
+ * out in a particular order, minimizing the time period
+ * that the keystore is in an indeterminate state.
+ *
+ * With the current implementation, there are some failures
+ * that are wholly unrecoverable, such as disk corruption.
+ * These routines attempt to minimize the risk, but not
+ * eliminate it. When better, atomic operations are available
+ * (such as a trued atabase with commit, rollback, and
+ * guaranteed atomicity), this implementation should use that.
+ *
+ * Arguments:
+ * err - Error object to add errors to
+ * keystore_file - keystore file path to restore.
+ *
+ * Returns:
+ * 0 - Success - Keystore file is restored, or untouched in the
+ * case that cleanup was unnecessary
+ * non-zero - Failure, errors and reasons recorded in err
+ */
+/* ARGSUSED */
+static boolean_t
+restore_keystore_file(PKG_ERR *err, char *keystore_file)
+{
+ char newpath[MAXPATHLEN];
+ char backuppath[MAXPATHLEN];
+ int newfd;
+ struct stat buf;
+ int len;
+
+ if (((len = snprintf(newpath, MAXPATHLEN, "%s.new",
+ keystore_file)) < 0) ||
+ (len >= ATTR_MAX)) {
+ pkgerr_add(err, PKGERR_WEB, gettext(ERR_LEN), keystore_file);
+ return (B_FALSE);
+ }
+
+ if (((len = snprintf(backuppath, MAXPATHLEN, "%s.bak",
+ keystore_file)) < 0) ||
+ (len >= ATTR_MAX)) {
+ pkgerr_add(err, PKGERR_WEB, gettext(ERR_LEN), keystore_file);
+ return (B_FALSE);
+ }
+
+ if ((newfd = open(newpath, O_RDWR|O_NONBLOCK, 0)) != -1) {
+ if (fstat(newfd, &buf) != -1) {
+ if (S_ISREG(buf.st_mode)) {
+ /*
+ * restore the file, waiting on it
+ * to be free for locking, or for
+ * it to disappear
+ */
+ if (!wait_restore(newfd, keystore_file,
+ newpath, backuppath)) {
+ pkgerr_add(err, PKGERR_WRITE,
+ gettext(ERR_WRITE),
+ newpath, strerror(errno));
+ (void) close(newfd);
+ return (B_FALSE);
+ } else {
+ return (B_TRUE);
+ }
+ } else {
+ /* "new" file is not a regular file */
+ pkgerr_add(err, PKGERR_WRITE,
+ gettext(ERR_NOT_REG), newpath);
+ (void) close(newfd);
+ return (B_FALSE);
+ }
+ } else {
+ /* couldn't stat "new" file */
+ pkgerr_add(err, PKGERR_WRITE,
+ gettext(ERR_WRITE), newpath,
+ strerror(errno));
+ (void) close(newfd);
+ return (B_FALSE);
+ }
+ } else {
+ /* "new" file doesn't exist */
+ return (B_TRUE);
+ }
+}
+
+static boolean_t
+wait_restore(int newfd, char *keystore_file,
+ char *origpath, char *backuppath)
+{
+ struct stat buf;
+ FILE *newstream;
+ PKCS12 *p12;
+
+ (void) alarm(LOCK_TIMEOUT);
+ if (file_lock(newfd, F_WRLCK, 1) == -1) {
+ /* could not lock file */
+ (void) alarm(0);
+ return (B_FALSE);
+ }
+ (void) alarm(0);
+
+ if (fstat(newfd, &buf) != -1) {
+ if (S_ISREG(buf.st_mode)) {
+ /*
+ * The new file still
+ * exists, with no
+ * owner. It must be
+ * the result of an
+ * aborted update.
+ */
+ newstream = fdopen(newfd, "r");
+ if ((p12 =
+ d2i_PKCS12_fp(newstream,
+ NULL)) != NULL) {
+ /*
+ * The file
+ * appears
+ * complete.
+ * Replace the
+ * exsisting
+ * keystore
+ * file with
+ * this one
+ */
+ (void) rename(keystore_file, backuppath);
+ (void) rename(origpath, keystore_file);
+ PKCS12_free(p12);
+ } else {
+ /* The file is not complete. Remove it */
+ (void) remove(origpath);
+ }
+ /* remove backup file */
+ (void) remove(backuppath);
+ (void) fclose(newstream);
+ (void) close(newfd);
+ return (B_TRUE);
+ } else {
+ /*
+ * new file exists, but is not a
+ * regular file
+ */
+ (void) close(newfd);
+ return (B_FALSE);
+ }
+ } else {
+ /*
+ * could not stat file. Unless
+ * the reason was that the file
+ * is now gone, this is an error
+ */
+ if (errno != ENOENT) {
+ (void) close(newfd);
+ return (B_FALSE);
+ }
+ /*
+ * otherwise, file is gone. The process
+ * that held the lock must have
+ * successfully cleaned up and
+ * exited with a valid keystore
+ * state
+ */
+ (void) close(newfd);
+ return (B_TRUE);
+ }
+}
+
+/*
+ * resolve_paths - figure out if we are dealing with a single-file
+ * or multi-file keystore
+ *
+ * The flags tell resolve_paths how to behave:
+ *
+ * KEYSTORE_PATH_SOFT
+ * If the keystore file does not exist at <base>/<app> then
+ * use <base> as the path to the keystore. This can be used,
+ * for example, to access an app-specific keystore iff it
+ * exists, otherwise revert back to an app-generic keystore.
+ *
+ * KEYSTORE_PATH_HARD
+ * Always use the keystore located at <keystore_path>/<app>.
+ * In read/write mode, if the files do not exist, then
+ * they will be created. This is used to avoid falling
+ * back to an app-generic keystore path when the app-specific
+ * one does not exist.
+ *
+ * Arguments:
+ * err - Error object to add errors to
+ * keystore_file - base keystore file path to lock
+ * app - Application making requests
+ * flags - Control flags (see above description)
+ * keystore - object which is being locked
+ *
+ * Returns:
+ * B_TRUE - Success - Keystore file is locked, paths to
+ * appropriate files placed in keystore.
+ * B_FALSE - Failure, errors and reasons recorded in err
+ */
+static boolean_t
+resolve_paths(PKG_ERR *err, char *keystore_file, char *app,
+ long flags, keystore_t *keystore)
+{
+ char storepath[PATH_MAX];
+ struct stat buf;
+ boolean_t multi = B_FALSE;
+ int fd1, fd2, len;
+
+ /*
+ * figure out whether we are dealing with a single-file keystore
+ * or a multi-file keystore
+ */
+ if (app != NULL) {
+ if (((len = snprintf(storepath, PATH_MAX, "%s/%s",
+ keystore_file, app)) < 0) ||
+ (len >= ATTR_MAX)) {
+ pkgerr_add(err, PKGERR_WEB, gettext(ERR_LEN),
+ keystore_file);
+ return (B_FALSE);
+ }
+
+ if (((fd1 = open(storepath, O_NONBLOCK|O_RDONLY)) == -1) ||
+ (fstat(fd1, &buf) == -1) ||
+ !S_ISDIR(buf.st_mode)) {
+ /*
+ * app-specific does not exist
+ * fallback to app-generic, if flags say we can
+ */
+ if ((flags & KEYSTORE_PATH_MASK) ==
+ KEYSTORE_PATH_SOFT) {
+
+ if (((fd2 = open(keystore_file,
+ O_NONBLOCK|O_RDONLY)) != -1) &&
+ (fstat(fd2, &buf) != -1)) {
+ if (S_ISDIR(buf.st_mode)) {
+ /*
+ * app-generic dir
+ * exists, so use it
+ * as a multi-file
+ * keystore
+ */
+ multi = B_TRUE;
+ app = NULL;
+ } else if (S_ISREG(buf.st_mode)) {
+ /*
+ * app-generic file exists, so
+ * use it as a single file ks
+ */
+ multi = B_FALSE;
+ app = NULL;
+ }
+ }
+ }
+ }
+ if (fd1 != -1)
+ (void) close(fd1);
+ if (fd2 != -1)
+ (void) close(fd2);
+ } else {
+ if (((fd1 = open(keystore_file,
+ O_NONBLOCK|O_RDONLY)) != -1) &&
+ (fstat(fd1, &buf) != -1) &&
+ S_ISDIR(buf.st_mode)) {
+ /*
+ * app-generic dir exists, so use
+ * it as a multi-file keystore
+ */
+ multi = B_TRUE;
+ }
+ if (fd1 != -1)
+ (void) close(fd1);
+ }
+
+ if (app != NULL) {
+ /* app-specific keystore */
+ (void) snprintf(storepath, PATH_MAX, "%s/%s/%s",
+ keystore_file, app, TRUSTSTORE);
+ keystore->capath = xstrdup(storepath);
+ (void) snprintf(storepath, PATH_MAX, "%s/%s/%s",
+ keystore_file, app, CERTSTORE);
+ keystore->clpath = xstrdup(storepath);
+ (void) snprintf(storepath, PATH_MAX, "%s/%s/%s",
+ keystore_file, app, KEYSTORE);
+ keystore->keypath = xstrdup(storepath);
+ } else {
+ /* app-generic keystore */
+ if (!multi) {
+ /* single-file app-generic keystore */
+ keystore->capath = xstrdup(keystore_file);
+ keystore->keypath = NULL;
+ keystore->clpath = NULL;
+ } else {
+ /* multi-file app-generic keystore */
+ (void) snprintf(storepath, PATH_MAX, "%s/%s",
+ keystore_file, TRUSTSTORE);
+ keystore->capath = xstrdup(storepath);
+ (void) snprintf(storepath, PATH_MAX, "%s/%s",
+ keystore_file, CERTSTORE);
+ keystore->clpath = xstrdup(storepath);
+ (void) snprintf(storepath, PATH_MAX, "%s/%s",
+ keystore_file, KEYSTORE);
+ keystore->keypath = xstrdup(storepath);
+ }
+ }
+
+ return (B_TRUE);
+}
+
+/*
+ * lock_keystore - Locks a keystore for shared (read-only)
+ * or exclusive (read-write) access.
+ *
+ * The flags tell lock_keystore how to behave:
+ *
+ * KEYSTORE_ACCESS_READONLY
+ * opens keystore read-only. Attempts to modify results in an error
+ *
+ * KEYSTORE_ACCESS_READWRITE
+ * opens keystore read-write
+ *
+ * KEYSTORE_PATH_SOFT
+ * If the keystore file does not exist at <base>/<app> then
+ * use <base> as the path to the keystore. This can be used,
+ * for example, to access an app-specific keystore iff it
+ * exists, otherwise revert back to an app-generic keystore.
+ *
+ * KEYSTORE_PATH_HARD
+ * Always use the keystore located at <keystore_path>/<app>.
+ * In read/write mode, if the files do not exist, then
+ * they will be created. This is used to avoid falling
+ * back to an app-generic keystore path when the app-specific
+ * one does not exist.
+ *
+ * Arguments:
+ * err - Error object to add errors to
+ * flags - Control flags (see above description)
+ * keystore - object which is being locked
+ *
+ * Returns:
+ * 0 - Success - Keystore file is locked, paths to
+ * appropriate files placed in keystore.
+ * non-zero - Failure, errors and reasons recorded in err
+ */
+static boolean_t
+lock_keystore(PKG_ERR *err, long flags, keystore_t *keystore)
+{
+ boolean_t ret = B_TRUE;
+ struct stat buf;
+
+ switch (flags & KEYSTORE_ACCESS_MASK) {
+ case KEYSTORE_ACCESS_READONLY:
+ if ((keystore->cafd =
+ open(keystore->capath, O_NONBLOCK|O_RDONLY)) == -1) {
+ if (errno == ENOENT) {
+ /*
+ * no keystore. try to create an
+ * empty one so we can lock on it and
+ * prevent others from gaining
+ * exclusive access. It will be
+ * deleted when the keystore is closed.
+ */
+ if ((keystore->cafd =
+ open(keystore->capath,
+ O_NONBLOCK|O_RDWR|O_CREAT|O_EXCL,
+ S_IRUSR|S_IWUSR)) == -1) {
+ pkgerr_add(err, PKGERR_READ,
+ gettext(ERR_NO_KEYSTORE),
+ keystore->capath);
+ ret = B_FALSE;
+ goto cleanup;
+ }
+ } else {
+ pkgerr_add(err, PKGERR_READ,
+ gettext(ERR_KEYSTORE_OPEN),
+ keystore->capath, strerror(errno));
+ ret = B_FALSE;
+ goto cleanup;
+ }
+ }
+ if (fstat(keystore->cafd, &buf) != -1) {
+ if (S_ISREG(buf.st_mode)) {
+ if (file_lock(keystore->cafd, F_RDLCK,
+ 0) == -1) {
+ pkgerr_add(err, PKGERR_LOCKED,
+ gettext(ERR_KEYSTORE_LOCKED_READ),
+ keystore->capath);
+ ret = B_FALSE;
+ goto cleanup;
+ }
+ } else {
+ /* ca file not a regular file! */
+ pkgerr_add(err, PKGERR_READ,
+ gettext(ERR_NOT_REG),
+ keystore->capath);
+ ret = B_FALSE;
+ goto cleanup;
+ }
+ } else {
+ pkgerr_add(err, PKGERR_READ,
+ gettext(ERR_KEYSTORE_OPEN),
+ keystore->capath, strerror(errno));
+ ret = B_FALSE;
+ goto cleanup;
+ }
+ break;
+ case KEYSTORE_ACCESS_READWRITE:
+
+ if ((keystore->cafd = open(keystore->capath,
+ O_RDWR|O_NONBLOCK)) == -1) {
+ /* does not exist. try to create an empty one */
+ if (errno == ENOENT) {
+ if ((keystore->cafd =
+ open(keystore->capath,
+ O_NONBLOCK|O_RDWR|O_CREAT|O_EXCL,
+ S_IRUSR|S_IWUSR)) == -1) {
+ pkgerr_add(err, PKGERR_READ,
+ gettext(ERR_KEYSTORE_WRITE),
+ keystore->capath);
+ ret = B_FALSE;
+ goto cleanup;
+ }
+ } else {
+ pkgerr_add(err, PKGERR_READ,
+ gettext(ERR_KEYSTORE_OPEN),
+ keystore->capath, strerror(errno));
+ ret = B_FALSE;
+ goto cleanup;
+ }
+ }
+ if (fstat(keystore->cafd, &buf) != -1) {
+ if (S_ISREG(buf.st_mode)) {
+ if (file_lock(keystore->cafd, F_WRLCK,
+ 0) == -1) {
+ pkgerr_add(err, PKGERR_LOCKED,
+ gettext(ERR_KEYSTORE_LOCKED),
+ keystore->capath);
+ ret = B_FALSE;
+ goto cleanup;
+ }
+ } else {
+ /* ca file not a regular file! */
+ pkgerr_add(err, PKGERR_READ,
+ gettext(ERR_NOT_REG),
+ keystore->capath);
+ ret = B_FALSE;
+ goto cleanup;
+ }
+ } else {
+ pkgerr_add(err, PKGERR_READ,
+ gettext(ERR_KEYSTORE_OPEN),
+ keystore->capath, strerror(errno));
+ ret = B_FALSE;
+ goto cleanup;
+ }
+
+ break;
+ default:
+ pkgerr_add(err, PKGERR_INTERNAL,
+ gettext(ERR_KEYSTORE_INTERNAL),
+ __FILE__, __LINE__);
+ ret = B_FALSE;
+ goto cleanup;
+ }
+
+cleanup:
+ if (!ret) {
+ if (keystore->cafd > 0) {
+ (void) file_unlock(keystore->cafd);
+ (void) close(keystore->cafd);
+ keystore->cafd = -1;
+ }
+
+ if (keystore->capath != NULL)
+ free(keystore->capath);
+ if (keystore->clpath != NULL)
+ free(keystore->clpath);
+ if (keystore->keypath != NULL)
+ free(keystore->keypath);
+ keystore->capath = NULL;
+ keystore->clpath = NULL;
+ keystore->keypath = NULL;
+ }
+
+ return (ret);
+}
+
+/*
+ * unlock_keystore - Unocks a keystore
+ *
+ * Arguments:
+ * err - Error object to add errors to
+ * keystore - keystore object to unlock
+ * Returns:
+ * 0 - Success - Keystore files are unlocked, files are closed,
+ * non-zero - Failure, errors and reasons recorded in err
+ */
+/* ARGSUSED */
+static boolean_t
+unlock_keystore(PKG_ERR *err, keystore_t *keystore)
+{
+
+ /*
+ * Release lock on the CA file.
+ * Delete file if it is empty
+ */
+ if (file_empty(keystore->capath)) {
+ (void) remove(keystore->capath);
+ }
+
+ (void) file_unlock(keystore->cafd);
+ (void) close(keystore->cafd);
+ return (B_TRUE);
+}
+
+/*
+ * read_keystore - Reads keystore files of disk, parses
+ * into internal structures.
+ *
+ * Arguments:
+ * err - Error object to add errors to
+ * keystore - keystore object to read into
+ * cb - callback to get password, if required
+ * Returns:
+ * 0 - Success - Keystore files are read, and placed
+ * into keystore structure.
+ * non-zero - Failure, errors and reasons recorded in err
+ */
+static boolean_t
+read_keystore(PKG_ERR *err, keystore_t *keystore, keystore_passphrase_cb cb)
+{
+ boolean_t ret = B_TRUE;
+ PKCS12 *p12 = NULL;
+ boolean_t ca_empty;
+ boolean_t have_passwd = B_FALSE;
+ boolean_t cl_empty = B_TRUE;
+ boolean_t key_empty = B_TRUE;
+
+ ca_empty = file_empty(keystore->capath);
+
+ if (keystore->clpath != NULL)
+ cl_empty = file_empty(keystore->clpath);
+ if (keystore->keypath != NULL)
+ key_empty = file_empty(keystore->keypath);
+
+ if (ca_empty && cl_empty && key_empty) {
+ keystore->new = B_TRUE;
+ }
+
+ if (!ca_empty) {
+ /* first read the ca file */
+ if ((p12 = read_keystore_file(err,
+ keystore->capath)) == NULL) {
+ pkgerr_add(err, PKGERR_CORRUPT,
+ gettext(ERR_KEYSTORE_CORRUPT), keystore->capath);
+ ret = B_FALSE;
+ goto cleanup;
+ }
+
+ /* Get password, using callback if necessary */
+ if (!have_passwd) {
+ if (!get_keystore_passwd(err, p12, cb, keystore)) {
+ ret = B_FALSE;
+ goto cleanup;
+ }
+ have_passwd = B_TRUE;
+ }
+
+ /* decrypt and parse keystore file */
+ if (sunw_PKCS12_contents(p12, keystore->passphrase,
+ &keystore->pkeys, &keystore->cacerts) < 0) {
+ /* could not parse the contents */
+ pkgerr_add(err, PKGERR_CORRUPT,
+ gettext(ERR_KEYSTORE_CORRUPT), keystore->capath);
+ ret = B_FALSE;
+ goto cleanup;
+ }
+
+ PKCS12_free(p12);
+ p12 = NULL;
+ } else {
+
+ /*
+ * truststore is empty, so we don't have any trusted
+ * certs
+ */
+ keystore->cacerts = NULL;
+ }
+
+ /*
+ * if there is no cl file or key file, use the cl's and key's found
+ * in the ca file
+ */
+ if (keystore->clpath == NULL && !ca_empty) {
+ if (sunw_split_certs(keystore->pkeys, keystore->cacerts,
+ &keystore->clcerts, NULL) < 0) {
+ pkgerr_add(err, PKGERR_CORRUPT,
+ gettext(ERR_KEYSTORE_CORRUPT), keystore->capath);
+ ret = B_FALSE;
+ goto cleanup;
+ }
+ } else {
+ /*
+ * files are in separate files. read keys out of the keystore
+ * certs out of the certstore, if they are not empty
+ */
+ if (!cl_empty) {
+ if ((p12 = read_keystore_file(err,
+ keystore->clpath)) == NULL) {
+ pkgerr_add(err, PKGERR_CORRUPT,
+ gettext(ERR_KEYSTORE_CORRUPT),
+ keystore->clpath);
+ ret = B_FALSE;
+ goto cleanup;
+ }
+
+ /* Get password, using callback if necessary */
+ if (!have_passwd) {
+ if (!get_keystore_passwd(err, p12, cb,
+ keystore)) {
+ ret = B_FALSE;
+ goto cleanup;
+ }
+ have_passwd = B_TRUE;
+ }
+
+ if (check_password(p12,
+ keystore->passphrase) == B_FALSE) {
+ /*
+ * password in client cert file
+ * is different than
+ * the one in the other files!
+ */
+ pkgerr_add(err, PKGERR_BADPASS,
+ gettext(ERR_MISMATCHPASS),
+ keystore->clpath,
+ keystore->capath, keystore->path);
+ ret = B_FALSE;
+ goto cleanup;
+ }
+
+ if (sunw_PKCS12_contents(p12, keystore->passphrase,
+ NULL, &keystore->clcerts) < 0) {
+ /* could not parse the contents */
+ pkgerr_add(err, PKGERR_CORRUPT,
+ gettext(ERR_KEYSTORE_CORRUPT),
+ keystore->clpath);
+ ret = B_FALSE;
+ goto cleanup;
+ }
+
+ PKCS12_free(p12);
+ p12 = NULL;
+ } else {
+ keystore->clcerts = NULL;
+ }
+
+ if (!key_empty) {
+ if ((p12 = read_keystore_file(err,
+ keystore->keypath)) == NULL) {
+ pkgerr_add(err, PKGERR_CORRUPT,
+ gettext(ERR_KEYSTORE_CORRUPT),
+ keystore->keypath);
+ ret = B_FALSE;
+ goto cleanup;
+ }
+
+ /* Get password, using callback if necessary */
+ if (!have_passwd) {
+ if (!get_keystore_passwd(err, p12, cb,
+ keystore)) {
+ ret = B_FALSE;
+ goto cleanup;
+ }
+ have_passwd = B_TRUE;
+ }
+
+ if (check_password(p12,
+ keystore->passphrase) == B_FALSE) {
+ pkgerr_add(err, PKGERR_BADPASS,
+ gettext(ERR_MISMATCHPASS),
+ keystore->keypath,
+ keystore->capath, keystore->path);
+ ret = B_FALSE;
+ goto cleanup;
+ }
+
+ if (sunw_PKCS12_contents(p12, keystore->passphrase,
+ &keystore->pkeys, NULL) < 0) {
+ /* could not parse the contents */
+ pkgerr_add(err, PKGERR_CORRUPT,
+ gettext(ERR_KEYSTORE_CORRUPT),
+ keystore->keypath);
+ ret = B_FALSE;
+ goto cleanup;
+ }
+
+ PKCS12_free(p12);
+ p12 = NULL;
+ } else {
+ keystore->pkeys = NULL;
+ }
+ }
+
+cleanup:
+ if (p12 != NULL)
+ PKCS12_free(p12);
+ return (ret);
+}
+
+/*
+ * get_keystore_password - retrieves pasword used to
+ * decrypt PKCS12 structure.
+ *
+ * Arguments:
+ * err - Error object to add errors to
+ * p12 - PKCS12 structure which returned password should
+ * decrypt
+ * cb - callback to collect password.
+ * keystore - The keystore in which the PKCS12 structure
+ * will eventually populate.
+ * Returns:
+ * B_TRUE - success.
+ * keystore password is set in keystore->passphrase.
+ * B_FALSE - failure, errors logged
+ */
+static boolean_t
+get_keystore_passwd(PKG_ERR *err, PKCS12 *p12, keystore_passphrase_cb cb,
+ keystore_t *keystore)
+{
+ char *passwd;
+ char passbuf[KEYSTORE_PASS_MAX + 1];
+ keystore_passphrase_data data;
+
+ /* see if no password is the right password */
+ if (check_password(p12, "") == B_TRUE) {
+ passwd = "";
+ } else if (check_password(p12, NULL) == B_TRUE) {
+ passwd = NULL;
+ } else {
+ /* oops, it's encrypted. get password */
+ data.err = err;
+ if (cb(passbuf, KEYSTORE_PASS_MAX, 0,
+ &data) == -1) {
+ /* could not get password */
+ return (B_FALSE);
+ }
+
+ if (check_password(p12, passbuf) == B_FALSE) {
+ /* wrong password */
+ pkgerr_add(err, PKGERR_BADPASS,
+ gettext(ERR_BADPASS));
+ return (B_FALSE);
+ }
+
+ /*
+ * make copy of password buffer, since it
+ * goes away upon return
+ */
+ passwd = xstrdup(passbuf);
+ }
+ keystore->passphrase = passwd;
+ return (B_TRUE);
+}
+
+/*
+ * write_keystore - Writes keystore files to disk
+ *
+ * Arguments:
+ * err - Error object to add errors to
+ * keystore - keystore object to write from
+ * passwd - password used to encrypt keystore
+ * Returns:
+ * 0 - Success - Keystore contents are written out to
+ * the same locations as read from
+ * non-zero - Failure, errors and reasons recorded in err
+ */
+static boolean_t
+write_keystore(PKG_ERR *err, keystore_t *keystore,
+ keystore_passphrase_cb cb)
+{
+ PKCS12 *p12 = NULL;
+ boolean_t ret = B_TRUE;
+ keystore_passphrase_data data;
+ char passbuf[KEYSTORE_PASS_MAX + 1];
+
+ if (keystore->capath != NULL && keystore->clpath == NULL &&
+ keystore->keypath == NULL) {
+
+ /*
+ * keystore is a file.
+ * just write out a single file
+ */
+ if ((keystore->pkeys == NULL) &&
+ (keystore->clcerts == NULL) &&
+ (keystore->cacerts == NULL)) {
+ if (!clear_keystore_file(err, keystore->capath)) {
+ /*
+ * no keys or certs to write out, so
+ * blank the ca file. we do not
+ * delete it since it is used as a
+ * lock by lock_keystore() in
+ * subsequent invocations
+ */
+ pkgerr_add(err, PKGERR_WRITE,
+ gettext(ERR_KEYSTORE_WRITE),
+ keystore->capath);
+ ret = B_FALSE;
+ goto cleanup;
+ }
+ } else {
+ /*
+ * if the keystore is being created for the first time,
+ * prompt for a passphrase for encryption
+ */
+ if (keystore->new) {
+ data.err = err;
+ if (cb(passbuf, KEYSTORE_PASS_MAX,
+ 1, &data) == -1) {
+ ret = B_FALSE;
+ goto cleanup;
+ }
+ } else {
+ /*
+ * use the one used when the keystore
+ * was read
+ */
+ strlcpy(passbuf, keystore->passphrase,
+ KEYSTORE_PASS_MAX);
+ }
+
+ p12 = sunw_PKCS12_create(passbuf, keystore->pkeys,
+ keystore->clcerts, keystore->cacerts);
+
+ if (p12 == NULL) {
+ pkgerr_add(err, PKGERR_WRITE,
+ gettext(ERR_KEYSTORE_FORM),
+ keystore->capath);
+ ret = B_FALSE;
+ goto cleanup;
+ }
+
+ if (!write_keystore_file(err, keystore->capath, p12)) {
+ pkgerr_add(err, PKGERR_WRITE,
+ gettext(ERR_KEYSTORE_WRITE),
+ keystore->capath);
+ ret = B_FALSE;
+ goto cleanup;
+ }
+ }
+
+ } else {
+ /* files are seprate. Do one at a time */
+
+ /*
+ * if the keystore is being created for the first time,
+ * prompt for a passphrase for encryption
+ */
+ if (keystore->new && ((keystore->pkeys != NULL) ||
+ (keystore->clcerts != NULL) ||
+ (keystore->cacerts != NULL))) {
+ data.err = err;
+ if (cb(passbuf, KEYSTORE_PASS_MAX,
+ 1, &data) == -1) {
+ ret = B_FALSE;
+ goto cleanup;
+ }
+ } else {
+ /* use the one used when the keystore was read */
+ strlcpy(passbuf, keystore->passphrase,
+ KEYSTORE_PASS_MAX);
+ }
+
+ /* do private keys first */
+ if (keystore->pkeys != NULL) {
+ p12 = sunw_PKCS12_create(passbuf, keystore->pkeys,
+ NULL, NULL);
+
+ if (p12 == NULL) {
+ pkgerr_add(err, PKGERR_WRITE,
+ gettext(ERR_KEYSTORE_FORM),
+ keystore->keypath);
+ ret = B_FALSE;
+ goto cleanup;
+ }
+
+ if (!write_keystore_file(err, keystore->keypath,
+ p12)) {
+ pkgerr_add(err, PKGERR_WRITE,
+ gettext(ERR_KEYSTORE_WRITE),
+ keystore->keypath);
+ ret = B_FALSE;
+ goto cleanup;
+ }
+
+ PKCS12_free(p12);
+ } else {
+ if ((remove(keystore->keypath) != 0) &&
+ (errno != ENOENT)) {
+ pkgerr_add(err, PKGERR_WRITE,
+ gettext(ERR_KEYSTORE_REMOVE),
+ keystore->keypath);
+ ret = B_FALSE;
+ goto cleanup;
+ }
+ }
+
+ /* do user certs next */
+ if (keystore->clcerts != NULL) {
+ p12 = sunw_PKCS12_create(passbuf, NULL,
+ keystore->clcerts, NULL);
+
+ if (p12 == NULL) {
+ pkgerr_add(err, PKGERR_WRITE,
+ gettext(ERR_KEYSTORE_FORM),
+ keystore->clpath);
+ ret = B_FALSE;
+ goto cleanup;
+ }
+
+ if (!write_keystore_file(err, keystore->clpath, p12)) {
+ pkgerr_add(err, PKGERR_WRITE,
+ gettext(ERR_KEYSTORE_WRITE),
+ keystore->clpath);
+ ret = B_FALSE;
+ goto cleanup;
+ }
+
+ PKCS12_free(p12);
+ } else {
+ if ((remove(keystore->clpath) != 0) &&
+ (errno != ENOENT)) {
+ pkgerr_add(err, PKGERR_WRITE,
+ gettext(ERR_KEYSTORE_REMOVE),
+ keystore->clpath);
+ ret = B_FALSE;
+ goto cleanup;
+ }
+ }
+
+
+ /* finally do CA cert file */
+ if (keystore->cacerts != NULL) {
+ p12 = sunw_PKCS12_create(passbuf, NULL,
+ NULL, keystore->cacerts);
+
+ if (p12 == NULL) {
+ pkgerr_add(err, PKGERR_WRITE,
+ gettext(ERR_KEYSTORE_FORM),
+ keystore->capath);
+ ret = B_FALSE;
+ goto cleanup;
+ }
+
+ if (!write_keystore_file(err, keystore->capath, p12)) {
+ pkgerr_add(err, PKGERR_WRITE,
+ gettext(ERR_KEYSTORE_WRITE),
+ keystore->capath);
+ ret = B_FALSE;
+ goto cleanup;
+ }
+
+ PKCS12_free(p12);
+ p12 = NULL;
+ } else {
+ /*
+ * nothing to write out, so truncate the file
+ * (it will be deleted during close_keystore)
+ */
+ if (!clear_keystore_file(err, keystore->capath)) {
+ pkgerr_add(err, PKGERR_WRITE,
+ gettext(ERR_KEYSTORE_WRITE),
+ keystore->capath);
+ ret = B_FALSE;
+ goto cleanup;
+ }
+ }
+ }
+
+cleanup:
+ if (p12 != NULL)
+ PKCS12_free(p12);
+
+ return (ret);
+}
+
+/*
+ * clear_keystore_file - Clears (zeros out) a keystore file.
+ *
+ * Arguments:
+ * err - Error object to add errors to
+ * dest - Path of keystore file to zero out.
+ * Returns:
+ * 0 - Success - Keystore file is truncated to zero length
+ * non-zero - Failure, errors and reasons recorded in err
+ */
+static boolean_t
+clear_keystore_file(PKG_ERR *err, char *dest)
+{
+ int fd;
+ struct stat buf;
+
+ fd = open(dest, O_RDWR|O_NONBLOCK);
+ if (fd == -1) {
+ /* can't open for writing */
+ pkgerr_add(err, PKGERR_WRITE, gettext(MSG_OPEN),
+ errno);
+ return (B_FALSE);
+ }
+
+ if ((fstat(fd, &buf) == -1) || !S_ISREG(buf.st_mode)) {
+ /* not a regular file */
+ (void) close(fd);
+ pkgerr_add(err, PKGERR_WRITE, gettext(ERR_NOT_REG),
+ dest);
+ return (B_FALSE);
+ }
+
+ if (ftruncate(fd, 0) == -1) {
+ (void) close(fd);
+ pkgerr_add(err, PKGERR_WRITE, gettext(ERR_WRITE),
+ dest, strerror(errno));
+ return (B_FALSE);
+ }
+
+ (void) close(fd);
+ return (B_TRUE);
+}
+
+/*
+ * write_keystore_file - Writes keystore file to disk.
+ *
+ * Keystore files can possibly be corrupted by a variety
+ * of error conditions during reading/writing. This
+ * routine, along with restore_keystore_file, tries to
+ * maintain keystore integity by writing the files
+ * out in a particular order, minimizing the time period
+ * that the keystore is in an indeterminate state.
+ *
+ * With the current implementation, there are some failures
+ * that are wholly unrecoverable, such as disk corruption.
+ * These routines attempt to minimize the risk, but not
+ * eliminate it. When better, atomic operations are available
+ * (such as a true database with commit, rollback, and
+ * guaranteed atomicity), this implementation should use that.
+ *
+ *
+ * Arguments:
+ * err - Error object to add errors to
+ * dest - Destination filename
+ * contents - Contents to write to the file
+ * Returns:
+ * 0 - Success - Keystore contents are written out to
+ * the destination.
+ * non-zero - Failure, errors and reasons recorded in err
+ */
+static boolean_t
+write_keystore_file(PKG_ERR *err, char *dest, PKCS12 *contents)
+{
+ FILE *newfile = NULL;
+ boolean_t ret = B_TRUE;
+ char newpath[MAXPATHLEN];
+ char backuppath[MAXPATHLEN];
+ struct stat buf;
+ int fd;
+
+ (void) snprintf(newpath, MAXPATHLEN, "%s.new", dest);
+ (void) snprintf(backuppath, MAXPATHLEN, "%s.bak", dest);
+
+ if ((fd = open(newpath, O_CREAT|O_EXCL|O_WRONLY|O_NONBLOCK,
+ S_IRUSR|S_IWUSR)) == -1) {
+ pkgerr_add(err, PKGERR_READ, gettext(ERR_KEYSTORE_OPEN),
+ newpath, strerror(errno));
+ ret = B_FALSE;
+ goto cleanup;
+ }
+
+ if (fstat(fd, &buf) == -1) {
+ pkgerr_add(err, PKGERR_READ, gettext(ERR_KEYSTORE_OPEN),
+ newpath, strerror(errno));
+ ret = B_FALSE;
+ goto cleanup;
+ }
+
+ if (!S_ISREG(buf.st_mode)) {
+ pkgerr_add(err, PKGERR_READ, gettext(ERR_NOT_REG),
+ newpath);
+ ret = B_FALSE;
+ goto cleanup;
+ }
+
+ if ((newfile = fdopen(fd, "w")) == NULL) {
+ pkgerr_add(err, PKGERR_READ, gettext(ERR_KEYSTORE_OPEN),
+ newpath, strerror(errno));
+ ret = B_FALSE;
+ goto cleanup;
+ }
+
+ if (i2d_PKCS12_fp(newfile, contents) == 0) {
+ pkgerr_add(err, PKGERR_WRITE, gettext(ERR_KEYSTORE_WRITE),
+ newpath);
+ ret = B_FALSE;
+ goto cleanup;
+ }
+
+ /* flush, then close */
+ (void) fflush(newfile);
+ (void) fclose(newfile);
+ newfile = NULL;
+
+ /* now back up the original file */
+ (void) rename(dest, backuppath);
+
+ /* put new one in its place */
+ (void) rename(newpath, dest);
+
+ /* remove backup */
+ (void) remove(backuppath);
+
+cleanup:
+ if (newfile != NULL)
+ (void) fclose(newfile);
+ if (fd != -1)
+ (void) close(fd);
+
+ return (ret);
+}
+
+/*
+ * read_keystore_file - Reads single keystore file
+ * off disk in PKCS12 format.
+ *
+ * Arguments:
+ * err - Error object to add errors to
+ * file - File path to read
+ * Returns:
+ * PKCS12 contents of file, or NULL if an error occurred.
+ * errors recorded in 'err'.
+ */
+static PKCS12
+*read_keystore_file(PKG_ERR *err, char *file)
+{
+ int fd;
+ struct stat buf;
+ FILE *newfile;
+ PKCS12 *p12 = NULL;
+
+ if ((fd = open(file, O_RDONLY|O_NONBLOCK)) == -1) {
+ pkgerr_add(err, PKGERR_READ, gettext(ERR_KEYSTORE_OPEN),
+ file, strerror(errno));
+ goto cleanup;
+ }
+
+ if (fstat(fd, &buf) == -1) {
+ pkgerr_add(err, PKGERR_READ, gettext(ERR_KEYSTORE_OPEN),
+ file, strerror(errno));
+ goto cleanup;
+ }
+
+ if (!S_ISREG(buf.st_mode)) {
+ pkgerr_add(err, PKGERR_READ, gettext(ERR_NOT_REG),
+ file);
+ goto cleanup;
+ }
+
+ if ((newfile = fdopen(fd, "r")) == NULL) {
+ pkgerr_add(err, PKGERR_READ, gettext(ERR_KEYSTORE_OPEN),
+ file, strerror(errno));
+ goto cleanup;
+ }
+
+ if ((p12 = d2i_PKCS12_fp(newfile, NULL)) == NULL) {
+ pkgerr_add(err, PKGERR_CORRUPT,
+ gettext(ERR_KEYSTORE_CORRUPT), file);
+ goto cleanup;
+ }
+
+cleanup:
+ if (newfile != NULL)
+ (void) fclose(newfile);
+ if (fd != -1)
+ (void) close(fd);
+
+ return (p12);
+}
+
+
+/*
+ * Locks the specified file.
+ */
+static int
+file_lock(int fd, int type, int wait)
+{
+ struct flock lock;
+
+ lock.l_type = type;
+ lock.l_start = 0;
+ lock.l_whence = SEEK_SET;
+ lock.l_len = 0;
+
+ if (!wait) {
+ if (file_lock_test(fd, type)) {
+ /*
+ * The caller would have to wait to get the
+ * lock on this file.
+ */
+ return (-1);
+ }
+ }
+
+ return (fcntl(fd, F_SETLKW, &lock));
+}
+
+/*
+ * Returns FALSE if the file is not locked; TRUE
+ * otherwise.
+ */
+static boolean_t
+file_lock_test(int fd, int type)
+{
+ struct flock lock;
+
+ lock.l_type = type;
+ lock.l_start = 0;
+ lock.l_whence = SEEK_SET;
+ lock.l_len = 0;
+
+ if (fcntl(fd, F_GETLK, &lock) != -1) {
+ if (lock.l_type != F_UNLCK) {
+ /*
+ * The caller would have to wait to get the
+ * lock on this file.
+ */
+ return (B_TRUE);
+ }
+ }
+
+ /*
+ * The file is not locked.
+ */
+ return (B_FALSE);
+}
+
+/*
+ * Unlocks the specified file.
+ */
+static int
+file_unlock(int fd)
+{
+ struct flock lock;
+
+ lock.l_type = F_UNLCK;
+ lock.l_start = 0;
+ lock.l_whence = SEEK_SET;
+ lock.l_len = 0;
+
+ return (fcntl(fd, F_SETLK, &lock));
+}
+
+/*
+ * Determines if file has a length of 0 or not
+ */
+static boolean_t
+file_empty(char *path)
+{
+ struct stat buf;
+
+ /* file is empty if size = 0 or it doesn't exist */
+ if (lstat(path, &buf) == 0) {
+ if (buf.st_size == 0) {
+ return (B_TRUE);
+ }
+ } else {
+ if (errno == ENOENT) {
+ return (B_TRUE);
+ }
+ }
+
+ return (B_FALSE);
+}
+
+/*
+ * Name: get_time_string
+ * Description: Generates a human-readable string from an ASN1_TIME
+ *
+ * Arguments: intime - The time to convert
+ *
+ * Returns : A pointer to a static string representing the passed-in time.
+ */
+static char
+*get_time_string(ASN1_TIME *intime)
+{
+
+ static char time[ATTR_MAX];
+ BIO *mem;
+ char *p;
+
+ if (intime == NULL) {
+ return (NULL);
+ }
+ if ((mem = BIO_new(BIO_s_mem())) == NULL) {
+ return (NULL);
+ }
+
+ if (ASN1_TIME_print(mem, intime) == 0) {
+ (void) BIO_free(mem);
+ return (NULL);
+ }
+
+ if (BIO_gets(mem, time, ATTR_MAX) <= 0) {
+ (void) BIO_free(mem);
+ return (NULL);
+ }
+
+ (void) BIO_free(mem);
+
+ /* trim the end of the string */
+ for (p = time + strlen(time) - 1; isspace(*p); p--) {
+ *p = '\0';
+ }
+
+ return (time);
+}
+
+/*
+ * check_password - do various password checks to see if the current password
+ * will work or we need to prompt for a new one.
+ *
+ * Arguments:
+ * pass - password to check
+ *
+ * Returns:
+ * B_TRUE - Password is OK.
+ * B_FALSE - Password not valid.
+ */
+static boolean_t
+check_password(PKCS12 *p12, char *pass)
+{
+ boolean_t ret = B_TRUE;
+
+ /*
+ * If password is zero length or NULL then try verifying both cases
+ * to determine which password is correct. The reason for this is that
+ * under PKCS#12 password based encryption no password and a zero
+ * length password are two different things...
+ */
+
+ /* Check the mac */
+ if (pass == NULL || *pass == '\0') {
+ if (PKCS12_verify_mac(p12, NULL, 0) == 0 &&
+ PKCS12_verify_mac(p12, "", 0) == 0)
+ ret = B_FALSE;
+ } else if (PKCS12_verify_mac(p12, pass, -1) == 0) {
+ ret = B_FALSE;
+ }
+ return (ret);
+}
diff --git a/usr/src/lib/libpkg/common/keystore.h b/usr/src/lib/libpkg/common/keystore.h
new file mode 100644
index 0000000000..b48ba030aa
--- /dev/null
+++ b/usr/src/lib/libpkg/common/keystore.h
@@ -0,0 +1,145 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2003 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _KEYSTORE_H
+#define _KEYSTORE_H
+
+
+/*
+ * Module: keystore.h
+ * Description: This module contains the structure definitions for processing
+ * package keystore files.
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <openssl/evp.h>
+#include <openssl/x509.h>
+#include "pkgerr.h"
+
+/* keystore structures */
+
+/* this opaque type represents a keystore */
+typedef void *keystore_handle_t;
+
+/* flags passed to open_keystore */
+
+/* opens keystore read-only. Attempts to modify results in an error */
+#define KEYSTORE_ACCESS_READONLY 0x00000001L
+
+/* opens keystore read-write */
+#define KEYSTORE_ACCESS_READWRITE 0x00000002L
+
+/*
+ * tells open_keystore to fall back to app-generic paths in the case that
+ * the app-specific paths do not exist.
+ */
+#define KEYSTORE_PATH_SOFT 0x00000010L
+
+/*
+ * tells open_keystore to use the app-specific paths no matter what,
+ * failing if they cannot be used for any reason.
+ */
+#define KEYSTORE_PATH_HARD 0x00000020L
+
+/* masks off various types of flags */
+#define KEYSTORE_ACCESS_MASK 0x0000000FL
+#define KEYSTORE_PATH_MASK 0x000000F0L
+
+/* default is read-only, soft */
+#define KEYSTORE_DFLT_FLAGS \
+ (KEYSTORE_ACCESS_READONLY|KEYSTORE_PATH_SOFT)
+
+/*
+ * possible encoding formats used by the library, used
+ * by print_cert
+ */
+typedef enum {
+ KEYSTORE_FORMAT_PEM,
+ KEYSTORE_FORMAT_DER,
+ KEYSTORE_FORMAT_TEXT
+} keystore_encoding_format_t;
+
+/*
+ * structure passed back to password callback for determining how
+ * to prompt for passphrase, and where to record errors
+ */
+typedef struct {
+ PKG_ERR *err;
+} keystore_passphrase_data;
+
+
+/* max length of a passphrase. One could use a short story! */
+#define KEYSTORE_PASS_MAX 1024
+
+/* callback for collecting passphrase when open_keystore() is called */
+typedef int keystore_passphrase_cb(char *, int, int, void *);
+
+/* names of the individual files within the keystore path */
+#define TRUSTSTORE "truststore"
+#define KEYSTORE "keystore"
+#define CERTSTORE "certstore"
+
+/* keystore.c */
+extern int open_keystore(PKG_ERR *, char *, char *,
+ keystore_passphrase_cb, long flags, keystore_handle_t *);
+
+extern int print_certs(PKG_ERR *, keystore_handle_t, char *,
+ keystore_encoding_format_t, FILE *);
+
+extern int check_cert(PKG_ERR *, X509 *);
+
+extern int check_cert_and_key(PKG_ERR *, X509 *, EVP_PKEY *);
+
+extern int print_cert(PKG_ERR *, X509 *,
+ keystore_encoding_format_t, char *, boolean_t, FILE *);
+
+extern int close_keystore(PKG_ERR *, keystore_handle_t,
+ keystore_passphrase_cb);
+
+extern int merge_ca_cert(PKG_ERR *, X509 *, keystore_handle_t);
+extern int merge_cert_and_key(PKG_ERR *, X509 *, EVP_PKEY *,
+ char *, keystore_handle_t);
+
+extern int delete_cert_and_keys(PKG_ERR *, keystore_handle_t,
+ char *);
+
+extern int find_key_cert_pair(PKG_ERR *, keystore_handle_t,
+ char *, EVP_PKEY **, X509 **);
+
+extern int find_ca_certs(PKG_ERR *, keystore_handle_t,
+ STACK_OF(X509) **);
+
+extern int find_cl_certs(PKG_ERR *, keystore_handle_t,
+ STACK_OF(X509) **);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _KEYSTORE_H */
diff --git a/usr/src/lib/libpkg/common/llib-lpkg b/usr/src/lib/libpkg/common/llib-lpkg
new file mode 100644
index 0000000000..b60597a9bc
--- /dev/null
+++ b/usr/src/lib/libpkg/common/llib-lpkg
@@ -0,0 +1,36 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+
+/* LINTLIBRARY */
+/* PROTOLIB1 */
+#include <cfext.h>
+#include <keystore.h>
+#include <p12lib.h>
+#include <pkgerr.h>
+#include <pkglib.h>
+#include <pkglocale.h>
+#include <pkgweb.h>
diff --git a/usr/src/lib/libpkg/common/logerr.c b/usr/src/lib/libpkg/common/logerr.c
new file mode 100644
index 0000000000..7d304b059e
--- /dev/null
+++ b/usr/src/lib/libpkg/common/logerr.c
@@ -0,0 +1,72 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
+/* All Rights Reserved */
+
+
+
+#include <stdio.h>
+#include <string.h>
+#include <stdarg.h>
+#include "pkglocale.h"
+
+/*VARARGS*/
+void
+logerr(char *fmt, ...)
+{
+ va_list ap;
+ char *pt, buffer[2048];
+ int flag;
+ char *estr = pkg_gt("ERROR:");
+ char *wstr = pkg_gt("WARNING:");
+ char *nstr = pkg_gt("NOTE:");
+
+ va_start(ap, fmt);
+ flag = 0;
+ /* This may have to use the i18n strcmp() routines. */
+ if (strncmp(fmt, estr, strlen(estr)) &&
+ strncmp(fmt, wstr, strlen(wstr)) &&
+ strncmp(fmt, nstr, strlen(nstr))) {
+ flag++;
+ (void) fprintf(stderr, " ");
+ }
+ /*
+ * NOTE: internationalization in next line REQUIRES that caller of
+ * this routine be in the same internationalization domain
+ * as this library.
+ */
+ (void) vsprintf(buffer, fmt, ap);
+
+ va_end(ap);
+
+ for (pt = buffer; *pt; pt++) {
+ (void) putc(*pt, stderr);
+ if (flag && (*pt == '\n') && pt[1])
+ (void) fprintf(stderr, " ");
+ }
+ (void) putc('\n', stderr);
+}
diff --git a/usr/src/lib/libpkg/common/mapfile-vers b/usr/src/lib/libpkg/common/mapfile-vers
new file mode 100644
index 0000000000..7cb5ad56f3
--- /dev/null
+++ b/usr/src/lib/libpkg/common/mapfile-vers
@@ -0,0 +1,223 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+
+#
+# MAPFILE HEADER START
+#
+# WARNING: STOP NOW. DO NOT MODIFY THIS FILE.
+# Object versioning must comply with the rules detailed in
+#
+# usr/src/lib/README.mapfiles
+#
+# You should not be making modifications here until you've read the most current
+# copy of that file. If you need help, contact a gatekeeper for guidance.
+#
+# MAPFILE HEADER END
+#
+
+SUNWprivate {
+ global:
+ add_cache;
+ attrdefault;
+ attrpreset;
+ averify;
+ backoff;
+ basepath;
+ canonize;
+ canonize_slashes;
+ cgrgid;
+ cgrnam;
+ check_cert;
+ check_cert_and_key;
+ checksum_off;
+ checksum_on;
+ ckparam;
+ ckvolseq;
+ clgrgid;
+ clgrnam;
+ close_keystore;
+ clpwnam;
+ clpwuid;
+ compute_checksum;
+ cpwnam;
+ cpwuid;
+ cverify;
+ delete_cert_and_keys;
+ devtype;
+ disable_attribute_check;
+ ds_close;
+ ds_fd_open;
+ ds_findpkg;
+ ds_getinfo;
+ ds_getpkg;
+ ds_ginit;
+ ds_init;
+ ds_next;
+ ds_order;
+ ds_putinfo;
+ ds_readbuf;
+ ds_skiptoend;
+ ds_validate_signature;
+ e_ExecCmdArray;
+ e_ExecCmdList;
+ echo_out;
+ ecleanup;
+ enable_local_fs;
+ epclose;
+ epopen;
+ esystem;
+ find_ca_certs;
+ find_cl_certs;
+ find_key_cert_pair;
+ fmkdir;
+ fverify;
+ getErrbufAddr;
+ getErrbufSize;
+ getErrstr;
+ get_categories;
+ get_cert_chain;
+ get_disable_attribute_check;
+ get_endof_string;
+ get_prog_name;
+ get_proxy_port;
+ get_signature;
+ get_startof_string;
+ get_subject_display_name;
+ getmapmode;
+ gpkglist;
+ gpkgmap;
+ gpkgmapvfp;
+ holdcinfo;
+ init_cache;
+ isFdRemote;
+ isFstypeRemote;
+ isPathRemote;
+ is_not_valid_category;
+ is_not_valid_length;
+ is_web_install;
+ iscpio;
+ isdir;
+ isfile;
+ logerr;
+ lookup_cache;
+ mappath;
+ mapvar;
+ merge_ca_cert;
+ merge_cert_and_key;
+ nonABI_symlinks;
+ open_keystore;
+ path_valid;
+ pkg_passphrase_cb;
+ pkgalias;
+ pkgerr;
+ pkgerr_add;
+ pkgerr_clear;
+ pkgerr_dump;
+ pkgerr_free;
+ pkgerr_get;
+ pkgerr_new;
+ pkgerr_num;
+ pkgexecl;
+ pkgexecv;
+ pkghead;
+ pkglist_cont;
+ pkgmount;
+ pkgstrAddToken;
+ pkgstrContainsToken;
+ pkgstrConvertPathToBasename;
+ pkgstrConvertPathToDirname;
+ pkgstrConvertUllToTimeString_r;
+ pkgstrExpandTokens;
+ pkgstrGetToken;
+ pkgstrGetToken_r;
+ pkgstrLocatePathBasename;
+ pkgstrNumTokens;
+ pkgstrPrintf;
+ pkgstrPrintf_r;
+ pkgstrRemoveLeadingWhitespace;
+ pkgstrRemoveToken;
+ pkgstrScaleNumericString;
+ pkgtrans;
+ pkgumount;
+ ppkgmap;
+ print_cert;
+ print_certs;
+ progerr;
+ putcfile;
+ putcvfpfile;
+ reset_backoff;
+ restore_local_fs;
+ rpterr;
+ rrmdir;
+ sec_init;
+ setErrstr;
+ set_memalloc_failure_func;
+ set_nonABI_symlinks;
+ set_passphrase_passarg;
+ set_passphrase_prompt;
+ set_prog_name;
+ set_web_install;
+ setmapmode;
+ srchcfile;
+ strip_port;
+ sunw_PKCS12_contents;
+ sunw_PKCS12_create;
+ sunw_check_cert_times;
+ sunw_check_keys;
+ sunw_evp_pkey_free;
+ sunw_find_fname;
+ sunw_find_localkeyid;
+ sunw_get_cert_fname;
+ sunw_get_pkey_fname;
+ sunw_get_pkey_localkeyid;
+ sunw_set_fname;
+ sunw_set_localkeyid;
+ sunw_split_certs;
+ sunw_PEM_contents;
+ tputcfent;
+ validate_signature;
+ vfpCheckpointFile;
+ vfpCheckpointOpen;
+ vfpClearModified;
+ vfpClose;
+ vfpGetModified;
+ vfpOpen;
+ vfpRewind;
+ vfpSafePwrite;
+ vfpSafeWrite;
+ vfpSetFlags;
+ vfpSetModified;
+ vfpSetSize;
+ vfpTruncate;
+ vfpWriteToFile;
+ web_cleanup;
+ web_session_control;
+ xmalloc;
+ xrealloc;
+ xstrdup;
+ local:
+ *;
+};
diff --git a/usr/src/lib/libpkg/common/mappath.c b/usr/src/lib/libpkg/common/mappath.c
new file mode 100644
index 0000000000..b0ae99d827
--- /dev/null
+++ b/usr/src/lib/libpkg/common/mappath.c
@@ -0,0 +1,236 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
+/* All Rights Reserved */
+
+
+
+#include <limits.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <ctype.h>
+
+/* 0 = both upper and lower case */
+/* 1 = initial lower case only (build variables) */
+/* 2 = initial upper case only (install variables) */
+#define mode(flag, pt) (!flag || ((flag == 1) && islower(pt[1])) || \
+ ((flag == 2) && isupper(pt[1])))
+
+/*
+ * For next and last functions below, values indicate whether resolution
+ * was possible.
+ *
+ * 0 = all OK - the variable resolved within the established parameters
+ * or it wasn't time for the variable to bind.
+ * 1 = parameter did not resolve because there was no value in the
+ * environment or because it was a build variable at install
+ * time.
+ */
+
+/*
+ * This gets a raw path which may contain shell variables and returns in path
+ * a pathname with all appropriate parameters resolved. If it comes in
+ * relative, it goes out relative.
+ */
+int
+mappath(int flag, char *path)
+{
+ char buffer[PATH_MAX];
+ char varname[64];
+ char *npt, *pt, *pt2, *copy;
+ char *token;
+ int retvalue = 0;
+
+ copy = buffer;
+
+ /*
+ * For each "/" separated token. If the token contains an environment
+ * variable, then evaluate the variable and insert it into path.
+ */
+ for (pt = path; *pt; /* void */) {
+ /*
+ * If this is a token and it's an environment variable
+ * properly situated in the path...
+ */
+ if ((*pt == '$') && isalpha(pt[1]) &&
+ ((pt == path) || (pt[-1] == '/'))) {
+ /* ... and it's the right time to evaluate it... */
+ if (mode(flag, pt)) {
+ /* replace the parameter with its value. */
+ pt2 = varname;
+ for (npt = pt+1; *npt && (*npt != '/');
+ /* void */)
+ *pt2++ = *npt++;
+ *pt2 = '\0';
+ /*
+ * At this point EVERY token should evaluate
+ * to a value. If it doesn't, there's an
+ * error.
+ */
+ if ((token = getenv(varname)) != NULL &&
+ *token != NULL) {
+ /* copy in parameter value */
+ while (*token)
+ *copy++ = *token++;
+ pt = npt;
+ } else {
+ retvalue = 1;
+ *copy++ = *pt++;
+ }
+ /*
+ * If evaluate time is wrong, determine of this is an
+ * error.
+ */
+ } else {
+ if (flag == 2) { /* install-time. */
+ /*
+ * ALL variables MUST evaluate at
+ * install time.
+ */
+ *copy++ = *pt++;
+ retvalue = 1;
+ } else if (flag == 1 && /* build-time */
+ islower(pt[1])) {
+ /*
+ * All build-time variables must
+ * evaluate at build time.
+ */
+ retvalue = 1;
+ *copy++ = *pt++;
+ } else /* no problem. */
+ *copy++ = *pt++;
+ }
+ /*
+ * If it's a separator, copy it over to the target buffer and
+ * move to the start of the next token.
+ */
+ } else if (*pt == '/') {
+ while (pt[1] == '/')
+ pt++;
+ if ((pt[1] == '\0') && (pt > path))
+ break;
+ *copy++ = *pt++;
+ /*
+ * If we're in the middle of a non-parametric token, copy
+ * that character over and try the next character.
+ */
+ } else
+ *copy++ = *pt++;
+ }
+ *copy = '\0';
+ (void) strcpy(path, buffer);
+ return (retvalue);
+}
+
+/*
+ * This function resolves the path into an absolute path referred to
+ * an install root of ir.
+ */
+void
+basepath(char *path, char *basedir, char *ir)
+{
+ char buffer[PATH_MAX];
+
+ /* For a relative path, prepend the basedir */
+ if (*path != '/') {
+ (void) strcpy(buffer, path);
+ if (ir && *ir) {
+ while (*ir)
+ *path++ = *ir++;
+ if (path[-1] == '/')
+ path--;
+ }
+ if (basedir && *basedir) {
+ if (ir && *ir && *basedir != '/')
+ *path++ = '/';
+ while (*basedir)
+ *path++ = *basedir++;
+ if (path[-1] == '/')
+ path--;
+ }
+ *path++ = '/';
+ (void) strcpy(path, buffer);
+
+ /* For an absolute path, just prepend the install root */
+ } else {
+ if (ir && *ir) {
+ (void) strcpy(buffer, path);
+ while (*ir)
+ *path++ = *ir++;
+ if (path[-1] == '/')
+ path--;
+ (void) strcpy(path, buffer);
+ }
+ }
+}
+
+/*
+ * Evaluate varname and return with environment variables resolved.
+ * NOTE: This assumes that varname is a buffer long enough to hold the
+ * evaluated string.
+ */
+int
+mapvar(int flag, char *varname)
+{
+ char *token;
+ int retvalue = 0;
+
+ /* If its a parametric entry beginning with an alpha character. */
+ if (*varname == '$' && isalpha(varname[1])) {
+ /* ...and it's the right time to evaluate it... */
+ if (mode(flag, varname)) {
+ /*
+ * then it MUST be possible to evaluate it. If not,
+ * there's an error.
+ */
+ if (((token = getenv(&varname[1])) != NULL) &&
+ *token) {
+ /* copy token into varname */
+ while (*token)
+ *varname++ = *token++;
+ *varname = '\0';
+ } else
+ retvalue = 1;
+ } else {
+ if (flag == 2) /* install-time. */
+ /*
+ * ALL variables MUST evaluate at install
+ * time.
+ */
+ retvalue = 1;
+ else if (flag == 1 && /* build-time */
+ islower(varname[1]))
+ /*
+ * all build-time variables must evaluate at
+ * build time.
+ */
+ retvalue = 1;
+ }
+ }
+ return (retvalue);
+}
diff --git a/usr/src/lib/libpkg/common/ncgrpw.c b/usr/src/lib/libpkg/common/ncgrpw.c
new file mode 100644
index 0000000000..cf3151164e
--- /dev/null
+++ b/usr/src/lib/libpkg/common/ncgrpw.c
@@ -0,0 +1,740 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+
+/*
+ * This module fetches group and passwd structs for the caller. It
+ * uses a hash table to speed up retrieval of repeated entries. If
+ * the attempts to initialize the hash tables fail, this just
+ * continues the slow way.
+ */
+
+#include <pwd.h>
+#include <grp.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include "pkglib.h"
+#include "pkglocale.h"
+#include "nhash.h"
+
+#define HASHSIZE 151
+#define BSZ 4
+
+#define ERR_DUPFAIL "%s: strdup(%s) failed.\n"
+#define ERR_ADDFAIL "%s: add_cache() failed.\n"
+#define ERR_BADMEMB "%s: %s in \"%s\" %s structure is invalid.\n"
+#define ERR_NOGRP "dup_gr_ent(): no group entry provided.\n"
+#define ERR_NOPWD "dup_pw_ent(): no passwd entry provided.\n"
+#define ERR_NOINIT "%s: init_cache() failed.\n"
+#define ERR_MALLOC "%s: malloc(%d) failed for %s.\n"
+
+static Cache *pwnam_cache = (Cache *) NULL;
+static Cache *grnam_cache = (Cache *) NULL;
+static Cache *pwuid_cache = (Cache *) NULL;
+static Cache *grgid_cache = (Cache *) NULL;
+
+static int dup_gr_ent(struct group *grp);
+static int dup_pw_ent(struct passwd *pwp);
+
+/*
+ * These indicate whether the hash table has been initialized for the four
+ * categories.
+ */
+static int is_a_pwnam_cache;
+static int is_a_grnam_cache;
+static int is_a_pwuid_cache;
+static int is_a_grgid_cache;
+
+extern char *get_install_root(void);
+
+/*
+ * If there's a grnam cache, then update it with this new
+ * group, otherwise, skip it.
+ */
+static Item *
+cache_alloc(char *fname, int len, size_t struct_size)
+{
+ Item *itemp;
+
+ /*
+ * Allocate space for the Item pointer, key and data.
+ */
+ if ((itemp = (Item *) malloc(sizeof (*itemp))) ==
+ Null_Item) {
+ (void) fprintf(stderr,
+ pkg_gt(ERR_MALLOC), fname,
+ sizeof (*itemp), "itemp");
+ } else if ((itemp->key = (char *)malloc(len)) == NULL) {
+ (void) fprintf(stderr, pkg_gt(ERR_MALLOC), fname, len,
+ "itemp->key");
+ free(itemp);
+ } else if ((itemp->data = malloc(struct_size)) == NULL) {
+ (void) fprintf(stderr, pkg_gt(ERR_MALLOC), fname,
+ struct_size, "itemp->data");
+ free(itemp->key);
+ free(itemp);
+ } else {
+ /* Set length parameters. */
+ itemp->keyl = len;
+ itemp->datal = struct_size;
+
+ return (itemp);
+ }
+
+ return ((Item *) NULL);
+}
+
+/* Get the required group structure based upon the group name. */
+struct group *
+cgrnam(char *nam)
+{
+ struct group *grp;
+ Item *itemp;
+ int len;
+ static int cache_failed;
+
+ /* Attempt to initialize the grname cache. */
+ if (!is_a_grnam_cache && !cache_failed) {
+ if (init_cache(&grnam_cache, HASHSIZE, BSZ,
+ (int (*)())NULL, (int (*)())NULL) == -1) {
+ (void) fprintf(stderr, pkg_gt(ERR_NOINIT), "cgrnam()");
+ grnam_cache = (Cache *) NULL;
+ cache_failed = 1;
+ } else
+ is_a_grnam_cache = 1;
+ }
+
+ len = strlen(nam) + 1;
+
+ /* First look in the cache. Failing that, do it the hard way. */
+ if ((itemp = lookup_cache(grnam_cache, nam, len)) == Null_Item) {
+
+ /* Get the group by name. */
+ if ((grp = clgrnam(nam)) != NULL ||
+ (grp = getgrnam(nam)) != NULL) {
+ /* A group by that name exists on this machine. */
+ if (dup_gr_ent(grp))
+ /*
+ * Effectively no such group since struct
+ * couldn't be duplicated.
+ */
+ grp = (struct group *)NULL;
+ /*
+ * If there's a grnam cache, then update it with this
+ * new group, otherwise, skip it.
+ */
+ else if (is_a_grnam_cache) {
+ if ((itemp = cache_alloc("cgrnam()", len,
+ sizeof (struct group))) != Null_Item) {
+ /*
+ * With that allocated, insert the
+ * group name as key and set the key
+ * length.
+ */
+ (void) memmove(itemp->key, nam, len);
+
+ /*
+ * Insert the data associated with
+ * the key and the data length.
+ */
+ (void) memmove(itemp->data, grp,
+ sizeof (struct group));
+
+ /* Insert the Item into the cache. */
+ if (add_cache(grnam_cache, itemp) == -1)
+ (void) fprintf(stderr,
+ pkg_gt(ERR_ADDFAIL),
+ "cgrnam()");
+ }
+ }
+ }
+ return (grp);
+ } else /* Found it in the cache. */
+ return ((struct group *)itemp->data);
+}
+
+struct passwd *
+cpwnam(char *nam)
+{
+ struct passwd *pwd;
+ Item *itemp;
+ int len;
+ static int cache_failed;
+
+ if (!is_a_pwnam_cache && !cache_failed) {
+ if (init_cache(&pwnam_cache, HASHSIZE, BSZ,
+ (int (*)())NULL, (int (*)())NULL) == -1) {
+ (void) fprintf(stderr, pkg_gt(ERR_NOINIT), "cpwnam()");
+ pwnam_cache = (Cache *) NULL;
+ cache_failed = 1;
+ } else
+ is_a_pwnam_cache = 1;
+ }
+
+ len = strlen(nam) + 1;
+
+ /* First look in the cache. Failing that, do it the hard way. */
+ if ((itemp = lookup_cache(pwnam_cache, nam, len)) == Null_Item) {
+
+ /* Get the passwd by name. */
+ if ((pwd = clpwnam(nam)) != NULL ||
+ (pwd = getpwnam(nam)) != NULL) {
+ /* A passwd by that name exists on this machine. */
+ if (dup_pw_ent(pwd))
+ /*
+ * Effectively no such passwd since struct
+ * couldn't be duplicated.
+ */
+ pwd = (struct passwd *)NULL;
+ /*
+ * If there's a pwnam cache, then update it with this
+ * new passwd, otherwise, skip it.
+ */
+ else if (is_a_pwnam_cache) {
+ /*
+ * Allocate space for the Item pointer, key
+ * and data.
+ */
+ if ((itemp = cache_alloc("cpwnam()", len,
+ sizeof (struct passwd))) != Null_Item) {
+ /*
+ * With that allocated, insert the
+ * group name as key and set the key
+ * length.
+ */
+ (void) memmove(itemp->key, nam, len);
+
+ /*
+ * Insert the data associated with
+ * the key and the data length.
+ */
+ (void) memmove(itemp->data, pwd,
+ sizeof (struct passwd));
+
+ if (add_cache(pwnam_cache, itemp) == -1)
+ (void) fprintf(stderr,
+ pkg_gt(ERR_ADDFAIL),
+ "cpwnam()");
+ }
+ }
+ }
+ return (pwd);
+ } else /* Found it in the cache. */
+ return ((struct passwd *)itemp->data);
+}
+
+static int
+uid_hash(void *datap, int datalen, int hsz)
+{
+#ifdef lint
+ int i = datalen;
+ datalen = i;
+#endif /* lint */
+
+ return (*((uid_t *)datap) % hsz);
+}
+
+static int
+uid_comp(void *datap1, void *datap2, int datalen)
+{
+#ifdef lint
+ int i = datalen;
+ datalen = i;
+#endif /* lint */
+
+ return (*((uid_t *)datap1) - *((uid_t *)datap2));
+}
+
+struct group *
+cgrgid(gid_t gid)
+{
+ struct group *grp;
+ Item *itemp;
+ int len;
+ static int cache_failed;
+
+ if (!is_a_grgid_cache && !cache_failed) {
+ if (init_cache(&grgid_cache, HASHSIZE, BSZ,
+ uid_hash, uid_comp) == -1) {
+ (void) fprintf(stderr, pkg_gt(ERR_NOINIT), "cgrgid()");
+ grgid_cache = (Cache *) NULL;
+ cache_failed = 1;
+ } else
+ is_a_grgid_cache = 1;
+ }
+
+ len = sizeof (uid_t);
+
+ /* First look in the cache. Failing that, do it the hard way. */
+ if ((itemp = lookup_cache(grgid_cache, &gid, len)) == Null_Item) {
+ if ((grp = clgrgid(gid)) != NULL ||
+ (grp = getgrgid(gid)) != NULL) {
+ /* A group by that number exists on this machine. */
+ if (dup_gr_ent(grp))
+ /*
+ * Effectively no such group since struct
+ * couldn't be duplicated.
+ */
+ grp = (struct group *)NULL;
+ /*
+ * If there's a grnam cache, then update it with this
+ * new group, otherwise, skip it.
+ */
+ else if (is_a_grgid_cache) {
+ if ((itemp = cache_alloc("cgrgid()", len,
+ sizeof (struct group))) != Null_Item) {
+ /*
+ * With that allocated, insert the
+ * group name as key and set the key
+ * length.
+ */
+ (void) memmove(itemp->key, &gid, len);
+
+ /*
+ * Insert the data associated with
+ * the key and the data length.
+ */
+ (void) memmove(itemp->data, grp,
+ sizeof (struct group));
+
+ if (add_cache(grgid_cache, itemp) == -1)
+ (void) fprintf(stderr,
+ pkg_gt(ERR_ADDFAIL),
+ "cgrgid()");
+ }
+ }
+ }
+ return (grp);
+ } else /* Found it in the cache. */
+ return ((struct group *)itemp->data);
+}
+
+struct passwd *
+cpwuid(uid_t uid)
+{
+ struct passwd *pwd;
+ Item *itemp;
+ int len;
+ static int cache_failed;
+
+ if (!is_a_pwuid_cache && !cache_failed) {
+ if (init_cache(&pwuid_cache, HASHSIZE, BSZ,
+ uid_hash, uid_comp) == -1) {
+ (void) fprintf(stderr,
+ pkg_gt(ERR_NOINIT), "cpwuid()");
+ pwuid_cache = (Cache *) NULL;
+ cache_failed = 1;
+ } else
+ is_a_pwuid_cache = 1;
+ }
+
+ len = sizeof (uid_t);
+
+ /* First look in the cache. Failing that, do it the hard way. */
+ if ((itemp = lookup_cache(pwuid_cache, &uid, len)) == Null_Item) {
+
+ /* Get the passwd by number. */
+ if ((pwd = clpwuid(uid)) != NULL ||
+ (pwd = getpwuid(uid)) != NULL) {
+ /* A passwd by that user ID exists on this machine. */
+ if (dup_pw_ent(pwd))
+ /*
+ * Effectively no such passwd since struct
+ * couldn't be duplicated.
+ */
+ pwd = (struct passwd *)NULL;
+ /*
+ * If there's a pwuid cache, then update it with this
+ * new passwd, otherwise, skip it.
+ */
+ else if (is_a_pwuid_cache) {
+ if ((itemp = cache_alloc("cpwuid()", len,
+ sizeof (struct passwd))) != Null_Item) {
+ /*
+ * With that allocated, insert the
+ * group name as key and set the key
+ * length.
+ */
+ (void) memmove(itemp->key, &uid, len);
+
+ /*
+ * Insert the data associated with
+ * the key and the data length.
+ */
+ (void) memmove(itemp->data, pwd,
+ sizeof (struct passwd));
+
+ if (add_cache(pwuid_cache, itemp) == -1)
+ (void) fprintf(stderr,
+ pkg_gt(ERR_ADDFAIL),
+ "cpwuid()");
+ }
+ }
+ }
+ return (pwd);
+ } else /* Found it in the cache. */
+ return ((struct passwd *)itemp->data);
+}
+
+/*
+ * This function duplicates the group structure provided from kernel static
+ * memory. There is a lot of defensive coding here because there have been
+ * problems with the getgr*() functions. They will sometimes provide NULL
+ * values instead of pointers to NULL values. There has been no explanation
+ * for the reason behind this; but, this function takes a NULL to be an
+ * invalid (char *) and returns an error.
+ */
+static int
+dup_gr_ent(struct group *grp)
+{
+ char **tp = NULL;
+ char **memp = NULL;
+ int nent = 0; /* Number of entries in the member list. */
+
+ if (grp) {
+ if (grp->gr_name == NULL) {
+ (void) fprintf(stderr,
+ pkg_gt(ERR_BADMEMB), "dup_gr_ent()", "gr_name",
+ "unknown", "group");
+ return (-1);
+ } else if ((grp->gr_name = strdup(grp->gr_name)) == NULL) {
+ (void) fprintf(stderr,
+ pkg_gt(ERR_DUPFAIL), "dup_gr_ent()", "gr_name");
+ return (-1);
+ }
+ if (grp->gr_passwd == NULL) {
+ (void) fprintf(stderr,
+ pkg_gt(ERR_BADMEMB), "dup_gr_ent()", "gr_passwd",
+ grp->gr_name, "group");
+ return (-1);
+ } else if ((grp->gr_passwd = strdup(grp->gr_passwd)) == NULL) {
+ (void) fprintf(stderr,
+ pkg_gt(ERR_DUPFAIL), "dup_gr_ent()", "gr_passwd");
+ return (-1);
+ }
+ /*
+ * Allocate space for the member list and move the members
+ * into it.
+ */
+ if (grp->gr_mem) {
+ /*
+ * First count the members. The nent variable will be
+ * the number of members + 1 for the terminator.
+ */
+ for (tp = grp->gr_mem; *tp; nent++, tp++);
+
+ /* Now allocate room for the pointers. */
+ memp = malloc(sizeof (char **)* (nent+1));
+
+ if (memp == NULL) {
+ (void) fprintf(stderr,
+ pkg_gt(ERR_MALLOC), "dup_gr_ent()",
+ (sizeof (char **)* (nent+1)),
+ "memp");
+ return (-1);
+ }
+
+ /*
+ * Now copy over the pointers and entries. It should
+ * be noted that if the structure is messed up here,
+ * the resulting member list will be truncated at the
+ * NULL entry.
+ */
+ for (nent = 0, tp = grp->gr_mem; *tp; tp++) {
+ if ((memp[nent++] = strdup(*tp)) == NULL) {
+ (void) fprintf(stderr,
+ pkg_gt(ERR_DUPFAIL), "dup_gr_ent()",
+ "gr_mem");
+ return (-1);
+ }
+ }
+ } else {
+ (void) fprintf(stderr,
+ pkg_gt(ERR_BADMEMB), "dup_gr_ent()", "gr_mem",
+ grp->gr_name, "group");
+ return (-1);
+ }
+ } else {
+ (void) fprintf(stderr, pkg_gt(ERR_NOGRP));
+ return (-1);
+ }
+ memp[nent++] = '\0';
+ return (0);
+}
+
+/*
+ * This function duplicates the passwd structure provided from kernel static
+ * memory. As in the above function, since there have been problems with the
+ * getpw*() functions, the structure provided is rigorously scrubbed. This
+ * function takes a NULL to be an invalid (char *) and returns an error if
+ * one is detected.
+ */
+static int
+dup_pw_ent(struct passwd *pwd)
+{
+ if (pwd) {
+ if (pwd->pw_name == NULL) {
+ (void) fprintf(stderr,
+ pkg_gt(ERR_BADMEMB), "dup_pw_ent()", "pw_name",
+ "unknown", "passwd");
+ return (-1);
+ } else if ((pwd->pw_name = strdup(pwd->pw_name)) == NULL) {
+ (void) fprintf(stderr,
+ pkg_gt(ERR_DUPFAIL), "dup_pw_ent()", "pw_name");
+ return (-1);
+ }
+
+ if (pwd->pw_passwd == NULL) {
+ (void) fprintf(stderr,
+ pkg_gt(ERR_BADMEMB), "dup_pw_ent()", "pw_passwd",
+ pwd->pw_name, "passwd");
+ return (-1);
+ } else if ((pwd->pw_passwd = strdup(pwd->pw_passwd)) == NULL) {
+ (void) fprintf(stderr,
+ pkg_gt(ERR_DUPFAIL), "dup_pw_ent()", "pw_passwd");
+ return (-1);
+ }
+
+ if (pwd->pw_age == NULL) {
+ (void) fprintf(stderr,
+ pkg_gt(ERR_BADMEMB), "dup_pw_ent()", "pw_age",
+ pwd->pw_name, "passwd");
+ return (-1);
+ } else if ((pwd->pw_age = strdup(pwd->pw_age)) == NULL) {
+ (void) fprintf(stderr,
+ pkg_gt(ERR_DUPFAIL), "dup_pw_ent()", "pw_age");
+ return (-1);
+ }
+
+ if (pwd->pw_comment == NULL) {
+ (void) fprintf(stderr,
+ pkg_gt(ERR_BADMEMB), "dup_pw_ent()", "pw_comment",
+ pwd->pw_name, "passwd");
+ return (-1);
+ } else if ((pwd->pw_comment = strdup(pwd->pw_comment)) ==
+ NULL) {
+ (void) fprintf(stderr,
+ pkg_gt(ERR_DUPFAIL), "dup_pw_ent()", "pw_comment");
+ return (-1);
+ }
+
+ if (pwd->pw_gecos == NULL) {
+ (void) fprintf(stderr,
+ pkg_gt(ERR_BADMEMB), "dup_pw_ent()", "pw_gecos",
+ pwd->pw_name, "passwd");
+ return (-1);
+ } else if ((pwd->pw_gecos = strdup(pwd->pw_gecos)) == NULL) {
+ (void) fprintf(stderr,
+ pkg_gt(ERR_DUPFAIL), "dup_pw_ent()", "pw_gecos");
+ return (-1);
+ }
+
+ if (pwd->pw_dir == NULL) {
+ (void) fprintf(stderr,
+ pkg_gt(ERR_BADMEMB), "dup_pw_ent()", "pw_dir",
+ pwd->pw_name, "passwd");
+ return (-1);
+ } else if ((pwd->pw_dir = strdup(pwd->pw_dir)) == NULL) {
+ (void) fprintf(stderr,
+ pkg_gt(ERR_DUPFAIL), "dup_pw_ent()", "pw_dir");
+ return (-1);
+ }
+
+ if (pwd->pw_shell == NULL) {
+ (void) fprintf(stderr,
+ pkg_gt(ERR_BADMEMB), "dup_pw_ent()", "pw_shell",
+ pwd->pw_name, "passwd");
+ return (-1);
+ } else if ((pwd->pw_shell = strdup(pwd->pw_shell)) == NULL) {
+ (void) fprintf(stderr,
+ pkg_gt(ERR_DUPFAIL), "dup_pw_ent()", "pw_shell");
+ return (-1);
+ }
+ } else {
+ (void) fprintf(stderr, pkg_gt(ERR_NOPWD));
+ return (-1);
+ }
+
+ return (0);
+}
+
+/*
+ * Check the client's etc/group file for the group name
+ *
+ * returns a pointer to the group structure if the group is found
+ * returns NULL if not found
+ */
+struct group *
+clgrnam(char *nam)
+{
+ struct group *gr;
+ char *instroot, *buf;
+ FILE *gr_ptr;
+
+ if ((instroot = get_install_root()) != NULL) {
+ if ((buf = (char *)malloc(strlen(instroot) +
+ strlen(GROUP) + 1)) == NULL) {
+ (void) fprintf(stderr,
+ pkg_gt(ERR_MALLOC), "clgrnam()",
+ strlen(instroot) + strlen(GROUP), "buf");
+ }
+ (void) sprintf(buf, "%s%s", instroot, GROUP);
+ if ((gr_ptr = fopen(buf, "r")) == NULL) {
+ free(buf);
+ return (NULL);
+ } else {
+ while ((gr = fgetgrent(gr_ptr)) != NULL) {
+ if (strcmp(gr->gr_name, nam) == 0) {
+ break;
+ }
+ }
+ }
+ free(buf);
+ (void) fclose(gr_ptr);
+ return (gr);
+ } else {
+ return (NULL);
+ }
+}
+
+/*
+ * Check the client's etc/passwd file for the user name
+ *
+ * returns a pointer to the passwd structure if the passwd is found
+ * returns NULL if not found
+ */
+struct passwd *
+clpwnam(char *nam)
+{
+ struct passwd *pw;
+ char *instroot, *buf;
+ FILE *pw_ptr;
+
+ if ((instroot = get_install_root()) != NULL) {
+ if ((buf = (char *)malloc(strlen(instroot) +
+ strlen(PASSWD) + 1)) == NULL) {
+ (void) fprintf(stderr,
+ pkg_gt(ERR_MALLOC), "clpwnam()",
+ strlen(instroot) + strlen(PASSWD), "buf");
+ }
+ (void) sprintf(buf, "%s%s", instroot, PASSWD);
+ if ((pw_ptr = fopen(buf, "r")) == NULL) {
+ free(buf);
+ return (NULL);
+ } else {
+ while ((pw = fgetpwent(pw_ptr)) != NULL) {
+ if (strcmp(pw->pw_name, nam) == 0) {
+ break;
+ }
+ }
+ }
+ free(buf);
+ (void) fclose(pw_ptr);
+ return (pw);
+ } else {
+ return (NULL);
+ }
+}
+
+/*
+ * Check the client's etc/group file for the group id
+ *
+ * returns a pointer to the group structure if the group id is found
+ * returns NULL if not found
+ */
+struct group *
+clgrgid(gid_t gid)
+{
+ struct group *gr;
+ char *instroot, *buf;
+ FILE *gr_ptr;
+
+ if ((instroot = get_install_root()) != NULL) {
+ if ((buf = (char *)malloc(strlen(instroot) +
+ strlen(GROUP) + 1)) == NULL) {
+ (void) fprintf(stderr,
+ pkg_gt(ERR_MALLOC), "clgrgid()",
+ strlen(instroot) + strlen(GROUP), "buf");
+ }
+ (void) sprintf(buf, "%s%s", instroot, GROUP);
+ if ((gr_ptr = fopen(buf, "r")) == NULL) {
+ free(buf);
+ return (NULL);
+ } else {
+ while ((gr = fgetgrent(gr_ptr)) != NULL) {
+ if (gr->gr_gid == gid) {
+ break;
+ }
+ }
+ }
+ free(buf);
+ (void) fclose(gr_ptr);
+ return (gr);
+ } else {
+ return (NULL);
+ }
+}
+
+/*
+ * Check the client's etc/passwd file for the user id
+ *
+ * returns a pointer to the passwd structure if the user id is found
+ * returns NULL if not found
+ */
+struct passwd *
+clpwuid(uid_t uid)
+{
+ struct passwd *pw;
+ char *instroot, *buf;
+ FILE *pw_ptr;
+
+ if ((instroot = get_install_root()) != NULL) {
+ if ((buf = (char *)malloc(strlen(instroot) +
+ strlen(PASSWD) + 1)) == NULL) {
+ (void) fprintf(stderr,
+ pkg_gt(ERR_MALLOC), "clpwuid()",
+ strlen(instroot) + strlen(PASSWD), "buf");
+ }
+ (void) sprintf(buf, "%s%s", instroot, PASSWD);
+ if ((pw_ptr = fopen(buf, "r")) == NULL) {
+ free(buf);
+ return (NULL);
+ } else {
+ while ((pw = fgetpwent(pw_ptr)) != NULL) {
+ if (pw->pw_uid == uid) {
+ break;
+ }
+ }
+ }
+ free(buf);
+ (void) fclose(pw_ptr);
+ return (pw);
+ } else {
+ return (NULL);
+ }
+}
diff --git a/usr/src/lib/libpkg/common/nhash.c b/usr/src/lib/libpkg/common/nhash.c
new file mode 100644
index 0000000000..1b42595fec
--- /dev/null
+++ b/usr/src/lib/libpkg/common/nhash.c
@@ -0,0 +1,182 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <strings.h>
+#include "pkglib.h"
+#include "nhash.h"
+#include "pkglocale.h"
+
+#ifndef _KERNEL
+#define bcopy(a, b, c) (void) memmove(b, a, c)
+#define bcmp memcmp
+#define bzero(a, c) (void) memset(a, '\0', c)
+#else /* _KERNEL */
+#define malloc bkmem_alloc
+#endif /* _KERNEL */
+
+#define VERIFY_HASH_REALLOC
+
+static int
+BCMP(void *str1, void *str2, int len)
+{
+ return (bcmp((char *)str1, (char *)str2, len));
+}
+
+static int
+HASH(void *datap, int datalen, int hsz)
+{
+ char *cp;
+ char *np;
+ int hv = 0;
+
+ /* determine starting and ending positions */
+
+ cp = (char *)datap;
+ np = ((char *)cp + datalen);
+
+ /* compute hash over all characters from start to end */
+
+ while (cp != np) {
+ hv += ((int)*cp++);
+ }
+
+ /* return computed hash */
+
+ return (hv % hsz);
+}
+
+int
+init_cache(Cache **cp, int hsz, int bsz,
+ int (*hfunc)(void *, int, int), int (*cfunc)(void *, void *, int))
+{
+ if ((*cp = (Cache *) malloc(sizeof (**cp))) == NULL) {
+ (void) fprintf(stderr, pkg_gt("malloc(Cache **cp)"));
+ return (-1);
+ }
+ if (((*cp)->bp =
+ (Bucket *) malloc(sizeof (*(*cp)->bp) * hsz)) == NULL) {
+ (void) fprintf(stderr, pkg_gt("malloc(Bucket cp->bp)"));
+ return (-1);
+ }
+
+ (*cp)->hsz = hsz;
+ (*cp)->bsz = bsz;
+
+ bzero((*cp)->bp, sizeof (*(*cp)->bp) * hsz);
+
+ if (hfunc != (int (*)()) NULL) {
+ (*cp)->hfunc = hfunc;
+ } else {
+ (*cp)->hfunc = HASH;
+ }
+
+ if (cfunc != (int (*)()) NULL) {
+ (*cp)->cfunc = cfunc;
+ } else {
+ (*cp)->cfunc = BCMP;
+ }
+ return (0);
+}
+
+int
+add_cache(Cache *cp, Item *itemp)
+{
+ Bucket *bp;
+ Item **titempp;
+
+ /*
+ * If cp is NULL, then init_cache() wasn't called. Quietly return the
+ * error code and let the caller deal with it.
+ */
+ if (cp == NULL)
+ return (-1);
+
+ bp = &cp->bp[(*cp->hfunc)(itemp->key, itemp->keyl, cp->hsz)];
+ if (bp->nent >= bp->nalloc) {
+ if (bp->nalloc == 0) {
+ bp->itempp =
+ (Item **) malloc(sizeof (*bp->itempp) * cp->bsz);
+ } else {
+#ifdef VERIFY_HASH_REALLOC
+ (void) fprintf(stderr,
+ pkg_gt("realloc(%d) bucket=%d\n"),
+ bp->nalloc + cp->bsz,
+ (*cp->hfunc)(itemp->key, itemp->keyl, cp->hsz));
+#endif /* VERIFY_HASH_REALLOC */
+ if ((titempp =
+ (Item **) malloc(sizeof (*bp->itempp) *
+ (bp->nalloc + cp->bsz))) != NULL) {
+ bcopy((char *)bp->itempp, (char *)titempp,
+ (sizeof (*bp->itempp) * bp->nalloc));
+#ifdef _KERNEL
+ bkmem_free(bp->itempp,
+ (sizeof (*bp->itempp) * bp->nalloc));
+#else /* !_KERNEL */
+ free(bp->itempp);
+#endif /* _KERNEL */
+ bp->itempp = titempp;
+ } else
+ bp->itempp = NULL;
+ }
+ if (bp->itempp == NULL) {
+ (void) fprintf(stderr,
+ pkg_gt("add_cache(): out of memory\n"));
+ return (-1);
+ }
+ bp->nalloc += cp->bsz;
+ }
+ bp->itempp[bp->nent] = itemp;
+ bp->nent++;
+ return (0);
+}
+
+Item *
+lookup_cache(Cache *cp, void *datap, int datalen)
+{
+ int i;
+ Bucket *bp;
+
+ /*
+ * If cp is NULL, then init_cache() wasn't called. Quietly return the
+ * error code and let the caller deal with it.
+ */
+ if (cp == NULL) {
+ return (Null_Item);
+ }
+
+ bp = &cp->bp[(*cp->hfunc)(datap, datalen, cp->hsz)];
+
+ for (i = 0; i < bp->nent; i++) {
+ if (!(*cp->cfunc)((void *)bp->itempp[i]->key, datap, datalen)) {
+ return (bp->itempp[i]);
+ }
+ }
+ return (Null_Item);
+}
diff --git a/usr/src/lib/libpkg/common/nhash.h b/usr/src/lib/libpkg/common/nhash.h
new file mode 100644
index 0000000000..cc32228cb9
--- /dev/null
+++ b/usr/src/lib/libpkg/common/nhash.h
@@ -0,0 +1,75 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2003 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _NHASH_H
+#define _NHASH_H
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifndef NULL
+#define NULL 0
+#endif /* NULL */
+
+typedef struct item_t {
+ void *key;
+ int keyl;
+ void *data;
+ int datal;
+} Item;
+
+#define Null_Item ((Item *) NULL)
+
+typedef struct bucket_t {
+ int nent;
+ int nalloc;
+ Item **itempp;
+} Bucket;
+
+typedef struct cache_t {
+ int hsz;
+ int bsz;
+ Bucket *bp;
+ int (*hfunc)(void *, int, int);
+ int (*cfunc)(void *, void *, int);
+} Cache;
+
+#ifdef _KERNEL
+#define malloc bkmem_alloc
+#endif /* _KERNEL */
+
+extern int init_cache(Cache **cp, int hsz, int bsz,
+ int (*hfunc)(void *, int, int), int (*cfunc)(void *, void *, int));
+extern int add_cache(Cache *cp, Item *itemp);
+extern Item *lookup_cache(Cache *cp, void *datap, int datalen);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _NHASH_H */
diff --git a/usr/src/lib/libpkg/common/p12lib.c b/usr/src/lib/libpkg/common/p12lib.c
new file mode 100644
index 0000000000..3ae72a4b50
--- /dev/null
+++ b/usr/src/lib/libpkg/common/p12lib.c
@@ -0,0 +1,2786 @@
+/*
+ * ====================================================================
+ * Copyright (c) 1999 The OpenSSL Project. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ * software must display the following acknowledgment:
+ * "This product includes software developed by the OpenSSL Project
+ * for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)"
+ *
+ * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
+ * endorse or promote products derived from this software without
+ * prior written permission. For written permission, please contact
+ * licensing@OpenSSL.org.
+ *
+ * 5. Products derived from this software may not be called "OpenSSL"
+ * nor may "OpenSSL" appear in their names without prior written
+ * permission of the OpenSSL Project.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ * acknowledgment:
+ * "This product includes software developed by the OpenSSL Project
+ * for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OpenSSL PROJECT OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This product includes cryptographic software written by Eric Young
+ * (eay@cryptsoft.com). This product includes software written by Tim
+ * Hudson (tjh@cryptsoft.com).
+ *
+ */
+
+/*
+ * Copyright 2003 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+
+#include <strings.h>
+#include <stdlib.h>
+#include <assert.h>
+
+#include <openssl/crypto.h>
+#include <openssl/err.h>
+#include <openssl/x509.h>
+#include <openssl/pem.h>
+
+#include <openssl/pkcs12.h>
+#include "p12lib.h"
+
+/*
+ * OpenSSL provides a framework for pushing error codes onto a stack.
+ * When an error occurs, the consumer may use the framework to
+ * pop the errors off the stack and provide a trace of where the
+ * errors occurred.
+ *
+ * Our PKCS12 code plugs into this framework by calling
+ * ERR_load_SUNW_strings(). To push an error (which by the way, consists
+ * of a function code and an error code) onto the stack our PKCS12 code
+ * calls SUNWerr().
+ *
+ * Consumers of our PKCS12 code can then call the OpenSSL error routines
+ * when an error occurs and retrieve the stack of errors.
+ */
+
+#ifndef OPENSSL_NO_ERR
+
+/* Function codes and their matching strings */
+static ERR_STRING_DATA SUNW_str_functs[] = {
+ { ERR_PACK(0, SUNW_F_USE_X509CERT, 0), "sunw_use_x509cert" },
+ { ERR_PACK(0, SUNW_F_USE_PKEY, 0), "sunw_use_pkey" },
+ { ERR_PACK(0, SUNW_F_USE_TASTORE, 0), "sunw_use_tastore" },
+ { ERR_PACK(0, SUNW_F_USE_CERTFILE, 0), "sunw_p12_use_certfile" },
+ { ERR_PACK(0, SUNW_F_USE_KEYFILE, 0), "sunw_p12_use_keyfile" },
+ { ERR_PACK(0, SUNW_F_USE_TRUSTFILE, 0), "sunw_p12_use_trustfile" },
+ { ERR_PACK(0, SUNW_F_READ_FILE, 0), "p12_read_file" },
+ { ERR_PACK(0, SUNW_F_DOPARSE, 0), "p12_doparse" },
+ { ERR_PACK(0, SUNW_F_PKCS12_PARSE, 0), "sunw_PKCS12_parse" },
+ { ERR_PACK(0, SUNW_F_PKCS12_CONTENTS, 0), "sunw_PKCS12_contents" },
+ { ERR_PACK(0, SUNW_F_PARSE_ONE_BAG, 0), "parse_one_bag" },
+ { ERR_PACK(0, SUNW_F_PKCS12_CREATE, 0), "sunw_PKCS12_create" },
+ { ERR_PACK(0, SUNW_F_SPLIT_CERTS, 0), "sunw_split_certs" },
+ { ERR_PACK(0, SUNW_F_FIND_LOCALKEYID, 0), "sunw_find_localkeyid" },
+ { ERR_PACK(0, SUNW_F_SET_LOCALKEYID, 0), "sunw_set_localkeyid" },
+ { ERR_PACK(0, SUNW_F_GET_LOCALKEYID, 0), "sunw_get_localkeyid" },
+ { ERR_PACK(0, SUNW_F_SET_FNAME, 0), "sunw_set_fname" },
+ { ERR_PACK(0, SUNW_F_GET_PKEY_FNAME, 0), "sunw_get_pkey_fname" },
+ { ERR_PACK(0, SUNW_F_APPEND_KEYS, 0), "sunw_append_keys" },
+ { ERR_PACK(0, SUNW_F_PEM_CONTENTS, 0), "sunw_PEM_contents" },
+ { ERR_PACK(0, SUNW_F_PEM_INFO, 0), "pem_info" },
+ { ERR_PACK(0, SUNW_F_ASC2BMPSTRING, 0), "asc2bmpstring" },
+ { ERR_PACK(0, SUNW_F_UTF82ASCSTR, 0), "utf82ascstr" },
+ { ERR_PACK(0, SUNW_F_FINDATTR, 0), "findattr" },
+ { ERR_PACK(0, SUNW_F_TYPE2ATTRIB, 0), "type2attrib" },
+ { ERR_PACK(0, SUNW_F_MOVE_CERTS, 0), "move_certs" },
+ { ERR_PACK(0, SUNW_F_FIND_FNAME, 0), "sunw_find_fname" },
+ { ERR_PACK(0, SUNW_F_PARSE_OUTER, 0), "parse_outer" },
+ { ERR_PACK(0, SUNW_F_CHECKFILE, 0), "checkfile" },
+ { 0, NULL }
+};
+
+/* Error codes and their matching strings */
+static ERR_STRING_DATA SUNW_str_reasons[] = {
+ { SUNW_R_INVALID_ARG, "invalid argument" },
+ { SUNW_R_MEMORY_FAILURE, "memory failure" },
+ { SUNW_R_MAC_VERIFY_FAILURE, "mac verify failure" },
+ { SUNW_R_MAC_CREATE_FAILURE, "mac create failure" },
+ { SUNW_R_BAD_FILETYPE, "bad file type" },
+ { SUNW_R_BAD_PKEY, "bad or missing private key" },
+ { SUNW_R_BAD_PKEYTYPE, "unsupported key type" },
+ { SUNW_R_PKEY_READ_ERR, "unable to read private key" },
+ { SUNW_R_NO_TRUST_ANCHOR, "no trust anchors found" },
+ { SUNW_R_READ_TRUST_ERR, "unable to read trust anchor" },
+ { SUNW_R_ADD_TRUST_ERR, "unable to add trust anchor" },
+ { SUNW_R_PKCS12_PARSE_ERR, "PKCS12 parse error" },
+ { SUNW_R_PKCS12_CREATE_ERR, "PKCS12 create error" },
+ { SUNW_R_BAD_CERTTYPE, "unsupported certificate type" },
+ { SUNW_R_PARSE_CERT_ERR, "error parsing PKCS12 certificate" },
+ { SUNW_R_PARSE_BAG_ERR, "error parsing PKCS12 bag" },
+ { SUNW_R_MAKE_BAG_ERR, "error making PKCS12 bag" },
+ { SUNW_R_BAD_LKID, "bad localKeyID format" },
+ { SUNW_R_SET_LKID_ERR, "error setting localKeyID" },
+ { SUNW_R_BAD_FNAME, "bad friendlyName format" },
+ { SUNW_R_SET_FNAME_ERR, "error setting friendlyName" },
+ { SUNW_R_BAD_TRUST, "bad or missing trust anchor" },
+ { SUNW_R_BAD_BAGTYPE, "unsupported bag type" },
+ { SUNW_R_CERT_ERR, "certificate error" },
+ { SUNW_R_PKEY_ERR, "private key error" },
+ { SUNW_R_READ_ERR, "error reading file" },
+ { SUNW_R_ADD_ATTR_ERR, "error adding attribute" },
+ { SUNW_R_STR_CONVERT_ERR, "error converting string" },
+ { SUNW_R_PKCS12_EMPTY_ERR, "empty PKCS12 structure" },
+ { SUNW_R_PASSWORD_ERR, "bad password" },
+ { 0, NULL }
+};
+
+/*
+ * The library name that our module will be known as. This name
+ * may be retrieved via OpenSSLs error APIs.
+ */
+static ERR_STRING_DATA SUNW_lib_name[] = {
+ { 0, SUNW_LIB_NAME },
+ { 0, NULL }
+};
+#endif
+
+/*
+ * The value of this variable (initialized by a call to
+ * ERR_load_SUNW_strings()) is what identifies our errors
+ * to OpenSSL as being ours.
+ */
+static int SUNW_lib_error_code = 0;
+
+/* local routines */
+static int parse_pkcs12(PKCS12 *, const char *, int, char *, int, char *,
+ EVP_PKEY **, X509 **, STACK_OF(X509) **);
+static int pem_info(FILE *, pem_password_cb, void *,
+ STACK_OF(EVP_PKEY) **, STACK_OF(X509) **);
+
+static int parse_outer(PKCS12 *, const char *, STACK_OF(EVP_PKEY) *,
+ STACK_OF(X509) *);
+
+static int parse_all_bags(STACK_OF(PKCS12_SAFEBAG) *, const char *,
+ STACK_OF(EVP_PKEY) *, STACK_OF(X509) *);
+
+static int parse_one_bag(PKCS12_SAFEBAG *, const char *,
+ STACK_OF(EVP_PKEY) *, STACK_OF(X509) *);
+
+static X509_ATTRIBUTE *type2attrib(ASN1_TYPE *, int);
+static ASN1_TYPE *attrib2type(X509_ATTRIBUTE *);
+static uchar_t *utf82ascstr(ASN1_UTF8STRING *);
+static ASN1_BMPSTRING *asc2bmpstring(const char *, int);
+static int find_attr_by_nid(STACK_OF(X509_ATTRIBUTE) *, int);
+static int find_attr(int, ASN1_STRING *, STACK_OF(EVP_PKEY) *,
+ EVP_PKEY **, STACK_OF(X509) *, X509 **);
+
+static chk_errs_t check_time(chk_actions_t, X509 *);
+static int get_key_cert(int, STACK_OF(EVP_PKEY) *, EVP_PKEY **,
+ STACK_OF(X509) *, X509 **cert);
+static int move_certs(STACK_OF(X509) *, STACK_OF(X509) *);
+static int sunw_append_keys(STACK_OF(EVP_PKEY) *,
+ STACK_OF(EVP_PKEY) *);
+static int set_results(STACK_OF(EVP_PKEY) **,
+ STACK_OF(EVP_PKEY) **, STACK_OF(X509) **, STACK_OF(X509) **,
+ STACK_OF(X509) **, STACK_OF(X509) **,
+ STACK_OF(EVP_PKEY) **, STACK_OF(EVP_PKEY) **);
+
+/*
+ * ----------------------------------------------------------------------------
+ * Public routines
+ * ----------------------------------------------------------------------------
+ */
+
+/*
+ * sunw_PKCS12_parse - Parse a PKCS12 structure and break it into its parts.
+ *
+ * Parse and decrypt a PKCS#12 structure returning user key, user cert and/or
+ * other (CA) certs. Note either ca should be NULL, *ca should be NULL,
+ * or it should point to a valid STACK_OF(X509) structure. pkey and cert can
+ * be passed uninitialized.
+ *
+ * Arguments:
+ * p12 - Structure with pkcs12 info to be parsed
+ * pass - Pass phrase for the private key (possibly empty) or NULL if
+ * there is none.
+ * matchty - Info about which certs/keys to return if many are in the file.
+ * keyid - If private key localkeyids friendlynames are to match a
+ * predetermined value, the value to match. This value should
+ * be an octet string.
+ * keyid_len- Length of the keyid byte string.
+ * name_str - If friendlynames are to match a predetermined value, the value
+ * to match. This value should be a NULL terminated string.
+ * pkey - Points to location pointing to the private key returned.
+ * cert - Points to locaiton which points to the client cert returned
+ * ca - Points to location that points to a stack of 'certificate
+ * authority' certs/trust anchors.
+ *
+ * Match based on the value of 'matchty' and the contents of 'keyid'
+ * and/or 'name_str', as appropriate. Go through the lists of certs and
+ * private keys which were taken from the pkcs12 structure, looking for
+ * matches of the requested type. This function only searches the lists of
+ * matching private keys and client certificates. Kinds of matches allowed,
+ * and the order in which they will be checked, are:
+ *
+ * 1) Find the key and/or cert whose localkeyid attributes matches
+ * 'keyid'.
+ * 2) Find the key and/or cert whose friendlyname attributes matches
+ * 'name_str'
+ * 3) Return the first matching key/cert pair found.
+ * 4) Return the last matching key/cert pair found.
+ * 5) Return whatever cert and/or key are available, even unmatching.
+ *
+ * Append to the CA list, the certs which do not have matching private
+ * keys and which were not selected.
+ *
+ * If none of the bits are set, no client certs or private keys will be
+ * returned. CA (aka trust anchor) certs can be.
+ *
+ * Notes: If #3 is selected, then #4 will never occur. CA certs will be
+ * selected after a cert/key pairs are isolated.
+ *
+ * Returns:
+ * < 0 - An error returned. Call ERR_get_error() to get errors information.
+ * Where possible, memory has been freed.
+ * >= 0 - Objects were found and returned. Which objects are indicated by
+ * which bits are set (FOUND_PKEY, FOUND_CERT, FOUND_CA_CERTS).
+ */
+int
+sunw_PKCS12_parse(PKCS12 *p12, const char *pass, int matchty, char *keyid,
+ int keyid_len, char *name_str, EVP_PKEY **pkey, X509 **cert,
+ STACK_OF(X509) **ca)
+{
+ boolean_t ca_supplied;
+ int retval = -1;
+
+ /* If NULL PKCS12 structure, this is an error */
+ if (p12 == NULL) {
+ SUNWerr(SUNW_F_PKCS12_PARSE, SUNW_R_INVALID_ARG);
+ return (-1);
+ }
+
+ /* Set up arguments.... These will be allocated if needed */
+ if (pkey)
+ *pkey = NULL;
+ if (cert)
+ *cert = NULL;
+
+ /*
+ * If there is already a ca list, use it. Otherwise, allocate one
+ * and free is later if an error occurs or whatever.)
+ */
+ ca_supplied = (ca != NULL && *ca != NULL);
+ if (ca != NULL && *ca == NULL) {
+ if ((*ca = sk_X509_new_null()) == NULL) {
+ SUNWerr(SUNW_F_PKCS12_PARSE, SUNW_R_MEMORY_FAILURE);
+ return (-1);
+ }
+ }
+
+ /*
+ * If password is zero length or NULL then try verifying both cases
+ * to determine which password is correct. The reason for this is that
+ * under PKCS#12 password based encryption no password and a zero
+ * length password are two different things. If the password has a
+ * non-zero length and is not NULL then call PKCS12_verify_mac() with
+ * a length of '-1' and let it use strlen() to figure out the length
+ * of the password.
+ */
+ /* Check the mac */
+ if (pass == NULL || *pass == '\0') {
+ if (PKCS12_verify_mac(p12, NULL, 0))
+ pass = NULL;
+ else if (PKCS12_verify_mac(p12, "", 0))
+ pass = "";
+ else {
+ SUNWerr(SUNW_F_PKCS12_PARSE,
+ SUNW_R_MAC_VERIFY_FAILURE);
+ goto err;
+ }
+ } else if (PKCS12_verify_mac(p12, pass, -1) == 0) {
+ SUNWerr(SUNW_F_PKCS12_PARSE, SUNW_R_MAC_VERIFY_FAILURE);
+ goto err;
+ }
+
+ retval = parse_pkcs12(p12, pass, matchty, keyid, keyid_len,
+ name_str, pkey, cert, ca);
+ if (retval < 0) {
+ SUNWerr(SUNW_F_PKCS12_PARSE, SUNW_R_PKCS12_PARSE_ERR);
+ goto err;
+ }
+ return (retval);
+
+err:
+ if (pkey && *pkey) {
+ sunw_evp_pkey_free(*pkey);
+ }
+ if (cert && *cert)
+ X509_free(*cert);
+ if (ca_supplied == B_FALSE && ca != NULL)
+ sk_X509_pop_free(*ca, X509_free);
+
+ return (-1);
+
+}
+
+
+/*
+ * sunw_PEM_contents() parses a PEM file and returns component parts found
+ *
+ * Parse and decrypt a PEM file, returning any user keys and certs.
+ *
+ * There are some limits to this function. It will ignore the following:
+ * - certificates identified by "TRUSTED CERTIFICATE"
+ * - CERTIFICATE REQUEST and NEW CERTIFICATE REQUEST records.
+ * - X509 CRL
+ * - DH PARAMETERS
+ * - DSA PARAMETERS
+ * - Any PUBLIC KEY
+ * - PKCS7
+ * - PRIVATE KEY or ENCRYPTED PRIVATE KEY (PKCS 8)
+ *
+ * Arguments:
+ * fp - File pointer for file containing PEM data.
+ * pass - Pass phrase for the private key or NULL if there is none.
+ * pkeys - Points to address of a stack of private keys to return.
+ * certs - Points to address of a stack of client certs to return.
+ *
+ * The pointers to stacks should either be NULL or their contents should
+ * either be NULL or should point to a valid STACK_OF(X509) structure.
+ * If the stacks contain information, corresponding information from the
+ * file will be appended to the original contents.
+ *
+ * Note: Client certs and and their matching private keys will be in any
+ * order.
+ *
+ * Certs which have no matching private key are assumed to be ca certs.
+ *
+ * Returns:
+ * < 0 - An error returned. Call ERR_get_error() to get errors information.
+ * Where possible, memory has been freed.
+ * >= 0 - Objects were found and returned. Which objects are indicated by
+ * which bits are set (FOUND_PKEY, FOUND_CERT)
+ */
+int sunw_PEM_contents(FILE *fp, pem_password_cb *cb, void *userdata,
+ STACK_OF(EVP_PKEY) **pkey, STACK_OF(X509) **certs)
+{
+ STACK_OF(EVP_PKEY) *work_kl = NULL;
+ STACK_OF(X509) *work_ca = NULL;
+ int retval = -1;
+
+ /*
+ * Allocate the working stacks for private key and for the
+ * ca certs.
+ */
+ if ((work_kl = sk_EVP_PKEY_new_null()) == NULL) {
+ SUNWerr(SUNW_F_PEM_CONTENTS, SUNW_R_MEMORY_FAILURE);
+ goto cleanup;
+ }
+
+ if ((work_ca = sk_X509_new_null()) == NULL) {
+ SUNWerr(SUNW_F_PEM_CONTENTS, SUNW_R_MEMORY_FAILURE);
+ goto cleanup;
+ }
+
+ /* Error strings are set within the following. */
+ if (pem_info(fp, cb, userdata, &work_kl, &work_ca) <= 0) {
+ goto cleanup;
+ }
+
+ /* on error, set_results() returns an error on the stack */
+ retval = set_results(pkey, &work_kl, certs, &work_ca, NULL, NULL, NULL,
+ NULL);
+cleanup:
+ if (work_kl != NULL) {
+ sk_EVP_PKEY_pop_free(work_kl, sunw_evp_pkey_free);
+ }
+ if (work_ca != NULL)
+ sk_X509_pop_free(work_ca, X509_free);
+
+ return (retval);
+}
+
+
+/*
+ * sunw_PKCS12_contents() parses a pkcs#12 structure and returns component
+ * parts found, without evaluation.
+ *
+ * Parse and decrypt a PKCS#12 structure returning any user keys and/or
+ * various certs. Note these should either be NULL, *whatever should
+ * be NULL, or it should point to a valid STACK_OF(X509) structure.
+ *
+ * Arguments:
+ * p12 - Structure with pkcs12 info to be parsed
+ * pass - Pass phrase for the private key and entire pkcs12 wad (possibly
+ * empty) or NULL if there is none.
+ * pkeys - Points to address of a stack of private keys to return.
+ * certs - Points to address of a stack of client certs return.
+ *
+ * Note: The certs and keys being returned are in random order.
+ *
+ * Returns:
+ * < 0 - An error returned. Call ERR_get_error() to get errors information.
+ * Where possible, memory has been freed.
+ * >= 0 - Objects were found and returned. Which objects are indicated by
+ * which bits are set (FOUND_PKEY or FOUND_CERT)
+ */
+int
+sunw_PKCS12_contents(PKCS12 *p12, const char *pass, STACK_OF(EVP_PKEY) **pkey,
+ STACK_OF(X509) **certs)
+{
+ STACK_OF(EVP_PKEY) *work_kl = NULL;
+ STACK_OF(X509) *work_ca = NULL;
+ int retval = -1;
+
+ /*
+ * Allocate the working stacks for private key and for the
+ * ca certs.
+ */
+ if ((work_kl = sk_EVP_PKEY_new_null()) == NULL) {
+ SUNWerr(SUNW_F_PKCS12_CONTENTS, SUNW_R_MEMORY_FAILURE);
+ goto cleanup;
+ }
+
+ if ((work_ca = sk_X509_new_null()) == NULL) {
+ SUNWerr(SUNW_F_PKCS12_CONTENTS, SUNW_R_MEMORY_FAILURE);
+ goto cleanup;
+ }
+
+ if (parse_outer(p12, pass, work_kl, work_ca) == 0) {
+ /*
+ * Error already on stack
+ */
+ goto cleanup;
+ }
+
+ /* on error, set_results() returns an error on the stack */
+ retval = set_results(pkey, &work_kl, certs, &work_ca, NULL,
+ NULL, NULL, NULL);
+
+cleanup:
+ if (work_kl != NULL) {
+ sk_EVP_PKEY_pop_free(work_kl, sunw_evp_pkey_free);
+ }
+
+ return (retval);
+}
+
+
+
+/*
+ * sunw_split_certs() - Given a list of certs and a list of private keys,
+ * moves certs which match one of the keys to a different stack.
+ *
+ * Arguments:
+ * allkeys - Points to a stack of private keys to search.
+ * allcerts - Points to a stack of certs to be searched.
+ * keycerts - Points to address of a stack of certs with matching private
+ * keys. They are moved from 'allcerts'. This may not be NULL
+ * when called. If *keycerts is NULL upon entry, a new stack will
+ * be allocated. Otherwise, it must be a valid STACK_OF(509).
+ * nocerts - Points to address of a stack for keys which have no matching
+ * certs. Keys are moved from 'allkeys' here when they have no
+ * matching certs. If this is NULL, matchless keys will be
+ * discarded.
+ *
+ * Notes: If an error occurs while moving certs, the cert being move may be
+ * lost. 'keycerts' may only contain part of the matching certs. The number
+ * of certs successfully moved can be found by checking sk_X509_num(keycerts).
+ *
+ * If there is a key which does not have a matching cert, it is moved to
+ * the list nocerts.
+ *
+ * If all certs are removed from 'certs' and/or 'pkeys', it will be the
+ * caller's responsibility to free the empty stacks.
+ *
+ * Returns:
+ * < 0 - An error returned. Call ERR_get_error() to get errors information.
+ * Where possible, memory has been freed.
+ * >= 0 - The number of certs moved from 'cert' to 'pkcerts'.
+ */
+int
+sunw_split_certs(STACK_OF(EVP_PKEY) *allkeys, STACK_OF(X509) *allcerts,
+ STACK_OF(X509) **keycerts, STACK_OF(EVP_PKEY) **nocerts)
+{
+ STACK_OF(X509) *matching;
+ STACK_OF(EVP_PKEY) *nomatch;
+ EVP_PKEY *tmpkey;
+ X509 *tmpcert;
+ int count = 0;
+ int found;
+ int res;
+ int i;
+ int k;
+
+ *keycerts = NULL;
+ if (nocerts != NULL)
+ *nocerts = NULL;
+ nomatch = NULL;
+
+ if ((matching = sk_X509_new_null()) == NULL) {
+ SUNWerr(SUNW_F_SPLIT_CERTS, SUNW_R_MEMORY_FAILURE);
+ return (-1);
+ }
+ *keycerts = matching;
+
+ k = 0;
+ while (k < sk_EVP_PKEY_num(allkeys)) {
+ found = 0;
+ tmpkey = sk_EVP_PKEY_value(allkeys, k);
+
+ for (i = 0; i < sk_X509_num(allcerts); i++) {
+ tmpcert = sk_X509_value(allcerts, i);
+ res = X509_check_private_key(tmpcert, tmpkey);
+ if (res != 0) {
+ count++;
+ found = 1;
+ tmpcert = sk_X509_delete(allcerts, i);
+ if (sk_X509_push(matching, tmpcert) == 0) {
+ X509_free(tmpcert);
+ SUNWerr(SUNW_F_SPLIT_CERTS,
+ SUNW_R_MEMORY_FAILURE);
+ return (-1);
+ }
+ break;
+ }
+ }
+ if (found != 0) {
+ /*
+ * Found a match - keep the key & check out the next
+ * one.
+ */
+ k++;
+ } else {
+ /*
+ * No cert matching this key. Move the key if
+ * possible or discard it. Don't increment the
+ * index.
+ */
+ if (nocerts == NULL) {
+ tmpkey = sk_EVP_PKEY_delete(allkeys, k);
+ sunw_evp_pkey_free(tmpkey);
+ } else {
+ if (*nocerts == NULL) {
+ nomatch = sk_EVP_PKEY_new_null();
+ if (nomatch == NULL) {
+ SUNWerr(SUNW_F_SPLIT_CERTS,
+ SUNW_R_MEMORY_FAILURE);
+ return (-1);
+ }
+ *nocerts = nomatch;
+ }
+ tmpkey = sk_EVP_PKEY_delete(allkeys, k);
+ if (sk_EVP_PKEY_push(nomatch, tmpkey) == 0) {
+ sunw_evp_pkey_free(tmpkey);
+ SUNWerr(SUNW_F_SPLIT_CERTS,
+ SUNW_R_MEMORY_FAILURE);
+ return (-1);
+ }
+ }
+ }
+ }
+
+ return (count);
+}
+
+/*
+ * sunw_PKCS12_create() creates a pkcs#12 structure and given component parts.
+ *
+ * Given one or more of user private key, user cert and/or other (CA) certs,
+ * return an encrypted PKCS12 structure containing them.
+ *
+ * Arguments:
+ * pass - Pass phrase for the pkcs12 structure and private key (possibly
+ * empty) or NULL if there is none. It will be used to encrypt
+ * both the private key(s) and as the pass phrase for the whole
+ * pkcs12 wad.
+ * pkeys - Points to stack of private keys.
+ * certs - Points to stack of client (public ke) certs
+ * cacerts - Points to stack of 'certificate authority' certs (or trust
+ * anchors).
+ *
+ * Note that any of these may be NULL.
+ *
+ * Returns:
+ * NULL - An error occurred.
+ * != NULL - Address of PKCS12 structure. The user is responsible for
+ * freeing the memory when done.
+ */
+PKCS12 *
+sunw_PKCS12_create(const char *pass, STACK_OF(EVP_PKEY) *pkeys,
+ STACK_OF(X509) *certs, STACK_OF(X509) *cacerts)
+{
+ int nid_cert = NID_pbe_WithSHA1And40BitRC2_CBC;
+ int nid_key = NID_pbe_WithSHA1And3_Key_TripleDES_CBC;
+ STACK_OF(PKCS12_SAFEBAG) *bags = NULL;
+ STACK_OF(PKCS7) *safes = NULL;
+ PKCS12_SAFEBAG *bag = NULL;
+ PKCS8_PRIV_KEY_INFO *p8 = NULL;
+ EVP_PKEY *pkey = NULL;
+ PKCS12 *ret_p12 = NULL;
+ PKCS12 *p12 = NULL;
+ PKCS7 *authsafe = NULL;
+ X509 *cert = NULL;
+ uchar_t *str = NULL;
+ int certs_there = 0;
+ int keys_there = 0;
+ int len;
+ int i;
+
+ if ((safes = sk_PKCS7_new_null()) == NULL) {
+ SUNWerr(SUNW_F_PKCS12_CREATE, SUNW_R_MEMORY_FAILURE);
+ return (NULL);
+ }
+
+ if ((bags = sk_PKCS12_SAFEBAG_new_null()) == NULL) {
+ SUNWerr(SUNW_F_PKCS12_CREATE, SUNW_R_MEMORY_FAILURE);
+ goto err_ret;
+ }
+
+ if (certs != NULL && sk_X509_num(certs) > 0) {
+
+ for (i = 0; i < sk_X509_num(certs); i++) {
+ cert = sk_X509_value(certs, i);
+
+ /* Add user certificate */
+ if ((bag = M_PKCS12_x5092certbag(cert)) == NULL) {
+ SUNWerr(SUNW_F_PKCS12_CREATE, SUNW_R_CERT_ERR);
+ goto err_ret;
+ }
+ if (cert->aux != NULL && cert->aux->alias != NULL &&
+ cert->aux->alias->type == V_ASN1_UTF8STRING) {
+ str = utf82ascstr(cert->aux->alias);
+ if (str == NULL) {
+ /*
+ * Error already on stack
+ */
+ goto err_ret;
+ }
+ if (PKCS12_add_friendlyname_asc(bag,
+ (char const *) str,
+ strlen((char const *) str)) == 0) {
+ SUNWerr(SUNW_F_PKCS12_CREATE,
+ SUNW_R_ADD_ATTR_ERR);
+ goto err_ret;
+ }
+ }
+ if (cert->aux != NULL && cert->aux->keyid != NULL &&
+ cert->aux->keyid->type == V_ASN1_OCTET_STRING) {
+ str = cert->aux->keyid->data;
+ len = cert->aux->keyid->length;
+
+ if (str != NULL &&
+ PKCS12_add_localkeyid(bag, str, len) == 0) {
+ SUNWerr(SUNW_F_PKCS12_CREATE,
+ SUNW_R_ADD_ATTR_ERR);
+ goto err_ret;
+ }
+ }
+ if (sk_PKCS12_SAFEBAG_push(bags, bag) == 0) {
+ SUNWerr(SUNW_F_PKCS12_CREATE,
+ SUNW_R_MEMORY_FAILURE);
+ goto err_ret;
+ }
+ certs_there++;
+ bag = NULL;
+ }
+ }
+
+ if (cacerts != NULL && sk_X509_num(cacerts) > 0) {
+
+ /* Put all certs in structure */
+ for (i = 0; i < sk_X509_num(cacerts); i++) {
+ cert = sk_X509_value(cacerts, i);
+ if ((bag = M_PKCS12_x5092certbag(cert)) == NULL) {
+ SUNWerr(SUNW_F_PKCS12_CREATE, SUNW_R_CERT_ERR);
+ goto err_ret;
+ }
+
+ if (cert->aux != NULL && cert->aux->alias != NULL &&
+ cert->aux->alias->type == V_ASN1_UTF8STRING) {
+ str = utf82ascstr(cert->aux->alias);
+ if (str == NULL) {
+ /*
+ * Error already on stack
+ */
+ goto err_ret;
+ }
+ if (PKCS12_add_friendlyname_asc(
+ bag, (char const *) str,
+ strlen((char const *) str)) == 0) {
+ SUNWerr(SUNW_F_PKCS12_CREATE,
+ SUNW_R_ADD_ATTR_ERR);
+ goto err_ret;
+ }
+ }
+ if (cert->aux != NULL && cert->aux->keyid != NULL &&
+ cert->aux->keyid->type == V_ASN1_OCTET_STRING) {
+ str = cert->aux->keyid->data;
+ len = cert->aux->keyid->length;
+
+ if (str != NULL &&
+ PKCS12_add_localkeyid(bag, str, len) == 0) {
+ SUNWerr(SUNW_F_PKCS12_CREATE,
+ SUNW_R_ADD_ATTR_ERR);
+ goto err_ret;
+ }
+ }
+ if (sk_PKCS12_SAFEBAG_push(bags, bag) == 0) {
+ SUNWerr(SUNW_F_PKCS12_CREATE,
+ SUNW_R_MEMORY_FAILURE);
+ goto err_ret;
+ }
+ certs_there++;
+ bag = NULL;
+ }
+ }
+
+ if (certs != NULL || cacerts != NULL && certs_there) {
+ /* Turn certbags into encrypted authsafe */
+ authsafe = PKCS12_pack_p7encdata(nid_cert, pass, -1,
+ NULL, 0, PKCS12_DEFAULT_ITER, bags);
+ if (authsafe == NULL) {
+ SUNWerr(SUNW_F_PKCS12_CREATE, SUNW_R_CERT_ERR);
+ goto err_ret;
+ }
+ sk_PKCS12_SAFEBAG_pop_free(bags, PKCS12_SAFEBAG_free);
+ bags = NULL;
+
+ if (sk_PKCS7_push(safes, authsafe) == 0) {
+ SUNWerr(SUNW_F_PKCS12_CREATE, SUNW_R_MEMORY_FAILURE);
+ goto err_ret;
+ }
+ authsafe = NULL;
+ }
+
+ if (pkeys != NULL && sk_EVP_PKEY_num(pkeys) > 0) {
+
+ if (bags == NULL &&
+ (bags = sk_PKCS12_SAFEBAG_new_null()) == NULL) {
+ SUNWerr(SUNW_F_PKCS12_CREATE, SUNW_R_MEMORY_FAILURE);
+ goto err_ret;
+ }
+
+ for (i = 0; i < sk_EVP_PKEY_num(pkeys); i++) {
+
+ pkey = sk_EVP_PKEY_value(pkeys, i);
+
+ /* Make a shrouded key bag */
+ if ((p8 = EVP_PKEY2PKCS8(pkey)) == NULL) {
+ SUNWerr(SUNW_F_PKCS12_CREATE, SUNW_R_PKEY_ERR);
+ goto err_ret;
+ }
+
+ bag = PKCS12_MAKE_SHKEYBAG(nid_key, pass, -1, NULL, 0,
+ PKCS12_DEFAULT_ITER, p8);
+ if (bag == NULL) {
+ SUNWerr(SUNW_F_PKCS12_CREATE,
+ SUNW_R_MAKE_BAG_ERR);
+ goto err_ret;
+ }
+ PKCS8_PRIV_KEY_INFO_free(p8);
+ p8 = NULL;
+
+ len = sunw_get_pkey_fname(GETDO_COPY, pkey,
+ (char **)&str);
+ if (str != NULL) {
+ if (PKCS12_add_friendlyname_asc(bag,
+ (const char *)str, len) == 0) {
+ SUNWerr(SUNW_F_PKCS12_CREATE,
+ SUNW_R_ADD_ATTR_ERR);
+ goto err_ret;
+ }
+ }
+ str = NULL;
+
+ len = sunw_get_pkey_localkeyid(GETDO_COPY, pkey,
+ (char **)&str, &len);
+ if (str != NULL) {
+ if (PKCS12_add_localkeyid(bag, str, len) == 0) {
+ SUNWerr(SUNW_F_PKCS12_CREATE,
+ SUNW_R_ADD_ATTR_ERR);
+ goto err_ret;
+ }
+ }
+ str = NULL;
+
+ if (sk_PKCS12_SAFEBAG_push(bags, bag) == 0) {
+ SUNWerr(SUNW_F_PKCS12_CREATE,
+ SUNW_R_MEMORY_FAILURE);
+ goto err_ret;
+ }
+ keys_there++;
+ bag = NULL;
+ }
+
+ if (keys_there) {
+ /* Turn into unencrypted authsafe */
+ authsafe = PKCS12_pack_p7data(bags);
+ if (authsafe == NULL) {
+ SUNWerr(SUNW_F_PKCS12_CREATE,
+ SUNW_R_PKCS12_CREATE_ERR);
+ goto err_ret;
+ }
+ sk_PKCS12_SAFEBAG_pop_free(bags, PKCS12_SAFEBAG_free);
+ bags = NULL;
+
+ if (sk_PKCS7_push(safes, authsafe) == 0) {
+ SUNWerr(SUNW_F_PKCS12_CREATE,
+ SUNW_R_MEMORY_FAILURE);
+ }
+ authsafe = NULL;
+ }
+ }
+
+ if (certs_there == 0 && keys_there == 0) {
+ SUNWerr(SUNW_F_PKCS12_CREATE, SUNW_R_PKCS12_EMPTY_ERR);
+ goto err_ret;
+ }
+
+ if ((p12 = PKCS12_init(NID_pkcs7_data)) == NULL) {
+ SUNWerr(SUNW_F_PKCS12_CREATE, SUNW_R_PKCS12_CREATE_ERR);
+ goto err_ret;
+ }
+
+ /*
+ * Note that safes is copied by the following. Therefore, it needs
+ * to be freed whether or not the following succeeds.
+ */
+ if (M_PKCS12_pack_authsafes(p12, safes) == 0) {
+ SUNWerr(SUNW_F_PKCS12_CREATE, SUNW_R_PKCS12_CREATE_ERR);
+ goto err_ret;
+ }
+ if (PKCS12_set_mac(p12, pass, -1, NULL, 0, 2048, NULL) == 0) {
+ SUNWerr(SUNW_F_PKCS12_CREATE, SUNW_R_MAC_CREATE_FAILURE);
+ goto err_ret;
+ }
+
+ ret_p12 = p12;
+ p12 = NULL;
+
+ /* Fallthrough is intentional */
+
+err_ret:
+
+ if (str != NULL)
+ free(str);
+
+ if (p8 != NULL)
+ PKCS8_PRIV_KEY_INFO_free(p8);
+
+ if (bag != NULL)
+ PKCS12_SAFEBAG_free(bag);
+ if (bags != NULL)
+ sk_PKCS12_SAFEBAG_pop_free(bags, PKCS12_SAFEBAG_free);
+ if (authsafe != NULL)
+ PKCS7_free(authsafe);
+ if (safes != NULL)
+ sk_PKCS7_pop_free(safes, PKCS7_free);
+ if (p12 != NULL)
+ PKCS12_free(p12);
+
+ return (ret_p12);
+}
+
+/*
+ * sunw_evp_pkey_free() Given an EVP_PKEY structure, free any attributes
+ * that are attached. Then free the EVP_PKEY itself.
+ *
+ * This is a replacement for EVP_PKEY_free() for the sunw stuff.
+ * It should be used in places where EVP_PKEY_free would be used,
+ * including calls to sk_EVP_PKEY_pop_free().
+ *
+ * Arguments:
+ * pkey - Entry which potentially has attributes to be freed.
+ *
+ * Returns:
+ * None.
+ */
+void
+sunw_evp_pkey_free(EVP_PKEY *pkey)
+{
+ if (pkey != NULL) {
+ if (pkey->attributes != NULL) {
+ sk_X509_ATTRIBUTE_pop_free(pkey->attributes,
+ X509_ATTRIBUTE_free);
+ pkey->attributes = NULL;
+ }
+ EVP_PKEY_free(pkey);
+ }
+}
+
+/*
+ * sunw_set_localkeyid() sets the localkeyid in a cert, a private key or
+ * both. Any existing localkeyid will be discarded.
+ *
+ * Arguments:
+ * keyid_str- A byte string with the localkeyid to set
+ * keyid_len- Length of the keyid byte string.
+ * pkey - Points to a private key to set the keyidstr in.
+ * cert - Points to a cert to set the keyidstr in.
+ *
+ * Note that setting a keyid into a cert which will not be written out as
+ * a PKCS12 cert is pointless since it will be lost.
+ *
+ * Returns:
+ * 0 - Success.
+ * < 0 - An error occurred. It was probably an error in allocating
+ * memory. The error will be set in the error stack. Call
+ * ERR_get_error() to get specific information.
+ */
+int
+sunw_set_localkeyid(const char *keyid_str, int keyid_len, EVP_PKEY *pkey,
+ X509 *cert)
+{
+ X509_ATTRIBUTE *attr = NULL;
+ ASN1_STRING *str = NULL;
+ ASN1_TYPE *keyid = NULL;
+ int retval = -1;
+ int i;
+
+ if (cert != NULL) {
+ if (X509_keyid_set1(cert, (uchar_t *)keyid_str, keyid_len)
+ == 0) {
+ SUNWerr(SUNW_F_SET_LOCALKEYID, SUNW_R_SET_LKID_ERR);
+ goto cleanup;
+ }
+ }
+ if (pkey != NULL) {
+ str = (ASN1_STRING *)M_ASN1_OCTET_STRING_new();
+ if (str == NULL ||
+ M_ASN1_OCTET_STRING_set(str, keyid_str, keyid_len) == 0 ||
+ (keyid = ASN1_TYPE_new()) == NULL) {
+ SUNWerr(SUNW_F_SET_LOCALKEYID, SUNW_R_MEMORY_FAILURE);
+ goto cleanup;
+ }
+
+ ASN1_TYPE_set(keyid, V_ASN1_OCTET_STRING, str);
+ str = NULL;
+
+ attr = type2attrib(keyid, NID_localKeyID);
+ if (attr == NULL) {
+ /*
+ * Error already on stack
+ */
+ goto cleanup;
+ }
+ keyid = NULL;
+
+ if (pkey->attributes == NULL) {
+ pkey->attributes = sk_X509_ATTRIBUTE_new_null();
+ if (pkey->attributes == NULL) {
+ SUNWerr(SUNW_F_SET_LOCALKEYID,
+ SUNW_R_MEMORY_FAILURE);
+ goto cleanup;
+ }
+ } else {
+ i = find_attr_by_nid(pkey->attributes, NID_localKeyID);
+ if (i >= 0)
+ sk_X509_ATTRIBUTE_delete(pkey->attributes, i);
+ }
+ if (sk_X509_ATTRIBUTE_push(pkey->attributes, attr) == 0) {
+ SUNWerr(SUNW_F_SET_LOCALKEYID, SUNW_R_MEMORY_FAILURE);
+ goto cleanup;
+ }
+ attr = NULL;
+ }
+ retval = 0;
+
+cleanup:
+ if (str != NULL)
+ ASN1_STRING_free(str);
+ if (keyid != NULL)
+ ASN1_TYPE_free(keyid);
+ if (attr != NULL)
+ X509_ATTRIBUTE_free(attr);
+
+ return (retval);
+}
+
+/*
+ * sunw_get_pkey_localkeyid() gets the localkeyid from a private key. It can
+ * optionally remove the value found.
+ *
+ * Arguments:
+ * dowhat - What to do with the attributes (remove them or copy them).
+ * pkey - Points to a private key to set the keyidstr in.
+ * keyid_str- Points to a location which will receive the pointer to
+ * a byte string containing the binary localkeyid. Note that
+ * this is a copy, and the caller must free it.
+ * keyid_len- Length of keyid_str.
+ *
+ * Returns:
+ * >= 0 - The number of characters in the keyid returned.
+ * < 0 - An error occurred. It was probably an error in allocating
+ * memory. The error will be set in the error stack. Call
+ * ERR_get_error() to get specific information.
+ */
+int
+sunw_get_pkey_localkeyid(getdo_actions_t dowhat, EVP_PKEY *pkey,
+ char **keyid_str, int *keyid_len)
+{
+ X509_ATTRIBUTE *attr = NULL;
+ ASN1_OCTET_STRING *str = NULL;
+ ASN1_TYPE *ty = NULL;
+ int len = 0;
+ int i;
+
+ if (keyid_str != NULL)
+ *keyid_str = NULL;
+ if (keyid_len != NULL)
+ *keyid_len = 0;
+
+ if (pkey == NULL || pkey->attributes == NULL) {
+ return (0);
+ }
+
+ if ((i = find_attr_by_nid(pkey->attributes, NID_localKeyID)) < 0) {
+ return (0);
+ }
+ attr = sk_X509_ATTRIBUTE_value(pkey->attributes, i);
+
+ if ((ty = attrib2type(attr)) == NULL ||
+ ty->type != V_ASN1_OCTET_STRING) {
+ return (0);
+ }
+
+ if (dowhat == GETDO_DEL) {
+ attr = sk_X509_ATTRIBUTE_delete(pkey->attributes, i);
+ if (attr != NULL)
+ X509_ATTRIBUTE_free(attr);
+ return (0);
+ }
+
+ str = ty->value.octet_string;
+ len = str->length;
+ if ((*keyid_str = malloc(len)) == NULL) {
+ SUNWerr(SUNW_F_GET_LOCALKEYID, SUNW_R_MEMORY_FAILURE);
+ return (-1);
+ }
+
+ (void) memcpy(*keyid_str, str->data, len);
+ *keyid_len = len;
+
+ return (len);
+}
+
+/*
+ * sunw_get_pkey_fname() gets the friendlyName from a private key. It can
+ * optionally remove the value found.
+ *
+ * Arguments:
+ * dowhat - What to do with the attributes (remove them or copy them).
+ * pkey - Points to a private key to get the frientlyname from
+ * fname - Points to a location which will receive the pointer to a
+ * byte string with the ASCII friendlyname
+ *
+ * Returns:
+ * >= 0 - The number of characters in the frienlyname returned.
+ * < 0 - An error occurred. It was probably an error in allocating
+ * memory. The error will be set in the error stack. Call
+ * ERR_get_error() to get specific information.
+ */
+int
+sunw_get_pkey_fname(getdo_actions_t dowhat, EVP_PKEY *pkey, char **fname)
+{
+ X509_ATTRIBUTE *attr = NULL;
+ ASN1_BMPSTRING *str = NULL;
+ ASN1_TYPE *ty = NULL;
+ int len = 0;
+ int i;
+
+ if (fname != NULL)
+ *fname = NULL;
+
+ if (pkey == NULL || pkey->attributes == NULL) {
+ return (0);
+ }
+
+ if ((i = find_attr_by_nid(pkey->attributes, NID_friendlyName)) < 0) {
+ return (0);
+ }
+ attr = sk_X509_ATTRIBUTE_value(pkey->attributes, i);
+
+ if ((ty = attrib2type(attr)) == NULL ||
+ ty->type != V_ASN1_BMPSTRING) {
+ return (0);
+ }
+
+ if (dowhat == GETDO_DEL) {
+ attr = sk_X509_ATTRIBUTE_delete(pkey->attributes, i);
+ if (attr != NULL)
+ X509_ATTRIBUTE_free(attr);
+ return (0);
+ }
+
+ str = ty->value.bmpstring;
+ *fname = uni2asc(str->data, str->length);
+ if (*fname == NULL) {
+ SUNWerr(SUNW_F_GET_PKEY_FNAME, SUNW_R_MEMORY_FAILURE);
+ return (-1);
+ }
+
+ len = strlen(*fname);
+
+ return (len);
+}
+
+/*
+ * sunw_find_localkeyid() searches stacks of certs and private keys,
+ * and returns the first matching cert/private key found.
+ *
+ * Look for a keyid in a stack of certs. if 'certs' is NULL and 'pkeys' is
+ * not NULL, search the list of private keys. Move the matching cert to
+ * 'matching_cert' and its matching private key to 'matching_pkey'. If no
+ * cert or keys match, no match occurred.
+ *
+ * Arguments:
+ * keyid_str- A byte string with the localkeyid to match
+ * keyid_len- Length of the keyid byte string.
+ * pkeys - Points to a stack of private keys which match the certs.
+ * This may be NULL, in which case no keys are returned.
+ * certs - Points to a stack of certs to search. If NULL, search the
+ * stack of keys instead.
+ * matching_pkey
+ * - Pointer to receive address of first matching pkey found.
+ * 'matching_pkey' must not be NULL; '*matching_pkey' will be
+ * reset.
+ * matching_cert
+ * - Pointer to receive address of first matching cert found.
+ * 'matching_cert' must not be NULL; '*matching_cert' will be
+ * reset.
+ *
+ * Returns:
+ * < 0 - An error returned. Call ERR_get_error() to get errors information.
+ * Where possible, memory has been freed.
+ * >= 0 - Objects were found and returned. Which objects are indicated by
+ * which bits are set (FOUND_PKEY and/or FOUND_CERT).
+ */
+int
+sunw_find_localkeyid(char *keyid_str, int len, STACK_OF(EVP_PKEY) *pkeys,
+STACK_OF(X509) *certs, EVP_PKEY **matching_pkey, X509 **matching_cert)
+{
+ ASN1_STRING *cmpstr = NULL;
+ EVP_PKEY *tmp_pkey = NULL;
+ X509 *tmp_cert = NULL;
+ int retval = 0;
+
+ /* If NULL arguments, this is an error */
+ if (keyid_str == NULL ||
+ (pkeys == NULL || certs == NULL) ||
+ (pkeys != NULL && matching_pkey == NULL) ||
+ (certs != NULL && matching_cert == NULL)) {
+ SUNWerr(SUNW_F_FIND_LOCALKEYID, SUNW_R_INVALID_ARG);
+ return (-1);
+ }
+
+ if (matching_pkey != NULL)
+ *matching_pkey = NULL;
+ if (matching_cert != NULL)
+ *matching_cert = NULL;
+
+ cmpstr = (ASN1_STRING *)M_ASN1_OCTET_STRING_new();
+ if (cmpstr == NULL ||
+ M_ASN1_OCTET_STRING_set(cmpstr, keyid_str, len) == 0) {
+ SUNWerr(SUNW_F_FIND_LOCALKEYID, SUNW_R_MEMORY_FAILURE);
+ return (-1);
+ }
+
+ retval = find_attr(NID_localKeyID, cmpstr, pkeys, &tmp_pkey, certs,
+ &tmp_cert);
+ if (retval == 0) {
+ ASN1_STRING_free(cmpstr);
+ return (retval);
+ }
+
+ if (matching_pkey != NULL)
+ *matching_pkey = tmp_pkey;
+ if (matching_cert != NULL)
+ *matching_cert = tmp_cert;
+
+ return (retval);
+}
+
+/*
+ * sunw_find_fname() searches stacks of certs and private keys for one with
+ * a matching friendlyname and returns the first matching cert/private
+ * key found.
+ *
+ * Look for a friendlyname in a stack of certs. if 'certs' is NULL and 'pkeys'
+ * is not NULL, search the list of private keys. Move the matching cert to
+ * 'matching_cert' and its matching private key to 'matching_pkey'. If no
+ * cert or keys match, no match occurred.
+ *
+ * Arguments:
+ * fname - Friendlyname to find (NULL-terminated ASCII string).
+ * pkeys - Points to a stack of private keys which match the certs.
+ * This may be NULL, in which case no keys are returned.
+ * certs - Points to a stack of certs to search. If NULL, search the
+ * stack of keys instead.
+ * matching_pkey
+ * - Pointer to receive address of first matching pkey found.
+ * matching_cert
+ * - Pointer to receive address of first matching cert found.
+ *
+ * Returns:
+ * < 0 - An error returned. Call ERR_get_error() to get errors information.
+ * Where possible, memory has been freed.
+ * >= 0 - Objects were found and returned. Which objects are indicated by
+ * which bits are set (FOUND_PKEY and/or FOUND_CERT).
+ */
+int
+sunw_find_fname(char *fname, STACK_OF(EVP_PKEY) *pkeys, STACK_OF(X509) *certs,
+ EVP_PKEY **matching_pkey, X509 ** matching_cert)
+{
+ ASN1_STRING *cmpstr = NULL;
+ EVP_PKEY *tmp_pkey = NULL;
+ X509 *tmp_cert = NULL;
+ int retval = 0;
+
+ /* If NULL arguments, this is an error */
+ if (fname == NULL ||
+ (pkeys == NULL && certs == NULL) ||
+ (pkeys != NULL && matching_pkey == NULL) ||
+ (certs != NULL && matching_cert == NULL)) {
+ SUNWerr(SUNW_F_FIND_FNAME, SUNW_R_INVALID_ARG);
+ return (-1);
+ }
+
+ if (matching_pkey != NULL)
+ *matching_pkey = NULL;
+ if (matching_cert != NULL)
+ *matching_cert = NULL;
+
+ cmpstr = (ASN1_STRING *)asc2bmpstring(fname, strlen(fname));
+ if (cmpstr == NULL) {
+ /*
+ * Error already on stack
+ */
+ return (-1);
+ }
+
+ retval = find_attr(NID_friendlyName, cmpstr, pkeys, &tmp_pkey, certs,
+ &tmp_cert);
+ if (retval == 0) {
+ ASN1_STRING_free(cmpstr);
+ return (retval);
+ }
+
+ if (matching_pkey != NULL)
+ *matching_pkey = tmp_pkey;
+ if (matching_cert != NULL)
+ *matching_cert = tmp_cert;
+
+ return (retval);
+}
+
+/*
+ * sunw_get_cert_fname() gets the fiendlyname from a cert. It can
+ * optionally remove the value found.
+ *
+ * Arguments:
+ * dowhat - What to do with the attributes (remove them or copy them).
+ * cert - Points to a cert to get the friendlyName from.
+ * fname - Points to a location which will receive the pointer to a
+ * byte string with the ASCII friendlyname
+ *
+ * Returns:
+ * >= 0 - The number of characters in the friendlyname returned.
+ * < 0 - An error occurred. It was probably an error in allocating
+ * memory. The error will be set in the error stack. Call
+ * ERR_get_error() to get specific information.
+ */
+int
+sunw_get_cert_fname(getdo_actions_t dowhat, X509 *cert, char **fname)
+{
+ int len;
+
+ if (fname != NULL)
+ *fname = NULL;
+
+ if (cert == NULL || cert->aux == NULL || cert->aux->alias == NULL) {
+ return (0);
+ }
+
+ if (dowhat == GETDO_DEL) {
+ /* Delete the entry */
+ ASN1_UTF8STRING_free(cert->aux->alias);
+ cert->aux->alias = NULL;
+ return (0);
+ }
+
+ *((uchar_t **)fname) = utf82ascstr(cert->aux->alias);
+ if (*fname == NULL) {
+ /*
+ * Error already on stack
+ */
+ return (-1);
+ }
+
+ len = strlen(*fname);
+
+ return (len);
+}
+
+/*
+ * sunw_set_fname() sets the friendlyName in a cert, a private key or
+ * both. Any existing friendlyname will be discarded.
+ *
+ * Arguments:
+ * ascname - An ASCII string with the friendlyName to set
+ * pkey - Points to a private key to set the fname in.
+ * cert - Points to a cert to set the fname in.
+ *
+ * Note that setting a friendlyName into a cert which will not be written out
+ * as a PKCS12 cert is pointless since it will be lost.
+ *
+ * Returns:
+ * 0 - Success.
+ * <0 - An error occurred. It was probably an error in allocating
+ * memory. The error will be set in the error stack. Call
+ * ERR_get_error() to get specific information.
+ */
+int
+sunw_set_fname(const char *ascname, EVP_PKEY *pkey, X509 *cert)
+{
+ X509_ATTRIBUTE *attr = NULL;
+ ASN1_BMPSTRING *str = NULL;
+ ASN1_TYPE *fname = NULL;
+ unsigned char *data = NULL;
+ int retval = -1;
+ int len;
+ int i;
+
+ str = asc2bmpstring(ascname, strlen(ascname));
+ if (str == NULL) {
+ /*
+ * Error already on stack
+ */
+ return (-1);
+ }
+
+ if (cert != NULL) {
+ if (cert->aux != NULL && cert->aux->alias != NULL) {
+ ASN1_UTF8STRING_free(cert->aux->alias);
+ }
+
+ len = ASN1_STRING_to_UTF8(&data, str);
+ i = -23;
+ if (len <= 0 || (i = X509_alias_set1(cert, data, len)) == 0) {
+ SUNWerr(SUNW_F_SET_FNAME, SUNW_R_SET_FNAME_ERR);
+ goto cleanup;
+ }
+ }
+ if (pkey != NULL) {
+ if ((fname = ASN1_TYPE_new()) == NULL) {
+ SUNWerr(SUNW_F_SET_FNAME, SUNW_R_MEMORY_FAILURE);
+ goto cleanup;
+ }
+
+ ASN1_TYPE_set(fname, V_ASN1_BMPSTRING, str);
+ str = NULL;
+
+ attr = type2attrib(fname, NID_friendlyName);
+ if (attr == NULL) {
+ /*
+ * Error already on stack
+ */
+ goto cleanup;
+ }
+ fname = NULL;
+
+ if (pkey->attributes == NULL) {
+ pkey->attributes = sk_X509_ATTRIBUTE_new_null();
+ if (pkey->attributes == NULL) {
+ SUNWerr(SUNW_F_SET_FNAME,
+ SUNW_R_MEMORY_FAILURE);
+ goto cleanup;
+ }
+ } else if ((i = find_attr_by_nid(pkey->attributes,
+ NID_friendlyName)) >= 0) {
+ (void) sk_X509_ATTRIBUTE_delete(pkey->attributes, i);
+ }
+
+ if (sk_X509_ATTRIBUTE_push(pkey->attributes, attr) == 0) {
+ SUNWerr(SUNW_F_SET_FNAME, SUNW_R_MEMORY_FAILURE);
+ goto cleanup;
+ }
+
+ attr = NULL;
+ }
+ retval = 0;
+
+cleanup:
+ if (data != NULL)
+ OPENSSL_free(data);
+ if (str != NULL)
+ ASN1_BMPSTRING_free(str);
+ if (fname != NULL)
+ ASN1_TYPE_free(fname);
+ if (attr != NULL)
+ X509_ATTRIBUTE_free(attr);
+
+ return (retval);
+}
+
+/*
+ * sunw_check_keys() compares the public key in the certificate and a
+ * private key to ensure that they match.
+ *
+ * Arguments:
+ * cert - Points to a certificate.
+ * pkey - Points to a private key.
+ *
+ * Returns:
+ * == 0 - These do not match.
+ * != 0 - The cert's public key and the private key match.
+ */
+int
+sunw_check_keys(X509 *cert, EVP_PKEY *pkey)
+{
+ int retval = 0;
+
+ if (pkey != NULL && cert != NULL)
+ retval = X509_check_private_key(cert, pkey);
+
+ return (retval);
+}
+
+/*
+ * sunw_check_cert_times() compares the time fields in a certificate
+ *
+ * Compare the 'not before' and the 'not after' times in the cert
+ * to the current time. Return the results of the comparison (bad time formats,
+ * cert not yet in force, cert expired or in range)
+ *
+ * Arguments:
+ * dowhat - what field(s) to check.
+ * cert - Points to a cert to check
+ *
+ * Returns:
+ * Results of the comparison.
+ */
+chk_errs_t
+sunw_check_cert_times(chk_actions_t chkwhat, X509 *cert)
+{
+ return (check_time(chkwhat, cert));
+}
+
+/*
+ * ----------------------------------------------------------------------------
+ * Local routines
+ * ----------------------------------------------------------------------------
+ */
+
+
+/*
+ * parse_pkcs12 - Oversee parsing of the pkcs12 structure. Get it
+ * parsed. After that either return what's found directly, or
+ * do any required matching.
+ *
+ * Arguments:
+ * p12 - Structure with pkcs12 info to be parsed
+ * pass - Pass phrase for the private key (possibly empty) or NULL if
+ * there is none.
+ * matchty - Info about which certs/keys to return if many are in the file.
+ * keyid - If private key localkeyids friendlynames are to match a
+ * predetermined value, the value to match. This value should
+ * be an octet string.
+ * keyid_len- Length of the keyid byte string.
+ * name_str - If friendlynames are to match a predetermined value, the value
+ * to match. This value should be a NULL terminated string.
+ * pkey - Points to location pointing to the private key returned.
+ * cert - Points to locaiton which points to the client cert returned
+ * ca - Points to location that points to a stack of 'certificate
+ * authority' certs/trust anchors.
+ *
+ * Note about error codes: This function is an internal function, and the
+ * place where it is called sets error codes. Therefore only set an error
+ * code if it is something that is unique or if the function which detected
+ * the error doesn't set one.
+ *
+ * Returns:
+ * == -1 - An error occurred. Call ERR_get_error() to get error information.
+ * Where possible, memory has been freed.
+ * == 0 - No matching returns were found.
+ * > 0 - This is the aithmetic 'or' of the FOUND_* bits that indicate which
+ * of the requested entries were found.
+ */
+static int
+parse_pkcs12(PKCS12 *p12, const char *pass, int matchty, char *keyid,
+ int kstr_len, char *name_str, EVP_PKEY **pkey, X509 **cert,
+ STACK_OF(X509) **ca)
+{
+ STACK_OF(EVP_PKEY) *work_kl = NULL; /* Head for private key list */
+ STACK_OF(EVP_PKEY) *nocerts = NULL; /* Head for alt. key list */
+ STACK_OF(X509) *work_ca = NULL; /* Head for cert list */
+ STACK_OF(X509) *work_cl = NULL;
+ int retval = 0;
+ int n;
+
+ retval = sunw_PKCS12_contents(p12, pass, &work_kl, &work_ca);
+ if (retval < 0) {
+ goto cleanup;
+ } else if (retval == 0) {
+ /*
+ * Not really an error here - its just that nothing was found.
+ */
+ goto cleanup;
+ }
+
+ if (sk_EVP_PKEY_num(work_kl) > 0) {
+
+ if (sunw_split_certs(work_kl, work_ca, &work_cl, &nocerts)
+ < 0) {
+ goto cleanup;
+ }
+ }
+
+ /*
+ * Go through the lists of certs and private keys which were
+ * returned, looking for matches of the appropriate type. Do these
+ * in the order described above.
+ */
+ if ((matchty & DO_FIND_KEYID) != 0) {
+
+ if (keyid == NULL) {
+ SUNWerr(SUNW_F_PKCS12_PARSE, SUNW_R_INVALID_ARG);
+ retval = -1;
+ goto cleanup;
+ }
+
+ /* See if string matches localkeyid's */
+ retval = sunw_find_localkeyid(keyid, kstr_len,
+ work_kl, work_cl, pkey, cert);
+ if (retval != 0) {
+ if (retval == -1)
+ goto cleanup;
+ else
+ goto last_part;
+ }
+ }
+ if ((matchty & DO_FIND_FN) != 0) {
+
+ if (name_str == NULL) {
+ SUNWerr(SUNW_F_PKCS12_PARSE, SUNW_R_INVALID_ARG);
+ retval = -1;
+ goto cleanup;
+ }
+
+ /* See if string matches friendly names */
+ retval = sunw_find_fname(name_str, work_kl, work_cl,
+ pkey, cert);
+ if (retval != 0) {
+ if (retval == -1)
+ goto cleanup;
+ else
+ goto last_part;
+ }
+ }
+
+ if (matchty & DO_FIRST_PAIR) {
+
+ /* Find the first cert and private key and return them */
+ retval = get_key_cert(0, work_kl, pkey, work_cl, cert);
+ if (retval != 0) {
+ if (retval == -1)
+ goto cleanup;
+ else
+ goto last_part;
+ }
+ }
+
+ if (matchty & DO_LAST_PAIR) {
+
+ /*
+ * Find the last matching cert and private key and return
+ * them. Since keys which don't have matching client certs
+ * are at the end of the list of keys, use the number of
+ * client certs to compute the position of the last private
+ * key which matches a client cert.
+ */
+ n = sk_X509_num(work_cl) - 1;
+ retval = get_key_cert(n, work_kl, pkey, work_cl, cert);
+ if (retval != 0) {
+ if (retval == -1)
+ goto cleanup;
+ else
+ goto last_part;
+ }
+ }
+
+ if (matchty & DO_UNMATCHING) {
+ STACK_OF(EVP_PKEY) *tmpk;
+ STACK_OF(X509) *tmpc;
+
+ /* Find the first cert and private key and return them */
+ tmpc = work_cl;
+ if (work_cl == NULL || sk_X509_num(work_cl) == 0)
+ tmpc = work_ca;
+ tmpk = work_kl;
+ if (work_kl == NULL || sk_EVP_PKEY_num(work_kl) == 0)
+ tmpk = nocerts;
+ retval = get_key_cert(0, tmpk, pkey, tmpc, cert);
+ if (retval != 0) {
+ if (retval == -1)
+ goto cleanup;
+ else
+ goto last_part;
+ }
+ }
+
+last_part:
+ /* If no errors, terminate normally */
+ if (retval != -1)
+ retval |= set_results(NULL, NULL, NULL, NULL, ca, &work_ca,
+ NULL, NULL);
+ if (retval >= 0) {
+ goto clean_part;
+ }
+
+ /* Fallthrough is intentional in error cases. */
+cleanup:
+ if (pkey != NULL && *pkey != NULL) {
+ sunw_evp_pkey_free(*pkey);
+ *pkey = NULL;
+ }
+ if (cert != NULL && *cert != NULL) {
+ X509_free(*cert);
+ *cert = NULL;
+ }
+
+clean_part:
+
+ if (work_kl != NULL) {
+ sk_EVP_PKEY_pop_free(work_kl, sunw_evp_pkey_free);
+ }
+ if (work_ca != NULL)
+ sk_X509_pop_free(work_ca, X509_free);
+ if (work_cl != NULL)
+ sk_X509_pop_free(work_cl, X509_free);
+
+ return (retval);
+}
+
+/*
+ * parse_outer - Unpack the outer PKCS#12 structure and go through the
+ * individual bags. Return stacks of certs, private keys found and
+ * CA certs found.
+ *
+ * Note about error codes: This function is an internal function, and the
+ * place where it is called sets error codes.
+ *
+ * Returns:
+ * 0 - An error returned. Call ERR_get_error() to get errors information.
+ * Where possible, memory has been freed.
+ * 1 - PKCS12 data object was parsed and lists of certs and private keys
+ * were returned.
+ */
+static int
+parse_outer(PKCS12 *p12, const char *pass, STACK_OF(EVP_PKEY) *kl,
+ STACK_OF(X509) *cl)
+{
+ STACK_OF(PKCS12_SAFEBAG) *bags;
+ STACK_OF(PKCS7) *asafes;
+ int i, bagnid;
+ PKCS7 *p7;
+
+ if ((asafes = M_PKCS12_unpack_authsafes(p12)) == NULL)
+ return (0);
+
+ for (i = 0; i < sk_PKCS7_num(asafes); i++) {
+ p7 = sk_PKCS7_value(asafes, i);
+ bagnid = OBJ_obj2nid(p7->type);
+ if (bagnid == NID_pkcs7_data) {
+ bags = M_PKCS12_unpack_p7data(p7);
+ } else if (bagnid == NID_pkcs7_encrypted) {
+ /*
+ * A length of '-1' means strlen() can be used
+ * to determine the password length.
+ */
+ bags = M_PKCS12_unpack_p7encdata(p7, pass, -1);
+ } else {
+ SUNWerr(SUNW_F_PARSE_OUTER, SUNW_R_BAD_BAGTYPE);
+ return (0);
+ }
+
+ if (bags == NULL) {
+ SUNWerr(SUNW_F_PARSE_OUTER, SUNW_R_PARSE_BAG_ERR);
+ sk_PKCS7_pop_free(asafes, PKCS7_free);
+ return (0);
+ }
+ if (parse_all_bags(bags, pass, kl, cl) == 0) {
+ sk_PKCS12_SAFEBAG_pop_free(bags, PKCS12_SAFEBAG_free);
+ sk_PKCS7_pop_free(asafes, PKCS7_free);
+ return (0);
+ }
+ }
+
+ return (1);
+}
+
+/*
+ * parse_all_bags - go through the stack of bags, parsing each.
+ *
+ * Note about error codes: This function is an internal function, and the
+ * place where it is called sets error codes.
+ *
+ * Returns:
+ * 0 - An error returned. Call ERR_get_error() to get errors information.
+ * Where possible, memory has been freed.
+ * 1 - Stack of safebags was parsed and lists of certs and private keys
+ * were returned.
+ */
+static int
+parse_all_bags(STACK_OF(PKCS12_SAFEBAG) *bags, const char *pass,
+ STACK_OF(EVP_PKEY) *kl, STACK_OF(X509) *cl)
+{
+ int i;
+ for (i = 0; i < sk_PKCS12_SAFEBAG_num(bags); i++) {
+ if (parse_one_bag(sk_PKCS12_SAFEBAG_value(bags, i),
+ pass, kl, cl) == 0)
+ return (0);
+ }
+ return (1);
+}
+
+/*
+ * parse_one_bag - Parse an individual bag
+ *
+ * i = parse_one_bag(bag, pass, kl, cl);
+ *
+ * Arguments:
+ * bag - pkcs12 safebag to parse.
+ * pass - password for use in decryption of shrouded keybag
+ * kl - Stack of private keys found so far. New private keys will
+ * be added here if found.
+ * cl - Stack of certs found so far. New certificates will be
+ * added here if found.
+ *
+ * Returns:
+ * 0 - An error returned. Call ERR_get_error() to get errors information.
+ * Where possible, memory has been freed.
+ * 1 - one safebag was parsed. If it contained a cert or private key, it
+ * was added to the stack of certs or private keys found, respectively.
+ * localKeyId or friendlyName attributes are returned with the
+ * private key or certificate.
+ */
+static int
+parse_one_bag(PKCS12_SAFEBAG *bag, const char *pass, STACK_OF(EVP_PKEY) *kl,
+ STACK_OF(X509) *cl)
+{
+ X509_ATTRIBUTE *attr = NULL;
+ ASN1_TYPE *keyid = NULL;
+ ASN1_TYPE *fname = NULL;
+ PKCS8_PRIV_KEY_INFO *p8;
+ EVP_PKEY *pkey = NULL;
+ X509 *x509 = NULL;
+ uchar_t *data = NULL;
+ char *str = NULL;
+ int retval = 1;
+
+ keyid = PKCS12_get_attr(bag, NID_localKeyID);
+ fname = PKCS12_get_attr(bag, NID_friendlyName);
+
+ switch (M_PKCS12_bag_type(bag)) {
+ case NID_keyBag:
+ if ((pkey = EVP_PKCS82PKEY(bag->value.keybag)) == NULL) {
+ SUNWerr(SUNW_F_PARSE_ONE_BAG, SUNW_R_PARSE_BAG_ERR);
+ retval = 0;
+ break;
+ }
+ break;
+
+ case NID_pkcs8ShroudedKeyBag:
+ /*
+ * A length of '-1' means strlen() can be used
+ * to determine the password length.
+ */
+ if ((p8 = M_PKCS12_decrypt_skey(bag, pass, -1)) == NULL) {
+ SUNWerr(SUNW_F_PARSE_ONE_BAG, SUNW_R_PARSE_BAG_ERR);
+ retval = 0;
+ break;
+ }
+ pkey = EVP_PKCS82PKEY(p8);
+ PKCS8_PRIV_KEY_INFO_free(p8);
+ if (pkey == NULL) {
+ SUNWerr(SUNW_F_PARSE_ONE_BAG, SUNW_R_PARSE_BAG_ERR);
+ retval = 0;
+ }
+ break;
+
+ case NID_certBag:
+ if (M_PKCS12_cert_bag_type(bag) != NID_x509Certificate) {
+ SUNWerr(SUNW_F_PARSE_ONE_BAG, SUNW_R_BAD_CERTTYPE);
+ break;
+ }
+ if ((x509 = M_PKCS12_certbag2x509(bag)) == NULL) {
+ SUNWerr(SUNW_F_PARSE_ONE_BAG,
+ SUNW_R_PARSE_CERT_ERR);
+ retval = 0;
+ break;
+ }
+
+ if (keyid != NULL) {
+ if (keyid->type != V_ASN1_OCTET_STRING) {
+ SUNWerr(SUNW_F_PARSE_ONE_BAG,
+ SUNW_R_BAD_LKID);
+ retval = 0;
+ break;
+ }
+ if (X509_keyid_set1(x509,
+ keyid->value.octet_string->data,
+ keyid->value.octet_string->length) == 0) {
+ SUNWerr(SUNW_F_PARSE_ONE_BAG,
+ SUNW_R_SET_LKID_ERR);
+ retval = 0;
+ break;
+ }
+ }
+
+ if (fname != NULL) {
+ ASN1_STRING *tmpstr = NULL;
+ int len;
+
+ if (fname->type != V_ASN1_BMPSTRING) {
+ SUNWerr(SUNW_F_PARSE_ONE_BAG,
+ SUNW_R_BAD_FNAME);
+ retval = 0;
+ break;
+ }
+
+ tmpstr = fname->value.asn1_string;
+ len = ASN1_STRING_to_UTF8(&data, tmpstr);
+ if (len < 0) {
+ SUNWerr(SUNW_F_PARSE_ONE_BAG,
+ SUNW_R_SET_FNAME_ERR);
+ retval = 0;
+ break;
+ }
+
+ if (X509_alias_set1(x509, data, len) == 0) {
+ SUNWerr(SUNW_F_PARSE_ONE_BAG,
+ SUNW_R_SET_FNAME_ERR);
+ retval = 0;
+ break;
+ }
+ }
+
+ if (sk_X509_push(cl, x509) == 0) {
+ SUNWerr(SUNW_F_PARSE_ONE_BAG, SUNW_R_MEMORY_FAILURE);
+ retval = 0;
+ break;
+ }
+ x509 = NULL;
+ break;
+
+ case NID_safeContentsBag:
+ if (keyid != NULL)
+ ASN1_TYPE_free(keyid);
+ if (fname != NULL)
+ ASN1_TYPE_free(fname);
+ if (parse_all_bags(bag->value.safes, pass, kl, cl) == 0) {
+ /*
+ * Error already on stack
+ */
+ return (0);
+ }
+ return (1);
+
+ default:
+ if (keyid != NULL)
+ ASN1_TYPE_free(keyid);
+ if (fname != NULL)
+ ASN1_TYPE_free(fname);
+ SUNWerr(SUNW_F_PARSE_ONE_BAG, SUNW_R_BAD_BAGTYPE);
+ return (0);
+ }
+
+
+ if (pkey != NULL) {
+ if (retval != 0 && (keyid != NULL || fname != NULL) &&
+ pkey->attributes == NULL) {
+ pkey->attributes = sk_X509_ATTRIBUTE_new_null();
+ if (pkey->attributes == NULL) {
+ SUNWerr(SUNW_F_PARSE_ONE_BAG,
+ SUNW_R_MEMORY_FAILURE);
+ retval = 0;
+ }
+ }
+
+ if (retval != 0 && keyid != NULL) {
+ attr = type2attrib(keyid, NID_localKeyID);
+ if (attr == NULL)
+ /*
+ * Error already on stack
+ */
+ retval = 0;
+ else {
+ keyid = NULL;
+ if (sk_X509_ATTRIBUTE_push(pkey->attributes,
+ attr) == 0) {
+ SUNWerr(SUNW_F_PARSE_ONE_BAG,
+ SUNW_R_MEMORY_FAILURE);
+ retval = 0;
+ } else {
+ attr = NULL;
+ }
+ }
+ }
+
+ if (retval != 0 && fname != NULL) {
+ attr = type2attrib(fname, NID_friendlyName);
+ if (attr == NULL) {
+ /*
+ * Error already on stack
+ */
+ retval = 0;
+ } else {
+ fname = NULL;
+ if (sk_X509_ATTRIBUTE_push(pkey->attributes,
+ attr) == 0) {
+ SUNWerr(SUNW_F_PARSE_ONE_BAG,
+ SUNW_R_MEMORY_FAILURE);
+ retval = 0;
+ } else {
+ attr = NULL;
+ }
+ }
+ }
+
+ /* Save the private key */
+ if (retval != 0) {
+ if (sk_EVP_PKEY_push(kl, pkey) == 0) {
+ SUNWerr(SUNW_F_PARSE_ONE_BAG,
+ SUNW_R_MEMORY_FAILURE);
+ retval = 0;
+ } else {
+ pkey = NULL;
+ }
+ }
+ }
+
+ if (pkey != NULL) {
+ sunw_evp_pkey_free(pkey);
+ }
+
+ if (x509 != NULL)
+ X509_free(x509);
+
+ if (keyid != NULL)
+ ASN1_TYPE_free(keyid);
+
+ if (fname != NULL)
+ ASN1_TYPE_free(fname);
+
+ if (attr != NULL)
+ X509_ATTRIBUTE_free(attr);
+
+ if (data != NULL)
+ OPENSSL_free(data);
+
+ if (str != NULL)
+ OPENSSL_free(str);
+
+ return (retval);
+}
+
+/*
+ * This function uses the only function that reads PEM files, regardless of
+ * the kinds of information included (private keys, public keys, cert requests,
+ * certs). Other interfaces that read files require that the application
+ * specifically know what kinds of things to read next, and call different
+ * interfaces for the different kinds of entities.
+ *
+ * There is only one aspect of this function that's a bit problematic.
+ * If it finds an encrypted private key, it does not decrypt it. It returns
+ * the encrypted data and other information needed to decrypt it. The caller
+ * must do the decryption. This function does the decoding.
+ */
+static int
+pem_info(FILE *fp, pem_password_cb cb, void *userdata,
+ STACK_OF(EVP_PKEY) **pkeys, STACK_OF(X509) **certs)
+{
+ STACK_OF(X509_INFO) *info;
+ STACK_OF(EVP_PKEY) *work_kl;
+ STACK_OF(X509) *work_cl;
+ X509_INFO *x;
+ int retval = 0;
+ int i;
+
+ info = PEM_X509_INFO_read(fp, NULL, cb, userdata);
+ if (info == NULL) {
+ SUNWerr(SUNW_F_PEM_INFO, SUNW_R_READ_ERR);
+ return (-1);
+ }
+
+ /*
+ * Allocate the working stacks for private key(s) and for the cert(s).
+ */
+ if ((work_kl = sk_EVP_PKEY_new_null()) == NULL) {
+ SUNWerr(SUNW_F_PEM_INFO, SUNW_R_MEMORY_FAILURE);
+ retval = -1;
+ goto cleanup;
+ }
+
+ if ((work_cl = sk_X509_new_null()) == NULL) {
+ SUNWerr(SUNW_F_PEM_INFO, SUNW_R_MEMORY_FAILURE);
+ retval = -1;
+ goto cleanup;
+ }
+
+ /*
+ * Go through the entries in the info structure.
+ */
+ for (i = 0; i < sk_X509_INFO_num(info); i++) {
+ x = sk_X509_INFO_value(info, i);
+ if (x->x509) {
+ if (sk_X509_push(work_cl, x->x509) == 0) {
+ retval = -1;
+ break;
+ }
+ x->x509 = NULL;
+ }
+ if (x->x_pkey != NULL && x->x_pkey->dec_pkey != NULL &&
+ (x->x_pkey->dec_pkey->type == EVP_PKEY_RSA ||
+ x->x_pkey->dec_pkey->type == EVP_PKEY_DSA)) {
+ const uchar_t *p;
+
+ /*
+ * If the key was encrypted, PEM_X509_INFO_read does
+ * not decrypt it. If that is the case, the 'enc_pkey'
+ * field is set to point to the unencrypted key data.
+ * Go through the additional steps to decode it before
+ * going on.
+ */
+ if (x->x_pkey->enc_pkey != NULL) {
+
+ if (PEM_do_header(&x->enc_cipher,
+ (uchar_t *)x->enc_data,
+ (long *)&x->enc_len,
+ cb, userdata) == 0) {
+ if (ERR_GET_REASON(ERR_peek_error()) ==
+ PEM_R_BAD_PASSWORD_READ) {
+ SUNWerr(SUNW_F_PEM_INFO,
+ SUNW_R_PASSWORD_ERR);
+ } else {
+ SUNWerr(SUNW_F_PEM_INFO,
+ SUNW_R_PKEY_READ_ERR);
+ }
+ retval = -1;
+ break;
+ }
+ if (x->x_pkey->dec_pkey->type == EVP_PKEY_RSA) {
+ RSA **pp;
+
+ pp = &(x->x_pkey->dec_pkey->pkey.rsa);
+ p = (uchar_t *)x->enc_data;
+ if (d2i_RSAPrivateKey(pp, &p,
+ x->enc_len) == NULL) {
+ SUNWerr(SUNW_F_PEM_INFO,
+ SUNW_R_PKEY_READ_ERR);
+ retval = -1;
+ break;
+ }
+ } else {
+ DSA **pp;
+
+ pp = &(x->x_pkey->dec_pkey->pkey.dsa);
+ p = (uchar_t *)x->enc_data;
+ if (d2i_DSAPrivateKey(pp, &p,
+ x->enc_len) == NULL) {
+ SUNWerr(SUNW_F_PEM_INFO,
+ SUNW_R_PKEY_READ_ERR);
+ retval = -1;
+ break;
+ }
+ }
+ }
+
+ /* Save the key. */
+ retval = sk_EVP_PKEY_push(work_kl, x->x_pkey->dec_pkey);
+ if (retval == 0) {
+ retval = -1;
+ break;
+ }
+ x->x_pkey->dec_pkey = NULL;
+ } else if (x->x_pkey != NULL) {
+ SUNWerr(SUNW_F_PEM_INFO, SUNW_R_BAD_PKEYTYPE);
+ retval = -1;
+ break;
+ }
+ }
+ if (retval == -1)
+ goto cleanup;
+
+ /* If error occurs, then error already on stack */
+ retval = set_results(pkeys, &work_kl, certs, &work_cl, NULL, NULL,
+ NULL, NULL);
+
+cleanup:
+ if (work_kl != NULL) {
+ sk_EVP_PKEY_pop_free(work_kl, sunw_evp_pkey_free);
+ }
+ if (work_cl != NULL)
+ sk_X509_pop_free(work_cl, X509_free);
+
+ sk_X509_INFO_pop_free(info, X509_INFO_free);
+
+ return (retval);
+}
+
+/*
+ * sunw_append_keys - Given two stacks of private keys, remove the keys from
+ * the second stack and append them to the first. Both stacks must exist
+ * at time of call.
+ *
+ * Arguments:
+ * dst - the stack to receive the keys from 'src'
+ * src - the stack whose keys are to be moved.
+ *
+ * Returns:
+ * -1 - An error occurred. The error status is set.
+ * >= 0 - The number of keys that were copied.
+ */
+static int
+sunw_append_keys(STACK_OF(EVP_PKEY) *dst, STACK_OF(EVP_PKEY) *src)
+{
+ EVP_PKEY *tmpk;
+ int count = 0;
+
+ while (sk_EVP_PKEY_num(src) > 0) {
+ tmpk = sk_EVP_PKEY_delete(src, 0);
+ if (sk_EVP_PKEY_push(dst, tmpk) == 0) {
+ sunw_evp_pkey_free(tmpk);
+ SUNWerr(SUNW_F_APPEND_KEYS, SUNW_R_MEMORY_FAILURE);
+ return (-1);
+ }
+ count ++;
+ }
+
+ return (count);
+}
+
+/*
+ * move_certs - Given two stacks of certs, remove the certs from
+ * the second stack and append them to the first.
+ *
+ * Arguments:
+ * dst - the stack to receive the certs from 'src'
+ * src - the stack whose certs are to be moved.
+ *
+ * Returns:
+ * -1 - An error occurred. The error status is set.
+ * >= 0 - The number of certs that were copied.
+ */
+static int
+move_certs(STACK_OF(X509) *dst, STACK_OF(X509) *src)
+{
+ X509 *tmpc;
+ int count = 0;
+
+ while (sk_X509_num(src) > 0) {
+ tmpc = sk_X509_delete(src, 0);
+ if (sk_X509_push(dst, tmpc) == 0) {
+ X509_free(tmpc);
+ SUNWerr(SUNW_F_MOVE_CERTS, SUNW_R_MEMORY_FAILURE);
+ return (-1);
+ }
+ count++;
+ }
+
+ return (count);
+}
+
+/*
+ * get_key_cert - Get a cert and its matching key from the stacks of certs
+ * and keys. They are removed from the stacks.
+ *
+ * Arguments:
+ * n - Offset of the entries to return.
+ * kl - Points to a stack of private keys that matches the list of
+ * certs below.
+ * pkey - Points at location where the address of the matching private
+ * key will be stored.
+ * cl - Points to a stack of client certs with matching private keys.
+ * cert - Points to locaiton where the address of the matching client cert
+ * will be returned
+ *
+ * The assumption is that the stacks of keys and certs contain key/cert pairs,
+ * with entries in the same order and hence at the same offset. Provided
+ * the key and cert selected match, each will be removed from its stack and
+ * returned.
+ *
+ * A stack of certs can be passed in without a stack of private keys, and vise
+ * versa. In that case, the indicated key/cert will be returned.
+ *
+ * Returns:
+ * 0 - No matches were found.
+ * > 0 - Bits set based on FOUND_* definitions, indicating what is returned.
+ * This can be FOUND_PKEY, FOUND_CERT or (FOUND_PKEY | FOUND_CERT).
+ */
+static int
+get_key_cert(int n, STACK_OF(EVP_PKEY) *kl, EVP_PKEY **pkey, STACK_OF(X509) *cl,
+ X509 **cert)
+{
+ int retval = 0;
+ int nk;
+ int nc;
+
+ nk = (kl != NULL) ? sk_EVP_PKEY_num(kl) : 0;
+ nc = (cl != NULL) ? sk_X509_num(cl) : 0;
+
+ if (pkey != NULL && *pkey == NULL) {
+ if (nk > 0 && n >= 0 || n < nk) {
+ *pkey = sk_EVP_PKEY_delete(kl, n);
+ if (*pkey != NULL)
+ retval |= FOUND_PKEY;
+ }
+ }
+
+ if (cert != NULL && *cert == NULL) {
+ if (nc > 0 && n >= 0 && n < nc) {
+ *cert = sk_X509_delete(cl, n);
+ if (*cert != NULL)
+ retval |= FOUND_CERT;
+ }
+ }
+
+ return (retval);
+}
+
+
+/*
+ * asc2bmpstring - Convert a regular C ASCII string to an ASn1_STRING in
+ * ASN1_BMPSTRING format.
+ *
+ * Arguments:
+ * str - String to be convered.
+ * len - Length of the string.
+ *
+ * Returns:
+ * == NULL - An error occurred. Error information (accessible by
+ * ERR_get_error()) is set.
+ * != NULL - Points to an ASN1_BMPSTRING structure with the converted
+ * string as a value.
+ */
+static ASN1_BMPSTRING *
+asc2bmpstring(const char *str, int len)
+{
+ ASN1_BMPSTRING *bmp = NULL;
+ uchar_t *uni = NULL;
+ int unilen;
+
+ /* Convert the character to the bmp format. */
+ if (asc2uni(str, len, &uni, &unilen) == 0) {
+ SUNWerr(SUNW_F_ASC2BMPSTRING, SUNW_R_MEMORY_FAILURE);
+ return (NULL);
+ }
+
+ /*
+ * Adjust for possible pair of NULL bytes at the end because
+ * asc2uni() returns a doubly null terminated string.
+ */
+ if (uni[unilen - 1] == '\0' && uni[unilen - 2] == '\0')
+ unilen -= 2;
+
+ /* Construct comparison string with correct format */
+ bmp = M_ASN1_BMPSTRING_new();
+ if (bmp == NULL) {
+ SUNWerr(SUNW_F_ASC2BMPSTRING, SUNW_R_MEMORY_FAILURE);
+ OPENSSL_free(uni);
+ return (NULL);
+ }
+
+ bmp->data = uni;
+ bmp->length = unilen;
+
+ return (bmp);
+}
+
+/*
+ * utf82ascstr - Convert a UTF8STRING string to a regular C ASCII string.
+ * This goes through an intermediate step with a ASN1_STRING type of
+ * IA5STRING (International Alphabet 5, which is the same as ASCII).
+ *
+ * Arguments:
+ * str - UTF8STRING to be converted.
+ *
+ * Returns:
+ * == NULL - An error occurred. Error information (accessible by
+ * ERR_get_error()) is set.
+ * != NULL - Points to a NULL-termianted ASCII string. The caller must
+ * free it.
+ */
+static uchar_t *
+utf82ascstr(ASN1_UTF8STRING *ustr)
+{
+ ASN1_STRING tmpstr;
+ ASN1_STRING *astr = &tmpstr;
+ uchar_t *retstr = NULL;
+ int mbflag;
+ int ret;
+
+ if (ustr == NULL || ustr->type != V_ASN1_UTF8STRING) {
+ SUNWerr(SUNW_F_UTF82ASCSTR, SUNW_R_INVALID_ARG);
+ return (NULL);
+ }
+
+ mbflag = MBSTRING_ASC;
+ tmpstr.data = NULL;
+ tmpstr.length = 0;
+
+ ret = ASN1_mbstring_copy(&astr, ustr->data, ustr->length, mbflag,
+ B_ASN1_IA5STRING);
+ if (ret < 0) {
+ SUNWerr(SUNW_F_UTF82ASCSTR, SUNW_R_STR_CONVERT_ERR);
+ return (NULL);
+ }
+
+ retstr = OPENSSL_malloc(astr->length + 1);
+ if (retstr == NULL) {
+ SUNWerr(SUNW_F_UTF82ASCSTR, SUNW_R_MEMORY_FAILURE);
+ return (NULL);
+ }
+
+ (void) memcpy(retstr, astr->data, astr->length);
+ retstr[astr->length] = '\0';
+ OPENSSL_free(astr->data);
+
+ return (retstr);
+}
+
+
+/*
+ * type2attrib - Given a ASN1_TYPE, return a X509_ATTRIBUTE of the type
+ * specified by the given NID.
+ *
+ * Arguments:
+ * ty - Type structure to be made into an attribute
+ * nid - NID of the attribute
+ *
+ * Returns:
+ * NULL An error occurred.
+ * != NULL An X509_ATTRIBUTE structure.
+ */
+X509_ATTRIBUTE *
+type2attrib(ASN1_TYPE *ty, int nid)
+{
+ X509_ATTRIBUTE *a;
+
+ if ((a = X509_ATTRIBUTE_new()) == NULL ||
+ (a->value.set = sk_ASN1_TYPE_new_null()) == NULL ||
+ sk_ASN1_TYPE_push(a->value.set, ty) == 0) {
+ if (a != NULL)
+ X509_ATTRIBUTE_free(a);
+ SUNWerr(SUNW_F_TYPE2ATTRIB, SUNW_R_MEMORY_FAILURE);
+ return (NULL);
+ }
+ a->single = 0;
+ a->object = OBJ_nid2obj(nid);
+
+ return (a);
+}
+
+/*
+ * attrib2type - Given a X509_ATTRIBUTE, return pointer to the ASN1_TYPE
+ * component
+ *
+ * Arguments:
+ * attr - Attribute structure containing a type.
+ *
+ * Returns:
+ * NULL An error occurred.
+ * != NULL An ASN1_TYPE structure.
+ */
+static ASN1_TYPE *
+attrib2type(X509_ATTRIBUTE *attr)
+{
+ ASN1_TYPE *ty = NULL;
+
+ if (attr == NULL || attr->single == 1)
+ return (NULL);
+
+ if (sk_ASN1_TYPE_num(attr->value.set) > 0)
+ ty = sk_ASN1_TYPE_value(attr->value.set, 0);
+
+ return (ty);
+}
+
+/*
+ * find_attr_by_nid - Given a ASN1_TYPE, return the offset of a X509_ATTRIBUTE
+ * of the type specified by the given NID.
+ *
+ * Arguments:
+ * attrs - Stack of attributes to search
+ * nid - NID of the attribute being searched for
+ *
+ * Returns:
+ * -1 None found
+ * != -1 Offset of the matching attribute.
+ */
+static int
+find_attr_by_nid(STACK_OF(X509_ATTRIBUTE) *attrs, int nid)
+{
+ X509_ATTRIBUTE *a;
+ int i;
+
+ if (attrs == NULL)
+ return (-1);
+
+ for (i = 0; i < sk_X509_ATTRIBUTE_num(attrs); i++) {
+ a = sk_X509_ATTRIBUTE_value(attrs, i);
+ if (OBJ_obj2nid(a->object) == nid)
+ return (i);
+ }
+ return (-1);
+}
+
+/*
+ * Called by our PKCS12 code to read our function and error codes
+ * into memory so that the OpenSSL framework can retrieve them.
+ */
+void
+ERR_load_SUNW_strings(void)
+{
+ assert(SUNW_lib_error_code == 0);
+#ifndef OPENSSL_NO_ERR
+ /*
+ * Have OpenSSL provide us with a unique ID.
+ */
+ SUNW_lib_error_code = ERR_get_next_error_library();
+
+ ERR_load_strings(SUNW_lib_error_code, SUNW_str_functs);
+ ERR_load_strings(SUNW_lib_error_code, SUNW_str_reasons);
+
+ SUNW_lib_name->error = ERR_PACK(SUNW_lib_error_code, 0, 0);
+ ERR_load_strings(0, SUNW_lib_name);
+#endif
+}
+
+/*
+ * The SUNWerr macro resolves to this routine. So when we need
+ * to push an error, this routine does it for us. Notice that
+ * the SUNWerr macro provides a filename and line #.
+ */
+void
+ERR_SUNW_error(int function, int reason, char *file, int line)
+{
+ assert(SUNW_lib_error_code != 0);
+#ifndef OPENSSL_NO_ERR
+ ERR_PUT_error(SUNW_lib_error_code, function, reason, file, line);
+#endif
+}
+
+/*
+ * check_time - Given an indication of the which time(s) to check, check
+ * that time or those times against the current time and return the
+ * relationship.
+ *
+ * Arguments:
+ * chkwhat - What kind of check to do.
+ * cert - The cert to check.
+ *
+ * Returns:
+ * CHKERR_* values.
+ */
+static chk_errs_t
+check_time(chk_actions_t chkwhat, X509 *cert)
+{
+ int i;
+
+ if (chkwhat == CHK_NOT_BEFORE || chkwhat == CHK_BOTH) {
+ i = X509_cmp_time(X509_get_notBefore(cert), NULL);
+ if (i == 0)
+ return (CHKERR_TIME_BEFORE_BAD);
+ if (i > 0)
+ return (CHKERR_TIME_IS_BEFORE);
+
+ /* The current time is after the 'not before' time */
+ }
+
+ if (chkwhat == CHK_NOT_AFTER || chkwhat == CHK_BOTH) {
+ i = X509_cmp_time(X509_get_notAfter(cert), NULL);
+ if (i == 0)
+ return (CHKERR_TIME_AFTER_BAD);
+ if (i < 0)
+ return (CHKERR_TIME_HAS_EXPIRED);
+ }
+
+ return (CHKERR_TIME_OK);
+}
+
+/*
+ * find_attr - Look for a given attribute of the type associated with the NID.
+ *
+ * Arguments:
+ * nid - NID for the attribute to be found (either NID_friendlyName or
+ * NID_locakKeyId)
+ * str - ASN1_STRING-type structure containing the value to be found,
+ * FriendlyName expects a ASN1_BMPSTRING and localKeyID uses a
+ * ASN1_STRING.
+ * kl - Points to a stack of private keys.
+ * pkey - Points at a location where the address of the matching private
+ * key will be stored.
+ * cl - Points to a stack of client certs with matching private keys.
+ * cert - Points to locaiton where the address of the matching client cert
+ * will be returned
+ *
+ * This function is designed to process lists of certs and private keys.
+ * This is made complex because these the attributes are stored differently
+ * for certs and for keys. For certs, only a few attributes are retained.
+ * FriendlyName is stored in the aux structure, under the name 'alias'.
+ * LocalKeyId is also stored in the aux structure, under the name 'keyid'.
+ * A pkey structure has a stack of attributes.
+ *
+ * The basic approach is:
+ * - If there there is no stack of certs but a stack of private keys exists,
+ * search the stack of keys for a match. Alternately, if there is a stack
+ * of certs and no private keys, search the certs.
+ *
+ * - If there are both certs and keys, assume that the matching certs and
+ * keys are in their respective stacks, with matching entries in the same
+ * order. Search for the name or keyid in the stack of certs. If it is
+ * not found, then this function returns 0 (nothing found).
+ *
+ * - Once a cert is found, verify that the key actually matches by
+ * comparing the private key with the public key (in the cert).
+ * If they don't match, return an error.
+ *
+ * A pointer to cert and/or pkey which matches the name or keyid is stored
+ * in the return arguments.
+ *
+ * Returns:
+ * 0 - No matches were found.
+ * > 0 - Bits set based on FOUND_* definitions, indicating what was found.
+ * This can be FOUND_PKEY, FOUND_CERT or (FOUND_PKEY | FOUND_CERT).
+ */
+static int
+find_attr(int nid, ASN1_STRING *str, STACK_OF(EVP_PKEY) *kl, EVP_PKEY **pkey,
+ STACK_OF(X509) *cl, X509 **cert)
+{
+ ASN1_UTF8STRING *ustr = NULL;
+ ASN1_STRING *s;
+ ASN1_TYPE *t;
+ EVP_PKEY *p;
+ uchar_t *fname = NULL;
+ X509 *x;
+ int found = 0;
+ int chkcerts;
+ int len;
+ int res;
+ int c = -1;
+ int k = -1;
+
+ chkcerts = (cert != NULL || pkey != NULL) && cl != NULL;
+ if (chkcerts && nid == NID_friendlyName &&
+ str->type == V_ASN1_BMPSTRING) {
+ ustr = ASN1_UTF8STRING_new();
+ if (ustr == NULL) {
+ SUNWerr(SUNW_F_FINDATTR, SUNW_R_MEMORY_FAILURE);
+ return (0);
+ }
+ len = ASN1_STRING_to_UTF8(&fname, str);
+ if (fname == NULL) {
+ ASN1_UTF8STRING_free(ustr);
+ SUNWerr(SUNW_F_FINDATTR, SUNW_R_STR_CONVERT_ERR);
+ return (0);
+ }
+
+ if (ASN1_STRING_set(ustr, fname, len) == 0) {
+ ASN1_UTF8STRING_free(ustr);
+ OPENSSL_free(fname);
+ SUNWerr(SUNW_F_FINDATTR, SUNW_R_MEMORY_FAILURE);
+ return (0);
+ }
+ }
+
+ if (chkcerts) {
+ for (c = 0; c < sk_X509_num(cl); c++) {
+ res = -1;
+ x = sk_X509_value(cl, c);
+ if (nid == NID_friendlyName && ustr != NULL) {
+ if (x->aux == NULL || x->aux->alias == NULL)
+ continue;
+ s = x->aux->alias;
+ if (s != NULL && s->type == ustr->type &&
+ s->data != NULL) {
+ res = ASN1_STRING_cmp(s, ustr);
+ }
+ } else {
+ if (x->aux == NULL || x->aux->keyid == NULL)
+ continue;
+ s = x->aux->keyid;
+ if (s != NULL && s->type == str->type &&
+ s->data != NULL) {
+ res = ASN1_STRING_cmp(s, str);
+ }
+ }
+ if (res == 0) {
+ if (cert != NULL)
+ *cert = sk_X509_delete(cl, c);
+ found = FOUND_CERT;
+ break;
+ }
+ }
+ if (ustr != NULL) {
+ ASN1_UTF8STRING_free(ustr);
+ OPENSSL_free(fname);
+ }
+ }
+
+ if (pkey != NULL && kl != NULL) {
+ /*
+ * Looking for pkey to match a cert? If so, assume that
+ * lists of certs and their matching pkeys are in the same
+ * order. Call X509_check_private_key() to verify this
+ * assumption.
+ */
+ if (found != 0 && cert != NULL) {
+ k = c;
+ p = sk_EVP_PKEY_value(kl, k);
+ if (X509_check_private_key(x, p) != 0) {
+ if (pkey != NULL)
+ *pkey = sk_EVP_PKEY_delete(kl, k);
+ found |= FOUND_PKEY;
+ }
+ } else if (cert == NULL) {
+ for (k = 0; k < sk_EVP_PKEY_num(kl); k++) {
+ p = sk_EVP_PKEY_value(kl, k);
+ if (p == NULL || p->attributes == NULL)
+ continue;
+
+ t = PKCS12_get_attr_gen(p->attributes, nid);
+ if (t != NULL || ASN1_STRING_cmp(str,
+ t->value.asn1_string) == 0)
+ continue;
+
+ found |= FOUND_PKEY;
+ if (pkey != NULL)
+ *pkey = sk_EVP_PKEY_delete(kl, k);
+ break;
+ }
+ }
+ }
+
+ return (found);
+}
+
+/*
+ * set_results - Given two pointers to stacks of private keys, certs or CA
+ * CA certs, either copy the second stack to the first, or append the
+ * contents of the second to the first.
+ *
+ * Arguments:
+ * pkeys - Points to stack of pkeys
+ * work_kl - Points to working stack of pkeys
+ * certs - Points to stack of certs
+ * work_cl - Points to working stack of certs
+ * cacerts - Points to stack of CA certs
+ * work_ca - Points to working stack of CA certs
+ * xtrakeys - Points to stack of unmatcned pkeys
+ * work_xl - Points to working stack of unmatcned pkeys
+ *
+ * The arguments are in pairs. The first of each pair points to a stack
+ * of keys or certs. The second of the pair points at a 'working stack'
+ * of the same type of entities. Actions taken are as follows:
+ *
+ * - If either the first or second argument is NULL, or if there are no
+ * members in the second stack, there is nothing to do.
+ * - If the first argument points to a pointer which is NULL, then there
+ * is no existing stack for the first argument. Copy the stack pointer
+ * from the second argument to the first argument and NULL out the stack
+ * pointer for the second.
+ * - Otherwise, go through the elements of the second stack, removing each
+ * and adding it to the first stack.
+ *
+ * Returns:
+ * == -1 - An error occurred. Call ERR_get_error() to get error information.
+ * == 0 - No matching returns were found.
+ * > 0 - This is the arithmetic 'or' of the FOUND_* bits that indicate which
+ * of the requested entries were manipulated.
+ */
+static int
+set_results(STACK_OF(EVP_PKEY) **pkeys, STACK_OF(EVP_PKEY) **work_kl,
+ STACK_OF(X509) **certs, STACK_OF(X509) **work_cl,
+ STACK_OF(X509) **cacerts, STACK_OF(X509) **work_ca,
+ STACK_OF(EVP_PKEY) **xtrakeys, STACK_OF(EVP_PKEY) **work_xl)
+{
+ int retval = 0;
+
+ if (pkeys != NULL && work_kl != NULL && *work_kl != NULL &&
+ sk_EVP_PKEY_num(*work_kl) > 0) {
+ if (*pkeys == NULL) {
+ *pkeys = *work_kl;
+ *work_kl = NULL;
+ } else {
+ if (sunw_append_keys(*pkeys, *work_kl) < 0) {
+ return (-1);
+ }
+ }
+ retval |= FOUND_PKEY;
+ }
+ if (certs != NULL && work_cl != NULL && *work_cl != NULL &&
+ sk_X509_num(*work_cl) > 0) {
+ if (*certs == NULL) {
+ *certs = *work_cl;
+ *work_cl = NULL;
+ } else {
+ if (move_certs(*certs, *work_cl) < 0) {
+ return (-1);
+ }
+ }
+ retval |= FOUND_CERT;
+ }
+
+ if (cacerts != NULL && work_ca != NULL && *work_ca != NULL &&
+ sk_X509_num(*work_ca) > 0) {
+ if (*cacerts == NULL) {
+ *cacerts = *work_ca;
+ *work_ca = NULL;
+ } else {
+ if (move_certs(*cacerts, *work_ca) < 0) {
+ return (-1);
+ }
+ }
+ retval |= FOUND_CA_CERTS;
+ }
+
+ if (xtrakeys != NULL && work_xl != NULL && *work_xl != NULL &&
+ sk_EVP_PKEY_num(*work_xl) > 0) {
+ if (*xtrakeys == NULL) {
+ *xtrakeys = *work_xl;
+ *work_xl = NULL;
+ } else {
+ if (sunw_append_keys(*xtrakeys, *work_xl) < 0) {
+ return (-1);
+ }
+ }
+ retval |= FOUND_XPKEY;
+ }
+
+ return (retval);
+}
diff --git a/usr/src/lib/libpkg/common/p12lib.h b/usr/src/lib/libpkg/common/p12lib.h
new file mode 100644
index 0000000000..3d80ddaa35
--- /dev/null
+++ b/usr/src/lib/libpkg/common/p12lib.h
@@ -0,0 +1,245 @@
+/*
+ * ====================================================================
+ * Copyright (c) 1999 The OpenSSL Project. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ * software must display the following acknowledgment:
+ * "This product includes software developed by the OpenSSL Project
+ * for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)"
+ *
+ * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
+ * endorse or promote products derived from this software without
+ * prior written permission. For written permission, please contact
+ * licensing@OpenSSL.org.
+ *
+ * 5. Products derived from this software may not be called "OpenSSL"
+ * nor may "OpenSSL" appear in their names without prior written
+ * permission of the OpenSSL Project.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ * acknowledgment:
+ * "This product includes software developed by the OpenSSL Project
+ * for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OpenSSL PROJECT OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This product includes cryptographic software written by Eric Young
+ * (eay@cryptsoft.com). This product includes software written by Tim
+ * Hudson (tjh@cryptsoft.com).
+ *
+ */
+
+/*
+ * Copyright 2003 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _P12LIB_H
+#define _P12LIB_H
+
+
+#include <openssl/pkcs12.h>
+#include <openssl/pem.h>
+
+/*
+ * PKCS12 file routines borrowed from SNT's libwanboot.
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* These declarations allow us to make stacks of EVP_PKEY objects */
+DECLARE_STACK_OF(EVP_PKEY)
+#define sk_EVP_PKEY_new_null() SKM_sk_new_null(EVP_PKEY)
+#define sk_EVP_PKEY_free(st) SKM_sk_free(EVP_PKEY, (st))
+#define sk_EVP_PKEY_num(st) SKM_sk_num(EVP_PKEY, (st))
+#define sk_EVP_PKEY_value(st, i) SKM_sk_value(EVP_PKEY, (st), (i))
+#define sk_EVP_PKEY_push(st, val) SKM_sk_push(EVP_PKEY, (st), (val))
+#define sk_EVP_PKEY_find(st, val) SKM_sk_find(EVP_PKEY, (st), (val))
+#define sk_EVP_PKEY_delete(st, i) SKM_sk_delete(EVP_PKEY, (st), (i))
+#define sk_EVP_PKEY_delete_ptr(st, ptr) SKM_sk_delete_ptr(EVP_PKEY, (st), (ptr))
+#define sk_EVP_PKEY_insert(st, val, i) SKM_sk_insert(EVP_PKEY, (st), (val), (i))
+#define sk_EVP_PKEY_pop_free(st, free_func) SKM_sk_pop_free(EVP_PKEY, (st), \
+ (free_func))
+#define sk_EVP_PKEY_pop(st) SKM_sk_pop(EVP_PKEY, (st))
+
+/* Error reporting routines required by OpenSSL */
+#define SUNW_LIB_NAME "SUNW_PKCS12"
+#define SUNWerr(f, r) ERR_SUNW_error((f), (r), __FILE__, __LINE__)
+
+/* Error codes for the SUNW functions. */
+/* OpenSSL prefers codes to start at 100 */
+
+/* Function codes. */
+typedef enum {
+ SUNW_F_USE_X509CERT = 100,
+ SUNW_F_USE_PKEY,
+ SUNW_F_USE_TASTORE,
+ SUNW_F_USE_CERTFILE,
+ SUNW_F_USE_KEYFILE,
+ SUNW_F_USE_TRUSTFILE,
+ SUNW_F_READ_FILE,
+ SUNW_F_DOPARSE,
+ SUNW_F_PKCS12_PARSE,
+ SUNW_F_PKCS12_CONTENTS,
+ SUNW_F_PARSE_ONE_BAG,
+ SUNW_F_PKCS12_CREATE,
+ SUNW_F_SPLIT_CERTS,
+ SUNW_F_FIND_LOCALKEYID,
+ SUNW_F_SET_LOCALKEYID,
+ SUNW_F_SET_FNAME,
+ SUNW_F_GET_LOCALKEYID,
+ SUNW_F_GET_PKEY_FNAME,
+ SUNW_F_APPEND_KEYS,
+ SUNW_F_PEM_CONTENTS,
+ SUNW_F_PEM_INFO,
+ SUNW_F_ASC2BMPSTRING,
+ SUNW_F_UTF82ASCSTR,
+ SUNW_F_FINDATTR,
+ SUNW_F_TYPE2ATTRIB,
+ SUNW_F_MOVE_CERTS,
+ SUNW_F_FIND_FNAME,
+ SUNW_F_PARSE_OUTER,
+ SUNW_F_CHECKFILE
+} sunw_err_func_t;
+
+/* Reason codes. */
+typedef enum {
+ SUNW_R_INVALID_ARG = 100,
+ SUNW_R_MEMORY_FAILURE,
+ SUNW_R_MAC_VERIFY_FAILURE,
+ SUNW_R_MAC_CREATE_FAILURE,
+ SUNW_R_BAD_FILETYPE,
+ SUNW_R_BAD_PKEY,
+ SUNW_R_BAD_PKEYTYPE,
+ SUNW_R_PKEY_READ_ERR,
+ SUNW_R_NO_TRUST_ANCHOR,
+ SUNW_R_READ_TRUST_ERR,
+ SUNW_R_ADD_TRUST_ERR,
+ SUNW_R_PKCS12_PARSE_ERR,
+ SUNW_R_PKCS12_CREATE_ERR,
+ SUNW_R_PARSE_BAG_ERR,
+ SUNW_R_MAKE_BAG_ERR,
+ SUNW_R_BAD_CERTTYPE,
+ SUNW_R_PARSE_CERT_ERR,
+ SUNW_R_BAD_LKID,
+ SUNW_R_SET_LKID_ERR,
+ SUNW_R_BAD_FNAME,
+ SUNW_R_SET_FNAME_ERR,
+ SUNW_R_BAD_TRUST,
+ SUNW_R_BAD_BAGTYPE,
+ SUNW_R_CERT_ERR,
+ SUNW_R_PKEY_ERR,
+ SUNW_R_READ_ERR,
+ SUNW_R_ADD_ATTR_ERR,
+ SUNW_R_STR_CONVERT_ERR,
+ SUNW_R_PKCS12_EMPTY_ERR,
+ SUNW_R_PASSWORD_ERR
+} sunw_err_reason_t;
+
+/*
+ * Type of checking to perform when calling sunw_check_cert_times
+ */
+typedef enum {
+ CHK_NOT_BEFORE = 1, /* Check 'not before' date */
+ CHK_NOT_AFTER, /* Check 'not after' date */
+ CHK_BOTH /* Check both dates */
+} chk_actions_t;
+
+/*
+ * Return type for sunw_check_cert_times
+ */
+typedef enum {
+ CHKERR_TIME_OK = 0, /* Current time meets requested checks */
+ CHKERR_TIME_BEFORE_BAD, /* 'not before' field is invalid */
+ CHKERR_TIME_AFTER_BAD, /* 'not after' field is invalid */
+ CHKERR_TIME_IS_BEFORE, /* Current time is before 'not before' */
+ CHKERR_TIME_HAS_EXPIRED /* Current time is after 'not after' */
+} chk_errs_t;
+
+/*
+ * This type indicates what to do with an attribute being returned.
+ */
+typedef enum {
+ GETDO_COPY = 1, /* Simply return the value of the attribute */
+ GETDO_DEL /* Delete the attribute at the same time. */
+} getdo_actions_t;
+
+/*
+ * For sunw_pkcs12_parse, the following are values for bits that indicate
+ * various types of searches/matching to do. Any of these values can be
+ * OR'd together. However, the order in which an attempt will be made
+ * to satisfy them is the order in which they are listed below. The
+ * exception is DO_NONE. It should not be OR'd with any other value.
+ */
+#define DO_NONE 0x00 /* Don't even try to match */
+#define DO_FIND_KEYID 0x01 /* 1st cert, key with matching localkeyid */
+#define DO_FIND_FN 0x02 /* 1st cert, key with matching friendlyname */
+#define DO_FIRST_PAIR 0x04 /* Return first matching cert/key pair found */
+#define DO_LAST_PAIR 0x08 /* Return last matching cert/key pair found */
+#define DO_UNMATCHING 0x10 /* Return first cert and/or key */
+
+/* Bits returned, which indicate what values were found. */
+#define FOUND_PKEY 0x01 /* Found one or more private key */
+#define FOUND_CERT 0x02 /* Found one or more client certificate */
+#define FOUND_CA_CERTS 0x04 /* Added at least one cert to the CA list */
+#define FOUND_XPKEY 0x08 /* Found at least one private key which does */
+ /* not match a certificate in the certs list */
+
+/* p12lib.c */
+PKCS12 *sunw_PKCS12_create(const char *, STACK_OF(EVP_PKEY) *,
+ STACK_OF(X509) *, STACK_OF(X509) *);
+
+int sunw_split_certs(STACK_OF(EVP_PKEY) *, STACK_OF(X509) *,
+ STACK_OF(X509) **, STACK_OF(EVP_PKEY) **);
+
+void sunw_evp_pkey_free(EVP_PKEY *);
+int sunw_set_localkeyid(const char *, int, EVP_PKEY *, X509 *);
+int sunw_get_pkey_localkeyid(getdo_actions_t, EVP_PKEY *, char **, int *);
+int sunw_get_pkey_fname(getdo_actions_t, EVP_PKEY *, char **);
+int sunw_find_localkeyid(char *, int, STACK_OF(EVP_PKEY) *,
+ STACK_OF(X509) *, EVP_PKEY **, X509 **);
+int sunw_find_fname(char *, STACK_OF(EVP_PKEY) *, STACK_OF(X509) *,
+ EVP_PKEY **, X509 **);
+int sunw_set_fname(const char *, EVP_PKEY *, X509 *);
+int sunw_check_keys(X509 *, EVP_PKEY *);
+
+chk_errs_t sunw_check_cert_times(chk_actions_t, X509 *);
+extern void ERR_SUNW_error(int function, int reason, char *file, int line);
+extern void ERR_load_SUNW_strings(void);
+int sunw_PKCS12_contents(PKCS12 *, const char *,
+ STACK_OF(EVP_PKEY) **, STACK_OF(X509) **);
+int sunw_get_cert_fname(getdo_actions_t, X509 *, char **);
+int sunw_PEM_contents(FILE *, pem_password_cb, void *,
+ STACK_OF(EVP_PKEY) **, STACK_OF(X509) **);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _P12LIB_H */
diff --git a/usr/src/lib/libpkg/common/pkgerr.c b/usr/src/lib/libpkg/common/pkgerr.c
new file mode 100644
index 0000000000..eb0254055a
--- /dev/null
+++ b/usr/src/lib/libpkg/common/pkgerr.c
@@ -0,0 +1,135 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+
+/*
+ * Module: pkgerr.c
+ * Description:
+ * Module for handling error messages that come from libpkg libraries.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+#include <locale.h>
+#include <libintl.h>
+#include <stdlib.h>
+#include <sys/varargs.h>
+#include "pkgerr.h"
+
+/* max length of any formatted error message */
+#define MAX_ERRMSGLEN 1024
+
+/* private structures (not visible outside this file) */
+struct _pkg_err_struct {
+ int nerrs;
+ char **msgs;
+ PKG_ERR_CODE *errs;
+};
+
+/* ---------------------- public functions ----------------------- */
+
+PKG_ERR
+*pkgerr_new()
+{
+ PKG_ERR *newerr;
+
+ newerr = (PKG_ERR *)malloc(sizeof (PKG_ERR));
+ newerr->nerrs = 0;
+ newerr->msgs = NULL;
+ newerr->errs = NULL;
+ return (newerr);
+}
+
+void
+pkgerr_add(PKG_ERR *err, PKG_ERR_CODE code, char *fmt, ...)
+{
+ char errmsgbuf[1024];
+ va_list ap;
+
+ va_start(ap, fmt);
+ (void) vsnprintf(errmsgbuf, MAX_ERRMSGLEN, fmt, ap);
+ va_end(ap);
+
+ err->nerrs++;
+
+ err->msgs = (char **)realloc(err->msgs,
+ err->nerrs * sizeof (char *));
+ err->errs = (PKG_ERR_CODE *)realloc(err->errs,
+ err->nerrs * sizeof (PKG_ERR_CODE));
+ err->msgs[err->nerrs - 1] = strdup(errmsgbuf);
+ err->errs[err->nerrs - 1] = code;
+}
+
+void
+pkgerr_clear(PKG_ERR *err)
+{
+ int i;
+
+ for (i = 0; i < err->nerrs; i++) {
+ free(err->msgs[i]);
+ }
+
+ free(err->msgs);
+ free(err->errs);
+ err->msgs = NULL;
+ err->errs = NULL;
+ err->nerrs = 0;
+}
+
+int
+pkgerr_dump(PKG_ERR *err, FILE *fp)
+{
+ int i;
+
+ for (i = 0; i < err->nerrs; i++) {
+ (void) fprintf(fp, err->msgs[i]);
+ }
+ return (0);
+}
+
+int
+pkgerr_num(PKG_ERR *err)
+{
+ return (err->nerrs);
+}
+
+char
+*pkgerr_get(PKG_ERR *err, int pos)
+{
+ if (pos < 0 || pos > (err->nerrs - 1)) {
+ return (NULL);
+ }
+
+ return (err->msgs[pos]);
+}
+
+void
+pkgerr_free(PKG_ERR *err)
+{
+ pkgerr_clear(err);
+ free(err);
+}
diff --git a/usr/src/lib/libpkg/common/pkgerr.h b/usr/src/lib/libpkg/common/pkgerr.h
new file mode 100644
index 0000000000..10e0e219d9
--- /dev/null
+++ b/usr/src/lib/libpkg/common/pkgerr.h
@@ -0,0 +1,104 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2003 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _PKGERR_H
+#define _PKGERR_H
+
+
+/*
+ * Module: pkgerr.h
+ * Description:
+ *
+ * Implements error routines to handle the creation,
+ * management, and destruction of error objects, which
+ * hold error messages and codes returned from libpkg
+ * routines that support the objects defined herein.
+ */
+
+#include <stdio.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * Public Definitions
+ */
+
+typedef enum {
+ PKGERR_OK = 0,
+ PKGERR_EXIST,
+ PKGERR_READ,
+ PKGERR_CORRUPT,
+ PKGERR_PARSE,
+ PKGERR_BADPASS,
+ PKGERR_BADALIAS,
+ PKGERR_INTERNAL,
+ PKGERR_UNSUP,
+ PKGERR_NOALIAS,
+ PKGERR_NOALIASMATCH,
+ PKGERR_MULTIPLE,
+ PKGERR_INCOMPLETE,
+ PKGERR_NOPRIVKEY,
+ PKGERR_NOPUBKEY,
+ PKGERR_NOCACERT,
+ PKGERR_NOMEM,
+ PKGERR_CHAIN,
+ PKGERR_LOCKED,
+ PKGERR_WRITE,
+ PKGERR_UNLOCK,
+ PKGERR_TIME,
+ PKGERR_DUPLICATE,
+ PKGERR_WEB,
+ PKGERR_VERIFY
+} PKG_ERR_CODE;
+
+/*
+ * Public Structures
+ */
+
+/* external reference to PKG_ERR object (contents private) */
+typedef PKG_ERR_CODE pkg_err_t;
+
+typedef struct _pkg_err_struct PKG_ERR;
+
+/*
+ * Public Methods
+ */
+
+PKG_ERR *pkgerr_new();
+void pkgerr_add(PKG_ERR *, PKG_ERR_CODE, char *, ...);
+void pkgerr_clear(PKG_ERR *);
+int pkgerr_dump(PKG_ERR *, FILE *);
+int pkgerr_num(PKG_ERR *);
+char *pkgerr_get(PKG_ERR *, int);
+void pkgerr_free(PKG_ERR *);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _PKGERR_H */
diff --git a/usr/src/lib/libpkg/common/pkgexecl.c b/usr/src/lib/libpkg/common/pkgexecl.c
new file mode 100644
index 0000000000..e8eda96059
--- /dev/null
+++ b/usr/src/lib/libpkg/common/pkgexecl.c
@@ -0,0 +1,80 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
+/* All Rights Reserved */
+
+
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <stdarg.h>
+#include <wait.h>
+#include <signal.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <pwd.h>
+#include <grp.h>
+#include <fcntl.h>
+#include "pkglocale.h"
+#include "pkglibmsgs.h"
+#include "pkglib.h"
+
+#define MAXARGS 64
+
+/*VARARGS4*/
+int
+pkgexecl(char *filein, char *fileout, char *uname, char *gname, ...)
+{
+ char *arg[MAXARGS+1];
+ char *pt;
+ int n;
+ va_list ap;
+
+ /* construct arg[] array from varargs passed in */
+
+ va_start(ap, gname);
+
+ n = 0;
+ while ((pt = va_arg(ap, char *)) != NULL) {
+ if (n >= MAXARGS) {
+ va_end(ap);
+ progerr(pkg_gt(ERR_TOO_MANY_ARGS),
+ arg[0] ? arg[0] : "??");
+ return (-1);
+ }
+ arg[n++] = pt;
+ }
+
+ arg[n] = NULL;
+ va_end(ap);
+
+ /* return results of executing command based on arg[] list */
+
+ return (pkgexecv(filein, fileout, uname, gname, arg));
+}
diff --git a/usr/src/lib/libpkg/common/pkgexecv.c b/usr/src/lib/libpkg/common/pkgexecv.c
new file mode 100644
index 0000000000..28a7a07250
--- /dev/null
+++ b/usr/src/lib/libpkg/common/pkgexecv.c
@@ -0,0 +1,445 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
+/* All Rights Reserved */
+
+
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <signal.h>
+#include <wait.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <pwd.h>
+#include <grp.h>
+#include "pkglib.h"
+#include "pkglibmsgs.h"
+#include "pkglocale.h"
+
+/* global environment inherited by this process */
+extern char **environ;
+
+/* dstream.c */
+extern int ds_curpartcnt;
+extern int ds_close(int pkgendflg);
+
+/*
+ * global internal (private) variables
+ */
+
+/* received signal count - bumped with hooked signals are caught */
+
+static int sig_received = 0;
+
+/*
+ * Name: sig_trap
+ * Description: hooked up to signal counts number of signals received
+ * Arguments: a_signo - [RO, *RO] - (int)
+ * Integer representing the signal received; see signal(3c)
+ * Returns: <void>
+ */
+
+static void
+sig_trap(int a_signo)
+{
+ sig_received++;
+}
+
+/*
+ * Name: pkgexecv
+ * Description: Asynchronously execute a package command in a separate process
+ * and return results - the subprocess MUST arm it's own SIGINT
+ * and SIGHUP signals and must return a standard package command
+ * exit code (see returns below)
+ * Only another package command (such as pkginstall, pkgremove,
+ * etc.) may be called via this interface. No files are closed
+ * because open files are passed across to certain commands using
+ * either implicit agreements between the two (yuk!) or by using
+ * the '-p' option which passes a string of digits, some of which
+ * represent open file descriptors passed through this interface!
+ * Arguments: filein - [RO, *RO] - (char *)
+ * Pointer to string representing the name of the file to
+ * use for the package commands's stdin
+ * == (char *)NULL or == "" - the current stdin
+ * is used for the new package command process
+ * fileout - [RO, *RO] - (char *)
+ * Pointer to string representing the name of the file to
+ * use for the package commands's stdout and stderr
+ * == (char *)NULL or == "" - the current stdout/stderr
+ * is used for the new package command process
+ * uname - [RO, *RO] - (char *)
+ * Pointer to string representing the user name to execute
+ * the package command as - the user name is looked up
+ * using the ncgrpw:cpwnam() interface
+ * == (char *)NULL or == "" - the user name of the current
+ * process is used for the new package command process
+ * gname - [RO, *RO] - (char *)
+ * Pointer to string representing the group name to execute
+ * the package command as - the group name is looked up
+ * using the ncgrpw:cgrnam() interface
+ * == (char *)NULL or == "" - the group name of the current
+ * process is used for the new package command process
+ * arg - [RO, *RO] - (char **)
+ * Pointer to array of character pointers representing the
+ * arguments to pass to the package command - the array is
+ * terminated with a pointer to (char *)NULL
+ * Returns: int
+ * == 99 - exec() of package command failed
+ * == -1 - fork failed or other fatal error during
+ * execution of the package command
+ * otherwise - exit code from package command:
+ * 0 - successful
+ * 1 - package operation failed (fatal error)
+ * 2 - non-fatal error (warning)
+ * 3 - operation interrupted (including SIGINT/SIGHUP)
+ * 4 - admin settings prevented operation
+ * 5 - administration required and -n was specified
+ * IN addition:
+ * 10 is added to the return code if reboot after the
+ * installation of all packages is required
+ * 20 is added to the return code if immediate reboot
+ * after installation of this package is required
+ */
+
+int
+pkgexecv(char *filein, char *fileout, char *uname, char *gname, char *arg[])
+{
+ int exit_no;
+ int n;
+ int status;
+ pid_t pid;
+ pid_t waitstat;
+ struct group *grp;
+ struct passwd *pwp;
+ struct sigaction nact;
+ struct sigaction oact;
+ void (*funcSighup)();
+ void (*funcSigint)();
+
+ /* flush standard i/o before creating new process */
+
+ (void) fflush(stdout);
+ (void) fflush(stderr);
+
+ /*
+ * hold SIGINT/SIGHUP signals and reset signal received counter;
+ * after the vfork() the parent and child need to setup their respective
+ * interrupt handling and release the hold on the signals
+ */
+
+ (void) sighold(SIGINT);
+ (void) sighold(SIGHUP);
+
+ sig_received = 0;
+
+ /*
+ * create new process to execute command in;
+ * vfork() is being used to avoid duplicating the parents
+ * memory space - this means that the child process may
+ * not modify any of the parents memory including the
+ * standard i/o descriptors - all the child can do is
+ * adjust interrupts and open files as a prelude to a
+ * call to exec().
+ */
+
+ pid = vfork();
+
+ if (pid < 0) {
+ /*
+ * *************************************************************
+ * fork failed!
+ * *************************************************************
+ */
+
+ progerr(pkg_gt(ERR_FORK_FAILED), errno, strerror(errno));
+
+ /* release hold on signals */
+
+ (void) sigrelse(SIGHUP);
+ (void) sigrelse(SIGINT);
+
+ return (-1);
+ }
+
+ if (pid > 0) {
+ /*
+ * *************************************************************
+ * This is the forking (parent) process
+ * *************************************************************
+ */
+
+ /* close datastream if any portion read */
+
+ if (ds_curpartcnt >= 0) {
+ if (ds_close(0) != 0) {
+ /* kill child process */
+
+ (void) sigsend(P_PID, pid, SIGKILL);
+
+ /* release hold on signals */
+
+ (void) sigrelse(SIGHUP);
+ (void) sigrelse(SIGINT);
+
+ return (-1);
+ }
+ }
+
+ /*
+ * setup signal handlers for SIGINT and SIGHUP and release hold
+ */
+
+ /* hook SIGINT to sig_trap() */
+
+ nact.sa_handler = sig_trap;
+ nact.sa_flags = SA_RESTART;
+ (void) sigemptyset(&nact.sa_mask);
+
+ if (sigaction(SIGINT, &nact, &oact) < 0) {
+ funcSigint = SIG_DFL;
+ } else {
+ funcSigint = oact.sa_handler;
+ }
+
+ /* hook SIGHUP to sig_trap() */
+
+ nact.sa_handler = sig_trap;
+ nact.sa_flags = SA_RESTART;
+ (void) sigemptyset(&nact.sa_mask);
+
+ if (sigaction(SIGHUP, &nact, &oact) < 0) {
+ funcSighup = SIG_DFL;
+ } else {
+ funcSighup = oact.sa_handler;
+ }
+
+ /* release hold on signals */
+
+ (void) sigrelse(SIGHUP);
+ (void) sigrelse(SIGINT);
+
+ /*
+ * wait for the process to exit, reap child exit status
+ */
+
+ for (;;) {
+ status = 0;
+ waitstat = waitpid(pid, (int *)&status, 0);
+ if (waitstat < 0) {
+ /* waitpid returned error */
+ if (errno == EAGAIN) {
+ /* try again */
+ continue;
+ }
+ if (errno == EINTR) {
+ continue;
+ }
+ /* error from waitpid: bail */
+ break;
+ } else if (waitstat == pid) {
+ /* child exit status available */
+ break;
+ }
+ }
+
+ /*
+ * reset signal handlers
+ */
+
+ /* reset SIGINT */
+
+ nact.sa_handler = funcSigint;
+ nact.sa_flags = SA_RESTART;
+ (void) sigemptyset(&nact.sa_mask);
+
+ (void) sigaction(SIGINT, &nact, (struct sigaction *)NULL);
+
+ /* reset SIGHUP */
+
+ nact.sa_handler = funcSighup;
+ nact.sa_flags = SA_RESTART;
+ (void) sigemptyset(&nact.sa_mask);
+
+ (void) sigaction(SIGHUP, &nact, (struct sigaction *)NULL);
+
+ /* error if child process does not match */
+
+ if (waitstat != pid) {
+ progerr(pkg_gt(ERR_WAIT_FAILED), pid, waitstat, status,
+ errno, strerror(errno));
+ return (-1);
+ }
+
+ /*
+ * determine final exit code:
+ * - if signal received, then return interrupted (3)
+ * - if child exit status is available, return exit child status
+ * - otherwise return error (-1)
+ */
+
+ if (sig_received != 0) {
+ exit_no = 3; /* interrupted */
+ } else if (WIFEXITED(status)) {
+ exit_no = WEXITSTATUS(status);
+ } else {
+ exit_no = -1; /* exec() or other process error */
+ }
+
+ return (exit_no);
+ }
+
+ /*
+ * *********************************************************************
+ * This is the forked (child) process
+ * *********************************************************************
+ */
+
+ /* reset all signals to default */
+
+ for (n = 0; n < NSIG; n++) {
+ (void) sigset(n, SIG_DFL);
+ }
+
+ /* release hold on signals held by parent before fork() */
+
+ (void) sigrelse(SIGHUP);
+ (void) sigrelse(SIGINT);
+
+ /*
+ * The caller wants to have stdin connected to filein.
+ */
+
+ if (filein && *filein) {
+ /*
+ * If input is supposed to be connected to /dev/tty
+ */
+ if (strncmp(filein, "/dev/tty", 8) == 0) {
+ /*
+ * If stdin is connected to a tty device.
+ */
+ if (isatty(STDIN_FILENO)) {
+ /*
+ * Reopen it to /dev/tty.
+ */
+ n = open(filein, O_RDONLY);
+ if (n >= 0) {
+ (void) dup2(n, STDIN_FILENO);
+ }
+ }
+ } else {
+ /*
+ * If we did not want to be connected to /dev/tty, we
+ * connect input to the requested file no questions.
+ */
+ n = open(filein, O_RDONLY);
+ if (n >= 0) {
+ (void) dup2(n, STDIN_FILENO);
+ }
+ }
+ }
+
+ /*
+ * The caller wants to have stdout and stderr connected to fileout.
+ * If "fileout" is "/dev/tty" then reconnect stdout to "/dev/tty"
+ * only if /dev/tty is not already associated with "a tty".
+ */
+
+ if (fileout && *fileout) {
+ /*
+ * If output is supposed to be connected to /dev/tty
+ */
+ if (strncmp(fileout, "/dev/tty", 8) == 0) {
+ /*
+ * If stdout is connected to a tty device.
+ */
+ if (isatty(STDOUT_FILENO)) {
+ /*
+ * Reopen it to /dev/tty if /dev/tty available.
+ */
+ n = open(fileout, O_WRONLY);
+ if (n >= 0) {
+ /*
+ * /dev/tty is available - close the
+ * current standard output stream, and
+ * reopen it on /dev/tty
+ */
+ (void) dup2(n, STDOUT_FILENO);
+ }
+ }
+ /*
+ * not connected to tty device - probably redirect to
+ * file - preserve existing output device
+ */
+ } else {
+ /*
+ * If we did not want to be connected to /dev/tty, we
+ * connect output to the requested file no questions.
+ */
+ /* LINTED O_CREAT without O_EXCL specified in call to */
+ n = open(fileout, O_WRONLY|O_CREAT|O_APPEND, 0666);
+ if (n >= 0) {
+ (void) dup2(n, STDOUT_FILENO);
+ }
+ }
+
+ /*
+ * Dup stderr from stdout.
+ */
+
+ (void) dup2(STDOUT_FILENO, STDERR_FILENO);
+ }
+
+ /*
+ * do NOT close all file descriptors except stdio
+ * file descriptors are passed in to some subcommands
+ * (see dstream:ds_getinfo() and dstream:ds_putinfo())
+ */
+
+ /* set group/user i.d. if requested */
+
+ if (gname && *gname && (grp = cgrnam(gname)) != NULL) {
+ if (setgid(grp->gr_gid) == -1) {
+ progerr(pkg_gt(ERR_SETGID), grp->gr_gid);
+ }
+ }
+ if (uname && *uname && (pwp = cpwnam(uname)) != NULL) {
+ if (setuid(pwp->pw_uid) == -1) {
+ progerr(pkg_gt(ERR_SETUID), pwp->pw_uid);
+ }
+ }
+
+ /* execute target executable */
+
+ (void) execve(arg[0], arg, environ);
+ progerr(pkg_gt(ERR_EX_FAIL), arg[0], errno);
+ _exit(99);
+ /*NOTREACHED*/
+}
diff --git a/usr/src/lib/libpkg/common/pkglib.h b/usr/src/lib/libpkg/common/pkglib.h
new file mode 100644
index 0000000000..418cf632f7
--- /dev/null
+++ b/usr/src/lib/libpkg/common/pkglib.h
@@ -0,0 +1,622 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
+/* All Rights Reserved */
+
+#ifndef _PKGLIB_H
+#define _PKGLIB_H
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <sys/types.h>
+#include <limits.h>
+#include <stdio.h>
+#include <pkgdev.h>
+#include <pkgstrct.h>
+#include <openssl/bio.h>
+#include <openssl/x509.h>
+#include <netdb.h>
+#include <boot_http.h>
+#include "pkgerr.h"
+#include "keystore.h"
+#include "cfext.h"
+
+/*
+ * Virtual File Protocol definitions
+ */
+
+/*
+ * flags associated with virtual file protocol operations; note that these flags
+ * may only occupy the low order 16 bits of the 32-bit unsigned flag.
+ */
+
+typedef unsigned long VFPFLAGS_T;
+
+#define VFP_NONE 0x00000000 /* no special flags */
+#define VFP_NEEDNOW 0x00000001 /* need memory now */
+#define VFP_SEQUENTIAL 0x00000002 /* sequential access */
+#define VFP_RANDOM 0x00000004 /* random access */
+#define VFP_NOMMAP 0x00000008 /* do not use mmap to access file */
+#define VFP_NOMALLOC 0x00000010 /* do not use malloc to buffer file */
+
+/* virtual file protocol object */
+
+typedef struct _vfp VFP_T;
+
+/* structure behind the virtual file protocol object */
+
+struct _vfp {
+ FILE *_vfpFile; /* -> opened FILE */
+ char *_vfpCurr; /* -> current byte to read/write */
+ char *_vfpHighWater; /* -> last byte modified */
+ char *_vfpEnd; /* -> last data byte */
+ char *_vfpPath; /* -> path associated with FILE */
+ char *_vfpStart; /* -> first data byte */
+ void *_vfpExtra; /* undefined */
+ size_t _vfpSize; /* size of mapped/allocated area */
+ size_t _vfpMapSize; /* # mapped bytes */
+ VFPFLAGS_T _vfpFlags; /* flags associated with vfp/data */
+ int _vfpOverflow; /* non-zero if buffer write overflow */
+ blkcnt_t _vfpCkStBlocks; /* checkpoint # blocks */
+ dev_t _vfpCkDev; /* checkpoint device i.d. */
+ ino_t _vfpCkIno; /* checkpoint inode # */
+ off_t _vfpCkSize; /* checkpoint size */
+ time_t _vfpCkMtime; /* checkpoint modification time */
+};
+
+/*
+ * get highest modified byte (length) contained in vfp
+ *
+ * determine number of bytes to write - it will be the highest of:
+ * -- the current pointer into the file - this is updated whenever
+ * the location of the file is changed by a single byte
+ * -- the last "high water mark" - the last known location that
+ * was written to the file - updated only when the location
+ * of the file is directly changed - e.g. vfpSetCurrCharPtr,
+ * vfpTruncate, vfpRewind.
+ * this reduces the "bookkeeping" that needs to be done to know
+ * how many bytes to write out to the file - typically a file is
+ * written sequentially so the current file pointer is sufficient
+ * to determine how many bytes to write out.
+ */
+
+#define vfpGetModifiedLen(VFP) \
+ (size_t)(((VFP)->_vfpHighWater > (VFP)->_vfpCurr) ? \
+ (((ptrdiff_t)(VFP)->_vfpHighWater - \
+ (ptrdiff_t)(VFP)->_vfpStart)) : \
+ (((ptrdiff_t)(VFP)->_vfpCurr - \
+ (ptrdiff_t)(VFP)->_vfpStart)))
+
+/*
+ * increment current pointer by specified delta
+ * if the delta exceeds the buffer size, set pointer to buffer end
+ */
+#define vfpIncCurrPtrBy(VFP, INC) \
+ { \
+ ((VFP)->_vfpCurr) += (INC); \
+ if (((VFP)->_vfpCurr) > ((VFP)->_vfpEnd)) { \
+ (VFP)->_vfpCurr = (VFP)->_vfpEnd; \
+ (VFP)->_vfpOverflow = 1; \
+ } \
+ if ((VFP)->_vfpHighWater < (VFP)->_vfpCurr) { \
+ (VFP)->_vfpHighWater = (VFP)->_vfpCurr; \
+ } \
+ }
+
+/* get the path associated with the vfp */
+#define vfpGetPath(VFP) ((VFP)->_vfpPath)
+
+/* get a string from the vfp into a fixed size buffer */
+#define vfpGets(VFP, PTR, LEN) \
+ { \
+ char *XXpXX = (PTR); \
+ size_t XXlXX = (LEN); \
+ while ((*(VFP)->_vfpCurr != '\0') && \
+ (*(VFP)->_vfpCurr != '\n')) { \
+ if (XXlXX > 1) { \
+ *XXpXX++ = *(VFP)->_vfpCurr; \
+ XXlXX--; \
+ } \
+ (VFP)->_vfpCurr++; \
+ } \
+ *XXpXX++ = '\0'; \
+ if (*(VFP)->_vfpCurr != '\0') { \
+ (VFP)->_vfpCurr++; \
+ } \
+ }
+
+/* get number of bytes remaining to read */
+#define vfpGetBytesRemaining(VFP) \
+ (((((VFP)->_vfpHighWater) <= ((VFP)->_vfpCurr))) ? 0 : \
+ ((((ptrdiff_t)(VFP)->_vfpHighWater)-((ptrdiff_t)(VFP)->_vfpCurr))))
+
+/* get number of bytes remaining to write */
+#define vfpGetBytesAvailable(VFP) \
+ (((((VFP)->_vfpEnd) <= ((VFP)->_vfpCurr))) ? 0 : \
+ ((((ptrdiff_t)(VFP)->_vfpEnd)-((ptrdiff_t)(VFP)->_vfpCurr))))
+
+/* put current character and increment to next */
+#define vfpPutc(VFP, C) \
+ { \
+ (*(VFP)->_vfpCurr) = ((char)(C)); \
+ vfpIncCurrPtrBy((VFP), 1); \
+ }
+
+/* put integer to current character and increment */
+#define vfpPutInteger(VFP, NUMBER) vfpPutFormat((VFP), "%d", (NUMBER))
+
+/* put long to current character and increment */
+#define vfpPutLong(VFP, NUMBER) vfpPutFormat((VFP), "%ld", (NUMBER))
+
+/* get current character and increment to next */
+#define vfpGetc(VFP) (*(VFP)->_vfpCurr++)
+
+/* get current character - do not increment */
+#define vfpGetcNoInc(VFP) (*(VFP)->_vfpCurr)
+
+/* get pointer to current character */
+#define vfpGetCurrCharPtr(VFP) ((VFP)->_vfpCurr)
+
+/* increment current character pointer */
+#define vfpIncCurrPtr(VFP) vfpIncCurrPtrBy((VFP), 1)
+
+/* decrement current character pointer */
+#define vfpDecCurrPtr(VFP) ((VFP)->_vfpCurr--)
+
+/* get pointer to first data byte in buffer */
+#define vfpGetFirstCharPtr(VFP) ((VFP)->_vfpStart)
+
+/* get pointer to last data byte in buffer */
+#define vfpGetLastCharPtr(VFP) ((VFP)->_vfpHighWater)
+
+/* set pointer to current character */
+#define vfpSetCurrCharPtr(VFP, PTR) \
+ if ((VFP)->_vfpCurr > (VFP)->_vfpHighWater) { \
+ (VFP)->_vfpHighWater = (VFP)->_vfpCurr; \
+ } \
+ ((VFP)->_vfpCurr = (PTR))
+
+/* set pointer to last data byte in buffer */
+#define vfpSetLastCharPtr(VFP, PTR) \
+ if ((PTR) >= (VFP)->_vfpStart) { \
+ (VFP)->_vfpHighWater = (PTR); \
+ if ((VFP)->_vfpCurr > (VFP)->_vfpHighWater) { \
+ (VFP)->_vfpCurr = (VFP)->_vfpHighWater; \
+ } \
+ }
+
+/* seek to end of file - one past last data byte in file */
+#define vfpSeekToEnd(VFP) ((VFP)->_vfpCurr = ((VFP)->_vfpHighWater)+1)
+
+/* get number of bytes between current char and specified char */
+#define vfpGetCurrPtrDelta(VFP, P) \
+ (((ptrdiff_t)(P))-((ptrdiff_t)(VFP)->_vfpCurr))
+
+/* put string to current character and increment */
+#define vfpPuts(VFP, S) \
+ { \
+ size_t xxLen; \
+ size_t xxResult; \
+ xxLen = vfpGetBytesAvailable((VFP)); \
+ xxResult = strlcpy(((VFP)->_vfpCurr), (S), xxLen); \
+ vfpIncCurrPtrBy((VFP), xxResult); \
+ }
+
+/* put fixed number of bytes to current character and increment */
+#define vfpPutBytes(VFP, PTR, LEN) \
+ { \
+ size_t xxLen; \
+ xxLen = vfpGetBytesAvailable((VFP)); \
+ if (xxLen > (LEN)) { \
+ xxLen = (LEN); \
+ } else { \
+ (VFP)->_vfpOverflow = 1; \
+ } \
+ memcpy((VFP)->_vfpCurr, (PTR), (xxLen)); \
+ vfpIncCurrPtrBy((VFP), (xxLen)); \
+ }
+
+/* put format one arg to current character and increment */
+#define vfpPutFormat(VFP, FORMAT, ARG) \
+ { \
+ char xxTeMpXX[256]; \
+ (void) snprintf(xxTeMpXX, sizeof (xxTeMpXX), (FORMAT), (ARG)); \
+ vfpPuts((VFP), xxTeMpXX); \
+ }
+
+struct dm_buf {
+ char *text_buffer; /* start of allocated buffer */
+ int offset; /* number of bytes into the text_buffer */
+ int allocation; /* size of buffer in bytes */
+};
+
+/* This structure is used to hold a dynamically growing string */
+
+struct dstr {
+ char *pc;
+ int len;
+ int max;
+};
+
+/* setmapmode() defines */
+#define MAPALL 0 /* resolve all variables */
+#define MAPBUILD 1 /* map only build variables */
+#define MAPINSTALL 2 /* map only install variables */
+#define MAPNONE 3 /* map no variables */
+
+#define NON_ABI_NAMELNGTH 33 /* 32 chars for name + 1 for NULL */
+
+#define BLK_SIZE 512 /* size of logical block */
+
+/* max length for printed attributes */
+#define ATTR_MAX 80
+
+/*
+ * These three defines indicate that the prototype file contains a '?'
+ * meaning do not specify this data in the pkgmap entry.
+ */
+#define CURMODE BADMODE /* current mode has been specified */
+#define CUROWNER BADOWNER /* ... same for owner ... */
+#define CURGROUP BADGROUP /* ... and group. */
+
+#define WILDCARD BADMODE >> 1
+#define DB_UNDEFINED_ENTRY "?"
+
+#define DEFAULT_MODE 0755
+#define DEFAULT_MODE_FILE 0644
+#define DEFAULT_OWNER "root"
+#define DEFAULT_GROUP "other"
+
+#define INST_RELEASE "var/sadm/system/admin/INST_RELEASE"
+
+#define RANDOM "/dev/urandom"
+#define BLOCK 256
+
+#define TERM_WIDTH 60
+#define SMALL_DIVISOR 4
+#define MED_DIVISOR 5
+#define LARGE_DIVISOR 10
+#define MED_DWNLD (10 * 1024 * 1024) /* 10 MB */
+#define LARGE_DWNLD (5 * MED_DWNLD) /* 50 MB */
+
+#define HTTP "http://"
+#define HTTPS "https://"
+
+#define PKGADD "pkgadd"
+
+/* Settings for network admin defaults */
+
+#define NET_TIMEOUT_DEFAULT 60
+#define NET_RETRIES_DEFAULT 3
+#define NET_TIMEOUT_MIN 1 /* 1 second */
+#define NET_TIMEOUT_MAX (5 * 60) /* 5 minutes */
+#define NET_RETRIES_MIN 1
+#define NET_RETRIES_MAX 10
+#define AUTH_NOCHECK 0
+#define AUTH_QUIT 1
+
+/* package header magic tokens */
+#define HDR_PREFIX "# PaCkAgE DaTaStReAm"
+#define HDR_SUFFIX "# end of header"
+
+/* name of security files */
+#define PKGSEC "/var/sadm/security"
+#define SIGNATURE_FILENAME "signature"
+
+#define GROUP "/etc/group"
+#define PASSWD "/etc/passwd"
+
+/*
+ * The next three mean that no mode, owner or group was specified or that the
+ * one specified is invalid for some reason. Sometimes this is an error in
+ * which case it is generally converted to CUR* with a warning. Other times
+ * it means "look it up" by stating the existing file system object pointred
+ * to in the prototype file.
+ */
+#define NOMODE (BADMODE-1)
+#define NOOWNER "@"
+#define NOGROUP "@"
+
+/* string comparitor abbreviators */
+
+#define ci_streq(a, b) (strcasecmp((a), (b)) == 0)
+#define ci_strneq(a, b, c) (strncasecmp((a), (b), (c)) == 0)
+#define streq(a, b) (strcmp((a), (b)) == 0)
+#define strneq(a, b, c) (strncmp((a), (b), (c)) == 0)
+
+#ifdef __STDC__
+
+extern FILE *epopen(char *cmd, char *mode);
+extern char **gpkglist(char *dir, char **pkg, char **catg);
+extern int is_not_valid_length(char **category);
+extern int is_not_valid_category(char **category, char *progname);
+extern int is_same_CATEGORY(char **category, char *installed_category);
+extern char **get_categories(char *catg_arg);
+
+extern void pkglist_cont(char *keyword);
+extern char **pkgalias(char *pkg);
+extern char *get_prog_name(void);
+extern char *set_prog_name(char *name);
+extern int averify(int fix, char *ftype, char *path, struct ainfo *ainfo);
+extern int ckparam(char *param, char *value);
+extern int ckvolseq(char *dir, int part, int nparts);
+extern int cverify(int fix, char *ftype, char *path, struct cinfo *cinfo,
+ int allow_checksum);
+extern unsigned long compute_checksum(int *r_cksumerr, char *a_path);
+extern int fverify(int fix, char *ftype, char *path, struct ainfo *ainfo,
+ struct cinfo *cinfo);
+extern char *getErrbufAddr(void);
+extern int getErrbufSize(void);
+extern char *getErrstr(void);
+extern void setErrstr(char *errstr);
+extern int devtype(char *alias, struct pkgdev *devp);
+extern int ds_totread; /* total number of parts read */
+extern int ds_close(int pkgendflg);
+extern int ds_findpkg(char *device, char *pkg);
+extern int ds_getinfo(char *string);
+extern int ds_getpkg(char *device, int n, char *dstdir);
+extern int ds_ginit(char *device);
+extern boolean_t ds_fd_open(void);
+extern int ds_init(char *device, char **pkg, char *norewind);
+extern int BIO_ds_dump_header(PKG_ERR *, BIO *);
+extern int BIO_ds_dump(PKG_ERR *, char *, BIO *);
+extern int BIO_dump_cmd(char *cmd, BIO *bio);
+extern int ds_next(char *, char *);
+extern int ds_readbuf(char *device);
+extern int epclose(FILE *pp);
+extern int esystem(char *cmd, int ifd, int ofd);
+extern int e_ExecCmdArray(int *r_status, char **r_results,
+ char *a_inputFile, char *a_cmd, char **a_args);
+extern int e_ExecCmdList(int *r_status, char **r_results,
+ char *a_inputFile, char *a_cmd, ...);
+extern int gpkgmap(struct cfent *ept, FILE *fp);
+extern int gpkgmapvfp(struct cfent *ept, VFP_T *fpv);
+extern void setmapmode(int mode_no);
+extern int isFdRemote(int a_fd);
+extern int isFstypeRemote(char *a_fstype);
+extern int isPathRemote(char *a_path);
+extern int iscpio(char *path, int *iscomp);
+extern int isdir(char *path);
+extern int isfile(char *dir, char *file);
+extern int fmkdir(char *a_path, int a_mode);
+extern int pkgexecl(char *filein, char *fileout, char *uname, char *gname,
+ ...);
+extern int pkgexecv(char *filein, char *fileout, char *uname, char *gname,
+ char *arg[]);
+extern int pkghead(char *device);
+extern int pkgmount(struct pkgdev *devp, char *pkg, int part, int nparts,
+ int getvolflg);
+extern int pkgtrans(char *device1, char *device2, char **pkg,
+ int options, keystore_handle_t, char *);
+extern int pkgumount(struct pkgdev *devp);
+extern int ppkgmap(struct cfent *ept, FILE *fp);
+extern int putcfile(struct cfent *ept, FILE *fp);
+extern int putcvfpfile(struct cfent *ept, VFP_T *vfp);
+extern int rrmdir(char *path);
+extern void set_memalloc_failure_func(void (*)(int));
+extern void *xmalloc(size_t size);
+extern void *xrealloc(void *ptr, size_t size);
+extern char *xstrdup(char *str);
+extern void set_passphrase_prompt(char *);
+extern void set_passphrase_passarg(char *);
+extern int pkg_passphrase_cb(char *, int, int, void *);
+
+extern int srchcfile(struct cfent *ept, char *path, VFP_T *vfp,
+ VFP_T *vfpout);
+extern struct group *cgrgid(gid_t gid);
+extern struct group *cgrnam(char *nam);
+extern struct passwd *cpwnam(char *nam);
+extern struct passwd *cpwuid(uid_t uid);
+extern struct group *clgrgid(gid_t gid);
+extern struct group *clgrnam(char *nam);
+extern struct passwd *clpwnam(char *nam);
+extern struct passwd *clpwuid(uid_t uid);
+extern void basepath(char *path, char *basedir, char *ir);
+extern void canonize(char *file);
+extern void canonize_slashes(char *file);
+extern void checksum_off(void);
+extern void checksum_on(void);
+extern void cvtpath(char *path, char *copy);
+extern void ds_order(char *list[]);
+extern void ds_putinfo(char *buf);
+extern void ds_skiptoend(char *device);
+extern void ecleanup(void);
+/*PRINTFLIKE1*/
+extern void logerr(char *fmt, ...);
+extern int mappath(int flag, char *path);
+extern int mapvar(int flag, char *varname);
+/*PRINTFLIKE1*/
+extern void progerr(char *fmt, ...);
+extern void pkgerr(PKG_ERR *);
+extern void rpterr(void);
+extern void tputcfent(struct cfent *ept, FILE *fp);
+extern void set_nonABI_symlinks(void);
+extern int nonABI_symlinks(void);
+extern void disable_attribute_check(void);
+extern int get_disable_attribute_check(void);
+
+/* security.c */
+extern void sec_init(void);
+extern char *get_subject_display_name(X509 *);
+extern char *get_issuer_display_name(X509 *);
+extern char *get_serial_num(X509 *);
+extern char *get_fingerprint(X509 *, const EVP_MD *);
+extern int get_cert_chain(PKG_ERR *, X509 *, STACK_OF(X509) *,
+ STACK_OF(X509) *, STACK_OF(X509) **);
+
+/* pkgstr.c */
+void pkgstrConvertUllToTimeString_r(unsigned long long a_time,
+ char *a_buf, int a_bufLen);
+char *pkgstrConvertPathToBasename(char *a_path);
+char *pkgstrConvertPathToDirname(char *a_path);
+char *pkgstrDup(char *a_str);
+char *pkgstrLocatePathBasename(char *a_path);
+void pkgstrScaleNumericString(char *a_buf, unsigned long long scale);
+void pkgstrAddToken(char **a_old, char *a_new, char a_separator);
+boolean_t pkgstrContainsToken(char *a_string, char *a_token,
+ char *a_separators);
+void pkgstrExpandTokens(char **a_old, char *a_string,
+ char a_separator, char *a_separators);
+char *pkgstrGetToken(char *r_sep, char *a_string, int a_index,
+ char *a_separators);
+void pkgstrGetToken_r(char *r_sep, char *a_string, int a_index,
+ char *a_separators, char *a_buf, int a_bufLen);
+unsigned long pkgstrNumTokens(char *a_string, char *a_separators);
+char *pkgstrPrintf(char *a_format, ...);
+void pkgstrPrintf_r(char *a_buf, int a_bufLen, char *a_format, ...);
+void pkgstrRemoveToken(char **r_string, char *a_token,
+ char *a_separators, int a_index);
+void pkgstrRemoveLeadingWhitespace(char **a_str);
+/* vfpops.c */
+extern int vfpCheckpointFile(VFP_T **r_destVfp, VFP_T **a_vfp,
+ char *a_path);
+extern int vfpCheckpointOpen(VFP_T **a_cvfp, VFP_T **r_vfp, char *a_path,
+ char *a_mode, VFPFLAGS_T a_flags);
+extern int vfpClearModified(VFP_T *a_vfp);
+extern int vfpClose(VFP_T **r_vfp);
+extern int vfpGetModified(VFP_T *a_vfp);
+extern int vfpOpen(VFP_T **r_vfp, char *a_path, char *a_mode,
+ VFPFLAGS_T a_flags);
+extern void vfpRewind(VFP_T *a_vfp);
+extern ssize_t vfpSafePwrite(int a_fildes, void *a_buf,
+ size_t a_nbyte, off_t a_offset);
+extern ssize_t vfpSafeWrite(int a_fildes, void *a_buf, size_t a_nbyte);
+extern int vfpSetFlags(VFP_T *a_vfp, VFPFLAGS_T a_flags);
+extern int vfpSetModified(VFP_T *a_vfp);
+extern int vfpSetSize(VFP_T *a_vfp, size_t a_size);
+extern void vfpTruncate(VFP_T *a_vfp);
+extern int vfpWriteToFile(VFP_T *a_vfp, char *a_path);
+
+/* handlelocalfs.c */
+boolean_t enable_local_fs(void);
+boolean_t restore_local_fs(void);
+
+#else /* __STDC__ */
+
+extern FILE *epopen();
+extern void pkglist_cont();
+extern char **gpkglist();
+extern char **pkgalias();
+extern char *get_prog_name();
+extern char *set_prog_name();
+extern int averify();
+extern int ckparam();
+extern int ckvolseq();
+extern int cverify();
+extern unsigned long compute_checksum();
+extern int fverify();
+extern char *getErrbufAddr();
+extern int getErrbufSize();
+extern char *getErrstr();
+extern void setErrstr();
+extern int devtype();
+extern int ds_close();
+extern int ds_findpkg();
+extern int ds_getinfo();
+extern int ds_getpkg();
+extern int ds_ginit();
+extern boolean_t ds_fd_open();
+extern int ds_init();
+extern int ds_next();
+extern int ds_readbuf();
+extern int epclose();
+extern int esystem();
+extern int e_ExecCmdArray();
+extern int e_ExecCmdList();
+extern int gpkgmap();
+extern int isFdRemote();
+extern int isFstypeRemote();
+extern int isPathRemote();
+extern int iscpio();
+extern int isdir();
+extern int isfile();
+extern int pkgexecl();
+extern int pkgexecv();
+extern int pkghead();
+extern int pkgmount();
+extern int pkgtrans();
+extern int pkgumount();
+extern int ppkgmap();
+extern int putcfile();
+extern int putcvfpfile();
+extern int rrmdir();
+extern int srchcfile();
+extern struct group *cgrgid();
+extern struct group *cgrnam();
+extern struct passwd *cpwnam();
+extern struct passwd *cpwuid();
+extern void basepath();
+extern void canonize();
+extern void canonize_slashes();
+extern void checksum_off();
+extern void checksum_on();
+extern void cvtpath();
+extern void ds_order();
+extern void ds_putinfo();
+extern void ds_skiptoend();
+extern void ecleanup();
+extern void logerr();
+extern int mappath();
+extern int mapvar();
+extern void progerr();
+extern void rpterr();
+extern void tputcfent();
+extern void set_nonABI_symlinks();
+extern int nonABI_symlinks();
+extern void disable_attribute_check();
+extern int get_disable_attribute_check();
+/* vfpops.c */
+extern int vfpCheckpointFile();
+extern int vfpCheckpointOpen();
+extern int vfpClearModified();
+extern int vfpClose();
+extern int vfpGetModified();
+extern int vfpOpen();
+extern void vfpRewind();
+extern int vfpSetFlags();
+extern int vfpSetModified();
+extern int vfpSetSize();
+extern void vfpTruncate();
+extern int vfpWriteToFile();
+
+/* handlelocalfs.c */
+boolean_t enable_local_fs();
+boolean_t restore_local_fs();
+
+/* gpkgmap.c */
+int getmapmode(void);
+
+#endif /* __STDC__ */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _PKGLIB_H */
diff --git a/usr/src/lib/libpkg/common/pkglibmsgs.h b/usr/src/lib/libpkg/common/pkglibmsgs.h
new file mode 100644
index 0000000000..cf77b0f57a
--- /dev/null
+++ b/usr/src/lib/libpkg/common/pkglibmsgs.h
@@ -0,0 +1,431 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _PKGLIBMSGS_H
+#define _PKGLIBMSGS_H
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* srchcfile messages */
+#define ERR_MISSING_NEWLINE "missing newline at end of entry"
+#define ERR_ILLEGAL_SEARCH_PATH "illegal search path specified"
+#define ERR_CANNOT_READ_MM_NUMS "unable to read major/minor device numbers"
+#define ERR_INCOMPLETE_ENTRY "incomplete entry"
+#define ERR_VOLUMENO_UNEXPECTED "volume number not expected"
+#define ERR_FTYPE_I_UNEXPECTED "ftype <i> not expected"
+#define ERR_CANNOT_READ_CLASS_TOKEN "unable to read class token"
+#define ERR_CANNOT_READ_PATHNAME_FLD "unable to read pathname field"
+#define ERR_UNKNOWN_FTYPE "unknown ftype"
+#define ERR_CANNOT_READ_LL_PATH "unable to read local/link path"
+#define ERR_INCOMPLETE_ENTRY "incomplete entry"
+#define ERR_NO_LINK_SOURCE_SPECIFIED "no link source specified"
+#define ERR_CANNOT_READ_MOG "unable to read mode/owner/group"
+#define ERR_CANNOT_READ_CONTENT_INFO "unable to read content info"
+#define ERR_PACKAGE_NAME_TOO_LONG "package name too long"
+#define ERR_NO_MEMORY "no memory for package information"
+#define ERR_BAD_ENTRY_END "bad end of entry"
+#define ERR_EXTRA_TOKENS "extra token(s) on input line"
+
+/* pkgtrans messages */
+#define MSG_TRANSFER "Transferring <%s> package instance\n"
+#define MSG_STORE_ACC "Retrieving signature certificates from <%s>\n"
+#define MSG_SIGNING "Generating digital signature for signer <%s>\n"
+#define MSG_RENAME "\t... instance renamed <%s> on destination\n"
+
+#define ERR_TRANSFER "unable to complete package transfer"
+#define MSG_SEQUENCE "- volume is out of sequence"
+#define MSG_MEM "- no memory"
+#define MSG_CMDFAIL "- process <%s> failed, exit code %d"
+#define MSG_POPEN "- popen of <%s> failed, errno=%d"
+#define MSG_PCLOSE "- pclose of <%s> failed, errno=%d"
+#define MSG_BADDEV "- invalid or unknown device <%s>"
+#define MSG_GETVOL "- unable to obtain package volume"
+#define MSG_NOSIZE "- unable to obtain maximum part size from pkgmap"
+#define MSG_CHDIR "- unable to change directory to <%s>"
+#define MSG_SYMLINK "- unable to create symbolic link to <%s> from <%s>"
+#define MSG_STATDIR "- unable to stat <%s>"
+#define MSG_CHOWNDIR "- unable to chown <%s>"
+#define MSG_CHMODDIR "- unable to chmod <%s>"
+#define MSG_FSTYP "- unable to determine filesystem type for <%s>"
+#define MSG_NOTEMP "- unable to create or use temporary directory <%s>"
+#define MSG_SAMEDEV "- source and destination represent the same device"
+#define MSG_NOTMPFIL "- unable to create or use temporary file <%s>"
+#define MSG_NOPKGMAP "- unable to open pkgmap for <%s>"
+#define MSG_BADPKGINFO "- unable to determine contents of pkginfo file"
+#define MSG_NOPKGS "- no packages were selected from <%s>"
+#define MSG_MKDIR "- unable to make directory <%s>"
+#define MSG_NOEXISTS "- package instance <%s> does not exist on source " \
+ "device"
+#define MSG_EXISTS "- no permission to overwrite existing path <%s>"
+#define MSG_DUPVERS "- identical version of <%s> already exists on " \
+ "destination device"
+#define MSG_TWODSTREAM "- both source and destination devices cannot be a " \
+ "datastream"
+#define MSG_OPEN "- open of <%s> failed, errno=%d"
+#define MSG_STATVFS "- statvfs(%s) failed, errno=%d"
+
+/* security problems */
+#define ERR_PARSE "unable to parse keystore <%s>, invalid " \
+ "format or corrupt"
+#define ERR_BADPASS "Invalid password. Password does not " \
+ "decrypt keystore"
+
+#define MSG_PASSWD_FILE "Password file <%s> cannot be read"
+#define MSG_PASSWD_AGAIN "For Verification"
+#define MSG_PASSWD_NOMATCH "Passwords do not match"
+#define MSG_BADPASSARG "Password retrieval method <%s> invalid"
+#define MSG_NOPASS "Cannot get passphrase using " \
+ "retrieval method <%s>"
+
+#define ERR_MISMATCHPASS "<%s> encrypted with different password " \
+ " than <%s>, keystore <%s> corrupt"
+
+#define MSG_CHSIGDIR "- unable to change directory to <%s/%s>"
+#define MSG_MKSIGDIR "- unable to make directory <%s/%s>"
+#define ERR_CANTSIGN "- destination device must be datastream in order to" \
+ " sign contents"
+#define ERR_STORE "unable to find or use store <%s> from application " \
+ "<%s>:<%s>"
+
+#define ERR_NO_KEYSTORE "unable to open keystore <%s> for reading"
+#define ERR_NOT_REG "<%s> is not a regular file"
+#define ERR_KEYSTORE_CORRUPT "Keystore file <%s> is corrupt or unparseable"
+#define ERR_KEYSTORE_REPAIR "unable to repair keystore <%s>"
+#define ERR_KEYSTORE_LOCKED_READ "unable to lock keystore file <%s> " \
+ "for reading, try again later"
+#define ERR_KEYSTORE_LOCKED "unable to lock keystore <%s> for exclusive " \
+ "access"
+#define ERR_KEYSTORE_UNLOCK "unable to unlock keystore <%s> for " \
+ "application <%s>"
+#define ERR_KEYSTORE_WRITE "unable to open keystore <%s> for writing"
+#define ERR_KEYSTORE_REMOVE "unable to delete keystore file <%s>"
+#define ERR_KEYSTORE_READ "unable to open keystore <%s> for reading"
+#define ERR_KEYSTORE_OPEN "unable to open keystore <%s>:<%s>"
+#define ERR_KEYSTORE_FORM "unable to form PKCS12 keystore file for " \
+ "writing to <%s>"
+
+#define ERR_KEYSTORE_NOPUBCERTS "unable to find any public key certificates " \
+ "in keystore file <%s>"
+
+#define ERR_KEYSTORE_NOPRIVKEYS "unable to find any private keys in keystore "\
+ "file <%s>"
+
+#define ERR_KEYSTORE_NOCACERTS "unable to find any trusted certificates in "\
+ "file <%s>"
+
+#define ERR_KEYSTORE_NOTRUST "unable to find any trusted certificates in "\
+ "keystore"
+
+#define ERR_KEYSTORE_NOMATCH "unable to find certificate and key pair " \
+ "with alias <%s> in keystore"
+
+#define ERR_KEYSTORE_DUPLICATECERT "Certificate with alias <%s> " \
+ "already exists in keystore"
+#define ERR_KEYSTORE_DUPLICATEKEY "Private key with alias <%s> already" \
+ " exists in keystore"
+#define ERR_KEYSTORE_NO_ALIAS "Keystore certificate <%s> has no recorded " \
+ "alias, must be deleted from keystore"
+#define ERR_KEYSTORE_NOCERT "No certificate with alias <%s> found in " \
+ "keystore <%s>"
+#define ERR_KEYSTORE_NOCERTKEY "No certificates or private keys with alias " \
+ "<%s> found in keystore <%s>"
+
+#define ERR_KEYSTORE_INTERNAL "Internal Error file %s line %d"
+
+#define ERR_CURR_TIME "Cannot determine current time from system"
+#define ERR_CERT_TIME "Certificate <%s> has expired or is not yet valid.\n" \
+ "Current time: <%s>\n Certificate valid: <%s> - <%s>"
+#define ERR_MISMATCHED_KEYS "Private key does not match public key in " \
+ "certificate <%s>"
+#define ERR_CERT_TIME_BAD "Certificate has corrupt validity dates, " \
+ "cannot process"
+#define ERR_TRUSTSTORE "unable to find or use trusted certificate " \
+ "store <%s> from application <%s>:<%s>"
+
+#define ERR_STORE_PW "unable to read password from <%s>"
+
+#define ERR_SEC "unable to sign package contents using <%s> " \
+ "private key"
+
+#define ERR_NOGEN "unable to generate digital signature"
+
+#define ERR_STORE_PW "unable to read password from <%s>"
+#define ERR_CORRUPTSIG "Invalid or corrupt signature in datastream <%s>"
+#define ERR_CORRUPTSIG_TYPE "Wrong PKCS7 signature type in datastream <%s>"
+#define ERR_CORRUPTSIG_DT "Signature found but not detached in " \
+ "datastream <%s>"
+#define ERR_KEYSTORE "invalid or corrupt PKCS12 file <%s>."
+#define ERR_KEYSTORE_NOCERTS "Store <%s> contains no certificates"
+#define ERR_KEYSTORE_NOKEYS "Store <%s> contains no private keys"
+#define ERR_SIG_INT "Internal error during signature verification."
+#define MSG_VERIFY "## Verifying signature for signer <%s>"
+#define MSG_VERIFY_OK "## Signature for signer <%s> verified."
+#define ERR_VERIFY "Signature verification failed."
+#define ERR_VERIFY_SIG "Signature verification failed while verifying " \
+ "certificate <subject=%s, issuer=%s>:<%s>."
+#define ERR_VERIFY_ISSUER "Could not find issuer certificate for signer <%s>"
+#define ERR_OPENSIG "Signature found in datastream but cannot be " \
+ " opened: <%s>"
+
+#define ERR_SIGFOUND "signature found in datastream <%s>, you must " \
+ "specify a keystore with -k"
+#define ERR_DSINIT "could not process datastream from <%s>"
+
+#define MSG_KEYSTORE_AL "Keystore Alias"
+#define MSG_KEYSTORE_SN "Serial Number"
+#define MSG_KEYSTORE_FP "Fingerprint"
+#define MSG_KEYSTORE_CN "Common Name"
+#define MSG_KEYSTORE_IN "Issuer Common Name"
+#define MSG_KEYSTORE_VD "Validity Dates"
+#define MSG_KEYSTORE_TY "Certificate Type"
+#define MSG_KEYSTORE_TRUSTED "Trusted Certificate"
+#define MSG_KEYSTORE_UNTRUSTED "Signing Certificate"
+#define MSG_KEYSTORE_UNKNOWN "Unknown"
+
+/* parameter errors */
+#define ERR_LEN "length of parameter value <%s> exceeds limit"
+#define ERR_ASCII "parameter <%s> must be ascii"
+#define ERR_ALNUM "parameter <%s> must be alphanumeric"
+#define ERR_CHAR "parameter <%s> has incorrect first character"
+#define ERR_UNDEF "parameter <%s> cannot be null"
+
+/* volume sequence errors */
+#define MSG_SEQ "Volume is out of sequence."
+#define MSG_CORRUPT "Volume is corrupt or is not part of the appropriate " \
+ "package."
+#define ERR_NOPKGMAP "ERROR: unable to process <%s>"
+#define ERR_BADPKGINFO "ERROR: unable to process <%s>"
+
+/* datastream processing errors */
+#define ERR_UNPACK "attempt to process datastream failed"
+#define ERR_DSTREAMSEQ "datastream sequence corruption"
+#define ERR_TRANSFER "unable to complete package transfer"
+#define MSG_CMDFAIL "- process <%s> failed, exit code %d"
+#define MSG_TOC "- bad format in datastream table-of-contents"
+#define MSG_EMPTY "- datastream table-of-contents appears to be empty"
+#define MSG_POPEN "- popen of <%s> failed, errno=%d"
+#define MSG_OPEN "- open of <%s> failed, errno=%d"
+#define MSG_PCLOSE "- pclose of <%s> failed, errno=%d"
+#define MSG_PKGNAME "- invalid package name in datastream table-of-contents"
+#define MSG_NOPKG "- package <%s> not in datastream"
+#define MSG_STATFS "- unable to stat filesystem, errno=%d"
+#define MSG_NOSPACE "- not enough space, %d blocks required, %d available"
+
+/* pkglist errors */
+#define ERR_MEMORY "memory allocation failure, errno=%d"
+#define ERR_NOPKG "no package associated with <%s>"
+#define HEADER "The following packages are available:"
+#define HELP "Please enter the package instances you wish to " \
+ "process from the list provided (or 'all' to process " \
+ "all packages.)"
+
+#define PROMPT "Select package(s) you wish to process (or 'all' to " \
+ "process all packages)."
+/* pkgmap errors */
+#define ERR_READLINK "unable to read link specification."
+#define ERR_NOVAR "no value defined for%s variable <%s>."
+#define ERR_OWNTOOLONG "owner string is too long."
+#define ERR_GRPTOOLONG "group string is too long."
+#define ERR_IMODE "mode must not be parametric at install time."
+#define ERR_BASEINVAL "invalid base for mode."
+#define ERR_MODELONG "mode string is too long."
+#define ERR_MODEALPHA "mode is not numeric."
+#define ERR_MODEBITS "invalid bits set in mode."
+
+/* package mount errors and msgs */
+#define ERR_FSTYP "unable to determine fstype for <%s>"
+#define ERR_NOTROOT "You must be \"root\" when using mountable media."
+#define MOUNT "/sbin/mount"
+#define UMOUNT "/sbin/umount"
+#define FSTYP "/usr/sbin/fstyp"
+
+#define LABEL0 "Insert %%v %d of %d for <%s> package into %%p."
+#define LABEL1 "Insert %%v %d of %d into %%p."
+#define LABEL2 "Insert %%v for <%s> package into %%p."
+#define LABEL3 "Insert %%v into %%p."
+
+/* package verify errors */
+#define MSG_WLDDEVNO "NOTE: <%s> created as device (%d, %d)."
+
+#define WRN_QV_SIZE "WARNING: quick verify of <%s>; wrong size."
+#define WRN_QV_MTIME "WARNING: quick verify of <%s>; wrong mod time."
+
+#define ERR_PKG_INTERNAL "Internal package library failure file %s line %d"
+#define ERR_UNKNOWN "unable to determine object type"
+#define ERR_EXIST "pathname does not exist"
+#define ERR_FTYPE "file type <%c> expected <%c> actual"
+#define ERR_FTYPED "<%s> is a door and is not being modified"
+#define ERR_LINK "pathname not properly linked to <%s>"
+#define ERR_SLINK "pathname not symbolically linked to <%s>"
+#define ERR_MTIME "modtime <%s> expected <%s> actual"
+#define ERR_SIZE "file size <%llu> expected <%llu> actual"
+#define ERR_CKSUM "file cksum <%ld> expected <%ld> actual"
+#define ERR_NO_CKSUM "unable to checksum, may need to re-run command as " \
+ "user \"root\""
+#define ERR_MAJMIN "major/minor device <%d, %d> expected <%d, %d> actual"
+#define ERR_PERM "permissions <%04o> expected <%04o> actual"
+#define ERR_GROUP "group name <%s> expected <%s> actual"
+#define ERR_OWNER "owner name <%s> expected <%s> actual"
+#define ERR_MODFAIL "unable to fix modification time"
+#define ERR_LINKFAIL "unable to create link to <%s>"
+#define ERR_LINKISDIR "<%s> is a directory, link() not performed"
+#define ERR_SLINKFAIL "unable to create symbolic link to <%s>"
+#define ERR_DIRFAIL "unable to create directory"
+#define ERR_CDEVFAIL "unable to create character-special device"
+#define ERR_BDEVFAIL "unable to create block-special device"
+#define ERR_PIPEFAIL "unable to create named pipe"
+#define ERR_ATTRFAIL "unable to fix attributes"
+#define ERR_BADGRPID "unable to determine group name for gid <%d>"
+#define ERR_BADUSRID "unable to determine owner name for uid <%d>"
+#define ERR_BADGRPNM "group name <%s> not found in group table(s)"
+#define ERR_BADUSRNM "owner name <%s> not found in passwd table(s)"
+#define ERR_GETWD "unable to determine current working directory"
+#define ERR_CHDIR "unable to change current working directory to <%s>"
+#define ERR_RMDIR "unable to remove existing directory at <%s>"
+
+/* others */
+#define ERR_ISCPIO_OPEN "iscpio(): open(%s) failed!"
+#define ERR_ISCPIO_FSTAT "iscpio(): fstat(%s) failed!"
+#define ERR_ISCPIO_READ "iscpio(): read(%s) failed!"
+#define ERR_ISCPIO_NOCPIO "iscpio(): <%s> is not a cpio archive!"
+
+#define ERR_DUPFAIL "%s: strdup(%s) failed.\n"
+#define ERR_ADDFAIL "%s: add_cache() failed.\n"
+#define ERR_BADMEMB "%s: %s in \"%s\" %s structure is invalid.\n"
+#define ERR_NOGRP "dup_gr_ent(): no group entry provided.\n"
+#define ERR_NOPWD "dup_pw_ent(): no passwd entry provided.\n"
+#define ERR_NOINIT "%s: init_cache() failed.\n"
+#define ERR_MALLOC "%s: malloc(%d) failed for %s.\n"
+
+#define ERR_TOO_MANY_ARGS "too many arguments passed to pkgexecl " \
+ "for command <%s>"
+#define ERR_WAIT_FAILED "wait for process %ld failed, pid <%ld> status " \
+ "<0x%08lx> errno <%d> (%s)"
+#define ERR_FORK_FAILED "fork() failed errno=%d (%s)"
+#define ERR_FREOPEN "freopen(%s, \"%s\", %s) failed, errno=%d (%s)"
+#define ERR_FDOPEN "fdopen(%d, \"%s\") failed, errno=%d (%s)"
+#define ERR_CLOSE "close(%d) failed, errno=%d"
+#define ERR_SETGID "setgid(%d) failed."
+#define ERR_SETUID "setuid(%d) failed."
+#define ERR_EX_FAIL "exec of %s failed, errno=%d"
+
+/* pkgweb errors */
+#define MSG_DWNLD "\n## Downloading..."
+#define ERR_DWNLD_FAILED "\n## After %d retries, unable to complete transfer"
+#define MSG_DWNLD_TIMEOUT "\n## Timed out, retrying..."
+#define MSG_DWNLD_CONNREF "\n## Connection to <%s> refused, retrying..."
+#define MSG_DWNLD_HOSTDWN "\n## <%s> not responding, retrying..."
+#define MSG_DWNLD_PART "\n## Found partially downloaded file <%s> of " \
+ "size <%ld> bytes. To force a complete " \
+ "re-download, delete this file and try again"
+#define MSG_DWNLD_PREV "\n## Using previously spooled package datastream <%s>"
+#define MSG_DWNLD_CONT "\n## Continuing previously attempted download..."
+#define MSG_DWNLD_COMPLETE "## Download Complete\n"
+
+#define ERR_DWNLD_NO_CONT "unable to open partially downloaded file <%s> " \
+ "for appending"
+#define ERR_BAD_PATH "unable to locate keystore."
+#define ERR_EMPTYPATH "No valid path exists for the keystore file."
+#define ERR_RETRIES "The number of server retries is not a valid " \
+ "value. Please specify a value within the range of %d - %d."
+#define ERR_TIMEOUT "The network timeout value is not a valid " \
+ "value. Please specify a value within the range of %d - %d."
+#define ERR_PARSE_URL "unable to parse the url <%s>."
+#define ERR_MEM "unable to allocate memory."
+#define ERR_HTTPS_PASSWD "unable set password for HTTPS connection."
+#define ERR_HTTPS_CA "unable to set CA file for HTTPS connection."
+#define ERR_HTTP "Failure occurred with http(s) negotiation: <%s>"
+#define ERR_WRITE "Cannot write to file <%s> : <%s>"
+#define ERR_READ "Cannot read from file <%s> : <%s>"
+#define ERR_SVR_RESP "unable to establish a connection with the http(s) server."
+#define ERR_INIT_CONN "unable to establish a connection with <%s>."
+#define ERR_INIT_SESS "unable to intialize download session for <%s>."
+#define ERR_INIT_CONN_PROXY "unable to establish a connection with <%s> " \
+ "using <%s> as the proxy"
+#define ERR_CLOSE_CONN "unable to close the connection with <%s>."
+#define ERR_NO_HEAD_VAL "HTTP Response did not include header <%s>."
+/* CSTYLED */
+#define ERR_BAD_HEAD_VAL "HTTP Header value \"<%s>: <%s>\" unusable or " \
+ "unparseable."
+#define ERR_BAD_CONTENT "The package <%s> attempting to be installed " \
+ "is illegal."
+#define ERR_DWNLD "unable to download package datastream from <%s>."
+#define ERR_OPEN_TMP "unable to open temporary file for writing."
+#define ERR_WRITE_TMP "unable to write to temporary file."
+#define ERR_DISK_SPACE "Not enough disk space is available to download " \
+ "package to\n%s. %llukb needed, %llukb available."
+#define ERR_CERTS "unable to find a valid certificate in <%s>."
+#define ERR_CERTCHAIN "unable to build certificate chain for subject <%s>:<%s>."
+#define ERR_ILL_ENV "The environment variable <%s=%s> is illegal"
+#define ERR_BAD_PROXY "Invalid proxy specification: <%s>"
+#define ERR_TMPDIR "unable to find temporary directory <%s>"
+#define ERR_MEM "unable to allocate memory."
+#define ERR_NO_DWNLD_DIR "No download directory available."
+#define MSG_OCSP_VERIFY "## Contacting OCSP Responder <%s> for " \
+ "certificate <%s> status"
+#define MSG_OCSP_VERIFY_PROXY "## Contacting OCSP Responder <%s> through " \
+ "proxy <%s:%d> for certificate <%s> status"
+#define ERR_OCSP_PARSE "OCSP Responder URL <%s> invalid or unparseable"
+#define ERR_OCSP_RESP_PARSE "OCSP Response <%s> unparseable or empty"
+#define ERR_OCSP_RESP_NOTOK "OCSP Request failed. Expected status " \
+ "<%d>, got <%d>, Reason=<%s>"
+#define WRN_OCSP_RESP_NONCE "WARNING: Invalid or no nonce found in " \
+ "OCSP response."
+#define ERR_OCSP_RESP_TYPE "OCSP response message type invalid: <%s>, " \
+ "expecting <%s>"
+#define ERR_OCSP_CONNECT "Cannot connect to OCSP Responder <%s> port <%d>"
+#define ERR_OCSP_SEND "Cannot send OCSP request to OCSP Responder <%s>"
+#define ERR_OCSP_READ "Cannot read OCSP response from OCSP Responder <%s>"
+#define ERR_OCSP_RESPONDER "OCSP Responder cannot process OCSP Request"
+#define ERR_OCSP_UNSUP "Unsupported OCSP Option <%s>"
+#define ERR_OCSP_VERIFY_NOTIME "Cannot access system time() to determine " \
+ "OCSP Response validity"
+#define ERR_OCSP_VERIFY_SIG "OCSP Response, signed by <%s>, cannot be " \
+ "verified: <%s>"
+#define ERR_OCSP_VERIFY_FAIL "unable to validate response from OCSP " \
+ "Responder <%s>"
+#define ERR_OCSP_VERIFY_NO_STATUS "OCSP Responder did not supply validity " \
+ "of certificate <%s> "
+#define ERR_OCSP_VERIFY_VALIDITY_NOTBEFORE "OCSP Response is only valid " \
+ "after <%s>. Current time is <%s>."
+#define ERR_OCSP_VERIFY_VALIDITY "OCSP Response is only valid from <%s> " \
+ "to <%s>. Current time is <%s>."
+#define ERR_OCSP_VERIFY_STATUS "OCSP Responder indicates certificate <%s> " \
+ "status is <%s>"
+#define ERR_OCSP_VERIFY "OCSP Responder rejected certificate, or did not " \
+ "recognize"
+#define ERR_OCSP_NO_URI "No OCSP Responder URL"
+
+#define MSG_BASE_USED "Using <%s> as the package base directory."
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _PKGLIBMSGS_H */
diff --git a/usr/src/lib/libpkg/common/pkglocale.h b/usr/src/lib/libpkg/common/pkglocale.h
new file mode 100644
index 0000000000..93f57a07d5
--- /dev/null
+++ b/usr/src/lib/libpkg/common/pkglocale.h
@@ -0,0 +1,51 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2003 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _PKGLOCALE_H
+#define _PKGLOCALE_H
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <locale.h>
+#include <libintl.h>
+
+#if !defined(TEXT_DOMAIN)
+#define TEXT_DOMAIN "SYS_TEST"
+#endif
+#ifdef lint
+#define pkg_gt(x) x
+#else /* !lint */
+#define pkg_gt(x) dgettext(TEXT_DOMAIN, x)
+#endif /* lint */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _PKGLOCALE_H */
diff --git a/usr/src/lib/libpkg/common/pkgmount.c b/usr/src/lib/libpkg/common/pkgmount.c
new file mode 100644
index 0000000000..e3c560ec70
--- /dev/null
+++ b/usr/src/lib/libpkg/common/pkgmount.c
@@ -0,0 +1,167 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
+/* All Rights Reserved */
+
+
+
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <pkgdev.h>
+#include <pkginfo.h>
+#include <sys/types.h>
+#include <devmgmt.h>
+#include <sys/mount.h>
+#include "pkglib.h"
+#include "pkglibmsgs.h"
+#include "pkglocale.h"
+
+extern void quit(int retcode); /* Expected to be declared by caller! */
+/* libadm.a */
+extern int getvol(char *device, char *label, int options, char *prompt);
+
+#define CMDSIZ 256
+
+int
+pkgmount(struct pkgdev *devp, char *pkg, int part, int nparts, int getvolflg)
+{
+ int n;
+ char *pt, prompt[64], cmd[CMDSIZ];
+ FILE *pp;
+
+ if (getuid()) {
+ progerr(pkg_gt(ERR_NOTROOT));
+ return (99);
+ }
+
+ if (part && nparts) {
+ if (pkg) {
+ (void) sprintf(prompt, pkg_gt(LABEL0), part,
+ nparts, pkg);
+ } else {
+ (void) sprintf(prompt, pkg_gt(LABEL1), part,
+ nparts);
+ }
+ } else if (pkg)
+ (void) sprintf(prompt, pkg_gt(LABEL2), pkg);
+ else
+ (void) sprintf(prompt, pkg_gt(LABEL3));
+
+ n = 0;
+ for (;;) {
+ if (!getvolflg && n)
+ /*
+ * Return to caller if not prompting
+ * and error was encountered.
+ */
+ return (-1);
+ if (getvolflg && (n = getvol(devp->bdevice, NULL,
+ (devp->rdonly ? 0 : DM_FORMFS|DM_WLABEL), prompt))) {
+ if (n == 3)
+ return (3);
+ if (n == 2)
+ progerr(pkg_gt("unknown device <%s>"),
+ devp->bdevice);
+ else
+ progerr(
+ pkg_gt("unable to obtain package volume"));
+ return (99);
+ }
+
+ if (devp->fstyp == NULL) {
+ (void) sprintf(cmd, "%s %s", FSTYP, devp->bdevice);
+ if ((pp = epopen(cmd, "r")) == NULL) {
+ rpterr();
+ logerr(pkg_gt(ERR_FSTYP), devp->bdevice);
+ n = -1;
+ continue;
+ }
+ cmd[0] = '\0';
+ if (fgets(cmd, CMDSIZ, pp) == NULL) {
+ logerr(pkg_gt(ERR_FSTYP), devp->bdevice);
+ (void) pclose(pp);
+ n = -1;
+ continue;
+ }
+ if (epclose(pp)) {
+ rpterr();
+ logerr(pkg_gt(ERR_FSTYP), devp->bdevice);
+ n = -1;
+ continue;
+ }
+ if (pt = strpbrk(cmd, " \t\n"))
+ *pt = '\0';
+ if (cmd[0] == '\0') {
+ logerr(pkg_gt(ERR_FSTYP), devp->bdevice);
+ n = -1;
+ continue;
+ }
+ devp->fstyp = strdup(cmd);
+ }
+
+ if (devp->rdonly) {
+ n = pkgexecl(NULL, NULL, NULL, NULL, MOUNT, "-r", "-F",
+ devp->fstyp, devp->bdevice, devp->mount, NULL);
+ } else {
+ n = pkgexecl(NULL, NULL, NULL, NULL, MOUNT, "-F",
+ devp->fstyp, devp->bdevice, devp->mount, NULL);
+ }
+ if (n) {
+ progerr(pkg_gt("mount of %s failed"), devp->bdevice);
+ continue;
+ }
+ devp->mntflg++;
+ break;
+ }
+ return (0);
+}
+
+int
+pkgumount(struct pkgdev *devp)
+{
+ int n = 1;
+ int retry = 10;
+
+ if (!devp->mntflg)
+ return (0);
+
+ while (n != 0 && retry-- > 0) {
+ n = pkgexecl(NULL, NULL, NULL, NULL, UMOUNT, devp->bdevice,
+ NULL);
+ if (n != 0) {
+ progerr(pkg_gt("retrying umount of %s"),
+ devp->bdevice);
+ sleep(5);
+ }
+ }
+ if (n == 0)
+ devp->mntflg = 0;
+ return (n);
+}
diff --git a/usr/src/lib/libpkg/common/pkgstr.c b/usr/src/lib/libpkg/common/pkgstr.c
new file mode 100644
index 0000000000..8df053e570
--- /dev/null
+++ b/usr/src/lib/libpkg/common/pkgstr.c
@@ -0,0 +1,1132 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+
+/*
+ * Module: pkgstr.c
+ * Synopsis: general string services
+ * Taxonomy: project private
+ * Debug Flag: str
+ * Description:
+ *
+ * This module implements general string utility services
+ *
+ * Public Methods:
+ *
+ * pkgstrAddToken - Add a token to a string
+ * pkgstrContainsToken - Determine if a string contains a specified token
+ * pkgstrConvertPathToBasename - Return copy of base name in path string
+ * pkgstrConvertPathToDirname - Return copy of directory name in path string
+ * pkgstrConvertUllToTimeString_r - convert unsigned long long to time string
+ * pkgstrExpandTokens - Expand tokens from string appending tokens to another
+ * pkgstrGetToken - Get a token from a string
+ * pkgstrGetToken_r - Get a token from a string into a fixed buffer
+ * pkgstrLocatePathBasename - Locate position of base name in path string
+ * pkgstrNumTokens - Determine number of tokens in string
+ * pkgstrPrintf - Create a string from a printf style format and arguments
+ * pkgstrPrintf_r - Create a string from a printf style format and arguments
+ * into a fixed buffer
+ * pkgstrRemoveToken - Remove a token from a string
+ * pkgstrRemoveLeadingWhitespace - remove leading whitespace from string
+ * pkgstrScaleNumericString - Convert unsigned long long to human
+ * readable form
+ */
+
+/*
+ * Unix Includes
+ */
+
+#define __EXTENSIONS__
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <libintl.h>
+#include <limits.h>
+#include <sys/types.h>
+#include <assert.h>
+#include <errno.h>
+#include <libintl.h>
+#include <ctype.h>
+#include <unistd.h>
+#include <strings.h>
+#include <stdarg.h>
+
+/*
+ * pkglib Includes
+ */
+
+#include "pkglib.h"
+#include "pkgstrct.h"
+#include "libintl.h"
+#include "pkglocale.h"
+
+/*
+ * External definitions
+ */
+
+/*
+ * Public methods
+ */
+
+/*
+ * Name: pkgstrRemoveLeadingWhitespace
+ * Synopsis: Remove leading whitespace from string
+ * Description: Remove all leading whitespace characters from a string
+ * Arguments: a_str - [RO, *RW] - (char **)
+ * Pointer to handle to string (in allocated storage) to
+ * remove all leading whitespace from
+ * Returns: void
+ * The input string is modified as follows:
+ * == (char *)NULL:
+ * - input string was (char *)NULL
+ * - input string is all whitespace
+ * != (char *)NULL:
+ * - copy of input string with leading
+ * whitespace removed
+ * CAUTION: The input string must be allocated space (via mem* or
+ * pkgstr* methods) - it must not be a static or inline
+ * character string
+ * NOTE: The input string a_str will be freed with 'free'
+ * if it is all whitespace, or if it contains any leading
+ * whitespace characters
+ * NOTE: Any string returned is placed in new storage for the
+ * calling method. The caller must use 'free' to dispose
+ * of the storage once the string is no longer needed.
+ * Errors: If the string cannot be created, the process exits
+ */
+
+void
+pkgstrRemoveLeadingWhitespace(char **a_str)
+{
+ char *o_str;
+
+ /* entry assertions */
+
+ assert(a_str != (char **)NULL);
+
+ /* if string is null, just return */
+
+ if (*a_str == (char *)NULL) {
+ return;
+ }
+ o_str = *a_str;
+
+ /* if string is empty, deallocate and return NULL */
+
+ if (*o_str == '\0') {
+ /* free string - handle is reset to NULL by free */
+ free(*a_str);
+ *a_str = (char *)NULL;
+ return;
+ }
+
+ /* if first character is not a space, just return */
+
+ if (!isspace(*o_str)) {
+ return;
+ }
+
+ /* advance past all space characters */
+
+ while ((*o_str != '\0') && (isspace(*o_str))) {
+ o_str++;
+ }
+
+ /* if string was all space characters, deallocate and return NULL */
+
+ if (*o_str == '\0') {
+ /* free string - *a_str is reset to NULL by free */
+ free(*a_str);
+ *a_str = (char *)NULL;
+ return;
+ }
+
+ /* have non-space/null byte, return dup, deallocate original */
+
+ o_str = strdup(o_str);
+ assert(o_str != (char *)NULL);
+ if (o_str != (char *)NULL) {
+ free(*a_str);
+ *a_str = o_str;
+ }
+}
+
+unsigned long
+pkgstrNumTokens(char *a_string, char *a_separators)
+{
+ int index;
+
+ if (a_string == (char *)NULL) {
+ return (0);
+ }
+
+ if (*a_string == '\0') {
+ return (0);
+ }
+
+ for (index = 0 ; ; index ++) {
+ char *p;
+
+ p = pkgstrGetToken((char *)NULL, a_string, index, a_separators);
+ if (p == (char *)NULL) {
+ return (index);
+ }
+ free(p);
+ }
+}
+
+/*
+ * Name: pkgstrPrintf_r
+ * Synopsis: Create string from printf style format and arguments
+ * Description: Call to convert a printf style format and arguments into a
+ * string of characters placed in allocated storage
+ * Arguments: a_buf - [RO, *RW] - (char *)
+ * - Pointer to buffer used as storage space for the
+ * returned string created
+ * a_bufLen - [RO, *RO] - (int)
+ * - Size of 'a_buf' in bytes - a maximum of 'a_bufLen-1'
+ * bytes will be placed in 'a_buf' - the returned
+ * string is always null terminated
+ * a_format - [RO, RO*] (char *)
+ * printf-style format for string to be formatted
+ * VARG_LIST - [RO] (?)
+ * arguments as appropriate to 'format' specified
+ * Returns: void
+ */
+
+/*PRINTFLIKE3*/
+void
+pkgstrPrintf_r(char *a_buf, int a_bufLen, char *a_format, ...)
+{
+ va_list ap;
+ size_t vres = 0;
+
+ /* entry assertions */
+
+ assert(a_format != (char *)NULL);
+ assert(*a_format != '\0');
+ assert(a_buf != (char *)NULL);
+ assert(a_bufLen > 1);
+
+ /* generate the results of the printf conversion */
+
+ va_start(ap, a_format);
+ vres = vsnprintf(a_buf, a_bufLen-1, a_format, ap);
+ va_end(ap);
+
+ assert(vres > 0);
+ assert(vres < a_bufLen);
+
+ a_buf[a_bufLen-1] = '\0';
+}
+
+/*
+ * Name: pkgstrPrintf
+ * Synopsis: Create string from printf style format and arguments
+ * Description: Call to convert a printf style format and arguments into a
+ * string of characters placed in allocated storage
+ * Arguments: format - [RO, RO*] (char *)
+ * printf-style format for string to be formatted
+ * VARG_LIST - [RO] (?)
+ * arguments as appropriate to 'format' specified
+ * Returns: char *
+ * A string representing the printf conversion results
+ * NOTE: Any string returned is placed in new storage for the
+ * calling method. The caller must use 'free' to dispose
+ * of the storage once the string is no longer needed.
+ * Errors: If the string cannot be created, the process exits
+ */
+
+/*PRINTFLIKE1*/
+char *
+pkgstrPrintf(char *a_format, ...)
+{
+ va_list ap;
+ size_t vres = 0;
+ char bfr[1];
+ char *rstr = (char *)NULL;
+
+ /* entry assertions */
+
+ assert(a_format != (char *)NULL);
+ assert(*a_format != '\0');
+
+ /* determine size of the message in bytes */
+
+ va_start(ap, a_format);
+ vres = vsnprintf(bfr, 1, a_format, ap);
+ va_end(ap);
+
+ assert(vres > 0);
+ assert(vres < LINE_MAX);
+
+ /* allocate storage to hold the message */
+
+ rstr = (char *)calloc(1, vres+2);
+ assert(rstr != (char *)NULL);
+ if (rstr == (char *)NULL) {
+ return ((char *)NULL);
+ }
+
+ /* generate the results of the printf conversion */
+
+ va_start(ap, a_format);
+ vres = vsnprintf(rstr, vres+1, a_format, ap);
+ va_end(ap);
+
+ assert(vres > 0);
+ assert(vres < LINE_MAX);
+ assert(*rstr != '\0');
+
+ /* return the results */
+
+ return (rstr);
+}
+
+/*
+ * Name: pkgstrExpandTokens
+ * Synopsis: Expand tokens from string appending tokens to another
+ * Description: Given a string and a list of one or more separators,
+ * expand each token from the string and append those tokens
+ * to a string that is in allocated space - create new string
+ * if no string to append to exists.
+ * Arguments: a_old - [RO, *RW] - (char **)
+ * - Pointer to handle to string to append token to
+ * == (char *)NULL - new string is created
+ * a_separator - [RO, *RO] - (char *)
+ * - separator to end tokens returned
+ * a_separators - [RO, *RO] - (char *)
+ * - String containing one or more characters that
+ * can separate one "token" from a_string from another
+ * Returns: void
+ * NOTE: Any token string returned is placed in new storage for the
+ * calling method. The caller must use 'free' to dispose
+ * of the storage once the token string is no longer needed.
+ */
+
+void
+pkgstrExpandTokens(char **a_old, char *a_string, char a_separator,
+ char *a_separators)
+{
+ int i;
+ char sep[2] = {'\0', '\0'};
+
+ /* convert single separator character into character string */
+
+ sep[0] = a_separator;
+
+ /*
+ * iterate extracting tokens from the source string and adding
+ * those tokens to the target string when the tokens are not
+ * already present in the target string
+ */
+
+ for (i = 0; ; i++) {
+ char *p;
+
+ /* extract the next matching token from the source string */
+
+ p = pkgstrGetToken((char *)NULL, a_string, i, a_separators);
+
+ /* return if no token is available */
+
+ if (p == (char *)NULL) {
+ return;
+ }
+
+ /*
+ * obtained token from source string: if the token is not
+ * in the target string, add the token to the target string
+ */
+
+ if (pkgstrContainsToken(*a_old, p, sep) == B_FALSE) {
+ pkgstrAddToken(a_old, p, *sep);
+ }
+
+ /* free up temporary storage used by token from source string */
+
+ free(p);
+ }
+ /*NOTREACHED*/
+}
+
+
+/*
+ * Name: pkgstrGetToken
+ * Synopsis: Get a separator delimited token from a string
+ * Description: Given a string and a list of one or more separators,
+ * return the position specified token (sequence of one or
+ * more characters that do not include any of the separators)
+ * Arguments: r_sep - [*RW] - (char *)
+ * - separator that ended the token returned
+ * - NOTE: this is a pointer to a "char", e.g.:
+ * - char a;
+ * - pkgstrGetToken(&a, ...)
+ * a_string - [RO, *RO] - (char *)
+ * - pointer to string to extract token from
+ * a_index - [RO, *RO] - (int)
+ * - Index of token to return; '0' is first matching
+ * token, '1' is second matching token, etc.
+ * a_separators - [RO, *RO] - (char *)
+ * - String containing one or more characters that
+ * can separate one "token" from another
+ * Returns: char *
+ * == (char *)NULL - no token matching criteria found
+ * != (char *)NULL - token matching criteria
+ * NOTE: Any token string returned is placed in new storage for the
+ * calling method. The caller must use 'free' to dispose
+ * of the storage once the token string is no longer needed.
+ */
+
+char *
+pkgstrGetToken(char *r_sep, char *a_string, int a_index, char *a_separators)
+{
+ char *p;
+ char *q;
+ char *lasts;
+
+ /* entry assertions */
+
+ assert(a_string != (char *)NULL);
+ assert(a_index >= 0);
+ assert(a_separators != (char *)NULL);
+ assert(*a_separators != '\0');
+
+ /* if returned separator requested, reset to null until token found */
+
+ if (r_sep != (char *)NULL) {
+ *r_sep = '\0';
+ }
+
+ /* duplicate original string before breaking down into tokens */
+
+ p = strdup(a_string);
+ assert(p != (char *)NULL);
+ if (p == (char *)NULL) {
+ return ((char *)NULL);
+ }
+ lasts = p;
+
+ /* scan for separators and return 'index'th token found */
+
+ while (q = strtok_r((char *)NULL, a_separators, &lasts)) {
+ /* retrieve separator if requested */
+
+ if (r_sep != (char *)NULL) {
+ char *x;
+
+ x = strpbrk(a_string, a_separators);
+ if (x) {
+ *r_sep = *x;
+ }
+ }
+
+ /* if this is the 'index'th token requested return it */
+
+ if (a_index-- == 0) {
+ char *tmp;
+
+ /* duplicate token into its own storage */
+
+ tmp = strdup(q);
+ assert(tmp != (char *)NULL);
+ if (tmp == (char *)NULL) {
+ return ((char *)NULL);
+ }
+
+ /* free up copy of original input string */
+
+ free(p);
+
+ /* return token found */
+
+ return (tmp);
+ }
+ }
+
+ /*
+ * token not found
+ */
+
+ /* free up copy of original input string */
+
+ free(p);
+
+ /* return NULL pointer (token not found) */
+
+ return ((char *)NULL);
+}
+
+/*
+ * Name: pkgstrGetToken
+ * Synopsis: Get separator delimited token from a string into a fixed buffer
+ * Description: Given a string and a list of one or more separators,
+ * return the position specified token (sequence of one or
+ * more characters that do not include any of the separators)
+ * into a specified buffer of a fixed maximum size
+ * Arguments: r_sep - [*RW] - (char *)
+ * - separator that ended the token returned
+ * - NOTE: this is a pointer to a "char", e.g.:
+ * - char a;
+ * - pkgstrGetToken(&a, ...)
+ * a_string - [RO, *RO] - (char *)
+ * - pointer to string to extract token from
+ * a_index - [RO, *RO] - (int)
+ * - Index of token to return; '0' is first matching
+ * token, '1' is second matching token, etc.
+ * a_separators - [RO, *RO] - (char *)
+ * - String containing one or more characters that
+ * can separate one "token" from another
+ * a_buf - [RO, *RW] - (char *)
+ * - Pointer to buffer used as storage space for the
+ * returned token - the returned token is always
+ * null terminated
+ * a_buf[0] == '\0' - no token meeting criteria found
+ * a_buf[0] != '\0' - token meeting criteria returned
+ * a_bufLen - [RO, *RO] - (int)
+ * - Size of 'a_buf' in bytes - a maximum of 'a_bufLen-1'
+ * bytes will be placed in 'a_buf' - the returned
+ * token is always null terminated
+ * Returns: void
+ */
+
+void
+pkgstrGetToken_r(char *r_sep, char *a_string, int a_index,
+ char *a_separators, char *a_buf, int a_bufLen)
+{
+ char *p;
+ char *q;
+ char *lasts;
+
+ /* entry assertions */
+
+ assert(a_string != (char *)NULL);
+ assert(a_index >= 0);
+ assert(a_separators != (char *)NULL);
+ assert(*a_separators != '\0');
+ assert(a_buf != (char *)NULL);
+ assert(a_bufLen > 0);
+
+ /* reset returned separator */
+
+ if (r_sep != (char *)NULL) {
+ *r_sep = '\0';
+ }
+
+ /* zero out contents of return buffer */
+
+ bzero(a_buf, a_bufLen);
+
+ /* duplicate original string before breaking down into tokens */
+
+ p = strdup(a_string);
+ assert(p != (char *)NULL);
+ if (p == (char *)NULL) {
+ return;
+ }
+ lasts = p;
+
+ /* scan for separators and return 'index'th token found */
+
+ while (q = strtok_r((char *)NULL, a_separators, &lasts)) {
+ /* retrieve separator if requested */
+
+ if (r_sep != (char *)NULL) {
+ char *x;
+ x = strpbrk(a_string, a_separators);
+ if (x) {
+ *r_sep = *x;
+ }
+ }
+
+ /* if this is the 'index'th token requested return it */
+
+ if (a_index-- == 0) {
+ /* copy as many characters as possible to return buf */
+
+ (void) strncpy(a_buf, q, a_bufLen-1);
+ break;
+ }
+ }
+
+ /* free up copy of original input string */
+
+ free(p);
+}
+
+/*
+ * Name: pkgstrAddToken
+ * Synopsis: Add a token to a string
+ * Description: Append a token (sequence of one or more characters) to a
+ * string that is in allocated space - create new string if
+ * no string to append to exists
+ * Arguments: a_old - [RO, *RW] - (char **)
+ * - Pointer to handle to string to append token to
+ * == (char *)NULL - new string is created
+ * a_new - [RO, *RO] - (char *)
+ * - Pointer to string representing token to append
+ * to the end of the "a_old" string
+ * == (char *)NULL - no action is performed
+ * a_new[0] == '\0' - no action is performed
+ * a_separator - [RO, *RO] - (char)
+ * - One character placed between the old (existing)
+ * string and the new token to be added IF the old
+ * string exists and is not empty (zero length)
+ * Returns: void
+ * CAUTION: The old (existing) string must be allocated space (via lu_mem*
+ * or pkgstr* methods) - it must not be a static or inline
+ * character string
+ * NOTE: The old (existing) string may be freed with 'free'
+ * if a token is appended to it
+ * NOTE: Any string returned in 'a_old' is placed in new storage for the
+ * calling method. The caller must use 'free' to dispose
+ * of the storage once the token string is no longer needed.
+ */
+
+void
+pkgstrAddToken(char **a_old, char *a_new, char a_separator)
+{
+ /* entry assertions */
+
+ assert(a_old != (char **)NULL);
+ assert(a_separator != '\0');
+
+ /* if token to add is null, just return */
+
+ if (a_new == (char *)NULL) {
+ return;
+ }
+
+ /* if token to add is empty (zero length), just return */
+
+ if (*a_new == '\0') {
+ return;
+ }
+
+ /* make sure that new token does not contain the separator */
+
+ assert(strchr(a_new, (int)a_separator) == (char *)NULL);
+
+ /* if old string is empty (zero length), deallocate */
+
+ if ((*a_old != (char *)NULL) && ((*a_old)[0] == '\0')) {
+ /* *a_old is set to NULL by free */
+ free(*a_old);
+ *a_old = (char *)NULL;
+ }
+
+ /* if old string is exists, append separator and token */
+
+ if (*a_old != (char *)NULL) {
+ char *p;
+ p = pkgstrPrintf("%s%c%s", *a_old, a_separator, a_new);
+ free(*a_old);
+ *a_old = p;
+ return;
+ }
+
+ /* old string does not exist - return duplicate of token */
+
+ assert(*a_old == (char *)NULL);
+ *a_old = strdup(a_new);
+ assert(*a_old != (char *)NULL);
+}
+
+/*
+ * Name: pkgstrContainsToken
+ * Synopsis: Does a given string contain a specified substring
+ * Description: Determine if a given substring exists in a larger string
+ * Arguments: a_string - [RO, *RO] - (char *)
+ * Pointer to string to look for substring in
+ * a_token - [RO, *RO] - (char *)
+ * Pointer to substring to look for in larger string
+ * Results: boolean_t
+ * B_TRUE - substring exists in larger string
+ * B_FALSE - substring does NOT exist in larger string
+ * NOTE: The substring must match on a "token" basis; that is, the
+ * substring must exist in the larger string delineated with
+ * either spaces or tabs to match.
+ */
+
+boolean_t
+pkgstrContainsToken(char *a_string, char *a_token, char *a_separators)
+{
+ char *lasts;
+ char *current;
+ char *p;
+
+ /* entry assertions */
+
+ assert(a_separators != (char *)NULL);
+ assert(*a_separators != '\0');
+
+ /* if token is not supplied, return false */
+
+ if (a_token == (char *)NULL) {
+ return (B_FALSE);
+ }
+
+ /* if no string provided, return false */
+
+ if (a_string == (char *)NULL) {
+ return (B_FALSE);
+ }
+
+ /* if string empty (zero length), return false */
+
+ if (*a_string == '\0') {
+ return (B_FALSE);
+ }
+
+ /* duplicate larger string because strtok_r changes it */
+
+ p = strdup(a_string);
+ assert(p != (char *)NULL);
+ if (p == (char *)NULL) {
+ return (B_FALSE);
+ }
+
+ lasts = p;
+
+ /* scan each token looking for a match */
+
+ while ((current = strtok_r((char *)NULL, a_separators, &lasts)) !=
+ (char *)NULL) {
+ if (streq(current, a_token)) {
+ free(p);
+ return (B_TRUE);
+ }
+ }
+
+ /* free up temporary storage */
+
+ free(p);
+
+ /* not found */
+
+ return (B_FALSE);
+}
+
+/*
+ * Name: pkgstrRemoveToken
+ * Synopsis: Remove a token from a string
+ * Description: Remove a token (sequence of one or more characters) from a
+ * string that is in allocated space
+ * Arguments: r_string - [RO, *RW] - (char **)
+ * - Pointer to handle to string to remove token from
+ * a_token - [RO, *RO] - (char *)
+ * Pointer to token (substring) to look for and remove
+ * from r_string provided
+ * a_separators - [RO, *RO] - (char *)
+ * - String containing one or more characters that
+ * separate one "token" from another in r_string
+ * a_index - [RO, *RO] - (int)
+ * - Index of token to remove; '0' is first matching
+ * token, '1' is second matching token, etc.
+ * Returns: void
+ * CAUTION: The input string must be allocated space (via lu_mem* or
+ * pkgstr* methods) - it must not be a static or inline
+ * character string
+ * NOTE: The input string r_string will be freed with 'free'
+ * if the token to be removed is found
+ * NOTE: Any token string returned is placed in new storage for the
+ * calling method. The caller must use 'free' to dispose
+ * of the storage once the token string is no longer needed.
+ * Errors: If the new token string cannot be created, the process exits
+ */
+
+void
+pkgstrRemoveToken(char **r_string, char *a_token, char *a_separators,
+ int a_index)
+{
+ char *a_string;
+ char *copyString;
+ char sep = 0;
+ int copyLength;
+ int i;
+
+ /* entry assertions */
+
+ assert(r_string != (char **)NULL);
+ assert(a_token != (char *)NULL);
+ assert(*a_token != '\0');
+ assert(a_separators != (char *)NULL);
+ assert(*a_separators != '\0');
+
+ /* simple case: input string is null; return empty string */
+
+ a_string = *r_string;
+ if (*a_string == '\0') {
+ return;
+ }
+
+ /* simple case: token == input string; return empty string */
+
+ if (streq(a_string, a_token)) {
+ /* deallocate input string; free sets *r_string to NULL */
+
+ free(*r_string);
+ *r_string = (char *)NULL;
+ return;
+ }
+
+ /* simple case: token not in input string: return */
+
+ if (!pkgstrContainsToken(a_string, a_token, a_separators)) {
+ return;
+ }
+
+ /*
+ * Pick apart the old string building the new one as we go along
+ * removing the first occurance of the token provided
+ */
+
+ copyLength = (strlen(a_string)-strlen(a_token))+2;
+ copyString = calloc(1, copyLength);
+ assert(copyString != (char *)NULL);
+ if (copyString == (char *)NULL) {
+ return;
+ }
+
+ for (i = 0; ; i++) {
+ char *p;
+
+ p = pkgstrGetToken(&sep, a_string, i, a_separators);
+ if (p == (char *)NULL) {
+ break;
+ }
+
+ if (streq(p, a_token) && (a_index-- == 0)) {
+ continue;
+ }
+
+ if (*copyString) {
+ assert(sep != '\0');
+ (void) strncat(copyString, &sep, 1);
+ }
+
+ (void) strcat(copyString, p);
+ }
+
+ free(*r_string);
+ assert(*copyString);
+ *r_string = copyString;
+}
+
+/*
+ * Name: pkgstrScaleNumericString
+ * Synopsis: Convert unsigned long long to human readable form
+ * Description: Convert a string containing an unsigned long long representation
+ * and convert it into a human readable numeric string. The number
+ * is scaled down until it is small enough to be in a good human
+ * readable format i.e. in the range 0 thru scale-1.
+ * Arguments: a_buf - [RO, *RW] - (char *)
+ * Pointer to buffer containing string representation
+ * of unsigned long long to convert
+ * scale - [RO, *RO] - (unsigned long long)
+ * Value to scale the number into
+ * Returns: a_buf - contains human readable scaled representation of
+ * original value contained in the buffer
+ * Note: The value "(unsigned long long)-1" is a special case and
+ * is always converted to "-1".
+ * Errors: If the string cannot be created, the process exits
+ */
+
+void
+pkgstrScaleNumericString(char *a_buf, unsigned long long scale)
+{
+static char *M = " KMGTPE"; /* Measurement: */
+ /* kilo, mega, giga, tera, peta, exa */
+
+ unsigned long long number = 0; /* convert this number */
+ unsigned long long save = 0;
+ char *uom = M; /* unit of measurement, initially ' ' (=M[0]) */
+
+ /* entry assertions */
+
+ assert(scale > (unsigned long long)0);
+ assert(scale <= (unsigned long long)1048576);
+
+ /*
+ * Get the number - if no number of empty number, just return
+ */
+
+ if (a_buf == (char *)NULL) {
+ return;
+ }
+
+ if (*a_buf == '\0') {
+ (void) strcpy(a_buf, "0");
+ return;
+ }
+
+ /* convert out the number from the input buffer */
+
+ number = strtoull(a_buf, (char **)NULL, 10);
+
+ /* if conversion error, return "-1" */
+
+ if ((long long)number == (long long)-1) {
+ (void) strcpy(a_buf, "-1");
+ return;
+ }
+
+ /*
+ * Now have number as a count of scale units.
+ * Stop scaling when we reached exa-bytes, then something is
+ * probably wrong with our number (it is improbably large)
+ */
+
+ while ((number >= scale) && (*uom != 'E')) {
+ uom++; /* next unit of measurement */
+ save = number;
+ number = (number + (scale / 2)) / scale;
+ }
+
+ /* check if we should output a decimal place after the point */
+
+ if (save && ((save / scale) < 10)) {
+ /* sprintf() will round for us */
+ float fnum = (float)save / scale;
+ (void) sprintf(a_buf, "%4.1f%c", fnum, *uom);
+ } else {
+ (void) sprintf(a_buf, "%4llu%c", number, *uom);
+ }
+}
+
+/*
+ * Name: pkgstrLocatePathBasename
+ * Synopsis: Locate position of base name in path string
+ * Description: Locate the base name (last path item) in a path and
+ * return a pointer to the first byte of the base name
+ * within the given path
+ * Arguments: a_path - [RO, *RO] - (char *)
+ * - Pointer to string representing path to scan
+ * Returns: char *
+ * - Pointer into string of first byte of path base name
+ * - == (char *)NULL - input path is (char *)NULL
+ */
+
+char *
+pkgstrLocatePathBasename(char *a_path)
+{
+ char *p;
+
+ /* if path is NULL, return NULL */
+
+ if (!a_path) {
+ return (a_path);
+ }
+
+ /* locate last occurance of '/' in path */
+
+ p = strrchr(a_path, '/');
+ if (p != (char *)NULL) {
+ /* base name located - return -> first byte */
+ return (p+1);
+ }
+
+ /* no occurance of '/' - entry path must be basename */
+
+ return (a_path);
+}
+
+/*
+ * Name: pkgstrConvertPathToBasename
+ * Synopsis: Return copy of base name in path string
+ * Description: Locate the base name (last path item) in a path and
+ * return a copy of the base name in allocated storage
+ * Arguments: a_path - [RO, *RO] - (char *)
+ * - Pointer to string representing path to scan
+ * Returns: char *
+ * - String containing path base name
+ * - == (char *)NULL - input path is (char *)NULL
+ * NOTE: Any string returned is placed in new storage for the
+ * calling method. The caller must use 'free' to dispose
+ * of the storage once the string is no longer needed.
+ * Errors: If the string cannot be created, the process exits
+ */
+
+char *
+pkgstrConvertPathToBasename(char *a_path)
+{
+ char *p;
+
+ /* if path is NULL, return NULL */
+
+ if (a_path == (char *)NULL) {
+ return ((char *)NULL);
+ }
+
+ /* if path is empty (zero length), return NULL */
+
+ if (*a_path == '\0') {
+ return ((char *)NULL);
+ }
+
+ /* locate last occurance of '/' in path */
+
+ p = strrchr(a_path, '/');
+ if (p == (char *)NULL) {
+ /* no occurance of '/' - entry path must be basename */
+
+ return (strdup(a_path));
+ }
+
+ /* base name located - return string from -> first byte */
+
+ return (strdup(p+1));
+}
+
+/*
+ * Name: pkgstrConvertPathToDirname
+ * Synopsis: Return copy of directory in path string
+ * Description: Locate the directory name (everything but last path item) in a
+ * path and return a copy of the dir name in allocated storage
+ * Arguments: a_path - [RO, *RO] - (char *)
+ * - Pointer to string representing path to scan
+ * Returns: char *
+ * - String containing path directory name
+ * - == (char *)NULL - input path is (char *)NULL,
+ * or a_path is empty (*a_path == '\0'), or the
+ * a_path has no directory name in it.
+ * NOTE: Any string returned is placed in new storage for the
+ * calling method. The caller must use 'free' to dispose
+ * of the storage once the string is no longer needed.
+ * Errors: If the string cannot be created, the process exits
+ */
+
+char *
+pkgstrConvertPathToDirname(char *a_path)
+{
+ char *p;
+ char *retPath;
+
+ /* if path is NULL, return NULL */
+
+ if (a_path == (char *)NULL) {
+ return ((char *)NULL);
+ }
+
+ /* if path is empty (zero length), return NULL */
+
+ if (*a_path == '\0') {
+ return ((char *)NULL);
+ }
+
+ /* locate last occurance of '/' in path */
+
+ p = strrchr(a_path, '/');
+ if (p == (char *)NULL) {
+ /* no occurance of '/' - entire path must be basename */
+
+ return ((char *)NULL);
+ }
+
+ /* duplicate original path */
+
+ retPath = strdup(a_path);
+ assert(retPath != (char *)NULL);
+ if (retPath == (char *)NULL) {
+ return ((char *)NULL);
+ }
+
+ /* remove all trailing '/'s from copy of path */
+
+ for (p = strrchr(retPath, '/'); (p > retPath) && (*p == '/'); p--) {
+ *p = '\0';
+ }
+
+ /* if entire path was '/'s, return null string - no directory present */
+
+ if (*retPath == '\0') {
+ free(retPath);
+ return ((char *)NULL);
+ }
+
+ /* path has at least one non-'/' in it - return -> directory portion */
+
+ return (retPath);
+}
+
+/*
+ * Name: pkgstrConvertUllToTimeString_r
+ * Synopsis: Convert an unsigned long long into a "time string"
+ * Description: Given an unsigned long long, return a "time string" which is a
+ * conversion of the unsigned long long interpreted as a number of
+ * nanoseconds into a "hour:minute:second.ns" ascii string
+ * Arguments: a_time - [RO, *RO] - (unsigned long long)n
+ * - value to convert
+ * a_buf - [RO, *RW] - (char *)
+ * - Pointer to buffer used as storage space for the
+ * returned string
+ * a_bufLen - [RO, *RO] - (int)
+ * - Size of 'a_buf' in bytes - a maximum of 'a_bufLen-1'
+ * bytes will be placed in 'a_buf'
+ * Returns: char *
+ * - String containing converted value
+ * NOTE: Any string returned is placed in new storage for the
+ * calling method. The caller must use 'free' to dispose
+ * of the storage once the string is no longer needed.
+ * Errors: If the string cannot be created, the process exits
+ */
+
+void
+pkgstrConvertUllToTimeString_r(unsigned long long a_time,
+ char *a_buf, int a_bufLen)
+{
+ unsigned long long seconds;
+ unsigned long long minutes;
+ unsigned long long hours;
+ unsigned long long ns;
+
+ /* entry assertions */
+
+ assert(a_buf != (char *)NULL);
+ assert(a_bufLen > 0);
+
+ /* if time is 0, return immediate result */
+
+ if (a_time == 0) {
+ pkgstrPrintf_r(a_buf, a_bufLen, "%s", "0:00:00.000000000");
+ return;
+ }
+
+ /* break out individual time components */
+
+ ns = a_time % 1000000000ll; /* nanoseconds left over from seconds */
+ seconds = a_time / 1000000000ll; /* total seconds */
+ minutes = seconds / 60ll; /* total minutes */
+ seconds = seconds % 60ll; /* seconds left over from minutes */
+ hours = minutes / 60ll; /* total hours */
+ minutes = minutes % 60ll; /* minutes left over from hours */
+
+ /* return a converted string */
+
+ pkgstrPrintf_r(a_buf, a_bufLen, "%llu:%02llu:%02llu.%09llu",
+ hours, minutes, seconds, ns);
+}
diff --git a/usr/src/lib/libpkg/common/pkgtrans.c b/usr/src/lib/libpkg/common/pkgtrans.c
new file mode 100644
index 0000000000..dbaaa73adf
--- /dev/null
+++ b/usr/src/lib/libpkg/common/pkgtrans.c
@@ -0,0 +1,1973 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
+/* All Rights Reserved */
+
+
+
+#include <stdio.h>
+#include <errno.h>
+#include <stdarg.h>
+#include <limits.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <ctype.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <sys/statvfs.h>
+#include <sys/sysmacros.h>
+#include <dirent.h>
+#include <signal.h>
+#include <devmgmt.h>
+#include <openssl/pkcs12.h>
+#include <openssl/x509.h>
+#include <openssl/pkcs7.h>
+#include <openssl/err.h>
+#include <openssl/pem.h>
+#include "pkginfo.h"
+#include "pkgstrct.h"
+#include "pkgtrans.h"
+#include "pkgdev.h"
+#include "pkglib.h"
+#include "pkglibmsgs.h"
+#include "keystore.h"
+#include "pkglocale.h"
+#include "pkgerr.h"
+
+extern char *pkgdir; /* pkgparam.c */
+
+/* libadm.a */
+extern char *devattr(char *device, char *attribute);
+extern char *fpkginst(char *pkg, ...);
+extern int fpkginfo(struct pkginfo *info, char *pkginst);
+extern int getvol(char *device, char *label, int options, char *prompt);
+extern int _getvol(char *device, char *label, int options, char *prompt,
+ char *norewind);
+
+/* dstream.c */
+extern int ds_ginit(char *device);
+extern int ds_close(int pkgendflg);
+
+#define CPIOPROC "/usr/bin/cpio"
+
+#define CMDSIZE 512 /* command block size */
+
+#define BLK_SIZE 512 /* size of logical block */
+
+#define ENTRY_MAX 256 /* max size of entry for cpio cmd or header */
+
+#define PKGINFO "pkginfo"
+#define PKGMAP "pkgmap"
+#define MAP_STAT_SIZE 60 /* 1st line of pkgmap (3 numbers & a : */
+
+#define INSTALL "install"
+#define RELOC "reloc"
+#define ROOT "root"
+#define ARCHIVE "archive"
+
+static struct pkgdev srcdev, dstdev;
+static char *tmpdir;
+static char *tmppath;
+static char *tmpsymdir = NULL;
+static char dstinst[NON_ABI_NAMELNGTH];
+static char *ids_name, *ods_name;
+static int ds_volcnt;
+static int ds_volno;
+static int compressedsize, has_comp_size;
+
+static void (*sigintHandler)();
+static void (*sighupHandler)();
+static void cleanup(void);
+static void sigtrap(int signo);
+static int rd_map_size(FILE *fp, int *npts, int *maxpsz, int *cmpsize);
+
+static int cat_and_count(struct dm_buf *, char *);
+
+static int ckoverwrite(char *dir, char *inst, int options);
+static int pkgxfer(char *srcinst, int options);
+static int wdsheader(struct dm_buf *, char *src, char *device,
+ char **pkg, PKCS7 *);
+static struct dm_buf *genheader(char *, char *, char **);
+
+static int dump_hdr_and_pkgs(BIO *, struct dm_buf *, char **);
+
+extern int ds_fd; /* open file descriptor for data stream WHERE? */
+
+static char *root_names[] = {
+ "root",
+ "root.cpio",
+ "root.Z",
+ "root.cpio.Z",
+ 0
+};
+
+static char *reloc_names[] = {
+ "reloc",
+ "reloc.cpio",
+ "reloc.Z",
+ "reloc.cpio.Z",
+ 0
+};
+
+static int signal_received = 0;
+
+char **xpkg; /* array of transferred packages */
+int nxpkg;
+
+static char *allpkg[] = {
+ "all",
+ NULL
+};
+
+static struct dm_buf hdrbuf;
+static char *pinput, *nextpinput;
+
+int
+pkghead(char *device)
+{
+ char *pt;
+ int n;
+
+ cleanup();
+
+
+ if (device == NULL)
+ return (0);
+ else if ((device[0] == '/') && !isdir(device)) {
+ pkgdir = device;
+ return (0);
+ } else if ((pt = devattr(device, "pathname")) != NULL && !isdir(pt)) {
+ pkgdir = pt;
+ return (0);
+ }
+
+ /* check for datastream */
+ if (n = pkgtrans(device, (char *)0, allpkg, PT_SILENT|PT_INFO_ONLY,
+ NULL, NULL)) {
+ cleanup();
+ return (n);
+ }
+ /* pkgtrans has set pkgdir */
+ return (0);
+}
+
+static char *
+mgets(char *buf, int size)
+{
+ nextpinput = strchr(pinput, '\n');
+ if (nextpinput == NULL)
+ return (0);
+ *nextpinput = '\0';
+ if ((int)strlen(pinput) > size)
+ return (0);
+ (void) strncpy(buf, pinput, strlen(pinput));
+ buf[strlen(pinput)] = '\0';
+ pinput = nextpinput + 1;
+ return (buf);
+}
+/*
+ * Here we construct the package size summaries for the headers. The
+ * pkgmap file associated with fp must be rewound to the beginning of the
+ * file. Note that we read three values from pkgmap first line in order
+ * to get the *actual* size if this package is compressed.
+ * This returns
+ * 0 : error
+ * 2 : not a compressed package
+ * 3 : compressed package
+ * and sets has_comp_size to indicate whether or not this is a compressed
+ * package.
+ */
+static int
+rd_map_size(FILE *fp, int *npts, int *maxpsz, int *cmpsize)
+{
+ int n;
+ char line_buffer[MAP_STAT_SIZE];
+
+ /* First read the null terminated first line */
+ if (fgets(line_buffer, MAP_STAT_SIZE, fp) == NULL) {
+ progerr(pkg_gt(ERR_TRANSFER));
+ logerr(pkg_gt(MSG_NOSIZE));
+ (void) fclose(fp);
+ ecleanup();
+ return (0);
+ }
+
+ n = sscanf(line_buffer, ": %d %d %d", npts, maxpsz, cmpsize);
+
+ if (n == 3) /* A valid compressed package entry */
+ has_comp_size = 1;
+ else if (n == 2) /* A valid standard package entry */
+ has_comp_size = 0;
+ else { /* invalid entry */
+ progerr(pkg_gt(ERR_TRANSFER));
+ logerr(pkg_gt(MSG_NOSIZE));
+ (void) fclose(fp);
+ ecleanup();
+ return (0);
+ }
+
+ return (n);
+}
+
+/* will return 0, 1, 3, or 99 */
+static int
+_pkgtrans(char *device1, char *device2, char **pkg, int options,
+ keystore_handle_t keystore, char *keystore_alias)
+{
+ BIO *p7_bio = NULL;
+ EVP_PKEY *privkey = NULL;
+ PKCS7 *sec_pkcs7 = NULL;
+ PKCS7_SIGNER_INFO *sec_signerinfo = NULL;
+ PKG_ERR *err;
+ STACK_OF(X509) *cacerts = NULL;
+ STACK_OF(X509) *clcerts = NULL;
+ STACK_OF(X509) *sec_chain = NULL;
+ X509 *pubcert = NULL;
+ boolean_t making_sig = B_FALSE;
+ char *src, *dst;
+ int errflg, i, n;
+ struct dm_buf *hdr;
+
+ making_sig = (keystore != NULL) ? B_TRUE : B_FALSE;
+
+ if (making_sig) {
+
+ /* new error object */
+ err = pkgerr_new();
+
+ /* find matching cert and key */
+ if (find_key_cert_pair(err, keystore,
+ keystore_alias, &privkey, &pubcert) != 0) {
+ pkgerr(err);
+ pkgerr_free(err);
+ return (1);
+ }
+
+ /* get CA certificates */
+ if (find_ca_certs(err, keystore, &cacerts) != 0) {
+ pkgerr(err);
+ pkgerr_free(err);
+ return (1);
+ }
+
+ /* get CL (aka "chain") certificates */
+ if (find_cl_certs(err, keystore, &clcerts) != 0) {
+ pkgerr(err);
+ pkgerr_free(err);
+ return (1);
+ }
+
+ /* initialize PKCS7 object to be filled in later */
+ sec_pkcs7 = PKCS7_new();
+ PKCS7_set_type(sec_pkcs7, NID_pkcs7_signed);
+ sec_signerinfo = PKCS7_add_signature(sec_pkcs7,
+ pubcert, privkey, EVP_sha1());
+
+ if (sec_signerinfo == NULL) {
+ progerr(gettext(ERR_SEC), keystore_alias);
+ ERR_print_errors_fp(stderr);
+ pkgerr_free(err);
+ return (1);
+ }
+
+ /* add signer cert into signature */
+ PKCS7_add_certificate(sec_pkcs7, pubcert);
+
+ /* attempt to resolve cert chain starting at the signer cert */
+ if (get_cert_chain(err, pubcert, clcerts, cacerts,
+ &sec_chain) != 0) {
+ pkgerr(err);
+ pkgerr_free(err);
+ return (1);
+ }
+
+ /*
+ * add the verification chain of certs into the signature.
+ * The first cert is the user cert, which we don't need,
+ * since it's baked in already, so skip it
+ */
+ for (i = 1; i < sk_X509_num(sec_chain); i++) {
+ PKCS7_add_certificate(sec_pkcs7,
+ sk_X509_value(sec_chain, i));
+ }
+
+ pkgerr_free(err);
+ err = NULL;
+ }
+
+ if (signal_received > 0) {
+ return (1);
+ }
+
+ /* transfer spool to appropriate device */
+ if (devtype(device1, &srcdev)) {
+ progerr(pkg_gt(ERR_TRANSFER));
+ logerr(pkg_gt(MSG_BADDEV), device1);
+ return (1);
+ }
+ srcdev.rdonly++;
+
+ /* check for datastream */
+ ids_name = NULL;
+ if (srcdev.bdevice) {
+ if (n = _getvol(srcdev.bdevice, NULL, NULL,
+ pkg_gt("Insert %v into %p."), srcdev.norewind)) {
+ cleanup();
+ if (n == 3)
+ return (3);
+ progerr(pkg_gt(ERR_TRANSFER));
+ logerr(pkg_gt(MSG_GETVOL));
+ return (1);
+ }
+ if (ds_readbuf(srcdev.cdevice))
+ ids_name = srcdev.cdevice;
+ }
+
+ if (srcdev.cdevice && !srcdev.bdevice)
+ ids_name = srcdev.cdevice;
+ else if (srcdev.pathname) {
+ ids_name = srcdev.pathname;
+ if (access(ids_name, 0) == -1) {
+ progerr(ERR_TRANSFER);
+ logerr(pkg_gt(MSG_GETVOL));
+ return (1);
+ }
+ }
+
+ if (!ids_name && device2 == (char *)0) {
+ if (n = pkgmount(&srcdev, NULL, 1, 0, 0)) {
+ cleanup();
+ return (n);
+ }
+ if (srcdev.mount && *srcdev.mount)
+ pkgdir = strdup(srcdev.mount);
+ return (0);
+ }
+
+ if (ids_name && device2 == (char *)0) {
+ tmppath = tmpnam(NULL);
+ tmppath = strdup(tmppath);
+ if (tmppath == NULL) {
+ progerr(pkg_gt(ERR_TRANSFER));
+ logerr(pkg_gt(MSG_MEM));
+ return (1);
+ }
+ if (mkdir(tmppath, 0755)) {
+ progerr(pkg_gt(ERR_TRANSFER));
+ logerr(pkg_gt(MSG_MKDIR), tmppath);
+ return (1);
+ }
+ device2 = tmppath;
+ }
+
+ if (devtype(device2, &dstdev)) {
+ progerr(pkg_gt(ERR_TRANSFER));
+ logerr(pkg_gt(MSG_BADDEV), device2);
+ return (1);
+ }
+
+ if ((srcdev.cdevice && dstdev.cdevice) &&
+ strcmp(srcdev.cdevice, dstdev.cdevice) == 0) {
+ progerr(pkg_gt(ERR_TRANSFER));
+ logerr(pkg_gt(MSG_SAMEDEV));
+ return (1);
+ }
+
+ ods_name = NULL;
+ if (dstdev.cdevice && !dstdev.bdevice || dstdev.pathname)
+ options |= PT_ODTSTREAM;
+
+ if (options & PT_ODTSTREAM) {
+ if (!((ods_name = dstdev.cdevice) != NULL ||
+ (ods_name = dstdev.pathname) != NULL)) {
+ progerr(pkg_gt(ERR_TRANSFER));
+ logerr(pkg_gt(MSG_BADDEV), device2);
+ return (1);
+ }
+ if (ids_name) {
+ progerr(pkg_gt(ERR_TRANSFER));
+ logerr(pkg_gt(MSG_TWODSTREAM));
+ return (1);
+ }
+ } else {
+ /*
+ * output device isn't a stream. If we're making a signed
+ * package, then fail, since we can't make signed,
+ * non-stream pkgs
+ */
+ if (making_sig) {
+ progerr(pkg_gt(ERR_TRANSFER));
+ logerr(pkg_gt(ERR_CANTSIGN));
+ return (1);
+ }
+ }
+
+ if ((srcdev.dirname && dstdev.dirname) &&
+ strcmp(srcdev.dirname, dstdev.dirname) == 0) {
+ progerr(pkg_gt(ERR_TRANSFER));
+ logerr(pkg_gt(MSG_SAMEDEV));
+ return (1);
+ }
+
+ if ((srcdev.pathname && dstdev.pathname) &&
+ strcmp(srcdev.pathname, dstdev.pathname) == 0) {
+ progerr(pkg_gt(ERR_TRANSFER));
+ logerr(pkg_gt(MSG_SAMEDEV));
+ return (1);
+ }
+
+ if (signal_received > 0) {
+ return (1);
+ }
+
+ if (ids_name) {
+ if (srcdev.cdevice && !srcdev.bdevice &&
+ (n = _getvol(srcdev.cdevice, NULL, NULL, NULL,
+ srcdev.norewind))) {
+ cleanup();
+ if (n == 3)
+ return (3);
+ progerr(pkg_gt(ERR_TRANSFER));
+ logerr(pkg_gt(MSG_GETVOL));
+ return (1);
+ }
+ if (srcdev.dirname = tmpnam(NULL))
+ tmpdir = srcdev.dirname = strdup(srcdev.dirname);
+
+ if ((srcdev.dirname == NULL) || mkdir(srcdev.dirname, 0755) ||
+ chdir(srcdev.dirname)) {
+ progerr(pkg_gt(ERR_TRANSFER));
+ logerr(pkg_gt(MSG_NOTEMP), srcdev.dirname);
+ cleanup();
+ return (1);
+ }
+ if (ds_init(ids_name, pkg, srcdev.norewind)) {
+ cleanup();
+ return (1);
+ }
+ } else if (srcdev.mount) {
+ if (n = pkgmount(&srcdev, NULL, 1, 0, 0)) {
+ cleanup();
+ return (n);
+ }
+ }
+
+ src = srcdev.dirname;
+ dst = dstdev.dirname;
+
+ if (chdir(src)) {
+ progerr(pkg_gt(ERR_TRANSFER));
+ logerr(pkg_gt(MSG_CHDIR), src);
+ cleanup();
+ return (1);
+ }
+
+ if (signal_received > 0) {
+ return (1);
+ }
+
+ xpkg = pkg = gpkglist(src, pkg, NULL);
+ if (!pkg) {
+ progerr(pkg_gt(ERR_TRANSFER));
+ logerr(pkg_gt(MSG_NOPKGS), src);
+ cleanup();
+ return (1);
+ }
+
+ for (nxpkg = 0; pkg[nxpkg]; /* void */) {
+ nxpkg++; /* count */
+ }
+
+ if (ids_name) {
+ ds_order(pkg); /* order requests */
+ }
+
+ if (signal_received > 0) {
+ return (1);
+ }
+
+ if (options & PT_ODTSTREAM) {
+ char line[128];
+
+ if (!dstdev.pathname &&
+ (n = _getvol(ods_name, NULL, DM_FORMAT, NULL,
+ dstdev.norewind))) {
+ cleanup();
+ if (n == 3)
+ return (3);
+ progerr(pkg_gt(ERR_TRANSFER));
+ logerr(pkg_gt(MSG_GETVOL));
+ return (1);
+ }
+ if ((hdr = genheader(src, ods_name, pkg)) == NULL) {
+ cleanup();
+ return (1);
+ }
+ if (making_sig) {
+ /* start up signature data stream */
+ PKCS7_content_new(sec_pkcs7, NID_pkcs7_data);
+ PKCS7_set_detached(sec_pkcs7, 1);
+ p7_bio = PKCS7_dataInit(sec_pkcs7, NULL);
+
+ /*
+ * Here we generate all the data that will go into
+ * the package, and send it through the signature
+ * generator, essentially calculating the signature
+ * of the entire package so we can place it in the
+ * header. Otherwise we'd have to place it at the end
+ * of the pkg, which would break the ABI
+ */
+ if (!(options & PT_SILENT)) {
+ (void) fprintf(stderr, pkg_gt(MSG_SIGNING),
+ get_subject_display_name(pubcert));
+ }
+ if (dump_hdr_and_pkgs(p7_bio, hdr, pkg) != 0) {
+ progerr(gettext(ERR_NOGEN));
+ logerr(pkg_gt(MSG_GETVOL));
+ cleanup();
+ return (1);
+
+ }
+
+ BIO_flush(p7_bio);
+
+ /*
+ * now generate PKCS7 signature
+ */
+ if (!PKCS7_dataFinal(sec_pkcs7, p7_bio)) {
+ progerr(gettext(ERR_NOGEN));
+ logerr(pkg_gt(MSG_GETVOL));
+ cleanup();
+ return (1);
+ }
+
+ BIO_free(p7_bio);
+ }
+
+ /* write out header to stream, which includes signature */
+ if (wdsheader(hdr, src, ods_name, pkg, sec_pkcs7)) {
+ cleanup();
+ return (1);
+ }
+
+ if (sec_pkcs7 != NULL) {
+ /* nuke in-memory signature for safety */
+ PKCS7_free(sec_pkcs7);
+ sec_pkcs7 = NULL;
+ }
+
+ ds_volno = 1; /* number of volumes in datastream */
+ pinput = hdrbuf.text_buffer;
+ /* skip past first line in header */
+ (void) mgets(line, 128);
+ }
+
+ if (signal_received > 0) {
+ return (1);
+ }
+
+ errflg = 0;
+
+ for (i = 0; pkg[i]; i++) {
+
+ if (signal_received > 0) {
+ return (1);
+ }
+
+ if (!(options & PT_ODTSTREAM) && dstdev.mount) {
+ if (n = pkgmount(&dstdev, NULL, 0, 0, 1)) {
+ cleanup();
+ return (n);
+ }
+ }
+ if (errflg = pkgxfer(pkg[i], options)) {
+ pkg[i] = NULL;
+ if ((options & PT_ODTSTREAM) || (errflg != 2))
+ break;
+ } else if (strcmp(dstinst, pkg[i]))
+ pkg[i] = strdup(dstinst);
+ }
+
+ if (!(options & PT_ODTSTREAM) && dst) {
+ pkgdir = strdup(dst);
+ }
+
+ /*
+ * No cleanup of temporary directories created in this
+ * function is done here. The calling function must do
+ * the cleanup.
+ */
+
+ return (signal_received > 0 ? 1 : errflg);
+}
+
+int
+pkgtrans(char *device1, char *device2, char **pkg, int options,
+ keystore_handle_t keystore, char *keystore_alias)
+{
+ int r;
+ struct sigaction nact;
+ struct sigaction oact;
+
+ /*
+ * setup signal handlers for SIGINT and SIGHUP and release hold
+ */
+
+ /* hold SIGINT/SIGHUP interrupts */
+
+ (void) sighold(SIGHUP);
+ (void) sighold(SIGINT);
+
+ /* hook SIGINT to sigtrap */
+
+ nact.sa_handler = sigtrap;
+ nact.sa_flags = SA_RESTART;
+ (void) sigemptyset(&nact.sa_mask);
+
+ if (sigaction(SIGINT, &nact, &oact) < 0) {
+ sigintHandler = SIG_DFL;
+ } else {
+ sigintHandler = oact.sa_handler;
+ }
+
+ /* hook SIGHUP to sigtrap */
+
+ nact.sa_handler = sigtrap;
+ nact.sa_flags = SA_RESTART;
+ (void) sigemptyset(&nact.sa_mask);
+
+ if (sigaction(SIGHUP, &nact, &oact) < 0) {
+ sighupHandler = SIG_DFL;
+ } else {
+ sighupHandler = oact.sa_handler;
+ }
+
+ /* reset signal received count */
+
+ signal_received = 0;
+
+ /* release hold on signals */
+
+ (void) sigrelse(SIGHUP);
+ (void) sigrelse(SIGINT);
+
+ /*
+ * perform the package translation
+ */
+
+ r = _pkgtrans(device1, device2, pkg, options, keystore, keystore_alias);
+
+ /*
+ * reset signal handlers
+ */
+
+ /* hold SIGINT/SIGHUP interrupts */
+
+ (void) sighold(SIGHUP);
+ (void) sighold(SIGINT);
+
+ /* reset SIGINT */
+
+ nact.sa_handler = sigintHandler;
+ nact.sa_flags = SA_RESTART;
+ (void) sigemptyset(&nact.sa_mask);
+
+ (void) sigaction(SIGINT, &nact, (struct sigaction *)NULL);
+
+ /* reset SIGHUP */
+
+ nact.sa_handler = sighupHandler;
+ nact.sa_flags = SA_RESTART;
+ (void) sigemptyset(&nact.sa_mask);
+
+ (void) sigaction(SIGHUP, &nact, (struct sigaction *)NULL);
+
+ /* if signal received and pkgtrans returned error, call cleanup */
+
+ if (signal_received > 0) {
+ if (r != 0) {
+ cleanup();
+ }
+ (void) kill(getpid(), SIGINT);
+ }
+
+ /* release hold on signals */
+
+ (void) sigrelse(SIGHUP);
+ (void) sigrelse(SIGINT);
+
+ return (r);
+}
+
+/*
+ * This function concatenates append to the text described in the buf_ctrl
+ * structure. This code modifies data in this structure and handles all
+ * allocation issues. It returns '0' if everything was successful and '1'
+ * if not.
+ */
+static int
+cat_and_count(struct dm_buf *buf_ctrl, char *append)
+{
+
+ /* keep allocating until we have enough room to hold string */
+ while ((buf_ctrl->offset + (int)strlen(append))
+ >= buf_ctrl->allocation) {
+ /* reallocate (and maybe move) text buffer */
+ if ((buf_ctrl->text_buffer =
+ (char *)realloc(buf_ctrl->text_buffer,
+ buf_ctrl->allocation + BLK_SIZE)) == NULL) {
+ progerr(pkg_gt(ERR_TRANSFER));
+ logerr(pkg_gt(MSG_MEM));
+ free(buf_ctrl->text_buffer);
+ return (1);
+ }
+
+ /* clear the new memory */
+ (void) memset(buf_ctrl->text_buffer +
+ buf_ctrl->allocation, '\0', BLK_SIZE);
+
+ /* adjust total allocation */
+ buf_ctrl->allocation += BLK_SIZE;
+ }
+
+ /* append new string to end of buffer */
+ while (*append) {
+ *(buf_ctrl->text_buffer + buf_ctrl->offset) = *append++;
+ (buf_ctrl->offset)++;
+ }
+
+ return (0);
+}
+
+static struct dm_buf *
+genheader(char *src, char *device, char **pkg)
+{
+
+ FILE *fp;
+ char path[MAXPATHLEN], tmp_entry[ENTRY_MAX];
+ int i, n, nparts, maxpsize;
+ int partcnt, totsize;
+ struct stat statbuf;
+
+ if ((hdrbuf.text_buffer = (char *)malloc(BLK_SIZE)) == NULL) {
+ progerr(pkg_gt(ERR_TRANSFER));
+ logerr(pkg_gt(MSG_MEM));
+ return (NULL);
+ }
+
+ /* clear the new memory */
+ (void) memset(hdrbuf.text_buffer, '\0', BLK_SIZE);
+
+ /* set up the buffer control structure for the header */
+ hdrbuf.offset = 0;
+ hdrbuf.allocation = BLK_SIZE;
+
+ (void) cat_and_count(&hdrbuf, HDR_PREFIX);
+ (void) cat_and_count(&hdrbuf, "\n");
+
+ nparts = maxpsize = 0;
+
+ totsize = 0;
+ for (i = 0; pkg[i]; i++) {
+ (void) snprintf(path, MAXPATHLEN, "%s/%s/%s",
+ src, pkg[i], PKGINFO);
+ if (stat(path, &statbuf) < 0) {
+ progerr(pkg_gt(ERR_TRANSFER));
+ logerr(pkg_gt(MSG_BADPKGINFO));
+ ecleanup();
+ return (NULL);
+ }
+ totsize += statbuf.st_size/BLK_SIZE + 1;
+ }
+
+ /*
+ * totsize contains number of blocks used by the pkginfo files
+ */
+ totsize += i/4 + 1;
+ if (dstdev.capacity && totsize > dstdev.capacity) {
+ progerr(pkg_gt(ERR_TRANSFER));
+ logerr(pkg_gt(MSG_NOSPACE), totsize, dstdev.capacity);
+ ecleanup();
+ return (NULL);
+ }
+
+ ds_volcnt = 1;
+ for (i = 0; pkg[i]; i++) {
+ partcnt = 0;
+ (void) snprintf(path, MAXPATHLEN, "%s/%s/%s",
+ src, pkg[i], PKGMAP);
+ if ((fp = fopen(path, "r")) == NULL) {
+ progerr(pkg_gt(ERR_TRANSFER));
+ logerr(pkg_gt(MSG_NOPKGMAP), pkg[i]);
+ ecleanup();
+ return (NULL);
+ }
+
+ /* Evaluate the first entry in pkgmap */
+ n = rd_map_size(fp, &nparts, &maxpsize, &compressedsize);
+
+ if (n == 3) /* It's a compressed package */
+ /* The header needs the *real* size */
+ maxpsize = compressedsize;
+ else if (n == 0) /* pkgmap is corrupt */
+ return (NULL);
+
+ if (dstdev.capacity && maxpsize > dstdev.capacity) {
+ progerr(pkg_gt(ERR_TRANSFER));
+ logerr(pkg_gt(MSG_NOSPACE));
+ (void) fclose(fp);
+ ecleanup();
+ return (NULL);
+ }
+
+ /* add pkg name, number of parts and the max part size */
+ if (snprintf(tmp_entry, ENTRY_MAX, "%s %d %d",
+ pkg[i], nparts, maxpsize) >= ENTRY_MAX) {
+ progerr(pkg_gt(ERR_TRANSFER));
+ logerr(pkg_gt(ERR_MEM));
+ (void) fclose(fp);
+ ecleanup();
+ return (NULL);
+ }
+ if (cat_and_count(&hdrbuf, tmp_entry)) {
+ progerr(pkg_gt(ERR_TRANSFER));
+ logerr(pkg_gt(MSG_MEM));
+ (void) fclose(fp);
+ ecleanup();
+ return (NULL);
+ }
+
+ totsize += nparts * maxpsize;
+ if (dstdev.capacity && dstdev.capacity < totsize) {
+ int lastpartcnt = 0;
+#if 0
+ if (i != 0) {
+ progerr(pkg_gt(ERR_TRANSFER));
+ logerr(pkg_gt(MSG_NOSPACE));
+ (void) fclose(fp);
+ ecleanup();
+ return (NULL);
+ }
+#endif /* 0 */
+
+ if (totsize)
+ totsize -= nparts * maxpsize;
+ while (partcnt < nparts) {
+ while (totsize <= dstdev.capacity &&
+ partcnt <= nparts) {
+ totsize += maxpsize;
+ partcnt++;
+ }
+ /* partcnt == 0 means skip to next volume */
+ if (partcnt)
+ partcnt--;
+ (void) snprintf(tmp_entry, ENTRY_MAX,
+ " %d", partcnt - lastpartcnt);
+ if (cat_and_count(&hdrbuf, tmp_entry)) {
+ progerr(pkg_gt(ERR_TRANSFER));
+ logerr(pkg_gt(MSG_MEM));
+ (void) fclose(fp);
+ ecleanup();
+ return (NULL);
+ }
+ ds_volcnt++;
+ totsize = 0;
+ lastpartcnt = partcnt;
+ }
+ /* first parts/volume number does not count */
+ ds_volcnt--;
+ }
+
+ if (cat_and_count(&hdrbuf, "\n")) {
+ progerr(pkg_gt(ERR_TRANSFER));
+ logerr(pkg_gt(MSG_MEM));
+ (void) fclose(fp);
+ ecleanup();
+ return (NULL);
+ }
+
+ (void) fclose(fp);
+ }
+
+ if (cat_and_count(&hdrbuf, HDR_SUFFIX) ||
+ cat_and_count(&hdrbuf, "\n")) {
+ progerr(pkg_gt(ERR_TRANSFER));
+ logerr(pkg_gt(MSG_MEM));
+ (void) fclose(fp);
+ ecleanup();
+ return (NULL);
+ }
+ return (&hdrbuf);
+}
+
+static int
+wdsheader(struct dm_buf *hdr, char *src, char *device, char **pkg, PKCS7 *sig)
+{
+ FILE *fp;
+ char path[PATH_MAX], tmp_entry[ENTRY_MAX],
+ tmp_file[L_tmpnam+1];
+ char srcpath[PATH_MAX];
+ int i, n;
+ int list_fd;
+ int block_cnt;
+ int len;
+ char cwd[MAXPATHLEN + 1];
+ boolean_t making_sig = B_FALSE;
+
+ making_sig = (sig != NULL) ? B_TRUE : B_FALSE;
+
+ (void) ds_close(0);
+ if (dstdev.pathname)
+ ds_fd = creat(device, 0644);
+ else
+ ds_fd = open(device, 1);
+
+ if (ds_fd < 0) {
+ progerr(pkg_gt(ERR_TRANSFER));
+ logerr(pkg_gt(MSG_OPEN), device, errno);
+ return (1);
+ }
+
+ if (ds_ginit(device) < 0) {
+ progerr(pkg_gt(ERR_TRANSFER));
+ logerr(pkg_gt(MSG_OPEN), device, errno);
+ (void) ds_close(0);
+ return (1);
+ }
+
+ /*
+ * The loop below assures compatibility with tapes that don't
+ * have a block size (e.g.: Exabyte) by forcing EOR at the end
+ * of each 512 bytes.
+ */
+ for (block_cnt = 0; block_cnt < hdr->allocation;
+ block_cnt += BLK_SIZE) {
+ write(ds_fd, (hdr->text_buffer + block_cnt), BLK_SIZE);
+ }
+
+ /*
+ * write the first cpio() archive to the datastream
+ * which should contain the pkginfo & pkgmap files
+ * for all packages
+ */
+ (void) tmpnam(tmp_file); /* temporary file name */
+ if ((list_fd = open(tmp_file, O_RDWR | O_CREAT)) == -1) {
+ progerr(pkg_gt(ERR_TRANSFER));
+ logerr(pkg_gt(MSG_NOTMPFIL));
+ return (1);
+ }
+
+ /*
+ * Create a cpio-compatible list of the requisite files in
+ * the temporary file.
+ */
+ if (!making_sig) {
+ for (i = 0; pkg[i]; i++) {
+ register ssize_t entry_size;
+
+ /*
+ * Copy pkginfo and pkgmap filenames into the
+ * temporary string allowing for the first line
+ * as a special case.
+ */
+ entry_size = sprintf(tmp_entry,
+ (i == 0) ? "%s/%s\n%s/%s" : "\n%s/%s\n%s/%s",
+ pkg[i], PKGINFO, pkg[i], PKGMAP);
+
+ if (write(list_fd, tmp_entry,
+ entry_size) != entry_size) {
+ progerr(pkg_gt(ERR_TRANSFER));
+ logerr(pkg_gt(MSG_NOTMPFIL));
+ (void) close(list_fd);
+ ecleanup();
+ return (1);
+ }
+ }
+
+ } else {
+ register ssize_t entry_size;
+
+ /*
+ * if we're making a signature, we must make a
+ * temporary area full of symlinks to the requisite
+ * files, plus an extra entry for the signature, so
+ * that cpio will put all files and signature in the
+ * same archive in a single invocation of cpio.
+ */
+ tmpsymdir = xstrdup(tmpnam(NULL));
+
+ if (mkdir(tmpsymdir, S_IRWXU)) {
+ progerr(pkg_gt(ERR_TRANSFER));
+ logerr(pkg_gt(MSG_MKDIR), tmpsymdir);
+ return (1);
+ }
+
+ /* generate the signature */
+ if (((len = snprintf(path, PATH_MAX, "%s/%s",
+ tmpsymdir, SIGNATURE_FILENAME)) >= PATH_MAX) ||
+ len < 0) {
+ progerr(pkg_gt(ERR_TRANSFER));
+ logerr(pkg_gt(MSG_NOTMPFIL), tmpsymdir);
+ cleanup();
+ return (1);
+ }
+
+ if ((fp = fopen(path, "w")) == NULL) {
+ progerr(pkg_gt(ERR_TRANSFER));
+ logerr(pkg_gt(MSG_NOTMPFIL), path);
+ cleanup();
+ return (1);
+ }
+ PEM_write_PKCS7(fp, sig);
+ (void) fclose(fp);
+
+ for (i = 0; pkg[i]; i++) {
+ sprintf(path, "%s/%s", tmpsymdir, pkg[i]);
+ if (mkdir(path, 0755)) {
+ progerr(pkg_gt(ERR_TRANSFER));
+ logerr(pkg_gt(MSG_MKDIR), path);
+ cleanup();
+ return (1);
+ }
+ sprintf(path, "%s/%s/%s", tmpsymdir,
+ pkg[i], PKGINFO);
+ sprintf(srcpath, "%s/%s/%s", src, pkg[i], PKGINFO);
+ if (symlink(srcpath, path) != 0) {
+ progerr(pkg_gt(ERR_TRANSFER));
+ logerr(pkg_gt(MSG_SYMLINK), path, srcpath);
+ cleanup();
+ return (1);
+ }
+
+ sprintf(path, "%s/%s/%s", tmpsymdir,
+ pkg[i], PKGMAP);
+ sprintf(srcpath, "%s/%s/%s", src, pkg[i], PKGMAP);
+ if (symlink(srcpath, path) != 0) {
+ progerr(pkg_gt(ERR_TRANSFER));
+ logerr(pkg_gt(MSG_SYMLINK), path, srcpath);
+ cleanup();
+ return (1);
+ }
+
+ /*
+ * Copy pkginfo and pkgmap filenames into the
+ * temporary string allowing for the first line
+ * as a special case.
+ */
+ entry_size = sprintf(tmp_entry,
+ (i == 0) ? "%s/%s\n%s/%s" : "\n%s/%s\n%s/%s",
+ pkg[i], PKGINFO, pkg[i], PKGMAP);
+
+ if (write(list_fd, tmp_entry,
+ entry_size) != entry_size) {
+ progerr(pkg_gt(ERR_TRANSFER));
+ logerr(pkg_gt(MSG_NOTMPFIL));
+ (void) close(list_fd);
+ ecleanup();
+ cleanup();
+ return (1);
+ }
+ }
+
+ /* add signature to list of files */
+ entry_size = sprintf(tmp_entry, "\n%s", SIGNATURE_FILENAME);
+ if (write(list_fd, tmp_entry, entry_size) != entry_size) {
+ progerr(pkg_gt(ERR_TRANSFER));
+ logerr(pkg_gt(MSG_NOTMPFIL), tmp_file);
+ (void) close(list_fd);
+ ecleanup();
+ cleanup();
+ return (1);
+ }
+ }
+
+ (void) lseek(list_fd, 0, SEEK_SET);
+
+ if (!making_sig) {
+#ifndef SUNOS41
+ (void) sprintf(tmp_entry, "%s -ocD -C %d",
+ CPIOPROC, (int)BLK_SIZE);
+#else
+ (void) sprintf(tmp_entry, "%s -oc -C %d",
+ CPIOPROC, (int)BLK_SIZE);
+#endif
+ } else {
+ /*
+ * when making a signature, we must make sure to follow
+ * symlinks during the cpio so that we don't archive
+ * the links themselves
+ */
+#ifndef SUNOS41
+ (void) sprintf(tmp_entry, "%s -ocDL -C %d",
+ CPIOPROC, (int)BLK_SIZE);
+#else
+ (void) sprintf(tmp_entry, "%s -ocL -C %d",
+ CPIOPROC, (int)BLK_SIZE);
+#endif
+ }
+
+ if (making_sig) {
+ /* save cwd and change to symlink dir for cpio invocation */
+ if (getcwd(cwd, MAXPATHLEN + 1) == NULL) {
+ logerr(pkg_gt(ERR_GETWD));
+ progerr(pkg_gt(ERR_TRANSFER));
+ cleanup();
+ return (1);
+ }
+
+ if (chdir(tmpsymdir)) {
+ progerr(pkg_gt(ERR_TRANSFER));
+ logerr(pkg_gt(MSG_CHDIR), tmpsymdir);
+ cleanup();
+ return (1);
+ }
+ }
+
+ if (n = esystem(tmp_entry, list_fd, ds_fd)) {
+ rpterr();
+ progerr(pkg_gt(ERR_TRANSFER));
+ logerr(pkg_gt(MSG_CMDFAIL), tmp_entry, n);
+ (void) close(list_fd);
+ (void) unlink(tmp_file);
+ cleanup();
+ return (1);
+ }
+
+ (void) close(list_fd);
+ (void) unlink(tmp_file);
+
+ if (making_sig) {
+ /* change to back to src dir for subsequent operations */
+ if (chdir(cwd)) {
+ progerr(pkg_gt(ERR_TRANSFER));
+ logerr(pkg_gt(MSG_CHDIR), cwd);
+ cleanup();
+ return (1);
+ }
+ }
+ return (0);
+}
+
+static int
+ckoverwrite(char *dir, char *inst, int options)
+{
+ char path[PATH_MAX];
+
+ (void) sprintf(path, "%s/%s", dir, inst);
+ if (access(path, 0) == 0) {
+ if (options & PT_OVERWRITE)
+ return (rrmdir(path));
+ progerr(pkg_gt(ERR_TRANSFER));
+ logerr(pkg_gt(MSG_EXISTS), path);
+ return (1);
+ }
+ return (0);
+}
+
+static int
+pkgxfer(char *srcinst, int options)
+{
+ int r;
+ struct pkginfo info;
+ FILE *fp, *pp;
+ char *pt, *src, *dst;
+ char dstdir[PATH_MAX],
+ temp[PATH_MAX],
+ srcdir[PATH_MAX],
+ cmd[CMDSIZE],
+ pkgname[NON_ABI_NAMELNGTH];
+ int i, n, part, nparts, maxpartsize, curpartcnt, iscomp;
+ char volnos[128], tmpvol[128];
+ struct statvfs64 svfsb;
+ longlong_t free_blocks;
+ struct stat srcstat;
+
+ info.pkginst = NULL; /* required initialization */
+
+ /*
+ * when this routine is entered, the first part of
+ * the package to transfer is already available in
+ * the directory indicated by 'src' --- unless the
+ * source device is a datstream, in which case only
+ * the pkginfo and pkgmap files are available in 'src'
+ */
+ src = srcdev.dirname;
+ dst = dstdev.dirname;
+
+ if (!(options & PT_SILENT))
+ (void) fprintf(stderr, pkg_gt(MSG_TRANSFER), srcinst);
+ (void) strcpy(dstinst, srcinst);
+
+ if (!(options & PT_ODTSTREAM)) {
+ /* destination is a (possibly mounted) directory */
+ (void) sprintf(dstdir, "%s/%s", dst, dstinst);
+
+ /*
+ * need to check destination directory to assure
+ * that we will not be duplicating a package which
+ * already resides there (though we are allowed to
+ * overwrite the same version)
+ */
+ pkgdir = src;
+ if (fpkginfo(&info, srcinst)) {
+ progerr(pkg_gt(ERR_TRANSFER));
+ logerr(pkg_gt(MSG_NOEXISTS), srcinst);
+ (void) fpkginfo(&info, NULL);
+ return (1);
+ }
+ pkgdir = dst;
+
+ (void) strcpy(temp, srcinst);
+ if (pt = strchr(temp, '.'))
+ *pt = '\0';
+ (void) strcat(temp, ".*");
+
+ if (pt = fpkginst(temp, info.arch, info.version)) {
+ /*
+ * the same instance already exists, although
+ * its pkgid might be different
+ */
+ if (options & PT_OVERWRITE) {
+ (void) strcpy(dstinst, pt);
+ (void) sprintf(dstdir, "%s/%s", dst, dstinst);
+ } else {
+ progerr(pkg_gt(ERR_TRANSFER));
+ logerr(pkg_gt(MSG_DUPVERS), srcinst);
+ (void) fpkginfo(&info, NULL);
+ (void) fpkginst(NULL);
+ return (2);
+ }
+ } else if (options & PT_RENAME) {
+ /*
+ * find next available instance by appending numbers
+ * to the package abbreviation until the instance
+ * does not exist in the destination directory
+ */
+ if (pt = strchr(temp, '.'))
+ *pt = '\0';
+ for (i = 2; (access(dstdir, 0) == 0); i++) {
+ (void) sprintf(dstinst, "%s.%d", temp, i);
+ (void) sprintf(dstdir, "%s/%s", dst, dstinst);
+ }
+ } else if (options & PT_OVERWRITE) {
+ /*
+ * we're allowed to overwrite, but there seems
+ * to be no valid package to overwrite, and we are
+ * not allowed to rename the destination, so act
+ * as if we weren't given permission to overwrite
+ * --- this keeps us from removing a destination
+ * instance which is named the same as the source
+ * instance, but really reflects a different pkg!
+ */
+ options &= (~PT_OVERWRITE);
+ }
+ (void) fpkginfo(&info, NULL);
+ (void) fpkginst(NULL);
+
+ if (ckoverwrite(dst, dstinst, options))
+ return (2);
+
+ if (isdir(dstdir) && mkdir(dstdir, 0755)) {
+ progerr(pkg_gt(ERR_TRANSFER));
+ logerr(pkg_gt(MSG_MKDIR), dstdir);
+ return (1);
+ }
+
+ (void) sprintf(srcdir, "%s/%s", src, srcinst);
+ if (stat(srcdir, &srcstat) != -1) {
+ if (chmod(dstdir, (srcstat.st_mode & S_IAMB)) == -1) {
+ progerr(pkg_gt(ERR_TRANSFER));
+ logerr(pkg_gt(MSG_CHMODDIR), dstdir);
+ return (1);
+ }
+ } else {
+ progerr(pkg_gt(ERR_TRANSFER));
+ logerr(pkg_gt(MSG_STATDIR), srcdir);
+ return (1);
+ }
+ }
+
+ if (!(options & PT_SILENT) && strcmp(dstinst, srcinst))
+ (void) fprintf(stderr, pkg_gt(MSG_RENAME), dstinst);
+
+ (void) sprintf(srcdir, "%s/%s", src, srcinst);
+ if (chdir(srcdir)) {
+ progerr(pkg_gt(ERR_TRANSFER));
+ logerr(pkg_gt(MSG_CHDIR), srcdir);
+ return (1);
+ }
+
+ if (ids_name) { /* unpack the datatstream into a directory */
+ /*
+ * transfer pkginfo & pkgmap first
+ */
+ (void) sprintf(cmd, "%s -pudm %s", CPIOPROC, dstdir);
+ if ((pp = epopen(cmd, "w")) == NULL) {
+ rpterr();
+ progerr(pkg_gt(ERR_TRANSFER));
+ logerr(pkg_gt(MSG_POPEN), cmd, errno);
+ return (1);
+ }
+ (void) fprintf(pp, "%s\n%s\n", PKGINFO, PKGMAP);
+
+ sighold(SIGINT);
+ sighold(SIGHUP);
+ r = epclose(pp);
+ sigrelse(SIGINT);
+ sigrelse(SIGHUP);
+
+ if (r != 0) {
+ rpterr();
+ progerr(pkg_gt(ERR_TRANSFER));
+ logerr(pkg_gt(MSG_PCLOSE), cmd, errno);
+ return (1);
+ }
+
+ if (options & PT_INFO_ONLY)
+ return (0); /* don't transfer objects */
+
+ if (chdir(dstdir)) {
+ progerr(pkg_gt(ERR_TRANSFER));
+ logerr(pkg_gt(MSG_CHDIR), dstdir);
+ return (1);
+ }
+
+ /*
+ * for each part of the package, use cpio() to
+ * unpack the archive into the destination directory
+ */
+ nparts = ds_findpkg(srcdev.cdevice, srcinst);
+ if (nparts < 0) {
+ progerr(pkg_gt(ERR_TRANSFER));
+ return (1);
+ }
+ for (part = 1; part <= nparts; /* void */) {
+ if (ds_getpkg(srcdev.cdevice, part, dstdir)) {
+ progerr(pkg_gt(ERR_TRANSFER));
+ return (1);
+ }
+ part++;
+ if (dstdev.mount) {
+ (void) chdir("/");
+ if (pkgumount(&dstdev))
+ return (1);
+ if (part <= nparts) {
+ if (n = pkgmount(&dstdev, NULL, part+1,
+ nparts, 1))
+ return (n);
+ if (ckoverwrite(dst, dstinst, options))
+ return (1);
+ if (isdir(dstdir) &&
+ mkdir(dstdir, 0755)) {
+ progerr(
+ pkg_gt(ERR_TRANSFER));
+ logerr(pkg_gt(MSG_MKDIR),
+ dstdir);
+ return (1);
+ }
+ /*
+ * since volume is removable, each part
+ * must contain a duplicate of the
+ * pkginfo file to properly identify the
+ * volume
+ */
+ if (chdir(srcdir)) {
+ progerr(
+ pkg_gt(ERR_TRANSFER));
+ logerr(pkg_gt(MSG_CHDIR),
+ srcdir);
+ return (1);
+ }
+ if ((pp = epopen(cmd, "w")) == NULL) {
+ rpterr();
+ progerr(
+ pkg_gt(ERR_TRANSFER));
+ logerr(pkg_gt(MSG_POPEN),
+ cmd, errno);
+ return (1);
+ }
+ (void) fprintf(pp, "pkginfo");
+
+ sighold(SIGINT);
+ sighold(SIGHUP);
+ r = epclose(pp);
+ sigrelse(SIGINT);
+ sigrelse(SIGHUP);
+
+ if (r != 0) {
+ rpterr();
+ progerr(
+ pkg_gt(ERR_TRANSFER));
+ logerr(pkg_gt(MSG_PCLOSE),
+ cmd, errno);
+ return (1);
+ }
+ if (chdir(dstdir)) {
+ progerr(
+ pkg_gt(ERR_TRANSFER));
+ logerr(pkg_gt(MSG_CHDIR),
+ dstdir);
+ return (1);
+ }
+ }
+ }
+ }
+ return (0);
+ }
+
+ if ((fp = fopen(PKGMAP, "r")) == NULL) {
+ progerr(pkg_gt(ERR_TRANSFER));
+ logerr(pkg_gt(MSG_NOPKGMAP), srcinst);
+ return (1);
+ }
+
+ nparts = 1;
+ if (!rd_map_size(fp, &nparts, &maxpartsize, &compressedsize))
+ return (1);
+ else
+ (void) fclose(fp);
+
+ if (srcdev.mount) {
+ if (ckvolseq(srcdir, 1, nparts)) {
+ progerr(pkg_gt(ERR_TRANSFER));
+ logerr(pkg_gt(MSG_SEQUENCE));
+ return (1);
+ }
+ }
+
+ /* write each part of this package */
+ if (options & PT_ODTSTREAM) {
+ char line[128];
+ (void) mgets(line, 128);
+ curpartcnt = -1;
+ if (sscanf(line, "%s %d %d %[ 0-9]", &pkgname, &nparts,
+ &maxpartsize, volnos) == 4) {
+ sscanf(volnos, "%d %[ 0-9]", &curpartcnt, tmpvol);
+ strcpy(volnos, tmpvol);
+ }
+ }
+
+ for (part = 1; part <= nparts; /* void */) {
+ if (curpartcnt == 0 && (options & PT_ODTSTREAM)) {
+ char prompt[128];
+ int index;
+ ds_volno++;
+ (void) ds_close(0);
+ (void) sprintf(prompt,
+ pkg_gt("Insert %%v %d of %d into %%p"),
+ ds_volno, ds_volcnt);
+ if (n = getvol(ods_name, NULL, DM_FORMAT, prompt))
+ return (n);
+ if ((ds_fd = open(dstdev.cdevice, O_WRONLY)) < 0) {
+ progerr(pkg_gt(ERR_TRANSFER));
+ logerr(pkg_gt(MSG_OPEN), dstdev.cdevice,
+ errno);
+ return (1);
+ }
+ if (ds_ginit(dstdev.cdevice) < 0) {
+ progerr(pkg_gt(ERR_TRANSFER));
+ logerr(pkg_gt(MSG_OPEN), dstdev.cdevice,
+ errno);
+ (void) ds_close(0);
+ return (1);
+ }
+
+ (void) sscanf(volnos, "%d %[ 0-9]", &index, tmpvol);
+ (void) strcpy(volnos, tmpvol);
+ curpartcnt += index;
+ }
+
+ if (options & PT_INFO_ONLY)
+ nparts = 0;
+
+ if (part == 1) {
+ (void) sprintf(cmd, "find %s %s", PKGINFO, PKGMAP);
+ if (nparts && (isdir(INSTALL) == 0)) {
+ (void) strcat(cmd, " ");
+ (void) strcat(cmd, INSTALL);
+ }
+ } else
+ (void) sprintf(cmd, "find %s", PKGINFO);
+
+ if (nparts > 1) {
+ (void) sprintf(temp, "%s.%d", RELOC, part);
+ if (iscpio(temp, &iscomp) || isdir(temp) == 0) {
+ (void) strcat(cmd, " ");
+ (void) strcat(cmd, temp);
+ }
+ (void) sprintf(temp, "%s.%d", ROOT, part);
+ if (iscpio(temp, &iscomp) || isdir(temp) == 0) {
+ (void) strcat(cmd, " ");
+ (void) strcat(cmd, temp);
+ }
+ (void) sprintf(temp, "%s.%d", ARCHIVE, part);
+ if (isdir(temp) == 0) {
+ (void) strcat(cmd, " ");
+ (void) strcat(cmd, temp);
+ }
+ } else if (nparts) {
+ for (i = 0; reloc_names[i] != NULL; i++) {
+ if (iscpio(reloc_names[i], &iscomp) ||
+ isdir(reloc_names[i]) == 0) {
+ (void) strcat(cmd, " ");
+ (void) strcat(cmd, reloc_names[i]);
+ }
+ }
+ for (i = 0; root_names[i] != NULL; i++) {
+ if (iscpio(root_names[i], &iscomp) ||
+ isdir(root_names[i]) == 0) {
+ (void) strcat(cmd, " ");
+ (void) strcat(cmd, root_names[i]);
+ }
+ }
+ if (isdir(ARCHIVE) == 0) {
+ (void) strcat(cmd, " ");
+ (void) strcat(cmd, ARCHIVE);
+ }
+ }
+ if (options & PT_ODTSTREAM) {
+#ifndef SUNOS41
+ (void) sprintf(cmd+strlen(cmd),
+ " -print | %s -ocD -C %d",
+#else
+ (void) sprintf(cmd+strlen(cmd),
+ " -print | %s -oc -C %d",
+#endif
+ CPIOPROC, (int)BLK_SIZE);
+ } else {
+ if (statvfs64(dstdir, &svfsb) == -1) {
+ progerr(pkg_gt(ERR_TRANSFER));
+ logerr(pkg_gt(MSG_STATVFS), dstdir, errno);
+ return (1);
+ }
+
+ free_blocks = (((long)svfsb.f_frsize > 0) ?
+ howmany(svfsb.f_frsize, DEV_BSIZE) :
+ howmany(svfsb.f_bsize, DEV_BSIZE)) * svfsb.f_bavail;
+
+ if ((has_comp_size ? compressedsize : maxpartsize) >
+ free_blocks) {
+ progerr(pkg_gt(ERR_TRANSFER));
+ logerr(pkg_gt(MSG_NOSPACE));
+ return (1);
+ }
+ (void) sprintf(cmd+strlen(cmd), " -print | %s -pdum %s",
+ CPIOPROC, dstdir);
+ }
+
+ n = esystem(cmd, -1, (options & PT_ODTSTREAM) ? ds_fd : -1);
+ if (n) {
+ rpterr();
+ progerr(pkg_gt(ERR_TRANSFER));
+ logerr(pkg_gt(MSG_CMDFAIL), cmd, n);
+ return (1);
+ }
+
+ part++;
+ if (srcdev.mount && (nparts > 1)) {
+ /* unmount current source volume */
+ (void) chdir("/");
+ if (pkgumount(&srcdev))
+ return (1);
+ /* loop until volume is mounted successfully */
+ while (part <= nparts) {
+ /* read only */
+ n = pkgmount(&srcdev, NULL, part, nparts, 1);
+ if (n)
+ return (n);
+ if (chdir(srcdir)) {
+ progerr(pkg_gt(ERR_TRANSFER));
+ logerr(pkg_gt(MSG_CORRUPT), srcdir);
+ (void) chdir("/");
+ pkgumount(&srcdev);
+ continue;
+ }
+ if (ckvolseq(srcdir, part, nparts)) {
+ (void) chdir("/");
+ pkgumount(&srcdev);
+ continue;
+ }
+ break;
+ }
+ }
+ if (!(options & PT_ODTSTREAM) && dstdev.mount) {
+ /* unmount current volume */
+ if (pkgumount(&dstdev))
+ return (1);
+ /* loop until next volume is mounted successfully */
+ while (part <= nparts) {
+ /* writable */
+ n = pkgmount(&dstdev, NULL, part, nparts, 1);
+ if (n)
+ return (n);
+ if (ckoverwrite(dst, dstinst, options))
+ continue;
+ if (isdir(dstdir) && mkdir(dstdir, 0755)) {
+ progerr(pkg_gt(ERR_TRANSFER));
+ logerr(pkg_gt(MSG_MKDIR), dstdir);
+ continue;
+ }
+ break;
+ }
+ }
+
+ if ((options & PT_ODTSTREAM) && part <= nparts) {
+ if (curpartcnt >= 0 && part > curpartcnt) {
+ char prompt[128];
+ int index;
+ ds_volno++;
+ if (ds_close(0))
+ return (1);
+ (void) sprintf(prompt,
+ pkg_gt("Insert %%v %d of %d into %%p"),
+ ds_volno, ds_volcnt);
+ if (n = getvol(ods_name, NULL, DM_FORMAT,
+ prompt))
+ return (n);
+ if ((ds_fd = open(dstdev.cdevice, 1)) < 0) {
+ progerr(pkg_gt(ERR_TRANSFER));
+ logerr(pkg_gt(MSG_OPEN),
+ dstdev.cdevice, errno);
+ return (1);
+ }
+ if (ds_ginit(dstdev.cdevice) < 0) {
+ progerr(pkg_gt(ERR_TRANSFER));
+ logerr(pkg_gt(MSG_OPEN),
+ dstdev.cdevice, errno);
+ (void) ds_close(0);
+ return (1);
+ }
+
+ (void) sscanf(volnos, "%d %[ 0-9]", &index,
+ tmpvol);
+ (void) strcpy(volnos, tmpvol);
+ curpartcnt += index;
+ }
+ }
+
+ }
+ return (0);
+}
+
+/*
+ * Name: pkgdump
+ * Description: Dump a cpio archive of a package's contents to a BIO.
+ *
+ * Arguments: srcinst - Name of package, which resides on the
+ * device pointed to by the static 'srcdev' variable,
+ * to dump.
+ * bio - BIO object to dump data to
+ *
+ * Returns : 0 - success
+ * nonzero - failure. errors printed to screen.
+ */
+static int
+pkgdump(char *srcinst, BIO *bio)
+{
+ FILE *fp;
+ char *src;
+ char temp[MAXPATHLEN],
+ srcdir[MAXPATHLEN],
+ cmd[CMDSIZE];
+ int i, n, part, nparts, maxpartsize, iscomp;
+
+ /*
+ * when this routine is entered, the entire package
+ * is already available at 'src' - including the
+ * pkginfo/pkgmap files and the objects as well.
+ */
+
+ /* read the pkgmap to get it's size information */
+ if ((fp = fopen(PKGMAP, "r")) == NULL) {
+ progerr(pkg_gt(ERR_TRANSFER));
+ logerr(pkg_gt(MSG_NOPKGMAP), srcinst);
+ return (1);
+ }
+
+ nparts = 1;
+ if (!rd_map_size(fp, &nparts, &maxpartsize, &compressedsize))
+ return (1);
+ else
+ (void) fclose(fp);
+
+ /* make sure the first volume is available */
+ if (srcdev.mount) {
+ src = srcdev.dirname;
+ (void) snprintf(srcdir, MAXPATHLEN, "%s/%s", src, srcinst);
+ if (ckvolseq(srcdir, 1, nparts)) {
+ progerr(pkg_gt(ERR_TRANSFER));
+ logerr(pkg_gt(MSG_SEQUENCE));
+ return (1);
+ }
+ }
+
+ /*
+ * form cpio command that will output the contents of all of
+ * this package's parts
+ */
+ for (part = 1; part <= nparts; /* void */) {
+
+ if (part == 1) {
+ (void) snprintf(cmd, CMDSIZE, "find %s %s",
+ PKGINFO, PKGMAP);
+ if (nparts && (isdir(INSTALL) == 0)) {
+ (void) strcat(cmd, " ");
+ (void) strcat(cmd, INSTALL);
+ }
+ } else
+ (void) snprintf(cmd, CMDSIZE, "find %s", PKGINFO);
+
+ if (nparts > 1) {
+ (void) snprintf(temp, MAXPATHLEN, "%s.%d", RELOC, part);
+ if (iscpio(temp, &iscomp) || isdir(temp) == 0) {
+ (void) strlcat(cmd, " ", CMDSIZE);
+ (void) strlcat(cmd, temp, CMDSIZE);
+ }
+ (void) snprintf(temp, MAXPATHLEN, "%s.%d", ROOT, part);
+ if (iscpio(temp, &iscomp) || isdir(temp) == 0) {
+ (void) strlcat(cmd, " ", CMDSIZE);
+ (void) strlcat(cmd, temp, CMDSIZE);
+ }
+ (void) snprintf(temp, MAXPATHLEN, "%s.%d",
+ ARCHIVE, part);
+ if (isdir(temp) == 0) {
+ (void) strlcat(cmd, " ", CMDSIZE);
+ (void) strlcat(cmd, temp, CMDSIZE);
+ }
+ } else if (nparts) {
+ for (i = 0; reloc_names[i] != NULL; i++) {
+ if (iscpio(reloc_names[i], &iscomp) ||
+ isdir(reloc_names[i]) == 0) {
+ (void) strlcat(cmd, " ", CMDSIZE);
+ (void) strlcat(cmd, reloc_names[i],
+ CMDSIZE);
+ }
+ }
+ for (i = 0; root_names[i] != NULL; i++) {
+ if (iscpio(root_names[i], &iscomp) ||
+ isdir(root_names[i]) == 0) {
+ (void) strlcat(cmd, " ", CMDSIZE);
+ (void) strlcat(cmd, root_names[i],
+ CMDSIZE);
+ }
+ }
+ if (isdir(ARCHIVE) == 0) {
+ (void) strlcat(cmd, " ", CMDSIZE);
+ (void) strlcat(cmd, ARCHIVE, CMDSIZE);
+ }
+ }
+
+#ifndef SUNOS41
+ (void) sprintf(cmd+strlen(cmd),
+ " -print | %s -ocD -C %d",
+#else
+ (void) sprintf(cmd+strlen(cmd),
+ " -print | %s -oc -C %d",
+#endif
+ CPIOPROC, (int)BLK_SIZE);
+ /*
+ * execute the command, dumping all standard output
+ * to the BIO.
+ */
+ n = BIO_dump_cmd(cmd, bio);
+ if (n != 0) {
+ rpterr();
+ progerr(pkg_gt(ERR_TRANSFER));
+ logerr(pkg_gt(MSG_CMDFAIL), cmd, n);
+ return (1);
+ }
+
+ part++;
+ }
+ return (0);
+}
+
+static void
+sigtrap(int signo)
+{
+ signal_received++;
+}
+
+static void
+cleanup(void)
+{
+ chdir("/");
+ if (tmpdir) {
+ rrmdir(tmpdir);
+ free(tmpdir);
+ tmpdir = NULL;
+ }
+
+ if (tmppath) {
+ /* remove any previous tmppath stuff */
+ rrmdir(tmppath);
+ free(tmppath);
+ tmppath = NULL;
+ }
+
+ if (tmpsymdir) {
+ /* remove temp symbolic links made for signed pkg */
+ rrmdir(tmpsymdir);
+ free(tmpsymdir);
+ tmpsymdir = NULL;
+ }
+
+ if (srcdev.mount && !ids_name)
+ pkgumount(&srcdev);
+ if (dstdev.mount && !ods_name)
+ pkgumount(&dstdev);
+ (void) ds_close(1);
+}
+
+/*
+ * Name: dump_hdr_and_pkgs
+ * Description: Dumps datastream header and each package's contents
+ * to the supplied BIO
+ *
+ * Arguments: bio - BIO object to dump data to
+ * hdr - Header for the datastream being dumped
+ * pkglist - NULL-terminated list of packages
+ * to dump. The location of the packages are stored
+ * in the static 'srcdev' variable.
+ *
+ * Returns : 0 - success
+ * nonzero - failure. errors printed to screen.
+ */
+static int
+dump_hdr_and_pkgs(BIO *bio, struct dm_buf *hdr, char **pkglist)
+{
+ int block_cnt, i;
+ char srcdir[MAXPATHLEN];
+ char cwd[MAXPATHLEN + 1];
+ char *src;
+
+ /* write out the header to the signature stream */
+ for (block_cnt = 0; block_cnt < hdr->allocation;
+ block_cnt += BLK_SIZE) {
+ BIO_write(bio, (hdr->text_buffer + block_cnt), BLK_SIZE);
+ }
+
+ /* save current directory */
+ if (getcwd(cwd, MAXPATHLEN + 1) == NULL) {
+ logerr(pkg_gt(ERR_GETWD));
+ progerr(pkg_gt(ERR_TRANSFER));
+ return (1);
+ }
+
+ /* now write out each package's contents */
+ for (i = 0; pkglist[i]; i++) {
+ /*
+ * change to the source dir, so we can find and dump
+ * the package(s) bits into the BIO
+ *
+ */
+ src = srcdev.dirname;
+
+ /* change to the package source directory */
+ (void) snprintf(srcdir, MAXPATHLEN, "%s/%s", src, pkglist[i]);
+ if (chdir(srcdir)) {
+ progerr(pkg_gt(ERR_TRANSFER));
+ logerr(pkg_gt(MSG_CHDIR), srcdir);
+ return (1);
+ }
+
+ if (pkgdump(pkglist[i], bio)) {
+ pkglist[i] = NULL;
+ return (1);
+ }
+ }
+
+ /* change back to directory we were in upon entering this routine */
+ if (chdir(cwd)) {
+ progerr(pkg_gt(ERR_TRANSFER));
+ logerr(pkg_gt(MSG_CHDIR), cwd);
+ return (1);
+ }
+
+ return (0);
+}
+
+/*
+ * Name: BIO_dump_cmd
+ * Description: Dump the output of invoking a command
+ * to a BIO.
+ *
+ * Arguments: cmd - Command to invoke
+ * bio - BIO to dump output of command to
+ * only 'stdout' is dumped.
+ * Returns : 0 - success
+ * nonzero - failure. errors printed to screen.
+ */
+int
+BIO_dump_cmd(char *cmd, BIO *bio)
+{
+ char buf[BLK_SIZE];
+ FILE *fp;
+ int rc;
+
+ /* start up the process */
+ if ((fp = epopen(cmd, "r")) == NULL) {
+ rpterr();
+ return (1);
+ }
+
+ /* read output in chunks, transfer to BIO */
+ while (fread(buf, BLK_SIZE, 1, fp) == 1) {
+ if (BIO_write(bio, buf, BLK_SIZE) != BLK_SIZE) {
+ sighold(SIGINT);
+ sighold(SIGHUP);
+ (void) epclose(fp);
+ sigrelse(SIGINT);
+ sigrelse(SIGHUP);
+ rpterr();
+ return (1);
+ }
+ }
+
+ /* done with stream, make sure no errors were encountered */
+ if (ferror(fp)) {
+ (void) epclose(fp);
+ rpterr();
+ return (1);
+ }
+
+ /* done, close stream, report any errors */
+ sighold(SIGINT);
+ sighold(SIGHUP);
+ rc = epclose(fp);
+ sigrelse(SIGINT);
+ sigrelse(SIGHUP);
+ if (rc != 0) {
+ rpterr();
+ return (1);
+ }
+
+ return (rc);
+}
diff --git a/usr/src/lib/libpkg/common/pkgweb.c b/usr/src/lib/libpkg/common/pkgweb.c
new file mode 100644
index 0000000000..56559a0953
--- /dev/null
+++ b/usr/src/lib/libpkg/common/pkgweb.c
@@ -0,0 +1,3238 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
+/* All Rights Reserved */
+
+
+#include <stdio.h>
+#include <limits.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <pkglocs.h>
+#include <locale.h>
+#include <libintl.h>
+#include <libgen.h>
+#include <signal.h>
+#include <sys/stat.h>
+#include <sys/statvfs.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include <dirent.h>
+#include <boot_http.h>
+#include <errno.h>
+#include <ctype.h>
+#include <openssl/pkcs7.h>
+#include <openssl/ocsp.h>
+#include <openssl/pkcs12.h>
+#include <openssl/err.h>
+#include <openssl/x509.h>
+#include <openssl/pem.h>
+#include <openssl/evp.h>
+#include <openssl/rand.h>
+#include <openssl/x509v3.h>
+#include "pkglib.h"
+#include "pkglibmsgs.h"
+#include "pkglocale.h"
+#include "keystore.h"
+#include "pkgweb.h"
+#include "pkgerr.h"
+#include "p12lib.h"
+
+/* fixed format when making an OCSP request */
+#define OCSP_REQUEST_FORMAT \
+ "POST %s HTTP/1.0\r\n" \
+ "Content-Type: application/ocsp-request\r\n" \
+ "Content-Length: %d\r\n\r\n"
+
+/*
+ * no security is afforded by using this phrase to "encrypt" CA certificates,
+ * but it might aid in debugging and has to be non-null
+ */
+#define WEB_CA_PHRASE "schizophrenic"
+
+/* This one needs the ': ' at the end */
+#define CONTENT_TYPE_HDR "Content-Type"
+#define CONTENT_DISPOSITION_HDR "Content-Disposition"
+#define CONTENT_OCSP_RESP "application/ocsp-response"
+#define CONTENT_LENGTH_HDR "Content-Length"
+#define LAST_MODIFIED_HDR "Last-Modified"
+#define OCSP_BUFSIZ 1024
+
+/*
+ * default amount of time that is allowed for error when checking
+ * OCSP response validity.
+ * For example, if this is set to 5 minutes, then if a response
+ * is issued that is valid from 12:00 to 1:00, then we will
+ * accept it if the local time is between 11:55 and 1:05.
+ * This takes care of not-quite-synchronized server and client clocks.
+ */
+#define OCSP_VALIDITY_PERIOD (5 * 60)
+
+/* this value is defined by getpassphrase(3c) manpage */
+#define MAX_PHRASELEN 257
+
+/* Max length of "enter password again" prompt message */
+#define MAX_VERIFY_MSGLEN 1024
+
+/* local prototypes */
+static boolean_t remove_dwnld_file(char *);
+static boolean_t get_ENV_proxyport(PKG_ERR *, ushort_t *);
+static boolean_t make_link(char *, char *);
+static WebStatus web_send_request(PKG_ERR *, int, int, int);
+static boolean_t web_eval_headers(PKG_ERR *);
+static WebStatus web_get_file(PKG_ERR *, char *, int, char **);
+static boolean_t ck_dwnld_dir_space(PKG_ERR *, char *, ulong_t);
+static WebStatus web_connect(PKG_ERR *);
+static boolean_t web_setup(PKG_ERR *);
+static boolean_t check_dwnld_dir(PKG_ERR *, char *);
+static boolean_t parse_url_proxy(PKG_ERR *, char *, char *, ushort_t);
+static boolean_t web_disconnect(void);
+static char *get_unique_filename(char *, char *);
+static boolean_t get_ENV_proxy(PKG_ERR *, char **);
+static char *condense_lastmodified(char *);
+static int web_verify(int, X509_STORE_CTX *);
+static int get_issuer(X509 **issuer, X509_STORE_CTX *ctx, X509 *x);
+static boolean_t get_ocsp_uri(X509 *, char **);
+static OCSPStatus ocsp_verify(PKG_ERR *, X509 *, X509 *, char *, url_hport_t *,
+ STACK_OF(X509) *);
+static char *get_time_string(ASN1_GENERALIZEDTIME *);
+static char *write_ca_file(PKG_ERR *, char *, STACK_OF(X509) *, char *);
+static boolean_t _get_random_info(void *, int);
+static boolean_t init_session(void);
+static void progress_setup(int, ulong_t);
+static void progress_report(int, ulong_t);
+static void progress_finish(int);
+static char *replace_token(char *, char, char);
+static void dequote(char *);
+static void trim(char *);
+
+
+/*
+ * structure used to hold data passed back to the
+ * X509 verify callback routine in validate_signature()
+ */
+typedef struct {
+ url_hport_t *proxy;
+ PKG_ERR *err;
+ STACK_OF(X509) *cas;
+} verify_cb_data_t;
+
+/* Progress bar variables */
+static ulong_t const_increment, const_divider, completed, const_completed;
+
+/* current network backoff wait period */
+static int cur_backoff = 0;
+
+/* download session context handle */
+static WEB_SESSION *ps;
+
+static int webpkg_install = 0;
+static char *prompt = NULL;
+static char *passarg = NULL;
+
+
+/* ~~~~~~~~~~~~~~ Public Functions ~~~~~~~~~~~~~~~~~~~ */
+
+/*
+ * Name: set_prompt
+ * Description: Specifies the prompt to use with the pkglib
+ * passphrase callback routine.
+ *
+ * Arguments: newprompt - The prompt to display
+ *
+ * Returns : NONE
+ */
+void
+set_passphrase_prompt(char *newprompt)
+{
+ prompt = newprompt;
+}
+
+/*
+ * Name: set_passarg
+ * Description: Specifies the passphrase retrieval method
+ * to use with the pkglib
+ * passphrase callback routine.
+ *
+ * Arguments: newpassarg - The new password retrieval arg
+ *
+ * Returns : NONE
+ */
+void
+set_passphrase_passarg(char *newpassarg)
+{
+ passarg = newpassarg;
+}
+
+/*
+ * Name: get_proxy_port
+ * Description: Resolves proxy specification
+ *
+ * Arguments: err - where to record any errors.
+ * proxy - Location to store result - if *proxy is not
+ * null, then it will be validated, but not changed
+ *
+ * Returns : B_TRUE - success, B_FALSE otherwise
+ * on success, *proxy and *port are set to either
+ * the user-supplied proxy and port, or the
+ * ones found in the environment variables
+ * HTTPPROXY and/or HTTPROXYPORT
+ */
+boolean_t
+get_proxy_port(PKG_ERR *err, char **proxy, ushort_t *port)
+{
+ if (*proxy != NULL) {
+ if (!path_valid(*proxy)) {
+ /* bad proxy supplied */
+ pkgerr_add(err, PKGERR_WEB,
+ gettext(ERR_BAD_PROXY), *proxy);
+ return (B_FALSE);
+ }
+ if (!get_ENV_proxyport(err, port)) {
+ /* env set, but bad */
+ return (B_FALSE);
+ }
+ } else {
+ if (!get_ENV_proxy(err, proxy)) {
+ /* environment variable set, but bad */
+ return (B_FALSE);
+ }
+ if ((*proxy != NULL) && !path_valid(*proxy)) {
+ /* env variable set, but bad */
+ pkgerr_add(err, PKGERR_WEB,
+ gettext(ERR_BAD_PROXY), *proxy);
+ return (B_FALSE);
+ }
+ if (!get_ENV_proxyport(err, port)) {
+ /* env variable set, but bad */
+ return (B_FALSE);
+ }
+ }
+ return (B_TRUE);
+}
+
+/*
+ * Name: path_valid
+ * Description: Checks a string for being a valid path
+ *
+ * Arguments: path - path to validate
+ *
+ * Returns : B_TRUE - success, B_FALSE otherwise.
+ * B_FALSE means path was null, too long (>PATH_MAX),
+ * or too short (<1)
+ */
+boolean_t
+path_valid(char *path)
+{
+ if (path == NULL) {
+ return (B_FALSE);
+ } else if (strlen(path) > PATH_MAX) {
+ return (B_FALSE);
+ } else if (strlen(path) >= 1) {
+ return (B_TRUE);
+ } else {
+ /* path < 1 */
+ return (B_FALSE);
+ }
+}
+
+/*
+ * Name: web_cleanup
+ * Description: Deletes temp files, closes, frees memory taken
+ * by 'ps' static structure
+ *
+ * Arguments: none
+ *
+ * Returns : none
+ */
+void
+web_cleanup(void)
+{
+ PKG_ERR *err;
+
+ if (ps == NULL)
+ return;
+
+ err = pkgerr_new();
+
+ if (ps->keystore) {
+ (void) close_keystore(err, ps->keystore, NULL);
+ }
+
+ ps->keystore = NULL;
+
+ pkgerr_free(err);
+
+ if (ps->uniqfile) {
+ (void) remove_dwnld_file(ps->uniqfile);
+ free(ps->uniqfile);
+ ps->uniqfile = NULL;
+ }
+ if (ps->link) {
+ (void) remove_dwnld_file(ps->link);
+ free(ps->link);
+ ps->link = NULL;
+ }
+ if (ps->dwnld_dir) {
+ (void) rmdir(ps->dwnld_dir);
+ ps->dwnld_dir = NULL;
+ }
+ if (ps->errstr) {
+ free(ps->errstr);
+ ps->errstr = NULL;
+ }
+
+ if (ps->content) {
+ free(ps->content);
+ ps->content = NULL;
+ }
+
+ if (ps->resp) {
+ http_free_respinfo(ps->resp);
+ ps->resp = NULL;
+ }
+
+ if (ps) {
+ free(ps);
+ ps = NULL;
+ }
+}
+
+/*
+ * Name: web_session_control
+ * Description: Downloads an arbitrary URL and saves to disk.
+ *
+ * Arguments: err - where to record any errors.
+ * url - URL pointing to content to download - can be
+ * http:// or https://
+ * dwnld_dir - Directory to download into
+ * keystore - keystore to use for accessing trusted
+ * certs when downloading using SSL
+ * proxy - HTTP proxy to use, or NULL for no proxy
+ * proxy_port - HTTP proxy port to use, ignored
+ * if proxy is NULL
+ * passarg - method to retrieve password
+ * retries - # of times to retry download before
+ * giving up
+ * timeout - how long to wait before retrying,
+ * when download is interrupted
+ * nointeract - if non-zero, do not output
+ * download progress to screen
+ *
+ * Returns : B_TRUE - success, B_FALSE otherwise
+ */
+boolean_t
+web_session_control(PKG_ERR *err, char *url, char *dwnld_dir,
+ keystore_handle_t keystore, char *proxy, ushort_t proxy_port,
+ int retries, int timeout, int nointeract, char **fname)
+{
+ int i;
+ boolean_t ret = B_TRUE;
+ boolean_t retrieved = B_FALSE;
+
+ if (!init_session()) {
+ ret = B_FALSE;
+ goto cleanup;
+ }
+
+ if (!parse_url_proxy(err, url, proxy, proxy_port)) {
+ ret = B_FALSE;
+ goto cleanup;
+ }
+
+ ps->timeout = timeout;
+
+ if (keystore != NULL)
+ ps->keystore = keystore;
+
+ if (dwnld_dir != NULL)
+ ps->dwnld_dir = xstrdup(dwnld_dir);
+ else {
+ pkgerr_add(err, PKGERR_WEB, gettext(ERR_NO_DWNLD_DIR));
+ ret = B_FALSE;
+ goto cleanup;
+ }
+
+ if (!check_dwnld_dir(err, dwnld_dir)) {
+ ret = B_FALSE;
+ goto cleanup;
+ }
+
+ for (i = 0; i < retries && !retrieved; i++) {
+ if (!web_setup(err)) {
+ ret = B_FALSE;
+ goto cleanup;
+ }
+
+ switch (web_connect(err)) {
+ /* time out and wait a little bit for these failures */
+ case WEB_OK:
+ /* were able to connect */
+ reset_backoff();
+ break;
+ case WEB_TIMEOUT:
+ echo_out(nointeract, gettext(MSG_DWNLD_TIMEOUT));
+ (void) web_disconnect();
+ backoff();
+ continue;
+
+ case WEB_CONNREFUSED:
+ echo_out(nointeract, gettext(MSG_DWNLD_CONNREF),
+ ps->url.hport.hostname);
+ (void) web_disconnect();
+ backoff();
+ continue;
+ case WEB_HOSTDOWN:
+ echo_out(nointeract, gettext(MSG_DWNLD_HOSTDWN),
+ ps->url.hport.hostname);
+ (void) web_disconnect();
+ backoff();
+ continue;
+
+ default:
+ /* every other failure is a hard failure, so bail */
+ ret = B_FALSE;
+ goto cleanup;
+ }
+
+ switch (web_send_request(err, HTTP_REQ_TYPE_HEAD,
+ ps->data.cur_pos, ps->data.content_length)) {
+ case WEB_OK:
+ /* were able to connect */
+ reset_backoff();
+ break;
+ case WEB_TIMEOUT:
+ echo_out(nointeract, gettext(MSG_DWNLD_TIMEOUT));
+ (void) web_disconnect();
+ backoff();
+ continue;
+
+ case WEB_CONNREFUSED:
+ echo_out(nointeract, gettext(MSG_DWNLD_CONNREF),
+ ps->url.hport.hostname);
+ (void) web_disconnect();
+ backoff();
+ continue;
+ case WEB_HOSTDOWN:
+ echo_out(nointeract, gettext(MSG_DWNLD_HOSTDWN),
+ ps->url.hport.hostname);
+ (void) web_disconnect();
+ backoff();
+ continue;
+ default:
+ /* every other case is failure, so bail */
+ ret = B_FALSE;
+ goto cleanup;
+ }
+
+ if (!web_eval_headers(err)) {
+ ret = B_FALSE;
+ goto cleanup;
+ }
+
+ switch (web_get_file(err, dwnld_dir, nointeract, fname)) {
+ case WEB_OK:
+ /* were able to retrieve file */
+ retrieved = B_TRUE;
+ reset_backoff();
+ break;
+
+ case WEB_TIMEOUT:
+ echo_out(nointeract, gettext(MSG_DWNLD_TIMEOUT));
+ (void) web_disconnect();
+ backoff();
+ continue;
+
+ case WEB_CONNREFUSED:
+ echo_out(nointeract, gettext(MSG_DWNLD_CONNREF),
+ ps->url.hport.hostname);
+ (void) web_disconnect();
+ backoff();
+ continue;
+ case WEB_HOSTDOWN:
+ echo_out(nointeract, gettext(MSG_DWNLD_HOSTDWN),
+ ps->url.hport.hostname);
+ (void) web_disconnect();
+ backoff();
+ continue;
+ default:
+ /* every other failure is a hard failure, so bail */
+ ret = B_FALSE;
+ goto cleanup;
+ }
+ }
+
+ if (!retrieved) {
+ /* max retries attempted */
+ pkgerr_add(err, PKGERR_WEB,
+ gettext(ERR_DWNLD_FAILED), retries);
+ ret = B_FALSE;
+ }
+cleanup:
+ (void) web_disconnect();
+ if (!ret) {
+ pkgerr_add(err, PKGERR_WEB, gettext(ERR_DWNLD), url);
+ }
+ return (ret);
+}
+
+/*
+ * Name: get_signature
+ * Description: retrieves signature from signed package.
+ *
+ * Arguments: err - where to record any errors.
+ * ids_name - name of package stream, for error reporting
+ * devp - Device on which package resides that we
+ * result - where to store resulting PKCS7 signature
+ *
+ * Returns : B_TRUE - package is signed and signature returned OR
+ * package is not signed, in which case result is NULL
+ *
+ * B_FALSE - there were problems accessing signature,
+ * and it is unknown whether it is signed or not. Errors
+ * recorded in 'err'.
+ */
+boolean_t
+get_signature(PKG_ERR *err, char *ids_name, struct pkgdev *devp, PKCS7 **result)
+{
+ char path[PATH_MAX];
+ int len, fd = -1;
+ struct stat buf;
+ FILE *fp = NULL;
+ boolean_t ret = B_TRUE;
+ BIO *sig_in = NULL;
+ PKCS7 *p7 = NULL;
+
+ /*
+ * look for signature. If one was in the stream,
+ * it is now extracted
+ */
+ if (((len = snprintf(path, PATH_MAX, "%s/%s", devp->dirname,
+ SIGNATURE_FILENAME)) >= PATH_MAX) || (len < 0)) {
+ pkgerr_add(err, PKGERR_WEB, gettext(ERR_LEN), ids_name);
+ ret = B_FALSE;
+ goto cleanup;
+ }
+
+ if ((fd = open(path, O_RDONLY|O_NONBLOCK)) == -1) {
+ /*
+ * only if the signature is non-existant
+ * do we "pass"
+ */
+ if (errno != ENOENT) {
+ pkgerr_add(err, PKGERR_WEB, gettext(ERR_OPENSIG),
+ strerror(errno));
+ ret = B_FALSE;
+ goto cleanup;
+ }
+ } else {
+ /* found sig file. parse it. */
+ if (fstat(fd, &buf) == -1) {
+ pkgerr_add(err, PKGERR_WEB,
+ gettext(ERR_OPENSIG), strerror(errno));
+ ret = B_FALSE;
+ goto cleanup;
+ }
+
+ if (!S_ISREG(buf.st_mode)) {
+ pkgerr_add(err, PKGERR_WEB, gettext(ERR_OPENSIG),
+ (gettext(ERR_NOT_REG)));
+ ret = B_FALSE;
+ goto cleanup;
+ }
+
+ if ((fp = fdopen(fd, "r")) == NULL) {
+ pkgerr_add(err, PKGERR_WEB,
+ gettext(ERR_OPENSIG), strerror(errno));
+ ret = B_FALSE;
+ goto cleanup;
+ }
+
+ /*
+ * read in signature. If it's invalid, we
+ * punt, unless we're ignoring it
+ */
+ if ((sig_in = BIO_new_fp(fp, BIO_NOCLOSE)) == NULL) {
+ pkgerr_add(err, PKGERR_WEB,
+ gettext(ERR_OPENSIG), strerror(errno));
+ goto cleanup;
+ }
+
+ if ((p7 = PEM_read_bio_PKCS7(sig_in,
+ NULL, NULL, NULL)) == NULL) {
+ pkgerr_add(err, PKGERR_WEB, gettext(ERR_CORRUPTSIG),
+ ids_name);
+ ret = B_FALSE;
+ goto cleanup;
+ }
+ *result = p7;
+ p7 = NULL;
+ }
+
+cleanup:
+ if (sig_in)
+ (void) BIO_free(sig_in);
+ if (fp)
+ (void) fclose(fp);
+ if (fd != -1)
+ (void) close(fd);
+ if (p7)
+ (void) PKCS7_free(p7);
+
+ return (ret);
+}
+
+/*
+ * Name: echo_out
+ * Description: Conditionally output a message to stdout
+ *
+ * Arguments: nointeract - if non-zero, do not output anything
+ * fmt - print format
+ * ... - print arguments
+ *
+ * Returns : none
+ */
+void
+echo_out(int nointeract, char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+
+ if (nointeract)
+ return;
+
+ (void) vfprintf(stdout, fmt, ap);
+
+ va_end(ap);
+
+ (void) putc('\n', stdout);
+}
+
+/*
+ * Name: strip_port
+ * Description: Returns "port" portion of a "hostname:port" string
+ *
+ * Arguments: proxy - full "hostname:port" string pointer
+ *
+ * Returns : the "port" portion of a "hostname:port" string,
+ * converted to a decimal integer, or (int)0
+ * if string contains no :port suffix.
+ */
+ushort_t
+strip_port(char *proxy)
+{
+ char *tmp_port;
+
+ if ((tmp_port = strpbrk(proxy, ":")) != NULL)
+ return (atoi(tmp_port));
+ else
+ return (0);
+}
+
+/*
+ * Name: set_web_install
+ * Description: Sets flag indicating we are doing a web-based install
+ *
+ * Arguments: none
+ *
+ * Returns : none
+ */
+void
+set_web_install(void)
+{
+ webpkg_install++;
+}
+
+/*
+ * Name: is_web_install
+ * Description: Determines whether we are doing a web-based install
+ *
+ * Arguments: none
+ *
+ * Returns : non-zero if we are doing a web-based install, 0 otherwise
+ */
+int
+is_web_install(void)
+{
+ return (webpkg_install);
+}
+
+/* ~~~~~~~~~~~~~~ Private Functions ~~~~~~~~~~~~~~~~~~~ */
+
+/*
+ * Name: web_disconnect
+ * Description: Disconnects connection to web server
+ *
+ * Arguments: none
+ *
+ * Returns : B_TRUE - successful disconnect, B_FALSE otherwise
+ * Temp certificiate files are deleted,
+ * if one was used to initiate the connection
+ * (such as when using SSL)
+ */
+static boolean_t
+web_disconnect(void)
+{
+ if (ps->certfile) {
+ (void) unlink(ps->certfile);
+ }
+ if (http_srv_disconnect(ps->hps) == 0)
+ if (http_srv_close(ps->hps) == 0)
+ return (B_TRUE);
+
+ return (B_FALSE);
+}
+
+/*
+ * Name: check_dwnld_dir
+ * Description: Creates temp download directory
+ *
+ * Arguments: err - where to record any errors.
+ * dwnld_dir - name of directory to create
+ *
+ * Returns : B_TRUE - success, B_FALSE otherwise
+ * on success, directory is created with
+ * safe permissions
+ */
+static boolean_t
+check_dwnld_dir(PKG_ERR *err, char *dwnld_dir)
+{
+ DIR *dirp;
+
+ /*
+ * Check the directory passed in. If it doesn't exist, create it
+ * with strict permissions
+ */
+ if ((dirp = opendir(dwnld_dir)) == NULL) {
+ if (mkdir(dwnld_dir, 0744) == -1) {
+ pkgerr_add(err, PKGERR_WEB, gettext(MSG_NOTEMP),
+ dwnld_dir);
+ return (B_FALSE);
+ }
+ }
+ if (dirp) {
+ (void) closedir(dirp);
+ }
+ return (B_TRUE);
+}
+
+/*
+ * Name: ds_validate_signature
+ * Description: Validates signature found in a package datastream
+ *
+ * Arguments: err - where to record any errors.
+ * pkgdev - Package context handle of package to verify
+ * pkgs - Null-terminated List of package name to verify
+ * ids_name - Pathname to stream to validate
+ * p7 - PKCS7 signature decoded from stream header
+ * cas - List of trusted CA certificates
+ * proxy - Proxy to use when doing online validation (OCSP)
+ * nointeract - if non-zero, do not output to screen
+ *
+ * Returns : B_TRUE - success, B_FALSE otherwise
+ * success means signature was completely validated,
+ * and contents of stream checked against signature.
+ */
+boolean_t
+ds_validate_signature(PKG_ERR *err, struct pkgdev *pkgdev, char **pkgs,
+ char *ids_name, PKCS7 *p7, STACK_OF(X509) *cas,
+ url_hport_t *proxy, int nointeract)
+{
+ BIO *p7_bio;
+ boolean_t ret = B_TRUE;
+
+ /* make sure it's a Signed PKCS7 message */
+ if (!PKCS7_type_is_signed(p7)) {
+ pkgerr_add(err, PKGERR_WEB, gettext(ERR_CORRUPTSIG_TYPE),
+ ids_name);
+ ret = B_FALSE;
+ goto cleanup;
+ }
+
+ /* initialize PKCS7 object to be filled in */
+ if (!PKCS7_get_detached(p7)) {
+ pkgerr_add(err, PKGERR_WEB, gettext(ERR_CORRUPTSIG_DT),
+ ids_name);
+ ret = B_FALSE;
+ goto cleanup;
+ }
+
+ /* dump header and packages into BIO to calculate the message digest */
+ if ((p7_bio = PKCS7_dataInit(p7, NULL)) == NULL) {
+ pkgerr_add(err, PKGERR_WEB, gettext(ERR_CORRUPTSIG),
+ ids_name);
+ ret = B_FALSE;
+ goto cleanup;
+ }
+
+ if ((BIO_ds_dump_header(err, p7_bio) != 0) ||
+ (BIO_ds_dump(err, ids_name, p7_bio) != 0)) {
+ ret = B_FALSE;
+ goto cleanup;
+ }
+ (void) BIO_flush(p7_bio);
+
+ /* validate the stream and its signature */
+ if (!validate_signature(err, ids_name, p7_bio, p7, cas,
+ proxy, nointeract)) {
+ ret = B_FALSE;
+ goto cleanup;
+ }
+
+ /* reset device stream (really bad performance for tapes) */
+ (void) ds_close(1);
+ (void) ds_init(ids_name, pkgs, pkgdev->norewind);
+
+cleanup:
+ return (ret);
+}
+
+
+/*
+ * Name: validate_signature
+ * Description: Validates signature of an arbitrary stream of bits
+ *
+ * Arguments: err - where to record any errors.
+ * name - Descriptive name of object being validated,
+ * for good error reporting messages
+ * indata - BIO object to read stream bits from
+ * p7 - PKCS7 signature of stream
+ * cas - List of trusted CA certificates
+ * proxy - Proxy to use when doing online validation (OCSP)
+ * nointeract - if non-zero, do not output to screen
+ *
+ * Returns : B_TRUE - success, B_FALSE otherwise
+ * success means signature was completely validated,
+ * and contents of stream checked against signature.
+ */
+boolean_t
+validate_signature(PKG_ERR *err, char *name, BIO *indata, PKCS7 *p7,
+ STACK_OF(X509) *cas, url_hport_t *proxy, int nointeract)
+{
+ STACK_OF(PKCS7_SIGNER_INFO) *sec_sinfos = NULL;
+
+ PKCS7_SIGNER_INFO *signer = NULL;
+ X509_STORE *sec_truststore = NULL;
+ X509_STORE_CTX *ctx = NULL;
+ X509 *signer_cert = NULL, *issuer = NULL;
+ STACK_OF(X509) *chaincerts = NULL;
+ int i, k;
+ unsigned long errcode;
+ const char *err_data = NULL;
+ const char *err_reason = NULL;
+ char *err_string;
+ int err_flags;
+ verify_cb_data_t verify_data;
+ char *signer_sname;
+ char *signer_iname;
+ PKCS7_ISSUER_AND_SERIAL *ias;
+ boolean_t ret = B_TRUE;
+
+ /* only support signed PKCS7 signatures */
+ if (!PKCS7_type_is_signed(p7)) {
+ PKCS7err(PKCS7_F_PKCS7_DATAVERIFY, PKCS7_R_WRONG_PKCS7_TYPE);
+ ret = B_FALSE;
+ goto cleanup;
+ }
+
+ /* initialize temporary internal trust store used for verification */
+ sec_truststore = X509_STORE_new();
+
+ for (i = 0; i < sk_X509_num(cas); i++) {
+ if (X509_STORE_add_cert(sec_truststore,
+ sk_X509_value(cas, i)) == 0) {
+ pkgerr_add(err, PKGERR_VERIFY, gettext(ERR_MEM));
+ ret = B_FALSE;
+ goto cleanup;
+ }
+ }
+
+ /* get signers from the signature */
+ if ((sec_sinfos = PKCS7_get_signer_info(p7)) == NULL) {
+ pkgerr_add(err, PKGERR_WEB, gettext(ERR_CORRUPTSIG), name);
+ ret = B_FALSE;
+ goto cleanup;
+ }
+
+ /* verify each signer found in the PKCS7 signature */
+ for (k = 0; k < sk_PKCS7_SIGNER_INFO_num(sec_sinfos); k++) {
+ signer = sk_PKCS7_SIGNER_INFO_value(sec_sinfos, k);
+ signer_cert = PKCS7_cert_from_signer_info(p7, signer);
+ signer_sname = get_subject_display_name(signer_cert);
+ signer_iname = get_issuer_display_name(signer_cert);
+
+ echo_out(nointeract, gettext(MSG_VERIFY), signer_sname);
+
+ /* find the issuer of the current cert */
+ chaincerts = p7->d.sign->cert;
+ ias = signer->issuer_and_serial;
+ issuer = X509_find_by_issuer_and_serial(chaincerts,
+ ias->issuer, ias->serial);
+
+ /* were we not able to find the issuer cert */
+ if (issuer == NULL) {
+ pkgerr_add(err, PKGERR_WEB,
+ gettext(ERR_VERIFY_ISSUER),
+ signer_iname, signer_sname);
+ ret = B_FALSE;
+ goto cleanup;
+ }
+
+ /* Lets verify */
+ if ((ctx = X509_STORE_CTX_new()) == NULL) {
+ pkgerr_add(err, PKGERR_VERIFY, gettext(ERR_MEM));
+ ret = B_FALSE;
+ goto cleanup;
+ }
+ (void) X509_STORE_CTX_init(ctx, sec_truststore,
+ issuer, chaincerts);
+ (void) X509_STORE_CTX_set_purpose(ctx,
+ X509_PURPOSE_ANY);
+
+ /* callback will perform OCSP on certificates with OCSP data */
+ X509_STORE_CTX_set_verify_cb(ctx, web_verify);
+
+ /* pass needed data into callback through the app_data handle */
+ verify_data.proxy = proxy;
+ verify_data.cas = cas;
+ verify_data.err = err;
+ (void) X509_STORE_CTX_set_app_data(ctx, &verify_data);
+
+ /* first verify the certificate chain */
+ i = X509_verify_cert(ctx);
+ if (i <= 0 && ctx->error != X509_V_ERR_CERT_HAS_EXPIRED) {
+ signer_sname =
+ get_subject_display_name(ctx->current_cert);
+ signer_iname =
+ get_issuer_display_name(ctx->current_cert);
+ /* if the verify context holds an error, print it */
+ if (ctx->error != X509_V_OK) {
+ pkgerr_add(err, PKGERR_VERIFY,
+ gettext(ERR_VERIFY_SIG), signer_sname,
+ signer_iname,
+ (char *)X509_verify_cert_error_string(ctx->error));
+ } else {
+ /* some other error. print them all. */
+ while ((errcode = ERR_get_error_line_data(NULL,
+ NULL, &err_data, &err_flags)) != 0) {
+ err_reason =
+ ERR_reason_error_string(errcode);
+ if (err_reason == NULL) {
+ err_reason =
+ gettext(ERR_SIG_INT);
+ }
+
+ if (!(err_flags & ERR_TXT_STRING)) {
+ err_data =
+ gettext(ERR_SIG_INT);
+ }
+ err_string =
+ xmalloc(strlen(err_reason) +
+ strlen(err_data) + 3);
+ (void) sprintf(err_string, "%s: %s",
+ err_reason, err_data);
+ pkgerr_add(err, PKGERR_VERIFY,
+ gettext(ERR_VERIFY_SIG),
+ signer_sname, signer_iname,
+ err_string);
+ free(err_string);
+ }
+ }
+ ret = B_FALSE;
+ goto cleanup;
+ }
+
+ /* now verify the signature */
+ i = PKCS7_signatureVerify(indata, p7, signer, issuer);
+
+ if (i <= 0) {
+ /* print out any OpenSSL-specific errors */
+ signer_sname =
+ get_subject_display_name(ctx->current_cert);
+ signer_iname =
+ get_subject_display_name(ctx->current_cert);
+ while ((errcode = ERR_get_error_line_data(NULL,
+ NULL, &err_data, &err_flags)) != 0) {
+ err_reason =
+ ERR_reason_error_string(errcode);
+ if (err_reason == NULL) {
+ err_reason =
+ gettext(ERR_SIG_INT);
+ }
+
+ if (!(err_flags & ERR_TXT_STRING)) {
+ err_data =
+ gettext(ERR_SIG_INT);
+ }
+ pkgerr_add(err, PKGERR_VERIFY,
+ gettext(ERR_VERIFY_SIG), signer_sname,
+ signer_iname, err_reason);
+ pkgerr_add(err, PKGERR_VERIFY,
+ gettext(ERR_VERIFY_SIG), signer_sname,
+ signer_iname, err_data);
+ }
+ ret = B_FALSE;
+ goto cleanup;
+ }
+
+ echo_out(nointeract, gettext(MSG_VERIFY_OK), signer_sname);
+ }
+
+ /* signature(s) verified successfully */
+cleanup:
+ if (ctx)
+ X509_STORE_CTX_cleanup(ctx);
+ return (ret);
+}
+
+/*
+ * Name: web_verify
+ * Description: Callback used by PKCS7_dataVerify when
+ * verifying a certificate chain.
+ *
+ * Arguments: err - where to record any errors.
+ * ctx - The context handle of the current verification operation
+ *
+ * Returns : B_TRUE - success, B_FALSE otherwise
+ * if it's '0' (not OK) we simply return it, since the
+ * verification operation has already determined that the
+ * cert is invalid. if 'ok' is non-zero, then we do our
+ * checks, and return 0 or 1 based on if the cert is
+ * invalid or valid.
+ */
+static int
+web_verify(int ok, X509_STORE_CTX *ctx)
+{
+ X509 *curr_cert;
+ X509 *curr_issuer;
+ char *uri;
+ url_hport_t *proxy;
+ PKG_ERR *err = NULL;
+ STACK_OF(X509) *cas;
+ if (!ok) {
+ /* don't override a verify failure */
+ return (ok);
+ }
+
+
+ /* get app data supplied through callback context */
+ err = ((verify_cb_data_t *)X509_STORE_CTX_get_app_data(ctx))->err;
+ proxy = ((verify_cb_data_t *)X509_STORE_CTX_get_app_data(ctx))->proxy;
+ cas = ((verify_cb_data_t *)X509_STORE_CTX_get_app_data(ctx))->cas;
+
+ /* Check revocation status */
+ curr_cert = X509_STORE_CTX_get_current_cert(ctx);
+
+ /* this shouldn't happen */
+ if (curr_cert == NULL) {
+ pkgerr_add(err, PKGERR_INTERNAL, gettext(ERR_PKG_INTERNAL),
+ __FILE__, __LINE__);
+ return (0);
+ }
+
+ /* don't perform OCSP unless cert has required OCSP extensions */
+ if (get_ocsp_uri(curr_cert, &uri)) {
+ if (get_issuer(&curr_issuer, ctx, curr_cert) <= 0) {
+ /* no issuer! */
+ pkgerr_add(err, PKGERR_INTERNAL,
+ gettext(ERR_PKG_INTERNAL),
+ __FILE__, __LINE__);
+ return (0);
+ }
+
+ /*
+ * ok we have the current cert
+ * and its issuer. Do the OCSP check
+ */
+
+ /*
+ * OCSP extensions are, by, RFC 2459, never critical
+ * extensions, therefore, we only fail if we were able
+ * to explicitly contact an OCSP responder, and that
+ * responder did not indicate the cert was valid. We
+ * also fail if user-supplied data could not be parsed
+ * or we run out of memory. We succeeed for "soft"
+ * failures, such as not being able to connect to the
+ * OCSP responder, or trying to use if the OCSP URI
+ * indicates SSL must be used (which we do not
+ * support)
+ */
+ switch (ocsp_verify(err, curr_cert, curr_issuer,
+ uri, proxy, cas)) {
+ case OCSPMem: /* Ran out of memory */
+ case OCSPInternal: /* Some internal error */
+ case OCSPVerify: /* OCSP responder indicated fail */
+ return (0);
+ }
+ /* all other cases are success, or soft failures */
+ pkgerr_clear(err);
+ }
+
+ return (ok);
+}
+
+/*
+ * Name: get_time_string
+ * Description: Generates a human-readable string from an ASN1_GENERALIZED_TIME
+ *
+ * Arguments: intime - The time to convert
+ *
+ * Returns : A pointer to a static string representing the passed-in time.
+ */
+static char
+*get_time_string(ASN1_GENERALIZEDTIME *intime)
+{
+
+ static char time[ATTR_MAX];
+ BIO *mem;
+ char *p;
+
+ if (intime == NULL) {
+ return (NULL);
+ }
+ if ((mem = BIO_new(BIO_s_mem())) == NULL) {
+ return (NULL);
+ }
+
+ if (ASN1_GENERALIZEDTIME_print(mem, intime) == 0) {
+ (void) BIO_free(mem);
+ return (NULL);
+ }
+
+ if (BIO_gets(mem, time, ATTR_MAX) <= 0) {
+ (void) BIO_free(mem);
+ return (NULL);
+ }
+
+ (void) BIO_free(mem);
+
+ /* trim the end of the string */
+ for (p = time + strlen(time) - 1; isspace(*p); p--) {
+ *p = '\0';
+ }
+
+ return (time);
+}
+
+/*
+ * Name: get_ocsp_uri
+ * Description: Examines an X509 certificate and retrieves the embedded
+ * OCSP Responder URI if one exists.
+ *
+ * Arguments: cert - The cert to inspect
+ * uri - pointer where the newly-allocated URI is placed, if found
+ *
+ * Returns : Success if the URI was found. Appropriate status otherwise.
+ */
+static boolean_t
+get_ocsp_uri(X509 *cert, char **uri)
+{
+ AUTHORITY_INFO_ACCESS *aia;
+ ACCESS_DESCRIPTION *ad;
+ int i;
+
+ if (getenv("PKGWEB_TEST_OCSP")) {
+ *uri = xstrdup(getenv("PKGWEB_TEST_OCSP"));
+ return (B_TRUE);
+ }
+
+ /* get the X509v3 extension holding the OCSP URI */
+ if ((aia = X509_get_ext_d2i(cert, NID_info_access,
+ NULL, NULL)) != NULL) {
+ for (i = 0; i < sk_ACCESS_DESCRIPTION_num(aia); i++) {
+ ad = sk_ACCESS_DESCRIPTION_value(aia, i);
+ if (OBJ_obj2nid(ad->method) == NID_ad_OCSP) {
+ if (ad->location->type == GEN_URI) {
+ *uri =
+ xstrdup((char *)ASN1_STRING_data(ad->location->d.ia5));
+ return (B_TRUE);
+ }
+ }
+ }
+ }
+
+ /* no URI was found */
+ return (B_FALSE);
+}
+
+/*
+ * Name: ocsp_verify
+ * Description: Attempts to contact an OCSP Responder and ascertain the validity
+ * of an X509 certificate.
+ *
+ * Arguments: err - Error object to add error messages to
+ * cert - The cert to validate
+ * issuer - The certificate of the issuer of 'cert'
+ * uri - The OCSP Responder URI
+ * cas - The trusted CA certificates used to verify the
+ * signed OCSP response
+ * Returns : Success - The OCSP Responder reported a 'good'
+ * status for the cert otherwise, appropriate
+ * error is returned.
+ */
+static OCSPStatus
+ocsp_verify(PKG_ERR *err, X509 *cert, X509 *issuer,
+ char *uri, url_hport_t *proxy, STACK_OF(X509) *cas)
+{
+ OCSP_CERTID *id;
+ OCSP_REQUEST *req;
+ OCSP_RESPONSE *resp;
+ OCSP_BASICRESP *bs;
+ BIO *cbio, *mem;
+ char ocspbuf[OCSP_BUFSIZ];
+ char *host = NULL, *portstr = NULL, *path = "/", *p, *q, *r;
+ int port, status, reason;
+ int len, retval, respcode, use_ssl = 0;
+ ASN1_GENERALIZEDTIME *rev, *thisupd, *nextupd;
+ char *subjname;
+ time_t currtime;
+ char currtimestr[ATTR_MAX];
+ unsigned long errcode;
+ const char *err_reason;
+
+ subjname = get_subject_display_name(cert);
+
+ /* parse the URI into its constituent parts */
+ if (OCSP_parse_url(uri, &host, &portstr, &path, &use_ssl) == NULL) {
+ pkgerr_add(err, PKGERR_PARSE, gettext(ERR_OCSP_PARSE), uri);
+ return (OCSPParse);
+ }
+
+ /* we don't currently support SSL-based OCSP Responders */
+ if (use_ssl) {
+ pkgerr_add(err, PKGERR_PARSE, gettext(ERR_OCSP_UNSUP), uri);
+ return (OCSPUnsupported);
+ }
+
+ /* default port if none specified */
+ if (portstr == NULL) {
+ port = (int)URL_DFLT_SRVR_PORT;
+ } else {
+ port = (int)strtoul(portstr, &r, 10);
+ if (*r != '\0') {
+ pkgerr_add(err, PKGERR_PARSE,
+ gettext(ERR_OCSP_PARSE), uri);
+ return (OCSPParse);
+ }
+ }
+
+ /* allocate new request structure */
+ if ((req = OCSP_REQUEST_new()) == NULL) {
+ pkgerr_add(err, PKGERR_PARSE, gettext(ERR_MEM));
+ return (OCSPMem);
+ }
+
+ /* convert cert and issuer fields into OCSP request data */
+ if ((id = OCSP_cert_to_id(NULL, cert, issuer)) == NULL) {
+ pkgerr_add(err, PKGERR_PARSE, gettext(ERR_PKG_INTERNAL),
+ __FILE__, __LINE__);
+ return (OCSPInternal);
+ }
+
+ /* fill out request structure with request data */
+ if ((OCSP_request_add0_id(req, id)) == NULL) {
+ pkgerr_add(err, PKGERR_PARSE, gettext(ERR_PKG_INTERNAL),
+ __FILE__, __LINE__);
+ return (OCSPInternal);
+ }
+
+ /* add nonce */
+ OCSP_request_add1_nonce(req, NULL, -1);
+
+ /* connect to host, or proxy */
+ if (proxy != NULL) {
+ if ((cbio = BIO_new_connect(proxy->hostname)) == NULL) {
+ pkgerr_add(err, PKGERR_PARSE, gettext(ERR_MEM));
+ return (OCSPMem);
+ }
+
+ /*
+ * BIO_set_conn_int_port takes an int *, so let's give it one
+ * rather than an ushort_t *
+ */
+ port = proxy->port;
+ (void) BIO_set_conn_int_port(cbio, &port);
+ if (BIO_do_connect(cbio) <= 0) {
+ pkgerr_add(err, PKGERR_PARSE,
+ gettext(ERR_OCSP_CONNECT),
+ proxy->hostname, port);
+ return (OCSPConnect);
+ }
+ } else {
+ if ((cbio = BIO_new_connect(host)) == NULL) {
+ pkgerr_add(err, PKGERR_PARSE, gettext(ERR_MEM));
+ return (OCSPMem);
+ }
+
+ (void) BIO_set_conn_int_port(cbio, &port);
+ if (BIO_do_connect(cbio) <= 0) {
+ pkgerr_add(err, PKGERR_PARSE,
+ gettext(ERR_OCSP_CONNECT),
+ host, port);
+ return (OCSPConnect);
+ }
+ }
+
+ /* calculate length of binary request data */
+ len = i2d_OCSP_REQUEST(req, NULL);
+
+ /* send the request headers */
+ if (proxy != NULL) {
+ retval = BIO_printf(cbio, OCSP_REQUEST_FORMAT, uri, len);
+ } else {
+ retval = BIO_printf(cbio, OCSP_REQUEST_FORMAT, path, len);
+ }
+
+ if (retval <= 0) {
+ pkgerr_add(err, PKGERR_PARSE, gettext(ERR_OCSP_SEND), host);
+ return (OCSPRequest);
+ }
+
+ /* send the request binary data */
+ if (i2d_OCSP_REQUEST_bio(cbio, req) <= 0) {
+ pkgerr_add(err, PKGERR_PARSE, gettext(ERR_OCSP_SEND), host);
+ return (OCSPRequest);
+ }
+
+ /*
+ * read the response into a memory BIO, so we can 'gets'
+ * (socket bio's don't support BIO_gets)
+ */
+ if ((mem = BIO_new(BIO_s_mem())) == NULL) {
+ pkgerr_add(err, PKGERR_PARSE, gettext(ERR_MEM));
+ return (OCSPMem);
+ }
+
+ while ((len = BIO_read(cbio, ocspbuf, OCSP_BUFSIZ))) {
+ if (len < 0) {
+ pkgerr_add(err, PKGERR_PARSE,
+ gettext(ERR_OCSP_READ), host);
+ return (OCSPRequest);
+ }
+ if (BIO_write(mem, ocspbuf, len) != len) {
+ pkgerr_add(err, PKGERR_PARSE, gettext(ERR_MEM));
+ return (OCSPMem);
+ }
+ }
+
+ /* now get the first line of the response */
+ if (BIO_gets(mem, ocspbuf, OCSP_BUFSIZ) <= 0) {
+ pkgerr_add(err, PKGERR_PARSE, gettext(ERR_OCSP_RESP_PARSE));
+ return (OCSPRequest);
+ }
+
+ /* parse the header response */
+ /* it should look like "HTTP/x.x 200 OK" */
+
+ /* skip past the protocol info */
+ for (p = ocspbuf; (*p != '\0') && !isspace(*p); p++)
+ continue;
+
+ /* skip past whitespace betwen protocol and start of response code */
+ while ((*p != '\0') && isspace(*p)) {
+ p++;
+ }
+
+ if (*p == '\0') {
+ /* premature end */
+ pkgerr_add(err, PKGERR_PARSE,
+ gettext(ERR_OCSP_RESP_PARSE), ocspbuf);
+ return (OCSPRequest);
+ }
+
+ /* find end of response code */
+ for (q = p; (*q != NULL) && !isspace(*q); q++)
+ continue;
+
+ /* mark end of response code */
+ *q++ = '\0';
+
+ /* parse response code */
+ respcode = strtoul(p, &r, 10);
+ if (*r != '\0') {
+ pkgerr_add(err, PKGERR_PARSE,
+ gettext(ERR_OCSP_RESP_PARSE), ocspbuf);
+ return (OCSPRequest);
+ }
+
+ /* now find beginning of the response string */
+ while ((*q != NULL) && isspace(*q)) {
+ q++;
+ }
+
+ /* trim whitespace from end of message */
+ for (r = (q + strlen(q) - 1); isspace(*r); r--) {
+ *r = '\0';
+ }
+
+ /* response must be OK */
+ if (respcode != 200) {
+ pkgerr_add(err, PKGERR_PARSE,
+ gettext(ERR_OCSP_RESP_NOTOK), 200,
+ respcode, q);
+ return (OCSPRequest);
+ }
+
+ /* read headers, looking for content-type or a blank line */
+ while (BIO_gets(mem, ocspbuf, OCSP_BUFSIZ) > 0) {
+
+ /* if we get a content type, make sure it's the right type */
+ if (ci_strneq(ocspbuf, CONTENT_TYPE_HDR,
+ strlen(CONTENT_TYPE_HDR))) {
+
+ /* look for the delimiting : */
+ p = strchr(ocspbuf + strlen(CONTENT_TYPE_HDR), ':');
+
+ if (p == NULL) {
+ pkgerr_add(err, PKGERR_PARSE,
+ gettext(ERR_OCSP_RESP_PARSE), ocspbuf);
+ return (OCSPResponder);
+ }
+
+ /* skip over ':' */
+ p++;
+
+ /* find beginning of the content type */
+ while ((*p != NULL) && isspace(*p)) {
+ p++;
+ }
+
+ if (!ci_strneq(p, CONTENT_OCSP_RESP,
+ strlen(CONTENT_OCSP_RESP))) {
+ /* response is not right type */
+ pkgerr_add(err, PKGERR_PARSE,
+ gettext(ERR_OCSP_RESP_TYPE),
+ p, CONTENT_OCSP_RESP);
+ return (OCSPResponder);
+ }
+
+ /* continue with next header line */
+ continue;
+ }
+
+ /* scan looking for a character */
+ for (p = ocspbuf; (*p != '\0') && isspace(*p); p++) {
+ continue;
+ }
+ /*
+ * if we got to the end of the line with
+ * no chars, then this is a blank line
+ */
+ if (*p == '\0') {
+ break;
+ }
+ }
+
+
+ if (*p != '\0') {
+ /* last line was not blank */
+ pkgerr_add(err, PKGERR_PARSE,
+ gettext(ERR_OCSP_RESP_PARSE), ocspbuf);
+ return (OCSPResponder);
+ }
+
+ /* now read in the binary response */
+ if ((resp = d2i_OCSP_RESPONSE_bio(mem, NULL)) == NULL) {
+ pkgerr_add(err, PKGERR_PARSE, gettext(ERR_OCSP_READ), host);
+ return (OCSPResponder);
+ }
+
+ /* free temp BIOs */
+ (void) BIO_free(mem);
+ (void) BIO_free_all(cbio);
+ cbio = NULL;
+
+ /* make sure request was successful */
+ if (OCSP_response_status(resp) != OCSP_RESPONSE_STATUS_SUCCESSFUL) {
+ pkgerr_add(err, PKGERR_PARSE, gettext(ERR_OCSP_RESP_NOTOK),
+ OCSP_RESPONSE_STATUS_SUCCESSFUL,
+ OCSP_response_status(resp),
+ OCSP_response_status_str(OCSP_response_status(resp)));
+ return (OCSPResponder);
+ }
+
+ /* parse binary response into internal structure */
+ if ((bs = OCSP_response_get1_basic(resp)) == NULL) {
+ pkgerr_add(err, PKGERR_PARSE, gettext(ERR_OCSP_READ), host);
+ return (OCSPParse);
+ }
+
+ /*
+ * From here to the end of the code, the return values
+ * should be hard failures
+ */
+
+ /* verify the response, warn if no nonce */
+ if (OCSP_check_nonce(req, bs) <= 0) {
+ logerr(pkg_gt(WRN_OCSP_RESP_NONCE));
+ }
+
+ if (OCSP_basic_verify(bs, cas, NULL, OCSP_TRUSTOTHER) <= 0) {
+ while ((errcode = ERR_get_error()) != NULL) {
+ err_reason = ERR_reason_error_string(errcode);
+ if (err_reason == NULL) {
+ err_reason =
+ gettext(ERR_SIG_INT);
+ }
+ pkgerr_add(err, PKGERR_PARSE, (char *)err_reason);
+ }
+ pkgerr_add(err, PKGERR_PARSE, gettext(ERR_OCSP_VERIFY_FAIL),
+ uri);
+ return (OCSPVerify);
+ }
+
+ /* check the validity of our certificate */
+ if (OCSP_resp_find_status(bs, id, &status, &reason,
+ &rev, &thisupd, &nextupd) == NULL) {
+ pkgerr_add(err, PKGERR_PARSE,
+ gettext(ERR_OCSP_VERIFY_NO_STATUS), subjname);
+ return (OCSPVerify);
+ }
+
+ if ((currtime = time(NULL)) == (time_t)-1) {
+ pkgerr_add(err, PKGERR_PARSE,
+ gettext(ERR_OCSP_VERIFY_NOTIME));
+ return (OCSPVerify);
+ }
+
+ (void) strlcpy(currtimestr, ctime(&currtime), ATTR_MAX);
+
+ /* trim end */
+ for (r = currtimestr + strlen(currtimestr) - 1;
+ isspace(*r); r--) {
+ *r = '\0';
+ }
+
+ if (!OCSP_check_validity(thisupd, nextupd,
+ OCSP_VALIDITY_PERIOD, -1)) {
+ if (nextupd != NULL) {
+ pkgerr_add(err, PKGERR_PARSE,
+ gettext(ERR_OCSP_VERIFY_VALIDITY),
+ get_time_string(thisupd), get_time_string(nextupd),
+ currtimestr);
+ } else {
+ pkgerr_add(err, PKGERR_PARSE,
+ gettext(ERR_OCSP_VERIFY_VALIDITY),
+ get_time_string(thisupd),
+ currtimestr);
+ }
+ return (OCSPVerify);
+ }
+
+ if (status != V_OCSP_CERTSTATUS_GOOD) {
+ pkgerr_add(err, PKGERR_PARSE,
+ gettext(ERR_OCSP_VERIFY_STATUS), subjname,
+ OCSP_cert_status_str(status));
+ return (OCSPVerify);
+ }
+
+ /* everythign checks out */
+ return (OCSPSuccess);
+}
+
+/*
+ * Name: get_issuer
+ * Description: Attempts to find the issuing certificate for a given certificate
+ * This will look in both the list of trusted certificates found in
+ * the X509_STORE_CTX structure, as well as the list of untrusted
+ * chain certificates found in the X509_STORE_CTX structure.
+ * Arguments:
+ * issuer - The resulting issuer cert is placed here, if found
+ * ctx - The current verification context
+ * x - The certificate whose issuer we are looking for
+ * Returns : Success - The issuer cert was found and placed in *issuer.
+ * otherwise, appropriate error is returned.
+ */
+static int
+get_issuer(X509 **issuer, X509_STORE_CTX *ctx, X509 *x)
+{
+ int i, ok;
+
+ /*
+ * first look in the list of trusted
+ * certs, using the context's method to do so
+ */
+ if ((ok = ctx->get_issuer(issuer, ctx, x)) > 0) {
+ return (ok);
+ }
+
+ if (ctx->untrusted != NULL) {
+ /* didn't find it in trusted certs, look through untrusted */
+ for (i = 0; i < sk_X509_num(ctx->untrusted); i++) {
+ if (X509_check_issued(sk_X509_value(ctx->untrusted, i),
+ x) == X509_V_OK) {
+ *issuer = sk_X509_value(ctx->untrusted, i);
+ return (1);
+ }
+ }
+ }
+ *issuer = NULL;
+ return (0);
+}
+
+/*
+ * Name: parse_url_proxy
+ * Description: Parses URL and optional proxy specification, populates static
+ * 'ps' structure
+ *
+ * Arguments: err - where to record any errors.
+ * url - URL to parse
+ * proxy - proxy to parse, or NULL for no proxy
+ * proxy_port - Default proxy port to use if no proxy
+ * port specified in 'proxy'
+ *
+ * Returns : B_TRUE - success, B_FALSE otherwise
+ * on success, 'ps->url' and 'ps->proxy' are populated
+ * with parsed data.
+ */
+static boolean_t
+parse_url_proxy(PKG_ERR *err, char *url, char *proxy, ushort_t proxy_port)
+{
+ boolean_t ret = B_TRUE;
+ if (!path_valid(url)) {
+ ret = B_FALSE;
+ goto cleanup;
+ }
+
+ if (url_parse(url, &ps->url) != URL_PARSE_SUCCESS) {
+ pkgerr_add(err, PKGERR_WEB, gettext(ERR_PARSE_URL), url);
+ ret = B_FALSE;
+ goto cleanup;
+ }
+
+ if (proxy != NULL) {
+ if (url_parse_hostport(proxy, &ps->proxy, proxy_port)
+ != URL_PARSE_SUCCESS) {
+ pkgerr_add(err, PKGERR_WEB,
+ gettext(ERR_BAD_PROXY), proxy);
+ ret = B_FALSE;
+ goto cleanup;
+ }
+ }
+
+cleanup:
+ return (ret);
+}
+
+/*
+ * Name: web_setup
+ * Description: Initializes http library settings
+ *
+ * Arguments: err - where to record any errors.
+ *
+ * Returns : B_TRUE - success, B_FALSE otherwise
+ */
+static boolean_t
+web_setup(PKG_ERR *err)
+{
+ boolean_t ret = B_TRUE;
+ static boolean_t keepalive = B_TRUE;
+
+ if ((ps->hps = http_srv_init(&ps->url)) == NULL) {
+ pkgerr_add(err, PKGERR_WEB, gettext(ERR_INIT_SESS), ps->url);
+ ret = B_FALSE;
+ goto cleanup;
+ }
+
+ if (getenv("WEBPKG_DEBUG") != NULL) {
+ http_set_verbose(B_TRUE);
+ }
+
+ if (ps->proxy.hostname[0] != '\0' &&
+ http_set_proxy(ps->hps, &ps->proxy) != 0) {
+ pkgerr_add(err, PKGERR_WEB, gettext(ERR_INIT_SESS), ps->url);
+ ret = B_FALSE;
+ goto cleanup;
+ }
+ if (http_set_keepalive(ps->hps, keepalive) != 0) {
+ pkgerr_add(err, PKGERR_WEB, gettext(ERR_INIT_SESS), ps->url);
+ ret = B_FALSE;
+ goto cleanup;
+ }
+ if (http_set_socket_read_timeout(ps->hps, ps->timeout) != 0) {
+ pkgerr_add(err, PKGERR_WEB, gettext(ERR_INIT_SESS), ps->url);
+ ret = B_FALSE;
+ goto cleanup;
+ }
+ if (http_set_random_file(ps->hps, RANDOM) != 0) {
+ pkgerr_add(err, PKGERR_WEB, gettext(ERR_INIT_SESS), ps->url);
+ ret = B_FALSE;
+ goto cleanup;
+ }
+
+ (void) http_set_p12_format(B_TRUE);
+
+cleanup:
+ return (ret);
+}
+
+/*
+ * Name: web_connect
+ * Description: Makes connection with URL stored in static 'ps' structure.
+ *
+ * Arguments: err - where to record any errors.
+ *
+ * Returns : WEB_OK - connection successful
+ * WEB_VERIFY_SETUP - Unable to complete necessary
+ * SSL setup
+ * WEB_CONNREFUSED - Connection was refused to web site
+ * WEB_HOSTDOWN - Host was not responding to request
+ * WEB_NOCONNECT - Some other connection failure
+ */
+static WebStatus
+web_connect(PKG_ERR *err)
+{
+ STACK_OF(X509) *sec_cas = NULL;
+ char *path;
+ WebStatus ret = WEB_OK;
+ ulong_t errcode;
+ uint_t errsrc;
+ int my_errno = 0;
+ const char *libhttperr = NULL;
+
+ if (ps->url.https == B_TRUE) {
+ /* get CA certificates */
+ if (find_ca_certs(err, ps->keystore, &sec_cas) != 0) {
+ ret = WEB_VERIFY_SETUP;
+ goto cleanup;
+ }
+
+ if (sk_X509_num(sec_cas) < 1) {
+ /* no trusted websites */
+ pkgerr_add(err, PKGERR_WEB,
+ gettext(ERR_KEYSTORE_NOTRUST));
+ ret = WEB_VERIFY_SETUP;
+ goto cleanup;
+ }
+
+ /*
+ * write out all CA certs to temp file. libwanboot should
+ * have an interface for giving it a list of trusted certs
+ * through an in-memory structure, but currently that does
+ * not exist
+ */
+ if ((path = write_ca_file(err, ps->dwnld_dir, sec_cas,
+ WEB_CA_PHRASE)) == NULL) {
+ ret = WEB_VERIFY_SETUP;
+ goto cleanup;
+ }
+
+ ps->certfile = path;
+ if (http_set_password(ps->hps, WEB_CA_PHRASE) != 0) {
+ pkgerr_add(err, PKGERR_WEB,
+ gettext(ERR_HTTPS_PASSWD));
+ ret = WEB_VERIFY_SETUP;
+ goto cleanup;
+ }
+
+ if (http_set_certificate_authority_file(path) != 0) {
+ pkgerr_add(err, PKGERR_WEB,
+ gettext(ERR_HTTPS_CA));
+ ret = WEB_VERIFY_SETUP;
+ goto cleanup;
+ }
+ }
+
+ if (http_srv_connect(ps->hps) != 0) {
+ while ((errcode = http_get_lasterr(ps->hps, &errsrc)) != 0) {
+ /* Have an error - is it EINTR? */
+ if (errsrc == ERRSRC_SYSTEM) {
+ my_errno = errcode;
+ break;
+ } else if (libhttperr == NULL) {
+ /* save the first non-system error message */
+ libhttperr = http_errorstr(errsrc, errcode);
+ }
+ }
+ switch (my_errno) {
+ case EINTR:
+ case ETIMEDOUT:
+ /* Timed out. Try, try again */
+ ret = WEB_TIMEOUT;
+ break;
+ case ECONNREFUSED:
+ ret = WEB_CONNREFUSED;
+ break;
+ case EHOSTDOWN:
+ ret = WEB_HOSTDOWN;
+ break;
+ default:
+ /* some other fatal error */
+ ret = WEB_NOCONNECT;
+ if (libhttperr == NULL) {
+ pkgerr_add(err, PKGERR_WEB,
+ gettext(ERR_INIT_CONN),
+ ps->url.hport.hostname);
+ } else {
+ pkgerr_add(err, PKGERR_WEB,
+ gettext(ERR_HTTP), libhttperr);
+ }
+ break;
+ }
+ }
+cleanup:
+ return (ret);
+}
+
+/*
+ * Name: write_ca_file
+ * Description: Writes out a PKCS12 file containing all trusted certs
+ * found in keystore recorded in static 'ps' structure
+ *
+ * This routine is used because the libwanboot library's
+ * HTTPS routines cannot accept trusted certificates
+ * through an in-memory structure, when initiating an
+ * SSL connection. They must be in a PKCS12, which is
+ * admittedly a poor interface.
+ *
+ * Arguments: err - where to record any errors.
+ * tmpdir - Directory to write certificate file in
+ * cacerts - Certs to write out
+ * passwd - password used to encrypt certs
+ *
+ * Returns : path to resulting file, if successfullly written,
+ * otherwise NULL.
+ */
+static char
+*write_ca_file(PKG_ERR *err, char *tmpdir, STACK_OF(X509) *cacerts,
+ char *passwd)
+{
+ int fd, len;
+ FILE *fp;
+ PKCS12 *p12 = NULL;
+ char *ret = NULL;
+ static char tmp_file[PATH_MAX] = "";
+ struct stat buf;
+
+ if (!path_valid(tmpdir)) {
+ pkgerr_add(err, PKGERR_WEB, gettext(MSG_NOTEMP), tmpdir);
+ goto cleanup;
+ }
+
+ /* mkstemp replaces XXXXXX with a unique string */
+ if (((len = snprintf(tmp_file, PATH_MAX, "%s/%sXXXXXX", tmpdir,
+ "cert")) < 0) ||
+ (len >= PATH_MAX)) {
+ pkgerr_add(err, PKGERR_WEB, gettext(MSG_NOTEMP), tmpdir);
+ goto cleanup;
+ }
+
+ if ((fd = mkstemp(tmp_file)) == -1) {
+ pkgerr_add(err, PKGERR_WEB, gettext(MSG_NOTMPFIL), tmp_file);
+ goto cleanup;
+ }
+
+ if (fstat(fd, &buf) == -1) {
+ pkgerr_add(err, PKGERR_WEB, gettext(MSG_NOTMPFIL), tmp_file);
+ goto cleanup;
+ }
+
+ if (!S_ISREG(buf.st_mode)) {
+ pkgerr_add(err, PKGERR_WEB, gettext(MSG_NOTMPFIL), tmp_file);
+ goto cleanup;
+ }
+
+ if ((fp = fdopen(fd, "w")) == NULL) {
+ pkgerr_add(err, PKGERR_WEB, gettext(MSG_NOTMPFIL), tmp_file);
+ goto cleanup;
+ }
+
+ if ((p12 = sunw_PKCS12_create(passwd, NULL, NULL, cacerts)) == NULL) {
+ pkgerr_add(err, PKGERR_WEB,
+ gettext(ERR_KEYSTORE_FORM), tmp_file);
+ goto cleanup;
+ }
+
+ if (i2d_PKCS12_fp(fp, p12) == 0) {
+ pkgerr_add(err, PKGERR_WEB,
+ gettext(ERR_KEYSTORE_FORM), tmp_file);
+ goto cleanup;
+ }
+
+ (void) fflush(fp);
+ (void) fclose(fp);
+ (void) close(fd);
+ fp = NULL;
+ fd = -1;
+ ret = tmp_file;
+
+cleanup:
+ if (p12 != NULL)
+ PKCS12_free(p12);
+ if (fp != NULL)
+ (void) fclose(fp);
+ if (fd != -1) {
+ (void) close(fd);
+ (void) unlink(tmp_file);
+ }
+
+ return (ret);
+}
+
+/*
+ * Name: web_send_request
+ * Description: Sends an HTTP request for a file to the
+ * web server being communicated with in the static
+ * 'ps' structure
+ *
+ * Arguments: err - where to record any errors.
+ * request_type - HTTP_REQ_TYPE_HEAD to send an HTTP HEAD request,
+ * or HTTP_REQ_TYPE_GET to send an HTTP GET request
+ * cp -
+ * Returns : WEB_OK - request sent successfully
+ * WEB_CONNREFUSED - Connection was refused to web site
+ * WEB_HOSTDOWN - Host was not responding to request
+ * WEB_NOCONNECT - Some other connection failure
+ */
+static WebStatus
+web_send_request(PKG_ERR *err, int request_type, int cp, int ep)
+{
+ WebStatus ret = WEB_OK;
+ ulong_t errcode;
+ uint_t errsrc;
+ int my_errno = 0;
+ const char *libhttperr = NULL;
+ switch (request_type) {
+ case HTTP_REQ_TYPE_HEAD:
+ if ((http_head_request(ps->hps, ps->url.abspath)) != 0) {
+ while ((errcode = http_get_lasterr(ps->hps,
+ &errsrc)) != 0) {
+ /* Have an error - is it EINTR? */
+ if (errsrc == ERRSRC_SYSTEM) {
+ my_errno = errcode;
+ break;
+ } else if (libhttperr == NULL) {
+ /* save first non-system error message */
+ libhttperr =
+ http_errorstr(errsrc, errcode);
+ }
+ }
+ switch (my_errno) {
+ case EINTR:
+ case ETIMEDOUT:
+ /* Timed out. Try, try again */
+ ret = WEB_TIMEOUT;
+ break;
+ case ECONNREFUSED:
+ ret = WEB_CONNREFUSED;
+ break;
+ case EHOSTDOWN:
+ ret = WEB_HOSTDOWN;
+ break;
+ default:
+ /* some other fatal error */
+ ret = WEB_NOCONNECT;
+ if (libhttperr == NULL) {
+ pkgerr_add(err, PKGERR_WEB,
+ gettext(ERR_INIT_CONN),
+ ps->url.hport.hostname);
+ } else {
+ pkgerr_add(err, PKGERR_WEB,
+ gettext(ERR_HTTP), libhttperr);
+ }
+ break;
+ }
+ goto cleanup;
+ }
+ break;
+
+ case HTTP_REQ_TYPE_GET:
+ if (cp && ep) {
+ if (http_get_range_request(ps->hps, ps->url.abspath,
+ cp, ep - cp) != 0) {
+ while ((errcode = http_get_lasterr(ps->hps,
+ &errsrc)) != 0) {
+ /* Have an error - is it EINTR? */
+ if (errsrc == ERRSRC_SYSTEM) {
+ my_errno = errcode;
+ break;
+ } else {
+ /*
+ * save first non-system
+ * error message
+ */
+ libhttperr =
+ http_errorstr(errsrc,
+ errcode);
+ }
+ }
+ switch (my_errno) {
+ case EINTR:
+ case ETIMEDOUT:
+ /* Timed out. Try, try again */
+ ret = WEB_TIMEOUT;
+ break;
+ case ECONNREFUSED:
+ ret = WEB_CONNREFUSED;
+ break;
+ case EHOSTDOWN:
+ ret = WEB_HOSTDOWN;
+ break;
+ default:
+ /* some other fatal error */
+ ret = WEB_NOCONNECT;
+ if (libhttperr == NULL) {
+ pkgerr_add(err, PKGERR_WEB,
+ gettext(ERR_INIT_CONN),
+ ps->url.hport.hostname);
+ } else {
+ pkgerr_add(err, PKGERR_WEB,
+ gettext(ERR_HTTP),
+ libhttperr);
+ }
+ break;
+ }
+ goto cleanup;
+ }
+
+ if (!web_eval_headers(err)) {
+ ret = WEB_NOCONNECT;
+ goto cleanup;
+ }
+ } else {
+ if ((http_get_request(ps->hps, ps->url.abspath))
+ != 0) {
+ while ((errcode = http_get_lasterr(ps->hps,
+ &errsrc)) != 0) {
+ /* Have an error - is it EINTR? */
+ if (errsrc == ERRSRC_SYSTEM) {
+ my_errno = errcode;
+ break;
+ } else {
+ /*
+ * save the first non-system
+ * error message
+ */
+ libhttperr =
+ http_errorstr(errsrc,
+ errcode);
+ }
+ }
+ switch (my_errno) {
+ case EINTR:
+ case ETIMEDOUT:
+ /* Timed out. Try, try again */
+ ret = WEB_TIMEOUT;
+ break;
+ case ECONNREFUSED:
+ ret = WEB_CONNREFUSED;
+ break;
+ case EHOSTDOWN:
+ ret = WEB_HOSTDOWN;
+ break;
+ default:
+ /* some other fatal error */
+ ret = WEB_NOCONNECT;
+ if (libhttperr == NULL) {
+ pkgerr_add(err, PKGERR_WEB,
+ gettext(ERR_INIT_CONN),
+ ps->url.hport.hostname);
+ } else {
+ pkgerr_add(err, PKGERR_WEB,
+ gettext(ERR_HTTP),
+ libhttperr);
+ }
+ break;
+ }
+ goto cleanup;
+ }
+
+ if (!web_eval_headers(err)) {
+ ret = WEB_NOCONNECT;
+ goto cleanup;
+ }
+ }
+ break;
+ default:
+ pkgerr_add(err, PKGERR_INTERNAL, gettext(ERR_PKG_INTERNAL),
+ __FILE__, __LINE__);
+ }
+
+cleanup:
+ return (ret);
+}
+
+/*
+ * Name: web_eval_headers
+ * Description: Evaluates HTTP headers returned during an HTTP request.
+ * This must be called before calling
+ * http_get_header_value().
+ *
+ * Arguments: err - where to record any errors.
+ *
+ * Returns : B_TRUE - success, B_FALSE otherwise
+ */
+static boolean_t
+web_eval_headers(PKG_ERR *err)
+{
+ const char *http_err;
+ ulong_t herr;
+ uint_t errsrc;
+
+ if (http_process_headers(ps->hps, &ps->resp) != 0) {
+ if ((ps->resp != NULL) && (ps->resp->statusmsg != NULL)) {
+ pkgerr_add(err, PKGERR_WEB, gettext(ERR_HTTP),
+ ps->resp->statusmsg);
+ }
+
+ herr = http_get_lasterr(ps->hps, &errsrc);
+ http_err = http_errorstr(errsrc, herr);
+ pkgerr_add(err, PKGERR_WEB, gettext(ERR_HTTP),
+ http_err);
+ return (B_FALSE);
+ }
+ return (B_TRUE);
+}
+
+/*
+ * Name: web_get_file
+ * Description: Downloads the file URL from the website, all of
+ * which are recorded in the static 'ps' struct
+ *
+ * Arguments: err - where to record any errors.
+ * dwnld_dir - Directory to download file into
+ * device - Where to store path to resulting
+ * file
+ * nointeract - if non-zero, do not output
+ * progress
+ * fname - name of downloaded file link in the dwnld_dir
+ *
+ * Returns : WEB_OK - download successful
+ * WEB_CONNREFUSED - Connection was refused to web site
+ * WEB_HOSTDOWN - Host was not responding to request
+ * WEB_GET_FAIL - Unable to initialize download
+ * state (temp file creation, header parsing, etc)
+ * WEB_NOCONNECT - Some other connection failure
+ */
+static WebStatus
+web_get_file(PKG_ERR *err, char *dwnld_dir, int nointeract, char **fname)
+{
+ int i, fd;
+ int n = 0;
+ ulong_t abs_pos = 0;
+ char *head_val = NULL;
+ char *lastmod_val = NULL;
+ char *bname = NULL;
+ struct stat status;
+ WebStatus ret = WEB_OK;
+ WebStatus req_ret;
+ ulong_t errcode;
+ uint_t errsrc;
+ int my_errno = 0;
+ const char *libhttperr = NULL;
+ char *disp;
+ char tmp_file[PATH_MAX];
+ int len;
+
+ ps->data.prev_cont_length =
+ ps->data.content_length =
+ ps->data.cur_pos = 0;
+
+ if ((head_val = http_get_header_value(ps->hps,
+ CONTENT_LENGTH_HDR)) != NULL) {
+ ps->data.content_length = atol(head_val);
+ } else {
+ pkgerr_add(err, PKGERR_WEB, gettext(ERR_NO_HEAD_VAL),
+ CONTENT_LENGTH_HDR);
+ ret = WEB_GET_FAIL;
+ goto cleanup;
+ }
+
+ free(head_val);
+ head_val = NULL;
+
+ if ((head_val = http_get_header_value(ps->hps,
+ CONTENT_DISPOSITION_HDR)) != NULL) {
+ /* "inline; parm=val; parm=val */
+ if ((disp = strtok(head_val, "; \t\n\f\r")) != NULL) {
+ /* disp = "inline" */
+ while ((disp = strtok(NULL, "; \t\n\f\r")) != NULL) {
+ /* disp = "parm=val" */
+ if (ci_strneq(disp, "filename=", 9)) {
+ bname = xstrdup(basename(disp + 9));
+ trim(bname);
+ dequote(bname);
+ }
+ }
+ }
+ free(head_val);
+ head_val = NULL;
+ }
+
+ if (bname == NULL) {
+ /*
+ * couldn't determine filename from header value,
+ * so take basename of URL
+ */
+ if ((bname = get_endof_string(ps->url.abspath, '/')) == NULL) {
+ /* URL is bad */
+ pkgerr_add(err, PKGERR_PARSE,
+ gettext(ERR_PARSE_URL), ps->url.abspath);
+ ret = WEB_GET_FAIL;
+ goto cleanup;
+ }
+ }
+
+ *fname = bname;
+
+ if ((head_val = http_get_header_value(ps->hps, LAST_MODIFIED_HDR))
+ != NULL) {
+
+ if ((lastmod_val = condense_lastmodified(head_val)) == NULL) {
+ pkgerr_add(err, PKGERR_WEB, gettext(ERR_BAD_HEAD_VAL),
+ LAST_MODIFIED_HDR, head_val);
+ ret = WEB_GET_FAIL;
+ goto cleanup;
+ }
+ free(head_val);
+ head_val = NULL;
+
+ if ((ps->uniqfile = get_unique_filename(dwnld_dir,
+ lastmod_val)) == NULL) {
+ pkgerr_add(err, PKGERR_WEB, gettext(ERR_OPEN_TMP));
+ ret = WEB_GET_FAIL;
+ goto cleanup;
+ }
+
+ free(lastmod_val);
+ lastmod_val = NULL;
+
+ if ((fd = open(ps->uniqfile,
+ O_NONBLOCK|O_RDWR|O_APPEND|O_CREAT|O_EXCL,
+ 640)) == -1) {
+
+ /*
+ * A partial downloaded file
+ * already exists, so open it.
+ */
+ if ((fd = open(ps->uniqfile,
+ O_NONBLOCK|O_RDWR|O_APPEND)) != -1) {
+ if (fstat(fd, &status) == -1 ||
+ !S_ISREG(status.st_mode)) {
+ pkgerr_add(err, PKGERR_WEB,
+ gettext(ERR_DWNLD_NO_CONT),
+ ps->uniqfile);
+ ret = WEB_GET_FAIL;
+ goto cleanup;
+ } else {
+ echo_out(nointeract,
+ gettext(MSG_DWNLD_PART),
+ ps->uniqfile,
+ status.st_size);
+ ps->data.prev_cont_length =
+ status.st_size;
+ }
+ } else {
+ /* unable to open partial file */
+ pkgerr_add(err, PKGERR_WEB,
+ gettext(ERR_DWNLD_NO_CONT),
+ ps->uniqfile);
+ ret = WEB_GET_FAIL;
+ goto cleanup;
+ }
+ }
+ } else {
+ /*
+ * no "Last-Modified" header, so this file is not eligible for
+ * spooling and "resuming last download" operations
+ */
+ ps->spool = B_FALSE;
+
+ /* mkstemp replaces XXXXXX with a unique string */
+ if (((len = snprintf(tmp_file, PATH_MAX,
+ "%s/%sXXXXXX", dwnld_dir, "stream")) < 0) ||
+ (len >= PATH_MAX)) {
+ pkgerr_add(err, PKGERR_WEB,
+ gettext(MSG_NOTEMP), dwnld_dir);
+ ret = WEB_GET_FAIL;
+ goto cleanup;
+ }
+
+ if ((fd = mkstemp(tmp_file)) == -1) {
+ pkgerr_add(err, PKGERR_WEB,
+ gettext(MSG_NOTMPFIL), tmp_file);
+ ret = WEB_GET_FAIL;
+ goto cleanup;
+ }
+
+ if (fstat(fd, &status) == -1 ||
+ !S_ISREG(status.st_mode)) {
+ pkgerr_add(err, PKGERR_WEB,
+ gettext(ERR_DWNLD_NO_CONT),
+ ps->uniqfile);
+ ret = WEB_GET_FAIL;
+ goto cleanup;
+ }
+
+ ps->data.prev_cont_length = 0;
+ ps->uniqfile = xstrdup(tmp_file);
+ }
+
+ /* File has already been completely downloaded */
+ if (ps->data.prev_cont_length == ps->data.content_length) {
+ echo_out(nointeract, gettext(MSG_DWNLD_PREV), ps->uniqfile);
+ ps->data.cur_pos = ps->data.prev_cont_length;
+ if (!make_link(dwnld_dir, bname)) {
+ pkgerr_add(err, PKGERR_WEB, gettext(MSG_NOTEMP),
+ dwnld_dir);
+ ret = WEB_GET_FAIL;
+ goto cleanup;
+ }
+ /* we're done, so cleanup and return success */
+ goto cleanup;
+ } else if (ps->data.prev_cont_length != 0) {
+ ps->data.cur_pos = ps->data.prev_cont_length;
+ }
+
+ if (!ck_dwnld_dir_space(err, dwnld_dir,
+ (ps->data.prev_cont_length != 0) ?
+ (ps->data.content_length - ps->data.cur_pos) :
+ ps->data.content_length)) {
+ ret = WEB_GET_FAIL;
+ goto cleanup;
+ }
+
+ if ((req_ret = web_send_request(err, HTTP_REQ_TYPE_GET,
+ ps->data.cur_pos, ps->data.content_length)) != WEB_OK) {
+ ret = req_ret;
+ goto cleanup;
+ }
+
+ if (ps->data.prev_cont_length != 0)
+ echo_out(nointeract, gettext(MSG_DWNLD_CONT));
+ else
+ echo_out(nointeract, gettext(MSG_DWNLD));
+
+ progress_setup(nointeract, ps->data.content_length);
+
+ /* Download the file a BLOCK at a time */
+ while (ps->data.cur_pos < ps->data.content_length) {
+ progress_report(nointeract, abs_pos);
+ i = ((ps->data.content_length - ps->data.cur_pos) < BLOCK) ?
+ (ps->data.content_length - ps->data.cur_pos)
+ : BLOCK;
+ if ((n = http_read_body(ps->hps, ps->content, i)) <= 0) {
+ while ((errcode = http_get_lasterr(ps->hps,
+ &errsrc)) != 0) {
+ /* Have an error - is it EINTR? */
+ if (errsrc == ERRSRC_SYSTEM) {
+ my_errno = errcode;
+ break;
+ } else {
+ /*
+ * save first non-system
+ * error message
+ */
+ libhttperr =
+ http_errorstr(errsrc, errcode);
+ }
+ }
+ switch (my_errno) {
+ case EINTR:
+ case ETIMEDOUT:
+ /* Timed out. Try, try again */
+ ret = WEB_TIMEOUT;
+ break;
+ case ECONNREFUSED:
+ ret = WEB_CONNREFUSED;
+ break;
+ case EHOSTDOWN:
+ ret = WEB_HOSTDOWN;
+ break;
+ default:
+ /* some other fatal error */
+ ret = WEB_NOCONNECT;
+ if (libhttperr == NULL) {
+ pkgerr_add(err, PKGERR_WEB,
+ gettext(ERR_INIT_CONN),
+ ps->url.hport.hostname);
+ } else {
+ pkgerr_add(err, PKGERR_WEB,
+ gettext(ERR_HTTP), libhttperr);
+ }
+ break;
+ }
+ goto cleanup;
+ }
+ if ((n = write(fd, ps->content, n)) == 0) {
+ pkgerr_add(err, PKGERR_WEB, gettext(ERR_WRITE),
+ ps->uniqfile, strerror(errno));
+ ret = WEB_GET_FAIL;
+ goto cleanup;
+ }
+ ps->data.cur_pos += n;
+ abs_pos += n;
+ }
+
+ progress_finish(nointeract);
+ echo_out(nointeract, gettext(MSG_DWNLD_COMPLETE));
+
+ if (!make_link(dwnld_dir, bname)) {
+ pkgerr_add(err, PKGERR_WEB, gettext(MSG_NOTEMP),
+ dwnld_dir);
+ ret = WEB_GET_FAIL;
+ goto cleanup;
+ }
+
+cleanup:
+ sync();
+ if (fd != -1) {
+ (void) close(fd);
+ }
+
+ if (head_val != NULL)
+ free(head_val);
+
+ if (lastmod_val != NULL)
+ free(lastmod_val);
+
+ return (ret);
+}
+
+/*
+ * Name: make_link
+ * Description: Create new link to file being downloaded
+ *
+ * Arguments: dwnld_dir - directory in which downloaded file exists
+ * bname - name of link
+ *
+ * Returns : B_TRUE - success, B_FALSE otherwise
+ */
+static boolean_t
+make_link(char *dwnld_dir, char *bname)
+{
+ int len;
+
+ if ((ps->link = (char *)xmalloc(PATH_MAX)) == NULL)
+ return (B_FALSE);
+ if (((len = snprintf(ps->link, PATH_MAX, "%s/%s",
+ dwnld_dir, bname)) < 0) ||
+ len >= PATH_MAX)
+ return (B_FALSE);
+
+ (void) link(ps->uniqfile, ps->link);
+
+ return (B_TRUE);
+}
+
+/*
+ * Name: get_startof_string
+ * Description: searches string for token, returns a newly-allocated
+ * substring of the given string up to, but not
+ * including, token. for example
+ * get_startof_string("abcd", 'c') will return "ab"
+ *
+ * Arguments: path - path to split
+ * token - character to split on
+ *
+ * Returns : substring of 'path', up to, but not including,
+ * token, if token appears in path. Otherwise,
+ * returns NULL.
+ */
+char *
+get_startof_string(char *path, char token)
+{
+ char *p, *p2;
+
+ if (path == NULL)
+ return (NULL);
+
+ p = xstrdup(path);
+
+ p2 = strchr(p, token);
+ if (p2 == NULL) {
+ free(p);
+ return (NULL);
+ } else {
+ *p2 = '\0';
+ return (p);
+ }
+}
+
+/*
+ * Name: get_endof_string
+ * Description: searches string for token, returns a
+ * newly-allocated substring of the given string,
+ * starting at character following token, to end of
+ * string.
+ *
+ * for example get_end_string("abcd", 'c')
+ * will return "d"
+ *
+ * Arguments: path - path to split
+ * token - character to split on
+ *
+ * Returns : substring of 'path', beginning at character
+ * following token, to end of string, if
+ * token appears in path. Otherwise,
+ * returns NULL.
+ */
+char *
+get_endof_string(char *path, char token)
+{
+ char *p, *p2;
+
+ if (path == NULL)
+ return (NULL);
+
+ p = xstrdup(path);
+
+ if ((p2 = strrchr(p, token)) == NULL) {
+ return (NULL);
+ }
+
+ return (p2 + 1);
+}
+
+/*
+ * Name: progress_setup
+ * Description: Initialize session for reporting progress
+ *
+ * Arguments: nointeract - if non-zero, do not do anything
+ * ulong_t - size of job to report progress for
+ *
+ * Returns : none
+ */
+static void
+progress_setup(int nointeract, ulong_t size_of_load)
+{
+ ulong_t divisor;
+ ulong_t term_width = TERM_WIDTH;
+
+ if (nointeract)
+ return;
+
+ if (size_of_load > MED_DWNLD && size_of_load < LARGE_DWNLD)
+ divisor = MED_DIVISOR;
+ else if (size_of_load > LARGE_DWNLD) {
+ term_width = TERM_WIDTH - 8;
+ divisor = LARGE_DIVISOR;
+ } else
+ divisor = SMALL_DIVISOR;
+
+ const_increment = size_of_load / term_width;
+ const_divider = size_of_load / divisor;
+ const_completed = 100 / divisor;
+}
+
+/*
+ * Name: progress_report
+ * Description: Report progress for current progress context,
+ * to stderr
+ *
+ * Arguments: nointeract - if non-zero, do not do anything
+ * position - how far along in the job to report.
+ * This should be <= size used during progress_setup
+ *
+ * Returns : none
+ */
+static void
+progress_report(int nointeract, ulong_t position)
+{
+ static ulong_t increment;
+ static ulong_t divider;
+
+ if (nointeract)
+ return;
+
+ if (position == 0) {
+ increment = const_increment;
+ divider = const_divider;
+ }
+ if (position > increment && position < divider) {
+ (void) putc('.', stderr);
+ increment += const_increment;
+ } else if (position > divider) {
+ completed += const_completed;
+ (void) fprintf(stderr, "%ld%c", completed, '%');
+ increment += const_increment;
+ divider += const_divider;
+ }
+}
+
+/*
+ * Name: progress_finish
+ * Description: Finalize session for reporting progress.
+ * "100%" is reported to screen
+ *
+ * Arguments: nointeract - if non-zero, do not do anything
+ *
+ * Returns : none
+ */
+static void
+progress_finish(int nointeract)
+{
+ if (nointeract)
+ return;
+
+ (void) fprintf(stderr, "%d%c\n", 100, '%');
+}
+
+/*
+ * Name: init_session
+ * Description: Initializes static 'ps' structure with default
+ * values
+ *
+ * Arguments: none
+ *
+ * Returns : B_TRUE - success, B_FALSE otherwise
+ */
+static boolean_t
+init_session(void)
+{
+ if ((ps = (WEB_SESSION *)
+ xmalloc(sizeof (WEB_SESSION))) == NULL) {
+ return (B_FALSE);
+ }
+ (void) memset(ps, 0, sizeof (*ps));
+
+ if ((ps->content = (char *)xmalloc(BLOCK)) == NULL) {
+ return (B_FALSE);
+ }
+
+ (void) memset(ps->content, 0, BLOCK);
+
+ ps->data.cur_pos = 0UL;
+ ps->data.content_length = 0UL;
+ ps->url.https = B_FALSE;
+ ps->uniqfile = NULL;
+ ps->link = NULL;
+ ps->dwnld_dir = NULL;
+ ps->spool = B_TRUE;
+ ps->errstr = NULL;
+ ps->keystore = NULL;
+
+ return (B_TRUE);
+}
+
+/*
+ * Name: ck_downld_dir_space
+ * Description: Verify enough space exists in directory to hold file
+ *
+ * Arguments: err - where to record any errors.
+ * dwnld_dir - Directory to check available space in
+ * bytes_needed - How many bytes are need
+ *
+ * Returns : B_TRUE - enough space exists in dwnld_dir to hold
+ * bytes_needed bytes, otherwise B_FALSE
+ */
+static boolean_t
+ck_dwnld_dir_space(PKG_ERR *err, char *dwnld_dir, ulong_t bytes_needed)
+{
+ u_longlong_t bytes_avail;
+ u_longlong_t block_pad;
+ struct statvfs64 status;
+
+ if (statvfs64(dwnld_dir, &status)) {
+ pkgerr_add(err, PKGERR_WEB, gettext(ERR_TMPDIR), dwnld_dir);
+ return (B_FALSE);
+ }
+
+ block_pad = (status.f_frsize ? status.f_frsize : status.f_bsize);
+ bytes_avail = status.f_bavail * block_pad;
+
+ if ((((u_longlong_t)bytes_needed) + block_pad) > bytes_avail) {
+ pkgerr_add(err, PKGERR_WEB, gettext(ERR_DISK_SPACE),
+ dwnld_dir,
+ (((u_longlong_t)bytes_needed) + block_pad) / 1024ULL,
+ bytes_avail / 1024ULL);
+ return (B_FALSE);
+ }
+
+ return (B_TRUE);
+}
+
+/*
+ * Description:
+ * This function returns a unique file name based on the parts of the
+ * URI. This is done to enable partially downloaded files to be resumed.
+ * Arguments:
+ * dir - The directory that should contain the filename.
+ * last_modified - A string representing the date of last modification,
+ * used as part of generating unique name
+ * Returns:
+ * A valid filename or NULL.
+ */
+
+static char *
+get_unique_filename(char *dir, char *last_modified)
+{
+ char *buf, *buf2, *beg_str;
+ int len;
+
+ if ((buf = (char *)xmalloc(PATH_MAX)) == NULL) {
+ return (NULL);
+ }
+ if ((buf2 = (char *)xmalloc(PATH_MAX)) == NULL) {
+ return (NULL);
+ }
+
+ /* prepare strings for being cat'ed onto */
+ buf[0] = buf2[0] = '\0';
+ /*
+ * No validation of the path is done here. We just construct the path
+ * and it must be validated later
+ */
+
+ if (dir) {
+ if (((len = snprintf(buf2, PATH_MAX, "%s/", dir)) < 0) ||
+ (len >= PATH_MAX))
+ return (NULL);
+ } else {
+ return (NULL);
+ }
+
+ if (ps->url.abspath)
+ if (strlcat(buf, ps->url.abspath, PATH_MAX) >= PATH_MAX)
+ return (NULL);
+ if (ps->url.hport.hostname)
+ if (isdigit((int)ps->url.hport.hostname[0])) {
+ if (strlcat(buf, ps->url.hport.hostname, PATH_MAX)
+ >= PATH_MAX)
+ return (NULL);
+ } else {
+ if ((beg_str =
+ get_startof_string(ps->url.hport.hostname, '.'))
+ != NULL)
+ if (strlcat(buf, beg_str, PATH_MAX) >= PATH_MAX)
+ return (NULL);
+ }
+ if (last_modified != NULL)
+ if (strlcat(buf, last_modified, PATH_MAX) >= PATH_MAX)
+ return (NULL);
+
+ if ((buf = replace_token(buf, '/', '_')) != NULL) {
+ if (strlcat(buf2, buf, PATH_MAX) >= PATH_MAX) {
+ return (NULL);
+ } else {
+ if (buf) free(buf);
+ return (buf2);
+ }
+ } else {
+ if (buf) free(buf);
+ if (buf2) free(buf2);
+ return (NULL);
+ }
+}
+
+/*
+ * Description:
+ * Removes token(s) consisting of one character from any path.
+ * Arguments:
+ * path - The path to search for the token in.
+ * token - The token to search for
+ * Returns:
+ * The path with all tokens removed or NULL.
+ */
+static char *
+replace_token(char *path, char oldtoken, char newtoken)
+{
+ char *newpath, *p;
+
+ if ((path == NULL) || (oldtoken == '\0') || (newtoken == '\0')) {
+ return (NULL);
+ }
+
+ newpath = xstrdup(path);
+
+ for (p = newpath; *p != '\0'; p++) {
+ if (*p == oldtoken) {
+ *p = newtoken;
+ }
+ }
+
+ return (newpath);
+}
+
+/*
+ * Name: trim
+ * Description: Trims whitespace from a string
+ * has been registered)
+ * Scope: private
+ * Arguments: string - string to trim. It is assumed
+ * this string is writable up to it's entire
+ * length.
+ * Returns: none
+ */
+static void
+trim(char *str)
+{
+ int len, i;
+ if (str == NULL) {
+ return;
+ }
+
+ len = strlen(str);
+ /* strip from front */
+ while (isspace(*str)) {
+ for (i = 0; i < len; i++) {
+ str[i] = str[i+1];
+ }
+ }
+
+ /* strip from back */
+ len = strlen(str);
+ while (isspace(str[len-1])) {
+ len--;
+ }
+ str[len] = '\0';
+}
+
+/*
+ * Description:
+ * Resolves double quotes
+ * Arguments:
+ * str - The string to resolve
+ * Returns:
+ * None
+ */
+static void
+dequote(char *str)
+{
+ char *cp;
+
+ if ((str == NULL) || (str[0] != '"')) {
+ /* no quotes */
+ return;
+ }
+
+ /* remove first quote */
+ memmove(str, str + 1, strlen(str) - 1);
+
+ /*
+ * scan string looking for ending quote.
+ * escaped quotes like \" don't count
+ */
+ cp = str;
+
+ while (*cp != '\0') {
+ switch (*cp) {
+ case '\\':
+ /* found an escaped character */
+ /* make sure end of string is not '\' */
+ if (*++cp != '\0') {
+ cp++;
+ }
+ break;
+
+ case '"':
+ *cp = '\0';
+ break;
+ default:
+ cp++;
+ }
+ }
+}
+
+/*
+ * Name: get_ENV_proxy
+ * Description: Retrieves setting of proxy env variable
+ *
+ * Arguments: err - where to record any errors.
+ * proxy - where to store proxy
+ *
+ * Returns : B_TRUE - http proxy was found and valid, stored in proxy
+ * B_FALSE - error, errors recorded in err
+ */
+static boolean_t
+get_ENV_proxy(PKG_ERR *err, char **proxy)
+{
+ char *buf;
+
+ if ((buf = getenv("HTTPPROXY")) != NULL) {
+ if (!path_valid(buf)) {
+ pkgerr_add(err, PKGERR_WEB,
+ gettext(ERR_ILL_ENV), "HTTPPROXY", buf);
+ return (B_FALSE);
+ } else {
+ *proxy = buf;
+ return (B_TRUE);
+ }
+ } else {
+ /* try the other env variable */
+ if ((buf = getenv("http_proxy")) != NULL) {
+ if (!path_valid(buf)) {
+ pkgerr_add(err, PKGERR_WEB,
+ gettext(ERR_ILL_ENV), "http_proxy", buf);
+ return (B_FALSE);
+ }
+ if (!strneq(buf, "http://", 7)) {
+ pkgerr_add(err, PKGERR_WEB,
+ gettext(ERR_ILL_ENV), "http_proxy", buf);
+ return (B_FALSE);
+ }
+
+ /* skip over the http:// part of the proxy "url" */
+ *proxy = buf + 7;
+ return (B_TRUE);
+ }
+ }
+
+ /* either the env variable(s) were set and valid, or not set */
+ return (B_TRUE);
+}
+
+/*
+ * Name: get_ENV_proxyport
+ * Description: Retrieves setting of PROXYPORT env variable
+ *
+ * Arguments: err - where to record any errors.
+ * port - where to store resulting port
+ *
+ * Returns : B_TRUE - string found in PROXYPORT variable, converted
+ * to decimal integer, if it exists
+ * and is valid. Or, PROXYPORT not set, port set to 1.
+ * B_FALSE - env variable set, but invalid
+ * (not a number for example)
+ */
+static boolean_t
+get_ENV_proxyport(PKG_ERR *err, ushort_t *port)
+{
+ char *buf;
+ ushort_t newport;
+ buf = getenv("HTTPPROXYPORT");
+ if (buf != NULL) {
+ if (!path_valid(buf)) {
+ pkgerr_add(err, PKGERR_WEB,
+ gettext(ERR_ILL_ENV), "HTTPPROXYPORT", buf);
+ return (B_FALSE);
+ }
+ if ((newport = atoi(buf)) == 0) {
+ pkgerr_add(err, PKGERR_WEB,
+ gettext(ERR_ILL_ENV), "HTTPPROXYPORT", buf);
+ return (B_FALSE);
+ }
+ *port = newport;
+ return (B_TRUE);
+ } else {
+ *port = 1;
+ return (B_TRUE);
+ }
+}
+
+/*
+ * Name: remove_dwnld_file
+ * Description: Removes newly-downloaded file if completely downloaded.
+ *
+ * Arguments: path - path to file to remove
+ *
+ * Returns : B_TRUE - success, B_FALSE otherwise
+ * if it's '0' (not OK) we simply return it, since the
+ * verification operation has already determined that the
+ * cert is invalid. if 'ok' is non-zero, then we do our
+ * checks, and return 0 or 1 based on if the cert is
+ * invalid or valid.
+ */
+static boolean_t
+remove_dwnld_file(char *path)
+{
+ if (path && path != NULL) {
+ /*
+ * Only remove the downloaded file if it has been completely
+ * downloaded, or is not eligible for spooling
+ */
+ if ((!ps->spool) ||
+ (ps->data.cur_pos >= ps->data.content_length)) {
+ (void) unlink(path);
+ }
+ } else {
+ return (B_FALSE);
+ }
+ return (B_TRUE);
+}
+
+/*
+ * Name: condense_lastmodifided
+ * Description: generates a substring of a last-modified string,
+ * and removes colons.
+ *
+ * Arguments: last_modified - string of the form
+ * "Wed, 23 Oct 2002 21:59:45 GMT"
+ *
+ * Returns :
+ * new string, consisting of hours/minutes/seconds only,
+ * sans any colons.
+ */
+char *
+condense_lastmodified(char *last_modified)
+{
+ char *p, *p2;
+
+ /*
+ * Last-Modified: Wed, 23 Oct 2002 21:59:45 GMT
+ * Strip the hours, minutes and seconds, without the ':'s, from
+ * the above string, void of the ':".
+ */
+
+ if (last_modified == NULL)
+ return (NULL);
+
+ if ((p = xstrdup(last_modified)) == NULL)
+ return (NULL);
+ p2 = (strstr(p, ":") - 2);
+ p2[8] = '\0';
+ return (replace_token(p2, ':', '_'));
+}
+
+/*
+ * Name: backoff
+ * Description: sleeps for a certain # of seconds after a network
+ * failure.
+ * Scope: public
+ * Arguments: none
+ * Returns: none
+ */
+void
+backoff()
+{
+ static boolean_t initted = B_FALSE;
+ int backoff;
+ long seed;
+
+ if (!initted) {
+ /* seed the rng */
+ (void) _get_random_info(&seed, sizeof (seed));
+ srand48(seed);
+ initted = B_TRUE;
+ }
+
+ backoff = drand48() * (double)cur_backoff;
+ (void) sleep(backoff);
+ if (cur_backoff < MAX_BACKOFF) {
+ /*
+ * increase maximum time we might wait
+ * next time so as to fall off over
+ * time.
+ */
+ cur_backoff *= BACKOFF_FACTOR;
+ }
+}
+
+/*
+ * Name: reset_backoff
+ * Description: notifies the backoff service that whatever was
+ * being backoff succeeded.
+ * Scope: public
+ * Arguments: none
+ * Returns: none
+ */
+void
+reset_backoff()
+{
+ cur_backoff = MIN_BACKOFF;
+}
+
+/*
+ * Name: _get_random_info
+ * Description: generate an amount of random bits. Currently
+ * only a small amount (a long long) can be
+ * generated at one time.
+ * Scope: private
+ * Arguments: buf - [RO, *RW] (char *)
+ * Buffer to copy bits into
+ * size - amount to copy
+ * Returns: B_TRUE on success, B_FALSE otherwise. The buffer is filled
+ * with the amount of bytes of random data specified.
+ */
+static boolean_t
+_get_random_info(void *buf, int size)
+{
+ struct timeval tv;
+ typedef struct {
+ long low_time;
+ long hostid;
+ } randomness;
+ randomness r;
+
+ /* if the RANDOM file exists, use it */
+ if (access(RANDOM, R_OK) == 0) {
+ if ((RAND_load_file(RANDOM, 1024 * 1024)) > 0) {
+ if (RAND_bytes((uchar_t *)buf, size) == 1) {
+ /* success */
+ return (B_TRUE);
+ }
+ }
+ }
+
+ /* couldn't use RANDOM file, so fallback to time of day and hostid */
+ (void) gettimeofday(&tv, (struct timezone *)0);
+
+ /* Wouldn't it be nice if we could hash these */
+ r.low_time = tv.tv_usec;
+ r.hostid = gethostid();
+
+ if (sizeof (r) < size) {
+ /*
+ * Can't copy correctly
+ */
+ return (B_FALSE);
+ }
+ (void) memcpy(buf, &r, size);
+ return (B_TRUE);
+}
+
+/*
+ * Name: pkg_passphrase_cb
+ * Description: Default callback that applications can use when
+ * a passphrase is needed. This routine collects
+ * a passphrase from the user using the given
+ * passphrase retrieval method set with
+ * set_passphrase_passarg(). If the method
+ * indicates an interactive prompt, then the
+ * prompt set with set_passphrase_prompt()
+ * is displayed.
+ *
+ * Arguments: buf - Buffer to copy passphrase into
+ * size - Max amount to copy to buf
+ * rw - Whether this passphrase is needed
+ * to read something off disk, or
+ * write something to disk. Applications
+ * typically want to ask twice when getting
+ * a passphrase for writing something.
+ * data - application-specific data. In this
+ * callback, data is a pointer to
+ * a keystore_passphrase_data structure.
+ *
+ * Returns: Length of passphrase collected, or -1 on error.
+ * Errors recorded in 'err' object in the *data.
+ */
+int
+pkg_passphrase_cb(char *buf, int size, int rw, void *data)
+{
+ BIO *pwdbio = NULL;
+ char passphrase_copy[MAX_PHRASELEN + 1];
+ PKG_ERR *err;
+ int passlen;
+ char *ws;
+ char prompt_copy[MAX_VERIFY_MSGLEN];
+ char *passphrase;
+ char *arg;
+
+ err = ((keystore_passphrase_data *)data)->err;
+
+ if (passarg == NULL) {
+ arg = "console";
+ } else {
+ arg = passarg;
+ }
+
+ /* default method of collecting password is by prompting */
+ if (ci_streq(arg, "console")) {
+ if ((passphrase = getpassphrase(prompt)) == NULL) {
+ pkgerr_add(err, PKGERR_BADPASS,
+ gettext(MSG_NOPASS), arg);
+ return (-1);
+ }
+
+ if (rw) {
+ /*
+ * if the password is being supplied for
+ * writing something to disk, verify it first
+ */
+
+ /* make a copy (getpassphrase overwrites) */
+ strlcpy(passphrase_copy, passphrase,
+ MAX_PHRASELEN + 1);
+
+ if (((passlen = snprintf(prompt_copy,
+ MAX_VERIFY_MSGLEN, "%s: %s",
+ gettext(MSG_PASSWD_AGAIN),
+ prompt)) < 0) ||
+ (passlen >= (MAX_PHRASELEN + 1))) {
+ pkgerr_add(err, PKGERR_BADPASS,
+ gettext(MSG_NOPASS), arg);
+ return (-1);
+ }
+
+ if ((passphrase =
+ getpassphrase(prompt_copy)) == NULL) {
+ pkgerr_add(err, PKGERR_BADPASS,
+ gettext(MSG_NOPASS), arg);
+ return (-1);
+ }
+
+ if (!streq(passphrase_copy, passphrase)) {
+ pkgerr_add(err, PKGERR_READ,
+ gettext(MSG_PASSWD_NOMATCH));
+ return (-1);
+ }
+ }
+ } else if (ci_strneq(arg, "pass:", 5)) {
+ passphrase = arg + 5;
+ } else if (ci_strneq(arg, "env:", 4)) {
+ passphrase = getenv(arg + 4);
+ } else if (ci_strneq(arg, "file:", 5)) {
+
+ /* open file for reading */
+ if ((pwdbio = BIO_new_file(arg + 5, "r")) == NULL) {
+ pkgerr_add(err, PKGERR_EXIST,
+ gettext(MSG_PASSWD_FILE), arg + 5);
+ return (-1);
+ }
+
+ /* read first line */
+ if (((passlen = BIO_gets(pwdbio, buf, size)) < 1) ||
+ (passlen > size)) {
+ pkgerr_add(err, PKGERR_READ, gettext(MSG_PASSWD_FILE),
+ arg + 5);
+ return (-1);
+ }
+ BIO_free_all(pwdbio);
+ pwdbio = NULL;
+
+ if (passlen == size) {
+ /*
+ * password was maximum length, so there is
+ * no null terminator. null-terminate it
+ */
+ buf[size - 1] = '\0';
+ }
+
+ /* first newline found is end of passwd, so nuke it */
+ if ((ws = strchr(buf, '\n')) != NULL) {
+ *ws = '\0';
+ }
+ return (strlen(buf));
+ } else {
+ /* unrecognized passphrase */
+ pkgerr_add(err, PKGERR_BADPASS,
+ gettext(MSG_BADPASSARG), arg);
+ return (-1);
+ }
+
+ if (passphrase == NULL) {
+ /* unable to collect passwd from given source */
+ pkgerr_add(err, PKGERR_BADPASS,
+ gettext(MSG_NOPASS), arg);
+ return (-1);
+ }
+
+ strlcpy(buf, passphrase, size);
+ return (strlen(buf));
+}
diff --git a/usr/src/lib/libpkg/common/pkgweb.h b/usr/src/lib/libpkg/common/pkgweb.h
new file mode 100644
index 0000000000..fe9dc372d9
--- /dev/null
+++ b/usr/src/lib/libpkg/common/pkgweb.h
@@ -0,0 +1,130 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _PKGWEB_H
+#define _PKGWEB_H
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <netdb.h>
+#include <boot_http.h>
+
+/* shortest backoff delay possible (in seconds) */
+#define MIN_BACKOFF 1
+
+/* how much to increase backoff time after each failure */
+#define BACKOFF_FACTOR 2
+
+/* Maximum amount of backoff for a heavy network or flaky server */
+#define MAX_BACKOFF 128
+
+typedef enum {
+ HTTP_REQ_TYPE_HEAD,
+ HTTP_REQ_TYPE_GET
+} HTTPRequestType;
+
+typedef enum {
+ OCSPSuccess,
+ OCSPMem,
+ OCSPParse,
+ OCSPConnect,
+ OCSPRequest,
+ OCSPResponder,
+ OCSPUnsupported,
+ OCSPVerify,
+ OCSPInternal,
+ OCSPNoURI
+} OCSPStatus;
+
+typedef enum {
+ none,
+ web_http,
+ web_https,
+ web_ftp
+} WebScheme;
+
+typedef enum {
+ WEB_OK,
+ WEB_TIMEOUT,
+ WEB_CONNREFUSED,
+ WEB_HOSTDOWN,
+ WEB_VERIFY_SETUP,
+ WEB_NOCONNECT,
+ WEB_GET_FAIL
+} WebStatus;
+
+typedef struct {
+ ulong_t prev_cont_length;
+ ulong_t content_length;
+ ulong_t cur_pos;
+} DwnldData;
+
+typedef struct {
+ keystore_handle_t keystore;
+ char *certfile;
+ char *uniqfile;
+ char *link;
+ char *errstr;
+ char *dwnld_dir;
+ boolean_t spool;
+ void *content;
+ int timeout;
+ url_hport_t proxy;
+ url_t url;
+ DwnldData data;
+ http_respinfo_t *resp;
+ boot_http_ver_t *http_vers;
+ http_handle_t *hps;
+} WEB_SESSION;
+
+extern boolean_t web_session_control(PKG_ERR *, char *, char *,
+ keystore_handle_t, char *, ushort_t, int, int, int, char **);
+extern boolean_t get_signature(PKG_ERR *, char *, struct pkgdev *,
+ PKCS7 **);
+extern boolean_t validate_signature(PKG_ERR *, char *, BIO *, PKCS7 *,
+ STACK_OF(X509) *, url_hport_t *, int);
+extern boolean_t ds_validate_signature(PKG_ERR *, struct pkgdev *, char **,
+ char *, PKCS7 *, STACK_OF(X509) *, url_hport_t *, int);
+extern boolean_t get_proxy_port(PKG_ERR *, char **, ushort_t *);
+extern boolean_t path_valid(char *);
+extern void web_cleanup(void);
+extern ushort_t strip_port(char *proxy);
+extern void set_web_install(void);
+extern int is_web_install(void);
+extern void echo_out(int, char *, ...);
+extern void backoff(void);
+extern void reset_backoff(void);
+extern char *get_endof_string(char *, char);
+extern char *get_startof_string(char *, char);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _PKGWEB_H */
diff --git a/usr/src/lib/libpkg/common/pkgxpand.c b/usr/src/lib/libpkg/common/pkgxpand.c
new file mode 100644
index 0000000000..7f92aa8420
--- /dev/null
+++ b/usr/src/lib/libpkg/common/pkgxpand.c
@@ -0,0 +1,118 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
+/* All Rights Reserved */
+
+
+
+#include <stdio.h>
+#include <limits.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include "pkglib.h"
+#include "pkglocale.h"
+
+extern char *fpkginst(char *pkg, ...); /* libadm.a */
+extern char *pkgdir; /* WHERE? */
+
+#define ispkgalias(p) (*p == '+')
+#define LSIZE 512
+#define MALSIZ 16
+
+char **
+pkgalias(char *pkg)
+{
+ FILE *fp;
+ char path[PATH_MAX], *pkginst;
+ char *mypkg, *myarch, *myvers, **pkglist;
+ char line[LSIZE];
+ int n, errflg;
+
+ pkglist = (char **)calloc(MALSIZ, sizeof (char *));
+ if (pkglist == NULL)
+ return ((char **)0);
+
+ (void) sprintf(path, "%s/%s/pkgmap", pkgdir, pkg);
+ if ((fp = fopen(path, "r")) == NULL)
+ return ((char **)0);
+
+ n = errflg = 0;
+ while (fgets(line, LSIZE, fp)) {
+ mypkg = strtok(line, " \t\n");
+ myarch = strtok(NULL, "( \t\n)");
+ myvers = strtok(NULL, "\n");
+
+ (void) fpkginst(NULL);
+ pkginst = fpkginst(mypkg, myarch, myvers);
+ if (pkginst == NULL) {
+ logerr(
+ pkg_gt("no package instance for [%s]"), mypkg);
+ errflg++;
+ continue;
+ }
+ if (errflg)
+ continue;
+
+ pkglist[n] = strdup(pkginst);
+ if ((++n % MALSIZ) == 0) {
+ pkglist = (char **)realloc(pkglist,
+ (n+MALSIZ)*sizeof (char *));
+ if (pkglist == NULL)
+ return ((char **)0);
+ }
+ }
+ pkglist[n] = NULL;
+
+ (void) fclose(fp);
+ if (errflg) {
+ while (n-- >= 0)
+ free(pkglist[n]);
+ free(pkglist);
+ return ((char **)0);
+ }
+ return (pkglist);
+}
+
+#if 0
+char **
+pkgxpand(char *pkg[])
+{
+ static int level = 0;
+ char **pkglist;
+ int i;
+
+ if (++level >= 0)
+ printf(pkg_gt("too deep"));
+ for (i = 0; pkg[i]; i++) {
+ if (ispkgalias(pkg[i])) {
+ pkglist = pkgxpand(&pkg[i]);
+ pkgexpand(pkglist);
+ }
+ }
+}
+#endif /* 0 */
diff --git a/usr/src/lib/libpkg/common/ppkgmap.c b/usr/src/lib/libpkg/common/ppkgmap.c
new file mode 100644
index 0000000000..423def1bab
--- /dev/null
+++ b/usr/src/lib/libpkg/common/ppkgmap.c
@@ -0,0 +1,139 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
+/* All Rights Reserved */
+
+
+
+#include <stdio.h>
+#include <string.h>
+#include <limits.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include "pkgstrct.h"
+
+int holdcinfo = 0;
+
+int
+ppkgmap(struct cfent *ept, FILE *fp)
+{
+ if (ept->path == NULL)
+ return (-1);
+
+ if (ept->volno) {
+ if (fprintf(fp, "%d ", ept->volno) < 0)
+ return (-1);
+ }
+
+ if (ept->ftype == 'i') {
+ if (fprintf(fp, "%c %s", ept->ftype, ept->path) < 0)
+ return (-1);
+ } else {
+ if (fprintf(fp, "%c %s %s", ept->ftype, ept->pkg_class,
+ ept->path) < 0)
+ return (-1);
+ }
+
+ if (ept->ainfo.local) {
+ if (fprintf(fp, "=%s", ept->ainfo.local) < 0)
+ return (-1);
+ }
+
+ if (strchr("cb", ept->ftype)) {
+#ifdef SUNOS41
+ if (ept->ainfo.xmajor == BADMAJOR) {
+ if (fprintf(fp, " ?") < 0)
+ return (-1);
+ } else {
+ if (fprintf(fp, " %d", ept->ainfo.xmajor) < 0)
+ return (-1);
+ }
+#else
+ if (ept->ainfo.major == BADMAJOR) {
+ if (fprintf(fp, " ?") < 0)
+ return (-1);
+ } else {
+ if (fprintf(fp, " %d", ept->ainfo.major) < 0)
+ return (-1);
+ }
+#endif
+#ifdef SUNOS41
+ if (ept->ainfo.xminor == BADMINOR) {
+ if (fprintf(fp, " ?") < 0)
+ return (-1);
+ } else {
+ if (fprintf(fp, " %d", ept->ainfo.xminor) < 0)
+ return (-1);
+ }
+#else
+ if (ept->ainfo.minor == BADMINOR) {
+ if (fprintf(fp, " ?") < 0)
+ return (-1);
+ } else {
+ if (fprintf(fp, " %d", ept->ainfo.minor) < 0)
+ return (-1);
+ }
+#endif
+ }
+
+ if (strchr("dxcbpfve", ept->ftype)) {
+ if (fprintf(fp, ((ept->ainfo.mode == BADMODE) ? " ?" : " %04o"),
+ ept->ainfo.mode) < 0)
+ return (-1);
+ if (fprintf(fp, " %s %s", ept->ainfo.owner, ept->ainfo.group) <
+ 0)
+ return (-1);
+ }
+ if (holdcinfo) {
+ if (fputc('\n', fp) == EOF)
+ return (-1);
+ return (0);
+ }
+
+ if (strchr("ifve", ept->ftype)) {
+ if (fprintf(fp, ((ept->cinfo.size == BADCONT) ? " ?" : " %llu"),
+ ept->cinfo.size) < 0)
+ return (-1);
+ if (fprintf(fp, ((ept->cinfo.cksum == BADCONT) ? " ?" : " %ld"),
+ ept->cinfo.cksum) < 0)
+ return (-1);
+ if (fprintf(fp,
+ ((ept->cinfo.modtime == BADCONT) ? " ?" : " %ld"),
+ ept->cinfo.modtime) < 0)
+ return (-1);
+ }
+
+ if (ept->ftype == 'i') {
+ if (fputc('\n', fp) == EOF)
+ return (-1);
+ return (0);
+ }
+ if (fprintf(fp, "\n") < 0)
+ return (-1);
+ return (0);
+}
diff --git a/usr/src/lib/libpkg/common/progerr.c b/usr/src/lib/libpkg/common/progerr.c
new file mode 100644
index 0000000000..8feaf4b81b
--- /dev/null
+++ b/usr/src/lib/libpkg/common/progerr.c
@@ -0,0 +1,201 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
+/* All Rights Reserved */
+
+
+
+#include <stdio.h>
+#include <stdarg.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+#include "pkglocale.h"
+#include "pkgerr.h"
+
+static char *ProgName = NULL; /* Set via set_prog_name() */
+
+
+static void
+error_and_exit(int error_num)
+{
+ (void) fprintf(stderr, "%d\n", error_num);
+ exit(99);
+}
+
+static void (*fatal_err_func)() = &error_and_exit;
+
+char *
+set_prog_name(char *name)
+{
+ if (name == NULL)
+ return (NULL);
+ if ((name = strdup(name)) == NULL) {
+ (void) fprintf(stderr,
+ "set_prog_name(): strdup(name) failed.\n");
+ exit(1);
+ }
+ ProgName = strrchr(name, '/');
+ if (!ProgName++)
+ ProgName = name;
+
+ return (ProgName);
+}
+
+char *
+get_prog_name(void)
+{
+ return (ProgName);
+}
+
+
+/*VARARGS*/
+void
+progerr(char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+
+ if (ProgName && *ProgName)
+ (void) fprintf(stderr, pkg_gt("%s: ERROR: "), ProgName);
+ else
+ (void) fprintf(stderr, pkg_gt(" ERROR: "));
+
+ (void) vfprintf(stderr, fmt, ap);
+
+ va_end(ap);
+
+ (void) fprintf(stderr, "\n");
+}
+
+void
+pkgerr(PKG_ERR *err)
+{
+ int i;
+
+ for (i = 0; i < pkgerr_num(err); i++) {
+ progerr("%s", pkgerr_get(err, i));
+ }
+}
+
+
+/*
+ * set_memalloc_failure_func()
+ * Allows an appliation to specify the function to be called when
+ * a memory allocation function fails.
+ * Parameters:
+ * (*alloc_proc)(int) - specifies the function to call if fatal error
+ * (such as being unable to allocate memory) occurs.
+ * Return:
+ * none
+ * Status:
+ * Public
+ */
+void
+set_memalloc_failure_func(void (*alloc_proc)(int))
+{
+ if (alloc_proc != (void (*)())NULL)
+ fatal_err_func = alloc_proc;
+}
+
+/*
+ * xmalloc()
+ * Alloc 'size' bytes from heap using malloc()
+ * Parameters:
+ * size - number of bytes to malloc
+ * Return:
+ * NULL - malloc() failure
+ * void * - pointer to allocated structure
+ * Status:
+ * public
+ */
+void *
+xmalloc(size_t size)
+{
+ void *tmp;
+
+ if ((tmp = (void *) malloc(size)) == NULL) {
+ fatal_err_func(errno);
+ return (NULL);
+ } else
+ return (tmp);
+}
+
+/*
+ * xrealloc()
+ * Calls realloc() with the specfied parameters. xrealloc()
+ * checks for realloc failures and adjusts the return value
+ * automatically.
+ * Parameters:
+ * ptr - pointer to existing data block
+ * size - number of bytes additional
+ * Return:
+ * NULL - realloc() failed
+ * void * - pointer to realloc'd structured
+ * Status:
+ * public
+ */
+void *
+xrealloc(void *ptr, size_t size)
+{
+ void *tmp;
+
+ if ((tmp = (void *)realloc(ptr, size)) == (void *)NULL) {
+ fatal_err_func(errno);
+ return ((void *)NULL);
+ } else
+ return (tmp);
+}
+
+/*
+ * xstrdup()
+ * Allocate space for the string from the heap, copy 'str' into it,
+ * and return a pointer to it.
+ * Parameters:
+ * str - string to duplicate
+ * Return:
+ * NULL - duplication failed or 'str' was NULL
+ * char * - pointer to newly allocated/initialized structure
+ * Status:
+ * public
+ */
+char *
+xstrdup(char *str)
+{
+ char *tmp;
+
+ if (str == NULL)
+ return ((char *)NULL);
+
+ if ((tmp = strdup(str)) == NULL) {
+ fatal_err_func(errno);
+ return ((char *)NULL);
+ } else
+ return (tmp);
+}
diff --git a/usr/src/lib/libpkg/common/putcfile.c b/usr/src/lib/libpkg/common/putcfile.c
new file mode 100644
index 0000000000..959f2684b8
--- /dev/null
+++ b/usr/src/lib/libpkg/common/putcfile.c
@@ -0,0 +1,376 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
+/* All Rights Reserved */
+
+
+
+#include <stdio.h>
+#include <string.h>
+#include <limits.h>
+#include <sys/types.h>
+#include "pkgstrct.h"
+#include "pkglib.h"
+
+/*
+ * Name: putcfile
+ * Description: Write contents file entry to specified FILE
+ * Arguments: struct cfent a_ept - data for contents file entry
+ * FILE *a_fp - FP of file to write contents file entry to
+ * Notes: This is identical to putcvfpfile() but this function takes a
+ * stdio FILE* file to write to instead of a VFP_T file. It is
+ * MUCH slower than putcvfpfile().
+ */
+
+int
+putcfile(struct cfent *a_ept, FILE *a_fp)
+{
+ struct pinfo *pinfo;
+
+ if (a_ept->ftype == 'i') {
+ return (0); /* no ifiles stored in contents DB */
+ }
+
+ if (a_ept->path == NULL) {
+ return (-1); /* no path name - no entry to write */
+ }
+
+ if (fputs(a_ept->path, a_fp) == EOF) {
+ return (-1);
+ }
+
+ if (a_ept->ainfo.local) {
+ if (putc('=', a_fp) == EOF) {
+ return (-1);
+ }
+ if (fputs(a_ept->ainfo.local, a_fp) == EOF)
+ return (-1);
+ }
+
+ if (a_ept->volno) {
+ if (fprintf(a_fp, " %d", a_ept->volno) < 0) {
+ return (-1);
+ }
+ }
+
+ if (putc(' ', a_fp) == EOF) {
+ return (-1);
+ }
+
+ if (putc(a_ept->ftype, a_fp) == EOF) {
+ return (-1);
+ }
+
+ if (putc(' ', a_fp) == EOF) {
+ return (-1);
+ }
+
+ if (fputs(a_ept->pkg_class, a_fp) == EOF) {
+ return (-1);
+ }
+
+ if ((a_ept->ftype == 'c') || (a_ept->ftype == 'b')) {
+ if (a_ept->ainfo.major == BADMAJOR) {
+ if (putc(' ', a_fp) == EOF) {
+ return (-1);
+ }
+
+ if (putc('?', a_fp) == EOF) {
+ return (-1);
+ }
+ } else {
+ if (fprintf(a_fp, " %d", a_ept->ainfo.major) < 0)
+ return (-1);
+ }
+
+ if (a_ept->ainfo.minor == BADMINOR) {
+ if (putc(' ', a_fp) == EOF) {
+ return (-1);
+ }
+
+ if (putc('?', a_fp) == EOF) {
+ return (-1);
+ }
+ } else {
+ if (fprintf(a_fp, " %d", a_ept->ainfo.minor) < 0)
+ return (-1);
+ }
+ }
+
+ if ((a_ept->ftype == 'd') || (a_ept->ftype == 'x') ||
+ (a_ept->ftype == 'c') || (a_ept->ftype == 'b') ||
+ (a_ept->ftype == 'p') || (a_ept->ftype == 'f') ||
+ (a_ept->ftype == 'v') || (a_ept->ftype == 'e')) {
+ if (fprintf(a_fp,
+ ((a_ept->ainfo.mode == BADMODE) ? " ?" : " %04o"),
+ a_ept->ainfo.mode) < 0)
+ return (-1);
+
+ if (putc(' ', a_fp) == EOF) {
+ return (-1);
+ }
+
+ if (fputs(a_ept->ainfo.owner, a_fp) == EOF) {
+ return (-1);
+ }
+
+ if (putc(' ', a_fp) == EOF) {
+ return (-1);
+ }
+
+ if (fputs(a_ept->ainfo.group, a_fp) == EOF) {
+ return (-1);
+ }
+ }
+
+ if ((a_ept->ftype == 'f') || (a_ept->ftype == 'v') ||
+ (a_ept->ftype == 'e')) {
+ if (fprintf(a_fp,
+ ((a_ept->cinfo.size == BADCONT) ? " ?" : " %llu"),
+ a_ept->cinfo.size) < 0)
+ return (-1);
+
+ if (fprintf(a_fp,
+ ((a_ept->cinfo.cksum == BADCONT) ? " ?" : " %ld"),
+ a_ept->cinfo.cksum) < 0)
+ return (-1);
+
+ if (fprintf(a_fp,
+ ((a_ept->cinfo.modtime == BADCONT) ? " ?" : " %ld"),
+ a_ept->cinfo.modtime) < 0)
+ return (-1);
+ }
+
+ pinfo = a_ept->pinfo;
+ while (pinfo) {
+ if (putc(' ', a_fp) == EOF) {
+ return (-1);
+ }
+
+ if (pinfo->status) {
+ if (fputc(pinfo->status, a_fp) == EOF) {
+ return (-1);
+ }
+ }
+
+ if (fputs(pinfo->pkg, a_fp) == EOF) {
+ return (-1);
+ }
+
+ if (pinfo->editflag) {
+ if (putc('\\', a_fp) == EOF) {
+ return (-1);
+ }
+ }
+
+ if (pinfo->aclass[0]) {
+ if (putc(':', a_fp) == EOF) {
+ return (-1);
+ }
+ if (fputs(pinfo->aclass, a_fp) == EOF) {
+ return (-1);
+ }
+ }
+ pinfo = pinfo->next;
+ }
+
+ if (putc('\n', a_fp) == EOF) {
+ return (-1);
+ }
+ return (0);
+}
+
+/*
+ * Name: putcvfpfile
+ * Description: Write contents file entry to specified VFP
+ * Arguments: struct cfent a_ept - data for contents file entry
+ * VFP_T *a_vfp - VFP of file to write contents file entry to
+ * Notes: This is identical to putcfile() but this function takes a
+ * VFP_T file to write to instead of a stdio FILE file. It is
+ * MUCH faster tha putcfile().
+ */
+
+int
+putcvfpfile(struct cfent *a_ept, VFP_T *a_vfp)
+{
+ struct pinfo *pinfo;
+
+ /* contents file does not maintain any 'i' file entries */
+
+ if (a_ept->ftype == 'i') {
+ return (0);
+ }
+
+ /* cannot create an entry if it has no file name */
+
+ if (a_ept->path == NULL) {
+ return (-1);
+ }
+
+ /*
+ * Format of contents file line could be one of:
+ * /file=./dir/file s class SUNWxxx
+ * /file=../dir/file l class SUNWxxx
+ * /dir d class mode owner group SUNWxxx SUNWyyy
+ * /devices/name c class major minor mode owner group SUNWxxx
+ * /file f class mode owner group size cksum modtime SUNWxxx
+ * /file x class mode owner group SUNWppro
+ * /file v class mode owner group size cksum modtime SUNWxxx
+ * /file e class mode owner group size cksum modtime SUNWxxx
+ * The package name could be prefixed by one of the following
+ * status indicators: +-*!%@#~
+ */
+
+ /*
+ * Adding an entry to the specified VFP. During normal processing the
+ * contents file is copied to a temporary contents file and entries are
+ * added as appropriate. When this processing is completed, a decision
+ * is made on whether or not to overwrite the real contents file with
+ * the contents of the temporary contents file. If the temporary
+ * contents file is just a copy of the real contents file then there is
+ * no need to overwrite the real contents file with the contents of the
+ * temporary contents file. This decision is made in part on whether
+ * or not any new or modified entries have been added to the temporary
+ * contents file. Set the "data is modified" indication associated
+ * with this VFP so that the real contents file is overwritten when
+ * processing is done.
+ */
+
+ (void) vfpSetModified(a_vfp);
+
+ /* write initial path [all entries] */
+
+ vfpPuts(a_vfp, a_ept->path);
+
+ /* if link, write out '=' portion */
+
+ if (a_ept->ainfo.local) {
+ vfpPutc(a_vfp, '=');
+ vfpPuts(a_vfp, a_ept->ainfo.local);
+ }
+
+ /* if volume, write it out */
+
+ if (a_ept->volno) {
+ vfpPutc(a_vfp, ' ');
+ vfpPutInteger(a_vfp, a_ept->volno);
+ }
+
+ /* write out <space><entry type><space>class> */
+
+ vfpPutc(a_vfp, ' ');
+ vfpPutc(a_vfp, a_ept->ftype);
+ vfpPutc(a_vfp, ' ');
+ vfpPuts(a_vfp, a_ept->pkg_class);
+
+ /* if char/block device, write out major/minor numbers */
+
+ if ((a_ept->ftype == 'c') || (a_ept->ftype == 'b')) {
+ /* major device number */
+ if (a_ept->ainfo.major == BADMAJOR) {
+ vfpPutc(a_vfp, ' ');
+ vfpPutc(a_vfp, '?');
+ } else {
+ vfpPutc(a_vfp, ' ');
+ vfpPutInteger(a_vfp, a_ept->ainfo.major);
+ }
+
+ /* minor device number */
+ if (a_ept->ainfo.minor == BADMINOR) {
+ vfpPutc(a_vfp, ' ');
+ vfpPutc(a_vfp, '?');
+ } else {
+ vfpPutc(a_vfp, ' ');
+ vfpPutInteger(a_vfp, a_ept->ainfo.minor);
+ }
+ }
+
+ /* if dxcbpfve, write out mode, owner, group */
+
+ if ((a_ept->ftype == 'd') || (a_ept->ftype == 'x') ||
+ (a_ept->ftype == 'c') || (a_ept->ftype == 'b') ||
+ (a_ept->ftype == 'p') || (a_ept->ftype == 'f') ||
+ (a_ept->ftype == 'v') || (a_ept->ftype == 'e')) {
+
+ /* mode */
+ vfpPutFormat(a_vfp,
+ ((a_ept->ainfo.mode == BADMODE) ? " ?" : " %04o"),
+ a_ept->ainfo.mode);
+
+ /* owner */
+ vfpPutc(a_vfp, ' ');
+ vfpPuts(a_vfp, a_ept->ainfo.owner);
+
+ /* group */
+ vfpPutc(a_vfp, ' ');
+ vfpPuts(a_vfp, a_ept->ainfo.group);
+ }
+ /* if f/v/e, write out size, cksum, modtime */
+
+ if ((a_ept->ftype == 'f') || (a_ept->ftype == 'v') ||
+ (a_ept->ftype == 'e')) {
+ /* size */
+ vfpPutFormat(a_vfp,
+ ((a_ept->cinfo.size == BADCONT) ? " ?" : " %llu"),
+ a_ept->cinfo.size);
+
+ /* cksum */
+ vfpPutFormat(a_vfp,
+ ((a_ept->cinfo.cksum == BADCONT) ? " ?" : " %ld"),
+ a_ept->cinfo.cksum);
+
+ /* modtime */
+ vfpPutFormat(a_vfp,
+ ((a_ept->cinfo.modtime == BADCONT) ? " ?" : " %ld"),
+ a_ept->cinfo.modtime);
+ }
+
+ /* write out list of all packages referencing this entry */
+
+ pinfo = a_ept->pinfo;
+ while (pinfo) {
+ vfpPutc(a_vfp, ' ');
+ if (pinfo->status) {
+ vfpPutc(a_vfp, pinfo->status);
+ }
+
+ vfpPuts(a_vfp, pinfo->pkg);
+
+ if (pinfo->editflag) {
+ vfpPutc(a_vfp, '\\');
+ }
+
+ if (pinfo->aclass[0]) {
+ vfpPutc(a_vfp, ':');
+ vfpPuts(a_vfp, pinfo->aclass);
+ }
+ pinfo = pinfo->next;
+ }
+
+ vfpPutc(a_vfp, '\n');
+ return (0);
+}
diff --git a/usr/src/lib/libpkg/common/rrmdir.c b/usr/src/lib/libpkg/common/rrmdir.c
new file mode 100644
index 0000000000..baf5d61c19
--- /dev/null
+++ b/usr/src/lib/libpkg/common/rrmdir.c
@@ -0,0 +1,83 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
+/* All Rights Reserved */
+
+
+
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <strings.h>
+#include "pkglocale.h"
+#include "pkglib.h"
+
+int
+rrmdir(char *a_path)
+{
+ char path[PATH_MAX+13];
+ int i;
+ int status;
+
+ /*
+ * For some reason, a simple "rm -rf" will remove the contents
+ * of the directory, but will fail to remove the directory itself
+ * with "No such file or directory" when running the pkg commands
+ * under a virtual root via the "chroot" command. This has been
+ * seen so far only with the `pkgremove' command, and when the
+ * the directory is NFS mounted from a 4.x server. This should
+ * probably be revisited at a later time, but for now we'll just
+ * remove the directory contents first, then the directory.
+ */
+
+ /* do not allow removal of all root files via blank path */
+
+ if ((a_path == NULL) || (*a_path == '\0')) {
+ (void) fprintf(stderr,
+ pkg_gt("warning: rrmdir(path==NULL): nothing deleted\n"));
+ return (0);
+ }
+
+ /*
+ * first generate path with slash-star at the end and attempt to remove
+ * all files first. If successful then remove with just the path only.
+ */
+
+ (void) snprintf(path, sizeof (path), "%s/", a_path);
+ i = e_ExecCmdList(&status, (char **)NULL, (char *)NULL,
+ "/bin/rm", "rm", "-rf", path, (char *)NULL);
+
+ if (access(a_path, F_OK) == 0) {
+ i = e_ExecCmdList(&status, (char **)NULL, (char *)NULL,
+ "/bin/rmdir", "rmdir", a_path, (char *)NULL);
+ }
+
+ /* return 0 if last command successful, else return 1 */
+ return ((i == 0 && status == 0) ? 0 : 1);
+}
diff --git a/usr/src/lib/libpkg/common/runcmd.c b/usr/src/lib/libpkg/common/runcmd.c
new file mode 100644
index 0000000000..945673737e
--- /dev/null
+++ b/usr/src/lib/libpkg/common/runcmd.c
@@ -0,0 +1,808 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
+/* All Rights Reserved */
+
+
+
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+#include <strings.h>
+#include <signal.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <wait.h>
+#include <sys/types.h>
+#include "pkglib.h"
+#include "pkglocale.h"
+#include "pkglibmsgs.h"
+
+#ifndef _STDARG_H
+#include "stdarg.h"
+#endif
+
+/*
+ * Private definitions
+ */
+
+/* Maximum number of arguments to pkg_ExecCmdList */
+
+#define MAX_EXEC_CMD_ARGS 100
+
+/* Size of buffer increments when reading from pipe */
+
+#define PIPE_BUFFER_INCREMENT 256
+
+static char errfile[L_tmpnam+1];
+
+/*
+ * This is the "argument array" definition that is returned by e_new_args and is
+ * used by e_add_args, e_free_args, etc.
+ */
+
+struct _argArray_t {
+ long _aaNumArgs; /* number of arguments set */
+ long _aaMaxArgs; /* number of arguments allocated */
+ char **_aaArgs; /* actual arguments */
+};
+
+typedef struct _argArray_t argArray_t;
+
+/*
+ * Private Methods
+ */
+static void e_free_args(argArray_t *a_args);
+static argArray_t *e_new_args(int initialCount);
+/*PRINTFLIKE2*/
+static boolean_t e_add_arg(argArray_t *a_args, char *a_format, ...);
+static int e_get_argc(argArray_t *a_args);
+static char **e_get_argv(argArray_t *a_args);
+
+
+/*
+ * Public Methods
+ */
+
+
+void
+rpterr(void)
+{
+ FILE *fp;
+ int c;
+
+ if (errfile[0]) {
+ if (fp = fopen(errfile, "r")) {
+ while ((c = getc(fp)) != EOF)
+ putc(c, stderr);
+ (void) fclose(fp);
+ }
+ (void) unlink(errfile);
+ errfile[0] = '\0';
+ }
+}
+
+void
+ecleanup(void)
+{
+ if (errfile[0]) {
+ (void) unlink(errfile);
+ errfile[0] = NULL;
+ }
+}
+
+int
+esystem(char *cmd, int ifd, int ofd)
+{
+ char *perrfile;
+ int status = 0;
+ pid_t pid;
+
+ perrfile = tmpnam(NULL);
+ if (perrfile == NULL) {
+ progerr(
+ pkg_gt("unable to create temp error file, errno=%d"),
+ errno);
+ return (-1);
+ }
+ (void) strlcpy(errfile, perrfile, sizeof (errfile));
+
+ /* flush standard i/o before creating new process */
+
+ (void) fflush(stderr);
+ (void) fflush(stdout);
+
+ /*
+ * create new process to execute command in;
+ * vfork() is being used to avoid duplicating the parents
+ * memory space - this means that the child process may
+ * not modify any of the parents memory including the
+ * standard i/o descriptors - all the child can do is
+ * adjust interrupts and open files as a prelude to a
+ * call to exec().
+ */
+
+ pid = vfork();
+ if (pid == 0) {
+ /*
+ * this is the child process
+ */
+ int i;
+
+ /* reset any signals to default */
+
+ for (i = 0; i < NSIG; i++) {
+ (void) sigset(i, SIG_DFL);
+ }
+
+ if (ifd > 0) {
+ (void) dup2(ifd, STDIN_FILENO);
+ }
+
+ if (ofd >= 0 && ofd != STDOUT_FILENO) {
+ (void) dup2(ofd, STDOUT_FILENO);
+ }
+
+ i = open(errfile, O_WRONLY|O_CREAT|O_TRUNC, 0666);
+ if (i >= 0) {
+ dup2(i, STDERR_FILENO);
+ }
+
+ /* Close all open files except standard i/o */
+
+ closefrom(3);
+
+ /* execute target executable */
+
+ execl("/sbin/sh", "/sbin/sh", "-c", cmd, NULL);
+ progerr(pkg_gt("exec of <%s> failed, errno=%d"), cmd, errno);
+ _exit(99);
+ } else if (pid < 0) {
+ /* fork failed! */
+
+ logerr(pkg_gt("bad vfork(), errno=%d"), errno);
+ return (-1);
+ }
+
+ /*
+ * this is the parent process
+ */
+
+ sighold(SIGINT);
+ pid = waitpid(pid, &status, 0);
+ sigrelse(SIGINT);
+
+ if (pid < 0) {
+ return (-1); /* probably interrupted */
+ }
+
+ switch (status & 0177) {
+ case 0:
+ case 0177:
+ status = status >> 8;
+ /*FALLTHROUGH*/
+
+ default:
+ /* terminated by a signal */
+ status = status & 0177;
+ }
+
+ if (status == 0) {
+ ecleanup();
+ }
+
+ return (status);
+}
+
+FILE *
+epopen(char *cmd, char *mode)
+{
+ char *buffer, *perrfile;
+ FILE *pp;
+ size_t len;
+ size_t alen;
+
+ if (errfile[0]) {
+ /* cleanup previous errfile */
+ unlink(errfile);
+ }
+
+ perrfile = tmpnam(NULL);
+ if (perrfile == NULL) {
+ progerr(
+ pkg_gt("unable to create temp error file, errno=%d"),
+ errno);
+ return ((FILE *)0);
+ }
+
+ if (strlcpy(errfile, perrfile, sizeof (errfile)) > sizeof (errfile)) {
+ progerr(pkg_gt("file name max length %d; name is too long: %s"),
+ sizeof (errfile), perrfile);
+ return ((FILE *)0);
+ }
+
+ len = strlen(cmd)+6+strlen(errfile);
+ buffer = (char *)calloc(len, sizeof (char));
+ if (buffer == NULL) {
+ progerr(pkg_gt("no memory in epopen(), errno=%d"), errno);
+ return ((FILE *)0);
+ }
+
+ if (strchr(cmd, '|')) {
+ alen = snprintf(buffer, len, "(%s) 2>%s", cmd, errfile);
+ } else {
+ alen = snprintf(buffer, len, "%s 2>%s", cmd, errfile);
+ }
+
+ if (alen > len) {
+ progerr(pkg_gt("command max length %d; cmd is too long: %s"),
+ len, cmd);
+ return ((FILE *)0);
+ }
+
+ pp = popen(buffer, mode);
+
+ free(buffer);
+ return (pp);
+}
+
+int
+epclose(FILE *pp)
+{
+ int n;
+
+ n = pclose(pp);
+ if (n == 0)
+ ecleanup();
+ return (n);
+}
+
+/*
+ * Name: e_ExecCmdArray
+ * Synopsis: Execute Unix command and return results
+ * Description: Execute a Unix command and return results and status
+ * Arguments:
+ * r_status - [RO, *RW] - (int *)
+ * Return (exit) status from Unix command:
+ * == -1 : child terminated with a signal
+ * != -1 : lower 8-bit value child passed to exit()
+ * r_results - [RO, *RW] - (char **)
+ * Any output generated by the Unix command to stdout
+ * and to stderr
+ * == (char *)NULL if no output generated
+ * a_inputFile - [RO, *RO] - (char *)
+ * Pointer to character string representing file to be
+ * used as "standard input" for the command.
+ * == (char *)NULL to use "/dev/null" as standard input
+ * a_cmd - [RO, *RO] - (char *)
+ * Pointer to character string representing the full path
+ * of the Unix command to execute
+ * char **a_args - [RO, *RO] - (char **)
+ * List of character strings representing the arguments
+ * to be passed to the Unix command. The list must be
+ * terminated with an element that is (char *)NULL
+ * Returns: int
+ * == 0 - Command executed
+ * Look at r_status for results of Unix command
+ * != 0 - problems executing command
+ * r_status and r_results have no meaning;
+ * r_status will be -1
+ * r_results will be NULL
+ * NOTE: Any results returned is placed in new storage for the
+ * calling method. The caller must use 'free' to dispose
+ * of the storage once the results are no longer needed.
+ * NOTE: If 0 is returned, 'r_status' must be queried to
+ * determine the results of the Unix command.
+ * NOTE: The system "errno" value from immediately after waitpid() call
+ * is preserved for the calling method to use to determine
+ * the system reason why the operation failed.
+ */
+
+int
+e_ExecCmdArray(int *r_status, char **r_results,
+ char *a_inputFile, char *a_cmd, char **a_args)
+{
+ char *buffer;
+ int bufferIndex;
+ int bufferSize;
+ int ipipe[2] = {0, 0};
+ pid_t pid;
+ pid_t resultPid;
+ int status;
+ int lerrno;
+ int stdinfile = -1;
+
+ /* reset return results buffer pointer */
+
+ if (r_results != (char **)NULL) {
+ *r_results = (char *)NULL;
+ }
+
+ *r_status = -1;
+
+ /*
+ * See if command exists
+ */
+
+ if (access(a_cmd, F_OK|X_OK) != 0) {
+ return (-1);
+ }
+
+ /*
+ * See if input file exists
+ */
+
+ if (a_inputFile != (char *)NULL) {
+ stdinfile = open(a_inputFile, O_RDONLY);
+ } else {
+ stdinfile = open("/dev/null", O_RDONLY); /* stdin = /dev/null */
+ }
+
+ if (stdinfile < 0) {
+ return (-1);
+ }
+
+ /*
+ * Create a pipe to be used to capture the command output
+ */
+
+ if (pipe(ipipe) != 0) {
+ (void) close(stdinfile);
+ return (-1);
+ }
+
+
+ bufferSize = PIPE_BUFFER_INCREMENT;
+ bufferIndex = 0;
+ buffer = calloc(1, bufferSize);
+ if (buffer == (char *)NULL) {
+ (void) close(stdinfile);
+ return (-1);
+ }
+
+ /* flush standard i/o before creating new process */
+
+ (void) fflush(stderr);
+ (void) fflush(stdout);
+
+ /*
+ * create new process to execute command in;
+ * vfork() is being used to avoid duplicating the parents
+ * memory space - this means that the child process may
+ * not modify any of the parents memory including the
+ * standard i/o descriptors - all the child can do is
+ * adjust interrupts and open files as a prelude to a
+ * call to exec().
+ */
+
+ pid = vfork();
+
+ if (pid == 0) {
+ /*
+ * This is the forked (child) process ======================
+ */
+
+ int i;
+
+ /* reset any signals to default */
+
+ for (i = 0; i < NSIG; i++) {
+ (void) sigset(i, SIG_DFL);
+ }
+
+ /* assign stdin, stdout, stderr as appropriate */
+
+ (void) dup2(stdinfile, STDIN_FILENO);
+ (void) close(ipipe[0]); /* close out pipe reader side */
+ (void) dup2(ipipe[1], STDOUT_FILENO);
+ (void) dup2(ipipe[1], STDERR_FILENO);
+
+ /* Close all open files except standard i/o */
+
+ closefrom(3);
+
+ /* execute target executable */
+
+ (void) execvp(a_cmd, a_args);
+ perror(a_cmd); /* Emit error msg - ends up in callers buffer */
+ _exit(0x00FE);
+ }
+
+ /*
+ * This is the forking (parent) process ====================
+ */
+
+ (void) close(stdinfile);
+ (void) close(ipipe[1]); /* Close write side of pipe */
+
+ /*
+ * Spin reading data from the child into the buffer - when the read eofs
+ * the child has exited
+ */
+
+ for (;;) {
+ ssize_t bytesRead;
+
+ /* read as much child data as there is available buffer space */
+
+ bytesRead = read(ipipe[0], buffer + bufferIndex,
+ bufferSize - bufferIndex);
+
+ /* break out of read loop if end-of-file encountered */
+
+ if (bytesRead == 0) {
+ break;
+ }
+
+ /* if error, continue if recoverable, else break out of loop */
+
+ if (bytesRead == -1) {
+ /* try again: EAGAIN - insufficient resources */
+
+ if (errno == EAGAIN) {
+ continue;
+ }
+
+ /* try again: EINTR - interrupted system call */
+
+ if (errno == EINTR) {
+ continue;
+ }
+
+ /* break out of loop - error not recoverable */
+ break;
+ }
+
+ /* at least 1 byte read: expand buffer if at end */
+
+ bufferIndex += bytesRead;
+ if (bufferIndex >= bufferSize) {
+ buffer = realloc(buffer,
+ bufferSize += PIPE_BUFFER_INCREMENT);
+ (void) memset(buffer + bufferIndex, 0,
+ bufferSize - bufferIndex);
+ }
+ }
+
+ (void) close(ipipe[0]); /* Close read side of pipe */
+
+ /* Get subprocess exit status */
+
+ for (;;) {
+ resultPid = waitpid(pid, &status, 0L);
+ lerrno = (resultPid == -1 ? errno : 0);
+
+ /* break loop if child process status reaped */
+
+ if (resultPid != -1) {
+ break;
+ }
+
+ /* break loop if not interrupted out of waitpid */
+
+ if (errno != EINTR) {
+ break;
+ }
+ }
+
+ /*
+ * If the child process terminated due to a call to exit(), then
+ * set results equal to the 8-bit exit status of the child process;
+ * otherwise, set the exit status to "-1" indicating that the child
+ * exited via a signal.
+ */
+
+ *r_status = WIFEXITED(status) ? WEXITSTATUS(status) : -1;
+
+ /* return appropriate output */
+
+ if (!*buffer) {
+ /* No contents in output buffer - discard */
+ free(buffer);
+ } else if (r_results == (char **)NULL) {
+ /* Not requested to return results - discard */
+ free(buffer);
+ } else {
+ /* have output and request to return: pass to calling method */
+ *r_results = buffer;
+ }
+
+ errno = lerrno;
+ return (resultPid == -1 ? -1 : 0);
+}
+
+/*
+ * Name: e_ExecCmdList
+ * Synopsis: Execute Unix command and return results
+ * Description: Execute a Unix command and return results and status
+ * Arguments:
+ * r_status - [RO, *RW] - (int *)
+ * Return (exit) status from Unix command
+ * r_results - [RO, *RW] - (char **)
+ * Any output generated by the Unix command to stdout
+ * and to stderr
+ * == (char *)NULL if no output generated
+ * a_inputFile - [RO, *RO] - (char *)
+ * Pointer to character string representing file to be
+ * used as "standard input" for the command.
+ * == (char *)NULL to use "/dev/null" as standard input
+ * a_cmd - [RO, *RO] - (char *)
+ * Pointer to character string representing the full path
+ * of the Unix command to execute
+ * ... - [RO] (?)
+ * Zero or more arguments to the Unix command
+ * The argument list must be ended with (void *)NULL
+ * Returns: int
+ * == 0 - Command executed
+ * Look at r_status for results of Unix command
+ * != 0 - problems executing command
+ * r_status and r_results have no meaning
+ * NOTE: Any results returned is placed in new storage for the
+ * calling method. The caller must use 'free' to dispose
+ * of the storage once the results are no longer needed.
+ * NOTE: If LU_SUCCESS is returned, 'r_status' must be queried to
+ * determine the results of the Unix command.
+ */
+
+int
+e_ExecCmdList(int *r_status, char **r_results,
+ char *a_inputFile, char *a_cmd, ...)
+{
+ va_list ap; /* references variable argument list */
+ char *array[MAX_EXEC_CMD_ARGS+1];
+ int argno = 0;
+
+ /*
+ * Create argument array for exec system call
+ */
+
+ bzero(array, sizeof (array));
+
+ va_start(ap, a_cmd); /* Begin variable argument processing */
+
+ for (argno = 0; argno < MAX_EXEC_CMD_ARGS; argno++) {
+ array[argno] = va_arg(ap, char *);
+ if (array[argno] == (char *)NULL) {
+ break;
+ }
+ }
+
+ va_end(ap);
+ return (e_ExecCmdArray(r_status, r_results, a_inputFile,
+ a_cmd, array));
+}
+
+/*
+ * Name: e_new_args
+ * Description: create a new argument array for use in exec() calls
+ * Arguments: initialCount - [RO, *RO] - (int)
+ * Initial number of elements to populate the
+ * argument array with - use best guess
+ * Returns: argArray_t *
+ * Pointer to argument array that can be used in other
+ * functions that accept it as an argument
+ * == (argArray_t *)NULL - error
+ * NOTE: you must call e_free_args() when the returned argument array is
+ * no longer needed so that all storage used can be freed up.
+ */
+
+argArray_t *
+e_new_args(int initialCount)
+{
+ argArray_t *aa;
+
+ /* allocate new argument array structure */
+
+ aa = (argArray_t *)calloc(1, sizeof (argArray_t));
+ if (aa == (argArray_t *)NULL) {
+ progerr(ERR_MALLOC, strerror(errno), sizeof (argArray_t),
+ "<argArray_t>");
+ return ((argArray_t *)NULL);
+ }
+
+ /* allocate initial argument array */
+
+ aa->_aaArgs = (char **)calloc(initialCount+1, sizeof (char *));
+ if (aa->_aaArgs == (char **)NULL) {
+ progerr(ERR_MALLOC, strerror(errno),
+ (initialCount+1)*sizeof (char *), "<char **>");
+ return ((argArray_t *)NULL);
+ }
+
+ /* initialize argument indexes */
+
+ aa->_aaNumArgs = 0;
+ aa->_aaMaxArgs = initialCount;
+
+ return (aa);
+}
+
+/*
+ * Name: e_add_arg
+ * Description: add new argument to argument array for use in exec() calls
+ * Arguments: a_args - [RO, *RW] - (argArray_t *)
+ * Pointer to argument array (previously allocated via
+ * a call to e_new_args) to add the argument to
+ * a_format - [RO, *RO] - (char *)
+ * Pointer to "printf" style format argument
+ * ... - [RO, *RO] - (varies)
+ * Arguments as appropriate for format statement
+ * Returns: boolean_t
+ * B_TRUE - success
+ * B_FALSE - failure
+ * Examples:
+ * - to add an argument that specifies a file descriptor:
+ * int fd;
+ * e_add_arg(aa, "/proc/self/fd/%d", fd);
+ * - to add a flag or other known text:
+ * e_add_arg(aa, "-s")
+ * - to add random text:
+ * char *random_text;
+ * e_add_arg(aa, "%s", random_text);
+ */
+
+/*PRINTFLIKE2*/
+boolean_t
+e_add_arg(argArray_t *a_args, char *a_format, ...)
+{
+ char *rstr = (char *)NULL;
+ char bfr[MAX_CANON];
+ size_t vres = 0;
+ va_list ap;
+
+ /*
+ * double argument array if array is full
+ */
+
+ if (a_args->_aaNumArgs >= a_args->_aaMaxArgs) {
+ int newMax;
+ char **newArgs;
+
+ newMax = a_args->_aaMaxArgs * 2;
+ newArgs = (char **)realloc(a_args->_aaArgs,
+ (newMax+1) * sizeof (char *));
+ if (newArgs == (char **)NULL) {
+ progerr(ERR_MALLOC, strerror(errno),
+ ((newMax+1) * sizeof (char *)), "<char **>");
+ return (B_FALSE);
+ }
+ a_args->_aaArgs = newArgs;
+ a_args->_aaMaxArgs = newMax;
+ }
+
+ /* determine size of argument to add to list */
+
+ va_start(ap, a_format);
+ vres = vsnprintf(bfr, sizeof (bfr), a_format, ap);
+ va_end(ap);
+
+ /* if it fit in the built in buffer, use that */
+ if (vres < sizeof (bfr)) {
+ /* dup text already generated in bfr */
+ rstr = strdup(bfr);
+ if (rstr == (char *)NULL) {
+ progerr(ERR_MALLOC, strerror(errno), vres+2,
+ "<char *>");
+ return (B_FALSE);
+ }
+ } else {
+ /* allocate space for argument to add */
+
+ rstr = (char *)malloc(vres+2);
+ if (rstr == (char *)NULL) {
+ progerr(ERR_MALLOC, strerror(errno), vres+2,
+ "<char *>");
+ return (B_FALSE);
+ }
+
+ /* generate argument to add */
+
+ va_start(ap, a_format);
+ vres = vsnprintf(rstr, vres+1, a_format, ap);
+ va_end(ap);
+ }
+
+ /* add argument to the end of the argument array */
+
+ a_args->_aaArgs[a_args->_aaNumArgs++] = rstr;
+ a_args->_aaArgs[a_args->_aaNumArgs] = (char *)NULL;
+
+ return (B_TRUE);
+}
+
+/*
+ * Name: e_get_argv
+ * Description: return (char **)argv pointer from argument array
+ * Arguments: a_args - [RO, *RW] - (argArray_t *)
+ * Pointer to argument array (previously allocated via
+ * a call to e_new_args) to return argv pointer for
+ * Returns: char **
+ * Pointer to (char **)argv pointer suitable for use
+ * in an exec*() call
+ * NOTE: the actual character array is always terminated with a (char *)NULL
+ */
+
+char **
+e_get_argv(argArray_t *a_args)
+{
+ return (a_args->_aaArgs);
+}
+
+/*
+ * Name: e_get_argc
+ * Description: return (int) argc count from argument array
+ * Arguments: a_args - [RO, *RW] - (argArray_t *)
+ * Pointer to argument array (previously allocated via
+ * a call to e_new_args) to return argc count for
+ * Returns: int
+ * Count of the number of arguments in the argument array
+ * suitable for use in an exec*() call
+ */
+
+int
+e_get_argc(argArray_t *a_args)
+{
+ return (a_args->_aaNumArgs);
+}
+
+/*
+ * Name: e_free_args
+ * Description: free all storage contained in an argument array previously
+ * allocated by a call to e_new_args
+ * Arguments: a_args - [RO, *RW] - (argArray_t *)
+ * Pointer to argument array (previously allocated via
+ * a call to e_new_args) to free
+ * Returns: void
+ * NOTE: preserves errno (usually called right after e_execCmd*())
+ */
+
+void
+e_free_args(argArray_t *a_args)
+{
+ int i;
+ int lerrno = errno;
+
+ /* free all arguments in the argument array */
+
+ for (i = (a_args->_aaNumArgs-1); i >= 0; i--) {
+ (void) free(a_args->_aaArgs[i]);
+ a_args->_aaArgs[i] = (char *)NULL;
+ }
+
+ /* free argument array */
+
+ (void) free(a_args->_aaArgs);
+
+ /* free argument array structure */
+
+ (void) free(a_args);
+
+ /* restore errno */
+
+ errno = lerrno;
+}
diff --git a/usr/src/lib/libpkg/common/security.c b/usr/src/lib/libpkg/common/security.c
new file mode 100644
index 0000000000..9f2070c0c6
--- /dev/null
+++ b/usr/src/lib/libpkg/common/security.c
@@ -0,0 +1,282 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+
+/*
+ * Module: security.c
+ * Description:
+ * Module for handling certificates and various
+ * utilities to access their data.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <ctype.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <limits.h>
+#include <pkgstrct.h>
+#include <pkginfo.h>
+#include <locale.h>
+#include <libintl.h>
+#include <unistd.h>
+#include <stdlib.h>
+
+#include <openssl/bio.h>
+#include <openssl/pkcs12.h>
+#include <openssl/pkcs7.h>
+#include <openssl/x509.h>
+#include <openssl/err.h>
+#include <openssl/ssl.h>
+#include "pkgerr.h"
+#include "pkglib.h"
+#include "pkglibmsgs.h"
+#include "pkglocale.h"
+#include "p12lib.h"
+
+/* length of allowable passwords */
+#define MAX_PASSLEN 128
+
+/*
+ * Name: init_security
+ * Description: Initializes structures, libraries, for security operations
+ * Arguments: none
+ * Returns: 0 if we couldn't initialize, non-zero otherwise
+ */
+void
+sec_init(void)
+{
+ OpenSSL_add_all_algorithms();
+ SSL_load_error_strings();
+ ERR_load_SUNW_strings();
+ (void) SSL_library_init();
+}
+
+/*
+ * get_cert_chain - Builds a chain of certificates, from a given
+ * user certificate to a trusted certificate.
+ *
+ * Arguments:
+ * err - Error object to add errors to
+ * cert - User cert to start with
+ * cas - Trusted certs to use as trust anchors
+ * chain - The resulting chain of certs (in the form of an
+ * ordered set) is placed here.
+ *
+ * Returns:
+ * 0 - Success - chain is stored in 'chain'.
+ * non-zero - Failure, errors recorded in err
+ */
+int
+get_cert_chain(PKG_ERR *err, X509 *cert, STACK_OF(X509) *clcerts,
+ STACK_OF(X509) *cas, STACK_OF(X509) **chain)
+{
+ X509_STORE_CTX *store_ctx = NULL;
+ X509_STORE *ca_store = NULL;
+ X509 *ca_cert = NULL;
+ int i;
+ int ret = 0;
+
+ if ((ca_store = X509_STORE_new()) == NULL) {
+ pkgerr_add(err, PKGERR_NOMEM,
+ gettext(ERR_MEM));
+ ret = 1;
+ goto cleanup;
+ }
+
+ /* add all ca certs into the store */
+ for (i = 0; i < sk_X509_num(cas); i++) {
+ /* LINTED pointer cast may result in improper alignment */
+ ca_cert = sk_X509_value(cas, i);
+ if (X509_STORE_add_cert(ca_store, ca_cert) == 0) {
+ pkgerr_add(err, PKGERR_NOMEM, gettext(ERR_MEM));
+ ret = 1;
+ goto cleanup;
+ }
+ }
+
+ /* initialize context object used during the chain resolution */
+
+ if ((store_ctx = X509_STORE_CTX_new()) == NULL) {
+ pkgerr_add(err, PKGERR_NOMEM, gettext(ERR_MEM));
+ ret = 1;
+ goto cleanup;
+ }
+
+ (void) X509_STORE_CTX_init(store_ctx, ca_store, cert, clcerts);
+ /* attempt to verify the cert, which builds the cert chain */
+ if (X509_verify_cert(store_ctx) <= 0) {
+ pkgerr_add(err, PKGERR_CHAIN,
+ gettext(ERR_CERTCHAIN),
+ get_subject_display_name(cert),
+ X509_verify_cert_error_string(store_ctx->error));
+ ret = 1;
+ goto cleanup;
+ }
+ *chain = X509_STORE_CTX_get1_chain(store_ctx);
+
+cleanup:
+ if (ca_store != NULL)
+ (void) X509_STORE_free(ca_store);
+ if (store_ctx != NULL) {
+ (void) X509_STORE_CTX_cleanup(store_ctx);
+ (void) X509_STORE_CTX_free(store_ctx);
+ }
+
+ return (ret);
+}
+
+/*
+ * Name: get_subject_name
+ * Description: Retrieves a name used for identifying a certificate's subject.
+ *
+ * Arguments: cert - The certificate to get the name from
+ *
+ * Returns : A static buffer containing the common name (CN) of the
+ * subject of the cert.
+ *
+ * if the CN is not available, returns a string with the entire
+ * X509 distinguished name.
+ */
+char
+*get_subject_display_name(X509 *cert)
+{
+
+ X509_NAME *xname;
+ static char sname[ATTR_MAX];
+
+ xname = X509_get_subject_name(cert);
+ if (X509_NAME_get_text_by_NID(xname,
+ NID_commonName, sname,
+ ATTR_MAX) <= 0) {
+ (void) strncpy(sname,
+ X509_NAME_oneline(xname,
+ NULL, 0), ATTR_MAX);
+ sname[ATTR_MAX - 1] = '\0';
+ }
+ return (sname);
+}
+
+/*
+ * Name: get_display_name
+ * Description: Retrieves a name used for identifying a certificate's issuer.
+ *
+ * Arguments: cert - The certificate to get the name from
+ *
+ * Returns : A static buffer containing the common name (CN)
+ * of the issuer of the cert.
+ *
+ * if the CN is not available, returns a string with the entire
+ * X509 distinguished name.
+ */
+char
+*get_issuer_display_name(X509 *cert)
+{
+
+ X509_NAME *xname;
+ static char sname[ATTR_MAX];
+
+ xname = X509_get_issuer_name(cert);
+ if (X509_NAME_get_text_by_NID(xname,
+ NID_commonName, sname,
+ ATTR_MAX) <= 0) {
+ (void) strncpy(sname,
+ X509_NAME_oneline(xname,
+ NULL, 0), ATTR_MAX);
+ sname[ATTR_MAX - 1] = '\0';
+ }
+ return (sname);
+}
+
+
+/*
+ * Name: get_serial_num
+ * Description: Retrieves the serial number of an X509 cert
+ *
+ * Arguments: cert - The certificate to get the data from
+ *
+ * Returns : A static buffer containing the serial number
+ * of the cert
+ *
+ * if the SN is not available, returns NULL
+ */
+char
+*get_serial_num(X509 *cert)
+{
+ static char sn_str[ATTR_MAX];
+ ASN1_INTEGER *sn;
+
+ if ((sn = X509_get_serialNumber(cert)) != 0) {
+ return (NULL);
+ } else {
+ (void) snprintf(sn_str, ATTR_MAX, "%ld",
+ ASN1_INTEGER_get(sn));
+ }
+
+ return (sn_str);
+}
+
+/*
+ * Name: get_fingerprint
+ * Description: Generates a fingerprint string given
+ * a digest algorithm with which to calculate
+ * the fingerprint
+ *
+ * Arguments: cert - The certificate to get the data from
+ * Arguments: alg - The algorithm to use to calculate the fingerprint
+ *
+ * Returns : A static buffer containing the digest
+ * NULL if cert is NULL, or digest cannot be calculated
+ */
+char
+*get_fingerprint(X509 *cert, const EVP_MD *alg)
+{
+ static char fp_str[ATTR_MAX];
+ char tmp[ATTR_MAX] = "";
+ unsigned int n;
+ unsigned char md[EVP_MAX_MD_SIZE];
+ int i;
+
+ if (!X509_digest(cert, alg, md, &n)) {
+ return (NULL);
+ }
+
+ /* start with empty string */
+ fp_str[0] = '\0';
+
+ for (i = 0; i < (int)n; i++) {
+ /* form a byte of the fingerprint */
+ (void) snprintf(tmp, ATTR_MAX, "%02X:", md[i]);
+ /* cat it onto the end of the result */
+ (void) strlcat(fp_str, tmp, ATTR_MAX);
+ }
+
+ /* nuke trailing ':' */
+ fp_str[strlen(fp_str) - 1] = '\0';
+
+ return (fp_str);
+}
diff --git a/usr/src/lib/libpkg/common/srchcfile.c b/usr/src/lib/libpkg/common/srchcfile.c
new file mode 100644
index 0000000000..dd24a855cd
--- /dev/null
+++ b/usr/src/lib/libpkg/common/srchcfile.c
@@ -0,0 +1,1278 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
+/* All Rights Reserved */
+
+
+
+#include <stdio.h>
+#include <limits.h>
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+#include <ctype.h>
+#include <sys/types.h>
+#include <libintl.h>
+#include "pkglib.h"
+#include "pkgstrct.h"
+#include "pkglocale.h"
+#include "pkglibmsgs.h"
+
+/*
+ * Forward declarations
+ */
+
+static void findend(char **cp);
+static int getend(char **cp);
+static int getstr(char **cp, int n, char *str, int separator[]);
+
+/* from gpkgmap.c */
+int getnumvfp(char **cp, int base, long *d, long bad);
+int getlnumvfp(char **cp, int base, fsblkcnt_t *d, long bad);
+
+/*
+ * Module globals
+ */
+
+static char lpath[PATH_MAX]; /* for ept->path */
+static char mylocal[PATH_MAX]; /* for ept->ainfo.local */
+static int decisionTableInit = 0;
+
+/*
+ * These arrays must be indexable by an unsigned char.
+ */
+
+static int ISPKGPATHSEP[UCHAR_MAX+1];
+static int ISWORDSEP[UCHAR_MAX+1];
+static int ISPKGNAMESEP[UCHAR_MAX+1];
+
+/*
+ * Name: WRITEDATA
+ * Description: write out data to VFP_T given start and end pointers
+ * Arguments: VFP - (VFP_T *) - [RO, *RW]
+ * Contents file VFP to narrow search on
+ * FIRSTPOS - (char *) - [RO, *RO]
+ * Pointer to first byte to write out
+ * LASTPOS - (char *) - [RO, *RO]
+ * Pointer to last byte to write out
+ */
+
+#define WRITEDATA(VFP, FIRSTPOS, LASTPOS) \
+ { \
+ ssize_t XXlenXX; \
+ /* compute number of bytes skipped */ \
+ XXlenXX = (ptrdiff_t)(LASTPOS) - (ptrdiff_t)(FIRSTPOS); \
+ /* write the bytes out */ \
+ vfpPutBytes((VFP), (FIRSTPOS), XXlenXX); \
+ }
+
+/*
+ * Name: COPYPATH
+ * Description: copy path limiting size to destination capacity
+ * Arguments: DEST - (char []) - [RW]
+ * SRC - (char *) - [RO, *RO]
+ * Pointer to first byte of path to copy
+ * LEN - (int) - [RO]
+ * Number of bytes to copy
+ */
+
+#define COPYPATH(DEST, SRC, LEN) \
+ { \
+ /* assure return path does not overflow */ \
+ if ((LEN) > sizeof ((DEST))) { \
+ (LEN) = sizeof ((DEST))-1; \
+ } \
+ /* copy return path to local storage */ \
+ (void) memcpy((DEST), (SRC), (LEN)); \
+ (DEST)[(LEN)] = '\0'; \
+ }
+
+/*
+ * Name: narrowSearch
+ * Description: narrow the search location for a specified path
+ * The contents and package map files are always sorted by path.
+ * This function is given a target path to search for given the
+ * current location in a contents file. It is assured that the
+ * target path has not been searched for yet in the contents file
+ * so the current location in the contents file is guaranteed to
+ * be less than the location of the target path (if present).
+ * Given this employ a binary search to speed up the search for
+ * the path nearest to a specified target path.
+ * Arguments: a_vfp - (VFP_T *) - [RO, *RW]
+ * Contents file VFP to narrow search on
+ * a_path - (char *) - [RO, *RO]
+ * Pointer to path to search for
+ * a_pathLen - (size_t) - [RO]
+ * Length of string (a_path)
+ * Returns: char * - pointer to first byte of entry in contents file that
+ * is guaranteed to be the closest match to the specified
+ * a_path without being "greater than" the path.
+ * == (char *)NULL if no entry found
+ */
+
+static char *
+narrowSearch(VFP_T *a_vfp, char *a_path, size_t a_pathLen)
+{
+ char *phigh;
+ char *plow;
+ char *pmid;
+ int n;
+ size_t plen;
+
+ /* if no path to compare, start at beginning */
+
+ if ((a_path == (char *)NULL) || (*a_path == '\0')) {
+ return ((char *)NULL);
+ }
+
+ /* if the contents file is empty, resort to sequential search */
+
+ if (vfpGetBytesRemaining(a_vfp) <= 1) {
+ return ((char *)NULL);
+ }
+
+ /*
+ * test against first path - if the path specified is less than the
+ * first path in the contents file, then the path can be inserted
+ * before the first entry in the contents file.
+ */
+
+ /* locate start of first line */
+
+ plow = vfpGetCurrCharPtr(a_vfp);
+ pmid = plow;
+
+ /* if first path not absolute, resort to sequential search */
+
+ if (*pmid != '/') {
+ return ((char *)NULL);
+ }
+
+ /* find end of path */
+
+ while (ISPKGPATHSEP[(int)*pmid] == 0) {
+ pmid++;
+ }
+
+ /* determine length of path */
+
+ plen = (ptrdiff_t)pmid - (ptrdiff_t)plow;
+
+ /* compare target path with current path */
+
+ n = strncmp(a_path, plow, plen);
+ if (n == 0) {
+ /* if lengths same exact match return position found */
+ if (a_pathLen == plen) {
+ return (plow);
+ }
+ /* not exact match - a_path > pm */
+ n = a_pathLen;
+ }
+
+ /* return if target is less than or equal to first entry */
+
+ if (n <= 0) {
+ return (plow);
+ }
+
+ /*
+ * test against last path - if the path specified is greater than the
+ * last path in the contents file, then the path can be appended after
+ * the last entry in the contents file.
+ */
+
+ /* locate start of last line */
+
+ plow = vfpGetCurrCharPtr(a_vfp);
+ pmid = vfpGetLastCharPtr(a_vfp);
+
+ while ((pmid > plow) && (!((pmid[0] == '/') && (pmid[-1] == '\n')))) {
+ pmid--;
+ }
+
+ /* if absolute path, do comparison */
+
+ if ((pmid > plow) && (*pmid == '/')) {
+ plow = pmid;
+
+ /* find end of path */
+
+ while (ISPKGPATHSEP[(int)*pmid] == 0) {
+ pmid++;
+ }
+
+ /* determine length of path */
+
+ plen = (ptrdiff_t)pmid - (ptrdiff_t)plow;
+
+ /* compare target path with current path */
+
+ n = strncmp(a_path, plow, plen);
+ if (n == 0) {
+ /* if lengths same exact match return position found */
+ if (a_pathLen == plen) {
+ return (plow);
+ }
+ /* not exact match - a_path > pm */
+ n = a_pathLen;
+ }
+
+ /* return if target is greater than or equal to entry */
+
+ if (n >= 0) {
+ return (plow);
+ }
+ }
+ /*
+ * firstPath < targetpath < lastPath:
+ * binary search looking for closest "less than" match
+ */
+
+ plow = vfpGetCurrCharPtr(a_vfp);
+ phigh = vfpGetLastCharPtr(a_vfp);
+
+ for (;;) {
+ char *pm;
+
+ /* determine number of bytes left in search area */
+
+ plen = (ptrdiff_t)phigh - (ptrdiff_t)plow;
+
+ /* calculate mid point between current low and high points */
+
+ pmid = plow + (plen >> 1);
+
+ /* backup and find first "\n/" -or- start of buffer */
+
+ while ((pmid > plow) &&
+ (!((pmid[0] == '/') && (pmid[-1] == '\n')))) {
+ pmid--;
+ }
+
+ /* return lowest line found if current line not past that */
+
+ if (pmid <= plow) {
+ return (plow);
+ }
+
+ /* remember start of this line */
+
+ pm = pmid;
+
+ /* find end of path */
+
+ while (ISPKGPATHSEP[(int)*pmid] == 0) {
+ pmid++;
+ }
+
+ /* determine length of path */
+
+ plen = (ptrdiff_t)pmid - (ptrdiff_t)pm;
+
+ /* compare target path with current path */
+
+ n = strncmp(a_path, pm, plen);
+
+ if (n == 0) {
+ /* if lengths same exact match return position found */
+ if (a_pathLen == plen) {
+ return (pm);
+ }
+ /* not exact match - a_path > pm */
+ n = a_pathLen;
+ }
+
+
+ /* not exact match - determine which watermark to split */
+
+ if (n > 0) { /* a_path > pm */
+ plow = pm;
+ } else { /* a_path < pm */
+ phigh = pm;
+ }
+ }
+ /*NOTREACHED*/
+}
+
+/*
+ * Name: srchcfile
+ * Description: search contents file looking for closest match to entry,
+ * creating a new contents file if output contents file specified
+ * Arguments: ept - (struct cfent *) - [RO, *RW]
+ * - contents file entry, describing last item found
+ * path - (char *) - [RO, *RO]
+ * - path to search for in contents file
+ * - If path is "*", then the next entry is returned;
+ * the next entry always matches this path
+ * - If the path is (char *)NULL or "", then all remaining
+ * entries are processed and copied out to the
+ * file specified by cfTmpVFp
+ * cfVfp - (VFP_T *) - [RO, *RW]
+ * - VFP_T open on contents file to search
+ * cfTmpVfp - (VFP_T *) - [RO, *RW]
+ * - VFP_T open on temporary contents file to populate
+ * Returns: int
+ * < 0 - error occurred
+ * - Use getErrstr to retrieve character-string describing
+ * the reason for failure
+ * == 0 - no match found
+ * - specified path not in the contents file
+ * - all contents of cfVfp copied to cfTmpVfp
+ * - current character of cfVfp is at end of file
+ * == 1 - exact match found
+ * - specified path found in contents file
+ * - contents of cfVfp up to entry found copied to cfTmpVfp
+ * - current character of cfVfp is first character of
+ * entry found
+ * - this value is always returned if path is "*" and the
+ * next entry is returned - -1 is returned when no more
+ * entries are left to process
+ * == 2 - entry found which is GREATER than path specified
+ * - specified path would fit BEFORE entry found
+ * - contents of cfVfp up to entry found copied to cfTmpVfp
+ * - current character of cfVfp is first character of
+ * entry found
+ * Side Effects:
+ * - The ept structure supplied is filled in with a description of
+ * the item that caused the search to terminate, except in the
+ * case of '0' in which case the contents of 'ept' is undefined.
+ * - NOTE: the ept->path item points to a path that is statically
+ * allocated and will be overwritten on the next call.
+ * - NOTE: the ept->ainfo.local item points to a path that is
+ * statically allocated and will be overwritten on the next call.
+ */
+
+int
+srchcfile(struct cfent *ept, char *path, VFP_T *cfVfp, VFP_T *cfTmpVfp)
+{
+ char *cpath_start = (char *)NULL;
+ char *firstPos = vfpGetCurrCharPtr(cfVfp);
+ char *lastPos = NULL;
+ char *pos;
+ char classname[CLSSIZ+1];
+ char pkgname[PKGSIZ+1];
+ int anypath = 0;
+ int c;
+ int dataSkipped = 0;
+ int n;
+ int rdpath;
+ size_t cpath_len = 0;
+ size_t pathLength;
+ struct pinfo *lastpinfo;
+ struct pinfo *pinfo;
+
+ /*
+ * this code does not use nested subroutines because execution time
+ * of this routine is especially critical to installation and upgrade
+ */
+
+ /* initialize local variables */
+
+ setErrstr(NULL); /* no error message currently cached */
+ pathLength = (path == (char *)NULL ? 0 : strlen(path));
+ lpath[0] = '\0';
+ lpath[sizeof (lpath)-1] = '\0';
+
+ /* initialize ept structure values */
+
+ (void) strlcpy(ept->ainfo.group, BADGROUP, sizeof (ept->ainfo.group));
+ (void) strlcpy(ept->ainfo.owner, BADOWNER, sizeof (ept->ainfo.owner));
+ (void) strlcpy(ept->pkg_class, BADCLASS, sizeof (ept->pkg_class));
+ ept->ainfo.local = (char *)NULL;
+ ept->ainfo.mode = BADMODE;
+ ept->cinfo.cksum = BADCONT;
+ ept->cinfo.modtime = BADCONT;
+ ept->cinfo.size = (fsblkcnt_t)BADCONT;
+ ept->ftype = BADFTYPE;
+ ept->npkgs = 0;
+ ept->path = (char *)NULL;
+ ept->pinfo = (struct pinfo *)NULL;
+ ept->pkg_class_idx = -1;
+ ept->volno = 0;
+
+ /*
+ * populate decision tables that implement fast character checking;
+ * this is much faster than the equivalent strpbrk() call or a
+ * while() loop checking for the characters. It is only faster if
+ * there are at least 3 characters to scan for - when checking for
+ * one or two characters (such as '\n' or '\0') its faster to do
+ * a simple while() loop.
+ */
+
+ if (decisionTableInit == 0) {
+ /*
+ * any chars listed stop scan;
+ * scan stops on first byte found that is set to '1' below
+ */
+
+ /*
+ * Separators for path names, normal space and =
+ * for linked filenames
+ */
+ bzero(ISPKGPATHSEP, sizeof (ISPKGPATHSEP));
+ ISPKGPATHSEP['='] = 1; /* = */
+ ISPKGPATHSEP[' '] = 1; /* space */
+ ISPKGPATHSEP['\t'] = 1; /* horizontal-tab */
+ ISPKGPATHSEP['\n'] = 1; /* new-line */
+ ISPKGPATHSEP['\0'] = 1; /* NULL character */
+
+ /*
+ * Separators for normal words
+ */
+ bzero(ISWORDSEP, sizeof (ISWORDSEP));
+ ISWORDSEP[' '] = 1;
+ ISWORDSEP['\t'] = 1;
+ ISWORDSEP['\n'] = 1;
+ ISWORDSEP['\0'] = 1;
+
+ /*
+ * Separators for list of packages, includes \\ for
+ * alternate ftype and : for classname
+ */
+ bzero(ISPKGNAMESEP, sizeof (ISPKGNAMESEP));
+ ISPKGNAMESEP[' '] = 1;
+ ISPKGNAMESEP['\t'] = 1;
+ ISPKGNAMESEP['\n'] = 1;
+ ISPKGNAMESEP[':'] = 1;
+ ISPKGNAMESEP['\\'] = 1;
+ ISPKGNAMESEP['\0'] = 1;
+
+ decisionTableInit = 1;
+ }
+
+ /* if no bytes in contents file, return 0 */
+
+ if (vfpGetBytesRemaining(cfVfp) <= 1) {
+ return (0);
+ }
+
+ /* if the path to scan for is empty, act like no path was specified */
+
+ if ((path != (char *)NULL) && (*path == '\0')) {
+ path = (char *)NULL;
+ }
+
+ /*
+ * if path to search for is "*", then we will return the first path
+ * we encounter as a match, otherwise we return an error
+ */
+
+ if ((path != (char *)NULL) && (path[0] != '/')) {
+ if (strcmp(path, "*") != 0) {
+ setErrstr(pkg_gt(ERR_ILLEGAL_SEARCH_PATH));
+ return (-1);
+ }
+ anypath = 1;
+ }
+
+ /* attempt to narrow down the search for the specified path */
+
+ if (anypath == 0) {
+ char *np;
+
+ np = narrowSearch(cfVfp, path, pathLength);
+ if (np != (char *)NULL) {
+ dataSkipped = 1;
+ lastPos = np;
+ vfpSetCurrCharPtr(cfVfp, np);
+ }
+ }
+
+ /*
+ * If the path to search for in the source contents file is NULL, then
+ * this is a request to scan to the end of the source contents file. If
+ * there is a temporary contents file to copy entries to, all that needs
+ * to be done is to copy the data remaining from the current location in
+ * the source contents file to the end of the temporary contents file.
+ * if there is no temporary contents file to copy to, then all that
+ * needs to be done is to seek to the end of the source contents file.
+ */
+
+ if ((anypath == 0) && (path == (char *)NULL)) {
+ if (cfTmpVfp != (VFP_T *)NULL) {
+ if (vfpGetBytesRemaining(cfVfp) > 0) {
+ WRITEDATA(cfTmpVfp, firstPos,
+ vfpGetLastCharPtr(cfVfp)+1);
+ }
+ *vfpGetLastCharPtr(cfTmpVfp) = '\0';
+ }
+ vfpSeekToEnd(cfVfp);
+ return (0);
+ }
+
+ /*
+ * *********************************************************************
+ * main loop processing entries from the contents file looking for
+ * the specified path
+ * *********************************************************************
+ */
+
+ for (;;) {
+ char *p;
+
+ /* not reading old style entry */
+
+ rdpath = 0;
+
+ /* determine first character of the next entry */
+
+ if (vfpGetBytesRemaining(cfVfp) <= 0) {
+ /* no bytes in contents file current char is NULL */
+
+ c = '\0';
+ } else {
+ /* grab path from first entry */
+
+ c = vfpGetcNoInc(cfVfp);
+ }
+
+ /* save current position in file */
+
+ pos = vfpGetCurrCharPtr(cfVfp);
+
+ /*
+ * =============================================================
+ * at the first character of the next entry in the contents file
+ * if not absolute path check for exceptions and old style entry
+ * --> if end of contents file write out skipped data and return
+ * --> if comment character skip to end of line and restart loop
+ * --> else process "old style entry: ftype class path"
+ * =============================================================
+ */
+
+ if (c != '/') {
+ /* if NULL character then end of contents file found */
+
+ if (c == '\0') {
+ /* write out skipped data before returning */
+ if (dataSkipped &&
+ (cfTmpVfp != (VFP_T *)NULL)) {
+ WRITEDATA(cfTmpVfp, firstPos, lastPos);
+ *vfpGetLastCharPtr(cfTmpVfp) = '\0';
+ }
+
+ return (0); /* no more entries */
+ }
+
+ /* ignore lines that begin with #, : or a "space" */
+
+ if ((isspace(c) != 0) || (c == '#') || (c == ':')) {
+ /* line is a comment */
+ findend(&vfpGetCurrCharPtr(cfVfp));
+ continue;
+ }
+
+ /*
+ * old style entry - format is:
+ * ftype class path
+ * set ept->ftype to the type
+ * set ept->class to the class
+ * set ept->path to point to lpath
+ * set cpath_start/cpath_len to point to the file name
+ * set rdpath to '1' to indicate old style entry parsed
+ */
+
+ while (isspace((c = vfpGetc(cfVfp))))
+ ;
+
+ switch (c) {
+ case '?': case 'f': case 'v': case 'e': case 'l':
+ case 's': case 'p': case 'c': case 'b': case 'd':
+ case 'x':
+ /* save ftype */
+ ept->ftype = (char)c;
+
+ /* save class */
+ if (getstr(&vfpGetCurrCharPtr(cfVfp), CLSSIZ,
+ ept->pkg_class, ISWORDSEP)) {
+ setErrstr(ERR_CANNOT_READ_CLASS_TOKEN);
+ findend(&vfpGetCurrCharPtr(cfVfp));
+ return (-1);
+ }
+
+ /*
+ * locate file name up to "=", set cpath_start
+ * and cpath_len to point to the file name
+ */
+ cpath_start = vfpGetCurrCharPtr(cfVfp);
+ p = vfpGetCurrCharPtr(cfVfp);
+
+ /*
+ * skip past all bytes until first '= \t\n\0':
+ */
+ while (ISPKGPATHSEP[(int)*p] == 0) {
+ p++;
+ }
+
+ cpath_len = vfpGetCurrPtrDelta(cfVfp, p);
+
+ /*
+ * if the path is zero bytes, line is corrupted
+ */
+
+ if (cpath_len < 1) {
+ setErrstr(ERR_CANNOT_READ_PATHNAME_FLD);
+ findend(&vfpGetCurrCharPtr(cfVfp));
+ return (-1);
+ }
+
+ vfpIncCurrPtrBy(cfVfp, cpath_len);
+
+ /* set path to point to local path cache */
+ ept->path = lpath;
+
+ /* set flag indicating path already parsed */
+ rdpath = 1;
+ break;
+
+ case '\0':
+ /* end of line before new-line seen */
+ vfpDecCurrPtr(cfVfp);
+ setErrstr(ERR_INCOMPLETE_ENTRY);
+ return (-1);
+
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ /* volume number seen */
+ setErrstr(ERR_VOLUMENO_UNEXPECTED);
+ findend(&vfpGetCurrCharPtr(cfVfp));
+ return (-1);
+
+ case 'i':
+ /* type i files are not cataloged */
+ setErrstr(ERR_FTYPE_I_UNEXPECTED);
+ findend(&vfpGetCurrCharPtr(cfVfp));
+ return (-1);
+
+ default:
+ /* unknown ftype */
+ setErrstr(ERR_UNKNOWN_FTYPE);
+ findend(&vfpGetCurrCharPtr(cfVfp));
+ return (-1);
+ }
+ } else {
+ /*
+ * current entry DOES start with absolute path
+ * set ept->path to point to lpath
+ * set cpath_start/cpath_len to point to the file name
+ */
+ /* copy first token into path element of passed structure */
+
+ cpath_start = vfpGetCurrCharPtr(cfVfp);
+
+ p = cpath_start;
+
+ /*
+ * skip past all bytes until first from '= \t\n\0':
+ */
+
+ while (ISPKGPATHSEP[(int)*p] == 0) {
+ p++;
+ }
+
+ cpath_len = vfpGetCurrPtrDelta(cfVfp, p);
+
+ vfpIncCurrPtrBy(cfVfp, cpath_len);
+
+ if (vfpGetcNoInc(cfVfp) == '\0') {
+ setErrstr(ERR_INCOMPLETE_ENTRY);
+ findend(&vfpGetCurrCharPtr(cfVfp));
+ return (-1);
+ }
+
+ ept->path = lpath;
+ }
+
+ /*
+ * =============================================================
+ * if absolute path then the path is collected and we are at the
+ * first byte following the absolute path name;
+ * if not an absolute path then an old style entry, ept has been
+ * filled with the type and class and path name.
+ * determine if we have read the pathname which identifies
+ * the entry we are searching for
+ * =============================================================
+ */
+
+ if (anypath != 0) {
+ n = 0; /* next entry is "equal to" */
+ } else if (path == (char *)NULL) {
+ n = 1; /* next entry is "greater than" */
+ } else {
+ n = strncmp(path, cpath_start, cpath_len);
+ if ((n == 0) && (cpath_len != pathLength)) {
+ n = cpath_len;
+ }
+ }
+
+ /* get first character following the end of the path */
+
+ c = vfpGetc(cfVfp);
+
+ /*
+ * if an exact match, always parse out the local path
+ */
+
+ if (n == 0) {
+ /*
+ * we want to return information about this path in
+ * the structure provided, so parse any local path
+ * and jump to code which parses rest of the input line
+ */
+ if (c == '=') {
+ /* parse local path specification */
+ if (getstr(&vfpGetCurrCharPtr(cfVfp), PATH_MAX,
+ mylocal, ISWORDSEP)) {
+
+ /* copy path found to 'lpath' */
+ COPYPATH(lpath, cpath_start, cpath_len);
+
+ setErrstr(ERR_CANNOT_READ_LL_PATH);
+ findend(&vfpGetCurrCharPtr(cfVfp));
+ return (-1);
+ }
+ ept->ainfo.local = mylocal;
+ }
+ }
+
+ /*
+ * if an exact match and processing a new style entry, read the
+ * remaining information from the new style entry - if this is
+ * an old style entry (rdpath != 0) then the existing info has
+ * already been processed as it exists before the pathname and
+ * not after like a new style entry
+ */
+
+ if (n == 0 && rdpath == 0) {
+ while (isspace((c = vfpGetc(cfVfp))))
+ ;
+
+ switch (c) {
+ case '?': case 'f': case 'v': case 'e': case 'l':
+ case 's': case 'p': case 'c': case 'b': case 'd':
+ case 'x':
+ /* save ftype */
+ ept->ftype = (char)c;
+
+ /* save class */
+ if (getstr(&vfpGetCurrCharPtr(cfVfp), CLSSIZ,
+ ept->pkg_class, ISWORDSEP)) {
+
+ /* copy path found to 'lpath' */
+ COPYPATH(lpath, cpath_start, cpath_len);
+
+ setErrstr(ERR_CANNOT_READ_CLASS_TOKEN);
+ findend(&vfpGetCurrCharPtr(cfVfp));
+ return (-1);
+ }
+ break; /* we already read the pathname */
+
+ case '\0':
+ /* end of line before new-line seen */
+ vfpDecCurrPtr(cfVfp);
+
+ /* copy path found to 'lpath' */
+ COPYPATH(lpath, cpath_start, cpath_len);
+
+ setErrstr(ERR_INCOMPLETE_ENTRY);
+ return (-1);
+
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+
+ /* copy path found to 'lpath' */
+ COPYPATH(lpath, cpath_start, cpath_len);
+
+ setErrstr(ERR_VOLUMENO_UNEXPECTED);
+ findend(&vfpGetCurrCharPtr(cfVfp));
+ return (-1);
+
+ case 'i':
+
+ /* copy path found to 'lpath' */
+ COPYPATH(lpath, cpath_start, cpath_len);
+
+ setErrstr(ERR_FTYPE_I_UNEXPECTED);
+ findend(&vfpGetCurrCharPtr(cfVfp));
+ return (-1);
+
+ default:
+ /* unknown ftype */
+
+ /* copy path found to 'lpath' */
+ COPYPATH(lpath, cpath_start, cpath_len);
+
+ setErrstr(ERR_UNKNOWN_FTYPE);
+ findend(&vfpGetCurrCharPtr(cfVfp));
+ return (-1);
+ }
+ }
+
+ /*
+ * if an exact match all processing is completed; break out of
+ * the main processing loop and finish processing this entry
+ * prior to returning to the caller.
+ */
+
+ if (n == 0) {
+ break;
+ }
+
+ /*
+ * this entry is not an exact match for the path being searched
+ * for - if this entry is GREATER THAN the path being searched
+ * for then finish processing and return GREATER THAN result
+ * to the caller so the entry for the path being searched for
+ * can be added to the contents file.
+ */
+
+ if (n < 0) {
+ /*
+ * the entry we want would fit BEFORE the one we just
+ * read, so we need to unread what we've read by
+ * seeking back to the start of this entry
+ */
+
+ vfpSetCurrCharPtr(cfVfp, pos);
+
+ /* copy path found to 'lpath' */
+ COPYPATH(lpath, cpath_start, cpath_len);
+
+ /* write out any skipped data before returning */
+ if (dataSkipped && (cfTmpVfp != (VFP_T *)NULL)) {
+ WRITEDATA(cfTmpVfp, firstPos, lastPos);
+ }
+
+ return (2); /* path would insert here */
+ }
+
+ /*
+ * This entry is "LESS THAN" the specified path to search for
+ * need to process the next entry from the contents file. First,
+ * if writing to new contents file, update new contents file if
+ * processing old style entry; otherwise, update skipped data
+ * information to remember current last byte of skipped data.
+ */
+
+ if (cfTmpVfp != (VFP_T *)NULL) {
+ char *px;
+ ssize_t len;
+
+ if (rdpath != 0) {
+ /* modify record: write out any skipped data */
+ if (dataSkipped) {
+ WRITEDATA(cfTmpVfp, firstPos, lastPos);
+ }
+
+ /*
+ * copy what we've read and the rest of this
+ * line onto the specified output stream
+ */
+ vfpPutBytes(cfTmpVfp, cpath_start, cpath_len);
+ vfpPutc(cfTmpVfp, c);
+ vfpPutc(cfTmpVfp, ept->ftype);
+ vfpPutc(cfTmpVfp, ' ');
+ vfpPuts(cfTmpVfp, ept->pkg_class);
+
+ px = strchr(vfpGetCurrCharPtr(cfVfp), '\n');
+
+ if (px == (char *)NULL) {
+ len = vfpGetBytesRemaining(cfVfp);
+ vfpPutBytes(cfTmpVfp,
+ vfpGetCurrCharPtr(cfVfp), len);
+ vfpPutc(cfTmpVfp, '\n');
+ vfpSeekToEnd(cfVfp);
+ } else {
+ len = vfpGetCurrPtrDelta(cfVfp, px);
+ vfpPutBytes(cfTmpVfp,
+ vfpGetCurrCharPtr(cfVfp), len);
+ vfpIncCurrPtrBy(cfVfp, len);
+ }
+
+ /* reset skiped bytes if any data skipped */
+ if (dataSkipped) {
+ dataSkipped = 0;
+ lastPos = (char *)NULL;
+ firstPos = vfpGetCurrCharPtr(cfVfp);
+ }
+ } else {
+ /* skip data */
+ dataSkipped = 1;
+
+ px = strchr(vfpGetCurrCharPtr(cfVfp), '\n');
+
+ if (px == (char *)NULL) {
+ vfpSeekToEnd(cfVfp);
+ } else {
+ len = vfpGetCurrPtrDelta(cfVfp, px)+1;
+ vfpIncCurrPtrBy(cfVfp, len);
+ }
+ lastPos = vfpGetCurrCharPtr(cfVfp);
+ }
+ } else {
+ /*
+ * since this isn't the entry we want, just read the
+ * stream until we find the end of this entry and
+ * then start this search loop again
+ */
+ char *px;
+
+ px = strchr(vfpGetCurrCharPtr(cfVfp), '\n');
+
+ if (px == (char *)NULL) {
+ vfpSeekToEnd(cfVfp);
+
+ /* copy path found to 'lpath' */
+ COPYPATH(lpath, cpath_start, cpath_len);
+
+ setErrstr(pkg_gt(ERR_MISSING_NEWLINE));
+ findend(&vfpGetCurrCharPtr(cfVfp));
+ return (-1);
+ } else {
+ ssize_t len;
+
+ len = vfpGetCurrPtrDelta(cfVfp, px)+1;
+ vfpIncCurrPtrBy(cfVfp, len);
+ }
+ }
+ }
+
+ /*
+ * *********************************************************************
+ * end of main loop processing entries from contents file
+ * the loop is broken out of when an exact match for the
+ * path being searched for has been found and the type is one of:
+ * - ?fvelspcbdx
+ * at this point parsing is at the first character past the full path
+ * name on an exact match for the path being looked for - parse the
+ * remainder of the entries information into the ept structure.
+ * *********************************************************************
+ */
+
+ /* link/symbolic link must have link destination */
+
+ if (((ept->ftype == 's') || (ept->ftype == 'l')) &&
+ (ept->ainfo.local == NULL)) {
+ /* copy path found to 'lpath' */
+ COPYPATH(lpath, cpath_start, cpath_len);
+
+ setErrstr(ERR_NO_LINK_SOURCE_SPECIFIED);
+ findend(&vfpGetCurrCharPtr(cfVfp));
+ return (-1);
+ }
+
+ /* character/block devices have major/minor device numbers */
+
+ if (((ept->ftype == 'c') || (ept->ftype == 'b'))) {
+ ept->ainfo.major = BADMAJOR;
+ ept->ainfo.minor = BADMINOR;
+ if (getnumvfp(&vfpGetCurrCharPtr(cfVfp), 10,
+ (long *)&ept->ainfo.major, BADMAJOR) ||
+ getnumvfp(&vfpGetCurrCharPtr(cfVfp), 10,
+ (long *)&ept->ainfo.minor, BADMINOR)) {
+ /* copy path found to 'lpath' */
+ COPYPATH(lpath, cpath_start, cpath_len);
+
+ setErrstr(pkg_gt(ERR_CANNOT_READ_MM_NUMS));
+ findend(&vfpGetCurrCharPtr(cfVfp));
+ return (-1);
+ }
+ }
+
+ /* most types have mode, owner, group identification components */
+
+ if ((ept->ftype == 'd') || (ept->ftype == 'x') || (ept->ftype == 'c') ||
+ (ept->ftype == 'b') || (ept->ftype == 'p') ||
+ (ept->ftype == 'f') || (ept->ftype == 'v') ||
+ (ept->ftype == 'e')) {
+ /* mode, owner, group should be here */
+ if (getnumvfp(&vfpGetCurrCharPtr(cfVfp), 8,
+ (long *)&ept->ainfo.mode, BADMODE) ||
+ getstr(&vfpGetCurrCharPtr(cfVfp), sizeof (ept->ainfo.owner),
+ ept->ainfo.owner, ISWORDSEP) ||
+ getstr(&vfpGetCurrCharPtr(cfVfp), sizeof (ept->ainfo.group),
+ ept->ainfo.group, ISWORDSEP)) {
+ /* copy path found to 'lpath' */
+ COPYPATH(lpath, cpath_start, cpath_len);
+
+ setErrstr(ERR_CANNOT_READ_MOG);
+ findend(&vfpGetCurrCharPtr(cfVfp));
+ return (-1);
+ }
+ }
+
+ /* i/f/v/e have size, checksum, modification time components */
+
+ if ((ept->ftype == 'i') || (ept->ftype == 'f') ||
+ (ept->ftype == 'v') || (ept->ftype == 'e')) {
+ /* look for content description */
+ if (getlnumvfp(&vfpGetCurrCharPtr(cfVfp), 10,
+ (fsblkcnt_t *)&ept->cinfo.size, BADCONT) ||
+ getnumvfp(&vfpGetCurrCharPtr(cfVfp), 10,
+ (long *)&ept->cinfo.cksum, BADCONT) ||
+ getnumvfp(&vfpGetCurrCharPtr(cfVfp), 10,
+ (long *)&ept->cinfo.modtime, BADCONT)) {
+ /* copy path found to 'lpath' */
+ COPYPATH(lpath, cpath_start, cpath_len);
+
+ setErrstr(ERR_CANNOT_READ_CONTENT_INFO);
+ findend(&vfpGetCurrCharPtr(cfVfp));
+ return (-1);
+ }
+ }
+
+ /* i files processing is completed - return 'exact match found' */
+
+ if (ept->ftype == 'i') {
+ /* copy path found to 'lpath' */
+ COPYPATH(lpath, cpath_start, cpath_len);
+
+ if (getend(&vfpGetCurrCharPtr(cfVfp))) {
+ /* copy path found to 'lpath' */
+ COPYPATH(lpath, cpath_start, cpath_len);
+
+ setErrstr(ERR_EXTRA_TOKENS);
+ return (-1);
+ }
+
+ /* write out any skipped data before returning */
+ if (dataSkipped && (cfTmpVfp != (VFP_T *)NULL)) {
+ WRITEDATA(cfTmpVfp, firstPos, lastPos);
+ }
+
+ return (1);
+ }
+
+ /*
+ * determine list of packages which reference this entry
+ */
+
+ lastpinfo = (struct pinfo *)NULL;
+ while ((c = getstr(&vfpGetCurrCharPtr(cfVfp), sizeof (pkgname),
+ pkgname, ISPKGNAMESEP)) <= 0) {
+ /* if c < 0 the string was too long to fix in the buffer */
+
+ if (c < 0) {
+ /* copy path found to 'lpath' */
+ COPYPATH(lpath, cpath_start, cpath_len);
+
+ setErrstr(ERR_PACKAGE_NAME_TOO_LONG);
+ findend(&vfpGetCurrCharPtr(cfVfp));
+ return (-1);
+ }
+
+ /* a package is present - create and populate pinfo structure */
+
+ pinfo = (struct pinfo *)calloc(1, sizeof (struct pinfo));
+ if (!pinfo) {
+ /* copy path found to 'lpath' */
+ COPYPATH(lpath, cpath_start, cpath_len);
+
+ setErrstr(ERR_NO_MEMORY);
+ findend(&vfpGetCurrCharPtr(cfVfp));
+ return (-1);
+ }
+ if (!lastpinfo) {
+ ept->pinfo = pinfo; /* first one */
+ } else {
+ lastpinfo->next = pinfo; /* link list */
+ }
+ lastpinfo = pinfo;
+
+ if ((pkgname[0] == '-') || (pkgname[0] == '+') ||
+ (pkgname[0] == '*') || (pkgname[0] == '~') ||
+ (pkgname[0] == '!') || (pkgname[0] == '%')) {
+ pinfo->status = pkgname[0];
+ (void) strlcpy(pinfo->pkg, pkgname+1,
+ sizeof (pinfo->pkg));
+ } else {
+ (void) strlcpy(pinfo->pkg, pkgname,
+ sizeof (pinfo->pkg));
+ }
+
+ /* pkg/[:[ftype][:class] */
+ c = (vfpGetc(cfVfp));
+ if (c == '\\') {
+ /* get alternate ftype */
+ pinfo->editflag++;
+ c = (vfpGetc(cfVfp));
+ }
+
+ if (c == ':') {
+ /* get special classname */
+ (void) getstr(&vfpGetCurrCharPtr(cfVfp),
+ sizeof (classname), classname, ISWORDSEP);
+ (void) strlcpy(pinfo->aclass, classname,
+ sizeof (pinfo->aclass));
+ c = (vfpGetc(cfVfp));
+ }
+ ept->npkgs++;
+
+ /* break out of while if at end of entry */
+
+ if ((c == '\n') || (c == '\0')) {
+ break;
+ }
+
+ /* if package not separated by a space return an error */
+
+ if (!isspace(c)) {
+ /* copy path found to 'lpath' */
+ COPYPATH(lpath, cpath_start, cpath_len);
+
+ setErrstr(ERR_BAD_ENTRY_END);
+ findend(&vfpGetCurrCharPtr(cfVfp));
+ return (-1);
+ }
+ }
+
+ /*
+ * parsing of the entry is complete
+ */
+
+ /* copy path found to 'lpath' */
+ COPYPATH(lpath, cpath_start, cpath_len);
+
+ /* write out any skipped data before returning */
+ if (dataSkipped && (cfTmpVfp != (VFP_T *)NULL)) {
+ WRITEDATA(cfTmpVfp, firstPos, lastPos);
+ }
+
+ /* if not at the end of the entry, make it so */
+
+ if ((c != '\n') && (c != '\0')) {
+ if (getend(&vfpGetCurrCharPtr(cfVfp)) && ept->pinfo) {
+ setErrstr(ERR_EXTRA_TOKENS);
+ return (-1);
+ }
+ }
+
+ return (1);
+}
+
+static int
+getstr(char **cp, int n, char *str, int separator[])
+{
+ int c;
+ char *p = *cp;
+ char *p1;
+ size_t len;
+
+ if (*p == '\0') {
+ return (1);
+ }
+
+ /* leading white space ignored */
+
+ while (((c = *p) != '\0') && (isspace(*p++)))
+ ;
+ if ((c == '\0') || (c == '\n')) {
+ p--;
+ *cp = p;
+ return (1); /* nothing there */
+ }
+
+ p--;
+
+ /* compute length based on delimiter found or not */
+
+ p1 = p;
+ while (separator[(int)*p1] == 0) {
+ p1++;
+ }
+
+ len = (ptrdiff_t)p1 - (ptrdiff_t)p;
+
+ /* if string will fit in result buffer copy string and return success */
+
+ if (len < n) {
+ (void) memcpy(str, p, len);
+ str[len] = '\0';
+ p += len;
+ *cp = p;
+ return (0);
+ }
+
+ /* result buffer too small; copy partial string, return error */
+ (void) memcpy(str, p, n-1);
+ str[n-1] = '\0';
+ p += n;
+ *cp = p;
+ return (-1);
+}
+
+static int
+getend(char **cp)
+{
+ int n;
+ char *p = *cp;
+
+ n = 0;
+
+ /* if at end of buffer return no more characters left */
+
+ if (*p == '\0') {
+ return (0);
+ }
+
+ while ((*p != '\0') && (*p != '\n')) {
+ if (n == 0) {
+ if (!isspace(*p)) {
+ n++;
+ }
+ }
+ p++;
+ }
+
+ *cp = ++p;
+ return (n);
+}
+
+static void
+findend(char **cp)
+{
+ char *p1;
+ char *p = *cp;
+
+ /* if at end of buffer return no more characters left */
+
+ if (*p == '\0') {
+ return;
+ }
+
+ /* find the end of the line */
+
+ p1 = strchr(p, '\n');
+
+ if (p1 != (char *)NULL) {
+ *cp = ++p1;
+ return;
+ }
+
+ *cp = strchr(p, '\0');
+}
diff --git a/usr/src/lib/libpkg/common/tputcfent.c b/usr/src/lib/libpkg/common/tputcfent.c
new file mode 100644
index 0000000000..492f0335af
--- /dev/null
+++ b/usr/src/lib/libpkg/common/tputcfent.c
@@ -0,0 +1,191 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
+/* All Rights Reserved */
+
+
+
+#include <stdio.h>
+#include <string.h>
+#include <limits.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <time.h>
+#include <sys/types.h>
+#include "pkgstrct.h"
+#include "pkglocale.h"
+
+#define MSG_INVALID "invalid entry"
+
+void
+tputcfent(struct cfent *ept, FILE *fp)
+{
+ int count, status;
+ char *pt;
+ struct pinfo *pinfo;
+ struct tm *timep;
+ char timeb[BUFSIZ];
+
+ if (ept->path == NULL)
+ return;
+
+ (void) fprintf(fp, pkg_gt("Pathname: %s\n"), ept->path);
+ (void) fprintf(fp, pkg_gt("Type: "));
+
+ switch (ept->ftype) {
+ case 'f':
+ (void) fputs(pkg_gt("regular file\n"), fp);
+ break;
+
+ case 'd':
+ (void) fputs(pkg_gt("directory\n"), fp);
+ break;
+
+ case 'x':
+ (void) fputs(pkg_gt("exclusive directory\n"), fp);
+ break;
+
+ case 'v':
+ (void) fputs(pkg_gt("volatile file\n"), fp);
+ break;
+
+ case 'e':
+ (void) fputs(pkg_gt("editted file\n"), fp);
+ break;
+
+ case 'p':
+ (void) fputs(pkg_gt("named pipe\n"), fp);
+ break;
+
+ case 'i':
+ (void) fputs(pkg_gt("installation file\n"), fp);
+ break;
+
+ case 'c':
+ case 'b':
+ (void) fprintf(fp, pkg_gt("%s special device\n"),
+ (ept->ftype == 'b') ? pkg_gt("block") :
+ pkg_gt("character"));
+
+ if (ept->ainfo.major == BADMAJOR)
+ (void) fprintf(fp, pkg_gt("Major device number: %s\n"),
+ MSG_INVALID);
+ else
+ (void) fprintf(fp, pkg_gt("Major device number: %d\n"),
+ ept->ainfo.major);
+
+ if (ept->ainfo.minor == BADMINOR)
+ (void) fprintf(fp, pkg_gt("Minor device number: %s\n"),
+ MSG_INVALID);
+ else
+ (void) fprintf(fp, pkg_gt("Minor device number: %d\n"),
+ ept->ainfo.minor);
+
+ break;
+
+ case 'l':
+ (void) fputs(pkg_gt("linked file\n"), fp);
+ pt = (ept->ainfo.local ? ept->ainfo.local :
+ (char *)pkg_gt("(unknown)"));
+ (void) fprintf(fp, pkg_gt("Source of link: %s\n"), pt);
+ break;
+
+ case 's':
+ (void) fputs(pkg_gt("symbolic link\n"), fp);
+ pt = (ept->ainfo.local ? ept->ainfo.local :
+ (char *)pkg_gt("(unknown)"));
+ (void) fprintf(fp, pkg_gt("Source of link: %s\n"), pt);
+ break;
+
+ default:
+ (void) fputs(pkg_gt("unknown\n"), fp);
+ break;
+ }
+
+ if (!strchr("lsin", ept->ftype)) {
+ if (ept->ainfo.mode == BADMODE)
+ (void) fprintf(fp, pkg_gt("Expected mode: %s\n"),
+ "?");
+ else
+ (void) fprintf(fp, pkg_gt("Expected mode: %04o\n"),
+ ept->ainfo.mode);
+
+ (void) fprintf(fp, pkg_gt("Expected owner: %s\n"),
+ ept->ainfo.owner);
+ (void) fprintf(fp, pkg_gt("Expected group: %s\n"),
+ ept->ainfo.group);
+ }
+ if (strchr("?infv", ept->ftype)) {
+ (void) fprintf(fp,
+ pkg_gt("Expected file size (bytes): %llu\n"),
+ ept->cinfo.size);
+ (void) fprintf(fp,
+ pkg_gt("Expected sum(1) of contents: %ld\n"),
+ ept->cinfo.cksum);
+ if (ept->cinfo.modtime > 0) {
+ timep = localtime(&(ept->cinfo.modtime));
+ strftime(timeb, sizeof (timeb),
+ pkg_gt("Expected last modification: %b %d %X %Y\n"),
+ timep);
+ (void) fprintf(fp, timeb);
+ } else
+ (void) fprintf(fp,
+ pkg_gt("Expected last modification: ?\n"));
+ }
+ if (ept->ftype == 'i') {
+ (void) fputc('\n', fp);
+ return;
+ }
+
+ status = count = 0;
+ if ((pinfo = ept->pinfo) != NULL) {
+ (void) fprintf(fp,
+ pkg_gt("Referenced by the following packages:\n\t"));
+ while (pinfo) {
+ /*
+ * Check for partially installed object. Need
+ * to explicitly check for '!', because objects
+ * that are provided by a server will have a
+ * different status character.
+ */
+ if (pinfo->status == '!')
+ status++;
+ (void) fprintf(fp, "%-15s", pinfo->pkg);
+ if ((++count % 5) == 0) {
+ (void) fputc('\n', fp);
+ (void) fputc('\t', fp);
+ count = 0;
+ }
+ pinfo = pinfo->next;
+ }
+ (void) fputc('\n', fp);
+ }
+ (void) fprintf(fp, pkg_gt("Current status: %s\n"),
+ status ? pkg_gt("partially installed") :
+ pkg_gt("installed"));
+ (void) fputc('\n', fp);
+}
diff --git a/usr/src/lib/libpkg/common/verify.c b/usr/src/lib/libpkg/common/verify.c
new file mode 100644
index 0000000000..c48c5b8c77
--- /dev/null
+++ b/usr/src/lib/libpkg/common/verify.c
@@ -0,0 +1,989 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
+/* All Rights Reserved */
+
+
+
+#include <stdio.h>
+#include <limits.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <utime.h>
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <sys/statvfs.h>
+#include <grp.h>
+#include <pwd.h>
+#include <errno.h>
+#include <string.h>
+#include <stdarg.h>
+#include <fcntl.h>
+#include <sys/mkdev.h>
+#include "pkgstrct.h"
+#include "pkglib.h"
+#include "pkglibmsgs.h"
+#include "pkglocale.h"
+
+#define WDMSK 0xFFFF
+#define DATEFMT "%D %r"
+#define LONG_BOUNDARY ((sizeof (unsigned long))-1)
+#define CHUNK 1024*1024
+
+static char theErrBuf[PATH_MAX+512] = {'\0'};
+static char *theErrStr = NULL;
+
+/* checksum disable switch */
+static int enable_checksum = 1;
+
+/* attribute disable flag */
+static int disable_attributes = 0;
+
+/* non-ABI symlinks supported */
+static int nonabi_symlinks;
+
+/*
+ * forward declarations
+ */
+
+static int clear_target(char *path, char *ftype, int is_a_dir);
+
+unsigned long compute_checksum(int *r_err, char *path);
+
+/* union used to generate checksum */
+typedef union hilo {
+ struct part {
+ uint16_t hi;
+ uint16_t lo;
+ } hl;
+ uint32_t lg;
+} CHECKSUM_T;
+
+/*PRINTFLIKE1*/
+static void
+reperr(char *fmt, ...)
+{
+ char *pt;
+ ssize_t ptln;
+ va_list ap;
+ int n;
+
+ if (fmt == (char *)NULL) {
+ theErrBuf[0] = '\0';
+ } else {
+ if (n = strlen(theErrBuf)) {
+ pt = theErrBuf + n;
+ *pt++ = '\n';
+ *pt = '\0';
+ ptln = sizeof (theErrBuf)-n;
+ } else {
+ pt = theErrBuf;
+ ptln = sizeof (theErrBuf);
+ }
+ va_start(ap, fmt);
+ /* LINTED variable format specifier to vsnprintf() */
+ (void) vsnprintf(pt, ptln, fmt, ap);
+ va_end(ap);
+ }
+}
+
+/*
+ * Name: cverify
+ * Description: This function verifies and (if fix > 0) fixes the contents
+ * of the file at the path provided
+ * Arguments: fix - 0 - do not fix entries, 1 - fix entries
+ * ftype - single character "type" the entry is supposed to be
+ * path - path to file
+ * cinfo - content info structure representing the contents
+ * the entry is supposed to contain
+ * allow_checksum - determine if checksumming should be disabled:
+ * == 0 - do not perform checksum ever - override enable_checksum.
+ * != 0 - use the default checksum flag "enable_checksum" to
+ * determine if checksumming should be done.
+ * NOTE: modification and creation times can be repaired; the contents
+ * of the file cannot be corrected if the checksum indicates that
+ * the contents are not correct - VE_CONT will be returned in this
+ * case.
+ * Possible return values:
+ * - 0 = successful
+ * - VE_EXIST = path name does not exist
+ * - VE_FTYPE = path file type is not recognized, is not supported,
+ * or is not what was expected
+ * - VE_ATTR = path mode/group/user is not what was expected
+ * - VE_CONT = mod time/link target/major/minor/size/file system type/current
+ * directory is not what was expected
+ * - VE_FAIL = utime/target directory/link/stat/symlink/mknod/chmod/statvfs/
+ * chown failed
+ */
+
+int
+cverify(int fix, char *ftype, char *path, struct cinfo *cinfo,
+ int allow_checksum)
+{
+ struct stat status; /* file status buffer */
+ struct utimbuf times;
+ unsigned long mycksum;
+ int setval, retcode;
+ char tbuf1[512];
+ char tbuf2[512];
+ int cksumerr;
+
+ setval = (*ftype == '?');
+ retcode = 0;
+ reperr(NULL);
+
+ if (stat(path, &status) < 0) {
+ reperr(pkg_gt(ERR_EXIST));
+ return (VE_EXIST);
+ }
+
+ /* -1 requires modtimes to be the same */
+ /* 0 reports modtime failure */
+ /* 1 fixes modtimes */
+
+ if (setval || (cinfo->modtime == BADCONT)) {
+ cinfo->modtime = status.st_mtime;
+ } else if (status.st_mtime != cinfo->modtime) {
+ if (fix > 0) {
+ /* reset times on the file */
+ times.actime = cinfo->modtime;
+ times.modtime = cinfo->modtime;
+ if (utime(path, &times)) {
+ reperr(pkg_gt(ERR_MODFAIL));
+ retcode = VE_FAIL;
+ }
+ } else if (fix < 0) {
+ /* modtimes must be the same */
+ if (strftime(tbuf1, sizeof (tbuf1), DATEFMT,
+ localtime(&cinfo->modtime)) == 0) {
+ reperr(pkg_gt(ERR_MEM));
+ }
+ if (strftime(tbuf2, sizeof (tbuf2), DATEFMT,
+ localtime(&status.st_mtime)) == 0) {
+ reperr(pkg_gt(ERR_MEM));
+ }
+ reperr(pkg_gt(ERR_MTIME), tbuf1, tbuf2);
+ retcode = VE_CONT;
+ }
+ }
+
+ if (setval || (cinfo->size == (fsblkcnt_t)BADCONT)) {
+ cinfo->size = status.st_size;
+ } else if (status.st_size != cinfo->size) {
+ if (!retcode) {
+ retcode = VE_CONT;
+ }
+ reperr(pkg_gt(ERR_SIZE), cinfo->size, status.st_size);
+ }
+
+ cksumerr = 0;
+
+ /*
+ * see if checksumming should be done: if checksumming is allowed,
+ * and checksumming is enabled, then checksum the file.
+ */
+
+ /* return if no need to compute checksum */
+
+ if ((allow_checksum == 0) || (enable_checksum == 0)) {
+ return (retcode);
+ }
+
+ /* compute checksum */
+
+ mycksum = compute_checksum(&cksumerr, path);
+
+ /* set value if not set or if checksum cannot be computed */
+
+ if (setval || (cinfo->cksum == BADCONT)) {
+ cinfo->cksum = mycksum;
+ return (retcode);
+ }
+
+ /* report / return error if checksums mismatch or there is an error */
+
+ if ((mycksum != cinfo->cksum) || cksumerr) {
+ if (!retcode) {
+ retcode = VE_CONT;
+ }
+ if (!cksumerr) {
+ reperr(pkg_gt(ERR_CKSUM), cinfo->cksum, mycksum);
+ }
+ }
+
+ return (retcode);
+}
+
+/*
+ * Name: compute_checksum
+ * Description: generate checksum for specified file
+ * Arguments: r_cksumerr (int *) [RO, *RW]
+ * - pointer to integer that is set on return to:
+ * == 0 - no error occurred
+ * != 0 - error occurred
+ * a_path (char *) [RO, *RO]
+ * - pointer to string representing path to file to
+ * generate checksum of
+ * Returns: unsigned long - results:
+ * - If *r_cksumerr == 0, checksum of specified file
+ * - If *r_cksumerr != 0, undefined
+ */
+unsigned long
+compute_checksum(int *r_cksumerr, char *a_path)
+{
+ CHECKSUM_T suma; /* to split four-bytes into 2 two-byte values */
+ CHECKSUM_T tempa;
+ int fd;
+ uint32_t lg; /* running checksum value */
+ uint32_t buf[CHUNK/4]; /* to read CHUNK bytes */
+ uint32_t lsavhi; /* high order two-bytes of four-byte checksum */
+ uint32_t lsavlo; /* low order two-bytes of four-byte checksum */
+ int leap = sizeof (uint32_t);
+ int notyet = 0;
+ int nread;
+ struct stat64 sbuf;
+
+ /* reset error flag */
+ *r_cksumerr = 0;
+
+ /* open file and obtain -> where file is mapped/read */
+ if ((fd = open(a_path, O_RDONLY)) < 0) {
+ *r_cksumerr = 1;
+ reperr(pkg_gt(ERR_NO_CKSUM));
+ perror(ERR_NO_CKSUM);
+ return (0);
+ }
+
+ if (fstat64(fd, &sbuf) != 0) {
+ *r_cksumerr = 1;
+ reperr(pkg_gt(ERR_NO_CKSUM));
+ perror(ERR_NO_CKSUM);
+ return (0);
+ }
+
+ /* initialize checksum value */
+ lg = 0;
+
+ /*
+ * Read CHUNK bytes off the file at a time; Read size of long bytes
+ * from memory at a time and process them.
+ * If last read, then read remnant bytes and process individually.
+ */
+ errno = 0;
+ while ((nread = read(fd, (void*)buf,
+ (sbuf.st_size < CHUNK) ? sbuf.st_size : CHUNK)) > 0) {
+ uchar_t *s;
+ uint32_t *p = buf;
+
+ notyet = nread % leap;
+ nread -= notyet;
+
+ for (; nread > 0; nread -= leap) {
+ lg += ((((*p)>>24)&0xFF) & WDMSK);
+ lg += ((((*p)>>16)&0xFF) & WDMSK);
+ lg += ((((*p)>>8)&0xFF) & WDMSK);
+ lg += (((*p)&0xFF) & WDMSK);
+ p++;
+ }
+ s = (uchar_t *)p;
+ /* leftover bytes less than four in number */
+ while (notyet--)
+ lg += (((uint32_t)(*s++)) & WDMSK);
+ }
+
+ /* wind up */
+ (void) close(fd);
+
+ /* compute checksum components */
+ suma.lg = lg;
+ tempa.lg = (suma.hl.lo & WDMSK) + (suma.hl.hi & WDMSK);
+ lsavhi = (uint32_t)tempa.hl.hi;
+ lsavlo = (uint32_t)tempa.hl.lo;
+
+ /* return final checksum value */
+ return (lsavhi+lsavlo);
+}
+
+static struct stat status; /* file status buffer */
+static struct statvfs vfsstatus; /* filesystem status buffer */
+
+/*
+ * Remove the thing that's currently in place so we can put down the package
+ * object. If we're replacing a directory with a directory, leave it alone.
+ * Returns 1 if all OK and 0 if failed.
+ */
+static int
+clear_target(char *path, char *ftype, int is_a_dir)
+{
+ int retcode = 1;
+
+ if (is_a_dir) { /* if there's a directory there already ... */
+ /* ... and this isn't, ... */
+ if ((*ftype != 'd') && (*ftype != 'x')) {
+ if (rmdir(path)) { /* try to remove it. */
+ reperr(pkg_gt(ERR_RMDIR), path);
+ retcode = 0;
+ }
+ }
+ } else {
+ if (remove(path)) {
+ if (errno != ENOENT) {
+ retcode = 0; /* It didn't work. */
+ }
+ }
+ }
+
+ return (retcode);
+}
+
+/*
+ * Name: averify
+ * Description: This function verifies and (if fix > 0) fixes the attributes
+ * of the file at the path provided.
+ * Arguments: fix - 0 - do not fix entries, 1 - fix entries
+ * ftype - single character "type" the entry is supposed to be
+ * path - path to file
+ * ainfo - attribute info structure representing the attributes
+ * the entry is supposed to be
+ * NOTE: attributes are links and permissions
+ * Possible return values:
+ * - 0 = successful
+ * - VE_EXIST = path name does not exist
+ * - VE_FTYPE = path file type is not recognized, is not supported,
+ * or is not what was expected
+ * - VE_ATTR = path mode/group/user is not what was expected
+ * - VE_CONT = mod time/link target/major/minor/size/file system type/current
+ * directory is not what was expected
+ * - VE_FAIL = utime/target directory/link/stat/symlink/mknod/chmod/statvfs/
+ * chown failed
+ */
+int
+averify(int fix, char *ftype, char *path, struct ainfo *ainfo)
+{
+ struct group *grp; /* group entry buffer */
+ struct passwd *pwd;
+ int n;
+ int setval;
+ int uid, gid;
+ int dochown;
+ int retcode;
+ int statError = 0;
+ int targ_is_dir = 0; /* replacing a directory */
+ char myftype;
+ char buf[PATH_MAX];
+ ino_t my_ino;
+ dev_t my_dev;
+ char cwd[MAXPATHLEN];
+ char *cd;
+ char *c;
+
+ setval = (*ftype == '?');
+ retcode = 0;
+ reperr(NULL);
+
+ if (get_disable_attribute_check()) {
+ return (0);
+ }
+
+ if (*ftype == 'l') {
+ if (stat(path, &status) < 0) {
+ retcode = VE_EXIST;
+ reperr(pkg_gt(ERR_EXIST));
+ }
+
+ my_ino = status.st_ino;
+ my_dev = status.st_dev;
+
+ /* Get copy of the current working directory */
+ if (getcwd(cwd, MAXPATHLEN) == NULL) {
+ reperr(pkg_gt(ERR_GETWD), ainfo->local);
+ return (VE_FAIL);
+ }
+
+ /*
+ * Change to the directory in which the hard
+ * link is to be created.
+ */
+ cd = strdup(path);
+ c = strrchr(cd, '/');
+ if (c) {
+ /* bugid 4247895 */
+ if (strcmp(cd, c) == 0)
+ strcpy(cd, "/");
+ else
+ *c = NULL;
+
+ if (chdir(cd) != 0) {
+ reperr(pkg_gt(ERR_CHDIR), cd);
+ return (VE_FAIL);
+ }
+ }
+ free(cd);
+
+ if (retcode || (status.st_nlink < 2) ||
+ (stat(ainfo->local, &status) < 0) ||
+ (my_dev != status.st_dev) || (my_ino != status.st_ino)) {
+ if (fix) {
+ /*
+ * Don't want to do a hard link to a
+ * directory.
+ */
+ if (!isdir(ainfo->local)) {
+ chdir(cwd);
+ reperr(pkg_gt(ERR_LINKISDIR),
+ ainfo->local);
+ return (VE_FAIL);
+ }
+ /* Now do the link. */
+ if (!clear_target(path, ftype, targ_is_dir))
+ return (VE_FAIL);
+
+ if (link(ainfo->local, path)) {
+ chdir(cwd);
+ reperr(pkg_gt(ERR_LINKFAIL),
+ ainfo->local);
+ return (VE_FAIL);
+ }
+ retcode = 0;
+ } else {
+ /* Go back to previous working directory */
+ if (chdir(cwd) != 0)
+ reperr(pkg_gt(ERR_CHDIR), cwd);
+
+ reperr(pkg_gt(ERR_LINK), ainfo->local);
+ return (VE_CONT);
+ }
+ }
+
+ /* Go back to previous working directory */
+ if (chdir(cwd) != 0) {
+ reperr(pkg_gt(ERR_CHDIR), cwd);
+ return (VE_CONT);
+ }
+
+ return (retcode);
+ }
+
+ retcode = 0;
+
+ /* If we are to process symlinks the old way then we follow the link */
+ if (nonABI_symlinks()) {
+ if ((*ftype == 's') ? lstat(path, &status) :
+ stat(path, &status)) {
+ reperr(pkg_gt(ERR_EXIST));
+ retcode = VE_EXIST;
+ myftype = '?';
+ statError++;
+ }
+ /* If not then we inspect the target of the link */
+ } else {
+ if ((n = lstat(path, &status)) == -1) {
+ reperr(pkg_gt(ERR_EXIST));
+ retcode = VE_EXIST;
+ myftype = '?';
+ statError++;
+ }
+ }
+ if (!statError) {
+ /* determining actual type of existing object */
+ switch (status.st_mode & S_IFMT) {
+ case S_IFLNK:
+ myftype = 's';
+ break;
+
+ case S_IFIFO:
+ myftype = 'p';
+ break;
+
+ case S_IFCHR:
+ myftype = 'c';
+ break;
+
+ case S_IFDIR:
+ myftype = 'd';
+ targ_is_dir = 1;
+ break;
+
+ case S_IFBLK:
+ myftype = 'b';
+ break;
+
+ case S_IFREG:
+ case 0:
+ myftype = 'f';
+ break;
+
+ case S_IFDOOR:
+ myftype = 'D';
+ break;
+
+ default:
+ reperr(pkg_gt(ERR_UNKNOWN));
+ return (VE_FTYPE);
+ }
+ }
+
+ if (setval) {
+ /*
+ * Check to make sure that a package or an installf that uses
+ * wild cards '?' to assume the ftype of an object on the
+ * system is not assuming a door ftype. Doors are not supported
+ * but should be ignored.
+ */
+ if (myftype == 'D') {
+ reperr(pkg_gt(ERR_FTYPED), path);
+ retcode = VE_FTYPE;
+ return (VE_FTYPE);
+ } else {
+ *ftype = myftype;
+ }
+ } else if (!retcode && (*ftype != myftype) &&
+ ((myftype != 'f') || !strchr("ilev", *ftype)) &&
+ ((myftype != 'd') || (*ftype != 'x'))) {
+ reperr(pkg_gt(ERR_FTYPE), *ftype, myftype);
+ retcode = VE_FTYPE;
+ }
+
+ if (!retcode && (*ftype == 's')) {
+ /* make sure that symbolic link is correct */
+ n = readlink(path, buf, PATH_MAX);
+ if (n < 0) {
+ reperr(pkg_gt(ERR_SLINK), ainfo->local);
+ retcode = VE_CONT;
+ } else if (ainfo->local != NULL) {
+ buf[n] = '\0';
+ if (strcmp(buf, ainfo->local)) {
+ reperr(pkg_gt(ERR_SLINK), ainfo->local);
+ retcode = VE_CONT;
+ }
+ } else if (ainfo->local == NULL) {
+ /*
+ * Since a sym link target exists, insert it
+ * into the ainfo structure
+ */
+ buf[n] = '\0';
+ ainfo->local = strdup(buf);
+ }
+ }
+
+ if (retcode) {
+ /* The path doesn't exist or is different than it should be. */
+ if (fix) {
+ /*
+ * Clear the way for the write. If it won't clear,
+ * there's nothing we can do.
+ */
+ if (!clear_target(path, ftype, targ_is_dir))
+ return (VE_FAIL);
+
+ if ((*ftype == 'd') || (*ftype == 'x')) {
+ char *pt, *p;
+
+ /* Try to make it the easy way */
+ if (mkdir(path, ainfo->mode)) {
+ /*
+ * Failing that, walk through the
+ * parent directories creating
+ * whatever is needed.
+ */
+ p = strdup(path);
+ pt = (*p == '/') ? p+1 : p;
+ do {
+ if (pt = strchr(pt, '/'))
+ *pt = '\0';
+ if (access(p, 0) &&
+ mkdir(p, ainfo->mode))
+ break;
+ if (pt)
+ *pt++ = '/';
+ } while (pt);
+ free(p);
+ }
+ if (stat(path, &status) < 0) {
+ reperr(pkg_gt(ERR_DIRFAIL));
+ return (VE_FAIL);
+ }
+ } else if (*ftype == 's') {
+ if (symlink(ainfo->local, path)) {
+ reperr(pkg_gt(ERR_SLINKFAIL),
+ ainfo->local);
+ return (VE_FAIL);
+ }
+
+ } else if (*ftype == 'c') {
+ int wilddevno = 0;
+ /*
+ * The next three if's support 2.4 and older
+ * packages that use "?" as device numbers.
+ * This should be considered for removal by
+ * release 2.7 or so.
+ */
+ if (ainfo->major == BADMAJOR) {
+ ainfo->major = 0;
+ wilddevno = 1;
+ }
+
+ if (ainfo->minor == BADMINOR) {
+ ainfo->minor = 0;
+ wilddevno = 1;
+ }
+
+ if (wilddevno) {
+ wilddevno = 0;
+ logerr(MSG_WLDDEVNO, path,
+ ainfo->major, ainfo->minor);
+ }
+
+ if (mknod(path, ainfo->mode | S_IFCHR,
+#ifdef SUNOS41
+ makedev(ainfo->xmajor, ainfo->xminor)) ||
+#else
+ makedev(ainfo->major, ainfo->minor)) ||
+#endif
+ (stat(path, &status) < 0)) {
+ reperr(pkg_gt(ERR_CDEVFAIL));
+ return (VE_FAIL);
+ }
+ } else if (*ftype == 'b') {
+ int wilddevno = 0;
+ /*
+ * The next three if's support 2.4 and older
+ * packages that use "?" as device numbers.
+ * This should be considered for removal by
+ * release 2.7 or so.
+ */
+ if (ainfo->major == BADMAJOR) {
+ ainfo->major = 0;
+ wilddevno = 1;
+ }
+
+ if (ainfo->minor == BADMINOR) {
+ ainfo->minor = 0;
+ wilddevno = 1;
+ }
+
+ if (wilddevno) {
+ wilddevno = 0;
+ logerr(MSG_WLDDEVNO, path,
+ ainfo->major, ainfo->minor);
+ }
+
+ if (mknod(path, ainfo->mode | S_IFBLK,
+#ifdef SUNOS41
+ makedev(ainfo->xmajor, ainfo->xminor)) ||
+#else
+ makedev(ainfo->major, ainfo->minor)) ||
+#endif
+ (stat(path, &status) < 0)) {
+ reperr(pkg_gt(ERR_BDEVFAIL));
+ return (VE_FAIL);
+ }
+ } else if (*ftype == 'p') {
+ if (mknod(path, ainfo->mode | S_IFIFO, NULL) ||
+ (stat(path, &status) < 0)) {
+ reperr(pkg_gt(ERR_PIPEFAIL));
+ return (VE_FAIL);
+ }
+ } else
+ return (retcode);
+
+ } else
+ return (retcode);
+ }
+
+ if (*ftype == 's')
+ return (0); /* don't check anything else */
+ if (*ftype == 'i')
+ return (0); /* don't check anything else */
+
+ retcode = 0;
+ if ((myftype == 'c') || (myftype == 'b')) {
+#ifdef SUNOS41
+ if (setval || (ainfo->xmajor < 0))
+ ainfo->xmajor = ((status.st_rdev>>8)&0377);
+ if (setval || (ainfo->xminor < 0))
+ ainfo->xminor = (status.st_rdev&0377);
+ /* check major & minor */
+ if (status.st_rdev != makedev(ainfo->xmajor, ainfo->xminor)) {
+ reperr(pkg_gt(ERR_MAJMIN), ainfo->xmajor,
+ ainfo->xminor,
+ (status.st_rdev>>8)&0377, status.st_rdev&0377);
+ retcode = VE_CONT;
+ }
+#else
+ if (setval || (ainfo->major == BADMAJOR))
+ ainfo->major = major(status.st_rdev);
+ if (setval || (ainfo->minor == BADMINOR))
+ ainfo->minor = minor(status.st_rdev);
+ /* check major & minor */
+ if (status.st_rdev != makedev(ainfo->major, ainfo->minor)) {
+ reperr(pkg_gt(ERR_MAJMIN), ainfo->major, ainfo->minor,
+ major(status.st_rdev), minor(status.st_rdev));
+ retcode = VE_CONT;
+ }
+#endif
+ }
+
+ /* compare specified mode w/ actual mode excluding sticky bit */
+ if (setval || (ainfo->mode == BADMODE) || (ainfo->mode == WILDCARD))
+ ainfo->mode = status.st_mode & 07777;
+ else if ((ainfo->mode & 06777) != (status.st_mode & 06777)) {
+ if (fix) {
+ if ((ainfo->mode == BADMODE) ||
+ (chmod(path, ainfo->mode) < 0))
+ retcode = VE_FAIL;
+ } else {
+ reperr(pkg_gt(ERR_PERM), ainfo->mode,
+ status.st_mode & 07777);
+ if (!retcode)
+ retcode = VE_ATTR;
+ }
+ }
+
+ dochown = 0;
+
+ /* get group entry for specified group */
+ if (setval || strcmp(ainfo->group, BADGROUP) == 0) {
+ grp = cgrgid(status.st_gid);
+ if (grp)
+ (void) strcpy(ainfo->group, grp->gr_name);
+ else {
+ if (!retcode)
+ retcode = VE_ATTR;
+ reperr(pkg_gt(ERR_BADGRPID), status.st_gid);
+ }
+ gid = status.st_gid;
+ } else if ((grp = cgrnam(ainfo->group)) == NULL) {
+ reperr(pkg_gt(ERR_BADGRPNM), ainfo->group);
+ if (!retcode)
+ retcode = VE_ATTR;
+ } else if ((gid = grp->gr_gid) != status.st_gid) {
+ if (fix) {
+ /* save specified GID */
+ gid = grp->gr_gid;
+ dochown++;
+ } else {
+ if ((grp = cgrgid((int)status.st_gid)) ==
+ (struct group *)NULL) {
+ reperr(pkg_gt(ERR_GROUP), ainfo->group,
+ "(null)");
+ } else {
+ reperr(pkg_gt(ERR_GROUP), ainfo->group,
+ grp->gr_name);
+ }
+ if (!retcode)
+ retcode = VE_ATTR;
+ }
+ }
+
+ /* get password entry for specified owner */
+ if (setval || strcmp(ainfo->owner, BADOWNER) == 0) {
+ pwd = cpwuid((int)status.st_uid);
+ if (pwd)
+ (void) strcpy(ainfo->owner, pwd->pw_name);
+ else {
+ if (!retcode)
+ retcode = VE_ATTR;
+ reperr(pkg_gt(ERR_BADUSRID), status.st_uid);
+ }
+ uid = status.st_uid;
+ } else if ((pwd = cpwnam(ainfo->owner)) == NULL) {
+ /* UID does not exist in password file */
+ reperr(pkg_gt(ERR_BADUSRNM), ainfo->owner);
+ if (!retcode)
+ retcode = VE_ATTR;
+ } else if ((uid = pwd->pw_uid) != status.st_uid) {
+ /* get owner name for actual UID */
+ if (fix) {
+ uid = pwd->pw_uid;
+ dochown++;
+ } else {
+ pwd = cpwuid((int)status.st_uid);
+ if (pwd == NULL)
+ reperr(pkg_gt(ERR_BADUSRID),
+ (int)status.st_uid);
+ else
+ reperr(pkg_gt(ERR_OWNER), ainfo->owner,
+ pwd->pw_name);
+
+ if (!retcode)
+ retcode = VE_ATTR;
+ }
+ }
+
+ if (statvfs(path, &vfsstatus) < 0) {
+ reperr(pkg_gt(ERR_EXIST));
+ retcode = VE_FAIL;
+ } else {
+ if (dochown) {
+ /* pcfs doesn't support file ownership */
+ if (strcmp(vfsstatus.f_basetype, "pcfs") != 0 &&
+ chown(path, uid, gid) < 0) {
+ retcode = VE_FAIL; /* chown failed */
+ }
+ }
+ }
+
+ if (retcode == VE_FAIL)
+ reperr(pkg_gt(ERR_ATTRFAIL));
+ return (retcode);
+}
+
+/*
+ * This is a special fast verify which basically checks the attributes
+ * and then, if all is OK, checks the size and mod time using the same
+ * stat and statvfs structures.
+ */
+int
+fverify(int fix, char *ftype, char *path, struct ainfo *ainfo,
+ struct cinfo *cinfo)
+{
+ int retval;
+
+ /* return success if attribute checks are disabled */
+
+ if (get_disable_attribute_check()) {
+ return (0);
+ }
+
+ if ((retval = averify(fix, ftype, path, ainfo)) == 0) {
+ if (*ftype == 'f' || *ftype == 'i') {
+ if (cinfo->size != status.st_size) {
+ reperr(pkg_gt(WRN_QV_SIZE), path);
+ retval = VE_CONT;
+ }
+ /* pcfs doesn't support modification times */
+ if (strcmp(vfsstatus.f_basetype, "pcfs") != 0) {
+ if (cinfo->modtime != status.st_mtime) {
+ reperr(pkg_gt(WRN_QV_MTIME), path);
+ retval = VE_CONT;
+ }
+ }
+ }
+ }
+
+ return (retval);
+}
+
+/*
+ * This function determines whether or not non-ABI symlinks are supported.
+ */
+
+int
+nonABI_symlinks(void)
+{
+ return (nonabi_symlinks);
+}
+
+void
+set_nonABI_symlinks(void)
+{
+ nonabi_symlinks = 1;
+}
+
+/*
+ * Disable attribute checking. Only disable attribute checking if files
+ * are guaranteed to exist in the FS.
+ */
+void
+disable_attribute_check(void)
+{
+ disable_attributes = 1;
+}
+
+/*
+ * This function determines whether or not to do attribute checking.
+ * Returns: 0 - Do attribute checking
+ * !0 - Don't do attribute checking
+ */
+int
+get_disable_attribute_check(void)
+{
+ return (disable_attributes);
+}
+
+/*
+ * This function returns the address of the "global" error buffer that
+ * is populated by the various functions in this module.
+ */
+
+char *
+getErrbufAddr(void)
+{
+ return (theErrBuf);
+}
+
+/*
+ * This function returns the size of the buffer returned by getErrbufAddr()
+ */
+
+int
+getErrbufSize(void)
+{
+ return (sizeof (theErrBuf));
+}
+
+/*
+ * This function returns the current global "error string"
+ */
+
+char *
+getErrstr(void)
+{
+ return (theErrStr);
+}
+
+/*
+ * This function sets the global "error string"
+ */
+
+void
+setErrstr(char *a_errstr)
+{
+ theErrStr = a_errstr;
+}
+
+/*
+ * This function enables checksumming
+ */
+
+void
+checksum_on(void)
+{
+ enable_checksum = 1;
+}
+
+/*
+ * This function disables checksumming
+ */
+
+void
+checksum_off(void)
+{
+ enable_checksum = 0;
+}
diff --git a/usr/src/lib/libpkg/common/vfpops.c b/usr/src/lib/libpkg/common/vfpops.c
new file mode 100644
index 0000000000..a4e6e54112
--- /dev/null
+++ b/usr/src/lib/libpkg/common/vfpops.c
@@ -0,0 +1,1283 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+
+
+/*
+ * Module: vfpops.c
+ * Synopsis: Implements virtual file protocol operations
+ * Description:
+ *
+ * This module implements the "Virtual File protocol" operations. These
+ * operations are intended to provide very fast access to file data,
+ * allowing a file to be accessed in very efficient ways with extremely
+ * low-cpu intensive operations. If possible file data is mapped directly
+ * into memory allowing the data to be accessed directly. If the data
+ * cannot be mapped directly into memory, memory will be allocated and
+ * the file data read directly into memory. If that fails currently the
+ * file data is not accessible. Other methods of making the file could
+ * be implemented in the future (e.g. stdio if all else fails).
+ *
+ * In general any code that uses stdio to access a file can be changed
+ * to use the various "vfp" operations to access a file, with a resulting
+ * increase in performance and decrease in cpu time required to access
+ * the file contents.
+ *
+ * Public Methods:
+ *
+ * vfpCheckpointFile - Create new VFP that checkpoints existing VFP
+ * vfpCheckpointOpen - open file, allocate storage, return pointer to VFP_T
+ * vfpClose - close file associated with vfp
+ * vfpDecCurrPtr - decrement current character pointer
+ * vfpGetBytesRemaining - get number of bytes remaining to read
+ * vfpGetCurrCharPtr - get pointer to current character
+ * vfpGetCurrPtrDelta - get number of bytes between current and specified char
+ * vfpGetFirstCharPtr - get pointer to first character
+ * vfpGetLastCharPtr - get pointer to last character
+ * vfpGetModifiedLen - get highest modified byte (length) contained in vfp
+ * vfpGetPath - get the path associated with the vfp
+ * vfpGetc - get current character and increment to next
+ * vfpGetcNoInc - get current character - do not increment
+ * vfpGets - get a string from the vfp into a fixed size buffer
+ * vfpIncCurrPtr - increment current character pointer
+ * vfpIncCurrPtrBy - increment current pointer by specified delta
+ * vfpOpen - open file on vfp
+ * vfpPutBytes - put fixed number of bytes to current character and increment
+ * vfpPutFormat - put format one arg to current character and increment
+ * vfpPutInteger - put integer to current character and increment
+ * vfpPutLong - put long to current character and increment
+ * vfpPutc - put current character and increment to next
+ * vfpPuts - put string to current character and increment
+ * vfpRewind - rewind file to first byte
+ * vfpSeekToEnd - seek to end of file
+ * vfpSetCurrCharPtr - set pointer to current character
+ * vfpSetFlags - set flags that affect file access
+ * vfpSetSize - set size of file (for writing)
+ * vfpTruncate - truncate file
+ * vfpWriteToFile - write data contained in vfp to specified file
+ */
+
+#include <stdio.h>
+#include <limits.h>
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+#include <unistd.h>
+#include <ctype.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+#include <errno.h>
+#include <libintl.h>
+#include "pkglib.h"
+#include "pkgstrct.h"
+#include "pkglocale.h"
+
+/*
+ * These are internal flags that occupy the high order byte of the VFPFLAGS_T
+ * flags element of the vfp. These flags may only occupy the high order order
+ * 16 bits of the 32-bit unsigned vfp "flags" object.
+ */
+
+#define _VFP_MMAP 0x00010000 /* mmap used */
+#define _VFP_MALLOC 0x00020000 /* malloc used */
+#define _VFP_WRITE 0x00040000 /* file opened for write */
+#define _VFP_READ 0x00080000 /* file opened for reading */
+#define _VFP_MODIFIED 0x00100000 /* contents are marked modified */
+
+/* path name given to "anonymous" (string) vfp */
+
+#define VFP_ANONYMOUS_PATH "<<string>>"
+
+/* minimum size file to mmap (64mb) */
+
+#define MIN_MMAP_SIZE (64*1024)
+
+/*
+ * *****************************************************************************
+ * global external (public) functions
+ * *****************************************************************************
+ */
+
+/*
+ * Name: vfpOpen
+ * Description: Open file on vfp, allocate storage, return pointer to VFP_T
+ * that can be used to access/modify file contents.
+ * Arguments: VFP_T **r_vfp - pointer to pointer to VFP_T
+ * char *a_path - path of file to open and associate with this VFP.
+ * - if the path is (char *)NULL then no file is associated
+ * with this VFP - this is a way to create a fixed length
+ * string that can be manipulated with the VFP operators.
+ * Before the VFP can be used "vfpSetSize" must be called
+ * to set the size of the string buffer.
+ * char *a_mode - fopen mode to open the file with
+ * VFPFLAGS_T a_flags - one or more flags to control the operation:
+ * - VFP_NONE - no special flags
+ * - VFP_NEEDNOW - file data needed in memory now
+ * - VFP_SEQUENTIAL - memory will be sequentially accessed
+ * - VFP_RANDOM - memory will be randomly accessed
+ * - VFP_NOMMAP - do not use mmap to access file
+ * - VFP_NOMALLOC - do not use malloc to buffer file
+ * Returns: int == 0 - operation was successful
+ * != 0 - operation failed, errno contains reason
+ * Side Effects: r_vfp -- filled in with a pointer to a newly allocated vfp
+ * which can be used with the various vfp functions.
+ * errno -- contains system error number if return is != 0
+ */
+
+int
+vfpOpen(VFP_T **r_vfp, char *a_path, char *a_mode, VFPFLAGS_T a_flags)
+{
+ FILE *fp = (FILE *)NULL;
+ VFP_T *vfp;
+ int lerrno;
+ struct stat statbuf;
+ int pagesize = getpagesize();
+
+ /* reset return VFP/FILE pointers */
+
+ (*r_vfp) = (VFP_T *)NULL;
+
+ /* allocate pre-zeroed vfp object */
+
+ vfp = (VFP_T *)calloc(sizeof (VFP_T), 1);
+ if (vfp == (VFP_T *)NULL) {
+ return (-1);
+ }
+
+ /* create "string" vfp if no path specified */
+
+ if (a_path == (char *)NULL) {
+ /*
+ * no path specified - no open file associated with vfp
+ * The vfp is initialized to all zeros - initialize just those
+ * values that need to be non-zero.
+ */
+
+ vfp->_vfpFlags = _VFP_MALLOC;
+ vfp->_vfpPath = strdup(VFP_ANONYMOUS_PATH);
+ (*r_vfp) = vfp;
+ return (0);
+ }
+
+ /*
+ * path specified - associate open file with vfp;
+ * return an error if no path or mode specified
+ */
+
+ if (a_mode == (char *)NULL) {
+ errno = EFAULT; /* Bad address */
+ (void) free(vfp);
+ return (-1);
+ }
+
+ /* return an error if an empty path or mode specified */
+
+ if ((*a_path == '\0') || (*a_mode == '\0')) {
+ errno = EINVAL; /* Invalid argument */
+ (void) free(vfp);
+ return (-1);
+ }
+
+ /* open the file */
+
+ fp = fopen(a_path, a_mode);
+ if (fp == (FILE *)NULL) {
+ lerrno = errno;
+ (void) free(vfp);
+ errno = lerrno;
+ return (-1);
+ }
+
+ /* Get the file size */
+
+ if (fstat(fileno(fp), &statbuf) != 0) {
+ lerrno = errno;
+ (void) fclose(fp);
+ (void) free(vfp);
+ errno = lerrno;
+ return (-1);
+ }
+
+ /*
+ * Obtain access to existing file contents:
+ * -> plan a: map contents file into memory
+ * -> plan b: on failure just read into large buffer
+ */
+
+ /* attempt to mmap file if mmap is allowed */
+
+ vfp->_vfpStart = MAP_FAILED; /* assume map failed if not allowed */
+
+ /*
+ * if file is a regular file, and if mmap allowed,
+ * and (malloc not forbidden or size is > minumum size to mmap)
+ */
+
+ if ((S_ISREG(statbuf.st_mode)) && (!(a_flags & VFP_NOMMAP)) &&
+ ((a_flags & VFP_NOMALLOC) || statbuf.st_size > MIN_MMAP_SIZE)) {
+ char *p;
+ /* set size to current size of file */
+
+ vfp->_vfpMapSize = statbuf.st_size;
+
+ /*
+ * compute proper size for mapping for the file contents;
+ * add in one extra page so falling off end when file size is
+ * exactly modulo page size does not cause a page fault to
+ * guarantee that the end of the file contents will always
+ * contain a '\0' null character.
+ */
+
+ vfp->_vfpSize = (statbuf.st_size + pagesize +
+ (pagesize-(statbuf.st_size % pagesize)));
+
+ /*
+ * mmap allowed: mmap file into memory
+ * first allocate space on top of which the mapping can be done;
+ * this way we can guarantee that if the mapping happens to be
+ * an exact multiple of a page size, that there will be at least
+ * one byte past the end of the mapping that can be accessed and
+ * that is guaranteed to be zero.
+ */
+
+ /* allocate backing space */
+
+ p = (char *)memalign(pagesize, vfp->_vfpSize);
+ if (p == (char *)NULL) {
+ vfp->_vfpStart = MAP_FAILED;
+ } else {
+ /* guarantee first byte after end of data is zero */
+
+ p[vfp->_vfpMapSize] = '\0';
+
+ /* map file on top of the backing space */
+
+ vfp->_vfpStart = mmap(p, vfp->_vfpMapSize, PROT_READ,
+ MAP_PRIVATE|MAP_FIXED, fileno(fp), (off_t)0);
+
+ /* if mmap succeeded set mmap used flag in vfp */
+
+ if (vfp->_vfpStart != MAP_FAILED) {
+ vfp->_vfpFlags |= _VFP_MMAP;
+ }
+ }
+ }
+
+ /* if map failed (or not allowed) attempt malloc (if allowed) */
+
+ if ((vfp->_vfpStart == MAP_FAILED) && (!(a_flags & VFP_NOMALLOC))) {
+ /* mmap failed - plan b: read directly into memory */
+ ssize_t rlen;
+
+ /*
+ * compute proper size for allocating storage for file contents;
+ * add in one extra page so falling off end when file size is
+ * exactly modulo page size does not cause a page fault to
+ * guarantee that the end of the file contents will always
+ * contain a '\0' null character.
+ */
+
+ vfp->_vfpSize = statbuf.st_size+pagesize;
+
+ /* allocate buffer to hold file data */
+
+ vfp->_vfpStart = memalign((size_t)pagesize, vfp->_vfpSize);
+ if (vfp->_vfpStart == (char *)NULL) {
+ lerrno = errno;
+ (void) fclose(fp);
+ (void) free(vfp);
+ errno = lerrno;
+ return (-1);
+ }
+
+ /* read the file into the buffer */
+
+ if (statbuf.st_size != 0) {
+ rlen = read(fileno(fp), vfp->_vfpStart,
+ statbuf.st_size);
+ if (rlen != statbuf.st_size) {
+ lerrno = errno;
+ if (lerrno == 0) {
+ lerrno = EIO;
+ }
+ (void) free(vfp->_vfpStart);
+ (void) fclose(fp);
+ (void) free(vfp);
+ errno = lerrno;
+ return (-1);
+ }
+
+ /* assure last byte+1 is null character */
+
+ ((char *)vfp->_vfpStart)[statbuf.st_size] = '\0';
+ }
+
+ /* set malloc used flag in vfp */
+
+ vfp->_vfpFlags |= _VFP_MALLOC;
+ }
+
+ /* if no starting address all read methods failed */
+
+ if (vfp->_vfpStart == MAP_FAILED) {
+ /* no mmap() - no read() - cannot allocate memory */
+ (void) fclose(fp);
+ (void) free(vfp);
+ errno = ENOMEM;
+ return (-1);
+ }
+
+ /*
+ * initialize vfp contents
+ */
+
+ /* _vfpCurr -> next byte to read */
+ vfp->_vfpCurr = (char *)vfp->_vfpStart;
+
+ /* _vfpEnd -> last data byte */
+ vfp->_vfpEnd = (((char *)vfp->_vfpStart) + statbuf.st_size)-1;
+
+ /* _vfpHighWater -> last byte written */
+ vfp->_vfpHighWater = (char *)vfp->_vfpEnd;
+
+ /* _vfpFile -> associated FILE* object */
+ vfp->_vfpFile = fp;
+
+ /* set flags as appropriate */
+
+ (void) vfpSetFlags(vfp, a_flags);
+
+ /* retain path name */
+
+ vfp->_vfpPath = strdup(a_path ? a_path : "");
+
+ /* set read/write flags */
+
+ if (*a_mode == 'w') {
+ vfp->_vfpFlags |= _VFP_WRITE;
+ }
+
+ if (*a_mode == 'r') {
+ vfp->_vfpFlags |= _VFP_READ;
+ }
+
+ /* set return vfp pointer */
+
+ (*r_vfp) = vfp;
+
+ /* All OK */
+
+ return (0);
+}
+
+/*
+ * Name: vfpClose
+ * Description: Close an open vfp, causing any modified data to be written out
+ * to the file associated with the vfp.
+ * Arguments: VFP_T **r_vfp - pointer to pointer to VFP_T returned by vfpOpen
+ * Returns: int == 0 - operation was successful
+ * != 0 - operation failed, errno contains reason
+ * Side Effects: r_vfp is set to (VFP_T)NULL
+ */
+
+int
+vfpClose(VFP_T **r_vfp)
+{
+ int ret;
+ int lerrno;
+ VFP_T *vfp;
+
+ /* return error if NULL VFP_T** provided */
+
+ if (r_vfp == (VFP_T **)NULL) {
+ errno = EFAULT;
+ return (-1);
+ }
+
+ /* localize access to VFP_T */
+
+ vfp = *r_vfp;
+
+ /* return successful if NULL VFP_T* provided */
+
+ if (vfp == (VFP_T *)NULL) {
+ return (0);
+ }
+
+ /* reset return VFP_T* handle */
+
+ *r_vfp = (VFP_T *)NULL;
+
+ /*
+ * if closing a file that is open for writing, commit all data if the
+ * backing memory is volatile and if there is a file open to write
+ * the data to.
+ */
+
+ if (vfp->_vfpFlags & _VFP_WRITE) {
+ if ((vfp->_vfpFlags & _VFP_MALLOC) &&
+ (vfp->_vfpFile != (FILE *)NULL)) {
+ size_t len;
+
+ /* determine number of bytes to write */
+ len = vfpGetModifiedLen(vfp);
+
+ /* if modified bytes present commit data to the file */
+ if (len > 0) {
+ (void) vfpSafePwrite(fileno(vfp->_vfpFile),
+ vfp->_vfpStart, len, (off_t)0);
+ }
+ }
+ }
+
+ /* deallocate any allocated storage/mappings/etc */
+
+ if (vfp->_vfpFlags & _VFP_MALLOC) {
+ (void) free(vfp->_vfpStart);
+ } else if (vfp->_vfpFlags & _VFP_MMAP) {
+ /* unmap the file mapping */
+
+ (void) munmap(vfp->_vfpStart, vfp->_vfpMapSize);
+
+ /* free the backing allocation */
+
+ (void) free(vfp->_vfpStart);
+ }
+
+ /* free up path */
+
+ (void) free(vfp->_vfpPath);
+
+ /* close the file */
+
+ ret = 0;
+ if (vfp->_vfpFile != (FILE *)NULL) {
+ ret = fclose(vfp->_vfpFile);
+ lerrno = errno;
+ }
+
+ /* deallocate the vfp itself */
+
+ (void) free(vfp);
+
+ /* if the fclose() failed, return error and errno */
+
+ if (ret != 0) {
+ errno = lerrno;
+ return (-1);
+ }
+
+ return (0);
+}
+
+/*
+ * Name: vfpSetFlags
+ * Description: Modify operation of VFP according to flags specified
+ * Arguments: VFP_T *a_vfp - VFP_T pointer associated with file to set flags
+ * VFPFLAGS_T a_flags - one or more flags to control the operation:
+ * - VFP_NEEDNOW - file data needed in memory now
+ * - VFP_SEQUENTIAL - file data sequentially accessed
+ * - VFP_RANDOM - file data randomly accessed
+ * Any other flags specified are silently ignored.
+ * Returns: int == 0 - operation was successful
+ * != 0 - operation failed, errno contains reason
+ */
+
+int
+vfpSetFlags(VFP_T *a_vfp, VFPFLAGS_T a_flags)
+{
+ /* return if no vfp specified */
+
+ if (a_vfp == (VFP_T *)NULL) {
+ return (0);
+ }
+
+ /* if file data mapped into memory, apply vm flags */
+
+ if ((a_vfp->_vfpSize != 0) && (a_vfp->_vfpFlags & _VFP_MMAP)) {
+ /* mmap succeeded: properly advise vm system */
+
+ if (a_flags & VFP_NEEDNOW) {
+ /* advise vm system data is needed now */
+ (void) madvise(a_vfp->_vfpStart, a_vfp->_vfpMapSize,
+ MADV_WILLNEED);
+ }
+ if (a_flags & VFP_SEQUENTIAL) {
+ /* advise vm system data access is sequential */
+ (void) madvise(a_vfp->_vfpStart, a_vfp->_vfpSize,
+ MADV_SEQUENTIAL);
+ }
+ if (a_flags & VFP_RANDOM) {
+ /* advise vm system data access is random */
+ (void) madvise(a_vfp->_vfpStart, a_vfp->_vfpSize,
+ MADV_RANDOM);
+ }
+ }
+
+ return (0);
+}
+
+/*
+ * Name: vfpRewind
+ * Description: Reset default pointer for next read/write to start of file data
+ * Arguments: VFP_T *a_vfp - VFP_T pointer associated with file to rewind
+ * Returns: void
+ * Operation is always successful
+ */
+
+void
+vfpRewind(VFP_T *a_vfp)
+{
+ /* return if no vfp specified */
+
+ if (a_vfp == (VFP_T *)NULL) {
+ return;
+ }
+
+ /* set high water mark of last modified data */
+
+ if (a_vfp->_vfpCurr > a_vfp->_vfpHighWater) {
+ a_vfp->_vfpHighWater = a_vfp->_vfpCurr;
+ }
+
+ /* reset next character pointer to start of file data */
+
+ a_vfp->_vfpCurr = a_vfp->_vfpStart;
+}
+
+/*
+ * Name: vfpSetSize
+ * Description: Set size of in-memory image associated with VFP
+ * Arguments: VFP_T *a_vfp - VFP_T pointer associated with file to set
+ * size_t a_size - number of bytes to associatge with VFP
+ * Returns: int == 0 - operation was successful
+ * != 0 - operation failed, errno contains reason
+ * Side Effects:
+ * Currently only a file that is in malloc()ed memory can
+ * have its in-memory size changed.
+ * An error is returned If the file is mapped into memory.
+ * A file cannot be decreased in size - if the specified
+ * size is less than the current size, the operation is
+ * successful but no change in file size occurs.
+ * If no file is associated with the VFP (no "name" was
+ * given to vfpOpen) the first call to vfpSetSize allocates
+ * the initial size of the file data - effectively calling
+ * "malloc" to allocate the initial memory for the file data.
+ * Once an initial allocation has been made, subsequent calls
+ * to vfpSetSize are effectively a "realloc" of the existing
+ * file data.
+ * All existing file data is preserved.
+ */
+
+int
+vfpSetSize(VFP_T *a_vfp, size_t a_size)
+{
+ char *np;
+ size_t curSize;
+
+ /* return if no vfp specified */
+
+ if (a_vfp == (VFP_T *)NULL) {
+ return (0);
+ }
+
+ /* if malloc not used don't know how to set size right now */
+
+ if (!(a_vfp->_vfpFlags & _VFP_MALLOC)) {
+ return (-1);
+ }
+
+ /* adjust size to reflect extra page of data maintained */
+
+ a_size += getpagesize();
+
+ /* if size is not larger than current nothing to do */
+
+ if (a_size <= a_vfp->_vfpSize) {
+ return (0);
+ }
+
+ /* remember new size */
+
+ curSize = a_vfp->_vfpSize;
+ a_vfp->_vfpSize = a_size;
+
+ /* allocate/reallocate memory as appropriate */
+
+ if (a_vfp->_vfpStart != (char *)NULL) {
+ np = (char *)realloc(a_vfp->_vfpStart, a_vfp->_vfpSize+1);
+ if (np == (char *)NULL) {
+ return (-1);
+ }
+ np[curSize-1] = '\0';
+ } else {
+ np = (char *)malloc(a_vfp->_vfpSize+1);
+ if (np == (char *)NULL) {
+ return (-1);
+ }
+ np[0] = '\0';
+ }
+
+ /* make sure last allocated byte is a null */
+
+ np[a_vfp->_vfpSize] = '\0';
+
+ /*
+ * adjust all pointers to account for buffer address change
+ */
+
+ /* _vfpCurr -> next byte to read */
+ a_vfp->_vfpCurr = (char *)(((ptrdiff_t)a_vfp->_vfpCurr -
+ (ptrdiff_t)a_vfp->_vfpStart) + np);
+
+ /* _vfpHighWater -> last byte written */
+ a_vfp->_vfpHighWater = (char *)(((ptrdiff_t)a_vfp->_vfpHighWater -
+ (ptrdiff_t)a_vfp->_vfpStart) + np);
+
+ /* _vfpEnd -> last data byte */
+ a_vfp->_vfpEnd = (np + a_vfp->_vfpSize)-1;
+
+ /* _vfpStart -> first data byte */
+ a_vfp->_vfpStart = np;
+
+ return (0);
+}
+
+/*
+ * Name: vfpTruncate
+ * Description: Truncate data associated with VFP
+ * Arguments: VFP_T *a_vfp - VFP_T pointer associated with file to truncate
+ * Returns: void
+ * Operation is always successful.
+ * Side Effects:
+ * In memory data associated with file is believed to be empty.
+ * Actual memory associated with file is not affected.
+ * If a file is associated with the VFP, it is truncated.
+ */
+
+void
+vfpTruncate(VFP_T *a_vfp)
+{
+ /* return if no vfp specified */
+
+ if (a_vfp == (VFP_T *)NULL) {
+ return;
+ }
+
+ /*
+ * reset all pointers so that no data is associated with file
+ */
+
+ /* current byte is start of data area */
+
+ a_vfp->_vfpCurr = a_vfp->_vfpStart;
+
+ /* last byte written is start of data area */
+
+ a_vfp->_vfpHighWater = a_vfp->_vfpStart;
+
+ /* current character is NULL */
+
+ *a_vfp->_vfpCurr = '\0';
+
+ /* if file associated with VFP, truncate actual file */
+
+ if (a_vfp->_vfpFile != (FILE *)NULL) {
+ (void) ftruncate(fileno(a_vfp->_vfpFile), 0);
+ }
+}
+
+/*
+ * Name: vfpWriteToFile
+ * Description: Write data associated with VFP to specified file
+ * Arguments: VFP_T *a_vfp - VFP_T pointer associated with file to write
+ * char *a_path - path of file to write file data to
+ * Returns: int == 0 - operation was successful
+ * != 0 - operation failed, errno contains reason
+ */
+
+int
+vfpWriteToFile(VFP_T *a_vfp, char *a_path)
+{
+ int fd;
+ int lerrno = 0;
+ size_t len;
+ ssize_t result = 0;
+
+ /* return if no vfp specified */
+
+ if (a_vfp == (VFP_T *)NULL) {
+ errno = EFAULT;
+ return (-1);
+ }
+
+ /* on buffer overflow generate error */
+
+ if ((a_vfp->_vfpOverflow != 0) || (vfpGetBytesAvailable(a_vfp) < 1)) {
+ errno = EFBIG;
+ return (-1);
+ }
+
+ /* open file to write data to */
+
+ fd = open(a_path, O_WRONLY|O_CREAT|O_TRUNC, 0644);
+ if (fd < 0) {
+ return (-1);
+ }
+
+ /* determine number of bytes to write */
+
+ len = vfpGetModifiedLen(a_vfp);
+
+ /*
+ * if there is data associated with the file, write it out;
+ * if an error occurs, close the file and return failure.
+ */
+
+ if (len > 0) {
+ result = vfpSafeWrite(fd, a_vfp->_vfpStart, len);
+ if (result != len) {
+ /* error comitting data - return failure */
+ lerrno = errno;
+ (void) close(fd);
+ errno = lerrno;
+ return (-1);
+ }
+ }
+
+ /* close the file */
+
+ (void) close(fd);
+
+ /* data committed to backing store - clear the modified flag */
+
+ (void) vfpClearModified(a_vfp);
+
+ /* return success */
+
+ return (0);
+}
+
+/*
+ * Name: vfpCheckpointFile
+ * Description: Create new VFP that checkpoints existing VFP, can be used by
+ * subsequent call to vfpCheckpointOpen to open a file using the
+ * existing in-memory cache of the contents of the file
+ * Arguments: VFP_T **r_cpVfp - pointer to pointer to VFP_T to be filled in
+ * with "checkpointed file" VFP (backing store)
+ * VFP_T **a_vfp - pointer to pointer to VFP_T returned by vfpOpen
+ * representing the VFP to checkpoint
+ * char *a_path - path to file that is the backing store for the
+ * in-memory data represented by a_vfp - used to verify
+ * that the data in memory is not out of date with respect
+ * to the backing store when vfpCheckpointOpen is called
+ * == (char *)NULL - use path associated with a_vfp
+ * that is, the backing store file in use
+ * Returns: int == 0 - operation was successful
+ * - r_destVfp contains a pointer to a new VFP that
+ * may be used in a subsequent call to
+ * vfpCheckpointOpen
+ * - the VFP referenced by *a_vfp is free()ed and
+ * must no longer be referenced
+ * != 0 - operation failed, errno contains reason
+ * - the VFP referenced by *a_vfp is not affected;
+ * the caller may continue to use it
+ * Notes: If the data of a VFP to checkpoint is mmap()ed then this method
+ * returns failure - only malloc()ed data VFPs can be
+ * checkpointed.
+ */
+
+int
+vfpCheckpointFile(VFP_T **r_cpVfp, VFP_T **a_vfp, char *a_path)
+{
+ VFP_T *vfp; /* newly allocated checkpointed VFP */
+ VFP_T *avfp; /* local -> to a_vfp */
+ struct stat statbuf; /* stat(2) info for backing store */
+
+ /* return error if NULL VFP_T** to checkpoint provided */
+
+ if (r_cpVfp == (VFP_T **)NULL) {
+ errno = EFAULT;
+ return (-1);
+ }
+
+ /* reset return checkpoint VFP pointer */
+
+ (*r_cpVfp) = (VFP_T *)NULL;
+
+ /* return error if no VFP to checkpoint specified */
+
+ if (a_vfp == (VFP_T **)NULL) {
+ errno = EFAULT;
+ return (-1);
+ }
+
+ /* localize reference to a_vfp */
+
+ avfp = *a_vfp;
+
+ /* return error if no VFP to checkpoint specified */
+
+ if (avfp == (VFP_T *)NULL) {
+ errno = EFAULT;
+ return (-1);
+ }
+
+ /* on buffer overflow generate error */
+
+ if ((avfp->_vfpOverflow != 0) || (vfpGetBytesAvailable(avfp) < 1)) {
+ errno = EFBIG;
+ return (-1);
+ }
+
+ /* no checkpointing is possible if the existing VFP is mmap()ed */
+
+ if (avfp->_vfpFlags & _VFP_MMAP) {
+ errno = EIO;
+ return (-1);
+ }
+
+ /* if no path specified, grab it from the VFP to checkpoint */
+
+ if ((a_path == (char *)NULL) || (*a_path == '\0')) {
+ a_path = avfp->_vfpPath;
+ }
+
+ /* backing store required: if VFP is "string" then this is an error */
+
+ if ((a_path == (char *)NULL) ||
+ strcmp(a_path, VFP_ANONYMOUS_PATH) == 0) {
+ errno = EINVAL;
+ return (-1);
+ }
+
+ /* Get the VFP to checkpoint (backing store) file size */
+
+ if (stat(a_path, &statbuf) != 0) {
+ return (-1);
+ }
+
+ /* allocate storage for checkpointed VFP (to return) */
+
+ vfp = (VFP_T *)malloc(sizeof (VFP_T));
+ if (vfp == (VFP_T *)NULL) {
+ return (-1);
+ }
+
+ /*
+ * close any file that is on the VFP to checkpoint (backing store);
+ * subsequent processes can modify the backing store data, and
+ * then when vfpCheckpointOpen is called, either the in-memory
+ * cached data will be used (if backing store unmodified) or else
+ * the in-memory data is released and the backing store is used.
+ */
+
+ if (avfp->_vfpFile != (FILE *)NULL) {
+ (void) fclose(avfp->_vfpFile);
+ avfp->_vfpFile = (FILE *)NULL;
+ }
+
+ /* free any path associated with VFP to checkpoint (backing store) */
+
+ if (avfp->_vfpPath != (char *)NULL) {
+ (void) free(avfp->_vfpPath);
+ avfp->_vfpPath = (char *)NULL;
+ }
+
+ /* copy contents of VFP to checkpoint to checkpointed VFP */
+
+ memcpy(vfp, avfp, sizeof (VFP_T));
+
+ /* free contents of VFP to checkpoint */
+
+ (void) free(avfp);
+
+ /* reset pointer to VFP that has been free'd */
+
+ *a_vfp = (VFP_T *)NULL;
+
+ /* remember path associated with the checkpointed VFP (backing store) */
+
+ vfp->_vfpPath = strdup(a_path);
+
+ /* save tokens that identify the backing store for the in-memory data */
+
+ vfp->_vfpCkDev = statbuf.st_dev; /* devid holding st_ino inode */
+ vfp->_vfpCkIno = statbuf.st_ino; /* backing store inode */
+ vfp->_vfpCkMtime = statbuf.st_mtime; /* last data modification */
+ vfp->_vfpCkSize = statbuf.st_size; /* backing store size (bytes) */
+ vfp->_vfpCkStBlocks = statbuf.st_blocks; /* blocks allocated to file */
+
+ /* pass checkpointed VFP to caller */
+
+ (*r_cpVfp) = vfp;
+
+ /* success! */
+
+ return (0);
+}
+
+/*
+ * Name: vfpCheckpointOpen
+ * Description: Open file on vfp, allocate storage, return pointer to VFP_T
+ * that can be used to access/modify file contents. If a VFP_T to
+ * a checkpointed VFP is passed in, and the in memory contents of
+ * the VFP are not out of date with respect to the backing store
+ * file, use the existing in-memory contents - otherwise, discard
+ * the in-memory contents and reopen and reread the file.
+ * Arguments: VFP_T **a_cpVfp - pointer to pointer to VFP_T that represents
+ * checkpointed VFP to use to open the file IF the contents
+ * of the backing store are identical to the in-memory data
+ * VFP_T **r_vfp - pointer to pointer to VFP_T to open file on
+ * char *a_path - path of file to open and associate with this VFP.
+ * - if the path is (char *)NULL then no file is associated
+ * with this VFP - this is a way to create a fixed length
+ * string that can be manipulated with the VFP operators.
+ * Before the VFP can be used "vfpSetSize" must be called
+ * to set the size of the string buffer.
+ * char *a_mode - fopen mode to open the file with
+ * VFPFLAGS_T a_flags - one or more flags to control the operation:
+ * - VFP_NONE - no special flags
+ * - VFP_NEEDNOW - file data needed in memory now
+ * - VFP_SEQUENTIAL - memory will be sequentially accessed
+ * - VFP_RANDOM - memory will be randomly accessed
+ * - VFP_NOMMAP - do not use mmap to access file
+ * - VFP_NOMALLOC - do not use malloc to buffer file
+ * Returns: int == 0 - operation was successful
+ * != 0 - operation failed, errno contains reason
+ * Side Effects: r_vfp -- filled in with a pointer to a newly allocated vfp
+ * which can be used with the various VFP functions.
+ * a_cpVfp -- contents reset to zero if used to open the file
+ * errno -- contains system error number if return is != 0
+ */
+
+int
+vfpCheckpointOpen(VFP_T **a_cpVfp, VFP_T **r_vfp, char *a_path,
+ char *a_mode, VFPFLAGS_T a_flags)
+{
+ FILE *fp; /* backing store */
+ VFP_T *cpVfp; /* local -> to a_cpVfp checkpointed VFP */
+ VFP_T *vfp; /* new VFP open on checkpointed backing store */
+ struct stat statbuf; /* stat(2) info on backing store */
+
+ /*
+ * if no source VFP, or source VFP empty,
+ * or no backing store, just open file
+ */
+
+ if ((a_cpVfp == (VFP_T **)NULL) || (*a_cpVfp == (VFP_T *)NULL) ||
+ ((*a_cpVfp)->_vfpStart == (char *)NULL)) {
+ (void) vfpClose(a_cpVfp);
+ return (vfpOpen(r_vfp, a_path, a_mode, a_flags));
+ }
+
+ /* localize access to checkpointed VFP_T (*a_cpVfp) */
+
+ cpVfp = *a_cpVfp;
+
+ /* if no path specified, grab it from the checkpointed VFP */
+
+ if ((a_path == (char *)NULL) || (*a_path == '\0')) {
+ a_path = cpVfp->_vfpPath;
+ }
+
+ /* return error if no path specified and no path in checkpointed VFP */
+
+ if ((a_path == (char *)NULL) && (*a_path == '\0')) {
+ errno = EINVAL;
+ return (-1);
+ }
+
+ /* if no backing store path, then just open file */
+
+ if (stat(a_path, &statbuf) != 0) {
+ (void) vfpClose(a_cpVfp);
+ return (vfpOpen(r_vfp, a_path, a_mode, a_flags));
+ }
+
+ /*
+ * if backing store tokens do not match checkpointed VFP,
+ * the backing store has been updated since the VFP was checkpointed;
+ * release the in-memory data, and open and read the backing store
+ */
+
+ if ((statbuf.st_size != cpVfp->_vfpCkSize) ||
+ (statbuf.st_mtime != cpVfp->_vfpCkMtime) ||
+ (statbuf.st_blocks != cpVfp->_vfpCkStBlocks) ||
+ (statbuf.st_ino != cpVfp->_vfpCkIno) ||
+ (statbuf.st_dev != cpVfp->_vfpCkDev)) {
+ (void) vfpClose(a_cpVfp);
+ return (vfpOpen(r_vfp, a_path, a_mode, a_flags));
+ }
+
+ /*
+ * backing store has not been updated since the VFP was checkpointed;
+ * use the in-memory data without re-reading the backing store; open the
+ * backing store file (if no file already open on the checkpointed VFP)
+ * so there is an open file associated with the in-memory data
+ */
+
+ fp = cpVfp->_vfpFile;
+ if (fp == (FILE *)NULL) {
+ fp = fopen(a_path, a_mode);
+ if (fp == (FILE *)NULL) {
+ int lerrno;
+
+ lerrno = errno;
+ (void) vfpClose(a_cpVfp);
+ errno = lerrno;
+ return (-1);
+ }
+ }
+
+ /* allocate new VFP object to return as open VFP */
+
+ vfp = (VFP_T *)malloc(sizeof (VFP_T));
+ if (vfp == (VFP_T *)NULL) {
+ (void) vfpClose(a_cpVfp);
+ return (vfpOpen(r_vfp, a_path, a_mode, a_flags));
+ }
+
+ /* copy cached checkpointed VFP to new VFP to return */
+
+ (void) memcpy(vfp, cpVfp, sizeof (VFP_T));
+
+ /*
+ * initialize VFP to return contents
+ */
+
+ /* FILE -> file opened on the VFPs backing store */
+
+ vfp->_vfpFile = fp;
+
+ /* release any existing path associated with the VFP */
+
+ if (vfp->_vfpPath != (char *)NULL) {
+ (void) free(vfp->_vfpPath);
+ }
+
+ /* path associated with the backing store for this VFP */
+
+ vfp->_vfpPath = strdup(a_path);
+
+ /*
+ * data pointers associated with in memory copy of backing store
+ * (such as _vfpHighWater, _vfpEnd, _vfpStart, etc.)
+ * do not need to be modified because we are using the same backing
+ * store as was checkpointed in cpVfp that is pointed to by vfp.
+ */
+
+ /* _vfpCurr -> next byte to read */
+ vfp->_vfpCurr = (char *)vfp->_vfpStart;
+
+ /* free checkpointed VFP as it is now open on "vfp" */
+
+ (void) free(cpVfp);
+
+ /* reset callers -> checkpointed VFP */
+
+ (*a_cpVfp) = (VFP_T *)NULL;
+
+ /* set return VFP pointer */
+
+ (*r_vfp) = vfp;
+
+ /* success! */
+
+ return (0);
+}
+
+/*
+ * Name: vfpClearModified
+ * Description: Clear the "data is modified" indication from the VFP
+ * Arguments: VFP_T *a_vfp - VFP_T pointer associated with file to clear
+ * the "data is modified" indication
+ * Returns: int - previous setting of "data is modified" indication
+ * == 0 - "data is modified" was NOT previously set
+ * != 0 - "data is modified" WAS previously set
+ */
+
+int
+vfpClearModified(VFP_T *a_vfp)
+{
+ VFPFLAGS_T flags;
+
+ /* save current flags settings */
+
+ flags = a_vfp->_vfpFlags;
+
+ /* clear "data is modified" flag */
+
+ a_vfp->_vfpFlags &= (~_VFP_MODIFIED);
+
+ /* return previous "data is modified" flag setting */
+
+ return ((flags & _VFP_MODIFIED) != 0);
+}
+
+/*
+ * Name: vfpSetModified
+ * Description: Set the "data is modified" indication from the VFP
+ * Arguments: VFP_T *a_vfp - VFP_T pointer associated with file to set
+ * the "data is modified" indication
+ * Returns: int - previous setting of "data is modified" indication
+ * == 0 - "data is modified" was NOT previously set
+ * != 0 - "data is modified" WAS previously set
+ */
+
+int
+vfpSetModified(VFP_T *a_vfp)
+{
+ VFPFLAGS_T flags;
+
+ /* save current flags settings */
+
+ flags = a_vfp->_vfpFlags;
+
+ /* set "data is modified" flag */
+
+ a_vfp->_vfpFlags |= _VFP_MODIFIED;
+
+ /* return previous "data is modified" flag setting */
+
+ return ((flags & _VFP_MODIFIED) != 0);
+}
+
+/*
+ * Name: vfpGetModified
+ * Description: Get the "data is modified" indication from the VFP
+ * Arguments: VFP_T *a_vfp - VFP_T pointer associated with file to get
+ * the "data is modified" indication
+ * Returns: int - current setting of "data is modified" indication
+ * == 0 - "data is modified" is NOT set
+ * != 0 - "data is modified" IS set
+ */
+
+int
+vfpGetModified(VFP_T *a_vfp)
+{
+ /* return current "data is modified" flag setting */
+
+ return ((a_vfp->_vfpFlags & _VFP_MODIFIED) != 0);
+}
+
+/*
+ * Name: vfpSafeWrite
+ * Description: write data to open file safely
+ * Arguments: a_fildes - file descriptor to write data to
+ * a_buf - pointer to buffer containing data to write
+ * a_nbyte - number of bytes to write to open file
+ * Returns: int
+ * < 0 - error, errno set
+ * >= 0 - success
+ * NOTE: unlike write(2), vfpSafeWrite() handles partial writes, and will
+ * ----- restart the write() until all bytes are written, or an error occurs.
+ */
+
+ssize_t
+vfpSafeWrite(int a_fildes, void *a_buf, size_t a_nbyte)
+{
+ ssize_t r;
+ size_t bytes = a_nbyte;
+
+ for (;;) {
+ /* write bytes to file */
+ r = write(a_fildes, a_buf, a_nbyte);
+
+ /* return error on failure of write() */
+ if (r < 0) {
+ /* EAGAIN: try again */
+ if (errno == EAGAIN) {
+ continue;
+ }
+ /* EINTR: interrupted - try again */
+ if (errno == EINTR) {
+ continue;
+ }
+ return (r);
+ }
+
+ /* return total bytes written on success */
+ if (r >= a_nbyte) {
+ return (bytes);
+ }
+
+ /* partial write, adjust pointers, call write again */
+ a_buf = (void *)((ptrdiff_t)a_buf + (ptrdiff_t)r);
+ a_nbyte -= (size_t)r;
+ }
+}
+
+/*
+ * Name: vfpSafePwrite
+ * Description: write data to open file safely
+ * Arguments: a_fildes - file descriptor to write data to
+ * a_buf - pointer to buffer containing data to write
+ * a_nbyte - number of bytes to write to open file
+ * a_offset - offset into open file to write the first byte to
+ * Returns: int
+ * < 0 - error, errno set
+ * >= 0 - success
+ * NOTE: unlike pwrite(2), vfpSafePwrite() handles partial writes, and will
+ * ----- restart the pwrite() until all bytes are written, or an error occurs.
+ */
+
+ssize_t
+vfpSafePwrite(int a_fildes, void *a_buf, size_t a_nbyte, off_t a_offset)
+{
+ ssize_t r;
+ size_t bytes = a_nbyte;
+
+ for (;;) {
+ /* write bytes to file */
+ r = pwrite(a_fildes, a_buf, a_nbyte, a_offset);
+
+ /* return error on failure of write() */
+ if (r < 0) {
+ /* EAGAIN: try again */
+ if (errno == EAGAIN) {
+ continue;
+ }
+ /* EINTR: interrupted - try again */
+ if (errno == EINTR) {
+ continue;
+ }
+ return (r);
+ }
+
+ /* return total bytes written on success */
+ if (r >= a_nbyte) {
+ return (bytes);
+ }
+
+ /* partial write, adjust pointers, call write again */
+ a_buf = (void *)((ptrdiff_t)a_buf + (ptrdiff_t)r);
+ a_nbyte -= (size_t)r;
+ a_offset += (off_t)r;
+ }
+}