elfsign.c revision 0ebf3797ed9aceba2a3b361cf14badb82ac13478
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2008 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 <fcntl.h>
#include <libintl.h>
#include <locale.h>
#include <errno.h>
#include <strings.h>
#include <langinfo.h>
#include <cryptoutil.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 {
};
enum field_e {
};
#define ES_DEFAULT_KEYSIZE 1024
static struct {
char *cert; /* -c <certificate_file> | */
/* -r <certificate_request_file> */
char **elfobj; /* -e <elf_object> */
int elfcnt;
int extracnt;
char internal_req; /* Sun internal certificate request */
char *pinpath; /* -P <pin> */
char *privpath; /* -k <private_key> */
char *token_label; /* -T <token_label> */
} cmd_info;
enum ret_e {
};
struct field_s {
char *name;
} fields[] = {
{ "subject", FLD_SUBJECT },
{ "issuer", FLD_ISSUER },
{ "format", FLD_FORMAT },
{ "signer", FLD_SIGNER },
{ "time", FLD_TIME },
NULL, 0
};
static void usage(void);
static char *getpin(void);
static ret_t do_cert_request(char *);
static ret_t do_gen_esa(char *);
int
{
extern char *optarg;
char *opts; /* The set of flags for cmd */
int errflag = 0; /* We had an options parse error */
char c; /* current getopts flag */
#if !defined(TEXT_DOMAIN) /* Should be defiend by cc -D */
#endif
(void) textdomain(TEXT_DOMAIN);
cryptodebug_init("elfsign");
usage();
return (EXIT_INVALID_ARG);
}
opts = VERIFY_OPTS;
opts = REQUEST_OPTS;
} else {
scmd);
usage();
return (EXIT_INVALID_ARG);
}
/*
* Note: There is no need to check that optarg isn't NULL
* because getopt does that for us.
*/
else
cryptodebug("c=%c", c);
switch (c) {
case 'a':
/* not a normal sign operation, change the action */
action = do_gen_esa;
break;
case 'c':
break;
case 'e':
"Too many elf objects specified."));
return (EXIT_INVALID_ARG);
}
break;
case 'f':
{
break;
}
}
cryptodebug("Invalid field option");
errflag++;
}
}
break;
case 'F':
else {
cryptodebug("Invalid format option");
errflag++;
}
break;
case 'i': /* Undocumented internal Sun use only */
break;
case 'k':
errflag++;
break;
case 'P':
errflag++;
break;
case 'r':
break;
case 'T':
errflag++;
break;
case 'v':
break;
default:
errflag++;
}
}
optind++; /* we skipped over subcommand */
cryptodebug("Extra arguments, optind=%d, argc=%d",
errflag++;
}
case ES_VERIFY:
cryptodebug("Missing elfobj");
errflag++;
}
break;
case ES_SIGN:
cryptodebug("Missing privpath|token_label/cert/elfobj");
errflag++;
}
break;
case ES_REQUEST:
cryptodebug("Missing privpath|token_label/certreq");
errflag++;
}
break;
case ES_LIST:
cryptodebug("Neither or both of cert/elfobj");
errflag++;
}
break;
}
if (errflag) {
usage();
return (EXIT_INVALID_ARG);
}
case ES_REQUEST:
case ES_LIST:
break;
default:
{
int i;
}
}
break;
}
}
return (ret);
}
static void
usage(void)
{
/* BEGIN CSTYLED */
"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
{
switch (estatus) {
case ELFSIGN_SUCCESS:
case ELFSIGN_RESTRICTED:
break;
case ELFSIGN_INVALID_ELFOBJ:
"Unable to open %s as an ELF object."),
elfpath);
break;
default:
}
}
return (ret);
}
static ret_t
setcertpath(void)
{
return (EXIT_OKAY);
switch (estatus) {
case ELFSIGN_SUCCESS:
break;
case ELFSIGN_INVALID_CERTPATH:
}
ret = EXIT_BAD_CERT;
break;
default:
}
}
return (ret);
}
/*
* getpin - return pointer to token PIN in static storage
*/
static char *
getpin(void)
{
char *pp;
return (getpassphrase(
gettext("Enter PIN for PKCS#11 token: ")));
return (NULL);
}
return (NULL);
}
if (*pp == '\n')
*pp = '\0';
return (pinbuf);
}
/*
* Add the .SUNW_signature sections for the ELF signature
*/
static ret_t
{
char *dn;
cryptodebug("do_sign");
return (ret);
if (cmd_info.token_label &&
goto cleanup;
}
goto cleanup;
ret = EXIT_BAD_CERT;
goto cleanup;
}
goto cleanup;
}
} else {
goto cleanup;
}
goto cleanup;
}
}
/*
* Get the DN from the certificate.
*/
goto cleanup;
}
if (elfstat != ELFSIGN_SUCCESS) {
if (elfstat != ELFSIGN_NOTSIGNED) {
"signature block in %s"), object);
goto cleanup;
}
/*
* force creation and naming of signature section
* so the hash doesn't change
*/
"signature block into %s"), object);
goto cleanup;
}
}
object);
goto cleanup;
}
goto cleanup;
}
{ /* DEBUG START */
} /* DEBUG END */
object);
goto cleanup;
}
object);
goto cleanup;
}
gettext("elfsign: %s signed successfully.\n"),
object);
}
struct ELFsign_sig_info *esip;
}
}
return (ret);
}
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)
{
/* variables used for signing and writing to .esa file */
char *elfobj_esa;
int esa_fd;
size_t esa_buf_len = 0;
char *dn;
int realerrno = 0;
/*
* variables used for finding information on signer of main
* elfobject.
*/
cryptodebug("do_gen_esa");
return (ret);
/*
* Find the certificate we need to sign the activation file with.
*/
ret = EXIT_BAD_CERT;
goto clean_esa;
}
goto clean_esa;
}
} else {
goto clean_esa;
}
goto clean_esa;
}
}
/*
* Get the DN from the certificate.
*/
goto clean_esa;
}
/*
* Make sure they are not trying to sign .esa file with a
* limited certificate.
*/
"certficate without %s."), USAGELIMITED);
goto clean_esa;
}
/*
* Find information in the associated elfobject that will
* be needed to generate the activation file.
*/
"associated activation file can be created."),
object);
goto clean_esa;
}
"an associated activation file for the "
"signature format of %s."),
object);
goto clean_esa;
}
{ /* DEBUG START */
} /* DEBUG END */
goto clean_esa;
}
/*
* Write eventual contents of .esa file to a temporary
* buffer, so we can sign it before writing out to
* the file.
*/
goto clean_esa;
}
/*
* sign the buffer for the .esa file
*/
goto clean_esa;
}
{ /* DEBUG START */
} /* DEBUG END */
/*
* Create the empty activation file once we know
* we are working with the good data.
*/
if (elfobj_esa == NULL) {
goto clean_esa;
}
ESA_ERROR("Unable to create activation file: %s. %s.",
}
ESA_ERROR("Unable to write contents to %s. %s.",
}
{ /* DEBUG START */
} /* DEBUG END */
}
}
ESA_ERROR("Unable to write .esa signature len to %s. %s.",
}
goto clean_esa;
}
if (esa_fd != -1)
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
*/
static ret_t
{
struct ELFsign_sig_info *esip;
cryptodebug("do_verify");
return (retval);
return (retval);
}
switch (res) {
case ELFSIGN_SUCCESS:
gettext("elfsign: verification of %s passed.\n"),
object);
break;
case ELFSIGN_RESTRICTED:
gettext("elfsign: verification of %s passed, "
"but restricted.\n"), object);
break;
case ELFSIGN_FAILED:
case ELFSIGN_INVALID_CERTPATH:
object);
break;
case ELFSIGN_NOTSIGNED:
object);
break;
default:
"of %s."), object);
break;
}
return (retval);
}
#define SET_VALUE(f, s) \
kmfrv = f; \
char *e = NULL; \
(void) KMF_GetKMFErrorString(kmfrv, &e); \
gettext("Failed to %s: %s\n"), \
s, (e ? e : "unknown error")); \
if (e) free(e); \
goto cleanup; \
}
static KMF_RETURN
create_csr(char *dn)
{
char *err;
gettext("Error initializing KMF: %s\n"),
if (err)
return (kmfrv);
}
/* Get a PIN to store the private key in the token */
(void) KMF_Finalize(kmfhandle);
return (KMF_ERR_AUTH_FAILED);
}
goto cleanup;
}
}
/* Create the RSA keypair */
}
goto cleanup;
}
}
goto cleanup;
}
"SignatureAlgorithm");
KMF_OK) {
}
(void) KMF_FreeData(&signedCsr);
(void) KMF_FreeSignedCSR(&csr);
(void) KMF_Finalize(kmfhandle);
return (kmfrv);
}
static boolean_t
is_restricted(void)
{
/*
* 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
* and Sun customers.
* Case #3 should be covered in the developer documentation.
*/
/* BEGIN CSTYLED */
"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"));
"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 (;;) {
"Do you have retail export approval for use without restrictions based \n"
/* END CSTYLED */
goto demand_answer;
/* BEGIN CSTYLED */
"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"
/* END CSTYLED */
/*
* flush standard input so any remaining text
* does not affect next read.
*/
goto demand_answer;
return (B_FALSE);
return (B_TRUE);
} else
goto demand_answer;
return (B_FALSE);
}
gettext("You must specify an answer.\n\n"));
}
}
/*
* 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";
char *restriction = "";
cryptodebug("do_cert_request");
/*
* Get the DN prefix from the user
*/
switch (cmd_info.internal_req) {
case 'c':
"Enter Sun Microsystems, Inc. Release name.\n"
"This will be the prefix of the Certificate DN: "));
break;
case 's':
"Enter Sun Microsystems, Inc. Release name.\n"
"This will be the prefix of the Certificate DN: "));
break;
default:
"Enter Company Name / Stock Symbol"
" or some other globally unique identifier.\n"
"This will be the prefix of the Certificate DN: "));
break;
}
return (EXIT_INVALID_ARG);
}
} else {
"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:
break;
}
/* Update DN string */
return (EXIT_OKAY);
else
return (EXIT_CSR_FAILED);
}
static void
str_print(char *s)
{
if (s == NULL)
return;
}
/*ARGSUSED*/
static ret_t
{
struct ELFsign_sig_info *esip;
return (retval);
if (elfstat == ELFSIGN_SUCCESS) {
case FLD_FORMAT:
break;
case FLD_SIGNER:
break;
case FLD_TIME:
else
break;
default:
}
}
} else
} else {
/*
* Initialize the ESS record here even though we are not
* actually opening any ELF files.
*/
return (EXIT_MEMORY_ERROR);
case FLD_SUBJECT:
break;
case FLD_ISSUER:
break;
default:
}
} else
}
return (retval);
}
static void
{
}
static char *
{
static char buf[80];
char *bufp;
return (bufp);
}
static void
{
return;
return;
}