root/usr/src/cmd/cmd-crypto/elfsign/elfsign.c
/*
 * 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 (c) 2002, 2010, Oracle and/or its affiliates. All rights reserved.
 */

/*
 * 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 <cryptoutil.h>
#include <sys/crypto/elfsign.h>
#include <libelfsign.h>

#include <kmfapi.h>

#define SIGN            "sign"
#define SIGN_OPTS       "c: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_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 */
        int 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("ceFihkPTr", c) != NULL)
                        cryptodebug("c=%c, '%s'", c, optarg);
                else
                        cryptodebug("c=%c", c);

                switch (c) {
                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 [-v] [-e <elf_object>] -c <certificate_file>\n"
 "\t\t[-F <format>] -k <private_key_file> [elf_object]..."
 "\n"
 "\telfsign sign [-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 = EXIT_SIGN_FAILED;

        estatus = elfsign_begin(elfpath, cmd_info.es_action, &(cmd_info.ess));
        switch (estatus) {
        case ELFSIGN_SUCCESS:
                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 = EXIT_SIGN_FAILED;

        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: %s"), 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)) {
                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);
}

/*
 * 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_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_get_kmf_error_str(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_KEY_HANDLE pubk, prik;
        KMF_X509_NAME csrSubject;
        KMF_CSR_DATA csr;
        KMF_ALGORITHM_INDEX sigAlg = KMF_ALGID_MD5WithRSA;
        KMF_DATA signedCsr = { 0, NULL };
        char *err;
        KMF_ATTRIBUTE   attrlist[16];
        KMF_ENCODE_FORMAT       format;
        KMF_KEYSTORE_TYPE       kstype;
        KMF_KEY_ALG     keytype;
        uint32_t        keylength;
        KMF_CREDENTIAL  cred;
        char    *pin = NULL;
        int     numattr;

        if ((kmfrv = kmf_initialize(&kmfhandle, NULL, NULL)) != KMF_OK) {
                (void) kmf_get_kmf_error_str(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));

        if (cmd_info.privpath != NULL) {
                kstype = KMF_KEYSTORE_OPENSSL;
                format = KMF_FORMAT_ASN1;
        } else {
                boolean_t       readonly;
                /* args checking verified (cmd_info.token_label != NULL) */

                /* Get a PIN to store the private key in the token */
                pin = getpin();

                if (pin == NULL) {
                        (void) kmf_finalize(kmfhandle);
                        return (KMF_ERR_AUTH_FAILED);
                }

                kstype = KMF_KEYSTORE_PK11TOKEN;
                readonly = B_FALSE;

                numattr = 0;
                kmf_set_attr_at_index(attrlist, numattr++,
                    KMF_KEYSTORE_TYPE_ATTR, &kstype, sizeof (kstype));
                kmf_set_attr_at_index(attrlist, numattr++,
                    KMF_TOKEN_LABEL_ATTR, cmd_info.token_label,
                    strlen(cmd_info.token_label));
                kmf_set_attr_at_index(attrlist, numattr++,
                    KMF_READONLY_ATTR, &readonly, sizeof (readonly));
                kmfrv = kmf_configure_keystore(kmfhandle, numattr, attrlist);
                if (kmfrv != KMF_OK) {
                        goto cleanup;
                }
        }

        /* Create the RSA keypair */
        keytype = KMF_RSA;
        keylength = ES_DEFAULT_KEYSIZE;
        (void) memset(&prik, 0, sizeof (prik));
        (void) memset(&pubk, 0, sizeof (pubk));

        numattr = 0;
        kmf_set_attr_at_index(attrlist, numattr++,
            KMF_KEYSTORE_TYPE_ATTR, &kstype, sizeof (kstype));
        kmf_set_attr_at_index(attrlist, numattr++,
            KMF_KEYALG_ATTR, &keytype, sizeof (keytype));
        kmf_set_attr_at_index(attrlist, numattr++,
            KMF_KEYLENGTH_ATTR, &keylength, sizeof (keylength));
        if (pin != NULL) {
                cred.cred = pin;
                cred.credlen = strlen(pin);
                kmf_set_attr_at_index(attrlist, numattr++,
                    KMF_CREDENTIAL_ATTR, &cred, sizeof (KMF_CREDENTIAL));
        }
        kmf_set_attr_at_index(attrlist, numattr++,
            KMF_PRIVKEY_HANDLE_ATTR, &prik, sizeof (KMF_KEY_HANDLE));
        kmf_set_attr_at_index(attrlist, numattr++,
            KMF_PUBKEY_HANDLE_ATTR, &pubk, sizeof (KMF_KEY_HANDLE));
        if (kstype == KMF_KEYSTORE_OPENSSL) {
                kmf_set_attr_at_index(attrlist, numattr++,
                    KMF_KEY_FILENAME_ATTR, cmd_info.privpath,
                    strlen(cmd_info.privpath));
                kmf_set_attr_at_index(attrlist, numattr++,
                    KMF_ENCODE_FORMAT_ATTR, &format, sizeof (format));
        }

        kmfrv = kmf_create_keypair(kmfhandle, numattr, attrlist);
        if (kmfrv != KMF_OK) {
                (void) kmf_get_kmf_error_str(kmfrv, &err);
                cryptoerror(LOG_STDERR,
                    gettext("Create RSA keypair failed: %s"),
                    (err ? err : "unknown error"));
                free(err);
                goto cleanup;
        }

        kmfrv = kmf_dn_parser(dn, &csrSubject);
        if (kmfrv != KMF_OK) {
                (void) kmf_get_kmf_error_str(kmfrv, &err);
                cryptoerror(LOG_STDERR,
                    gettext("Error parsing subject name: %s\n"),
                    (err ? err : "unknown error"));
                free(err);
                goto cleanup;
        }

        SET_VALUE(kmf_set_csr_pubkey(kmfhandle, &pubk, &csr), "keypair");

        SET_VALUE(kmf_set_csr_version(&csr, 2), "version number");

        SET_VALUE(kmf_set_csr_subject(&csr, &csrSubject), "subject name");

        SET_VALUE(kmf_set_csr_sig_alg(&csr, sigAlg), "SignatureAlgorithm");

        if ((kmfrv = kmf_sign_csr(kmfhandle, &csr, &prik, &signedCsr)) ==
            KMF_OK) {
                kmfrv = kmf_create_csr_file(&signedCsr, KMF_FORMAT_PEM,
                    cmd_info.cert);
        }

cleanup:
        (void) kmf_free_kmf_key(kmfhandle, &prik);
        (void) kmf_free_data(&signedCsr);
        (void) kmf_free_signed_csr(&csr);
        (void) kmf_finalize(kmfhandle);

        return (kmfrv);
}


#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, "
            "OU=Solaris Cryptographic Framework, "
            "OU=Partner Object Signing, "
            "O=Sun Microsystems Inc";
        const char       SunCDNFMT[] =
            "CN=%s, "
            "OU=Class B, "
            "OU=Solaris Cryptographic Framework, "
            "OU=Corporate Object Signing, "
            "O=Sun Microsystems Inc";
        const char       SunSDNFMT[] =
            "CN=%s, "
            "OU=Class B, "
            "OU=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;
        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;
        }
        if ((fgets(cn, sizeof (cn), stdin) == 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);
        }

        /* Update DN string */
        dn_len = strlen(cn) + strlen(dnfmt);
        dn = malloc(dn_len + 1);
        (void) snprintf(dn, dn_len, dnfmt, cn);

        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));
}