elfsignlib.c revision 5c0175258354931b92aa8f3c302005abc001f1f9
/*
* 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
* 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 <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 <cryptoutil.h>
#include <sha1.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";
struct filesig_extraction {
char *fsx_format;
char fsx_signer_DN[ELFCERT_MAX_DN_LEN];
char fsx_sig_oid[100];
};
static char *
{
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);
}
/*
* composed of signer DN, signature, and OID.
*/
static struct filesignatures *
const char *dn,
int dn_len,
int sig_len,
const char *oid,
int oid_len)
{
char *fsdatap;
/*
* 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.
*/
}
/*
* for now, always insert a single-signature signature block
*/
fssp = (struct filesignatures *)
return (fssp);
switch (version) {
case FILESIG_VERSION1:
case FILESIG_VERSION2:
break;
case FILESIG_VERSION3:
case FILESIG_VERSION4:
break;
default:
cryptodebug("filesig_insert_dso: unknown version: %d",
version);
return (NULL);
}
return (fssp);
}
/*
* filesig_extract - extract filesig structure to internal form
*/
static filesig_vers_t
{
char *fsdp;
sizeof (data_var) - 1); \
cryptodebug("filesig_extract: version=%s",
switch (fsxp->fsx_version) {
case FILESIG_VERSION1:
case FILESIG_VERSION2:
/*
* extract VERSION1 DN, signature, and OID
*/
break;
case FILESIG_VERSION3:
case FILESIG_VERSION4:
break;
default:
break;
}
return (fsxp->fsx_version);
}
{
int oflags = 0;
short l_type;
union {
char c[2];
short s;
} uorder;
char *ident;
switch (action) {
case ES_GET:
case ES_GET_CRYPTO:
cryptodebug("elfsign_begin for get");
elfcmd = ELF_C_READ;
break;
case ES_UPDATE_RSA_MD5_SHA1:
case ES_UPDATE_RSA_SHA1:
cryptodebug("elfsign_begin for update");
elfcmd = ELF_C_RDWR;
break;
default:
return (ELFSIGN_UNKNOWN);
}
return (ELFSIGN_UNKNOWN);
}
if (!elfcertlib_init(ess)) {
cryptodebug("elfsign_begin: failed initialization");
return (ELFSIGN_UNKNOWN);
}
return (ELFSIGN_SUCCESS);
}
return (ELFSIGN_INVALID_ELFOBJ);
}
return (ELFSIGN_INVALID_ELFOBJ);
}
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.
*/
cryptodebug("fcntl(F_SETLK) of %s failed with: %s",
return (ELFSIGN_UNKNOWN);
}
return (ELFSIGN_UNKNOWN);
}
return (ELFSIGN_INVALID_ELFOBJ);
}
return (ELFSIGN_INVALID_ELFOBJ);
}
if (ident == NULL) {
return (ELFSIGN_INVALID_ELFOBJ);
}
/*
* Call elf_getshstrndx to be sure we have a real ELF object
* this is required because elf_begin doesn't check that.
*/
cryptodebug("elfsign_begin: elf_getshstrndx failed");
return (ELFSIGN_INVALID_ELFOBJ);
}
/*
* Make sure libelf doesn't rearrange section ordering / offsets.
*/
return (ELFSIGN_SUCCESS);
}
/*
* elfsign_end - cleanup the ELFsign_t
*
*/
void
{
return;
cryptodebug("elf_update() failed: %s",
elf_errmsg(-1));
return;
}
}
}
}
}
}
}
/*
* set the certificate path
*/
{
/*
* 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.
*/
return (ELFSIGN_INVALID_CERTPATH);
return (ELFSIGN_FAILED);
char *subject;
/* set the version based on the certificate */
else
}
}
return (ELFSIGN_FAILED);
}
return (ELFSIGN_SUCCESS);
}
/*
* set the callback context
*/
void
{
}
/*
* set the signature extraction callback
*/
void
{
}
/*
* elfsign_signatures
*
* IN: ess, fsspp, action
* OUT: fsspp
*/
struct filesignatures **fsspp,
{
const char *elf_section = SUNW_ELF_SIGNATURE_ID;
uint64_t sig_offset = 0;
cryptodebug("elfsign_signature");
cryptodebug("invalid arguments");
return (ELFSIGN_UNKNOWN);
}
cryptodebug("elfsign_signature %s for %s",
(void) elf_errno();
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.
*/
cryptodebug("gelf_getshdr() failed: %s",
elf_errmsg(-1));
return (ELFSIGN_FAILED);
}
break;
}
}
}
if (elf_errmsg(0) != NULL) {
elf_errmsg(-1));
return (ELFSIGN_FAILED);
}
char *new_d_buf;
cryptodebug("elfsign_signature: %s not found - creating",
/*
* insert section name in .shstrtab
*/
cryptodebug("elf_getscn() failed: %s",
elf_errmsg(-1));
return (ELFSIGN_FAILED);
}
cryptodebug("gelf_getshdr() failed: %s",
elf_errmsg(-1));
return (ELFSIGN_FAILED);
}
cryptodebug("elf_getdata() failed: %s",
elf_errmsg(-1));
return (ELFSIGN_FAILED);
}
cryptodebug("mismatch between data size %d "
return (ELFSIGN_FAILED);
}
return (ELFSIGN_FAILED);
/*
* 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.
*/
cryptodebug("elf_newscn() failed: %s",
elf_errmsg(-1));
return (ELFSIGN_FAILED);
}
cryptodebug("gelf_getshdr() failed: %s",
elf_errmsg(-1));
return (ELFSIGN_FAILED);
}
/*
* Flush the changes to the underlying elf32 or elf64
* section header.
*/
cryptodebug("gelf_update_shdr failed");
return (ELFSIGN_FAILED);
}
cryptodebug("can't add elf data area for %s: %s",
return (ELFSIGN_FAILED);
}
cryptodebug("can't adjust for new section name %s",
return (ELFSIGN_FAILED);
}
} else {
cryptodebug("can't find signature section");
return (ELFSIGN_NOTSIGNED);
}
cryptodebug("can't get section data for %s",
return (ELFSIGN_FAILED);
}
}
if (ES_ACTISUPDATE(action)) {
fscnt++) {
}
}
cryptodebug("section %s is part of a loadable segment, "
"it cannot be changed.\n", elf_section);
return (ELFSIGN_FAILED);
}
return (ELFSIGN_FAILED);
(void) elfsign_switch(ess,
}
cryptodebug("elfsign_signature: data->d_size = %d",
cryptodebug("can't adjust for revised signature "
"section contents");
return (ELFSIGN_FAILED);
}
} else {
return (ELFSIGN_FAILED);
return (ELFSIGN_FAILED);
}
}
return (ELFSIGN_SUCCESS);
}
static ELFsign_status_t
{
char *name;
struct scninfo {
/* get the size of the current section */
return (ELFSIGN_FAILED);
return (ELFSIGN_SUCCESS);
cryptodebug("elfsign_adjustoffsets: "
return (ELFSIGN_FAILED);
}
/* resize the desired section */
cryptodebug("elfsign_adjustoffsets: "
"resizing %s at 0x%llx from 0x%llx to 0x%llx",
cryptodebug("gelf_update_shdr failed");
goto bad;
}
/*
* find sections whose data follows the changed section
* must scan all sections since section data may not
* be in same order as section headers
*/
goto bad;
continue;
/* .bss can occasionally overlap .shrtab */
continue;
}
cryptodebug("elfsign_adjustoffsets: "
"can't move allocated section %s",
goto bad;
}
/*
* force reading of data to memory image
*/
;
/*
* capture section information
* insert into list in order of sh_offset
*/
cryptodebug("elfsign_adjustoffsets: "
"may have to adjust section %s, offset 0x%llx",
cryptodebug("elfsign_adjustoffsets: "
"memory allocation failure");
goto bad;
}
break;
}
}
/* move following sections as necessary */
cryptodebug("elfsign_adjustoffsets: "
"elf_getshdr for section %d failed",
elf_ndxscn(scnp));
goto bad;
}
break;
(-shdr.sh_addralign);
cryptodebug("elfsign_adjustoffsets: "
"moving %s size 0x%llx from 0x%llx to 0x%llx",
cryptodebug("gelf_update_shdr failed");
goto bad;
}
}
/*
* adjust section header offset in elf header
*/
goto bad;
}
(-ELF32_FSZ_OFF);
(-ELF64_FSZ_OFF);
cryptodebug("elfsign_adjustoffsets: "
"move sh_off from 0x%llx to 0x%llx",
cryptodebug("elf_update_ehdr() failed: %s",
elf_errmsg(-1));
goto bad;
}
}
bad:
}
return (retval);
}
struct filesignatures *
struct filesignatures *fssp,
const char *dn,
int dn_len,
int sig_len,
const char *oid,
int oid_len)
{
}
/*ARGSUSED*/
struct filesignatures *fssp,
{
struct filesig_extraction fsx;
return (FILESIG_UNKNOWN);
return (FILESIG_UNKNOWN);
switch (version) {
case FILESIG_VERSION1:
case FILESIG_VERSION2:
case FILESIG_VERSION3:
case FILESIG_VERSION4:
*sig_len);
} else
break;
default:
break;
}
}
return (version);
}
static ELFsign_status_t
{
/* The buffer must be large enough to hold the hash */
if (*hash_len < SHA1_DIGEST_LENGTH)
return (ELFSIGN_FAILED);
/* Initialize the digest session */
(void) elf_errno();
goto done;
}
name = "NULL";
if (!hash_mem_resident &&
/*
* skip the signature section only
*/
continue;
}
/*
* select only memory resident sections
*/
continue;
}
/*
* throw this section into the hash
* use elf_rawdata for endian-independence
* use elf_getdata to get update of .shstrtab
*/
cryptodebug("elfsign_hash: %s has NULL data",
name);
continue;
}
cryptodebug("elfsign_hash: updating hash "
}
}
if (elf_errmsg(0) != NULL) {
goto done;
}
{ /* DEBUG START */
}
} /* DEBUG END */
done:
return (elfstat);
}
/*
* elfsign_hash - return the hash of the ELF sections affecting execution.
*
* IN: ess, hash_len
* OUT: hash, hash_len
*/
{
}
/*
* 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_hash_esa = return the hash of the esa_buffer
*
* IN: ess, esa_buf, esa_buf_len, hash_len
* OUT: hash, hash_len
*/
{
cryptodebug("esa_hash version is: %s",
/*
* old rsa_md5_sha1 format
* signed with MD5 digest, just pass full esa_buf
*/
*hash_len = esa_buf_len;
return (ELFSIGN_SUCCESS);
}
if (*hash_len < SHA1_DIGEST_LENGTH)
return (ELFSIGN_FAILED);
{ /* DEBUG START */
}
} /* DEBUG END */
return (ELFSIGN_SUCCESS);
}
/*
* elfsign_verify_signature - Verify the signature of the ELF object.
*
* IN: ess
* OUT: esipp
* RETURNS:
* ELFsign_status_t
*/
{
struct filesignatures *fssp;
struct filesig_extraction fsx;
int sigcnt;
int nocert = 0;
esip = (struct ELFsign_sig_info *)
}
/*
* Find out which cert we need, based on who signed the ELF object
*/
return (ELFSIGN_NOTSIGNED);
}
goto cleanup;
}
/*
* Scan the signature block, looking for a verifiable signature
*/
cryptodebug("elfsign_verify_signature: version=%s",
switch (ess->es_version) {
case FILESIG_VERSION1:
case FILESIG_VERSION2:
case FILESIG_VERSION3:
case FILESIG_VERSION4:
break;
default:
goto cleanup;
}
cryptodebug("elfsign_verify_signature: signer_DN=\"%s\"",
cryptodebug("elfsign_verify_signature: algorithmOID=\"%s\"",
/* return signer DN if requested */
}
/*
* look for certificate
*/
/*
* skip unfound certificates
*/
cryptodebug("unable to find certificate "
"with DN=\"%s\" for %s",
nocert++;
continue;
}
/*
* skip unverified certificates
* force verification of crypto certs
*/
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.
*/
continue;
}
nocert = 0;
/*
* compute file hash
*/
cryptodebug("elfsign_verify_signature:"
" elfsign_hash failed");
break;
}
{ /* DEBUG START */
sigstr, sigstr_len);
}
} /* DEBUG END */
if (ess->es_sigvercallback)
/*
* The signature is verified!
* Check if this is a restricted provider
*/
else {
cryptodebug("DN is tagged for usagelimited");
}
break;
}
cryptodebug("elfsign_verify_signature: invalid signature");
}
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
{
char *elfobj_esa = NULL;
int esa_fd = -1;
size_t esa_buf_len = 0;
size_t main_sig_len = 0;
size_t esa_dn_len = 0;
size_t esa_sig_len = 0;
cryptodebug("elfsign_verify_esa");
/* does the activation file exist? */
if (elfobj_esa == NULL) {
gettext("Unable to allocate buffer for esa filename."));
goto cleanup;
}
cryptodebug("No .esa file was found, or it was unreadable");
goto cleanup;
}
goto cleanup;
}
/*
* mmap the buffer to save on syscalls
*/
MAP_PRIVATE, esa_fd, 0);
if (esa_file_buffer == MAP_FAILED) {
gettext("Unable to mmap file to a buffer for %s."),
goto cleanup;
}
esa_file_ptr += sizeof (uint32_t);
/* verify .esa main signature versus original signature */
if (main_sig_len != orig_sig_len ||
gettext("Unable to match original signature from %s."),
goto cleanup;
}
esa_file_ptr += sizeof (uint32_t);
gettext("Unable to allocate memory for dn buffer."));
goto cleanup;
}
esa_file_ptr += sizeof (uint32_t);
cryptodebug("Read esa contents, now verifying");
/*
* dn used in .esa file should not be limited.
*/
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;
}
"with DN=\"%s\" for %s"),
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.
*/
gettext("Unable to hash activation contents."));
goto cleanup;
}
gettext("Unable to verify .esa contents for %s"),
ess->es_pathname);
goto cleanup;
}
cryptodebug("Verified esa contents");
if (ess->es_sigvercallback)
/*
* validate the certificate used to sign the activation file
*/
gettext("Unable to verify .esa certificate %s for %s"),
goto cleanup;
}
cryptodebug("Verified esa certificate");
if (elfobj_esa != NULL)
if (esa_fd != -1)
if (esa_file_buffer != NULL)
return (ret);
}
static uint32_t
{
return (((i & 0xff) << 24) | ((i & 0xff00) << 8) |
((i >> 8) & 0xff00) | ((i >> 24) & 0xff));
}
static uint64_t
{
(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
{
int fscnt;
if (ess->es_same_endian)
return (ELFSIGN_SUCCESS);
if (ES_ACTISUPDATE(action))
if (!ES_ACTISUPDATE(action))
if (ES_ACTISUPDATE(action)) {
}
fsgp->filesig_size =
if (!ES_ACTISUPDATE(action)) {
}
switch (version) {
case FILESIG_VERSION1:
case FILESIG_VERSION2:
break;
case FILESIG_VERSION3:
case FILESIG_VERSION4:
break;
default:
cryptodebug("elfsign_switch: failed");
return (ELFSIGN_FAILED);
}
}
return (ELFSIGN_SUCCESS);
}
/*
*/
void
{
if (!ES_ACTISUPDATE(action)) {
/* fetch integer from buffer */
if (!ess->es_same_endian) {
}
} else {
/* put integer into buffer */
if (!ess->es_same_endian) {
}
}
}
char const *
{
switch (elferror) {
case ELFSIGN_SUCCESS:
break;
case ELFSIGN_FAILED:
break;
case ELFSIGN_NOTSIGNED:
break;
case ELFSIGN_INVALID_CERTPATH:
break;
case ELFSIGN_INVALID_ELFOBJ:
break;
case ELFSIGN_RESTRICTED:
break;
case ELFSIGN_UNKNOWN:
default:
break;
}
return (msg);
}
{
struct filesig_extraction fsx;
struct ELFsign_sig_info *esip;
esip = (struct ELFsign_sig_info *)
return (B_FALSE);
case FILESIG_VERSION1:
case FILESIG_VERSION2:
case FILESIG_VERSION3:
case FILESIG_VERSION4:
break;
default:
}
}
void
{
}
}