15194Sjohnz /*
25194Sjohnz * CDDL HEADER START
35194Sjohnz *
45194Sjohnz * The contents of this file are subject to the terms of the
55194Sjohnz * Common Development and Distribution License (the "License").
65194Sjohnz * You may not use this file except in compliance with the License.
75194Sjohnz *
85194Sjohnz * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
95194Sjohnz * or http://www.opensolaris.org/os/licensing.
105194Sjohnz * See the License for the specific language governing permissions
115194Sjohnz * and limitations under the License.
125194Sjohnz *
135194Sjohnz * When distributing Covered Code, include this CDDL HEADER in each
145194Sjohnz * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
155194Sjohnz * If applicable, add the following below this CDDL HEADER, with the
165194Sjohnz * fields enclosed by brackets "[]" replaced with your own identifying
175194Sjohnz * information: Portions Copyright [yyyy] [name of copyright owner]
185194Sjohnz *
195194Sjohnz * CDDL HEADER END
205194Sjohnz */
215194Sjohnz
225194Sjohnz /*
23*12304SValerie.Fenwick@Oracle.COM * Copyright (c) 2002, 2010, Oracle and/or its affiliates. All rights reserved.
245194Sjohnz */
255194Sjohnz
265194Sjohnz #define ELF_TARGET_ALL /* get definitions of all section flags */
275194Sjohnz
285194Sjohnz #include <sys/types.h>
295194Sjohnz #include <sys/stat.h>
305194Sjohnz #include <fcntl.h>
315194Sjohnz #include <unistd.h>
325194Sjohnz #include <strings.h>
335194Sjohnz #include <stddef.h>
345194Sjohnz #include <stdlib.h>
355194Sjohnz #include <libintl.h>
365194Sjohnz #include <dirent.h>
375194Sjohnz #include <errno.h>
385194Sjohnz #include <libelf.h>
395194Sjohnz #include <gelf.h>
405194Sjohnz #include <cryptoutil.h>
415194Sjohnz #include <sha1.h>
425194Sjohnz #include <sys/crypto/elfsign.h>
435194Sjohnz #include <libelfsign.h>
445194Sjohnz
455194Sjohnz #ifndef SHA1_DIGEST_LENGTH
465194Sjohnz #define SHA1_DIGEST_LENGTH 20
475194Sjohnz #endif /* SHA1_DIGEST_LENGTH */
485194Sjohnz
495194Sjohnz const char SUNW_ELF_SIGNATURE_ID[] = ELF_SIGNATURE_SECTION;
505194Sjohnz const char OID_sha1WithRSAEncryption[] = "1.2.840.113549.1.1.5";
515194Sjohnz
525194Sjohnz static ELFsign_status_t elfsign_adjustoffsets(ELFsign_t ess,
535194Sjohnz Elf_Scn *scn, uint64_t new_size);
545194Sjohnz static uint32_t elfsign_switch_uint32(uint32_t i);
555194Sjohnz static ELFsign_status_t elfsign_switch(ELFsign_t ess,
565194Sjohnz struct filesignatures *fssp, enum ES_ACTION action);
575194Sjohnz
585194Sjohnz struct filesig_extraction {
595194Sjohnz filesig_vers_t fsx_version;
605194Sjohnz char *fsx_format;
615194Sjohnz char fsx_signer_DN[ELFCERT_MAX_DN_LEN];
625194Sjohnz size_t fsx_signer_DN_len;
635194Sjohnz uchar_t fsx_signature[SIG_MAX_LENGTH];
645194Sjohnz size_t fsx_sig_len;
655194Sjohnz char fsx_sig_oid[100];
665194Sjohnz size_t fsx_sig_oid_len;
675194Sjohnz time_t fsx_time;
685194Sjohnz };
695194Sjohnz
705194Sjohnz static char *
version_to_str(filesig_vers_t v)715194Sjohnz version_to_str(filesig_vers_t v)
725194Sjohnz {
735194Sjohnz char *ret;
745194Sjohnz
755194Sjohnz switch (v) {
765194Sjohnz case FILESIG_VERSION1:
775194Sjohnz ret = "VERSION1";
785194Sjohnz break;
795194Sjohnz case FILESIG_VERSION2:
805194Sjohnz ret = "VERSION2";
815194Sjohnz break;
825194Sjohnz case FILESIG_VERSION3:
835194Sjohnz ret = "VERSION3";
845194Sjohnz break;
855194Sjohnz case FILESIG_VERSION4:
865194Sjohnz ret = "VERSION4";
875194Sjohnz break;
885194Sjohnz default:
895194Sjohnz ret = "UNKNOWN";
905194Sjohnz break;
915194Sjohnz }
925194Sjohnz return (ret);
935194Sjohnz }
945194Sjohnz
955194Sjohnz /*
965194Sjohnz * Update filesignatures to include the v1/v2 filesig,
975194Sjohnz * composed of signer DN, signature, and OID.
985194Sjohnz */
995194Sjohnz 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)1005194Sjohnz filesig_insert_dso(struct filesignatures *fssp,
1015194Sjohnz filesig_vers_t version,
1025194Sjohnz const char *dn,
1035194Sjohnz int dn_len,
1045194Sjohnz const uchar_t *sig,
1055194Sjohnz int sig_len,
1065194Sjohnz const char *oid,
1075194Sjohnz int oid_len)
1085194Sjohnz {
1095194Sjohnz struct filesig *fsgp;
1105194Sjohnz char *fsdatap;
1115194Sjohnz
1125194Sjohnz if (oid == NULL) {
1135194Sjohnz /*
1145194Sjohnz * This OID is used for the rsa_md5_sha1 format signature also.
1155194Sjohnz * This use is historical, and is hence continued,
1165194Sjohnz * despite its lack of technical accuracy.
1175194Sjohnz */
1185194Sjohnz oid = OID_sha1WithRSAEncryption;
1195194Sjohnz oid_len = strlen(oid);
1205194Sjohnz }
1215194Sjohnz
1225194Sjohnz /*
1235194Sjohnz * for now, always insert a single-signature signature block
1245194Sjohnz */
1255194Sjohnz if (fssp != NULL)
1265194Sjohnz free(fssp);
1275194Sjohnz fssp = (struct filesignatures *)
1285194Sjohnz malloc(filesig_ALIGN(sizeof (struct filesignatures) +
1295194Sjohnz dn_len + sig_len + oid_len));
1305194Sjohnz if (fssp == NULL)
1315194Sjohnz return (fssp);
1325194Sjohnz
1335194Sjohnz fssp->filesig_cnt = 1;
1345194Sjohnz fssp->filesig_pad = 0; /* reserve for future use */
1355194Sjohnz
1365194Sjohnz fsgp = &fssp->filesig_sig;
1375194Sjohnz fsgp->filesig_size = sizeof (struct filesig) +
1385194Sjohnz dn_len + sig_len + oid_len;
1395194Sjohnz fsgp->filesig_version = version;
1405194Sjohnz switch (version) {
1415194Sjohnz case FILESIG_VERSION1:
1425194Sjohnz case FILESIG_VERSION2:
1435194Sjohnz fsgp->filesig_size -= sizeof (struct filesig) -
1445194Sjohnz offsetof(struct filesig, filesig_v1_data[0]);
1455194Sjohnz fsgp->filesig_v1_dnsize = dn_len;
1465194Sjohnz fsgp->filesig_v1_sigsize = sig_len;
1475194Sjohnz fsgp->filesig_v1_oidsize = oid_len;
1485194Sjohnz fsdatap = &fsgp->filesig_v1_data[0];
1495194Sjohnz break;
1505194Sjohnz case FILESIG_VERSION3:
1515194Sjohnz case FILESIG_VERSION4:
1525194Sjohnz fsgp->filesig_size -= sizeof (struct filesig) -
1535194Sjohnz offsetof(struct filesig, filesig_v3_data[0]);
1545194Sjohnz fsgp->filesig_v3_time = time(NULL);
1555194Sjohnz fsgp->filesig_v3_dnsize = dn_len;
1565194Sjohnz fsgp->filesig_v3_sigsize = sig_len;
1575194Sjohnz fsgp->filesig_v3_oidsize = oid_len;
1585194Sjohnz fsdatap = &fsgp->filesig_v3_data[0];
1595194Sjohnz break;
1605194Sjohnz default:
1615194Sjohnz cryptodebug("filesig_insert_dso: unknown version: %d",
1625194Sjohnz version);
1635194Sjohnz free(fssp);
1645194Sjohnz return (NULL);
1655194Sjohnz }
1665194Sjohnz (void) memcpy(fsdatap, dn, dn_len);
1675194Sjohnz fsdatap += dn_len;
1685194Sjohnz (void) memcpy(fsdatap, (char *)sig, sig_len);
1695194Sjohnz fsdatap += sig_len;
1705194Sjohnz (void) memcpy(fsdatap, oid, oid_len);
1715194Sjohnz fsdatap += oid_len;
1725194Sjohnz fsgp = filesig_next(fsgp);
1735194Sjohnz (void) memset(fsdatap, 0, (char *)(fsgp) - fsdatap);
1745194Sjohnz
1755194Sjohnz return (fssp);
1765194Sjohnz }
1775194Sjohnz
1785194Sjohnz /*
1795194Sjohnz * filesig_extract - extract filesig structure to internal form
1805194Sjohnz */
1815194Sjohnz static filesig_vers_t
filesig_extract(struct filesig * fsgp,struct filesig_extraction * fsxp)1825194Sjohnz filesig_extract(struct filesig *fsgp, struct filesig_extraction *fsxp)
1835194Sjohnz {
1845194Sjohnz char *fsdp;
1855194Sjohnz
1865194Sjohnz #define filesig_extract_common(cp, field, data_var, len_var, len_limit) { \
1875194Sjohnz len_var = len_limit; \
1885194Sjohnz if (len_var > fsgp->field) \
1895194Sjohnz len_var = fsgp->field; \
1905194Sjohnz (void) memcpy(data_var, cp, len_var); \
1915194Sjohnz cp += fsgp->field; }
1925194Sjohnz #define filesig_extract_str(cp, field, data_var, len_var) \
1935194Sjohnz filesig_extract_common(cp, field, data_var, len_var, \
1945194Sjohnz sizeof (data_var) - 1); \
1955194Sjohnz data_var[len_var] = '\0';
1965194Sjohnz #define filesig_extract_opaque(cp, field, data_var, len_var) \
1975194Sjohnz filesig_extract_common(cp, field, data_var, len_var, sizeof (data_var))
1985194Sjohnz
1995194Sjohnz fsxp->fsx_version = fsgp->filesig_version;
2005194Sjohnz cryptodebug("filesig_extract: version=%s",
2015194Sjohnz version_to_str(fsxp->fsx_version));
2025194Sjohnz switch (fsxp->fsx_version) {
2035194Sjohnz case FILESIG_VERSION1:
2045194Sjohnz case FILESIG_VERSION2:
2055194Sjohnz /*
2065194Sjohnz * extract VERSION1 DN, signature, and OID
2075194Sjohnz */
2085194Sjohnz fsdp = fsgp->filesig_v1_data;
2095194Sjohnz fsxp->fsx_format = ES_FMT_RSA_MD5_SHA1;
2105194Sjohnz fsxp->fsx_time = 0;
2115194Sjohnz filesig_extract_str(fsdp, filesig_v1_dnsize,
2125194Sjohnz fsxp->fsx_signer_DN, fsxp->fsx_signer_DN_len);
2135194Sjohnz filesig_extract_opaque(fsdp, filesig_v1_sigsize,
2145194Sjohnz fsxp->fsx_signature, fsxp->fsx_sig_len);
2155194Sjohnz filesig_extract_str(fsdp, filesig_v1_oidsize,
2165194Sjohnz fsxp->fsx_sig_oid, fsxp->fsx_sig_oid_len);
2175194Sjohnz break;
2185194Sjohnz case FILESIG_VERSION3:
2195194Sjohnz case FILESIG_VERSION4:
2205194Sjohnz fsdp = fsgp->filesig_v3_data;
2215194Sjohnz fsxp->fsx_format = ES_FMT_RSA_SHA1;
2225194Sjohnz fsxp->fsx_time = fsgp->filesig_v3_time;
2235194Sjohnz filesig_extract_str(fsdp, filesig_v3_dnsize,
2245194Sjohnz fsxp->fsx_signer_DN, fsxp->fsx_signer_DN_len);
2255194Sjohnz filesig_extract_opaque(fsdp, filesig_v3_sigsize,
2265194Sjohnz fsxp->fsx_signature, fsxp->fsx_sig_len);
2275194Sjohnz filesig_extract_str(fsdp, filesig_v3_oidsize,
2285194Sjohnz fsxp->fsx_sig_oid, fsxp->fsx_sig_oid_len);
2295194Sjohnz break;
2305194Sjohnz default:
2315194Sjohnz break;
2325194Sjohnz }
2335194Sjohnz
2345194Sjohnz return (fsxp->fsx_version);
2355194Sjohnz }
2365194Sjohnz
2375194Sjohnz ELFsign_status_t
elfsign_begin(const char * filename,enum ES_ACTION action,ELFsign_t * essp)2385194Sjohnz elfsign_begin(const char *filename, enum ES_ACTION action, ELFsign_t *essp)
2395194Sjohnz {
2405194Sjohnz Elf_Cmd elfcmd;
2415194Sjohnz int oflags = 0;
2425194Sjohnz short l_type;
2435194Sjohnz ELFsign_t ess;
2445194Sjohnz struct stat stb;
2455194Sjohnz union {
2465194Sjohnz char c[2];
2475194Sjohnz short s;
2485194Sjohnz } uorder;
2495194Sjohnz GElf_Ehdr elfehdr;
2505194Sjohnz char *ident;
2515194Sjohnz
2525194Sjohnz switch (action) {
2535194Sjohnz case ES_GET:
2545194Sjohnz case ES_GET_CRYPTO:
25510732SAnthony.Scarpino@Sun.COM case ES_GET_FIPS140:
2565194Sjohnz cryptodebug("elfsign_begin for get");
2575194Sjohnz elfcmd = ELF_C_READ;
2585194Sjohnz oflags = O_RDONLY | O_NOCTTY | O_NDELAY;
2595194Sjohnz l_type = F_RDLCK;
2605194Sjohnz break;
2615194Sjohnz case ES_UPDATE_RSA_MD5_SHA1:
2625194Sjohnz case ES_UPDATE_RSA_SHA1:
2635194Sjohnz cryptodebug("elfsign_begin for update");
2645194Sjohnz elfcmd = ELF_C_RDWR;
2655194Sjohnz oflags = O_RDWR | O_NOCTTY | O_NDELAY;
2665194Sjohnz l_type = F_WRLCK;
2675194Sjohnz break;
2685194Sjohnz default:
2695194Sjohnz return (ELFSIGN_UNKNOWN);
2705194Sjohnz }
2715194Sjohnz
2725194Sjohnz if ((ess = malloc(sizeof (struct ELFsign_s))) == NULL) {
2735194Sjohnz return (ELFSIGN_UNKNOWN);
2745194Sjohnz }
2755194Sjohnz (void) memset((void *)ess, 0, sizeof (struct ELFsign_s));
2765194Sjohnz
2775194Sjohnz if (!elfcertlib_init(ess)) {
2785194Sjohnz cryptodebug("elfsign_begin: failed initialization");
2795194Sjohnz return (ELFSIGN_UNKNOWN);
2805194Sjohnz }
2815194Sjohnz
2825194Sjohnz ess->es_elf = NULL;
2835194Sjohnz ess->es_action = action;
2845194Sjohnz ess->es_version = FILESIG_UNKNOWN;
2855194Sjohnz ess->es_pathname = NULL;
2865194Sjohnz ess->es_certpath = NULL;
2875194Sjohnz
2885194Sjohnz if (filename == NULL) {
2895194Sjohnz *essp = ess;
2905194Sjohnz return (ELFSIGN_SUCCESS);
2915194Sjohnz }
2925194Sjohnz
2935194Sjohnz if ((ess->es_fd = open(filename, oflags)) == -1) {
2945194Sjohnz elfsign_end(ess);
2955194Sjohnz return (ELFSIGN_INVALID_ELFOBJ);
2965194Sjohnz }
2975194Sjohnz if ((fstat(ess->es_fd, &stb) == -1) || !S_ISREG(stb.st_mode)) {
2985194Sjohnz elfsign_end(ess);
2995194Sjohnz return (ELFSIGN_INVALID_ELFOBJ);
3005194Sjohnz }
3015194Sjohnz if ((ess->es_pathname = strdup(filename)) == NULL) {
3025194Sjohnz elfsign_end(ess);
3035194Sjohnz return (ELFSIGN_UNKNOWN);
3045194Sjohnz }
3055194Sjohnz /*
3065194Sjohnz * The following lock is released in elfsign_end() when we close(2)
3075194Sjohnz * the es_fd. This ensures that we aren't trying verify a file
3085194Sjohnz * we are currently updating.
3095194Sjohnz */
3105194Sjohnz ess->es_flock.l_type = l_type;
3115194Sjohnz ess->es_flock.l_whence = SEEK_CUR;
3125194Sjohnz ess->es_flock.l_start = 0;
3135194Sjohnz ess->es_flock.l_len = 0;
3145194Sjohnz if (fcntl(ess->es_fd, F_SETLK, &ess->es_flock) == -1) {
3155194Sjohnz cryptodebug("fcntl(F_SETLK) of %s failed with: %s",
3165194Sjohnz ess->es_pathname, strerror(errno));
3175194Sjohnz elfsign_end(ess);
3185194Sjohnz return (ELFSIGN_UNKNOWN);
3195194Sjohnz }
3205194Sjohnz
3215194Sjohnz if (elf_version(EV_CURRENT) == EV_NONE) {
3225194Sjohnz elfsign_end(ess);
3235194Sjohnz return (ELFSIGN_UNKNOWN);
3245194Sjohnz }
3255194Sjohnz
3265194Sjohnz if ((ess->es_elf = elf_begin(ess->es_fd, elfcmd,
3275194Sjohnz (Elf *)NULL)) == NULL) {
3285194Sjohnz cryptodebug("elf_begin() failed: %s", elf_errmsg(-1));
3295194Sjohnz elfsign_end(ess);
3305194Sjohnz return (ELFSIGN_INVALID_ELFOBJ);
3315194Sjohnz }
3325194Sjohnz
3335194Sjohnz if (gelf_getehdr(ess->es_elf, &elfehdr) == NULL) {
3345194Sjohnz cryptodebug("elf_getehdr() failed: %s", elf_errmsg(-1));
3355194Sjohnz elfsign_end(ess);
3365194Sjohnz return (ELFSIGN_INVALID_ELFOBJ);
3375194Sjohnz }
3385194Sjohnz ess->es_has_phdr = (elfehdr.e_phnum != 0);
3395194Sjohnz
3405194Sjohnz uorder.s = ELFDATA2MSB << 8 | ELFDATA2LSB;
3415194Sjohnz ident = elf_getident(ess->es_elf, NULL);
3425194Sjohnz if (ident == NULL) {
3435194Sjohnz cryptodebug("elf_getident() failed: %s", elf_errmsg(-1));
3445194Sjohnz elfsign_end(ess);
3455194Sjohnz return (ELFSIGN_INVALID_ELFOBJ);
3465194Sjohnz }
3475194Sjohnz ess->es_same_endian = (ident[EI_DATA] == uorder.c[0]);
3485194Sjohnz ess->es_ei_class = ident[EI_CLASS];
3495194Sjohnz
3505194Sjohnz /*
3515194Sjohnz * Call elf_getshstrndx to be sure we have a real ELF object
3525194Sjohnz * this is required because elf_begin doesn't check that.
3535194Sjohnz */
3549902SAli.Bahrami@Sun.COM if (elf_getshstrndx(ess->es_elf, &ess->es_shstrndx) == 0) {
3555194Sjohnz elfsign_end(ess);
3565194Sjohnz cryptodebug("elfsign_begin: elf_getshstrndx failed");
3575194Sjohnz return (ELFSIGN_INVALID_ELFOBJ);
3585194Sjohnz }
3595194Sjohnz
3605194Sjohnz /*
3615194Sjohnz * Make sure libelf doesn't rearrange section ordering / offsets.
3625194Sjohnz */
3635194Sjohnz (void) elf_flagelf(ess->es_elf, ELF_C_SET, ELF_F_LAYOUT);
3645194Sjohnz
3655194Sjohnz *essp = ess;
3665194Sjohnz
3675194Sjohnz return (ELFSIGN_SUCCESS);
3685194Sjohnz }
3695194Sjohnz
3705194Sjohnz /*
3715194Sjohnz * elfsign_end - cleanup the ELFsign_t
3725194Sjohnz *
3735194Sjohnz * IN/OUT: ess
3745194Sjohnz */
3755194Sjohnz void
elfsign_end(ELFsign_t ess)3765194Sjohnz elfsign_end(ELFsign_t ess)
3775194Sjohnz {
3785194Sjohnz if (ess == NULL)
3795194Sjohnz return;
3805194Sjohnz
3815194Sjohnz if (ess->es_elf != NULL && ES_ACTISUPDATE(ess->es_action)) {
3825194Sjohnz if (elf_update(ess->es_elf, ELF_C_WRITE) == -1) {
3835194Sjohnz cryptodebug("elf_update() failed: %s",
3845194Sjohnz elf_errmsg(-1));
3855194Sjohnz return;
3865194Sjohnz }
3875194Sjohnz }
3885194Sjohnz
3895194Sjohnz if (ess->es_fd != -1) {
3905194Sjohnz (void) close(ess->es_fd);
3915194Sjohnz ess->es_fd = -1;
3925194Sjohnz }
3935194Sjohnz
3945194Sjohnz if (ess->es_pathname != NULL) {
3955194Sjohnz free(ess->es_pathname);
3965194Sjohnz ess->es_pathname = NULL;
3975194Sjohnz }
3985194Sjohnz if (ess->es_certpath != NULL) {
3995194Sjohnz free(ess->es_certpath);
4005194Sjohnz ess->es_certpath = NULL;
4015194Sjohnz }
4025194Sjohnz
4035194Sjohnz if (ess->es_elf != NULL) {
4045194Sjohnz (void) elf_end(ess->es_elf);
4055194Sjohnz ess->es_elf = NULL;
4065194Sjohnz }
4075194Sjohnz
4085194Sjohnz elfcertlib_fini(ess);
4095194Sjohnz
4105194Sjohnz free(ess);
4115194Sjohnz }
4125194Sjohnz
4135194Sjohnz /*
4145194Sjohnz * set the certificate path
4155194Sjohnz */
4165194Sjohnz ELFsign_status_t
elfsign_setcertpath(ELFsign_t ess,const char * certpath)4175194Sjohnz elfsign_setcertpath(ELFsign_t ess, const char *certpath)
4185194Sjohnz {
4195194Sjohnz /*
4205194Sjohnz * Normally use of access(2) is insecure, here we are only
4215194Sjohnz * doing it to help provide early failure and better error
4225194Sjohnz * checking, so there is no race condition.
4235194Sjohnz */
4245980Sjohnz if (access(certpath, R_OK) != 0)
4255194Sjohnz return (ELFSIGN_INVALID_CERTPATH);
4265980Sjohnz
4275980Sjohnz if ((ess->es_certpath = strdup(certpath)) == NULL)
4285980Sjohnz return (ELFSIGN_FAILED);
4295194Sjohnz
4305194Sjohnz if (ES_ACTISUPDATE(ess->es_action)) {
4315194Sjohnz ELFCert_t cert = NULL;
4325194Sjohnz char *subject;
4335194Sjohnz
4345194Sjohnz /* set the version based on the certificate */
4355194Sjohnz if (elfcertlib_getcert(ess, ess->es_certpath, NULL,
4365194Sjohnz &cert, ess->es_action)) {
4375194Sjohnz if ((subject = elfcertlib_getdn(cert)) != NULL) {
4385194Sjohnz if (strstr(subject, ELFSIGN_CRYPTO))
4395194Sjohnz ess->es_version = (ess->es_action ==
4405194Sjohnz ES_UPDATE_RSA_MD5_SHA1) ?
4415194Sjohnz FILESIG_VERSION1 : FILESIG_VERSION3;
4425194Sjohnz else
4435194Sjohnz ess->es_version = (ess->es_action ==
4445194Sjohnz ES_UPDATE_RSA_MD5_SHA1) ?
4455194Sjohnz FILESIG_VERSION2 : FILESIG_VERSION4;
4465194Sjohnz }
4475194Sjohnz elfcertlib_releasecert(ess, cert);
4485194Sjohnz }
4495194Sjohnz if (ess->es_version == FILESIG_UNKNOWN)
4505194Sjohnz return (ELFSIGN_FAILED);
4515194Sjohnz }
4525194Sjohnz return (ELFSIGN_SUCCESS);
4535194Sjohnz }
4545194Sjohnz
4555194Sjohnz /*
4565194Sjohnz * set the callback context
4575194Sjohnz */
4585194Sjohnz void
elfsign_setcallbackctx(ELFsign_t ess,void * ctx)4595194Sjohnz elfsign_setcallbackctx(ELFsign_t ess, void *ctx)
4605194Sjohnz {
4615194Sjohnz ess->es_callbackctx = ctx;
4625194Sjohnz }
4635194Sjohnz
4645194Sjohnz /*
4655194Sjohnz * set the signature extraction callback
4665194Sjohnz */
4675194Sjohnz void
elfsign_setsigvercallback(ELFsign_t ess,void (* cb)(void *,void *,size_t,ELFCert_t))4685194Sjohnz elfsign_setsigvercallback(ELFsign_t ess,
4695194Sjohnz void (*cb)(void *, void *, size_t, ELFCert_t))
4705194Sjohnz {
4715194Sjohnz ess->es_sigvercallback = cb;
4725194Sjohnz }
4735194Sjohnz
4745194Sjohnz /*
4755194Sjohnz * elfsign_signatures
4765194Sjohnz *
4775194Sjohnz * IN: ess, fsspp, action
4785194Sjohnz * OUT: fsspp
4795194Sjohnz */
4805194Sjohnz ELFsign_status_t
elfsign_signatures(ELFsign_t ess,struct filesignatures ** fsspp,size_t * fslen,enum ES_ACTION action)4815194Sjohnz elfsign_signatures(ELFsign_t ess,
4825194Sjohnz struct filesignatures **fsspp,
4835194Sjohnz size_t *fslen,
4845194Sjohnz enum ES_ACTION action)
4855194Sjohnz {
4865194Sjohnz Elf_Scn *scn = NULL, *sig_scn = NULL;
4875194Sjohnz GElf_Shdr shdr;
4885194Sjohnz Elf_Data *data = NULL;
4895194Sjohnz const char *elf_section = SUNW_ELF_SIGNATURE_ID;
4905194Sjohnz int fscnt, fssize;
4915194Sjohnz struct filesig *fsgp, *fsgpnext;
4925194Sjohnz uint64_t sig_offset = 0;
4935194Sjohnz
4945194Sjohnz cryptodebug("elfsign_signature");
4955194Sjohnz if ((ess == NULL) || (fsspp == NULL)) {
4965194Sjohnz cryptodebug("invalid arguments");
4975194Sjohnz return (ELFSIGN_UNKNOWN);
4985194Sjohnz }
4995194Sjohnz
5005194Sjohnz cryptodebug("elfsign_signature %s for %s",
5015194Sjohnz ES_ACTISUPDATE(action) ? "ES_UPDATE" : "ES_GET", elf_section);
5025194Sjohnz
5035194Sjohnz (void) elf_errno();
5045194Sjohnz while ((scn = elf_nextscn(ess->es_elf, scn)) != NULL) {
5055194Sjohnz const char *sh_name;
5065194Sjohnz /*
5075194Sjohnz * Do a string compare to examine each section header
5085194Sjohnz * to see if this is the section that needs to be updated.
5095194Sjohnz */
5105194Sjohnz if (gelf_getshdr(scn, &shdr) == NULL) {
5115194Sjohnz cryptodebug("gelf_getshdr() failed: %s",
5125194Sjohnz elf_errmsg(-1));
5135194Sjohnz return (ELFSIGN_FAILED);
5145194Sjohnz }
5155194Sjohnz sh_name = elf_strptr(ess->es_elf, ess->es_shstrndx,
5165194Sjohnz (size_t)shdr.sh_name);
5175194Sjohnz if (strcmp(sh_name, elf_section) == 0) {
5185194Sjohnz cryptodebug("elfsign_signature: found %s", elf_section);
5195194Sjohnz sig_scn = scn;
5205194Sjohnz break;
5215194Sjohnz }
5225194Sjohnz if (shdr.sh_type != SHT_NOBITS &&
5235194Sjohnz sig_offset < shdr.sh_offset + shdr.sh_size) {
5245194Sjohnz sig_offset = shdr.sh_offset + shdr.sh_size;
5255194Sjohnz }
5265194Sjohnz }
5275194Sjohnz if (elf_errmsg(0) != NULL) {
5285194Sjohnz cryptodebug("unexpected error: %s", elf_section,
5295194Sjohnz elf_errmsg(-1));
5305194Sjohnz return (ELFSIGN_FAILED);
5315194Sjohnz }
5325194Sjohnz
5335194Sjohnz if (ES_ACTISUPDATE(action) && (sig_scn == NULL)) {
5345194Sjohnz size_t old_size, new_size;
5355194Sjohnz char *new_d_buf;
5365194Sjohnz
5375194Sjohnz cryptodebug("elfsign_signature: %s not found - creating",
5385194Sjohnz elf_section);
5395194Sjohnz
5405194Sjohnz /*
5415194Sjohnz * insert section name in .shstrtab
5425194Sjohnz */
5435194Sjohnz if ((scn = elf_getscn(ess->es_elf, ess->es_shstrndx)) == 0) {
5445194Sjohnz cryptodebug("elf_getscn() failed: %s",
5455194Sjohnz elf_errmsg(-1));
5465194Sjohnz return (ELFSIGN_FAILED);
5475194Sjohnz }
5485194Sjohnz if (gelf_getshdr(scn, &shdr) == NULL) {
5495194Sjohnz cryptodebug("gelf_getshdr() failed: %s",
5505194Sjohnz elf_errmsg(-1));
5515194Sjohnz return (ELFSIGN_FAILED);
5525194Sjohnz }
5535194Sjohnz if ((data = elf_getdata(scn, data)) == NULL) {
5545194Sjohnz cryptodebug("elf_getdata() failed: %s",
5555194Sjohnz elf_errmsg(-1));
5565194Sjohnz return (ELFSIGN_FAILED);
5575194Sjohnz }
5585194Sjohnz old_size = data->d_size;
5595194Sjohnz if (old_size != shdr.sh_size) {
5605194Sjohnz cryptodebug("mismatch between data size %d "
5615194Sjohnz "and section size %lld", old_size, shdr.sh_size);
5625194Sjohnz return (ELFSIGN_FAILED);
5635194Sjohnz }
5645194Sjohnz new_size = old_size + strlen(elf_section) + 1;
5655194Sjohnz if ((new_d_buf = malloc(new_size)) == NULL)
5665194Sjohnz return (ELFSIGN_FAILED);
5675194Sjohnz
5685194Sjohnz (void) memcpy(new_d_buf, data->d_buf, old_size);
5695194Sjohnz (void) strlcpy(new_d_buf + old_size, elf_section,
5705194Sjohnz new_size - old_size);
5715194Sjohnz data->d_buf = new_d_buf;
5725194Sjohnz data->d_size = new_size;
5735194Sjohnz data->d_align = 1;
5745194Sjohnz /*
5755194Sjohnz * Add the section name passed in to the end of the file.
5765194Sjohnz * Initialize the fields in the Section Header that
5775194Sjohnz * libelf will not fill in.
5785194Sjohnz */
5795194Sjohnz if ((sig_scn = elf_newscn(ess->es_elf)) == 0) {
5805194Sjohnz cryptodebug("elf_newscn() failed: %s",
5815194Sjohnz elf_errmsg(-1));
5825194Sjohnz return (ELFSIGN_FAILED);
5835194Sjohnz }
5845194Sjohnz if (gelf_getshdr(sig_scn, &shdr) == 0) {
5855194Sjohnz cryptodebug("gelf_getshdr() failed: %s",
5865194Sjohnz elf_errmsg(-1));
5875194Sjohnz return (ELFSIGN_FAILED);
5885194Sjohnz }
5895194Sjohnz shdr.sh_name = old_size;
5905194Sjohnz shdr.sh_type = SHT_SUNW_SIGNATURE;
5915194Sjohnz shdr.sh_flags = SHF_EXCLUDE;
5925194Sjohnz shdr.sh_addr = 0;
5935194Sjohnz shdr.sh_link = 0;
5945194Sjohnz shdr.sh_info = 0;
5955194Sjohnz shdr.sh_size = 0;
5965194Sjohnz shdr.sh_offset = sig_offset;
5975194Sjohnz shdr.sh_addralign = 1;
5985194Sjohnz
5995194Sjohnz /*
6005194Sjohnz * Flush the changes to the underlying elf32 or elf64
6015194Sjohnz * section header.
6025194Sjohnz */
6035194Sjohnz if (gelf_update_shdr(sig_scn, &shdr) == 0) {
6045194Sjohnz cryptodebug("gelf_update_shdr failed");
6055194Sjohnz return (ELFSIGN_FAILED);
6065194Sjohnz }
6075194Sjohnz
6085194Sjohnz if ((data = elf_newdata(sig_scn)) == NULL) {
6095194Sjohnz cryptodebug("can't add elf data area for %s: %s",
6105194Sjohnz elf_section, elf_errmsg(-1));
6115194Sjohnz return (ELFSIGN_FAILED);
6125194Sjohnz }
6135194Sjohnz if (elfsign_adjustoffsets(ess, scn,
6145194Sjohnz old_size + strlen(elf_section) + 1) != ELFSIGN_SUCCESS) {
6155194Sjohnz cryptodebug("can't adjust for new section name %s",
6165194Sjohnz elf_section);
6175194Sjohnz return (ELFSIGN_FAILED);
6185194Sjohnz }
6195194Sjohnz } else {
6205194Sjohnz if (sig_scn == NULL) {
6215194Sjohnz cryptodebug("can't find signature section");
6225194Sjohnz *fsspp = NULL;
6235194Sjohnz return (ELFSIGN_NOTSIGNED);
6245194Sjohnz }
6255194Sjohnz if ((data = elf_getdata(sig_scn, NULL)) == 0) {
6265194Sjohnz cryptodebug("can't get section data for %s",
6275194Sjohnz elf_section);
6285194Sjohnz return (ELFSIGN_FAILED);
6295194Sjohnz }
6305194Sjohnz }
6315194Sjohnz
6325194Sjohnz if (ES_ACTISUPDATE(action)) {
6335194Sjohnz fssize = offsetof(struct filesignatures, _u1);
6345194Sjohnz if (*fsspp != NULL) {
6355194Sjohnz fsgp = &(*fsspp)->filesig_sig;
6365194Sjohnz for (fscnt = 0; fscnt < (*fsspp)->filesig_cnt;
6375194Sjohnz fscnt++) {
6385194Sjohnz fsgpnext = filesig_next(fsgp);
6395194Sjohnz fssize += (char *)(fsgpnext) - (char *)(fsgp);
6405194Sjohnz fsgp = fsgpnext;
6415194Sjohnz }
6425194Sjohnz }
6435194Sjohnz if (shdr.sh_addr != 0) {
6445194Sjohnz cryptodebug("section %s is part of a loadable segment, "
6455194Sjohnz "it cannot be changed.\n", elf_section);
6465194Sjohnz return (ELFSIGN_FAILED);
6475194Sjohnz }
6485194Sjohnz if ((data->d_buf = malloc(fssize)) == NULL)
6495194Sjohnz return (ELFSIGN_FAILED);
6505194Sjohnz if (*fsspp != NULL) {
6515194Sjohnz (void) memcpy(data->d_buf, *fsspp, fssize);
6525194Sjohnz (void) elfsign_switch(ess,
6535194Sjohnz (struct filesignatures *)data->d_buf, action);
6545194Sjohnz }
6555194Sjohnz data->d_size = fssize;
6565194Sjohnz data->d_align = 1;
6575194Sjohnz data->d_type = ELF_T_BYTE;
6585194Sjohnz cryptodebug("elfsign_signature: data->d_size = %d",
6595194Sjohnz data->d_size);
6605194Sjohnz if (elfsign_adjustoffsets(ess, sig_scn, fssize) !=
6615194Sjohnz ELFSIGN_SUCCESS) {
6625194Sjohnz cryptodebug("can't adjust for revised signature "
6635194Sjohnz "section contents");
6645194Sjohnz return (ELFSIGN_FAILED);
6655194Sjohnz }
6665194Sjohnz } else {
6675194Sjohnz *fsspp = malloc(data->d_size);
6685194Sjohnz if (*fsspp == NULL)
6695194Sjohnz return (ELFSIGN_FAILED);
6705194Sjohnz (void) memcpy(*fsspp, data->d_buf, data->d_size);
6715194Sjohnz if (elfsign_switch(ess, *fsspp, ES_GET) != ELFSIGN_SUCCESS) {
6725194Sjohnz free(*fsspp);
6735194Sjohnz *fsspp = NULL;
6745194Sjohnz return (ELFSIGN_FAILED);
6755194Sjohnz }
6765194Sjohnz *fslen = data->d_size;
6775194Sjohnz }
6785194Sjohnz
6795194Sjohnz return (ELFSIGN_SUCCESS);
6805194Sjohnz }
6815194Sjohnz
6825194Sjohnz static ELFsign_status_t
elfsign_adjustoffsets(ELFsign_t ess,Elf_Scn * scn,uint64_t new_size)6835194Sjohnz elfsign_adjustoffsets(ELFsign_t ess, Elf_Scn *scn, uint64_t new_size)
6845194Sjohnz {
6855194Sjohnz GElf_Ehdr elfehdr;
6865194Sjohnz GElf_Shdr shdr;
6875194Sjohnz uint64_t prev_end, scn_offset;
6885194Sjohnz char *name;
6895194Sjohnz Elf_Scn *scnp;
6905194Sjohnz Elf_Data *data;
6915194Sjohnz ELFsign_status_t retval = ELFSIGN_FAILED;
6925194Sjohnz struct scninfo {
6935194Sjohnz struct scninfo *scni_next;
6945194Sjohnz Elf_Scn *scni_scn;
6955194Sjohnz uint64_t scni_offset;
6965194Sjohnz } *scnip = NULL, *tmpscnip, **scnipp;
6975194Sjohnz
6985194Sjohnz /* get the size of the current section */
6995194Sjohnz if (gelf_getshdr(scn, &shdr) == NULL)
7005194Sjohnz return (ELFSIGN_FAILED);
7015194Sjohnz if (shdr.sh_size == new_size)
7025194Sjohnz return (ELFSIGN_SUCCESS);
7035194Sjohnz scn_offset = shdr.sh_offset;
7045194Sjohnz name = elf_strptr(ess->es_elf, ess->es_shstrndx,
7055194Sjohnz (size_t)shdr.sh_name);
7065194Sjohnz if (shdr.sh_flags & SHF_ALLOC && ess->es_has_phdr) {
7075194Sjohnz cryptodebug("elfsign_adjustoffsets: "
7085194Sjohnz "can't move allocated section %s", name ? name : "NULL");
7095194Sjohnz return (ELFSIGN_FAILED);
7105194Sjohnz }
7115194Sjohnz
7125194Sjohnz /* resize the desired section */
7135194Sjohnz cryptodebug("elfsign_adjustoffsets: "
7145194Sjohnz "resizing %s at 0x%llx from 0x%llx to 0x%llx",
7155194Sjohnz name ? name : "NULL", shdr.sh_offset, shdr.sh_size, new_size);
7165194Sjohnz shdr.sh_size = new_size;
7175194Sjohnz if (gelf_update_shdr(scn, &shdr) == 0) {
7185194Sjohnz cryptodebug("gelf_update_shdr failed");
7195194Sjohnz goto bad;
7205194Sjohnz }
7215194Sjohnz prev_end = shdr.sh_offset + shdr.sh_size;
7225194Sjohnz
7235194Sjohnz /*
7245194Sjohnz * find sections whose data follows the changed section
7255194Sjohnz * must scan all sections since section data may not
7265194Sjohnz * be in same order as section headers
7275194Sjohnz */
7285194Sjohnz scnp = elf_getscn(ess->es_elf, 0); /* "seek" to start */
7295194Sjohnz while ((scnp = elf_nextscn(ess->es_elf, scnp)) != NULL) {
7305194Sjohnz if (gelf_getshdr(scnp, &shdr) == NULL)
7315194Sjohnz goto bad;
7325194Sjohnz if (shdr.sh_offset <= scn_offset)
7335194Sjohnz continue;
7345194Sjohnz name = elf_strptr(ess->es_elf, ess->es_shstrndx,
7355194Sjohnz (size_t)shdr.sh_name);
7365194Sjohnz if (shdr.sh_flags & SHF_ALLOC && ess->es_has_phdr) {
7375194Sjohnz if (shdr.sh_type == SHT_NOBITS) {
7385194Sjohnz /* .bss can occasionally overlap .shrtab */
7395194Sjohnz continue;
7405194Sjohnz }
7415194Sjohnz cryptodebug("elfsign_adjustoffsets: "
7425194Sjohnz "can't move allocated section %s",
7435194Sjohnz name ? name : "NULL");
7445194Sjohnz goto bad;
7455194Sjohnz }
7465194Sjohnz /*
7475194Sjohnz * force reading of data to memory image
7485194Sjohnz */
7495194Sjohnz data = NULL;
7505194Sjohnz while ((data = elf_rawdata(scnp, data)) != NULL)
7515194Sjohnz ;
7525194Sjohnz /*
7535194Sjohnz * capture section information
7545194Sjohnz * insert into list in order of sh_offset
7555194Sjohnz */
7565194Sjohnz cryptodebug("elfsign_adjustoffsets: "
7575194Sjohnz "may have to adjust section %s, offset 0x%llx",
7585194Sjohnz name ? name : "NULL", shdr.sh_offset);
7595194Sjohnz tmpscnip = (struct scninfo *)malloc(sizeof (struct scninfo));
7605194Sjohnz if (tmpscnip == NULL) {
7615194Sjohnz cryptodebug("elfsign_adjustoffsets: "
7625194Sjohnz "memory allocation failure");
7635194Sjohnz goto bad;
7645194Sjohnz }
7655194Sjohnz tmpscnip->scni_scn = scnp;
7665194Sjohnz tmpscnip->scni_offset = shdr.sh_offset;
7675194Sjohnz for (scnipp = &scnip; *scnipp != NULL;
7685194Sjohnz scnipp = &(*scnipp)->scni_next) {
7695194Sjohnz if ((*scnipp)->scni_offset > tmpscnip->scni_offset)
7705194Sjohnz break;
7715194Sjohnz }
7725194Sjohnz tmpscnip->scni_next = *scnipp;
7735194Sjohnz *scnipp = tmpscnip;
7745194Sjohnz }
7755194Sjohnz
7765194Sjohnz /* move following sections as necessary */
7775194Sjohnz for (tmpscnip = scnip; tmpscnip != NULL;
7785194Sjohnz tmpscnip = tmpscnip->scni_next) {
7795194Sjohnz scnp = tmpscnip->scni_scn;
7805194Sjohnz if (gelf_getshdr(scnp, &shdr) == NULL) {
7815194Sjohnz cryptodebug("elfsign_adjustoffsets: "
7825194Sjohnz "elf_getshdr for section %d failed",
7835194Sjohnz elf_ndxscn(scnp));
7845194Sjohnz goto bad;
7855194Sjohnz }
7865194Sjohnz if (shdr.sh_offset >= prev_end)
7875194Sjohnz break;
7885194Sjohnz prev_end = (prev_end + shdr.sh_addralign - 1) &
7895194Sjohnz (-shdr.sh_addralign);
7905194Sjohnz name = elf_strptr(ess->es_elf, ess->es_shstrndx,
7915194Sjohnz (size_t)shdr.sh_name);
7925194Sjohnz cryptodebug("elfsign_adjustoffsets: "
7935194Sjohnz "moving %s size 0x%llx from 0x%llx to 0x%llx",
7945194Sjohnz name ? name : "NULL", shdr.sh_size,
7955194Sjohnz shdr.sh_offset, prev_end);
7965194Sjohnz shdr.sh_offset = prev_end;
7975194Sjohnz if (gelf_update_shdr(scnp, &shdr) == 0) {
7985194Sjohnz cryptodebug("gelf_update_shdr failed");
7995194Sjohnz goto bad;
8005194Sjohnz }
8015194Sjohnz prev_end = shdr.sh_offset + shdr.sh_size;
8025194Sjohnz }
8035194Sjohnz
8045194Sjohnz /*
8055194Sjohnz * adjust section header offset in elf header
8065194Sjohnz */
8075194Sjohnz if (gelf_getehdr(ess->es_elf, &elfehdr) == NULL) {
8085194Sjohnz cryptodebug("elf_getehdr() failed: %s", elf_errmsg(-1));
8095194Sjohnz goto bad;
8105194Sjohnz }
8115194Sjohnz if (elfehdr.e_shoff < prev_end) {
8125194Sjohnz if (ess->es_ei_class == ELFCLASS32)
8135194Sjohnz prev_end = (prev_end + ELF32_FSZ_OFF - 1) &
8145194Sjohnz (-ELF32_FSZ_OFF);
8155194Sjohnz else if (ess->es_ei_class == ELFCLASS64)
8165194Sjohnz prev_end = (prev_end + ELF64_FSZ_OFF - 1) &
8175194Sjohnz (-ELF64_FSZ_OFF);
8185194Sjohnz cryptodebug("elfsign_adjustoffsets: "
8195194Sjohnz "move sh_off from 0x%llx to 0x%llx",
8205194Sjohnz elfehdr.e_shoff, prev_end);
8215194Sjohnz elfehdr.e_shoff = prev_end;
8225194Sjohnz if (gelf_update_ehdr(ess->es_elf, &elfehdr) == 0) {
8235194Sjohnz cryptodebug("elf_update_ehdr() failed: %s",
8245194Sjohnz elf_errmsg(-1));
8255194Sjohnz goto bad;
8265194Sjohnz }
8275194Sjohnz }
8285194Sjohnz
8295194Sjohnz retval = ELFSIGN_SUCCESS;
8305194Sjohnz
8315194Sjohnz bad:
8325194Sjohnz while (scnip != NULL) {
8335194Sjohnz tmpscnip = scnip->scni_next;
8345194Sjohnz free(scnip);
8355194Sjohnz scnip = tmpscnip;
8365194Sjohnz }
8375194Sjohnz return (retval);
8385194Sjohnz }
8395194Sjohnz
8405194Sjohnz 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)8415194Sjohnz elfsign_insert_dso(ELFsign_t ess,
8425194Sjohnz struct filesignatures *fssp,
8435194Sjohnz const char *dn,
8445194Sjohnz int dn_len,
8455194Sjohnz const uchar_t *sig,
8465194Sjohnz int sig_len,
8475194Sjohnz const char *oid,
8485194Sjohnz int oid_len)
8495194Sjohnz {
8505194Sjohnz return (filesig_insert_dso(fssp, ess->es_version, dn, dn_len,
8515194Sjohnz sig, sig_len, oid, oid_len));
8525194Sjohnz }
8535194Sjohnz
8545194Sjohnz /*ARGSUSED*/
8555194Sjohnz filesig_vers_t
elfsign_extract_sig(ELFsign_t ess,struct filesignatures * fssp,uchar_t * sig,size_t * sig_len)8565194Sjohnz elfsign_extract_sig(ELFsign_t ess,
8575194Sjohnz struct filesignatures *fssp,
8585194Sjohnz uchar_t *sig,
8595194Sjohnz size_t *sig_len)
8605194Sjohnz {
8615194Sjohnz struct filesig_extraction fsx;
8625194Sjohnz filesig_vers_t version;
8635194Sjohnz
8645194Sjohnz if (fssp == NULL)
8655194Sjohnz return (FILESIG_UNKNOWN);
8665194Sjohnz if (fssp->filesig_cnt != 1)
8675194Sjohnz return (FILESIG_UNKNOWN);
8685194Sjohnz version = filesig_extract(&fssp->filesig_sig, &fsx);
8695194Sjohnz switch (version) {
8705194Sjohnz case FILESIG_VERSION1:
8715194Sjohnz case FILESIG_VERSION2:
8725194Sjohnz case FILESIG_VERSION3:
8735194Sjohnz case FILESIG_VERSION4:
8745194Sjohnz if (*sig_len >= fsx.fsx_sig_len) {
8755194Sjohnz (void) memcpy((char *)sig, (char *)fsx.fsx_signature,
8765194Sjohnz *sig_len);
8775194Sjohnz *sig_len = fsx.fsx_sig_len;
8785194Sjohnz } else
8795194Sjohnz version = FILESIG_UNKNOWN;
8805194Sjohnz break;
8815194Sjohnz default:
8825194Sjohnz version = FILESIG_UNKNOWN;
8835194Sjohnz break;
8845194Sjohnz }
8855194Sjohnz
8865194Sjohnz if (ess->es_version == FILESIG_UNKNOWN) {
8875194Sjohnz ess->es_version = version;
8885194Sjohnz }
8895194Sjohnz
8905194Sjohnz return (version);
8915194Sjohnz }
8925194Sjohnz
8935194Sjohnz static ELFsign_status_t
elfsign_hash_common(ELFsign_t ess,uchar_t * hash,size_t * hash_len,boolean_t hash_mem_resident)8945194Sjohnz elfsign_hash_common(ELFsign_t ess, uchar_t *hash, size_t *hash_len,
8955194Sjohnz boolean_t hash_mem_resident)
8965194Sjohnz {
8975194Sjohnz Elf_Scn *scn = NULL;
8985194Sjohnz ELFsign_status_t elfstat;
8995194Sjohnz GElf_Shdr shdr;
9005194Sjohnz SHA1_CTX ctx;
9015194Sjohnz
9025194Sjohnz /* The buffer must be large enough to hold the hash */
9035194Sjohnz if (*hash_len < SHA1_DIGEST_LENGTH)
9045194Sjohnz return (ELFSIGN_FAILED);
9055194Sjohnz
9065194Sjohnz bzero(hash, *hash_len);
9075194Sjohnz
9085194Sjohnz /* Initialize the digest session */
9095194Sjohnz SHA1Init(&ctx);
9105194Sjohnz
9115194Sjohnz scn = elf_getscn(ess->es_elf, 0); /* "seek" to start */
9125194Sjohnz (void) elf_errno();
9135194Sjohnz while ((scn = elf_nextscn(ess->es_elf, scn)) != 0) {
9145194Sjohnz char *name = NULL;
9155194Sjohnz Elf_Data *data = NULL;
9165194Sjohnz
9175194Sjohnz if (gelf_getshdr(scn, &shdr) == NULL) {
9185194Sjohnz elfstat = ELFSIGN_FAILED;
9195194Sjohnz goto done;
9205194Sjohnz }
9215194Sjohnz
9225194Sjohnz name = elf_strptr(ess->es_elf, ess->es_shstrndx,
9235194Sjohnz (size_t)shdr.sh_name);
9245194Sjohnz if (name == NULL)
9255194Sjohnz name = "NULL";
9265194Sjohnz
9275194Sjohnz if (!hash_mem_resident &&
9285194Sjohnz (ess->es_version == FILESIG_VERSION1 ||
9295194Sjohnz ess->es_version == FILESIG_VERSION3)) {
9305194Sjohnz /*
9315194Sjohnz * skip the signature section only
9325194Sjohnz */
9335194Sjohnz if (shdr.sh_type == SHT_SUNW_SIGNATURE) {
9345194Sjohnz cryptodebug("elfsign_hash: skipping %s", name);
9355194Sjohnz continue;
9365194Sjohnz }
9375194Sjohnz } else if (!(shdr.sh_flags & SHF_ALLOC)) {
9385194Sjohnz /*
9395194Sjohnz * select only memory resident sections
9405194Sjohnz */
9415194Sjohnz cryptodebug("elfsign_hash: skipping %s", name);
9425194Sjohnz continue;
9435194Sjohnz }
9445194Sjohnz
9455194Sjohnz /*
9465194Sjohnz * throw this section into the hash
9475194Sjohnz * use elf_rawdata for endian-independence
9485194Sjohnz * use elf_getdata to get update of .shstrtab
9495194Sjohnz */
9505194Sjohnz while ((data = (shdr.sh_type == SHT_STRTAB ?
9515194Sjohnz elf_getdata(scn, data) : elf_rawdata(scn, data))) != NULL) {
9525194Sjohnz if (data->d_buf == NULL) {
9535194Sjohnz cryptodebug("elfsign_hash: %s has NULL data",
9545194Sjohnz name);
9555194Sjohnz continue;
9565194Sjohnz }
9575194Sjohnz cryptodebug("elfsign_hash: updating hash "
9585194Sjohnz "with %s data size=%d", name, data->d_size);
9595194Sjohnz SHA1Update(&ctx, data->d_buf, data->d_size);
9605194Sjohnz }
9615194Sjohnz }
9625194Sjohnz if (elf_errmsg(0) != NULL) {
9635194Sjohnz cryptodebug("elfsign_hash: %s", elf_errmsg(-1));
9645194Sjohnz elfstat = ELFSIGN_FAILED;
9655194Sjohnz goto done;
9665194Sjohnz }
9675194Sjohnz
9685194Sjohnz SHA1Final(hash, &ctx);
9695194Sjohnz *hash_len = SHA1_DIGEST_LENGTH;
9705194Sjohnz { /* DEBUG START */
9715194Sjohnz const int hashstr_len = (*hash_len) * 2 + 1;
9725194Sjohnz char *hashstr = malloc(hashstr_len);
9735194Sjohnz
9745194Sjohnz if (hashstr != NULL) {
9755194Sjohnz tohexstr(hash, *hash_len, hashstr, hashstr_len);
9765194Sjohnz cryptodebug("hash value is: %s", hashstr);
9775194Sjohnz free(hashstr);
9785194Sjohnz }
9795194Sjohnz } /* DEBUG END */
9805194Sjohnz elfstat = ELFSIGN_SUCCESS;
9815194Sjohnz done:
9825194Sjohnz return (elfstat);
9835194Sjohnz }
9845194Sjohnz
9855194Sjohnz /*
9865194Sjohnz * elfsign_hash - return the hash of the ELF sections affecting execution.
9875194Sjohnz *
9885194Sjohnz * IN: ess, hash_len
9895194Sjohnz * OUT: hash, hash_len
9905194Sjohnz */
9915194Sjohnz ELFsign_status_t
elfsign_hash(ELFsign_t ess,uchar_t * hash,size_t * hash_len)9925194Sjohnz elfsign_hash(ELFsign_t ess, uchar_t *hash, size_t *hash_len)
9935194Sjohnz {
9945194Sjohnz return (elfsign_hash_common(ess, hash, hash_len, B_FALSE));
9955194Sjohnz }
9965194Sjohnz
9975194Sjohnz /*
9985194Sjohnz * elfsign_hash_mem_resident - return the hash of the ELF sections
9995194Sjohnz * with only memory resident sections.
10005194Sjohnz *
10015194Sjohnz * IN: ess, hash_len
10025194Sjohnz * OUT: hash, hash_len
10035194Sjohnz */
10045194Sjohnz ELFsign_status_t
elfsign_hash_mem_resident(ELFsign_t ess,uchar_t * hash,size_t * hash_len)10055194Sjohnz elfsign_hash_mem_resident(ELFsign_t ess, uchar_t *hash, size_t *hash_len)
10065194Sjohnz {
10075194Sjohnz return (elfsign_hash_common(ess, hash, hash_len, B_TRUE));
10085194Sjohnz }
10095194Sjohnz
10105194Sjohnz
10115194Sjohnz /*
10125194Sjohnz * elfsign_verify_signature - Verify the signature of the ELF object.
10135194Sjohnz *
10145194Sjohnz * IN: ess
10155194Sjohnz * OUT: esipp
10165194Sjohnz * RETURNS:
10175194Sjohnz * ELFsign_status_t
10185194Sjohnz */
10195194Sjohnz ELFsign_status_t
elfsign_verify_signature(ELFsign_t ess,struct ELFsign_sig_info ** esipp)10205194Sjohnz elfsign_verify_signature(ELFsign_t ess, struct ELFsign_sig_info **esipp)
10215194Sjohnz {
10225194Sjohnz ELFsign_status_t ret = ELFSIGN_FAILED;
10235194Sjohnz struct filesignatures *fssp;
10245194Sjohnz struct filesig *fsgp;
10255194Sjohnz size_t fslen;
10265194Sjohnz struct filesig_extraction fsx;
10275194Sjohnz uchar_t hash[SIG_MAX_LENGTH];
10285194Sjohnz size_t hash_len;
10295194Sjohnz ELFCert_t cert = NULL;
10305194Sjohnz int sigcnt;
10315194Sjohnz int nocert = 0;
10325194Sjohnz struct ELFsign_sig_info *esip = NULL;
10335194Sjohnz
10345194Sjohnz if (esipp != NULL) {
10355194Sjohnz esip = (struct ELFsign_sig_info *)
10365194Sjohnz calloc(1, sizeof (struct ELFsign_sig_info));
10375194Sjohnz *esipp = esip;
10385194Sjohnz }
10395194Sjohnz
10405194Sjohnz /*
10415194Sjohnz * Find out which cert we need, based on who signed the ELF object
10425194Sjohnz */
10435194Sjohnz if (elfsign_signatures(ess, &fssp, &fslen, ES_GET) != ELFSIGN_SUCCESS) {
10445194Sjohnz return (ELFSIGN_NOTSIGNED);
10455194Sjohnz }
10465194Sjohnz
10475194Sjohnz if (fssp->filesig_cnt < 1) {
10485194Sjohnz ret = ELFSIGN_FAILED;
10495194Sjohnz goto cleanup;
10505194Sjohnz }
10515194Sjohnz
10525194Sjohnz fsgp = &fssp->filesig_sig;
10535194Sjohnz
10545194Sjohnz /*
10555194Sjohnz * Scan the signature block, looking for a verifiable signature
10565194Sjohnz */
10575194Sjohnz for (sigcnt = 0; sigcnt < fssp->filesig_cnt;
10585194Sjohnz sigcnt++, fsgp = filesig_next(fsgp)) {
10595194Sjohnz ess->es_version = filesig_extract(fsgp, &fsx);
10605194Sjohnz cryptodebug("elfsign_verify_signature: version=%s",
10615194Sjohnz version_to_str(ess->es_version));
10625194Sjohnz switch (ess->es_version) {
10635194Sjohnz case FILESIG_VERSION1:
10645194Sjohnz case FILESIG_VERSION2:
10655194Sjohnz case FILESIG_VERSION3:
10665194Sjohnz case FILESIG_VERSION4:
10675194Sjohnz break;
10685194Sjohnz default:
10695194Sjohnz ret = ELFSIGN_FAILED;
10705194Sjohnz goto cleanup;
10715194Sjohnz }
10725194Sjohnz
10735194Sjohnz cryptodebug("elfsign_verify_signature: signer_DN=\"%s\"",
10745194Sjohnz fsx.fsx_signer_DN);
10755194Sjohnz cryptodebug("elfsign_verify_signature: algorithmOID=\"%s\"",
10765194Sjohnz fsx.fsx_sig_oid);
10775194Sjohnz /* return signer DN if requested */
10785194Sjohnz if (esipp != NULL) {
10795194Sjohnz esip->esi_format = fsx.fsx_format;
10805194Sjohnz if (esip->esi_signer != NULL)
10815194Sjohnz free(esip->esi_signer);
10825194Sjohnz esip->esi_signer = strdup(fsx.fsx_signer_DN);
10835194Sjohnz esip->esi_time = fsx.fsx_time;
10845194Sjohnz }
10855194Sjohnz
10865194Sjohnz /*
10875194Sjohnz * look for certificate
10885194Sjohnz */
10895194Sjohnz if (cert != NULL)
10905194Sjohnz elfcertlib_releasecert(ess, cert);
10915194Sjohnz
10925194Sjohnz /*
10935194Sjohnz * skip unfound certificates
10945194Sjohnz */
10955194Sjohnz if (!elfcertlib_getcert(ess, ess->es_certpath,
10965194Sjohnz fsx.fsx_signer_DN, &cert, ess->es_action)) {
10975194Sjohnz cryptodebug("unable to find certificate "
10985194Sjohnz "with DN=\"%s\" for %s",
10995194Sjohnz fsx.fsx_signer_DN, ess->es_pathname);
11005194Sjohnz nocert++;
11015194Sjohnz continue;
11025194Sjohnz }
11035194Sjohnz
11045194Sjohnz /*
11055194Sjohnz * skip unverified certificates
11065194Sjohnz * force verification of crypto certs
11075194Sjohnz */
11085194Sjohnz if ((ess->es_action == ES_GET_CRYPTO ||
110910732SAnthony.Scarpino@Sun.COM ess->es_action == ES_GET_FIPS140 ||
11105194Sjohnz strstr(fsx.fsx_signer_DN, ELFSIGN_CRYPTO)) &&
11115194Sjohnz !elfcertlib_verifycert(ess, cert)) {
11125194Sjohnz cryptodebug("elfsign_verify_signature: invalid cert");
11135194Sjohnz nocert++;
11145194Sjohnz continue;
11155194Sjohnz }
11165194Sjohnz
11175194Sjohnz /*
11185194Sjohnz * At this time the only sha1WithRSAEncryption is supported,
11195194Sjohnz * so check that is what we have and skip with anything else.
11205194Sjohnz */
11215194Sjohnz if (strcmp(fsx.fsx_sig_oid, OID_sha1WithRSAEncryption) != 0) {
11225194Sjohnz continue;
11235194Sjohnz }
11245194Sjohnz
11255194Sjohnz nocert = 0;
11265194Sjohnz /*
11275194Sjohnz * compute file hash
11285194Sjohnz */
11295194Sjohnz hash_len = sizeof (hash);
11305194Sjohnz if (elfsign_hash(ess, hash, &hash_len) != ELFSIGN_SUCCESS) {
11315194Sjohnz cryptodebug("elfsign_verify_signature:"
11325194Sjohnz " elfsign_hash failed");
11335194Sjohnz ret = ELFSIGN_FAILED;
11345194Sjohnz break;
11355194Sjohnz }
11365194Sjohnz
11375194Sjohnz { /* DEBUG START */
11385194Sjohnz const int sigstr_len = fsx.fsx_sig_len * 2 + 1;
11395194Sjohnz char *sigstr = malloc(sigstr_len);
11405194Sjohnz
11415194Sjohnz if (sigstr != NULL) {
11425194Sjohnz tohexstr(fsx.fsx_signature, fsx.fsx_sig_len,
11435194Sjohnz sigstr, sigstr_len);
11445194Sjohnz cryptodebug("signature value is: %s", sigstr);
11455194Sjohnz free(sigstr);
11465194Sjohnz }
11475194Sjohnz } /* DEBUG END */
11485194Sjohnz
11495194Sjohnz if (elfcertlib_verifysig(ess, cert,
11505194Sjohnz fsx.fsx_signature, fsx.fsx_sig_len, hash, hash_len)) {
11515194Sjohnz if (ess->es_sigvercallback)
11525194Sjohnz (ess->es_sigvercallback)
11535194Sjohnz (ess->es_callbackctx, fssp, fslen, cert);
11545194Sjohnz /*
11555194Sjohnz * The signature is verified!
11565194Sjohnz */
1157*12304SValerie.Fenwick@Oracle.COM ret = ELFSIGN_SUCCESS;
11585194Sjohnz }
11595194Sjohnz
11605194Sjohnz cryptodebug("elfsign_verify_signature: invalid signature");
11615194Sjohnz }
11625194Sjohnz
11635194Sjohnz cleanup:
11645194Sjohnz if (cert != NULL)
11655194Sjohnz elfcertlib_releasecert(ess, cert);
11665194Sjohnz
11675194Sjohnz free(fssp);
11685194Sjohnz if (ret == ELFSIGN_FAILED && nocert)
11695194Sjohnz ret = ELFSIGN_INVALID_CERTPATH;
11705194Sjohnz return (ret);
11715194Sjohnz }
11725194Sjohnz
11735194Sjohnz
11745194Sjohnz static uint32_t
elfsign_switch_uint32(uint32_t i)11755194Sjohnz elfsign_switch_uint32(uint32_t i)
11765194Sjohnz {
11775194Sjohnz return (((i & 0xff) << 24) | ((i & 0xff00) << 8) |
11785194Sjohnz ((i >> 8) & 0xff00) | ((i >> 24) & 0xff));
11795194Sjohnz }
11805194Sjohnz
11815194Sjohnz static uint64_t
elfsign_switch_uint64(uint64_t i)11825194Sjohnz elfsign_switch_uint64(uint64_t i)
11835194Sjohnz {
11845194Sjohnz return (((uint64_t)elfsign_switch_uint32(i) << 32) |
11855194Sjohnz (elfsign_switch_uint32(i >> 32)));
11865194Sjohnz }
11875194Sjohnz
11885194Sjohnz /*
11895194Sjohnz * If appropriate, switch the endianness of the filesignatures structure
11905194Sjohnz * Examine the structure only when it is in native endianness
11915194Sjohnz */
11925194Sjohnz static ELFsign_status_t
elfsign_switch(ELFsign_t ess,struct filesignatures * fssp,enum ES_ACTION action)11935194Sjohnz elfsign_switch(ELFsign_t ess, struct filesignatures *fssp,
11945194Sjohnz enum ES_ACTION action)
11955194Sjohnz {
11965194Sjohnz int fscnt;
11975194Sjohnz filesig_vers_t version;
11985194Sjohnz struct filesig *fsgp, *fsgpnext;
11995194Sjohnz
12005194Sjohnz if (ess->es_same_endian)
12015194Sjohnz return (ELFSIGN_SUCCESS);
12025194Sjohnz
12035194Sjohnz if (ES_ACTISUPDATE(action))
12045194Sjohnz fscnt = fssp->filesig_cnt;
12055194Sjohnz fssp->filesig_cnt = elfsign_switch_uint32(fssp->filesig_cnt);
12065194Sjohnz if (!ES_ACTISUPDATE(action))
12075194Sjohnz fscnt = fssp->filesig_cnt;
12085194Sjohnz
12095194Sjohnz fsgp = &(fssp)->filesig_sig;
12105194Sjohnz for (; fscnt > 0; fscnt--, fsgp = fsgpnext) {
12115194Sjohnz if (ES_ACTISUPDATE(action)) {
12125194Sjohnz version = fsgp->filesig_version;
12135194Sjohnz fsgpnext = filesig_next(fsgp);
12145194Sjohnz }
12155194Sjohnz fsgp->filesig_size =
12165194Sjohnz elfsign_switch_uint32(fsgp->filesig_size);
12175194Sjohnz fsgp->filesig_version =
12185194Sjohnz elfsign_switch_uint32(fsgp->filesig_version);
12195194Sjohnz if (!ES_ACTISUPDATE(action)) {
12205194Sjohnz version = fsgp->filesig_version;
12215194Sjohnz fsgpnext = filesig_next(fsgp);
12225194Sjohnz }
12235194Sjohnz switch (version) {
12245194Sjohnz case FILESIG_VERSION1:
12255194Sjohnz case FILESIG_VERSION2:
12265194Sjohnz fsgp->filesig_v1_dnsize =
12275194Sjohnz elfsign_switch_uint32(fsgp->filesig_v1_dnsize);
12285194Sjohnz fsgp->filesig_v1_sigsize =
12295194Sjohnz elfsign_switch_uint32(fsgp->filesig_v1_sigsize);
12305194Sjohnz fsgp->filesig_v1_oidsize =
12315194Sjohnz elfsign_switch_uint32(fsgp->filesig_v1_oidsize);
12325194Sjohnz break;
12335194Sjohnz case FILESIG_VERSION3:
12345194Sjohnz case FILESIG_VERSION4:
12355194Sjohnz fsgp->filesig_v3_time =
12365194Sjohnz elfsign_switch_uint64(fsgp->filesig_v3_time);
12375194Sjohnz fsgp->filesig_v3_dnsize =
12385194Sjohnz elfsign_switch_uint32(fsgp->filesig_v3_dnsize);
12395194Sjohnz fsgp->filesig_v3_sigsize =
12405194Sjohnz elfsign_switch_uint32(fsgp->filesig_v3_sigsize);
12415194Sjohnz fsgp->filesig_v3_oidsize =
12425194Sjohnz elfsign_switch_uint32(fsgp->filesig_v3_oidsize);
12435194Sjohnz break;
12445194Sjohnz default:
12455194Sjohnz cryptodebug("elfsign_switch: failed");
12465194Sjohnz return (ELFSIGN_FAILED);
12475194Sjohnz }
12485194Sjohnz }
12495194Sjohnz return (ELFSIGN_SUCCESS);
12505194Sjohnz }
12515194Sjohnz
12525194Sjohnz /*
12535194Sjohnz * get/put an integer value from/to a buffer, possibly of opposite endianness
12545194Sjohnz */
12555194Sjohnz void
elfsign_buffer_len(ELFsign_t ess,size_t * ip,uchar_t * cp,enum ES_ACTION action)12565194Sjohnz elfsign_buffer_len(ELFsign_t ess, size_t *ip, uchar_t *cp,
12575194Sjohnz enum ES_ACTION action)
12585194Sjohnz {
12595194Sjohnz uint32_t tmp;
12605194Sjohnz
12615194Sjohnz if (!ES_ACTISUPDATE(action)) {
12625194Sjohnz /* fetch integer from buffer */
12635194Sjohnz (void) memcpy(&tmp, cp, sizeof (tmp));
12645194Sjohnz if (!ess->es_same_endian) {
12655194Sjohnz tmp = elfsign_switch_uint32(tmp);
12665194Sjohnz }
12675194Sjohnz *ip = tmp;
12685194Sjohnz } else {
12695194Sjohnz /* put integer into buffer */
12705194Sjohnz tmp = *ip;
12715194Sjohnz if (!ess->es_same_endian) {
12725194Sjohnz tmp = elfsign_switch_uint32(tmp);
12735194Sjohnz }
12745194Sjohnz (void) memcpy(cp, &tmp, sizeof (tmp));
12755194Sjohnz }
12765194Sjohnz }
12775194Sjohnz
12785194Sjohnz char const *
elfsign_strerror(ELFsign_status_t elferror)12795194Sjohnz elfsign_strerror(ELFsign_status_t elferror)
12805194Sjohnz {
12815194Sjohnz char const *msg = NULL;
12825194Sjohnz
12835194Sjohnz switch (elferror) {
12845194Sjohnz case ELFSIGN_SUCCESS:
12855194Sjohnz msg = gettext("sign or verify of ELF object succeeded");
12865194Sjohnz break;
12875194Sjohnz case ELFSIGN_FAILED:
12885194Sjohnz msg = gettext("sign or verify of ELF object failed");
12895194Sjohnz break;
12905194Sjohnz case ELFSIGN_NOTSIGNED:
12915194Sjohnz msg = gettext("ELF object not signed");
12925194Sjohnz break;
12935194Sjohnz case ELFSIGN_INVALID_CERTPATH:
12945194Sjohnz msg = gettext("cannot access certificate");
12955194Sjohnz break;
12965194Sjohnz case ELFSIGN_INVALID_ELFOBJ:
12975194Sjohnz msg = gettext("unable to open as an ELF object");
12985194Sjohnz break;
12995194Sjohnz case ELFSIGN_UNKNOWN:
13005194Sjohnz default:
13015194Sjohnz msg = gettext("Unknown error");
13025194Sjohnz break;
13035194Sjohnz }
13045194Sjohnz
13055194Sjohnz return (msg);
13065194Sjohnz }
13075194Sjohnz
13085194Sjohnz boolean_t
elfsign_sig_info(struct filesignatures * fssp,struct ELFsign_sig_info ** esipp)13095194Sjohnz elfsign_sig_info(struct filesignatures *fssp, struct ELFsign_sig_info **esipp)
13105194Sjohnz {
13115194Sjohnz struct filesig_extraction fsx;
13125194Sjohnz struct ELFsign_sig_info *esip;
13135194Sjohnz
13145194Sjohnz esip = (struct ELFsign_sig_info *)
13155194Sjohnz calloc(1, sizeof (struct ELFsign_sig_info));
13165194Sjohnz *esipp = esip;
13175194Sjohnz if (esip == NULL)
13185194Sjohnz return (B_FALSE);
13195194Sjohnz
13205194Sjohnz switch (filesig_extract(&fssp->filesig_sig, &fsx)) {
13215194Sjohnz case FILESIG_VERSION1:
13225194Sjohnz case FILESIG_VERSION2:
13235194Sjohnz case FILESIG_VERSION3:
13245194Sjohnz case FILESIG_VERSION4:
13255194Sjohnz esip->esi_format = fsx.fsx_format;
13265194Sjohnz esip->esi_signer = strdup(fsx.fsx_signer_DN);
13275194Sjohnz esip->esi_time = fsx.fsx_time;
13285194Sjohnz break;
13295194Sjohnz default:
13305194Sjohnz free(esip);
13315194Sjohnz *esipp = NULL;
13325194Sjohnz }
13335194Sjohnz
13345194Sjohnz return (*esipp != NULL);
13355194Sjohnz }
13365194Sjohnz
13375194Sjohnz void
elfsign_sig_info_free(struct ELFsign_sig_info * esip)13385194Sjohnz elfsign_sig_info_free(struct ELFsign_sig_info *esip)
13395194Sjohnz {
13405194Sjohnz if (esip != NULL) {
13415194Sjohnz free(esip->esi_signer);
13425194Sjohnz free(esip);
13435194Sjohnz }
13445194Sjohnz }
1345