summaryrefslogtreecommitdiff
path: root/usr/src/lib/libelfsign/common/elfsignlib.c
diff options
context:
space:
mode:
Diffstat (limited to 'usr/src/lib/libelfsign/common/elfsignlib.c')
-rw-r--r--usr/src/lib/libelfsign/common/elfsignlib.c1598
1 files changed, 1598 insertions, 0 deletions
diff --git a/usr/src/lib/libelfsign/common/elfsignlib.c b/usr/src/lib/libelfsign/common/elfsignlib.c
new file mode 100644
index 0000000000..00f71a125d
--- /dev/null
+++ b/usr/src/lib/libelfsign/common/elfsignlib.c
@@ -0,0 +1,1598 @@
+/*
+ * 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 2007 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#define ELF_TARGET_ALL /* get definitions of all section flags */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <strings.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <libintl.h>
+#include <dirent.h>
+#include <errno.h>
+#include <libelf.h>
+#include <gelf.h>
+#include <sys/mman.h>
+#include <cryptoutil.h>
+#include <sha1.h>
+#include <sys/crypto/elfsign.h>
+#include <libelfsign.h>
+
+#ifndef SHA1_DIGEST_LENGTH
+#define SHA1_DIGEST_LENGTH 20
+#endif /* SHA1_DIGEST_LENGTH */
+
+const char SUNW_ELF_SIGNATURE_ID[] = ELF_SIGNATURE_SECTION;
+const char OID_sha1WithRSAEncryption[] = "1.2.840.113549.1.1.5";
+
+static ELFsign_status_t elfsign_adjustoffsets(ELFsign_t ess,
+ Elf_Scn *scn, uint64_t new_size);
+static ELFsign_status_t elfsign_verify_esa(ELFsign_t ess,
+ uchar_t *sig, size_t sig_len);
+static uint32_t elfsign_switch_uint32(uint32_t i);
+static ELFsign_status_t elfsign_switch(ELFsign_t ess,
+ struct filesignatures *fssp, enum ES_ACTION action);
+
+struct filesig_extraction {
+ filesig_vers_t fsx_version;
+ char *fsx_format;
+ char fsx_signer_DN[ELFCERT_MAX_DN_LEN];
+ size_t fsx_signer_DN_len;
+ uchar_t fsx_signature[SIG_MAX_LENGTH];
+ size_t fsx_sig_len;
+ char fsx_sig_oid[100];
+ size_t fsx_sig_oid_len;
+ time_t fsx_time;
+};
+
+static char *
+version_to_str(filesig_vers_t v)
+{
+ char *ret;
+
+ switch (v) {
+ case FILESIG_VERSION1:
+ ret = "VERSION1";
+ break;
+ case FILESIG_VERSION2:
+ ret = "VERSION2";
+ break;
+ case FILESIG_VERSION3:
+ ret = "VERSION3";
+ break;
+ case FILESIG_VERSION4:
+ ret = "VERSION4";
+ break;
+ default:
+ ret = "UNKNOWN";
+ break;
+ }
+ return (ret);
+}
+
+/*
+ * Update filesignatures to include the v1/v2 filesig,
+ * composed of signer DN, signature, and OID.
+ */
+static struct filesignatures *
+filesig_insert_dso(struct filesignatures *fssp,
+ filesig_vers_t version,
+ const char *dn,
+ int dn_len,
+ const uchar_t *sig,
+ int sig_len,
+ const char *oid,
+ int oid_len)
+{
+ struct filesig *fsgp;
+ char *fsdatap;
+
+ if (oid == NULL) {
+ /*
+ * This OID is used for the rsa_md5_sha1 format signature also.
+ * This use is historical, and is hence continued,
+ * despite its lack of technical accuracy.
+ */
+ oid = OID_sha1WithRSAEncryption;
+ oid_len = strlen(oid);
+ }
+
+ /*
+ * for now, always insert a single-signature signature block
+ */
+ if (fssp != NULL)
+ free(fssp);
+ fssp = (struct filesignatures *)
+ malloc(filesig_ALIGN(sizeof (struct filesignatures) +
+ dn_len + sig_len + oid_len));
+ if (fssp == NULL)
+ return (fssp);
+
+ fssp->filesig_cnt = 1;
+ fssp->filesig_pad = 0; /* reserve for future use */
+
+ fsgp = &fssp->filesig_sig;
+ fsgp->filesig_size = sizeof (struct filesig) +
+ dn_len + sig_len + oid_len;
+ fsgp->filesig_version = version;
+ switch (version) {
+ case FILESIG_VERSION1:
+ case FILESIG_VERSION2:
+ fsgp->filesig_size -= sizeof (struct filesig) -
+ offsetof(struct filesig, filesig_v1_data[0]);
+ fsgp->filesig_v1_dnsize = dn_len;
+ fsgp->filesig_v1_sigsize = sig_len;
+ fsgp->filesig_v1_oidsize = oid_len;
+ fsdatap = &fsgp->filesig_v1_data[0];
+ break;
+ case FILESIG_VERSION3:
+ case FILESIG_VERSION4:
+ fsgp->filesig_size -= sizeof (struct filesig) -
+ offsetof(struct filesig, filesig_v3_data[0]);
+ fsgp->filesig_v3_time = time(NULL);
+ fsgp->filesig_v3_dnsize = dn_len;
+ fsgp->filesig_v3_sigsize = sig_len;
+ fsgp->filesig_v3_oidsize = oid_len;
+ fsdatap = &fsgp->filesig_v3_data[0];
+ break;
+ default:
+ cryptodebug("filesig_insert_dso: unknown version: %d",
+ version);
+ free(fssp);
+ return (NULL);
+ }
+ (void) memcpy(fsdatap, dn, dn_len);
+ fsdatap += dn_len;
+ (void) memcpy(fsdatap, (char *)sig, sig_len);
+ fsdatap += sig_len;
+ (void) memcpy(fsdatap, oid, oid_len);
+ fsdatap += oid_len;
+ fsgp = filesig_next(fsgp);
+ (void) memset(fsdatap, 0, (char *)(fsgp) - fsdatap);
+
+ return (fssp);
+}
+
+/*
+ * filesig_extract - extract filesig structure to internal form
+ */
+static filesig_vers_t
+filesig_extract(struct filesig *fsgp, struct filesig_extraction *fsxp)
+{
+ char *fsdp;
+
+#define filesig_extract_common(cp, field, data_var, len_var, len_limit) { \
+ len_var = len_limit; \
+ if (len_var > fsgp->field) \
+ len_var = fsgp->field; \
+ (void) memcpy(data_var, cp, len_var); \
+ cp += fsgp->field; }
+#define filesig_extract_str(cp, field, data_var, len_var) \
+ filesig_extract_common(cp, field, data_var, len_var, \
+ sizeof (data_var) - 1); \
+ data_var[len_var] = '\0';
+#define filesig_extract_opaque(cp, field, data_var, len_var) \
+ filesig_extract_common(cp, field, data_var, len_var, sizeof (data_var))
+
+ fsxp->fsx_version = fsgp->filesig_version;
+ cryptodebug("filesig_extract: version=%s",
+ version_to_str(fsxp->fsx_version));
+ switch (fsxp->fsx_version) {
+ case FILESIG_VERSION1:
+ case FILESIG_VERSION2:
+ /*
+ * extract VERSION1 DN, signature, and OID
+ */
+ fsdp = fsgp->filesig_v1_data;
+ fsxp->fsx_format = ES_FMT_RSA_MD5_SHA1;
+ fsxp->fsx_time = 0;
+ filesig_extract_str(fsdp, filesig_v1_dnsize,
+ fsxp->fsx_signer_DN, fsxp->fsx_signer_DN_len);
+ filesig_extract_opaque(fsdp, filesig_v1_sigsize,
+ fsxp->fsx_signature, fsxp->fsx_sig_len);
+ filesig_extract_str(fsdp, filesig_v1_oidsize,
+ fsxp->fsx_sig_oid, fsxp->fsx_sig_oid_len);
+ break;
+ case FILESIG_VERSION3:
+ case FILESIG_VERSION4:
+ fsdp = fsgp->filesig_v3_data;
+ fsxp->fsx_format = ES_FMT_RSA_SHA1;
+ fsxp->fsx_time = fsgp->filesig_v3_time;
+ filesig_extract_str(fsdp, filesig_v3_dnsize,
+ fsxp->fsx_signer_DN, fsxp->fsx_signer_DN_len);
+ filesig_extract_opaque(fsdp, filesig_v3_sigsize,
+ fsxp->fsx_signature, fsxp->fsx_sig_len);
+ filesig_extract_str(fsdp, filesig_v3_oidsize,
+ fsxp->fsx_sig_oid, fsxp->fsx_sig_oid_len);
+ break;
+ default:
+ break;
+ }
+
+ return (fsxp->fsx_version);
+}
+
+ELFsign_status_t
+elfsign_begin(const char *filename, enum ES_ACTION action, ELFsign_t *essp)
+{
+ Elf_Cmd elfcmd;
+ int oflags = 0;
+ short l_type;
+ ELFsign_t ess;
+ struct stat stb;
+ union {
+ char c[2];
+ short s;
+ } uorder;
+ GElf_Ehdr elfehdr;
+ char *ident;
+
+ switch (action) {
+ case ES_GET:
+ case ES_GET_CRYPTO:
+ cryptodebug("elfsign_begin for get");
+ elfcmd = ELF_C_READ;
+ oflags = O_RDONLY | O_NOCTTY | O_NDELAY;
+ l_type = F_RDLCK;
+ break;
+ case ES_UPDATE_RSA_MD5_SHA1:
+ case ES_UPDATE_RSA_SHA1:
+ cryptodebug("elfsign_begin for update");
+ elfcmd = ELF_C_RDWR;
+ oflags = O_RDWR | O_NOCTTY | O_NDELAY;
+ l_type = F_WRLCK;
+ break;
+ default:
+ return (ELFSIGN_UNKNOWN);
+ }
+
+ if ((ess = malloc(sizeof (struct ELFsign_s))) == NULL) {
+ return (ELFSIGN_UNKNOWN);
+ }
+ (void) memset((void *)ess, 0, sizeof (struct ELFsign_s));
+
+ if (!elfcertlib_init(ess)) {
+ cryptodebug("elfsign_begin: failed initialization");
+ return (ELFSIGN_UNKNOWN);
+ }
+
+ ess->es_elf = NULL;
+ ess->es_action = action;
+ ess->es_version = FILESIG_UNKNOWN;
+ ess->es_pathname = NULL;
+ ess->es_certpath = NULL;
+
+ if (filename == NULL) {
+ *essp = ess;
+ return (ELFSIGN_SUCCESS);
+ }
+
+ if ((ess->es_fd = open(filename, oflags)) == -1) {
+ elfsign_end(ess);
+ return (ELFSIGN_INVALID_ELFOBJ);
+ }
+ if ((fstat(ess->es_fd, &stb) == -1) || !S_ISREG(stb.st_mode)) {
+ elfsign_end(ess);
+ return (ELFSIGN_INVALID_ELFOBJ);
+ }
+ if ((ess->es_pathname = strdup(filename)) == NULL) {
+ elfsign_end(ess);
+ return (ELFSIGN_UNKNOWN);
+ }
+ /*
+ * The following lock is released in elfsign_end() when we close(2)
+ * the es_fd. This ensures that we aren't trying verify a file
+ * we are currently updating.
+ */
+ ess->es_flock.l_type = l_type;
+ ess->es_flock.l_whence = SEEK_CUR;
+ ess->es_flock.l_start = 0;
+ ess->es_flock.l_len = 0;
+ if (fcntl(ess->es_fd, F_SETLK, &ess->es_flock) == -1) {
+ cryptodebug("fcntl(F_SETLK) of %s failed with: %s",
+ ess->es_pathname, strerror(errno));
+ elfsign_end(ess);
+ return (ELFSIGN_UNKNOWN);
+ }
+
+ if (elf_version(EV_CURRENT) == EV_NONE) {
+ elfsign_end(ess);
+ return (ELFSIGN_UNKNOWN);
+ }
+
+ if ((ess->es_elf = elf_begin(ess->es_fd, elfcmd,
+ (Elf *)NULL)) == NULL) {
+ cryptodebug("elf_begin() failed: %s", elf_errmsg(-1));
+ elfsign_end(ess);
+ return (ELFSIGN_INVALID_ELFOBJ);
+ }
+
+ if (gelf_getehdr(ess->es_elf, &elfehdr) == NULL) {
+ cryptodebug("elf_getehdr() failed: %s", elf_errmsg(-1));
+ elfsign_end(ess);
+ return (ELFSIGN_INVALID_ELFOBJ);
+ }
+ ess->es_has_phdr = (elfehdr.e_phnum != 0);
+
+ uorder.s = ELFDATA2MSB << 8 | ELFDATA2LSB;
+ ident = elf_getident(ess->es_elf, NULL);
+ if (ident == NULL) {
+ cryptodebug("elf_getident() failed: %s", elf_errmsg(-1));
+ elfsign_end(ess);
+ return (ELFSIGN_INVALID_ELFOBJ);
+ }
+ ess->es_same_endian = (ident[EI_DATA] == uorder.c[0]);
+ ess->es_ei_class = ident[EI_CLASS];
+
+ /*
+ * Call elf_getshstrndx to be sure we have a real ELF object
+ * this is required because elf_begin doesn't check that.
+ */
+ if (elf_getshstrndx(ess->es_elf, &ess->es_shstrndx) == 0) {
+ elfsign_end(ess);
+ cryptodebug("elfsign_begin: elf_getshstrndx failed");
+ return (ELFSIGN_INVALID_ELFOBJ);
+ }
+
+ /*
+ * Make sure libelf doesn't rearrange section ordering / offsets.
+ */
+ (void) elf_flagelf(ess->es_elf, ELF_C_SET, ELF_F_LAYOUT);
+
+ *essp = ess;
+
+ return (ELFSIGN_SUCCESS);
+}
+
+/*
+ * elfsign_end - cleanup the ELFsign_t
+ *
+ * IN/OUT: ess
+ */
+void
+elfsign_end(ELFsign_t ess)
+{
+ if (ess == NULL)
+ return;
+
+ if (ess->es_elf != NULL && ES_ACTISUPDATE(ess->es_action)) {
+ if (elf_update(ess->es_elf, ELF_C_WRITE) == -1) {
+ cryptodebug("elf_update() failed: %s",
+ elf_errmsg(-1));
+ return;
+ }
+ }
+
+ if (ess->es_fd != -1) {
+ (void) close(ess->es_fd);
+ ess->es_fd = -1;
+ }
+
+ if (ess->es_pathname != NULL) {
+ free(ess->es_pathname);
+ ess->es_pathname = NULL;
+ }
+ if (ess->es_certpath != NULL) {
+ free(ess->es_certpath);
+ ess->es_certpath = NULL;
+ }
+
+ if (ess->es_elf != NULL) {
+ (void) elf_end(ess->es_elf);
+ ess->es_elf = NULL;
+ }
+
+ elfcertlib_fini(ess);
+
+ free(ess);
+}
+
+/*
+ * set the certificate path
+ */
+ELFsign_status_t
+elfsign_setcertpath(ELFsign_t ess, const char *certpath)
+{
+ /*
+ * Normally use of access(2) is insecure, here we are only
+ * doing it to help provide early failure and better error
+ * checking, so there is no race condition.
+ */
+ if (access(certpath, R_OK) != 0) {
+ elfsign_end(ess);
+ return (ELFSIGN_INVALID_CERTPATH);
+ }
+ ess->es_certpath = strdup(certpath);
+
+ if (ES_ACTISUPDATE(ess->es_action)) {
+ ELFCert_t cert = NULL;
+ char *subject;
+
+ /* set the version based on the certificate */
+ if (elfcertlib_getcert(ess, ess->es_certpath, NULL,
+ &cert, ess->es_action)) {
+ if ((subject = elfcertlib_getdn(cert)) != NULL) {
+ if (strstr(subject, ELFSIGN_CRYPTO))
+ ess->es_version = (ess->es_action ==
+ ES_UPDATE_RSA_MD5_SHA1) ?
+ FILESIG_VERSION1 : FILESIG_VERSION3;
+ else
+ ess->es_version = (ess->es_action ==
+ ES_UPDATE_RSA_MD5_SHA1) ?
+ FILESIG_VERSION2 : FILESIG_VERSION4;
+ }
+ elfcertlib_releasecert(ess, cert);
+ }
+ if (ess->es_version == FILESIG_UNKNOWN)
+ return (ELFSIGN_FAILED);
+ }
+ return (ELFSIGN_SUCCESS);
+}
+
+/*
+ * set the callback context
+ */
+void
+elfsign_setcallbackctx(ELFsign_t ess, void *ctx)
+{
+ ess->es_callbackctx = ctx;
+}
+
+/*
+ * set the signature extraction callback
+ */
+void
+elfsign_setsigvercallback(ELFsign_t ess,
+ void (*cb)(void *, void *, size_t, ELFCert_t))
+{
+ ess->es_sigvercallback = cb;
+}
+
+/*
+ * elfsign_signatures
+ *
+ * IN: ess, fsspp, action
+ * OUT: fsspp
+ */
+ELFsign_status_t
+elfsign_signatures(ELFsign_t ess,
+ struct filesignatures **fsspp,
+ size_t *fslen,
+ enum ES_ACTION action)
+{
+ Elf_Scn *scn = NULL, *sig_scn = NULL;
+ GElf_Shdr shdr;
+ Elf_Data *data = NULL;
+ const char *elf_section = SUNW_ELF_SIGNATURE_ID;
+ int fscnt, fssize;
+ struct filesig *fsgp, *fsgpnext;
+ uint64_t sig_offset = 0;
+
+ cryptodebug("elfsign_signature");
+ if ((ess == NULL) || (fsspp == NULL)) {
+ cryptodebug("invalid arguments");
+ return (ELFSIGN_UNKNOWN);
+ }
+
+ cryptodebug("elfsign_signature %s for %s",
+ ES_ACTISUPDATE(action) ? "ES_UPDATE" : "ES_GET", elf_section);
+
+ (void) elf_errno();
+ while ((scn = elf_nextscn(ess->es_elf, scn)) != NULL) {
+ const char *sh_name;
+ /*
+ * Do a string compare to examine each section header
+ * to see if this is the section that needs to be updated.
+ */
+ if (gelf_getshdr(scn, &shdr) == NULL) {
+ cryptodebug("gelf_getshdr() failed: %s",
+ elf_errmsg(-1));
+ return (ELFSIGN_FAILED);
+ }
+ sh_name = elf_strptr(ess->es_elf, ess->es_shstrndx,
+ (size_t)shdr.sh_name);
+ if (strcmp(sh_name, elf_section) == 0) {
+ cryptodebug("elfsign_signature: found %s", elf_section);
+ sig_scn = scn;
+ break;
+ }
+ if (shdr.sh_type != SHT_NOBITS &&
+ sig_offset < shdr.sh_offset + shdr.sh_size) {
+ sig_offset = shdr.sh_offset + shdr.sh_size;
+ }
+ }
+ if (elf_errmsg(0) != NULL) {
+ cryptodebug("unexpected error: %s", elf_section,
+ elf_errmsg(-1));
+ return (ELFSIGN_FAILED);
+ }
+
+ if (ES_ACTISUPDATE(action) && (sig_scn == NULL)) {
+ size_t old_size, new_size;
+ char *new_d_buf;
+
+ cryptodebug("elfsign_signature: %s not found - creating",
+ elf_section);
+
+ /*
+ * insert section name in .shstrtab
+ */
+ if ((scn = elf_getscn(ess->es_elf, ess->es_shstrndx)) == 0) {
+ cryptodebug("elf_getscn() failed: %s",
+ elf_errmsg(-1));
+ return (ELFSIGN_FAILED);
+ }
+ if (gelf_getshdr(scn, &shdr) == NULL) {
+ cryptodebug("gelf_getshdr() failed: %s",
+ elf_errmsg(-1));
+ return (ELFSIGN_FAILED);
+ }
+ if ((data = elf_getdata(scn, data)) == NULL) {
+ cryptodebug("elf_getdata() failed: %s",
+ elf_errmsg(-1));
+ return (ELFSIGN_FAILED);
+ }
+ old_size = data->d_size;
+ if (old_size != shdr.sh_size) {
+ cryptodebug("mismatch between data size %d "
+ "and section size %lld", old_size, shdr.sh_size);
+ return (ELFSIGN_FAILED);
+ }
+ new_size = old_size + strlen(elf_section) + 1;
+ if ((new_d_buf = malloc(new_size)) == NULL)
+ return (ELFSIGN_FAILED);
+
+ (void) memcpy(new_d_buf, data->d_buf, old_size);
+ (void) strlcpy(new_d_buf + old_size, elf_section,
+ new_size - old_size);
+ data->d_buf = new_d_buf;
+ data->d_size = new_size;
+ data->d_align = 1;
+ /*
+ * Add the section name passed in to the end of the file.
+ * Initialize the fields in the Section Header that
+ * libelf will not fill in.
+ */
+ if ((sig_scn = elf_newscn(ess->es_elf)) == 0) {
+ cryptodebug("elf_newscn() failed: %s",
+ elf_errmsg(-1));
+ return (ELFSIGN_FAILED);
+ }
+ if (gelf_getshdr(sig_scn, &shdr) == 0) {
+ cryptodebug("gelf_getshdr() failed: %s",
+ elf_errmsg(-1));
+ return (ELFSIGN_FAILED);
+ }
+ shdr.sh_name = old_size;
+ shdr.sh_type = SHT_SUNW_SIGNATURE;
+ shdr.sh_flags = SHF_EXCLUDE;
+ shdr.sh_addr = 0;
+ shdr.sh_link = 0;
+ shdr.sh_info = 0;
+ shdr.sh_size = 0;
+ shdr.sh_offset = sig_offset;
+ shdr.sh_addralign = 1;
+
+ /*
+ * Flush the changes to the underlying elf32 or elf64
+ * section header.
+ */
+ if (gelf_update_shdr(sig_scn, &shdr) == 0) {
+ cryptodebug("gelf_update_shdr failed");
+ return (ELFSIGN_FAILED);
+ }
+
+ if ((data = elf_newdata(sig_scn)) == NULL) {
+ cryptodebug("can't add elf data area for %s: %s",
+ elf_section, elf_errmsg(-1));
+ return (ELFSIGN_FAILED);
+ }
+ if (elfsign_adjustoffsets(ess, scn,
+ old_size + strlen(elf_section) + 1) != ELFSIGN_SUCCESS) {
+ cryptodebug("can't adjust for new section name %s",
+ elf_section);
+ return (ELFSIGN_FAILED);
+ }
+ } else {
+ if (sig_scn == NULL) {
+ cryptodebug("can't find signature section");
+ *fsspp = NULL;
+ return (ELFSIGN_NOTSIGNED);
+ }
+ if ((data = elf_getdata(sig_scn, NULL)) == 0) {
+ cryptodebug("can't get section data for %s",
+ elf_section);
+ return (ELFSIGN_FAILED);
+ }
+ }
+
+ if (ES_ACTISUPDATE(action)) {
+ fssize = offsetof(struct filesignatures, _u1);
+ if (*fsspp != NULL) {
+ fsgp = &(*fsspp)->filesig_sig;
+ for (fscnt = 0; fscnt < (*fsspp)->filesig_cnt;
+ fscnt++) {
+ fsgpnext = filesig_next(fsgp);
+ fssize += (char *)(fsgpnext) - (char *)(fsgp);
+ fsgp = fsgpnext;
+ }
+ }
+ if (shdr.sh_addr != 0) {
+ cryptodebug("section %s is part of a loadable segment, "
+ "it cannot be changed.\n", elf_section);
+ return (ELFSIGN_FAILED);
+ }
+ if ((data->d_buf = malloc(fssize)) == NULL)
+ return (ELFSIGN_FAILED);
+ if (*fsspp != NULL) {
+ (void) memcpy(data->d_buf, *fsspp, fssize);
+ (void) elfsign_switch(ess,
+ (struct filesignatures *)data->d_buf, action);
+ }
+ data->d_size = fssize;
+ data->d_align = 1;
+ data->d_type = ELF_T_BYTE;
+ cryptodebug("elfsign_signature: data->d_size = %d",
+ data->d_size);
+ if (elfsign_adjustoffsets(ess, sig_scn, fssize) !=
+ ELFSIGN_SUCCESS) {
+ cryptodebug("can't adjust for revised signature "
+ "section contents");
+ return (ELFSIGN_FAILED);
+ }
+ } else {
+ *fsspp = malloc(data->d_size);
+ if (*fsspp == NULL)
+ return (ELFSIGN_FAILED);
+ (void) memcpy(*fsspp, data->d_buf, data->d_size);
+ if (elfsign_switch(ess, *fsspp, ES_GET) != ELFSIGN_SUCCESS) {
+ free(*fsspp);
+ *fsspp = NULL;
+ return (ELFSIGN_FAILED);
+ }
+ *fslen = data->d_size;
+ }
+
+ return (ELFSIGN_SUCCESS);
+}
+
+static ELFsign_status_t
+elfsign_adjustoffsets(ELFsign_t ess, Elf_Scn *scn, uint64_t new_size)
+{
+ GElf_Ehdr elfehdr;
+ GElf_Shdr shdr;
+ uint64_t prev_end, scn_offset;
+ char *name;
+ Elf_Scn *scnp;
+ Elf_Data *data;
+ ELFsign_status_t retval = ELFSIGN_FAILED;
+ struct scninfo {
+ struct scninfo *scni_next;
+ Elf_Scn *scni_scn;
+ uint64_t scni_offset;
+ } *scnip = NULL, *tmpscnip, **scnipp;
+
+ /* get the size of the current section */
+ if (gelf_getshdr(scn, &shdr) == NULL)
+ return (ELFSIGN_FAILED);
+ if (shdr.sh_size == new_size)
+ return (ELFSIGN_SUCCESS);
+ scn_offset = shdr.sh_offset;
+ name = elf_strptr(ess->es_elf, ess->es_shstrndx,
+ (size_t)shdr.sh_name);
+ if (shdr.sh_flags & SHF_ALLOC && ess->es_has_phdr) {
+ cryptodebug("elfsign_adjustoffsets: "
+ "can't move allocated section %s", name ? name : "NULL");
+ return (ELFSIGN_FAILED);
+ }
+
+ /* resize the desired section */
+ cryptodebug("elfsign_adjustoffsets: "
+ "resizing %s at 0x%llx from 0x%llx to 0x%llx",
+ name ? name : "NULL", shdr.sh_offset, shdr.sh_size, new_size);
+ shdr.sh_size = new_size;
+ if (gelf_update_shdr(scn, &shdr) == 0) {
+ cryptodebug("gelf_update_shdr failed");
+ goto bad;
+ }
+ prev_end = shdr.sh_offset + shdr.sh_size;
+
+ /*
+ * find sections whose data follows the changed section
+ * must scan all sections since section data may not
+ * be in same order as section headers
+ */
+ scnp = elf_getscn(ess->es_elf, 0); /* "seek" to start */
+ while ((scnp = elf_nextscn(ess->es_elf, scnp)) != NULL) {
+ if (gelf_getshdr(scnp, &shdr) == NULL)
+ goto bad;
+ if (shdr.sh_offset <= scn_offset)
+ continue;
+ name = elf_strptr(ess->es_elf, ess->es_shstrndx,
+ (size_t)shdr.sh_name);
+ if (shdr.sh_flags & SHF_ALLOC && ess->es_has_phdr) {
+ if (shdr.sh_type == SHT_NOBITS) {
+ /* .bss can occasionally overlap .shrtab */
+ continue;
+ }
+ cryptodebug("elfsign_adjustoffsets: "
+ "can't move allocated section %s",
+ name ? name : "NULL");
+ goto bad;
+ }
+ /*
+ * force reading of data to memory image
+ */
+ data = NULL;
+ while ((data = elf_rawdata(scnp, data)) != NULL)
+ ;
+ /*
+ * capture section information
+ * insert into list in order of sh_offset
+ */
+ cryptodebug("elfsign_adjustoffsets: "
+ "may have to adjust section %s, offset 0x%llx",
+ name ? name : "NULL", shdr.sh_offset);
+ tmpscnip = (struct scninfo *)malloc(sizeof (struct scninfo));
+ if (tmpscnip == NULL) {
+ cryptodebug("elfsign_adjustoffsets: "
+ "memory allocation failure");
+ goto bad;
+ }
+ tmpscnip->scni_scn = scnp;
+ tmpscnip->scni_offset = shdr.sh_offset;
+ for (scnipp = &scnip; *scnipp != NULL;
+ scnipp = &(*scnipp)->scni_next) {
+ if ((*scnipp)->scni_offset > tmpscnip->scni_offset)
+ break;
+ }
+ tmpscnip->scni_next = *scnipp;
+ *scnipp = tmpscnip;
+ }
+
+ /* move following sections as necessary */
+ for (tmpscnip = scnip; tmpscnip != NULL;
+ tmpscnip = tmpscnip->scni_next) {
+ scnp = tmpscnip->scni_scn;
+ if (gelf_getshdr(scnp, &shdr) == NULL) {
+ cryptodebug("elfsign_adjustoffsets: "
+ "elf_getshdr for section %d failed",
+ elf_ndxscn(scnp));
+ goto bad;
+ }
+ if (shdr.sh_offset >= prev_end)
+ break;
+ prev_end = (prev_end + shdr.sh_addralign - 1) &
+ (-shdr.sh_addralign);
+ name = elf_strptr(ess->es_elf, ess->es_shstrndx,
+ (size_t)shdr.sh_name);
+ cryptodebug("elfsign_adjustoffsets: "
+ "moving %s size 0x%llx from 0x%llx to 0x%llx",
+ name ? name : "NULL", shdr.sh_size,
+ shdr.sh_offset, prev_end);
+ shdr.sh_offset = prev_end;
+ if (gelf_update_shdr(scnp, &shdr) == 0) {
+ cryptodebug("gelf_update_shdr failed");
+ goto bad;
+ }
+ prev_end = shdr.sh_offset + shdr.sh_size;
+ }
+
+ /*
+ * adjust section header offset in elf header
+ */
+ if (gelf_getehdr(ess->es_elf, &elfehdr) == NULL) {
+ cryptodebug("elf_getehdr() failed: %s", elf_errmsg(-1));
+ goto bad;
+ }
+ if (elfehdr.e_shoff < prev_end) {
+ if (ess->es_ei_class == ELFCLASS32)
+ prev_end = (prev_end + ELF32_FSZ_OFF - 1) &
+ (-ELF32_FSZ_OFF);
+ else if (ess->es_ei_class == ELFCLASS64)
+ prev_end = (prev_end + ELF64_FSZ_OFF - 1) &
+ (-ELF64_FSZ_OFF);
+ cryptodebug("elfsign_adjustoffsets: "
+ "move sh_off from 0x%llx to 0x%llx",
+ elfehdr.e_shoff, prev_end);
+ elfehdr.e_shoff = prev_end;
+ if (gelf_update_ehdr(ess->es_elf, &elfehdr) == 0) {
+ cryptodebug("elf_update_ehdr() failed: %s",
+ elf_errmsg(-1));
+ goto bad;
+ }
+ }
+
+ retval = ELFSIGN_SUCCESS;
+
+bad:
+ while (scnip != NULL) {
+ tmpscnip = scnip->scni_next;
+ free(scnip);
+ scnip = tmpscnip;
+ }
+ return (retval);
+}
+
+struct filesignatures *
+elfsign_insert_dso(ELFsign_t ess,
+ struct filesignatures *fssp,
+ const char *dn,
+ int dn_len,
+ const uchar_t *sig,
+ int sig_len,
+ const char *oid,
+ int oid_len)
+{
+ return (filesig_insert_dso(fssp, ess->es_version, dn, dn_len,
+ sig, sig_len, oid, oid_len));
+}
+
+/*ARGSUSED*/
+filesig_vers_t
+elfsign_extract_sig(ELFsign_t ess,
+ struct filesignatures *fssp,
+ uchar_t *sig,
+ size_t *sig_len)
+{
+ struct filesig_extraction fsx;
+ filesig_vers_t version;
+
+ if (fssp == NULL)
+ return (FILESIG_UNKNOWN);
+ if (fssp->filesig_cnt != 1)
+ return (FILESIG_UNKNOWN);
+ version = filesig_extract(&fssp->filesig_sig, &fsx);
+ switch (version) {
+ case FILESIG_VERSION1:
+ case FILESIG_VERSION2:
+ case FILESIG_VERSION3:
+ case FILESIG_VERSION4:
+ if (*sig_len >= fsx.fsx_sig_len) {
+ (void) memcpy((char *)sig, (char *)fsx.fsx_signature,
+ *sig_len);
+ *sig_len = fsx.fsx_sig_len;
+ } else
+ version = FILESIG_UNKNOWN;
+ break;
+ default:
+ version = FILESIG_UNKNOWN;
+ break;
+ }
+
+ if (ess->es_version == FILESIG_UNKNOWN) {
+ ess->es_version = version;
+ }
+
+ return (version);
+}
+
+static ELFsign_status_t
+elfsign_hash_common(ELFsign_t ess, uchar_t *hash, size_t *hash_len,
+ boolean_t hash_mem_resident)
+{
+ Elf_Scn *scn = NULL;
+ ELFsign_status_t elfstat;
+ GElf_Shdr shdr;
+ SHA1_CTX ctx;
+
+ /* The buffer must be large enough to hold the hash */
+ if (*hash_len < SHA1_DIGEST_LENGTH)
+ return (ELFSIGN_FAILED);
+
+ bzero(hash, *hash_len);
+
+ /* Initialize the digest session */
+ SHA1Init(&ctx);
+
+ scn = elf_getscn(ess->es_elf, 0); /* "seek" to start */
+ (void) elf_errno();
+ while ((scn = elf_nextscn(ess->es_elf, scn)) != 0) {
+ char *name = NULL;
+ Elf_Data *data = NULL;
+
+ if (gelf_getshdr(scn, &shdr) == NULL) {
+ elfstat = ELFSIGN_FAILED;
+ goto done;
+ }
+
+ name = elf_strptr(ess->es_elf, ess->es_shstrndx,
+ (size_t)shdr.sh_name);
+ if (name == NULL)
+ name = "NULL";
+
+ if (!hash_mem_resident &&
+ (ess->es_version == FILESIG_VERSION1 ||
+ ess->es_version == FILESIG_VERSION3)) {
+ /*
+ * skip the signature section only
+ */
+ if (shdr.sh_type == SHT_SUNW_SIGNATURE) {
+ cryptodebug("elfsign_hash: skipping %s", name);
+ continue;
+ }
+ } else if (!(shdr.sh_flags & SHF_ALLOC)) {
+ /*
+ * select only memory resident sections
+ */
+ cryptodebug("elfsign_hash: skipping %s", name);
+ continue;
+ }
+
+ /*
+ * throw this section into the hash
+ * use elf_rawdata for endian-independence
+ * use elf_getdata to get update of .shstrtab
+ */
+ while ((data = (shdr.sh_type == SHT_STRTAB ?
+ elf_getdata(scn, data) : elf_rawdata(scn, data))) != NULL) {
+ if (data->d_buf == NULL) {
+ cryptodebug("elfsign_hash: %s has NULL data",
+ name);
+ continue;
+ }
+ cryptodebug("elfsign_hash: updating hash "
+ "with %s data size=%d", name, data->d_size);
+ SHA1Update(&ctx, data->d_buf, data->d_size);
+ }
+ }
+ if (elf_errmsg(0) != NULL) {
+ cryptodebug("elfsign_hash: %s", elf_errmsg(-1));
+ elfstat = ELFSIGN_FAILED;
+ goto done;
+ }
+
+ SHA1Final(hash, &ctx);
+ *hash_len = SHA1_DIGEST_LENGTH;
+ { /* DEBUG START */
+ const int hashstr_len = (*hash_len) * 2 + 1;
+ char *hashstr = malloc(hashstr_len);
+
+ if (hashstr != NULL) {
+ tohexstr(hash, *hash_len, hashstr, hashstr_len);
+ cryptodebug("hash value is: %s", hashstr);
+ free(hashstr);
+ }
+ } /* DEBUG END */
+ elfstat = ELFSIGN_SUCCESS;
+done:
+ return (elfstat);
+}
+
+/*
+ * elfsign_hash - return the hash of the ELF sections affecting execution.
+ *
+ * IN: ess, hash_len
+ * OUT: hash, hash_len
+ */
+ELFsign_status_t
+elfsign_hash(ELFsign_t ess, uchar_t *hash, size_t *hash_len)
+{
+ return (elfsign_hash_common(ess, hash, hash_len, B_FALSE));
+}
+
+/*
+ * elfsign_hash_mem_resident - return the hash of the ELF sections
+ * with only memory resident sections.
+ *
+ * IN: ess, hash_len
+ * OUT: hash, hash_len
+ */
+ELFsign_status_t
+elfsign_hash_mem_resident(ELFsign_t ess, uchar_t *hash, size_t *hash_len)
+{
+ return (elfsign_hash_common(ess, hash, hash_len, B_TRUE));
+}
+
+/*
+ * elfsign_hash_esa = return the hash of the esa_buffer
+ *
+ * IN: ess, esa_buf, esa_buf_len, hash_len
+ * OUT: hash, hash_len
+ */
+ELFsign_status_t
+elfsign_hash_esa(ELFsign_t ess, uchar_t *esa_buf, size_t esa_buf_len,
+ uchar_t **hash, size_t *hash_len)
+{
+ SHA1_CTX ctx;
+
+ cryptodebug("esa_hash version is: %s",
+ version_to_str(ess->es_version));
+ if (ess->es_version <= FILESIG_VERSION2) {
+ /*
+ * old rsa_md5_sha1 format
+ * signed with MD5 digest, just pass full esa_buf
+ */
+ *hash = esa_buf;
+ *hash_len = esa_buf_len;
+ return (ELFSIGN_SUCCESS);
+ }
+
+ if (*hash_len < SHA1_DIGEST_LENGTH)
+ return (ELFSIGN_FAILED);
+
+ bzero(*hash, *hash_len);
+ SHA1Init(&ctx);
+ SHA1Update(&ctx, esa_buf, esa_buf_len);
+ SHA1Final(*hash, &ctx);
+ *hash_len = SHA1_DIGEST_LENGTH;
+
+ { /* DEBUG START */
+ const int hashstr_len = (*hash_len) * 2 + 1;
+ char *hashstr = malloc(hashstr_len);
+
+ if (hashstr != NULL) {
+ tohexstr(*hash, *hash_len, hashstr, hashstr_len);
+ cryptodebug("esa_hash value is: %s", hashstr);
+ free(hashstr);
+ }
+ } /* DEBUG END */
+
+ return (ELFSIGN_SUCCESS);
+}
+
+/*
+ * elfsign_verify_signature - Verify the signature of the ELF object.
+ *
+ * IN: ess
+ * OUT: esipp
+ * RETURNS:
+ * ELFsign_status_t
+ */
+ELFsign_status_t
+elfsign_verify_signature(ELFsign_t ess, struct ELFsign_sig_info **esipp)
+{
+ ELFsign_status_t ret = ELFSIGN_FAILED;
+ struct filesignatures *fssp;
+ struct filesig *fsgp;
+ size_t fslen;
+ struct filesig_extraction fsx;
+ uchar_t hash[SIG_MAX_LENGTH];
+ size_t hash_len;
+ ELFCert_t cert = NULL;
+ int sigcnt;
+ int nocert = 0;
+ struct ELFsign_sig_info *esip = NULL;
+
+ if (esipp != NULL) {
+ esip = (struct ELFsign_sig_info *)
+ calloc(1, sizeof (struct ELFsign_sig_info));
+ *esipp = esip;
+ }
+
+ /*
+ * Find out which cert we need, based on who signed the ELF object
+ */
+ if (elfsign_signatures(ess, &fssp, &fslen, ES_GET) != ELFSIGN_SUCCESS) {
+ return (ELFSIGN_NOTSIGNED);
+ }
+
+ if (fssp->filesig_cnt < 1) {
+ ret = ELFSIGN_FAILED;
+ goto cleanup;
+ }
+
+ fsgp = &fssp->filesig_sig;
+
+ /*
+ * Scan the signature block, looking for a verifiable signature
+ */
+ for (sigcnt = 0; sigcnt < fssp->filesig_cnt;
+ sigcnt++, fsgp = filesig_next(fsgp)) {
+ ess->es_version = filesig_extract(fsgp, &fsx);
+ cryptodebug("elfsign_verify_signature: version=%s",
+ version_to_str(ess->es_version));
+ switch (ess->es_version) {
+ case FILESIG_VERSION1:
+ case FILESIG_VERSION2:
+ case FILESIG_VERSION3:
+ case FILESIG_VERSION4:
+ break;
+ default:
+ ret = ELFSIGN_FAILED;
+ goto cleanup;
+ }
+
+ cryptodebug("elfsign_verify_signature: signer_DN=\"%s\"",
+ fsx.fsx_signer_DN);
+ cryptodebug("elfsign_verify_signature: algorithmOID=\"%s\"",
+ fsx.fsx_sig_oid);
+ /* return signer DN if requested */
+ if (esipp != NULL) {
+ esip->esi_format = fsx.fsx_format;
+ if (esip->esi_signer != NULL)
+ free(esip->esi_signer);
+ esip->esi_signer = strdup(fsx.fsx_signer_DN);
+ esip->esi_time = fsx.fsx_time;
+ }
+
+ /*
+ * look for certificate
+ */
+ if (cert != NULL)
+ elfcertlib_releasecert(ess, cert);
+
+ /*
+ * skip unfound certificates
+ */
+ if (!elfcertlib_getcert(ess, ess->es_certpath,
+ fsx.fsx_signer_DN, &cert, ess->es_action)) {
+ cryptodebug("unable to find certificate "
+ "with DN=\"%s\" for %s",
+ fsx.fsx_signer_DN, ess->es_pathname);
+ nocert++;
+ continue;
+ }
+
+ /*
+ * skip unverified certificates
+ * force verification of crypto certs
+ */
+ if ((ess->es_action == ES_GET_CRYPTO ||
+ strstr(fsx.fsx_signer_DN, ELFSIGN_CRYPTO)) &&
+ !elfcertlib_verifycert(ess, cert)) {
+ cryptodebug("elfsign_verify_signature: invalid cert");
+ nocert++;
+ continue;
+ }
+
+ /*
+ * At this time the only sha1WithRSAEncryption is supported,
+ * so check that is what we have and skip with anything else.
+ */
+ if (strcmp(fsx.fsx_sig_oid, OID_sha1WithRSAEncryption) != 0) {
+ continue;
+ }
+
+ nocert = 0;
+ /*
+ * compute file hash
+ */
+ hash_len = sizeof (hash);
+ if (elfsign_hash(ess, hash, &hash_len) != ELFSIGN_SUCCESS) {
+ cryptodebug("elfsign_verify_signature:"
+ " elfsign_hash failed");
+ ret = ELFSIGN_FAILED;
+ break;
+ }
+
+ { /* DEBUG START */
+ const int sigstr_len = fsx.fsx_sig_len * 2 + 1;
+ char *sigstr = malloc(sigstr_len);
+
+ if (sigstr != NULL) {
+ tohexstr(fsx.fsx_signature, fsx.fsx_sig_len,
+ sigstr, sigstr_len);
+ cryptodebug("signature value is: %s", sigstr);
+ free(sigstr);
+ }
+ } /* DEBUG END */
+
+ if (elfcertlib_verifysig(ess, cert,
+ fsx.fsx_signature, fsx.fsx_sig_len, hash, hash_len)) {
+ if (ess->es_sigvercallback)
+ (ess->es_sigvercallback)
+ (ess->es_callbackctx, fssp, fslen, cert);
+ /*
+ * The signature is verified!
+ * Check if this is a restricted provider
+ */
+ if (strstr(fsx.fsx_signer_DN, USAGELIMITED) == NULL)
+ ret = ELFSIGN_SUCCESS;
+ else {
+ cryptodebug("DN is tagged for usagelimited");
+ ret = elfsign_verify_esa(ess,
+ fsx.fsx_signature, fsx.fsx_sig_len);
+ }
+ break;
+ }
+
+ cryptodebug("elfsign_verify_signature: invalid signature");
+ }
+
+cleanup:
+ if (cert != NULL)
+ elfcertlib_releasecert(ess, cert);
+
+ free(fssp);
+ if (ret == ELFSIGN_FAILED && nocert)
+ ret = ELFSIGN_INVALID_CERTPATH;
+ return (ret);
+}
+
+/*
+ * Verify the contents of the .esa file, as per Jumbo export control
+ * document. Logic in this function should remain unchanged, unless
+ * a misinterpretation of the jumbo case was found or if there are
+ * changes in export regulations necessitating a change.
+ *
+ * If the .esa file exists, but is somehow corrupted, we just return
+ * that this is restricted. This is consistent with the Jumbo export
+ * case covering this library and other compenents of ON. Do not change
+ * this logic without consulting export control.
+ *
+ * Please see do_gen_esa() for a description of the esa file format.
+ *
+ */
+static ELFsign_status_t
+elfsign_verify_esa(ELFsign_t ess, uchar_t *orig_sig, size_t orig_sig_len)
+{
+ ELFsign_status_t ret = ELFSIGN_RESTRICTED;
+ char *elfobj_esa = NULL;
+ size_t elfobj_esa_len;
+ int esa_fd = -1;
+ size_t esa_buf_len = 0;
+ uchar_t *main_sig;
+ size_t main_sig_len = 0;
+ uchar_t hash[SIG_MAX_LENGTH], *hash_ptr = hash;
+ size_t hash_len = SIG_MAX_LENGTH;
+ char *esa_dn = NULL;
+ size_t esa_dn_len = 0;
+ uchar_t *esa_sig;
+ size_t esa_sig_len = 0;
+ uchar_t *esa_file_buffer = NULL, *esa_file_ptr;
+ struct stat statbuf;
+ ELFCert_t cert = NULL;
+
+ cryptodebug("elfsign_verify_esa");
+
+ /* does the activation file exist? */
+ elfobj_esa_len = strlen(ess->es_pathname) + ESA_LEN + 1;
+ elfobj_esa = malloc(elfobj_esa_len);
+ if (elfobj_esa == NULL) {
+ cryptoerror(LOG_STDERR,
+ gettext("Unable to allocate buffer for esa filename."));
+ goto cleanup;
+ }
+
+ (void) strlcpy(elfobj_esa, ess->es_pathname, elfobj_esa_len);
+ (void) strlcat(elfobj_esa, ESA, elfobj_esa_len);
+
+ if ((esa_fd = open(elfobj_esa, O_RDONLY|O_NONBLOCK)) == -1) {
+ cryptodebug("No .esa file was found, or it was unreadable");
+ goto cleanup;
+ }
+
+ cryptodebug("Reading contents of esa file %s", elfobj_esa);
+
+ if (fstat(esa_fd, &statbuf) == -1) {
+ cryptoerror(LOG_STDERR,
+ gettext("Can't stat %s"), elfobj_esa);
+ goto cleanup;
+ }
+
+ /*
+ * mmap the buffer to save on syscalls
+ */
+ esa_file_buffer = (uchar_t *)mmap(NULL, statbuf.st_size, PROT_READ,
+ MAP_PRIVATE, esa_fd, 0);
+
+ if (esa_file_buffer == MAP_FAILED) {
+ cryptoerror(LOG_STDERR,
+ gettext("Unable to mmap file to a buffer for %s."),
+ elfobj_esa);
+ goto cleanup;
+ }
+
+ esa_file_ptr = esa_file_buffer;
+ elfsign_buffer_len(ess, &main_sig_len, esa_file_ptr, ES_GET);
+ esa_file_ptr += sizeof (uint32_t);
+ cryptodebug("Contents of esa file: main_sig_len=%d", main_sig_len);
+ main_sig = esa_file_ptr;
+
+ esa_file_ptr += main_sig_len;
+
+ /* verify .esa main signature versus original signature */
+ if (main_sig_len != orig_sig_len ||
+ memcmp(main_sig, orig_sig, orig_sig_len) != 0) {
+ cryptoerror(LOG_STDERR,
+ gettext("Unable to match original signature from %s."),
+ elfobj_esa);
+ goto cleanup;
+ }
+
+ elfsign_buffer_len(ess, &esa_dn_len, esa_file_ptr, ES_GET);
+ esa_file_ptr += sizeof (uint32_t);
+ cryptodebug("Contents of esa file: esa_dn_len=%d", esa_dn_len);
+
+ esa_dn = malloc(esa_dn_len + 1);
+ if (esa_dn == NULL) {
+ cryptoerror(LOG_ERR,
+ gettext("Unable to allocate memory for dn buffer."));
+ goto cleanup;
+ }
+ (void) memcpy(esa_dn, esa_file_ptr, esa_dn_len);
+ esa_dn[esa_dn_len] = '\0';
+ esa_file_ptr += esa_dn_len;
+ cryptodebug("Contents of esa file: esa_dn=%s", esa_dn);
+
+ elfsign_buffer_len(ess, &esa_sig_len, esa_file_ptr, ES_GET);
+ esa_file_ptr += sizeof (uint32_t);
+ cryptodebug("Contents of esa file: esa_sig_len=%d", esa_sig_len);
+
+ esa_sig = esa_file_ptr;
+
+ cryptodebug("Read esa contents, now verifying");
+
+ /*
+ * dn used in .esa file should not be limited.
+ */
+ if (strstr(esa_dn, USAGELIMITED) != NULL) {
+ cryptoerror(LOG_ERR,
+ gettext("DN for .esa file is tagged as limited for %s.\n"
+ "Activation files should only be tagged as unlimited.\n"
+ "Please contact vendor for this provider"),
+ ess->es_pathname);
+ goto cleanup;
+ }
+
+ if (!elfcertlib_getcert(ess, ess->es_certpath, esa_dn, &cert,
+ ess->es_action)) {
+ cryptodebug(gettext("unable to find certificate "
+ "with DN=\"%s\" for %s"),
+ esa_dn, ess->es_pathname);
+ goto cleanup;
+ }
+
+ /*
+ * Since we've already matched the original signature
+ * and the main file signature, we can just verify the esa signature
+ * against the main file signature.
+ */
+ esa_buf_len = sizeof (uint32_t) + main_sig_len;
+
+ if (elfsign_hash_esa(ess, esa_file_buffer, esa_buf_len,
+ &hash_ptr, &hash_len) != ELFSIGN_SUCCESS) {
+ cryptoerror(LOG_STDERR,
+ gettext("Unable to hash activation contents."));
+ goto cleanup;
+ }
+
+
+ if (!elfcertlib_verifysig(ess, cert, esa_sig, esa_sig_len,
+ hash_ptr, hash_len)) {
+ cryptoerror(LOG_STDERR,
+ gettext("Unable to verify .esa contents for %s"),
+ ess->es_pathname);
+ goto cleanup;
+ }
+
+ cryptodebug("Verified esa contents");
+ if (ess->es_sigvercallback)
+ (ess->es_sigvercallback) (ess->es_callbackctx,
+ esa_file_buffer, statbuf.st_size, cert);
+
+ /*
+ * validate the certificate used to sign the activation file
+ */
+ if (!elfcertlib_verifycert(ess, cert)) {
+ cryptoerror(LOG_STDERR,
+ gettext("Unable to verify .esa certificate %s for %s"),
+ esa_dn, ess->es_pathname);
+ goto cleanup;
+ }
+
+ cryptodebug("Verified esa certificate");
+ ret = ELFSIGN_SUCCESS;
+
+cleanup:
+ if (elfobj_esa != NULL)
+ free(elfobj_esa);
+
+ if (esa_fd != -1)
+ (void) close(esa_fd);
+
+ if (esa_file_buffer != NULL)
+ (void) munmap((caddr_t)esa_file_buffer, statbuf.st_size);
+
+ if (esa_dn != NULL)
+ free(esa_dn);
+
+ if (cert != NULL)
+ elfcertlib_releasecert(ess, cert);
+
+ return (ret);
+}
+
+static uint32_t
+elfsign_switch_uint32(uint32_t i)
+{
+ return (((i & 0xff) << 24) | ((i & 0xff00) << 8) |
+ ((i >> 8) & 0xff00) | ((i >> 24) & 0xff));
+}
+
+static uint64_t
+elfsign_switch_uint64(uint64_t i)
+{
+ return (((uint64_t)elfsign_switch_uint32(i) << 32) |
+ (elfsign_switch_uint32(i >> 32)));
+}
+
+/*
+ * If appropriate, switch the endianness of the filesignatures structure
+ * Examine the structure only when it is in native endianness
+ */
+static ELFsign_status_t
+elfsign_switch(ELFsign_t ess, struct filesignatures *fssp,
+ enum ES_ACTION action)
+{
+ int fscnt;
+ filesig_vers_t version;
+ struct filesig *fsgp, *fsgpnext;
+
+ if (ess->es_same_endian)
+ return (ELFSIGN_SUCCESS);
+
+ if (ES_ACTISUPDATE(action))
+ fscnt = fssp->filesig_cnt;
+ fssp->filesig_cnt = elfsign_switch_uint32(fssp->filesig_cnt);
+ if (!ES_ACTISUPDATE(action))
+ fscnt = fssp->filesig_cnt;
+
+ fsgp = &(fssp)->filesig_sig;
+ for (; fscnt > 0; fscnt--, fsgp = fsgpnext) {
+ if (ES_ACTISUPDATE(action)) {
+ version = fsgp->filesig_version;
+ fsgpnext = filesig_next(fsgp);
+ }
+ fsgp->filesig_size =
+ elfsign_switch_uint32(fsgp->filesig_size);
+ fsgp->filesig_version =
+ elfsign_switch_uint32(fsgp->filesig_version);
+ if (!ES_ACTISUPDATE(action)) {
+ version = fsgp->filesig_version;
+ fsgpnext = filesig_next(fsgp);
+ }
+ switch (version) {
+ case FILESIG_VERSION1:
+ case FILESIG_VERSION2:
+ fsgp->filesig_v1_dnsize =
+ elfsign_switch_uint32(fsgp->filesig_v1_dnsize);
+ fsgp->filesig_v1_sigsize =
+ elfsign_switch_uint32(fsgp->filesig_v1_sigsize);
+ fsgp->filesig_v1_oidsize =
+ elfsign_switch_uint32(fsgp->filesig_v1_oidsize);
+ break;
+ case FILESIG_VERSION3:
+ case FILESIG_VERSION4:
+ fsgp->filesig_v3_time =
+ elfsign_switch_uint64(fsgp->filesig_v3_time);
+ fsgp->filesig_v3_dnsize =
+ elfsign_switch_uint32(fsgp->filesig_v3_dnsize);
+ fsgp->filesig_v3_sigsize =
+ elfsign_switch_uint32(fsgp->filesig_v3_sigsize);
+ fsgp->filesig_v3_oidsize =
+ elfsign_switch_uint32(fsgp->filesig_v3_oidsize);
+ break;
+ default:
+ cryptodebug("elfsign_switch: failed");
+ return (ELFSIGN_FAILED);
+ }
+ }
+ return (ELFSIGN_SUCCESS);
+}
+
+/*
+ * get/put an integer value from/to a buffer, possibly of opposite endianness
+ */
+void
+elfsign_buffer_len(ELFsign_t ess, size_t *ip, uchar_t *cp,
+ enum ES_ACTION action)
+{
+ uint32_t tmp;
+
+ if (!ES_ACTISUPDATE(action)) {
+ /* fetch integer from buffer */
+ (void) memcpy(&tmp, cp, sizeof (tmp));
+ if (!ess->es_same_endian) {
+ tmp = elfsign_switch_uint32(tmp);
+ }
+ *ip = tmp;
+ } else {
+ /* put integer into buffer */
+ tmp = *ip;
+ if (!ess->es_same_endian) {
+ tmp = elfsign_switch_uint32(tmp);
+ }
+ (void) memcpy(cp, &tmp, sizeof (tmp));
+ }
+}
+
+char const *
+elfsign_strerror(ELFsign_status_t elferror)
+{
+ char const *msg = NULL;
+
+ switch (elferror) {
+ case ELFSIGN_SUCCESS:
+ msg = gettext("sign or verify of ELF object succeeded");
+ break;
+ case ELFSIGN_FAILED:
+ msg = gettext("sign or verify of ELF object failed");
+ break;
+ case ELFSIGN_NOTSIGNED:
+ msg = gettext("ELF object not signed");
+ break;
+ case ELFSIGN_INVALID_CERTPATH:
+ msg = gettext("cannot access certificate");
+ break;
+ case ELFSIGN_INVALID_ELFOBJ:
+ msg = gettext("unable to open as an ELF object");
+ break;
+ case ELFSIGN_RESTRICTED:
+ msg = gettext("ELF object is restricted");
+ break;
+ case ELFSIGN_UNKNOWN:
+ default:
+ msg = gettext("Unknown error");
+ break;
+ }
+
+ return (msg);
+}
+
+boolean_t
+elfsign_sig_info(struct filesignatures *fssp, struct ELFsign_sig_info **esipp)
+{
+ struct filesig_extraction fsx;
+ struct ELFsign_sig_info *esip;
+
+ esip = (struct ELFsign_sig_info *)
+ calloc(1, sizeof (struct ELFsign_sig_info));
+ *esipp = esip;
+ if (esip == NULL)
+ return (B_FALSE);
+
+ switch (filesig_extract(&fssp->filesig_sig, &fsx)) {
+ case FILESIG_VERSION1:
+ case FILESIG_VERSION2:
+ case FILESIG_VERSION3:
+ case FILESIG_VERSION4:
+ esip->esi_format = fsx.fsx_format;
+ esip->esi_signer = strdup(fsx.fsx_signer_DN);
+ esip->esi_time = fsx.fsx_time;
+ break;
+ default:
+ free(esip);
+ *esipp = NULL;
+ }
+
+ return (*esipp != NULL);
+}
+
+void
+elfsign_sig_info_free(struct ELFsign_sig_info *esip)
+{
+ if (esip != NULL) {
+ free(esip->esi_signer);
+ free(esip);
+ }
+}