OpenSolaris_b135/lib/libelfsign/common/elfsignlib.c

Compare this file to the similar file:
Show the results in this format:

/*
 * 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.
 */

#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:
	case ES_GET_FIPS140:
		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)
		return (ELFSIGN_INVALID_CERTPATH);

	if ((ess->es_certpath = strdup(certpath)) == NULL)
		return (ELFSIGN_FAILED);

	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 ||
		    ess->es_action == ES_GET_FIPS140 ||
		    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);
	}
}