diff options
| author | johnz <none@none> | 2007-10-05 14:39:44 -0700 |
|---|---|---|
| committer | johnz <none@none> | 2007-10-05 14:39:44 -0700 |
| commit | df8bdeb362277e8d95a74d6c097341fe97409948 (patch) | |
| tree | e17a7e8f36d19701153abab9087e6770576cecb2 | |
| parent | e5819b60c39cd7dfc3604fd88187c257625b42d0 (diff) | |
| download | illumos-joyent-df8bdeb362277e8d95a74d6c097341fe97409948.tar.gz | |
6592898 open elfsign, libelfsign
23 files changed, 4519 insertions, 102 deletions
diff --git a/usr/src/Makefile b/usr/src/Makefile index 939309b811..4183c65613 100644 --- a/usr/src/Makefile +++ b/usr/src/Makefile @@ -243,7 +243,6 @@ EXPORT_SRC: @cd lib/gss_mechs/mech_krb5; pwd; $(MAKE) EXPORT_SRC @cd lib/gss_mechs/mech_spnego; pwd; $(MAKE) EXPORT_SRC @cd lib/libcrypt; pwd; $(MAKE) EXPORT_SRC - @cd $(CLOSED)/lib/libelfsign; pwd; $(MAKE) EXPORT_SRC @cd lib/libgss; pwd; $(MAKE) EXPORT_SRC @cd $(CLOSED)/lib/libike; pwd; $(MAKE) EXPORT_SRC @cd lib/libnsl; pwd; $(MAKE) EXPORT_SRC @@ -316,7 +315,6 @@ CRYPT_SRC: @cd lib/gss_mechs/mech_dh/backend; pwd; $(MAKE) CRYPT_SRC @cd lib/gss_mechs/mech_krb5; pwd; $(MAKE) CRYPT_SRC @cd lib/gss_mechs/mech_spnego; pwd; $(MAKE) CRYPT_SRC - @cd $(CLOSED)/lib/libelfsign; pwd; $(MAKE) CRYPT_SRC @cd $(CLOSED)/lib/libike; pwd; $(MAKE) CRYPT_SRC @cd lib/libnsl; pwd; $(MAKE) CRYPT_SRC @cd lib/libsasl; pwd; $(MAKE) CRYPT_SRC diff --git a/usr/src/Makefile.lint b/usr/src/Makefile.lint index 4da8cc8309..fb62aad36b 100644 --- a/usr/src/Makefile.lint +++ b/usr/src/Makefile.lint @@ -329,6 +329,7 @@ COMMON_SUBDIRS = \ lib/libdoor \ lib/libdtrace \ lib/libefi \ + lib/libelfsign \ lib/libexacct \ lib/libgen \ lib/libgss \ @@ -423,7 +424,6 @@ $(CLOSED_BUILD)COMMON_SUBDIRS += \ $(CLOSED)/cmd/tail \ $(CLOSED)/cmd/tr \ $(CLOSED)/lib/libc_i18n \ - $(CLOSED)/lib/libelfsign \ $(CLOSED)/lib/smartcard i386_SUBDIRS= \ diff --git a/usr/src/cmd/cmd-crypto/Makefile b/usr/src/cmd/cmd-crypto/Makefile index a046256e8a..05646720de 100644 --- a/usr/src/cmd/cmd-crypto/Makefile +++ b/usr/src/cmd/cmd-crypto/Makefile @@ -19,7 +19,7 @@ # CDDL HEADER END # # -# Copyright 2006 Sun Microsystems, Inc. All rights reserved. +# Copyright 2007 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # # ident "%Z%%M% %I% %E% SMI" @@ -31,11 +31,11 @@ SUBDIRS1 = \ cryptoadm \ decrypt \ digest \ + elfsign \ pktool \ kmfcfg $(CLOSED_BUILD)SUBDIRS1 += \ - $(CLOSED)/cmd/cmd-crypto/elfsign \ $(CLOSED)/cmd/cmd-crypto/kcfd SUBDIRS2 = \ diff --git a/usr/src/cmd/cmd-crypto/elfsign/Makefile b/usr/src/cmd/cmd-crypto/elfsign/Makefile new file mode 100644 index 0000000000..edaaf597b7 --- /dev/null +++ b/usr/src/cmd/cmd-crypto/elfsign/Makefile @@ -0,0 +1,55 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# +# Copyright 2007 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +#pragma ident "%Z%%M% %I% %E% SMI" +# + +PROG = elfsign + +OBJS = elfsign.o + +SRCS = $(OBJS:.o=.c) + +include $(SRC)/cmd/Makefile.cmd + +CFLAGS += $(CCVERBOSE) + +LDLIBS += -lkmf -lelfsign -lcryptoutil + +.KEEP_STATE: + +all: $(PROG) + +$(PROG): $(OBJS) + $(LINK.c) $(OBJS) -o $@ $(LDLIBS) $(DYNFLAGS) + $(POST_PROCESS) + +install: all $(ROOTPROG) + +clean: + $(RM) -f $(OBJS) $(PROG) + +lint: lint_SRCS + +include $(SRC)/cmd/Makefile.targ diff --git a/usr/src/cmd/cmd-crypto/elfsign/elfsign.c b/usr/src/cmd/cmd-crypto/elfsign/elfsign.c new file mode 100644 index 0000000000..1ee38b28eb --- /dev/null +++ b/usr/src/cmd/cmd-crypto/elfsign/elfsign.c @@ -0,0 +1,1441 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * Developer command for adding the signature section to an ELF object + * PSARC 2001/488 + * + * DEBUG Information: + * This command uses the cryptodebug() function from libcryptoutil. + * Set SUNW_CRYPTO_DEBUG to stderr or syslog for all debug to go to auth.debug + */ + +#include <stdio.h> +#include <stdlib.h> +#include <stdarg.h> +#include <limits.h> +#include <time.h> +#include <unistd.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <libintl.h> +#include <locale.h> +#include <errno.h> +#include <strings.h> +#include <langinfo.h> + +#include <cryptoutil.h> +#include <sys/crypto/elfsign.h> +#include <libelfsign.h> + +#include <kmfapi.h> + +#define SIGN "sign" +#define SIGN_OPTS "ac:e:F:k:P:T:v" +#define VERIFY "verify" +#define VERIFY_OPTS "c:e:v" +#define REQUEST "request" +#define REQUEST_OPTS "i:k:r:T:" +#define LIST "list" +#define LIST_OPTS "c:e:f:" + +enum cmd_e { + ES_SIGN, + ES_VERIFY, + ES_REQUEST, + ES_LIST +}; + +enum field_e { + FLD_UNKNOWN, + FLD_SUBJECT, + FLD_ISSUER, + FLD_FORMAT, + FLD_SIGNER, + FLD_TIME +}; + +#define MIN_ARGS 3 /* The minimum # args to do anything */ +#define ES_DEFAULT_KEYSIZE 1024 + +static struct { + enum cmd_e cmd; /* sub command: sign | verify | request */ + char *cert; /* -c <certificate_file> | */ + /* -r <certificate_request_file> */ + char **elfobj; /* -e <elf_object> */ + int elfcnt; + enum ES_ACTION es_action; + ELFsign_t ess; /* libelfsign opaque "state" */ + int extracnt; + enum field_e field; /* -f <field> */ + char internal_req; /* Sun internal certificate request */ + char *pinpath; /* -P <pin> */ + char *privpath; /* -k <private_key> */ + char *token_label; /* -T <token_label> */ + boolean_t verbose; /* chatty output */ +} cmd_info; + +enum ret_e { + EXIT_OKAY, + EXIT_INVALID_ARG, + EXIT_VERIFY_FAILED, + EXIT_CANT_OPEN_ELF_OBJECT, + EXIT_BAD_CERT, + EXIT_BAD_PRIVATEKEY, + EXIT_SIGN_FAILED, + EXIT_VERIFY_FAILED_UNSIGNED, + EXIT_CSR_FAILED, + EXIT_MEMORY_ERROR +}; + +struct field_s { + char *name; + enum field_e field; +} fields[] = { + { "subject", FLD_SUBJECT }, + { "issuer", FLD_ISSUER }, + { "format", FLD_FORMAT }, + { "signer", FLD_SIGNER }, + { "time", FLD_TIME }, + NULL, 0 +}; + +typedef enum ret_e ret_t; + +static void usage(void); +static ret_t getelfobj(char *); +static char *getpin(void); +static ret_t do_sign(char *); +static ret_t do_verify(char *); +static ret_t do_cert_request(char *); +static ret_t do_gen_esa(char *); +static ret_t do_list(char *); +static void es_error(const char *fmt, ...); +static char *time_str(time_t t); +static void sig_info_print(struct ELFsign_sig_info *esip); + +int +main(int argc, char **argv) +{ + extern char *optarg; + char *scmd = NULL; + char *opts; /* The set of flags for cmd */ + int errflag = 0; /* We had an options parse error */ + char c; /* current getopts flag */ + ret_t (*action)(char *); /* Function pointer for the action */ + ret_t ret; + + (void) setlocale(LC_ALL, ""); +#if !defined(TEXT_DOMAIN) /* Should be defiend by cc -D */ +#define TEXT_DOMAIN "SYS_TEST" /* Use this only if it weren't */ +#endif + (void) textdomain(TEXT_DOMAIN); + + cryptodebug_init("elfsign"); + + if (argc < MIN_ARGS) { + es_error(gettext("invalid number of arguments")); + usage(); + return (EXIT_INVALID_ARG); + } + + scmd = argv[1]; + cmd_info.cert = NULL; + cmd_info.elfobj = NULL; + cmd_info.elfcnt = 0; + cmd_info.es_action = ES_GET; + cmd_info.ess = NULL; + cmd_info.extracnt = 0; + cmd_info.field = FLD_UNKNOWN; + cmd_info.internal_req = '\0'; + cmd_info.pinpath = NULL; + cmd_info.privpath = NULL; + cmd_info.token_label = NULL; + cmd_info.verbose = B_FALSE; + + if (strcmp(scmd, SIGN) == 0) { + cmd_info.cmd = ES_SIGN; + opts = SIGN_OPTS; + cryptodebug("cmd=sign opts=%s", opts); + action = do_sign; + cmd_info.es_action = ES_UPDATE_RSA_SHA1; + } else if (strcmp(scmd, VERIFY) == 0) { + cmd_info.cmd = ES_VERIFY; + opts = VERIFY_OPTS; + cryptodebug("cmd=verify opts=%s", opts); + action = do_verify; + } else if (strcmp(scmd, REQUEST) == 0) { + cmd_info.cmd = ES_REQUEST; + opts = REQUEST_OPTS; + cryptodebug("cmd=request opts=%s", opts); + action = do_cert_request; + } else if (strcmp(scmd, LIST) == 0) { + cmd_info.cmd = ES_LIST; + opts = LIST_OPTS; + cryptodebug("cmd=list opts=%s", opts); + action = do_list; + } else { + es_error(gettext("Unknown sub-command: %s"), + scmd); + usage(); + return (EXIT_INVALID_ARG); + } + + /* + * Note: There is no need to check that optarg isn't NULL + * because getopt does that for us. + */ + while (!errflag && (c = getopt(argc - 1, argv + 1, opts)) != EOF) { + if (strchr("ceFihkPr", c) != NULL) + cryptodebug("c=%c, '%s'", c, optarg); + else + cryptodebug("c=%c", c); + + switch (c) { + case 'a': + /* not a normal sign operation, change the action */ + cmd_info.es_action = ES_GET; + action = do_gen_esa; + break; + case 'c': + cmd_info.cert = optarg; + break; + case 'e': + cmd_info.elfcnt++; + cmd_info.elfobj = (char **)realloc(cmd_info.elfobj, + sizeof (char *) * cmd_info.elfcnt); + if (cmd_info.elfobj == NULL) { + es_error(gettext( + "Too many elf objects specified.")); + return (EXIT_INVALID_ARG); + } + cmd_info.elfobj[cmd_info.elfcnt - 1] = optarg; + break; + case 'f': + { + struct field_s *fp; + cmd_info.field = FLD_UNKNOWN; + for (fp = fields; fp->name != NULL; fp++) { + if (strcasecmp(optarg, fp->name) == 0) { + cmd_info.field = fp->field; + break; + } + } + if (cmd_info.field == FLD_UNKNOWN) { + cryptodebug("Invalid field option"); + errflag++; + } + } + break; + case 'F': + if (strcasecmp(optarg, ES_FMT_RSA_MD5_SHA1) == 0) + cmd_info.es_action = ES_UPDATE_RSA_MD5_SHA1; + else if (strcasecmp(optarg, ES_FMT_RSA_SHA1) == 0) + cmd_info.es_action = ES_UPDATE_RSA_SHA1; + else { + cryptodebug("Invalid format option"); + errflag++; + } + break; + case 'i': /* Undocumented internal Sun use only */ + cmd_info.internal_req = *optarg; + break; + case 'k': + cmd_info.privpath = optarg; + if (cmd_info.token_label != NULL || + cmd_info.pinpath != NULL) + errflag++; + break; + case 'P': + cmd_info.pinpath = optarg; + if (cmd_info.privpath != NULL) + errflag++; + break; + case 'r': + cmd_info.cert = optarg; + break; + case 'T': + cmd_info.token_label = optarg; + if (cmd_info.privpath != NULL) + errflag++; + break; + case 'v': + cmd_info.verbose = B_TRUE; + break; + default: + errflag++; + } + } + + optind++; /* we skipped over subcommand */ + cmd_info.extracnt = argc - optind; + + if (cmd_info.extracnt != 0 && + cmd_info.cmd != ES_SIGN && cmd_info.cmd != ES_VERIFY) { + cryptodebug("Extra arguments, optind=%d, argc=%d", + optind, argc); + errflag++; + } + + switch (cmd_info.cmd) { + case ES_VERIFY: + if (cmd_info.elfcnt + argc - optind == 0) { + cryptodebug("Missing elfobj"); + errflag++; + } + break; + + case ES_SIGN: + if (((cmd_info.privpath == NULL) && + (cmd_info.token_label == NULL)) || + (cmd_info.cert == NULL) || + (cmd_info.elfcnt + argc - optind == 0)) { + cryptodebug("Missing privpath|token_label/cert/elfobj"); + errflag++; + } + break; + + case ES_REQUEST: + if (((cmd_info.privpath == NULL) && + (cmd_info.token_label == NULL)) || + (cmd_info.cert == NULL)) { + cryptodebug("Missing privpath|token_label/certreq"); + errflag++; + } + break; + case ES_LIST: + if ((cmd_info.cert != NULL) == (cmd_info.elfcnt > 0)) { + cryptodebug("Neither or both of cert/elfobj"); + errflag++; + } + break; + } + + if (errflag) { + usage(); + return (EXIT_INVALID_ARG); + } + + switch (cmd_info.cmd) { + case ES_REQUEST: + case ES_LIST: + ret = action(NULL); + break; + default: + { + int i; + ret_t iret; + + ret = EXIT_OKAY; + iret = EXIT_OKAY; + for (i = 0; i < cmd_info.elfcnt && + (ret == EXIT_OKAY || cmd_info.cmd != ES_SIGN); i++) { + iret = action(cmd_info.elfobj[i]); + if (iret > ret) + ret = iret; + } + for (i = optind; i < argc && + (ret == EXIT_OKAY || cmd_info.cmd != ES_SIGN); i++) { + iret = action(argv[i]); + if (iret > ret) + ret = iret; + } + break; + } + } + + if (cmd_info.elfobj != NULL) + free(cmd_info.elfobj); + + return (ret); +} + + +static void +usage(void) +{ +/* BEGIN CSTYLED */ + (void) fprintf(stderr, gettext( + "usage:\n" + "\telfsign sign [-a] [-v] [-e <elf_object>] -c <certificate_file>\n" + "\t\t[-F <format>] -k <private_key_file> [elf_object]..." + "\n" + "\telfsign sign [-a] [-v] [-e <elf_object>] -c <certificate_file>\n" + "\t\t[-F <format>] -T <token_label> [-P <pin_file>] [elf_object]..." + "\n\n" + "\telfsign verify [-v] [-c <certificate_file>] [-e <elf_object>]\n" + "\t\t[elf_object]..." + "\n\n" + "\telfsign request -r <certificate_request_file> -k <private_key_file>" + "\n" + "\telfsign request -r <certificate_request_file> -T <token_label>" + "\n\n" + "\telfsign list -f field -c <certificate_file>" + "\n" + "\telfsign list -f field -e <elf_object>" + "\n")); +/* END CSTYLED */ +} + +static ret_t +getelfobj(char *elfpath) +{ + ELFsign_status_t estatus; + ret_t ret; + + estatus = elfsign_begin(elfpath, cmd_info.es_action, &(cmd_info.ess)); + switch (estatus) { + case ELFSIGN_SUCCESS: + case ELFSIGN_RESTRICTED: + ret = EXIT_OKAY; + break; + case ELFSIGN_INVALID_ELFOBJ: + es_error(gettext( + "Unable to open %s as an ELF object."), + elfpath); + ret = EXIT_CANT_OPEN_ELF_OBJECT; + break; + default: + es_error(gettext("unexpected failure: %d"), estatus); + if (cmd_info.cmd == ES_SIGN) { + ret = EXIT_SIGN_FAILED; + } else if (cmd_info.cmd == ES_VERIFY) { + ret = EXIT_VERIFY_FAILED; + } + } + + return (ret); +} + +static ret_t +setcertpath(void) +{ + ELFsign_status_t estatus; + ret_t ret; + + if (cmd_info.cert == NULL) + return (EXIT_OKAY); + estatus = elfsign_setcertpath(cmd_info.ess, cmd_info.cert); + switch (estatus) { + case ELFSIGN_SUCCESS: + ret = EXIT_OKAY; + break; + case ELFSIGN_INVALID_CERTPATH: + if (cmd_info.cert != NULL) { + es_error(gettext("Unable to open %s as a certificate."), + cmd_info.cert); + } + ret = EXIT_BAD_CERT; + break; + default: + es_error(gettext("unusable certificate: %d"), cmd_info.cert); + if (cmd_info.cmd == ES_SIGN) { + ret = EXIT_SIGN_FAILED; + } else if (cmd_info.cmd == ES_VERIFY) { + ret = EXIT_VERIFY_FAILED; + } + } + + return (ret); +} + +/* + * getpin - return pointer to token PIN in static storage + */ +static char * +getpin(void) +{ + static char pinbuf[PASS_MAX + 1]; + char *pp; + FILE *pinfile; + + if (cmd_info.pinpath == NULL) + return (getpassphrase( + gettext("Enter PIN for PKCS#11 token: "))); + if ((pinfile = fopen(cmd_info.pinpath, "r")) == NULL) { + es_error(gettext("failed to open %s."), + cmd_info.pinpath); + return (NULL); + } + + pp = fgets(pinbuf, sizeof (pinbuf), pinfile); + (void) fclose(pinfile); + if (pp == NULL) { + es_error(gettext("failed to read PIN from %s."), + cmd_info.pinpath); + return (NULL); + } + pp = &pinbuf[strlen(pinbuf) - 1]; + if (*pp == '\n') + *pp = '\0'; + return (pinbuf); +} + +/* + * Add the .SUNW_signature sections for the ELF signature + */ +static ret_t +do_sign(char *object) +{ + ret_t ret; + ELFsign_status_t elfstat; + struct filesignatures *fssp = NULL; + size_t fs_len; + uchar_t sig[SIG_MAX_LENGTH]; + size_t sig_len = SIG_MAX_LENGTH; + uchar_t hash[SIG_MAX_LENGTH]; + size_t hash_len = SIG_MAX_LENGTH; + ELFCert_t cert = NULL; + char *dn; + size_t dn_len; + + cryptodebug("do_sign"); + if ((ret = getelfobj(object)) != EXIT_OKAY) + return (ret); + + if (cmd_info.token_label && + !elfcertlib_settoken(cmd_info.ess, cmd_info.token_label)) { + elfsign_end(cmd_info.ess); + es_error(gettext("Unable to access token: %s"), + cmd_info.token_label); + ret = EXIT_SIGN_FAILED; + goto cleanup; + } + + if ((ret = setcertpath()) != EXIT_OKAY) + goto cleanup; + + if (!elfcertlib_getcert(cmd_info.ess, cmd_info.cert, NULL, &cert, + cmd_info.es_action)) { + es_error(gettext("Unable to load certificate: %s"), + cmd_info.cert); + ret = EXIT_BAD_CERT; + goto cleanup; + } + + if (cmd_info.privpath != NULL) { + if (!elfcertlib_loadprivatekey(cmd_info.ess, cert, + cmd_info.privpath)) { + es_error(gettext("Unable to load private key: %s"), + cmd_info.privpath); + ret = EXIT_BAD_PRIVATEKEY; + goto cleanup; + } + } else { + char *pin = getpin(); + if (pin == NULL) { + es_error(gettext("Unable to get PIN")); + ret = EXIT_BAD_PRIVATEKEY; + goto cleanup; + } + if (!elfcertlib_loadtokenkey(cmd_info.ess, cert, + cmd_info.token_label, pin)) { + es_error(gettext("Unable to access private key " + "in token %s"), cmd_info.token_label); + ret = EXIT_BAD_PRIVATEKEY; + goto cleanup; + } + } + + /* + * Get the DN from the certificate. + */ + if ((dn = elfcertlib_getdn(cert)) == NULL) { + es_error(gettext("Unable to find DN in certificate %s"), + cmd_info.cert); + ret = EXIT_SIGN_FAILED; + goto cleanup; + } + dn_len = strlen(dn); + cryptodebug("DN = %s", dn); + + elfstat = elfsign_signatures(cmd_info.ess, &fssp, &fs_len, ES_GET); + if (elfstat != ELFSIGN_SUCCESS) { + if (elfstat != ELFSIGN_NOTSIGNED) { + es_error(gettext("Unable to retrieve existing " + "signature block in %s"), object); + ret = EXIT_SIGN_FAILED; + goto cleanup; + } + fssp = NULL; + /* + * force creation and naming of signature section + * so the hash doesn't change + */ + if (elfsign_signatures(cmd_info.ess, &fssp, &fs_len, + cmd_info.es_action) != ELFSIGN_SUCCESS) { + es_error(gettext("Unable to insert " + "signature block into %s"), object); + ret = EXIT_SIGN_FAILED; + goto cleanup; + } + } + + bzero(hash, sizeof (hash)); + if (elfsign_hash(cmd_info.ess, hash, &hash_len) != ELFSIGN_SUCCESS) { + es_error(gettext("Unable to calculate hash of ELF object %s"), + object); + ret = EXIT_SIGN_FAILED; + goto cleanup; + } + + bzero(sig, sizeof (sig)); + if (!elfcertlib_sign(cmd_info.ess, cert, + hash, hash_len, sig, &sig_len)) { + es_error(gettext("Unable to sign %s using key from %s"), + object, cmd_info.privpath ? + cmd_info.privpath : cmd_info.token_label); + ret = EXIT_SIGN_FAILED; + goto cleanup; + } + + { /* DEBUG START */ + const int sigstr_len = sizeof (char) * sig_len * 2 + 1; + char *sigstr = malloc(sigstr_len); + + tohexstr(sig, sig_len, sigstr, sigstr_len); + cryptodebug("sig value is: %s", sigstr); + free(sigstr); + } /* DEBUG END */ + + fssp = elfsign_insert_dso(cmd_info.ess, fssp, + dn, dn_len, sig, sig_len, NULL, 0); + if (fssp == NULL) { + es_error(gettext("Unable to prepare signature for %s"), + object); + ret = EXIT_SIGN_FAILED; + goto cleanup; + } + if (elfsign_signatures(cmd_info.ess, &fssp, &fs_len, + cmd_info.es_action) != ELFSIGN_SUCCESS) { + es_error(gettext("Unable to update %s: with signature"), + object); + ret = EXIT_SIGN_FAILED; + goto cleanup; + } + if (cmd_info.verbose || (cmd_info.elfcnt + cmd_info.extracnt) > 1) { + (void) fprintf(stdout, + gettext("elfsign: %s signed successfully.\n"), + object); + } + if (cmd_info.verbose) { + struct ELFsign_sig_info *esip; + + if (elfsign_sig_info(fssp, &esip)) { + sig_info_print(esip); + elfsign_sig_info_free(esip); + } + } + + ret = EXIT_OKAY; + +cleanup: + free(fssp); + bzero(sig, sig_len); + bzero(hash, hash_len); + + if (cert != NULL) + elfcertlib_releasecert(cmd_info.ess, cert); + if (cmd_info.ess != NULL) + elfsign_end(cmd_info.ess); + + return (ret); +} + +#define ESA_ERROR(str, esa_file) { \ + int realerrno = errno; \ + es_error(gettext(str), esa_file, strerror(realerrno)); \ + goto clean_esa; \ +} + +/* + * Generate the elfsign activation file (.esa) for this request. + * The .esa file should contain the signature of main binary + * signed with an unlimited certificate, the DN and its own signature. + * + * The format is as follows: + * ----------------------------- + * A | main signature length | + * ----------------------------- + * B | main signature (copy of | + * | signature from original | + * | limited-use binary | + * ----------------------------- + * C | signing DN length | + * ----------------------------- + * D | signing DN | + * ----------------------------- + * E | esa signature length | + * ----------------------------- + * F | esa signature = | + * | RSA(HASH(A||B) | + * ----------------------------- + * (lengths are in the same endianness as the original object) + * + * cmd_info.ess set for the main binary is correct here, since this + * is the only elf object we are actually dealing with during the .esa + * generation. + */ +static ret_t +do_gen_esa(char *object) +{ + ret_t ret; + + /* variables used for signing and writing to .esa file */ + char *elfobj_esa; + size_t elfobj_esa_len; + int esa_fd; + mode_t mode = S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH; + uchar_t *esa_buf = NULL; + size_t esa_buf_len = 0; + uchar_t hash[SIG_MAX_LENGTH], *hash_ptr = hash; + size_t hash_len = SIG_MAX_LENGTH; + uchar_t esa_sig[SIG_MAX_LENGTH]; + size_t esa_sig_len = SIG_MAX_LENGTH; + struct filesignatures *fssp = NULL; + size_t fslen; + ELFCert_t cert = NULL; + char *dn; + size_t dn_len; + uchar_t tmp_buf[sizeof (uint32_t)]; + int realerrno = 0; + + /* + * variables used for finding information on signer of main + * elfobject. + */ + uchar_t orig_signature[SIG_MAX_LENGTH]; + size_t orig_sig_len = sizeof (orig_signature); + + cryptodebug("do_gen_esa"); + if ((ret = getelfobj(object)) != EXIT_OKAY) + return (ret); + ret = EXIT_SIGN_FAILED; + + /* + * Find the certificate we need to sign the activation file with. + */ + if (!elfcertlib_getcert(cmd_info.ess, cmd_info.cert, NULL, &cert, + cmd_info.es_action)) { + es_error(gettext("Unable to load certificate: %s"), + cmd_info.cert); + ret = EXIT_BAD_CERT; + goto clean_esa; + } + + if (cmd_info.privpath != NULL) { + if (!elfcertlib_loadprivatekey(cmd_info.ess, cert, + cmd_info.privpath)) { + es_error(gettext("Unable to load private key: %s"), + cmd_info.privpath); + ret = EXIT_BAD_PRIVATEKEY; + goto clean_esa; + } + } else { + char *pin = getpin(); + + if (pin == NULL) { + cryptoerror(LOG_STDERR, gettext("Unable to get PIN")); + ret = EXIT_BAD_PRIVATEKEY; + goto clean_esa; + } + if (!elfcertlib_loadtokenkey(cmd_info.ess, cert, + cmd_info.token_label, pin)) { + es_error(gettext("Unable to access private key " + "in token %s"), cmd_info.token_label); + ret = EXIT_BAD_PRIVATEKEY; + goto clean_esa; + } + } + + /* + * Get the DN from the certificate. + */ + if ((dn = elfcertlib_getdn(cert)) == NULL) { + es_error(gettext("Unable to find DN in certifiate %s"), + cmd_info.cert); + goto clean_esa; + } + dn_len = strlen(dn); + cryptodebug("DN = %s", dn); + + /* + * Make sure they are not trying to sign .esa file with a + * limited certificate. + */ + if (strstr(dn, USAGELIMITED) != NULL) { + es_error(gettext("Activation file must be signed with a " + "certficate without %s."), USAGELIMITED); + goto clean_esa; + } + + /* + * Find information in the associated elfobject that will + * be needed to generate the activation file. + */ + if (elfsign_signatures(cmd_info.ess, &fssp, &fslen, ES_GET) != + ELFSIGN_SUCCESS) { + es_error(gettext("%s must be signed first, before an " + "associated activation file can be created."), + object); + goto clean_esa; + } + if (elfsign_extract_sig(cmd_info.ess, fssp, + orig_signature, &orig_sig_len) == FILESIG_UNKNOWN) { + es_error(gettext("elfsign can not create " + "an associated activation file for the " + "signature format of %s."), + object); + goto clean_esa; + } + { /* DEBUG START */ + const int sigstr_len = orig_sig_len * 2 + 1; + char *sigstr = malloc(sigstr_len); + + tohexstr(orig_signature, orig_sig_len, sigstr, sigstr_len); + cryptodebug("signature value is: %s", sigstr); + cryptodebug("sig size value is: %d", orig_sig_len); + free(sigstr); + } /* DEBUG END */ + + esa_buf_len = sizeof (uint32_t) + orig_sig_len; + esa_buf = malloc(esa_buf_len); + if (esa_buf == NULL) { + es_error(gettext("Unable to allocate memory for .esa buffer")); + goto clean_esa; + } + + /* + * Write eventual contents of .esa file to a temporary + * buffer, so we can sign it before writing out to + * the file. + */ + elfsign_buffer_len(cmd_info.ess, &orig_sig_len, esa_buf, ES_UPDATE); + (void) memcpy(esa_buf + sizeof (uint32_t), orig_signature, + orig_sig_len); + + if (elfsign_hash_esa(cmd_info.ess, esa_buf, esa_buf_len, + &hash_ptr, &hash_len) != ELFSIGN_SUCCESS) { + es_error(gettext("Unable to calculate activation hash")); + goto clean_esa; + } + + /* + * sign the buffer for the .esa file + */ + if (!elfcertlib_sign(cmd_info.ess, cert, + hash_ptr, hash_len, esa_sig, &esa_sig_len)) { + es_error(gettext("Unable to sign .esa data using key from %s"), + cmd_info.privpath ? + cmd_info.privpath : cmd_info.token_label); + goto clean_esa; + } + + { /* DEBUG START */ + const int sigstr_len = esa_sig_len * 2 + 1; + char *sigstr = malloc(sigstr_len); + + tohexstr(esa_sig, esa_sig_len, sigstr, sigstr_len); + cryptodebug("esa signature value is: %s", sigstr); + cryptodebug("esa size value is: %d", esa_sig_len); + free(sigstr); + } /* DEBUG END */ + + /* + * Create the empty activation file once we know + * we are working with the good data. + */ + elfobj_esa_len = strlen(object) + ESA_LEN + 1; + elfobj_esa = malloc(elfobj_esa_len); + + if (elfobj_esa == NULL) { + es_error(gettext("Unable to allocate buffer for esa filename")); + goto clean_esa; + } + + (void) strlcpy(elfobj_esa, object, elfobj_esa_len); + (void) strlcat(elfobj_esa, ESA, elfobj_esa_len); + + cryptodebug("Creating .esa file: %s", elfobj_esa); + + if ((esa_fd = open(elfobj_esa, O_WRONLY|O_CREAT|O_EXCL, mode)) == -1) { + ESA_ERROR("Unable to create activation file: %s. %s.", + elfobj_esa); + } + + if (write(esa_fd, esa_buf, esa_buf_len) != esa_buf_len) { + ESA_ERROR("Unable to write contents to %s. %s.", + elfobj_esa); + } + + { /* DEBUG START */ + const int sigstr_len = dn_len * 2 + 1; + char *sigstr = malloc(sigstr_len); + + tohexstr((uchar_t *)dn, dn_len, sigstr, sigstr_len); + cryptodebug("dn value is: %s", sigstr); + cryptodebug("dn size value is: %d", dn_len); + free(sigstr); + } /* DEBUG END */ + + elfsign_buffer_len(cmd_info.ess, &dn_len, tmp_buf, ES_UPDATE); + if (write(esa_fd, tmp_buf, sizeof (tmp_buf)) != sizeof (tmp_buf)) { + ESA_ERROR("Unable to write dn_len to %s. %s.", elfobj_esa); + } + + if (write(esa_fd, dn, dn_len) != dn_len) { + ESA_ERROR("Unable to write dn to %s. %s.", elfobj_esa); + } + + elfsign_buffer_len(cmd_info.ess, &esa_sig_len, tmp_buf, ES_UPDATE); + if (write(esa_fd, tmp_buf, sizeof (tmp_buf)) != sizeof (tmp_buf)) { + ESA_ERROR("Unable to write .esa signature len to %s. %s.", + elfobj_esa); + } + + if (write(esa_fd, esa_sig, esa_sig_len) != esa_sig_len) { + realerrno = errno; + es_error(gettext("Unable to write .esa signature. %s."), + strerror(realerrno)); + goto clean_esa; + } + + ret = EXIT_OKAY; + +clean_esa: + free(fssp); + if (esa_fd != -1) + (void) close(esa_fd); + + if (esa_buf != NULL) + free(esa_buf); + + bzero(esa_sig, esa_sig_len); + + if (cert != NULL) + elfcertlib_releasecert(cmd_info.ess, cert); + if (cmd_info.ess != NULL) + elfsign_end(cmd_info.ess); + + return (ret); +} + +/* + * Verify the signature of the object + * This subcommand is intended to be used by developers during their build + * processes. Therefore we can not assume that the certificate is in + * /etc/crypto/certs so we must use the path we got from the commandline. + */ +static ret_t +do_verify(char *object) +{ + ELFsign_status_t res; + struct ELFsign_sig_info *esip; + ret_t retval; + + cryptodebug("do_verify"); + if ((retval = getelfobj(object)) != EXIT_OKAY) + return (retval); + + if ((retval = setcertpath()) != EXIT_OKAY) { + elfsign_end(cmd_info.ess); + return (retval); + } + + res = elfsign_verify_signature(cmd_info.ess, &esip); + switch (res) { + case ELFSIGN_SUCCESS: + (void) fprintf(stdout, + gettext("elfsign: verification of %s passed.\n"), + object); + if (cmd_info.verbose) + sig_info_print(esip); + retval = EXIT_OKAY; + break; + case ELFSIGN_RESTRICTED: + (void) fprintf(stdout, + gettext("elfsign: verification of %s passed, " + "but restricted.\n"), object); + if (cmd_info.verbose) + sig_info_print(esip); + retval = EXIT_OKAY; + break; + case ELFSIGN_FAILED: + case ELFSIGN_INVALID_CERTPATH: + es_error(gettext("verification of %s failed."), + object); + if (cmd_info.verbose) + sig_info_print(esip); + retval = EXIT_VERIFY_FAILED; + break; + case ELFSIGN_NOTSIGNED: + es_error(gettext("no signature found in %s."), + object); + retval = EXIT_VERIFY_FAILED_UNSIGNED; + break; + default: + es_error(gettext("unexpected failure attempting verification " + "of %s."), object); + retval = EXIT_VERIFY_FAILED_UNSIGNED; + break; + } + + if (esip != NULL) + elfsign_sig_info_free(esip); + if (cmd_info.ess != NULL) + elfsign_end(cmd_info.ess); + return (retval); +} + +#define SET_VALUE(f, s) \ + kmfrv = f; \ + if (kmfrv != KMF_OK) { \ + char *e = NULL; \ + (void) KMF_GetKMFErrorString(kmfrv, &e); \ + cryptoerror(LOG_STDERR, \ + gettext("Failed to %s: %s\n"), \ + s, (e ? e : "unknown error")); \ + if (e) free(e); \ + goto cleanup; \ + } + +static KMF_RETURN +create_csr(char *dn) +{ + KMF_RETURN kmfrv = KMF_OK; + KMF_HANDLE_T kmfhandle = NULL; + KMF_CREATEKEYPAIR_PARAMS kp_params; + KMF_KEY_HANDLE pubk, prik; + KMF_X509_NAME csrSubject; + KMF_CSR_DATA csr; + KMF_ALGORITHM_INDEX sigAlg = KMF_ALGID_MD5WithRSA; + KMF_DATA signedCsr = { NULL, 0 }; + KMF_CONFIG_PARAMS config; + char *err; + + if ((kmfrv = KMF_Initialize(&kmfhandle, NULL, NULL)) != KMF_OK) { + (void) KMF_GetKMFErrorString(kmfrv, &err); + cryptoerror(LOG_STDERR, + gettext("Error initializing KMF: %s\n"), + (err ? err : "unknown error")); + if (err) + free(err); + return (kmfrv); + } + (void) memset(&csr, 0, sizeof (csr)); + (void) memset(&csrSubject, 0, sizeof (csrSubject)); + (void) memset(&kp_params, 0, sizeof (kp_params)); + + if (cmd_info.privpath != NULL) { + kp_params.kstype = KMF_KEYSTORE_OPENSSL; + kp_params.sslparms.keyfile = cmd_info.privpath; + kp_params.sslparms.format = KMF_FORMAT_ASN1; + } else if (cmd_info.token_label != NULL) { + + /* Get a PIN to store the private key in the token */ + char *pin = getpin(); + + if (pin == NULL) { + (void) KMF_Finalize(kmfhandle); + return (KMF_ERR_AUTH_FAILED); + } + + kp_params.kstype = KMF_KEYSTORE_PK11TOKEN; + kp_params.cred.cred = pin; + kp_params.cred.credlen = strlen(pin); + + (void) memset(&config, 0, sizeof (config)); + config.kstype = KMF_KEYSTORE_PK11TOKEN; + config.pkcs11config.label = cmd_info.token_label; + config.pkcs11config.readonly = FALSE; + kmfrv = KMF_ConfigureKeystore(kmfhandle, &config); + if (kmfrv != KMF_OK) { + goto cleanup; + } + } + + /* Create the RSA keypair */ + kp_params.keytype = KMF_RSA; + kp_params.keylength = ES_DEFAULT_KEYSIZE; + + kmfrv = KMF_CreateKeypair(kmfhandle, &kp_params, &prik, &pubk); + if (kmfrv != KMF_OK) { + (void) KMF_GetKMFErrorString(kmfrv, &err); + if (err != NULL) { + cryptoerror(LOG_STDERR, + gettext("Create RSA keypair failed: %s"), err); + free(err); + } + goto cleanup; + } + + kmfrv = KMF_DNParser(dn, &csrSubject); + if (kmfrv != KMF_OK) { + (void) KMF_GetKMFErrorString(kmfrv, &err); + if (err != NULL) { + cryptoerror(LOG_STDERR, + gettext("Error parsing subject name: %s\n"), err); + free(err); + } + goto cleanup; + } + + SET_VALUE(KMF_SetCSRPubKey(kmfhandle, &pubk, &csr), "keypair"); + + SET_VALUE(KMF_SetCSRVersion(&csr, 2), "version number"); + + SET_VALUE(KMF_SetCSRSubjectName(&csr, &csrSubject), "subject name"); + + SET_VALUE(KMF_SetCSRSignatureAlgorithm(&csr, sigAlg), + "SignatureAlgorithm"); + + if ((kmfrv = KMF_SignCSR(kmfhandle, &csr, &prik, &signedCsr)) == + KMF_OK) { + kmfrv = KMF_CreateCSRFile(&signedCsr, KMF_FORMAT_PEM, + cmd_info.cert); + } + +cleanup: + (void) KMF_FreeKMFKey(kmfhandle, &prik); + (void) KMF_FreeData(&signedCsr); + (void) KMF_FreeSignedCSR(&csr); + (void) KMF_Finalize(kmfhandle); + + return (kmfrv); +} + +static boolean_t +is_restricted(void) +{ + char nr[80]; /* Non-retail provider? big buffer for l10n */ + char *yeschar = nl_langinfo(YESSTR); + char *nochar = nl_langinfo(NOSTR); + + /* + * Find out if user will need an activation file. + * These questions cover cases #1 and #2 from the Jumbo Export + * Control case. The logic of these questions should not be modified + * without consulting the jumbo case, unless there is a new + * export case or a change in export/import regulations for Sun + * and Sun customers. + * Case #3 should be covered in the developer documentation. + */ +/* BEGIN CSTYLED */ + (void) fprintf(stdout, gettext("\n" +"The government of the United States of America restricts the export of \n" +"\"open cryptographic interfaces\", also known as \"crypto-with-a-hole\".\n" +"Due to this restriction, all providers for the Solaris cryptographic\n" +"framework must be signed, regardless of the country of origin.\n\n")); + + (void) fprintf(stdout, gettext( +"The terms \"retail\" and \"non-retail\" refer to export classifications \n" +"for products manufactured in the USA. These terms define the portion of the\n" +"world where the product may be shipped. Roughly speaking, \"retail\" is \n" +"worldwide (minus certain excluded nations) and \"non-retail\" is domestic \n" +"only (plus some highly favored nations). If your provider is subject to\n" +"USA export control, then you must obtain an export approval (classification)\n" +"from the government of the USA before exporting your provider. It is\n" +"critical that you specify the obtained (or expected, when used during \n" +"development) classification to the following questions so that your provider\n" +"will be appropriately signed.\n\n")); + + for (;;) { + (void) fprintf(stdout, gettext( +"Do you have retail export approval for use without restrictions based \n" +"on the caller (for example, IPsec)? [Yes/No] ")); +/* END CSTYLED */ + + (void) fflush(stdout); + + (void) fgets(nr, sizeof (nr), stdin); + if (nr == NULL) + goto demand_answer; + + nr[strlen(nr) - 1] = '\0'; + + if (strncasecmp(nochar, nr, 1) == 0) { +/* BEGIN CSTYLED */ + (void) fprintf(stdout, gettext("\n" +"If you have non-retail export approval for unrestricted use of your provider\n" +"by callers, are you also planning to receive retail approval by restricting \n" +"which export sensitive callers (for example, IPsec) may use your \n" +"provider? [Yes/No] ")); +/* END CSTYLED */ + + (void) fflush(stdout); + + (void) fgets(nr, sizeof (nr), stdin); + + /* + * flush standard input so any remaining text + * does not affect next read. + */ + (void) fflush(stdin); + + if (nr == NULL) + goto demand_answer; + + nr[strlen(nr) - 1] = '\0'; + + if (strncasecmp(nochar, nr, 1) == 0) { + return (B_FALSE); + } else if (strncasecmp(yeschar, nr, 1) == 0) { + return (B_TRUE); + } else + goto demand_answer; + + } else if (strncasecmp(yeschar, nr, 1) == 0) { + return (B_FALSE); + } + + demand_answer: + (void) fprintf(stdout, + gettext("You must specify an answer.\n\n")); + } +} + +#define CN_MAX_LENGTH 64 /* Verisign implementation limit */ +/* + * Generate a certificate request into the file named cmd_info.cert + */ +/*ARGSUSED*/ +static ret_t +do_cert_request(char *object) +{ + const char PartnerDNFMT[] = + "CN=%s, " + "OU=Class B, " + "%sOU=Solaris Cryptographic Framework, " + "OU=Partner Object Signing, " + "O=Sun Microsystems Inc"; + const char SunCDNFMT[] = + "CN=%s, " + "OU=Class B, " + "%sOU=Solaris Cryptographic Framework, " + "OU=Corporate Object Signing, " + "O=Sun Microsystems Inc"; + const char SunSDNFMT[] = + "CN=%s, " + "OU=Class B, " + "%sOU=Solaris Signed Execution, " + "OU=Corporate Object Signing, " + "O=Sun Microsystems Inc"; + const char *dnfmt = NULL; + char cn[CN_MAX_LENGTH + 1]; + char *dn = NULL; + size_t dn_len; + char *restriction = ""; + KMF_RETURN kmfret; + cryptodebug("do_cert_request"); + + /* + * Get the DN prefix from the user + */ + switch (cmd_info.internal_req) { + case 'c': + dnfmt = SunCDNFMT; + (void) fprintf(stdout, gettext( + "Enter Sun Microsystems, Inc. Release name.\n" + "This will be the prefix of the Certificate DN: ")); + break; + case 's': + dnfmt = SunSDNFMT; + (void) fprintf(stdout, gettext( + "Enter Sun Microsystems, Inc. Release name.\n" + "This will be the prefix of the Certificate DN: ")); + break; + default: + dnfmt = PartnerDNFMT; + (void) fprintf(stdout, gettext( + "Enter Company Name / Stock Symbol" + " or some other globally unique identifier.\n" + "This will be the prefix of the Certificate DN: ")); + break; + } + + (void) fgets(cn, sizeof (cn), stdin); + if ((cn == NULL) || (cn[0] == '\n')) { + es_error(gettext("you must specify a Certificate DN prefix")); + return (EXIT_INVALID_ARG); + } + + if (cn[strlen(cn) - 1] == '\n') { + cn[strlen(cn) - 1] = '\0'; /* chop trailing \n */ + } else { + es_error(gettext("You must specify a Certificate DN prefix " + "of no more than %d characters"), CN_MAX_LENGTH); + return (EXIT_INVALID_ARG); + } + + /* + * determine if there is an export restriction + */ + switch (cmd_info.internal_req) { + case 's': + restriction = ""; + break; + default: + restriction = is_restricted() ? USAGELIMITED ", " : ""; + break; + } + + /* Update DN string */ + dn_len = strlen(cn) + strlen(dnfmt) + strlen(restriction); + dn = malloc(dn_len + 1); + (void) snprintf(dn, dn_len, dnfmt, cn, restriction); + + cryptodebug("Generating Certificate request for DN: %s", dn); + kmfret = create_csr(dn); + free(dn); + if (kmfret == KMF_OK) + return (EXIT_OKAY); + else + return (EXIT_CSR_FAILED); +} + +static void +str_print(char *s) +{ + if (s == NULL) + return; + (void) fprintf(stdout, "%s\n", s); +} + +/*ARGSUSED*/ +static ret_t +do_list(char *object) +{ + ret_t retval; + + if (cmd_info.elfcnt > 0) { + ELFsign_status_t elfstat; + struct filesignatures *fssp = NULL; + size_t fs_len; + struct ELFsign_sig_info *esip; + + if ((retval = getelfobj(cmd_info.elfobj[0])) != EXIT_OKAY) + return (retval); + elfstat = elfsign_signatures(cmd_info.ess, + &fssp, &fs_len, ES_GET); + if (elfstat == ELFSIGN_SUCCESS) { + retval = EXIT_OKAY; + if (elfsign_sig_info(fssp, &esip)) { + switch (cmd_info.field) { + case FLD_FORMAT: + str_print(esip->esi_format); + break; + case FLD_SIGNER: + str_print(esip->esi_signer); + break; + case FLD_TIME: + if (esip->esi_time == 0) + retval = EXIT_INVALID_ARG; + else + str_print(time_str( + esip->esi_time)); + break; + default: + retval = EXIT_INVALID_ARG; + } + elfsign_sig_info_free(esip); + } + free(fssp); + } else + retval = EXIT_VERIFY_FAILED_UNSIGNED; + elfsign_end(cmd_info.ess); + } else { + ELFCert_t cert; + /* + * Initialize the ESS record here even though we are not + * actually opening any ELF files. + */ + if (elfsign_begin(NULL, ES_GET, &(cmd_info.ess)) != + ELFSIGN_SUCCESS) + return (EXIT_MEMORY_ERROR); + + if (elfcertlib_getcert(cmd_info.ess, cmd_info.cert, NULL, + &cert, cmd_info.es_action)) { + retval = EXIT_OKAY; + switch (cmd_info.field) { + case FLD_SUBJECT: + str_print(elfcertlib_getdn(cert)); + break; + case FLD_ISSUER: + str_print(elfcertlib_getissuer(cert)); + break; + default: + retval = EXIT_INVALID_ARG; + } + elfcertlib_releasecert(cmd_info.ess, cert); + } else + retval = EXIT_BAD_CERT; + elfsign_end(cmd_info.ess); + } + + return (retval); +} + +static void +es_error(const char *fmt, ...) +{ + char msgbuf[BUFSIZ]; + va_list args; + + va_start(args, fmt); + (void) vsnprintf(msgbuf, sizeof (msgbuf), fmt, args); + va_end(args); + (void) fflush(stdout); + cryptoerror(LOG_STDERR, "%s", msgbuf); + (void) fflush(stderr); +} + +static char * +time_str(time_t t) +{ + static char buf[80]; + char *bufp; + + bufp = buf; + if (strftime(buf, sizeof (buf), NULL, localtime(&t)) == 0) + bufp = ctime(&t); + return (bufp); +} + +static void +sig_info_print(struct ELFsign_sig_info *esip) +{ + if (esip == NULL) + return; + (void) fprintf(stdout, gettext("format: %s.\n"), esip->esi_format); + (void) fprintf(stdout, gettext("signer: %s.\n"), esip->esi_signer); + if (esip->esi_time == 0) + return; + (void) fprintf(stdout, gettext("signed on: %s.\n"), + time_str(esip->esi_time)); +} diff --git a/usr/src/lib/Makefile b/usr/src/lib/Makefile index d72f17bb58..bc98394c86 100644 --- a/usr/src/lib/Makefile +++ b/usr/src/lib/Makefile @@ -176,10 +176,8 @@ SUBDIRS += \ libscf \ libinetsvc \ librestart \ - libsched -$(CLOSED_BUILD)SUBDIRS += \ - $(CLOSED)/lib/libelfsign -SUBDIRS += \ + libsched \ + libelfsign \ pkcs11 .WAIT \ libpctx .WAIT \ libcpc \ @@ -350,6 +348,7 @@ HDRSUBDIRS= \ libdns_sd \ libdtrace \ libdtrace_jni \ + libelfsign \ libeti \ libfstyp \ libgen \ @@ -499,8 +498,7 @@ libdtrace: libproc libgen libctf libdtrace_jni: libuutil libdtrace libefi: libuuid libfstyp: libnvpair -$(CLOSED_BUILD)$(CLOSED)/lib/libelfsign: \ - $(CLOSED)/lib/libike libcryptoutil pkcs11 +libelfsign: libcryptoutil libkmf libidmap: libnsl libinetcfg: libnsl libsocket libdevinfo libkmf: libcryptoutil pkcs11 openssl diff --git a/usr/src/lib/libelfsign/Makefile b/usr/src/lib/libelfsign/Makefile new file mode 100644 index 0000000000..1d663e73d1 --- /dev/null +++ b/usr/src/lib/libelfsign/Makefile @@ -0,0 +1,70 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# + +# +# Copyright 2007 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# lib/libelfsign/Makefile +# +#ident "%Z%%M% %I% %E% SMI" + +include $(SRC)/lib/Makefile.lib + +SUBDIRS= $(MACH) + +POFILE= libelfsign.po +MSGFILES= common/elfsignlib.c common/elfcertlib.c +XGETFLAGS= -a + +HDRS= libelfsign.h +ROOTHDRDIR= $(ROOT)/usr/include +HDRDIR= common + +all:= TARGET= all +clean:= TARGET= clean +clobber:= TARGET= clobber +install:= TARGET= install +lint:= TARGET= lint + +.KEEP_STATE: + +all clean clobber install: .WAIT $(SUBDIRS) + +install_h: $(ROOTHDRS) + +check: $(CHECKHDRS) + +lint: $(SUBDIRS) + +$(SUBDIRS): FRC + @cd $@; pwd; $(MAKE) $(TARGET) + +$(POFILE): $(MSGFILES) + $(BUILDPO.msgfiles) + +_msg: $(MSGDOMAINPOFILE) + +include $(SRC)/Makefile.msg.targ + +FRC: + +include $(SRC)/lib/Makefile.targ diff --git a/usr/src/lib/libelfsign/Makefile.com b/usr/src/lib/libelfsign/Makefile.com new file mode 100644 index 0000000000..c5faa4ea7e --- /dev/null +++ b/usr/src/lib/libelfsign/Makefile.com @@ -0,0 +1,63 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# + +# +# Copyright 2007 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# ident "%Z%%M% %I% %E% SMI" +# + +LIBRARY = libelfsign.a +VERS = .1 + +OBJECTS = \ + elfcertlib.o \ + elfsignlib.o + +include $(SRC)/lib/Makefile.lib + +SRCDIR = ../common + +LIBS = $(DYNLIB) $(LINTLIB) +$(LINTLIB):= SRCS = $(SRCDIR)/$(LINTSRC) + +LDLIBS += -lmd -lelf -lkmf -lcryptoutil -lc + +MAPFILE = mapfile +MAPFILES = $(MAPFILE) + +CFLAGS += $(CCMT) $(CCVERBOSE) +CPPFLAGS += -D_REENTRANT -D_POSIX_PTHREAD_SEMANTICS + +.KEEP_STATE: + +all: $(LIBS) + +lint: lintcheck + +$(MAPFILE): $(SRCDIR)/$(MAPFILE).map + $(RM) $@ + $(CPP) $(SRCDIR)/$(MAPFILE).map > $@ + +CLEANFILES += $(MAPFILE) + +include $(SRC)/lib/Makefile.targ diff --git a/usr/src/lib/libelfsign/README b/usr/src/lib/libelfsign/README new file mode 100644 index 0000000000..059b4c7869 --- /dev/null +++ b/usr/src/lib/libelfsign/README @@ -0,0 +1,38 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# + +# +# Copyright 2007 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +#ident "%Z%%M% %I% %E% SMI" + + +Note that this library does not have a spec file. + +There are no interfaces in this library that are public so an abi_ +library for appcert and friends doesn't make any sense. If the APIs +here are ever elevated to a public taxonomy then a spec file needs to +be introduced. + +A 64 bit version of this library does not exist. +That isn't an issue since kcfd(1m) and elfsign(1) are 32 bit +applications and are the only consumers. diff --git a/usr/src/lib/libelfsign/common/elfcertlib.c b/usr/src/lib/libelfsign/common/elfcertlib.c new file mode 100644 index 0000000000..ddf24e6659 --- /dev/null +++ b/usr/src/lib/libelfsign/common/elfcertlib.c @@ -0,0 +1,644 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <limits.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <unistd.h> +#include <dirent.h> +#include <strings.h> +#include <stdio.h> +#include <stdlib.h> +#include <errno.h> +#include <sys/mman.h> +#include <md5.h> +#include <pthread.h> + +#include <cryptoutil.h> + +#include <kmfapi.h> +#include <sys/crypto/elfsign.h> +#include <libelfsign.h> + +#include <synch.h> + +const char _PATH_ELFSIGN_CRYPTO_CERTS[] = CRYPTO_CERTS_DIR; +const char _PATH_ELFSIGN_ETC_CERTS[] = ETC_CERTS_DIR; + +/* + * The CACERT and OBJCACERT are the Cryptographic Trust Anchors + * for the Solaris Cryptographic Framework. + */ +static const char _PATH_CRYPTO_CACERT[] = CRYPTO_CERTS_DIR "/CA"; +static const char _PATH_CRYPTO_OBJCACERT[] = CRYPTO_CERTS_DIR "/SUNWObjectCA"; +static ELFCert_t CACERT = NULL; +static ELFCert_t OBJCACERT = NULL; +static pthread_mutex_t ca_mutex = PTHREAD_MUTEX_INITIALIZER; + +static void elfcertlib_freecert(ELFsign_t, ELFCert_t); +static ELFCert_t elfcertlib_allocatecert(void); + +/* + * elfcertlib_verifycert - Verify the Cert with a Trust Anchor + * + * IN ess - elfsign context structure + * cert + * OUT NONE + * RETURN TRUE/FALSE + * + * We first setup the Trust Anchor (CA and SUNWObjectCA) certs + * if it hasn't been done already. We verify that the files on disk + * are those we expected. + * + * We then verify the given cert using the publickey of a TA. + * If the passed in cert is a TA or it has been verified already we + * short cut and return TRUE without futher validation. + */ +/*ARGSUSED*/ +boolean_t +elfcertlib_verifycert(ELFsign_t ess, ELFCert_t cert) +{ + KMF_RETURN rv; + if ((cert->c_verified == E_OK) || (cert->c_verified == E_IS_TA)) { + return (B_TRUE); + } + + (void) pthread_mutex_lock(&ca_mutex); + if (CACERT == NULL) { + (void) elfcertlib_getcert(ess, (char *)_PATH_CRYPTO_CACERT, + NULL, &CACERT, ES_GET); + } + if (OBJCACERT == NULL) { + (void) elfcertlib_getcert(ess, (char *)_PATH_CRYPTO_OBJCACERT, + NULL, &OBJCACERT, ES_GET); + } + (void) pthread_mutex_unlock(&ca_mutex); + + if (CACERT != NULL) { + rv = KMF_VerifyCertWithCert(ess->es_kmfhandle, + (const KMF_DATA *)&cert->c_cert, + (const KMF_DATA *)&CACERT->c_cert.certificate); + if (rv == KMF_OK) { + if (ess->es_certCAcallback != NULL) + (ess->es_certvercallback)(ess->es_callbackctx, + cert, CACERT); + cert->c_verified = E_OK; + return (B_TRUE); + } + } + + if (OBJCACERT != NULL) { + rv = KMF_VerifyCertWithCert(ess->es_kmfhandle, + (const KMF_DATA *)&cert->c_cert, + (const KMF_DATA *)&OBJCACERT->c_cert.certificate); + if (rv == KMF_OK) { + if (ess->es_certCAcallback != NULL) + (ess->es_certvercallback)(ess->es_callbackctx, + cert, OBJCACERT); + cert->c_verified = E_OK; + return (B_TRUE); + } + } + + return (B_FALSE); +} + +/* + * elfcertlib_getcert - Get the certificate for signer_DN + * + * IN ess - elfsign context structure + * cert_pathname - path to cert (May be NULL) + * signer_DN - The DN we are looking for (May be NULL) + * action - indicates crypto verification call + * OUT certp - allocated/loaded ELFCert_t + * + * If the cert_pathname is passed use it and don't search. + * Otherwise, go looking in certificate directories + */ +boolean_t +elfcertlib_getcert(ELFsign_t ess, char *cert_pathname, + char *signer_DN, ELFCert_t *certp, enum ES_ACTION action) +{ + KMF_RETURN rv; + ELFCert_t cert = NULL; + KMF_FINDCERT_PARAMS fcparams; + KMF_X509_DER_CERT certbuf[2]; + uint32_t ncerts; + boolean_t ret = B_FALSE; + char *pathlist[3], **plp; + + cryptodebug("elfcertlib_getcert: path=%s, DN=%s", + cert_pathname ? cert_pathname : "-none-", + signer_DN ? signer_DN : "-none-"); + *certp = NULL; + if (cert_pathname == NULL && signer_DN == NULL) { + cryptodebug("elfcertlib_getcert: lack of specificity"); + return (ret); + } + + plp = pathlist; + if (cert_pathname != NULL) { + /* look in the specified object */ + *plp++ = cert_pathname; + } else { + /* look in the certificate directories */ + *plp++ = (char *)_PATH_ELFSIGN_CRYPTO_CERTS; + /* + * crypto verifications don't search beyond + * _PATH_ELFSIGN_CRYPTO_CERTS + */ + if (action != ES_GET_CRYPTO) + *plp++ = (char *)_PATH_ELFSIGN_ETC_CERTS; + } + *plp = NULL; + + if ((cert = elfcertlib_allocatecert()) == NULL) { + return (ret); + } + + for (plp = pathlist; *plp; plp++) { + (void) memset(&fcparams, 0, sizeof (fcparams)); + fcparams.kstype = KMF_KEYSTORE_OPENSSL; + fcparams.sslparms.certfile = *plp; + fcparams.subject = signer_DN; + ncerts = 2; + + rv = KMF_FindCert(ess->es_kmfhandle, &fcparams, certbuf, + &ncerts); + if (rv != KMF_OK) + continue; + if (ncerts > 1 && signer_DN == NULL) { + /* There can be only one */ + cryptodebug("elfcertlib_getcert: " + "too many certificates found in %s", + cert_pathname); + goto cleanup; + } + /* found it, cache subject and issuer */ + cert->c_cert = certbuf[0]; + rv = KMF_GetCertSubjectNameString(ess->es_kmfhandle, + &cert->c_cert.certificate, &cert->c_subject); + if (rv != KMF_OK) + goto cleanup; + + rv = KMF_GetCertIssuerNameString(ess->es_kmfhandle, + &cert->c_cert.certificate, &cert->c_issuer); + if (rv != KMF_OK) + goto cleanup; + break; + } + if (*plp == NULL) { + cryptodebug("elfcertlib_getcert: no certificate found"); + goto cleanup; + } + + cert->c_verified = E_UNCHECKED; + + /* + * If the cert we are loading it the trust anchor (ie the CA) then + * we mark it as such in cert. This is so that we don't attempt + * to verify it later. The CA is always implicitly verified. + */ + if (cert_pathname != NULL && ( + strcmp(cert_pathname, _PATH_CRYPTO_CACERT) == 0 || + strcmp(cert_pathname, _PATH_CRYPTO_OBJCACERT) == 0)) { + if (ess->es_certCAcallback != NULL) + (ess->es_certCAcallback)(ess->es_callbackctx, cert, + cert_pathname); + cert->c_verified = E_IS_TA; + } + + ret = B_TRUE; + +cleanup: + if (ret) { + *certp = cert; + } else { + if (cert != NULL) + elfcertlib_freecert(ess, cert); + if (signer_DN != NULL) + cryptoerror(LOG_ERR, "unable to find a certificate " + "for DN: %s", signer_DN); + else + cryptoerror(LOG_ERR, "unable to load certificate " + "from %s", cert_pathname); + } + return (ret); +} + +/* + * elfcertlib_loadprivatekey - Load the private key from path + * + * IN ess - elfsign context structure + * cert + * pathname + * OUT cert + * RETURNS TRUE/FALSE + */ +boolean_t +elfcertlib_loadprivatekey(ELFsign_t ess, ELFCert_t cert, const char *pathname) +{ + KMF_RETURN rv = KMF_OK; + uint32_t nkeys = 2; + KMF_FINDKEY_PARAMS fkparams; + KMF_KEY_HANDLE keybuf[2]; + + (void) memset(&fkparams, 0, sizeof (fkparams)); + fkparams.keyclass = KMF_ASYM_PRI; + fkparams.kstype = KMF_KEYSTORE_OPENSSL; + fkparams.sslparms.keyfile = (char *)pathname; + + rv = KMF_FindKey(ess->es_kmfhandle, &fkparams, keybuf, &nkeys); + if (rv != KMF_OK) + return (B_FALSE); + if (nkeys != 1) { + /* lack of specificity */ + cryptodebug("found %d keys at %s", nkeys, pathname); + return (B_FALSE); + } + cert->c_privatekey = keybuf[0]; + cryptodebug("key %s loaded", pathname); + return (B_TRUE); +} + +/* + * elfcertlib_loadtokenkey - Load the private key from token + * + * IN ess - elfsign context structure + * cert + * token_label + * pin + * OUT cert + * RETURNS TRUE/FALSE + */ +boolean_t +elfcertlib_loadtokenkey(ELFsign_t ess, ELFCert_t cert, + const char *token_label, const char *pin) +{ + KMF_RETURN rv = KMF_OK; + KMF_FINDKEY_PARAMS fkparams; + KMF_CONFIG_PARAMS cfgparams; + uint32_t nkeys = 1; + char *idstr = NULL; + char *err = NULL; + + (void) memset(&fkparams, 0, sizeof (fkparams)); + (void) memset(&cfgparams, 0, sizeof (cfgparams)); + + cfgparams.kstype = KMF_KEYSTORE_PK11TOKEN; + cfgparams.pkcs11config.label = (char *)token_label; + cfgparams.pkcs11config.readonly = B_TRUE; + rv = KMF_ConfigureKeystore(ess->es_kmfhandle, &cfgparams); + if (rv != KMF_OK) { + if (KMF_GetKMFErrorString(rv, &err) == KMF_OK) { + cryptodebug("Error configuring token access:" + " %s\n", err); + free(err); + } + return (B_FALSE); + } + + fkparams.idstr = idstr; + fkparams.kstype = KMF_KEYSTORE_PK11TOKEN; + fkparams.keyclass = KMF_ASYM_PRI; + fkparams.cred.cred = (char *)pin; + fkparams.cred.credlen = (pin != NULL ? strlen(pin) : 0); + fkparams.pkcs11parms.private = B_TRUE; + + /* + * We will search for the key based on the ID attribute + * which was added when the key was created. ID is + * a SHA-1 hash of the public modulus shared by the + * key and the certificate. + */ + rv = KMF_GetCertIDString(&cert->c_cert.certificate, &idstr); + if (rv != KMF_OK) { + if (KMF_GetKMFErrorString(rv, &err) == KMF_OK) { + cryptodebug("Error getting ID from cert: %s\n", err); + free(err); + } + return (B_FALSE); + } + fkparams.idstr = idstr; + + rv = KMF_FindKey(ess->es_kmfhandle, &fkparams, + &cert->c_privatekey, &nkeys); + if (rv != KMF_OK || nkeys != 1) { + if (KMF_GetKMFErrorString(rv, &err) == KMF_OK) { + cryptodebug("Error finding private key: %s\n", err); + free(err); + } + free(idstr); + return (B_FALSE); + } + cryptodebug("key found in %s", token_label); + cryptodebug("elfcertlib_loadprivatekey = 0x%.8X", + &cert->c_privatekey); + + free(idstr); + return (B_TRUE); +} + +static const CK_BYTE MD5_DER_PREFIX[] = {0x30, 0x20, 0x30, 0x0c, 0x06, 0x08, + 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x02, 0x05, 0x05, 0x00, 0x04, 0x10}; + +/* + * elfcertlib_sign - sign the given DATA using the privatekey in cert + * + * IN ess - elfsign context structure + * cert + * data + * data_len + * OUT sig - must be big enough to hold the signature of data + * Caller must allocate + * sig_len - actual length used; 0 on failure. + * RETURNS TRUE/FALSE + */ +/*ARGSUSED*/ +boolean_t +elfcertlib_sign(ELFsign_t ess, ELFCert_t cert, + const uchar_t *data, size_t data_len, + uchar_t *sig, size_t *sig_len) +{ + KMF_RETURN ret = KMF_OK; + KMF_DATA tobesigned; + KMF_DATA signature; + uchar_t der_data[sizeof (MD5_DER_PREFIX) + MD5_DIGEST_LENGTH]; + + if (ess->es_version <= FILESIG_VERSION2) { + /* compatibility: take MD5 hash of SHA1 hash */ + size_t derlen = MD5_DIGEST_LENGTH; + MD5_CTX ctx; + + /* + * first: digest using software-based methods, don't + * rely on the token for hashing. + */ + MD5Init(&ctx); + MD5Update(&ctx, data, data_len); + MD5Final(&der_data[sizeof (MD5_DER_PREFIX)], &ctx); + + /* + * second: insert prefix + */ + (void) memcpy(der_data, MD5_DER_PREFIX, + sizeof (MD5_DER_PREFIX)); + /* + * prepare to sign the local buffer + */ + tobesigned.Data = (uchar_t *)der_data; + tobesigned.Length = sizeof (MD5_DER_PREFIX) + derlen; + } else { + tobesigned.Data = (uchar_t *)data; + tobesigned.Length = data_len; + } + + signature.Data = (uchar_t *)sig; + signature.Length = *sig_len; + + ret = KMF_SignDataWithKey(ess->es_kmfhandle, + &cert->c_privatekey, (KMF_OID *)&KMFOID_RSA, + &tobesigned, &signature); + + if (ret != KMF_OK) { + char *err; + if (KMF_GetKMFErrorString(ret, &err) == KMF_OK && + err != NULL) { + cryptodebug("Error signing data: %s\n", err); + free(err); + } + *sig_len = 0; + return (B_FALSE); + } + *sig_len = signature.Length; + return (B_TRUE); +} + +/* + * elfcertlib_verifysig - verify the given DATA using the public key in cert + * + * IN ess - elfsign context structure + * cert + * signature + * sig_len + * data + * data_len + * OUT N/A + * RETURNS TRUE/FALSE + */ +boolean_t +elfcertlib_verifysig(ELFsign_t ess, ELFCert_t cert, + const uchar_t *signature, size_t sig_len, + const uchar_t *data, size_t data_len) +{ + KMF_RETURN rv; + KMF_DATA indata; + KMF_DATA insig; + KMF_ALGORITHM_INDEX algid; + + indata.Data = (uchar_t *)data; + indata.Length = data_len; + insig.Data = (uchar_t *)signature; + insig.Length = sig_len; + + if (ess->es_version <= FILESIG_VERSION2) + algid = KMF_ALGID_MD5WithRSA; + else + algid = KMF_ALGID_RSA; + + /* + * We tell KMF to use the OpenSSL verification + * APIs here to avoid a circular dependency with + * kcfd and libpkcs11. + */ + rv = KMF_VerifyDataWithCert(ess->es_kmfhandle, + KMF_KEYSTORE_OPENSSL, algid, + &indata, &insig, &cert->c_cert.certificate); + + return ((rv == KMF_OK)); +} + +/* + * elfcertlib_getdn + * + * IN cert + * OUT NONE + * RETURN dn or NULL + */ +char * +elfcertlib_getdn(ELFCert_t cert) +{ + cryptodebug("elfcertlib_getdn"); + + return (cert->c_subject); +} + +/* + * elfcertlib_getissuer + * + * IN cert + * OUT NONE + * RETURN dn or NULL + */ +char * +elfcertlib_getissuer(ELFCert_t cert) +{ + cryptodebug("elfcertlib_issuer"); + + return (cert->c_issuer); +} + +boolean_t +elfcertlib_init(ELFsign_t ess) +{ + boolean_t rc = B_TRUE; + KMF_RETURN rv; + if (ess->es_kmfhandle == NULL) { + rv = KMF_Initialize(&ess->es_kmfhandle, NULL, NULL); + if (rv != KMF_OK) { + cryptoerror(LOG_ERR, + "unable to initialize KMF library"); + rc = B_FALSE; + } + } + return (rc); +} + +void +elfcertlib_fini(ELFsign_t ess) +{ + (void) KMF_Finalize(ess->es_kmfhandle); +} + +/* + * set the token device + */ +boolean_t +elfcertlib_settoken(ELFsign_t ess, char *token) +{ + boolean_t rc = B_TRUE; + KMF_RETURN rv; + KMF_CONFIG_PARAMS cfgparams; + + (void) memset(&cfgparams, 0, sizeof (cfgparams)); + cfgparams.kstype = KMF_KEYSTORE_PK11TOKEN; + cfgparams.pkcs11config.label = token; + cfgparams.pkcs11config.readonly = B_TRUE; + rv = KMF_ConfigureKeystore(ess->es_kmfhandle, &cfgparams); + if (rv != KMF_OK) { + cryptoerror(LOG_ERR, "unable to select token\n"); + rc = B_FALSE; + } + + return (rc); +} + +/* + * set the certificate CA identification callback + */ +void +elfcertlib_setcertCAcallback(ELFsign_t ess, + void (*cb)(void *, ELFCert_t, char *)) +{ + ess->es_certCAcallback = cb; +} + +/* + * set the certificate verification callback + */ +void +elfcertlib_setcertvercallback(ELFsign_t ess, + void (*cb)(void *, ELFCert_t, ELFCert_t)) +{ + ess->es_certvercallback = cb; +} + + +/* + * elfcertlib_releasecert - release a cert + * + * IN cert + * OUT cert + * RETURN N/A + * + */ +void +elfcertlib_releasecert(ELFsign_t ess, ELFCert_t cert) +{ + elfcertlib_freecert(ess, cert); +} + +/* + * elfcertlib_allocatecert - create a new ELFCert_t + * + * IN N/A + * OUT N/A + * RETURN ELFCert_t, NULL on failure. + */ +static ELFCert_t +elfcertlib_allocatecert(void) +{ + ELFCert_t cert = NULL; + + cert = malloc(sizeof (struct ELFCert_s)); + if (cert == NULL) { + cryptoerror(LOG_ERR, + "elfcertlib_allocatecert: malloc failed %s", + strerror(errno)); + return (NULL); + } + (void) memset(cert, 0, sizeof (struct ELFCert_s)); + cert->c_verified = E_UNCHECKED; + cert->c_subject = NULL; + cert->c_issuer = NULL; + return (cert); +} + +/* + * elfcertlib_freecert - freeup the memory of a cert + * + * IN cert + * OUT cert + * RETURN N/A + * + */ +static void +elfcertlib_freecert(ELFsign_t ess, ELFCert_t cert) +{ + if (cert == NULL) + return; + + free(cert->c_subject); + free(cert->c_issuer); + + KMF_FreeKMFCert(ess->es_kmfhandle, &cert->c_cert); + KMF_FreeKMFKey(ess->es_kmfhandle, &cert->c_privatekey); + + free(cert); +} diff --git a/usr/src/lib/libelfsign/common/elfsignlib.c b/usr/src/lib/libelfsign/common/elfsignlib.c new file mode 100644 index 0000000000..00f71a125d --- /dev/null +++ b/usr/src/lib/libelfsign/common/elfsignlib.c @@ -0,0 +1,1598 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#define ELF_TARGET_ALL /* get definitions of all section flags */ + +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <unistd.h> +#include <strings.h> +#include <stddef.h> +#include <stdlib.h> +#include <libintl.h> +#include <dirent.h> +#include <errno.h> +#include <libelf.h> +#include <gelf.h> +#include <sys/mman.h> +#include <cryptoutil.h> +#include <sha1.h> +#include <sys/crypto/elfsign.h> +#include <libelfsign.h> + +#ifndef SHA1_DIGEST_LENGTH +#define SHA1_DIGEST_LENGTH 20 +#endif /* SHA1_DIGEST_LENGTH */ + +const char SUNW_ELF_SIGNATURE_ID[] = ELF_SIGNATURE_SECTION; +const char OID_sha1WithRSAEncryption[] = "1.2.840.113549.1.1.5"; + +static ELFsign_status_t elfsign_adjustoffsets(ELFsign_t ess, + Elf_Scn *scn, uint64_t new_size); +static ELFsign_status_t elfsign_verify_esa(ELFsign_t ess, + uchar_t *sig, size_t sig_len); +static uint32_t elfsign_switch_uint32(uint32_t i); +static ELFsign_status_t elfsign_switch(ELFsign_t ess, + struct filesignatures *fssp, enum ES_ACTION action); + +struct filesig_extraction { + filesig_vers_t fsx_version; + char *fsx_format; + char fsx_signer_DN[ELFCERT_MAX_DN_LEN]; + size_t fsx_signer_DN_len; + uchar_t fsx_signature[SIG_MAX_LENGTH]; + size_t fsx_sig_len; + char fsx_sig_oid[100]; + size_t fsx_sig_oid_len; + time_t fsx_time; +}; + +static char * +version_to_str(filesig_vers_t v) +{ + char *ret; + + switch (v) { + case FILESIG_VERSION1: + ret = "VERSION1"; + break; + case FILESIG_VERSION2: + ret = "VERSION2"; + break; + case FILESIG_VERSION3: + ret = "VERSION3"; + break; + case FILESIG_VERSION4: + ret = "VERSION4"; + break; + default: + ret = "UNKNOWN"; + break; + } + return (ret); +} + +/* + * Update filesignatures to include the v1/v2 filesig, + * composed of signer DN, signature, and OID. + */ +static struct filesignatures * +filesig_insert_dso(struct filesignatures *fssp, + filesig_vers_t version, + const char *dn, + int dn_len, + const uchar_t *sig, + int sig_len, + const char *oid, + int oid_len) +{ + struct filesig *fsgp; + char *fsdatap; + + if (oid == NULL) { + /* + * This OID is used for the rsa_md5_sha1 format signature also. + * This use is historical, and is hence continued, + * despite its lack of technical accuracy. + */ + oid = OID_sha1WithRSAEncryption; + oid_len = strlen(oid); + } + + /* + * for now, always insert a single-signature signature block + */ + if (fssp != NULL) + free(fssp); + fssp = (struct filesignatures *) + malloc(filesig_ALIGN(sizeof (struct filesignatures) + + dn_len + sig_len + oid_len)); + if (fssp == NULL) + return (fssp); + + fssp->filesig_cnt = 1; + fssp->filesig_pad = 0; /* reserve for future use */ + + fsgp = &fssp->filesig_sig; + fsgp->filesig_size = sizeof (struct filesig) + + dn_len + sig_len + oid_len; + fsgp->filesig_version = version; + switch (version) { + case FILESIG_VERSION1: + case FILESIG_VERSION2: + fsgp->filesig_size -= sizeof (struct filesig) - + offsetof(struct filesig, filesig_v1_data[0]); + fsgp->filesig_v1_dnsize = dn_len; + fsgp->filesig_v1_sigsize = sig_len; + fsgp->filesig_v1_oidsize = oid_len; + fsdatap = &fsgp->filesig_v1_data[0]; + break; + case FILESIG_VERSION3: + case FILESIG_VERSION4: + fsgp->filesig_size -= sizeof (struct filesig) - + offsetof(struct filesig, filesig_v3_data[0]); + fsgp->filesig_v3_time = time(NULL); + fsgp->filesig_v3_dnsize = dn_len; + fsgp->filesig_v3_sigsize = sig_len; + fsgp->filesig_v3_oidsize = oid_len; + fsdatap = &fsgp->filesig_v3_data[0]; + break; + default: + cryptodebug("filesig_insert_dso: unknown version: %d", + version); + free(fssp); + return (NULL); + } + (void) memcpy(fsdatap, dn, dn_len); + fsdatap += dn_len; + (void) memcpy(fsdatap, (char *)sig, sig_len); + fsdatap += sig_len; + (void) memcpy(fsdatap, oid, oid_len); + fsdatap += oid_len; + fsgp = filesig_next(fsgp); + (void) memset(fsdatap, 0, (char *)(fsgp) - fsdatap); + + return (fssp); +} + +/* + * filesig_extract - extract filesig structure to internal form + */ +static filesig_vers_t +filesig_extract(struct filesig *fsgp, struct filesig_extraction *fsxp) +{ + char *fsdp; + +#define filesig_extract_common(cp, field, data_var, len_var, len_limit) { \ + len_var = len_limit; \ + if (len_var > fsgp->field) \ + len_var = fsgp->field; \ + (void) memcpy(data_var, cp, len_var); \ + cp += fsgp->field; } +#define filesig_extract_str(cp, field, data_var, len_var) \ + filesig_extract_common(cp, field, data_var, len_var, \ + sizeof (data_var) - 1); \ + data_var[len_var] = '\0'; +#define filesig_extract_opaque(cp, field, data_var, len_var) \ + filesig_extract_common(cp, field, data_var, len_var, sizeof (data_var)) + + fsxp->fsx_version = fsgp->filesig_version; + cryptodebug("filesig_extract: version=%s", + version_to_str(fsxp->fsx_version)); + switch (fsxp->fsx_version) { + case FILESIG_VERSION1: + case FILESIG_VERSION2: + /* + * extract VERSION1 DN, signature, and OID + */ + fsdp = fsgp->filesig_v1_data; + fsxp->fsx_format = ES_FMT_RSA_MD5_SHA1; + fsxp->fsx_time = 0; + filesig_extract_str(fsdp, filesig_v1_dnsize, + fsxp->fsx_signer_DN, fsxp->fsx_signer_DN_len); + filesig_extract_opaque(fsdp, filesig_v1_sigsize, + fsxp->fsx_signature, fsxp->fsx_sig_len); + filesig_extract_str(fsdp, filesig_v1_oidsize, + fsxp->fsx_sig_oid, fsxp->fsx_sig_oid_len); + break; + case FILESIG_VERSION3: + case FILESIG_VERSION4: + fsdp = fsgp->filesig_v3_data; + fsxp->fsx_format = ES_FMT_RSA_SHA1; + fsxp->fsx_time = fsgp->filesig_v3_time; + filesig_extract_str(fsdp, filesig_v3_dnsize, + fsxp->fsx_signer_DN, fsxp->fsx_signer_DN_len); + filesig_extract_opaque(fsdp, filesig_v3_sigsize, + fsxp->fsx_signature, fsxp->fsx_sig_len); + filesig_extract_str(fsdp, filesig_v3_oidsize, + fsxp->fsx_sig_oid, fsxp->fsx_sig_oid_len); + break; + default: + break; + } + + return (fsxp->fsx_version); +} + +ELFsign_status_t +elfsign_begin(const char *filename, enum ES_ACTION action, ELFsign_t *essp) +{ + Elf_Cmd elfcmd; + int oflags = 0; + short l_type; + ELFsign_t ess; + struct stat stb; + union { + char c[2]; + short s; + } uorder; + GElf_Ehdr elfehdr; + char *ident; + + switch (action) { + case ES_GET: + case ES_GET_CRYPTO: + cryptodebug("elfsign_begin for get"); + elfcmd = ELF_C_READ; + oflags = O_RDONLY | O_NOCTTY | O_NDELAY; + l_type = F_RDLCK; + break; + case ES_UPDATE_RSA_MD5_SHA1: + case ES_UPDATE_RSA_SHA1: + cryptodebug("elfsign_begin for update"); + elfcmd = ELF_C_RDWR; + oflags = O_RDWR | O_NOCTTY | O_NDELAY; + l_type = F_WRLCK; + break; + default: + return (ELFSIGN_UNKNOWN); + } + + if ((ess = malloc(sizeof (struct ELFsign_s))) == NULL) { + return (ELFSIGN_UNKNOWN); + } + (void) memset((void *)ess, 0, sizeof (struct ELFsign_s)); + + if (!elfcertlib_init(ess)) { + cryptodebug("elfsign_begin: failed initialization"); + return (ELFSIGN_UNKNOWN); + } + + ess->es_elf = NULL; + ess->es_action = action; + ess->es_version = FILESIG_UNKNOWN; + ess->es_pathname = NULL; + ess->es_certpath = NULL; + + if (filename == NULL) { + *essp = ess; + return (ELFSIGN_SUCCESS); + } + + if ((ess->es_fd = open(filename, oflags)) == -1) { + elfsign_end(ess); + return (ELFSIGN_INVALID_ELFOBJ); + } + if ((fstat(ess->es_fd, &stb) == -1) || !S_ISREG(stb.st_mode)) { + elfsign_end(ess); + return (ELFSIGN_INVALID_ELFOBJ); + } + if ((ess->es_pathname = strdup(filename)) == NULL) { + elfsign_end(ess); + return (ELFSIGN_UNKNOWN); + } + /* + * The following lock is released in elfsign_end() when we close(2) + * the es_fd. This ensures that we aren't trying verify a file + * we are currently updating. + */ + ess->es_flock.l_type = l_type; + ess->es_flock.l_whence = SEEK_CUR; + ess->es_flock.l_start = 0; + ess->es_flock.l_len = 0; + if (fcntl(ess->es_fd, F_SETLK, &ess->es_flock) == -1) { + cryptodebug("fcntl(F_SETLK) of %s failed with: %s", + ess->es_pathname, strerror(errno)); + elfsign_end(ess); + return (ELFSIGN_UNKNOWN); + } + + if (elf_version(EV_CURRENT) == EV_NONE) { + elfsign_end(ess); + return (ELFSIGN_UNKNOWN); + } + + if ((ess->es_elf = elf_begin(ess->es_fd, elfcmd, + (Elf *)NULL)) == NULL) { + cryptodebug("elf_begin() failed: %s", elf_errmsg(-1)); + elfsign_end(ess); + return (ELFSIGN_INVALID_ELFOBJ); + } + + if (gelf_getehdr(ess->es_elf, &elfehdr) == NULL) { + cryptodebug("elf_getehdr() failed: %s", elf_errmsg(-1)); + elfsign_end(ess); + return (ELFSIGN_INVALID_ELFOBJ); + } + ess->es_has_phdr = (elfehdr.e_phnum != 0); + + uorder.s = ELFDATA2MSB << 8 | ELFDATA2LSB; + ident = elf_getident(ess->es_elf, NULL); + if (ident == NULL) { + cryptodebug("elf_getident() failed: %s", elf_errmsg(-1)); + elfsign_end(ess); + return (ELFSIGN_INVALID_ELFOBJ); + } + ess->es_same_endian = (ident[EI_DATA] == uorder.c[0]); + ess->es_ei_class = ident[EI_CLASS]; + + /* + * Call elf_getshstrndx to be sure we have a real ELF object + * this is required because elf_begin doesn't check that. + */ + if (elf_getshstrndx(ess->es_elf, &ess->es_shstrndx) == 0) { + elfsign_end(ess); + cryptodebug("elfsign_begin: elf_getshstrndx failed"); + return (ELFSIGN_INVALID_ELFOBJ); + } + + /* + * Make sure libelf doesn't rearrange section ordering / offsets. + */ + (void) elf_flagelf(ess->es_elf, ELF_C_SET, ELF_F_LAYOUT); + + *essp = ess; + + return (ELFSIGN_SUCCESS); +} + +/* + * elfsign_end - cleanup the ELFsign_t + * + * IN/OUT: ess + */ +void +elfsign_end(ELFsign_t ess) +{ + if (ess == NULL) + return; + + if (ess->es_elf != NULL && ES_ACTISUPDATE(ess->es_action)) { + if (elf_update(ess->es_elf, ELF_C_WRITE) == -1) { + cryptodebug("elf_update() failed: %s", + elf_errmsg(-1)); + return; + } + } + + if (ess->es_fd != -1) { + (void) close(ess->es_fd); + ess->es_fd = -1; + } + + if (ess->es_pathname != NULL) { + free(ess->es_pathname); + ess->es_pathname = NULL; + } + if (ess->es_certpath != NULL) { + free(ess->es_certpath); + ess->es_certpath = NULL; + } + + if (ess->es_elf != NULL) { + (void) elf_end(ess->es_elf); + ess->es_elf = NULL; + } + + elfcertlib_fini(ess); + + free(ess); +} + +/* + * set the certificate path + */ +ELFsign_status_t +elfsign_setcertpath(ELFsign_t ess, const char *certpath) +{ + /* + * Normally use of access(2) is insecure, here we are only + * doing it to help provide early failure and better error + * checking, so there is no race condition. + */ + if (access(certpath, R_OK) != 0) { + elfsign_end(ess); + return (ELFSIGN_INVALID_CERTPATH); + } + ess->es_certpath = strdup(certpath); + + if (ES_ACTISUPDATE(ess->es_action)) { + ELFCert_t cert = NULL; + char *subject; + + /* set the version based on the certificate */ + if (elfcertlib_getcert(ess, ess->es_certpath, NULL, + &cert, ess->es_action)) { + if ((subject = elfcertlib_getdn(cert)) != NULL) { + if (strstr(subject, ELFSIGN_CRYPTO)) + ess->es_version = (ess->es_action == + ES_UPDATE_RSA_MD5_SHA1) ? + FILESIG_VERSION1 : FILESIG_VERSION3; + else + ess->es_version = (ess->es_action == + ES_UPDATE_RSA_MD5_SHA1) ? + FILESIG_VERSION2 : FILESIG_VERSION4; + } + elfcertlib_releasecert(ess, cert); + } + if (ess->es_version == FILESIG_UNKNOWN) + return (ELFSIGN_FAILED); + } + return (ELFSIGN_SUCCESS); +} + +/* + * set the callback context + */ +void +elfsign_setcallbackctx(ELFsign_t ess, void *ctx) +{ + ess->es_callbackctx = ctx; +} + +/* + * set the signature extraction callback + */ +void +elfsign_setsigvercallback(ELFsign_t ess, + void (*cb)(void *, void *, size_t, ELFCert_t)) +{ + ess->es_sigvercallback = cb; +} + +/* + * elfsign_signatures + * + * IN: ess, fsspp, action + * OUT: fsspp + */ +ELFsign_status_t +elfsign_signatures(ELFsign_t ess, + struct filesignatures **fsspp, + size_t *fslen, + enum ES_ACTION action) +{ + Elf_Scn *scn = NULL, *sig_scn = NULL; + GElf_Shdr shdr; + Elf_Data *data = NULL; + const char *elf_section = SUNW_ELF_SIGNATURE_ID; + int fscnt, fssize; + struct filesig *fsgp, *fsgpnext; + uint64_t sig_offset = 0; + + cryptodebug("elfsign_signature"); + if ((ess == NULL) || (fsspp == NULL)) { + cryptodebug("invalid arguments"); + return (ELFSIGN_UNKNOWN); + } + + cryptodebug("elfsign_signature %s for %s", + ES_ACTISUPDATE(action) ? "ES_UPDATE" : "ES_GET", elf_section); + + (void) elf_errno(); + while ((scn = elf_nextscn(ess->es_elf, scn)) != NULL) { + const char *sh_name; + /* + * Do a string compare to examine each section header + * to see if this is the section that needs to be updated. + */ + if (gelf_getshdr(scn, &shdr) == NULL) { + cryptodebug("gelf_getshdr() failed: %s", + elf_errmsg(-1)); + return (ELFSIGN_FAILED); + } + sh_name = elf_strptr(ess->es_elf, ess->es_shstrndx, + (size_t)shdr.sh_name); + if (strcmp(sh_name, elf_section) == 0) { + cryptodebug("elfsign_signature: found %s", elf_section); + sig_scn = scn; + break; + } + if (shdr.sh_type != SHT_NOBITS && + sig_offset < shdr.sh_offset + shdr.sh_size) { + sig_offset = shdr.sh_offset + shdr.sh_size; + } + } + if (elf_errmsg(0) != NULL) { + cryptodebug("unexpected error: %s", elf_section, + elf_errmsg(-1)); + return (ELFSIGN_FAILED); + } + + if (ES_ACTISUPDATE(action) && (sig_scn == NULL)) { + size_t old_size, new_size; + char *new_d_buf; + + cryptodebug("elfsign_signature: %s not found - creating", + elf_section); + + /* + * insert section name in .shstrtab + */ + if ((scn = elf_getscn(ess->es_elf, ess->es_shstrndx)) == 0) { + cryptodebug("elf_getscn() failed: %s", + elf_errmsg(-1)); + return (ELFSIGN_FAILED); + } + if (gelf_getshdr(scn, &shdr) == NULL) { + cryptodebug("gelf_getshdr() failed: %s", + elf_errmsg(-1)); + return (ELFSIGN_FAILED); + } + if ((data = elf_getdata(scn, data)) == NULL) { + cryptodebug("elf_getdata() failed: %s", + elf_errmsg(-1)); + return (ELFSIGN_FAILED); + } + old_size = data->d_size; + if (old_size != shdr.sh_size) { + cryptodebug("mismatch between data size %d " + "and section size %lld", old_size, shdr.sh_size); + return (ELFSIGN_FAILED); + } + new_size = old_size + strlen(elf_section) + 1; + if ((new_d_buf = malloc(new_size)) == NULL) + return (ELFSIGN_FAILED); + + (void) memcpy(new_d_buf, data->d_buf, old_size); + (void) strlcpy(new_d_buf + old_size, elf_section, + new_size - old_size); + data->d_buf = new_d_buf; + data->d_size = new_size; + data->d_align = 1; + /* + * Add the section name passed in to the end of the file. + * Initialize the fields in the Section Header that + * libelf will not fill in. + */ + if ((sig_scn = elf_newscn(ess->es_elf)) == 0) { + cryptodebug("elf_newscn() failed: %s", + elf_errmsg(-1)); + return (ELFSIGN_FAILED); + } + if (gelf_getshdr(sig_scn, &shdr) == 0) { + cryptodebug("gelf_getshdr() failed: %s", + elf_errmsg(-1)); + return (ELFSIGN_FAILED); + } + shdr.sh_name = old_size; + shdr.sh_type = SHT_SUNW_SIGNATURE; + shdr.sh_flags = SHF_EXCLUDE; + shdr.sh_addr = 0; + shdr.sh_link = 0; + shdr.sh_info = 0; + shdr.sh_size = 0; + shdr.sh_offset = sig_offset; + shdr.sh_addralign = 1; + + /* + * Flush the changes to the underlying elf32 or elf64 + * section header. + */ + if (gelf_update_shdr(sig_scn, &shdr) == 0) { + cryptodebug("gelf_update_shdr failed"); + return (ELFSIGN_FAILED); + } + + if ((data = elf_newdata(sig_scn)) == NULL) { + cryptodebug("can't add elf data area for %s: %s", + elf_section, elf_errmsg(-1)); + return (ELFSIGN_FAILED); + } + if (elfsign_adjustoffsets(ess, scn, + old_size + strlen(elf_section) + 1) != ELFSIGN_SUCCESS) { + cryptodebug("can't adjust for new section name %s", + elf_section); + return (ELFSIGN_FAILED); + } + } else { + if (sig_scn == NULL) { + cryptodebug("can't find signature section"); + *fsspp = NULL; + return (ELFSIGN_NOTSIGNED); + } + if ((data = elf_getdata(sig_scn, NULL)) == 0) { + cryptodebug("can't get section data for %s", + elf_section); + return (ELFSIGN_FAILED); + } + } + + if (ES_ACTISUPDATE(action)) { + fssize = offsetof(struct filesignatures, _u1); + if (*fsspp != NULL) { + fsgp = &(*fsspp)->filesig_sig; + for (fscnt = 0; fscnt < (*fsspp)->filesig_cnt; + fscnt++) { + fsgpnext = filesig_next(fsgp); + fssize += (char *)(fsgpnext) - (char *)(fsgp); + fsgp = fsgpnext; + } + } + if (shdr.sh_addr != 0) { + cryptodebug("section %s is part of a loadable segment, " + "it cannot be changed.\n", elf_section); + return (ELFSIGN_FAILED); + } + if ((data->d_buf = malloc(fssize)) == NULL) + return (ELFSIGN_FAILED); + if (*fsspp != NULL) { + (void) memcpy(data->d_buf, *fsspp, fssize); + (void) elfsign_switch(ess, + (struct filesignatures *)data->d_buf, action); + } + data->d_size = fssize; + data->d_align = 1; + data->d_type = ELF_T_BYTE; + cryptodebug("elfsign_signature: data->d_size = %d", + data->d_size); + if (elfsign_adjustoffsets(ess, sig_scn, fssize) != + ELFSIGN_SUCCESS) { + cryptodebug("can't adjust for revised signature " + "section contents"); + return (ELFSIGN_FAILED); + } + } else { + *fsspp = malloc(data->d_size); + if (*fsspp == NULL) + return (ELFSIGN_FAILED); + (void) memcpy(*fsspp, data->d_buf, data->d_size); + if (elfsign_switch(ess, *fsspp, ES_GET) != ELFSIGN_SUCCESS) { + free(*fsspp); + *fsspp = NULL; + return (ELFSIGN_FAILED); + } + *fslen = data->d_size; + } + + return (ELFSIGN_SUCCESS); +} + +static ELFsign_status_t +elfsign_adjustoffsets(ELFsign_t ess, Elf_Scn *scn, uint64_t new_size) +{ + GElf_Ehdr elfehdr; + GElf_Shdr shdr; + uint64_t prev_end, scn_offset; + char *name; + Elf_Scn *scnp; + Elf_Data *data; + ELFsign_status_t retval = ELFSIGN_FAILED; + struct scninfo { + struct scninfo *scni_next; + Elf_Scn *scni_scn; + uint64_t scni_offset; + } *scnip = NULL, *tmpscnip, **scnipp; + + /* get the size of the current section */ + if (gelf_getshdr(scn, &shdr) == NULL) + return (ELFSIGN_FAILED); + if (shdr.sh_size == new_size) + return (ELFSIGN_SUCCESS); + scn_offset = shdr.sh_offset; + name = elf_strptr(ess->es_elf, ess->es_shstrndx, + (size_t)shdr.sh_name); + if (shdr.sh_flags & SHF_ALLOC && ess->es_has_phdr) { + cryptodebug("elfsign_adjustoffsets: " + "can't move allocated section %s", name ? name : "NULL"); + return (ELFSIGN_FAILED); + } + + /* resize the desired section */ + cryptodebug("elfsign_adjustoffsets: " + "resizing %s at 0x%llx from 0x%llx to 0x%llx", + name ? name : "NULL", shdr.sh_offset, shdr.sh_size, new_size); + shdr.sh_size = new_size; + if (gelf_update_shdr(scn, &shdr) == 0) { + cryptodebug("gelf_update_shdr failed"); + goto bad; + } + prev_end = shdr.sh_offset + shdr.sh_size; + + /* + * find sections whose data follows the changed section + * must scan all sections since section data may not + * be in same order as section headers + */ + scnp = elf_getscn(ess->es_elf, 0); /* "seek" to start */ + while ((scnp = elf_nextscn(ess->es_elf, scnp)) != NULL) { + if (gelf_getshdr(scnp, &shdr) == NULL) + goto bad; + if (shdr.sh_offset <= scn_offset) + continue; + name = elf_strptr(ess->es_elf, ess->es_shstrndx, + (size_t)shdr.sh_name); + if (shdr.sh_flags & SHF_ALLOC && ess->es_has_phdr) { + if (shdr.sh_type == SHT_NOBITS) { + /* .bss can occasionally overlap .shrtab */ + continue; + } + cryptodebug("elfsign_adjustoffsets: " + "can't move allocated section %s", + name ? name : "NULL"); + goto bad; + } + /* + * force reading of data to memory image + */ + data = NULL; + while ((data = elf_rawdata(scnp, data)) != NULL) + ; + /* + * capture section information + * insert into list in order of sh_offset + */ + cryptodebug("elfsign_adjustoffsets: " + "may have to adjust section %s, offset 0x%llx", + name ? name : "NULL", shdr.sh_offset); + tmpscnip = (struct scninfo *)malloc(sizeof (struct scninfo)); + if (tmpscnip == NULL) { + cryptodebug("elfsign_adjustoffsets: " + "memory allocation failure"); + goto bad; + } + tmpscnip->scni_scn = scnp; + tmpscnip->scni_offset = shdr.sh_offset; + for (scnipp = &scnip; *scnipp != NULL; + scnipp = &(*scnipp)->scni_next) { + if ((*scnipp)->scni_offset > tmpscnip->scni_offset) + break; + } + tmpscnip->scni_next = *scnipp; + *scnipp = tmpscnip; + } + + /* move following sections as necessary */ + for (tmpscnip = scnip; tmpscnip != NULL; + tmpscnip = tmpscnip->scni_next) { + scnp = tmpscnip->scni_scn; + if (gelf_getshdr(scnp, &shdr) == NULL) { + cryptodebug("elfsign_adjustoffsets: " + "elf_getshdr for section %d failed", + elf_ndxscn(scnp)); + goto bad; + } + if (shdr.sh_offset >= prev_end) + break; + prev_end = (prev_end + shdr.sh_addralign - 1) & + (-shdr.sh_addralign); + name = elf_strptr(ess->es_elf, ess->es_shstrndx, + (size_t)shdr.sh_name); + cryptodebug("elfsign_adjustoffsets: " + "moving %s size 0x%llx from 0x%llx to 0x%llx", + name ? name : "NULL", shdr.sh_size, + shdr.sh_offset, prev_end); + shdr.sh_offset = prev_end; + if (gelf_update_shdr(scnp, &shdr) == 0) { + cryptodebug("gelf_update_shdr failed"); + goto bad; + } + prev_end = shdr.sh_offset + shdr.sh_size; + } + + /* + * adjust section header offset in elf header + */ + if (gelf_getehdr(ess->es_elf, &elfehdr) == NULL) { + cryptodebug("elf_getehdr() failed: %s", elf_errmsg(-1)); + goto bad; + } + if (elfehdr.e_shoff < prev_end) { + if (ess->es_ei_class == ELFCLASS32) + prev_end = (prev_end + ELF32_FSZ_OFF - 1) & + (-ELF32_FSZ_OFF); + else if (ess->es_ei_class == ELFCLASS64) + prev_end = (prev_end + ELF64_FSZ_OFF - 1) & + (-ELF64_FSZ_OFF); + cryptodebug("elfsign_adjustoffsets: " + "move sh_off from 0x%llx to 0x%llx", + elfehdr.e_shoff, prev_end); + elfehdr.e_shoff = prev_end; + if (gelf_update_ehdr(ess->es_elf, &elfehdr) == 0) { + cryptodebug("elf_update_ehdr() failed: %s", + elf_errmsg(-1)); + goto bad; + } + } + + retval = ELFSIGN_SUCCESS; + +bad: + while (scnip != NULL) { + tmpscnip = scnip->scni_next; + free(scnip); + scnip = tmpscnip; + } + return (retval); +} + +struct filesignatures * +elfsign_insert_dso(ELFsign_t ess, + struct filesignatures *fssp, + const char *dn, + int dn_len, + const uchar_t *sig, + int sig_len, + const char *oid, + int oid_len) +{ + return (filesig_insert_dso(fssp, ess->es_version, dn, dn_len, + sig, sig_len, oid, oid_len)); +} + +/*ARGSUSED*/ +filesig_vers_t +elfsign_extract_sig(ELFsign_t ess, + struct filesignatures *fssp, + uchar_t *sig, + size_t *sig_len) +{ + struct filesig_extraction fsx; + filesig_vers_t version; + + if (fssp == NULL) + return (FILESIG_UNKNOWN); + if (fssp->filesig_cnt != 1) + return (FILESIG_UNKNOWN); + version = filesig_extract(&fssp->filesig_sig, &fsx); + switch (version) { + case FILESIG_VERSION1: + case FILESIG_VERSION2: + case FILESIG_VERSION3: + case FILESIG_VERSION4: + if (*sig_len >= fsx.fsx_sig_len) { + (void) memcpy((char *)sig, (char *)fsx.fsx_signature, + *sig_len); + *sig_len = fsx.fsx_sig_len; + } else + version = FILESIG_UNKNOWN; + break; + default: + version = FILESIG_UNKNOWN; + break; + } + + if (ess->es_version == FILESIG_UNKNOWN) { + ess->es_version = version; + } + + return (version); +} + +static ELFsign_status_t +elfsign_hash_common(ELFsign_t ess, uchar_t *hash, size_t *hash_len, + boolean_t hash_mem_resident) +{ + Elf_Scn *scn = NULL; + ELFsign_status_t elfstat; + GElf_Shdr shdr; + SHA1_CTX ctx; + + /* The buffer must be large enough to hold the hash */ + if (*hash_len < SHA1_DIGEST_LENGTH) + return (ELFSIGN_FAILED); + + bzero(hash, *hash_len); + + /* Initialize the digest session */ + SHA1Init(&ctx); + + scn = elf_getscn(ess->es_elf, 0); /* "seek" to start */ + (void) elf_errno(); + while ((scn = elf_nextscn(ess->es_elf, scn)) != 0) { + char *name = NULL; + Elf_Data *data = NULL; + + if (gelf_getshdr(scn, &shdr) == NULL) { + elfstat = ELFSIGN_FAILED; + goto done; + } + + name = elf_strptr(ess->es_elf, ess->es_shstrndx, + (size_t)shdr.sh_name); + if (name == NULL) + name = "NULL"; + + if (!hash_mem_resident && + (ess->es_version == FILESIG_VERSION1 || + ess->es_version == FILESIG_VERSION3)) { + /* + * skip the signature section only + */ + if (shdr.sh_type == SHT_SUNW_SIGNATURE) { + cryptodebug("elfsign_hash: skipping %s", name); + continue; + } + } else if (!(shdr.sh_flags & SHF_ALLOC)) { + /* + * select only memory resident sections + */ + cryptodebug("elfsign_hash: skipping %s", name); + continue; + } + + /* + * throw this section into the hash + * use elf_rawdata for endian-independence + * use elf_getdata to get update of .shstrtab + */ + while ((data = (shdr.sh_type == SHT_STRTAB ? + elf_getdata(scn, data) : elf_rawdata(scn, data))) != NULL) { + if (data->d_buf == NULL) { + cryptodebug("elfsign_hash: %s has NULL data", + name); + continue; + } + cryptodebug("elfsign_hash: updating hash " + "with %s data size=%d", name, data->d_size); + SHA1Update(&ctx, data->d_buf, data->d_size); + } + } + if (elf_errmsg(0) != NULL) { + cryptodebug("elfsign_hash: %s", elf_errmsg(-1)); + elfstat = ELFSIGN_FAILED; + goto done; + } + + SHA1Final(hash, &ctx); + *hash_len = SHA1_DIGEST_LENGTH; + { /* DEBUG START */ + const int hashstr_len = (*hash_len) * 2 + 1; + char *hashstr = malloc(hashstr_len); + + if (hashstr != NULL) { + tohexstr(hash, *hash_len, hashstr, hashstr_len); + cryptodebug("hash value is: %s", hashstr); + free(hashstr); + } + } /* DEBUG END */ + elfstat = ELFSIGN_SUCCESS; +done: + return (elfstat); +} + +/* + * elfsign_hash - return the hash of the ELF sections affecting execution. + * + * IN: ess, hash_len + * OUT: hash, hash_len + */ +ELFsign_status_t +elfsign_hash(ELFsign_t ess, uchar_t *hash, size_t *hash_len) +{ + return (elfsign_hash_common(ess, hash, hash_len, B_FALSE)); +} + +/* + * elfsign_hash_mem_resident - return the hash of the ELF sections + * with only memory resident sections. + * + * IN: ess, hash_len + * OUT: hash, hash_len + */ +ELFsign_status_t +elfsign_hash_mem_resident(ELFsign_t ess, uchar_t *hash, size_t *hash_len) +{ + return (elfsign_hash_common(ess, hash, hash_len, B_TRUE)); +} + +/* + * elfsign_hash_esa = return the hash of the esa_buffer + * + * IN: ess, esa_buf, esa_buf_len, hash_len + * OUT: hash, hash_len + */ +ELFsign_status_t +elfsign_hash_esa(ELFsign_t ess, uchar_t *esa_buf, size_t esa_buf_len, + uchar_t **hash, size_t *hash_len) +{ + SHA1_CTX ctx; + + cryptodebug("esa_hash version is: %s", + version_to_str(ess->es_version)); + if (ess->es_version <= FILESIG_VERSION2) { + /* + * old rsa_md5_sha1 format + * signed with MD5 digest, just pass full esa_buf + */ + *hash = esa_buf; + *hash_len = esa_buf_len; + return (ELFSIGN_SUCCESS); + } + + if (*hash_len < SHA1_DIGEST_LENGTH) + return (ELFSIGN_FAILED); + + bzero(*hash, *hash_len); + SHA1Init(&ctx); + SHA1Update(&ctx, esa_buf, esa_buf_len); + SHA1Final(*hash, &ctx); + *hash_len = SHA1_DIGEST_LENGTH; + + { /* DEBUG START */ + const int hashstr_len = (*hash_len) * 2 + 1; + char *hashstr = malloc(hashstr_len); + + if (hashstr != NULL) { + tohexstr(*hash, *hash_len, hashstr, hashstr_len); + cryptodebug("esa_hash value is: %s", hashstr); + free(hashstr); + } + } /* DEBUG END */ + + return (ELFSIGN_SUCCESS); +} + +/* + * elfsign_verify_signature - Verify the signature of the ELF object. + * + * IN: ess + * OUT: esipp + * RETURNS: + * ELFsign_status_t + */ +ELFsign_status_t +elfsign_verify_signature(ELFsign_t ess, struct ELFsign_sig_info **esipp) +{ + ELFsign_status_t ret = ELFSIGN_FAILED; + struct filesignatures *fssp; + struct filesig *fsgp; + size_t fslen; + struct filesig_extraction fsx; + uchar_t hash[SIG_MAX_LENGTH]; + size_t hash_len; + ELFCert_t cert = NULL; + int sigcnt; + int nocert = 0; + struct ELFsign_sig_info *esip = NULL; + + if (esipp != NULL) { + esip = (struct ELFsign_sig_info *) + calloc(1, sizeof (struct ELFsign_sig_info)); + *esipp = esip; + } + + /* + * Find out which cert we need, based on who signed the ELF object + */ + if (elfsign_signatures(ess, &fssp, &fslen, ES_GET) != ELFSIGN_SUCCESS) { + return (ELFSIGN_NOTSIGNED); + } + + if (fssp->filesig_cnt < 1) { + ret = ELFSIGN_FAILED; + goto cleanup; + } + + fsgp = &fssp->filesig_sig; + + /* + * Scan the signature block, looking for a verifiable signature + */ + for (sigcnt = 0; sigcnt < fssp->filesig_cnt; + sigcnt++, fsgp = filesig_next(fsgp)) { + ess->es_version = filesig_extract(fsgp, &fsx); + cryptodebug("elfsign_verify_signature: version=%s", + version_to_str(ess->es_version)); + switch (ess->es_version) { + case FILESIG_VERSION1: + case FILESIG_VERSION2: + case FILESIG_VERSION3: + case FILESIG_VERSION4: + break; + default: + ret = ELFSIGN_FAILED; + goto cleanup; + } + + cryptodebug("elfsign_verify_signature: signer_DN=\"%s\"", + fsx.fsx_signer_DN); + cryptodebug("elfsign_verify_signature: algorithmOID=\"%s\"", + fsx.fsx_sig_oid); + /* return signer DN if requested */ + if (esipp != NULL) { + esip->esi_format = fsx.fsx_format; + if (esip->esi_signer != NULL) + free(esip->esi_signer); + esip->esi_signer = strdup(fsx.fsx_signer_DN); + esip->esi_time = fsx.fsx_time; + } + + /* + * look for certificate + */ + if (cert != NULL) + elfcertlib_releasecert(ess, cert); + + /* + * skip unfound certificates + */ + if (!elfcertlib_getcert(ess, ess->es_certpath, + fsx.fsx_signer_DN, &cert, ess->es_action)) { + cryptodebug("unable to find certificate " + "with DN=\"%s\" for %s", + fsx.fsx_signer_DN, ess->es_pathname); + nocert++; + continue; + } + + /* + * skip unverified certificates + * force verification of crypto certs + */ + if ((ess->es_action == ES_GET_CRYPTO || + strstr(fsx.fsx_signer_DN, ELFSIGN_CRYPTO)) && + !elfcertlib_verifycert(ess, cert)) { + cryptodebug("elfsign_verify_signature: invalid cert"); + nocert++; + continue; + } + + /* + * At this time the only sha1WithRSAEncryption is supported, + * so check that is what we have and skip with anything else. + */ + if (strcmp(fsx.fsx_sig_oid, OID_sha1WithRSAEncryption) != 0) { + continue; + } + + nocert = 0; + /* + * compute file hash + */ + hash_len = sizeof (hash); + if (elfsign_hash(ess, hash, &hash_len) != ELFSIGN_SUCCESS) { + cryptodebug("elfsign_verify_signature:" + " elfsign_hash failed"); + ret = ELFSIGN_FAILED; + break; + } + + { /* DEBUG START */ + const int sigstr_len = fsx.fsx_sig_len * 2 + 1; + char *sigstr = malloc(sigstr_len); + + if (sigstr != NULL) { + tohexstr(fsx.fsx_signature, fsx.fsx_sig_len, + sigstr, sigstr_len); + cryptodebug("signature value is: %s", sigstr); + free(sigstr); + } + } /* DEBUG END */ + + if (elfcertlib_verifysig(ess, cert, + fsx.fsx_signature, fsx.fsx_sig_len, hash, hash_len)) { + if (ess->es_sigvercallback) + (ess->es_sigvercallback) + (ess->es_callbackctx, fssp, fslen, cert); + /* + * The signature is verified! + * Check if this is a restricted provider + */ + if (strstr(fsx.fsx_signer_DN, USAGELIMITED) == NULL) + ret = ELFSIGN_SUCCESS; + else { + cryptodebug("DN is tagged for usagelimited"); + ret = elfsign_verify_esa(ess, + fsx.fsx_signature, fsx.fsx_sig_len); + } + break; + } + + cryptodebug("elfsign_verify_signature: invalid signature"); + } + +cleanup: + if (cert != NULL) + elfcertlib_releasecert(ess, cert); + + free(fssp); + if (ret == ELFSIGN_FAILED && nocert) + ret = ELFSIGN_INVALID_CERTPATH; + return (ret); +} + +/* + * Verify the contents of the .esa file, as per Jumbo export control + * document. Logic in this function should remain unchanged, unless + * a misinterpretation of the jumbo case was found or if there are + * changes in export regulations necessitating a change. + * + * If the .esa file exists, but is somehow corrupted, we just return + * that this is restricted. This is consistent with the Jumbo export + * case covering this library and other compenents of ON. Do not change + * this logic without consulting export control. + * + * Please see do_gen_esa() for a description of the esa file format. + * + */ +static ELFsign_status_t +elfsign_verify_esa(ELFsign_t ess, uchar_t *orig_sig, size_t orig_sig_len) +{ + ELFsign_status_t ret = ELFSIGN_RESTRICTED; + char *elfobj_esa = NULL; + size_t elfobj_esa_len; + int esa_fd = -1; + size_t esa_buf_len = 0; + uchar_t *main_sig; + size_t main_sig_len = 0; + uchar_t hash[SIG_MAX_LENGTH], *hash_ptr = hash; + size_t hash_len = SIG_MAX_LENGTH; + char *esa_dn = NULL; + size_t esa_dn_len = 0; + uchar_t *esa_sig; + size_t esa_sig_len = 0; + uchar_t *esa_file_buffer = NULL, *esa_file_ptr; + struct stat statbuf; + ELFCert_t cert = NULL; + + cryptodebug("elfsign_verify_esa"); + + /* does the activation file exist? */ + elfobj_esa_len = strlen(ess->es_pathname) + ESA_LEN + 1; + elfobj_esa = malloc(elfobj_esa_len); + if (elfobj_esa == NULL) { + cryptoerror(LOG_STDERR, + gettext("Unable to allocate buffer for esa filename.")); + goto cleanup; + } + + (void) strlcpy(elfobj_esa, ess->es_pathname, elfobj_esa_len); + (void) strlcat(elfobj_esa, ESA, elfobj_esa_len); + + if ((esa_fd = open(elfobj_esa, O_RDONLY|O_NONBLOCK)) == -1) { + cryptodebug("No .esa file was found, or it was unreadable"); + goto cleanup; + } + + cryptodebug("Reading contents of esa file %s", elfobj_esa); + + if (fstat(esa_fd, &statbuf) == -1) { + cryptoerror(LOG_STDERR, + gettext("Can't stat %s"), elfobj_esa); + goto cleanup; + } + + /* + * mmap the buffer to save on syscalls + */ + esa_file_buffer = (uchar_t *)mmap(NULL, statbuf.st_size, PROT_READ, + MAP_PRIVATE, esa_fd, 0); + + if (esa_file_buffer == MAP_FAILED) { + cryptoerror(LOG_STDERR, + gettext("Unable to mmap file to a buffer for %s."), + elfobj_esa); + goto cleanup; + } + + esa_file_ptr = esa_file_buffer; + elfsign_buffer_len(ess, &main_sig_len, esa_file_ptr, ES_GET); + esa_file_ptr += sizeof (uint32_t); + cryptodebug("Contents of esa file: main_sig_len=%d", main_sig_len); + main_sig = esa_file_ptr; + + esa_file_ptr += main_sig_len; + + /* verify .esa main signature versus original signature */ + if (main_sig_len != orig_sig_len || + memcmp(main_sig, orig_sig, orig_sig_len) != 0) { + cryptoerror(LOG_STDERR, + gettext("Unable to match original signature from %s."), + elfobj_esa); + goto cleanup; + } + + elfsign_buffer_len(ess, &esa_dn_len, esa_file_ptr, ES_GET); + esa_file_ptr += sizeof (uint32_t); + cryptodebug("Contents of esa file: esa_dn_len=%d", esa_dn_len); + + esa_dn = malloc(esa_dn_len + 1); + if (esa_dn == NULL) { + cryptoerror(LOG_ERR, + gettext("Unable to allocate memory for dn buffer.")); + goto cleanup; + } + (void) memcpy(esa_dn, esa_file_ptr, esa_dn_len); + esa_dn[esa_dn_len] = '\0'; + esa_file_ptr += esa_dn_len; + cryptodebug("Contents of esa file: esa_dn=%s", esa_dn); + + elfsign_buffer_len(ess, &esa_sig_len, esa_file_ptr, ES_GET); + esa_file_ptr += sizeof (uint32_t); + cryptodebug("Contents of esa file: esa_sig_len=%d", esa_sig_len); + + esa_sig = esa_file_ptr; + + cryptodebug("Read esa contents, now verifying"); + + /* + * dn used in .esa file should not be limited. + */ + if (strstr(esa_dn, USAGELIMITED) != NULL) { + cryptoerror(LOG_ERR, + gettext("DN for .esa file is tagged as limited for %s.\n" + "Activation files should only be tagged as unlimited.\n" + "Please contact vendor for this provider"), + ess->es_pathname); + goto cleanup; + } + + if (!elfcertlib_getcert(ess, ess->es_certpath, esa_dn, &cert, + ess->es_action)) { + cryptodebug(gettext("unable to find certificate " + "with DN=\"%s\" for %s"), + esa_dn, ess->es_pathname); + goto cleanup; + } + + /* + * Since we've already matched the original signature + * and the main file signature, we can just verify the esa signature + * against the main file signature. + */ + esa_buf_len = sizeof (uint32_t) + main_sig_len; + + if (elfsign_hash_esa(ess, esa_file_buffer, esa_buf_len, + &hash_ptr, &hash_len) != ELFSIGN_SUCCESS) { + cryptoerror(LOG_STDERR, + gettext("Unable to hash activation contents.")); + goto cleanup; + } + + + if (!elfcertlib_verifysig(ess, cert, esa_sig, esa_sig_len, + hash_ptr, hash_len)) { + cryptoerror(LOG_STDERR, + gettext("Unable to verify .esa contents for %s"), + ess->es_pathname); + goto cleanup; + } + + cryptodebug("Verified esa contents"); + if (ess->es_sigvercallback) + (ess->es_sigvercallback) (ess->es_callbackctx, + esa_file_buffer, statbuf.st_size, cert); + + /* + * validate the certificate used to sign the activation file + */ + if (!elfcertlib_verifycert(ess, cert)) { + cryptoerror(LOG_STDERR, + gettext("Unable to verify .esa certificate %s for %s"), + esa_dn, ess->es_pathname); + goto cleanup; + } + + cryptodebug("Verified esa certificate"); + ret = ELFSIGN_SUCCESS; + +cleanup: + if (elfobj_esa != NULL) + free(elfobj_esa); + + if (esa_fd != -1) + (void) close(esa_fd); + + if (esa_file_buffer != NULL) + (void) munmap((caddr_t)esa_file_buffer, statbuf.st_size); + + if (esa_dn != NULL) + free(esa_dn); + + if (cert != NULL) + elfcertlib_releasecert(ess, cert); + + return (ret); +} + +static uint32_t +elfsign_switch_uint32(uint32_t i) +{ + return (((i & 0xff) << 24) | ((i & 0xff00) << 8) | + ((i >> 8) & 0xff00) | ((i >> 24) & 0xff)); +} + +static uint64_t +elfsign_switch_uint64(uint64_t i) +{ + return (((uint64_t)elfsign_switch_uint32(i) << 32) | + (elfsign_switch_uint32(i >> 32))); +} + +/* + * If appropriate, switch the endianness of the filesignatures structure + * Examine the structure only when it is in native endianness + */ +static ELFsign_status_t +elfsign_switch(ELFsign_t ess, struct filesignatures *fssp, + enum ES_ACTION action) +{ + int fscnt; + filesig_vers_t version; + struct filesig *fsgp, *fsgpnext; + + if (ess->es_same_endian) + return (ELFSIGN_SUCCESS); + + if (ES_ACTISUPDATE(action)) + fscnt = fssp->filesig_cnt; + fssp->filesig_cnt = elfsign_switch_uint32(fssp->filesig_cnt); + if (!ES_ACTISUPDATE(action)) + fscnt = fssp->filesig_cnt; + + fsgp = &(fssp)->filesig_sig; + for (; fscnt > 0; fscnt--, fsgp = fsgpnext) { + if (ES_ACTISUPDATE(action)) { + version = fsgp->filesig_version; + fsgpnext = filesig_next(fsgp); + } + fsgp->filesig_size = + elfsign_switch_uint32(fsgp->filesig_size); + fsgp->filesig_version = + elfsign_switch_uint32(fsgp->filesig_version); + if (!ES_ACTISUPDATE(action)) { + version = fsgp->filesig_version; + fsgpnext = filesig_next(fsgp); + } + switch (version) { + case FILESIG_VERSION1: + case FILESIG_VERSION2: + fsgp->filesig_v1_dnsize = + elfsign_switch_uint32(fsgp->filesig_v1_dnsize); + fsgp->filesig_v1_sigsize = + elfsign_switch_uint32(fsgp->filesig_v1_sigsize); + fsgp->filesig_v1_oidsize = + elfsign_switch_uint32(fsgp->filesig_v1_oidsize); + break; + case FILESIG_VERSION3: + case FILESIG_VERSION4: + fsgp->filesig_v3_time = + elfsign_switch_uint64(fsgp->filesig_v3_time); + fsgp->filesig_v3_dnsize = + elfsign_switch_uint32(fsgp->filesig_v3_dnsize); + fsgp->filesig_v3_sigsize = + elfsign_switch_uint32(fsgp->filesig_v3_sigsize); + fsgp->filesig_v3_oidsize = + elfsign_switch_uint32(fsgp->filesig_v3_oidsize); + break; + default: + cryptodebug("elfsign_switch: failed"); + return (ELFSIGN_FAILED); + } + } + return (ELFSIGN_SUCCESS); +} + +/* + * get/put an integer value from/to a buffer, possibly of opposite endianness + */ +void +elfsign_buffer_len(ELFsign_t ess, size_t *ip, uchar_t *cp, + enum ES_ACTION action) +{ + uint32_t tmp; + + if (!ES_ACTISUPDATE(action)) { + /* fetch integer from buffer */ + (void) memcpy(&tmp, cp, sizeof (tmp)); + if (!ess->es_same_endian) { + tmp = elfsign_switch_uint32(tmp); + } + *ip = tmp; + } else { + /* put integer into buffer */ + tmp = *ip; + if (!ess->es_same_endian) { + tmp = elfsign_switch_uint32(tmp); + } + (void) memcpy(cp, &tmp, sizeof (tmp)); + } +} + +char const * +elfsign_strerror(ELFsign_status_t elferror) +{ + char const *msg = NULL; + + switch (elferror) { + case ELFSIGN_SUCCESS: + msg = gettext("sign or verify of ELF object succeeded"); + break; + case ELFSIGN_FAILED: + msg = gettext("sign or verify of ELF object failed"); + break; + case ELFSIGN_NOTSIGNED: + msg = gettext("ELF object not signed"); + break; + case ELFSIGN_INVALID_CERTPATH: + msg = gettext("cannot access certificate"); + break; + case ELFSIGN_INVALID_ELFOBJ: + msg = gettext("unable to open as an ELF object"); + break; + case ELFSIGN_RESTRICTED: + msg = gettext("ELF object is restricted"); + break; + case ELFSIGN_UNKNOWN: + default: + msg = gettext("Unknown error"); + break; + } + + return (msg); +} + +boolean_t +elfsign_sig_info(struct filesignatures *fssp, struct ELFsign_sig_info **esipp) +{ + struct filesig_extraction fsx; + struct ELFsign_sig_info *esip; + + esip = (struct ELFsign_sig_info *) + calloc(1, sizeof (struct ELFsign_sig_info)); + *esipp = esip; + if (esip == NULL) + return (B_FALSE); + + switch (filesig_extract(&fssp->filesig_sig, &fsx)) { + case FILESIG_VERSION1: + case FILESIG_VERSION2: + case FILESIG_VERSION3: + case FILESIG_VERSION4: + esip->esi_format = fsx.fsx_format; + esip->esi_signer = strdup(fsx.fsx_signer_DN); + esip->esi_time = fsx.fsx_time; + break; + default: + free(esip); + *esipp = NULL; + } + + return (*esipp != NULL); +} + +void +elfsign_sig_info_free(struct ELFsign_sig_info *esip) +{ + if (esip != NULL) { + free(esip->esi_signer); + free(esip); + } +} diff --git a/usr/src/lib/libelfsign/common/libelfsign.h b/usr/src/lib/libelfsign/common/libelfsign.h new file mode 100644 index 0000000000..8637fc4c6d --- /dev/null +++ b/usr/src/lib/libelfsign/common/libelfsign.h @@ -0,0 +1,194 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _LIBELFSIGN_H +#define _LIBELFSIGN_H + +#pragma ident "%Z%%M% %I% %E% SMI" + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * libelfsign Private Interfaces + * This Header file should not be shipped as part of Solaris binary or + * source products. + */ + +#include <sys/crypto/elfsign.h> +#include <libelf.h> +#include <fcntl.h> +#include <md5.h> +#include <sha1.h> +#include <kmfapi.h> + +/* + * Certificate-related definitions + */ +#define ELFSIGN_CRYPTO "Solaris Cryptographic Framework" +#define USAGELIMITED "OU=UsageLimited" +#define ESA ".esa" +#define ESA_LEN sizeof (".esa") + +typedef enum ELFCert_VStatus_e { + E_UNCHECKED, + E_OK, + E_IS_TA, + E_FAILED +} ELFCert_VStatus_t; + +typedef struct ELFCert_s { + ELFCert_VStatus_t c_verified; + char *c_subject; + char *c_issuer; + KMF_X509_DER_CERT c_cert; + KMF_KEY_HANDLE c_privatekey; +} *ELFCert_t; + +#define CRYPTO_CERTS_DIR "/etc/crypto/certs" +#define ETC_CERTS_DIR "/etc/certs" + +/* + * libelfsign actions + */ +enum ES_ACTION { + ES_GET, + ES_GET_CRYPTO, + ES_UPDATE, + ES_UPDATE_RSA_MD5_SHA1, + ES_UPDATE_RSA_SHA1 +}; +#define ES_ACTISUPDATE(a) ((a) >= ES_UPDATE) + +/* + * Context for elfsign operation + */ +struct ELFsign_s { + Elf *es_elf; + char *es_pathname; + char *es_certpath; + int es_fd; + size_t es_shstrndx; + enum ES_ACTION es_action; + KMF_KEY_HANDLE es_privatekey; + filesig_vers_t es_version; + boolean_t es_same_endian; + boolean_t es_has_phdr; + char es_ei_class; + struct flock es_flock; + KMF_HANDLE_T es_kmfhandle; + void *es_callbackctx; + void (*es_sigvercallback)(void *, void *, size_t, ELFCert_t); + void (*es_certCAcallback)(void *, ELFCert_t, char *); + void (*es_certvercallback)(void *, ELFCert_t, ELFCert_t); +}; + +#define ES_FMT_RSA_MD5_SHA1 "rsa_md5_sha1" +#define ES_FMT_RSA_SHA1 "rsa_sha1" + +/* + * ELF signature handling + */ +typedef struct ELFsign_s *ELFsign_t; +struct ELFsign_sig_info { + char *esi_format; + char *esi_signer; + time_t esi_time; +}; + +extern struct filesignatures *elfsign_insert_dso(ELFsign_t ess, + struct filesignatures *fsp, const char *dn, int dn_len, + const uchar_t *sig, int sig_len, const char *oid, int oid_len); +extern filesig_vers_t elfsign_extract_sig(ELFsign_t ess, + struct filesignatures *fsp, uchar_t *sig, size_t *sig_len); +extern ELFsign_status_t elfsign_begin(const char *, + enum ES_ACTION, ELFsign_t *); +extern void elfsign_end(ELFsign_t ess); +extern ELFsign_status_t elfsign_setcertpath(ELFsign_t ess, const char *path); +extern ELFsign_status_t elfsign_verify_signature(ELFsign_t ess, + struct ELFsign_sig_info **esipp); +extern ELFsign_status_t elfsign_hash(ELFsign_t ess, uchar_t *hash, + size_t *hash_len); +extern ELFsign_status_t elfsign_hash_mem_resident(ELFsign_t ess, + uchar_t *hash, size_t *hash_len); +extern 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); +extern void elfsign_buffer_len(ELFsign_t ess, size_t *ip, uchar_t *cp, + enum ES_ACTION action); + +extern void elfsign_setcallbackctx(ELFsign_t ess, void *ctx); +extern void elfsign_setsigvercallback(ELFsign_t ess, + void (*cb)(void *, void *, size_t, ELFCert_t)); +extern ELFsign_status_t elfsign_signatures(ELFsign_t ess, + struct filesignatures **fspp, size_t *fs_len, enum ES_ACTION action); + +extern char const *elfsign_strerror(ELFsign_status_t); +extern boolean_t elfsign_sig_info(struct filesignatures *fssp, + struct ELFsign_sig_info **esipp); +extern void elfsign_sig_info_free(struct ELFsign_sig_info *); + +/* + * ELF "Certificate Library" + */ + +extern const char _PATH_ELFSIGN_CERTS[]; + +#define ELFCERT_MAX_DN_LEN 255 + +extern boolean_t elfcertlib_init(ELFsign_t); +extern void elfcertlib_fini(ELFsign_t); +extern boolean_t elfcertlib_settoken(ELFsign_t, char *); +extern void elfcertlib_setcertCAcallback(ELFsign_t ess, + void (*cb)(void *, ELFCert_t, char *)); +extern void elfcertlib_setcertvercallback(ELFsign_t ess, + void (*cb)(void *, ELFCert_t, ELFCert_t)); + +extern boolean_t elfcertlib_getcert(ELFsign_t ess, char *cert_pathname, + char *signer_DN, ELFCert_t *certp, enum ES_ACTION action); +extern void elfcertlib_releasecert(ELFsign_t, ELFCert_t); +extern char *elfcertlib_getdn(ELFCert_t cert); +extern char *elfcertlib_getissuer(ELFCert_t cert); + +extern boolean_t elfcertlib_loadprivatekey(ELFsign_t ess, ELFCert_t cert, + const char *path); +extern boolean_t elfcertlib_loadtokenkey(ELFsign_t ess, ELFCert_t cert, + const char *token_id, const char *pin); + +extern boolean_t elfcertlib_sign(ELFsign_t ess, ELFCert_t cert, + const uchar_t *data, size_t data_len, uchar_t *sig, + size_t *sig_len); + +extern boolean_t elfcertlib_verifycert(ELFsign_t ess, ELFCert_t cert); +extern boolean_t elfcertlib_verifysig(ELFsign_t ess, ELFCert_t cert, + const uchar_t *sig, size_t sig_len, + const uchar_t *data, size_t data_len); + +#ifdef __cplusplus +} +#endif + +#endif /* _LIBELFSIGN_H */ diff --git a/usr/src/lib/libelfsign/common/llib-lelfsign b/usr/src/lib/libelfsign/common/llib-lelfsign new file mode 100644 index 0000000000..5ed60d4a75 --- /dev/null +++ b/usr/src/lib/libelfsign/common/llib-lelfsign @@ -0,0 +1,32 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" +/* LINTLIBRARY */ +/* PROTOLIB1 */ + +#include <sys/crypto/elfsign.h> +#include <libelfsign.h> diff --git a/usr/src/lib/libelfsign/common/mapfile.map b/usr/src/lib/libelfsign/common/mapfile.map new file mode 100644 index 0000000000..b79ca2d3a9 --- /dev/null +++ b/usr/src/lib/libelfsign/common/mapfile.map @@ -0,0 +1,62 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +SUNW_1.1 { + global: + elfsign_begin; + elfsign_buffer_len; + elfsign_end; + elfsign_extract_sig; + elfsign_hash; + elfsign_hash_esa; + elfsign_hash_mem_resident; + elfsign_insert_dso; + elfsign_setcallbackctx; + elfsign_setsigvercallback; + elfsign_setcertpath; + elfsign_sig_info; + elfsign_sig_info_free; + elfsign_signatures; + elfsign_strerror; + elfsign_verify_signature; + elfcertlib_getcert; + elfcertlib_getdn; + elfcertlib_getissuer; + elfcertlib_init; + elfcertlib_loadprivatekey; + elfcertlib_loadtokenkey; + elfcertlib_releasecert; + elfcertlib_setcertCAcallback; + elfcertlib_setcertvercallback; + elfcertlib_settoken; + elfcertlib_sign; + elfcertlib_verifycert; + elfcertlib_verifysig; + local: + *; +}; diff --git a/usr/src/lib/libelfsign/i386/Makefile b/usr/src/lib/libelfsign/i386/Makefile new file mode 100644 index 0000000000..a31def5dc7 --- /dev/null +++ b/usr/src/lib/libelfsign/i386/Makefile @@ -0,0 +1,35 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# + +# +# Copyright 2007 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +#ident "%Z%%M% %I% %E% SMI" +# +# lib/libelfsign/i386/Makefile +# + +include ../Makefile.com + +.KEEP_STATE: + +install: all $(ROOTLIBS) $(ROOTLINKS) $(ROOTLINT) diff --git a/usr/src/lib/libelfsign/sparc/Makefile b/usr/src/lib/libelfsign/sparc/Makefile new file mode 100644 index 0000000000..7c783f69c9 --- /dev/null +++ b/usr/src/lib/libelfsign/sparc/Makefile @@ -0,0 +1,33 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# + +# +# Copyright 2007 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# ident "%Z%%M% %I% %E% SMI" +# + +include ../Makefile.com + +.KEEP_STATE: + +install: all $(ROOTLIBS) $(ROOTLINKS) $(ROOTLINT) diff --git a/usr/src/pkgdefs/etc/exception_list_i386 b/usr/src/pkgdefs/etc/exception_list_i386 index 6137bd6000..99ade1781a 100644 --- a/usr/src/pkgdefs/etc/exception_list_i386 +++ b/usr/src/pkgdefs/etc/exception_list_i386 @@ -65,6 +65,7 @@ usr/lib/llib-lcryptoutil.ln i386 usr/lib/llib-lcryptoutil i386 usr/lib/amd64/llib-lcryptoutil.ln i386 usr/include/cryptoutil.h i386 +usr/include/libelfsign.h i386 # # The following files are used by the DHCP service, the # standalone's DHCP implementation, and the kernel (nfs_dlboot). diff --git a/usr/src/pkgdefs/etc/exception_list_sparc b/usr/src/pkgdefs/etc/exception_list_sparc index 7eaadb2d7d..321746afcb 100644 --- a/usr/src/pkgdefs/etc/exception_list_sparc +++ b/usr/src/pkgdefs/etc/exception_list_sparc @@ -54,6 +54,7 @@ usr/lib/llib-lcryptoutil.ln sparc usr/lib/llib-lcryptoutil sparc usr/lib/sparcv9/llib-lcryptoutil.ln sparc usr/include/cryptoutil.h sparc +usr/include/libelfsign.h sparc # # The following files are used by libdhcpsvc, the # standalone's DHCP implementation, and the kernel (nfs_dlboot). diff --git a/usr/src/tools/Makefile b/usr/src/tools/Makefile index 55d1ee70c0..b42ce1b22f 100644 --- a/usr/src/tools/Makefile +++ b/usr/src/tools/Makefile @@ -54,7 +54,7 @@ COMMON_SUBDIRS= \ # # special versions of commands for use only in build # -CLOSED_UNSHIPPED_SUBDIRS = \ +UNSHIPPED_SUBDIRS = \ elfsign sparc_SUBDIRS= \ @@ -75,9 +75,11 @@ LINTSUBDIRS= \ protocmp \ protolist -SUBDIRS= $(BOOT_SUBDIRS) $($(MACH)_SUBDIRS) $(COMMON_SUBDIRS) - -$(CLOSED_BUILD)CLOSED_SUBDIRS= $(CLOSED_UNSHIPPED_SUBDIRS) +SUBDIRS= \ + $(BOOT_SUBDIRS) \ + $($(MACH)_SUBDIRS) \ + $(COMMON_SUBDIRS) \ + $(UNSHIPPED_SUBDIRS) # # Packages built here @@ -116,11 +118,11 @@ _msg := TARGET= _msg .KEEP_STATE: -all install: $(ROOTDIRS) .WAIT $(SUBDIRS) $(CLOSED_SUBDIRS) +all install: $(ROOTDIRS) .WAIT $(SUBDIRS) -clean: $(SUBDIRS) $(CLOSED_SUBDIRS) +clean: $(SUBDIRS) -clobber: $(SUBDIRS) $(CLOSED_SUBDIRS) +clobber: $(SUBDIRS) $(RM) -rf $(TOOLS_PROTO) pkg: install .WAIT $(PKG_SUBDIRS) @@ -135,9 +137,6 @@ $(SUBDIRS): FRC $(PKG_SUBDIRS): FRC @cd $@; pwd; $(MAKE) install -$(CLOSED_SUBDIRS): FRC - cd $(CLOSED)/tools/$@; pwd; $(MAKE) $(TARGET) - FRC: $(ROOTDIRS): diff --git a/usr/src/tools/elfsign/Makefile b/usr/src/tools/elfsign/Makefile new file mode 100644 index 0000000000..21a095650f --- /dev/null +++ b/usr/src/tools/elfsign/Makefile @@ -0,0 +1,88 @@ +# +# 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 +# + +# +#ident "%Z%%M% %I% %E% SMI" +# +# Copyright 2007 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# + +PROG= elfsign +SHFILES= elfsigncmp + +LIBOBJS= elfcertlib.o elfsignlib.o +OBJS= $(PROG).o $(LIBOBJS) + +.PARALLEL: $(OBJS) + +CMDDIR= $(SRC)/cmd/cmd-crypto/elfsign +LIBDIR= $(SRC)/lib/libelfsign/common +SRCS= $(CMDDIR)/$(PROG).c $(LIBOBJS:%.o=$(LIBDIR)/%.c) +CLEANFILES= $(PROG) $(OBJS) $(SHFILES) + +include ../../../src/tools/Makefile.tools + +OWNER= root +GROUP= bin +CFLAGS += $(CCVERBOSE) +CFLAGS += -_gcc=-fasm + +CPPFLAGS += -D_POSIX_PTHREAD_SEMANTICS +CPPFLAGS += -I$(SRC)/lib/libelfsign/common +CPPFLAGS += -I$(SRC)/uts/common +CPPFLAGS += -I$(SRC)/lib/libkmf/include +CPPFLAGS += -I$(SRC)/lib/libcryptoutil/common +LDFLAGS += -lmd -lelf -lkmf -lcryptoutil -lc + +# +# While the gate builds a libelfsign.so linked staticly against +# pksc11_softtoken, the tools version of libelfsign is dynamically linked +# against the build machines pkcs11_softtoken.so. +# +SOFTTOKENDIR = /usr/lib/security +SOFTTOKENLIB = pkcs11_softtoken.so +LDFLAGS += -R $(SOFTTOKENDIR) $(SOFTTOKENDIR)/$(SOFTTOKENLIB) + +MKDIR= mkdir + +.KEEP_STATE: + +all: $(PROG) $(SHFILES) + +install: all .WAIT $(ROOTONBLDMACHPROG) $(ROOTONBLDSHFILES) + +$(PROG): $(OBJS) + $(LINK.c) -o $@ $(OBJS) $(LDLIBS) + $(POST_PROCESS) + +%.o: $(CMDDIR)/%.c + $(COMPILE.c) -o $@ $< + +%.o: $(LIBDIR)/%.c + $(COMPILE.c) -o $@ $< + +lint: lint_SRCS + +clean: + $(RM) $(CLEANFILES) + +include ../../../src/tools/Makefile.targ diff --git a/usr/src/tools/elfsign/elfsigncmp.sh b/usr/src/tools/elfsign/elfsigncmp.sh new file mode 100644 index 0000000000..97f046a166 --- /dev/null +++ b/usr/src/tools/elfsign/elfsigncmp.sh @@ -0,0 +1,114 @@ +#! /usr/bin/sh +# +# 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 +# + +# +# ident "%Z%%M% %I% %E% SMI" +# +# Copyright 2007 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# + +prog=$0 +pd=`dirname $prog` +MACH=`uname -p` +elfcmp=$pd/elfcmp +elfsign=$pd/$MACH/elfsign + +aopt= +copt= +eopt= +Fopt= +kopt= +vopt= + +Usage() { + echo "Usage: $prog {sign|verify} [-v] [-a]" \ + "[-c <cert>] [-k <key>] [-F <format>] -e <elf>" 1>&2 + exit 1 +} + +if [ $# -lt 1 ]; then + Usage + fi +cmd=$1 +shift + +while getopts "ac:e:F:k:v" opt ; do + case $opt in + a) aopt=-a;; + c) copt="$OPTARG";; + e) eopt="$OPTARG";; + F) Fopt="$OPTARG";; + k) kopt="$OPTARG";; + v) vopt=-v;; + \?) Usage;; + esac +done + +case X$eopt in X) Usage;; esac + +tmpe=$eopt.e$$ +tmpo=$eopt.o$$ + +cpq() { + cp -p $1 $2 > /dev/null 2>&1 +} + +restore() { + cpq $tmpe $eopt +} + +cleanup() { + restore + rm -f $tmpe $tmpo +} + +trap cleanup 1 2 3 13 15 + +cpq $eopt $tmpe + +eval $elfsign $cmd $aopt $vopt ${copt:+-c} ${copt} ${kopt:+-k} ${kopt} \ + ${Fopt:+-F} ${Fopt} -e ${eopt} +rv=$? + +case $cmd:$rv in +sign:0) + if $elfcmp -v -S $tmpe $eopt > $tmpo 2>&1 + then + : # all's fine + else + rv=$? + echo "Warning: elfcmp failed: $eopt" 1>&2 + cat ${tmpo} 1>&2 + echo "current directory: `pwd`" 1>&2 + restore + cpq ${eopt} ${eopt}.elfcmp.failed.$$ + fi + ;; +sign:*) + restore + cpq ${eopt} ${eopt}.elfsign.failed.$$ + ;; +esac + +rm -f $tmpe $tmpo +exit $rv diff --git a/usr/src/tools/elfsign/inc.flg b/usr/src/tools/elfsign/inc.flg new file mode 100644 index 0000000000..315fa0558d --- /dev/null +++ b/usr/src/tools/elfsign/inc.flg @@ -0,0 +1,34 @@ +#!/bin/sh +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# + +# +# Copyright 2007 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# ident "%Z%%M% %I% %E% SMI" + +echo_file usr/src/uts/common/sys/crypto/elfsign.h +find_files "s.*.h" \ + usr/src/lib/libcryptoutil/ +find_files "s.*" \ + usr/src/cmd/cmd-crypto/elfsign \ + usr/src/lib/libelfsign/ diff --git a/usr/src/uts/common/sys/crypto/elfsign.h b/usr/src/uts/common/sys/crypto/elfsign.h index 107a0c8220..bbf58b87f4 100644 --- a/usr/src/uts/common/sys/crypto/elfsign.h +++ b/usr/src/uts/common/sys/crypto/elfsign.h @@ -58,10 +58,6 @@ typedef enum ELFsign_status_e { #define SIG_MAX_LENGTH 1024 #define ELF_SIGNATURE_SECTION ".SUNW_signature" -#define ELFSIGN_CRYPTO "Solaris Cryptographic Framework" -#define USAGELIMITED "OU=UsageLimited" -#define ESA ".esa" -#define ESA_LEN sizeof (".esa") typedef struct kcf_door_arg_s { short da_version; @@ -139,83 +135,6 @@ struct filesignatures { #define _PATH_KCFD_DOOR "/var/run/kcfd_door" -#define ES_FMT_RSA_MD5_SHA1 "rsa_md5_sha1" -#define ES_FMT_RSA_SHA1 "rsa_sha1" -enum ES_ACTION { - ES_GET, - ES_GET_CRYPTO, - ES_UPDATE, - ES_UPDATE_RSA_MD5_SHA1, - ES_UPDATE_RSA_SHA1 -}; -#define ES_ACTISUPDATE(a) ((a) >= ES_UPDATE) - -/* - * ELF signature handling - */ -typedef struct ELFsign_s *ELFsign_t; -struct ELFsign_sig_info { - char *esi_format; - char *esi_signer; - time_t esi_time; -}; - -extern struct filesignatures *elfsign_insert_dso(ELFsign_t ess, - struct filesignatures *fsp, const char *dn, int dn_len, - const uchar_t *sig, int sig_len, const char *oid, int oid_len); -extern filesig_vers_t elfsign_extract_sig(ELFsign_t ess, - struct filesignatures *fsp, uchar_t *sig, size_t *sig_len); -extern ELFsign_status_t elfsign_begin(const char *, - const char *, char *, enum ES_ACTION, ELFsign_t *); -extern void elfsign_end(ELFsign_t ess); -extern ELFsign_status_t elfsign_verify_signature(ELFsign_t ess, - struct ELFsign_sig_info **esipp); -extern ELFsign_status_t elfsign_hash(ELFsign_t ess, uchar_t *hash, - size_t *hash_len); -extern ELFsign_status_t elfsign_hash_mem_resident(ELFsign_t ess, - uchar_t *hash, size_t *hash_len); -extern 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); -extern void elfsign_buffer_len(ELFsign_t ess, size_t *ip, uchar_t *cp, - enum ES_ACTION action); - -extern ELFsign_status_t elfsign_signatures(ELFsign_t ess, - struct filesignatures **fspp, size_t *fs_len, enum ES_ACTION action); - -extern char const *elfsign_strerror(ELFsign_status_t); -extern boolean_t elfsign_sig_info(struct filesignatures *fssp, - struct ELFsign_sig_info **esipp); -extern void elfsign_sig_info_free(struct ELFsign_sig_info *); - -extern ELFsign_t elfsign_new_ess(void); - -/* - * ELF "Certificate Library" - */ - -extern const char _PATH_ELFSIGN_CERTS[]; - -#define ELFCERT_MAX_DN_LEN 255 - -typedef struct ELFCert_s *ELFCert_t; - -extern boolean_t elfcertlib_init(ELFsign_t, char *); - -extern boolean_t elfcertlib_getcert(ELFsign_t ess, char *cert_pathname, - char *signer_DN, ELFCert_t *certp, enum ES_ACTION action); -extern void elfcertlib_releasecert(ELFsign_t, ELFCert_t); -extern char *elfcertlib_getdn(ELFCert_t cert); -extern char *elfcertlib_getissuer(ELFCert_t cert); - -extern boolean_t elfcertlib_loadprivatekey(ELFsign_t ess, ELFCert_t cert, - const char *path); -extern boolean_t elfcertlib_loadtokenkey(ELFsign_t ess, ELFCert_t cert, - const char *token_id, const char *pin); - -extern boolean_t elfcertlib_sign(ELFsign_t ess, ELFCert_t cert, - const uchar_t *data, size_t data_len, uchar_t *sig, - size_t *sig_len); - #endif /* _KERNEL */ #ifdef __cplusplus |
