blob: 5aa87d74c6cde429cec1cfd84ce7932430234743 [file] [log] [blame]
/*
*
* Copyright (c) 2013-2017 Nest Labs, Inc.
* All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* @file
* This file implements the command handler for the 'weave' tool
* that re-signs a Weave certificate.
*
*/
#ifndef __STDC_LIMIT_MACROS
#define __STDC_LIMIT_MACROS
#endif
#include <stdint.h>
#include <unistd.h>
#include <limits.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <getopt.h>
#include "weave-tool.h"
using namespace nl::Weave::Profiles::Security;
using namespace nl::Weave::ASN1;
#define CMD_NAME "weave resign-cert"
static bool HandleOption(const char *progName, OptionSet *optSet, int id, const char *name, const char *arg);
static OptionDef gCmdOptionDefs[] =
{
{ "cert", kArgumentRequired, 'c' },
{ "out", kArgumentRequired, 'o' },
{ "ca-cert", kArgumentRequired, 'C' },
{ "ca-key", kArgumentRequired, 'K' },
{ "self", kNoArgument, 's' },
{ "sha1", kNoArgument, '1' },
{ "sha256", kNoArgument, '2' },
{ NULL }
};
static const char *const gCmdOptionHelp =
" -c, --cert <file>\n"
"\n"
" File containing the certificate to be re-signed.\n"
"\n"
" -o, --out <file>\n"
"\n"
" File to contain the re-signed certificate.\n"
"\n"
" -C, --ca-cert <file>\n"
"\n"
" File containing CA certificate to be used to re-sign the certificate\n"
" (in PEM format).\n"
"\n"
" -K, --ca-key <file>\n"
"\n"
" File containing CA private key to be used to re-sign the certificate\n"
" (in PEM format).\n"
"\n"
" -s, --self\n"
"\n"
" Generate a self-signed certificate.\n"
"\n"
" -1, --sha1\n"
"\n"
" Re-sign the certificate using a SHA-1 hash.\n"
"\n"
" -2, --sha256\n"
"\n"
" Re-sign the certificate using a SHA-256 hash.\n"
"\n"
;
static OptionSet gCmdOptions =
{
HandleOption,
gCmdOptionDefs,
"COMMAND OPTIONS",
gCmdOptionHelp
};
static HelpOptions gHelpOptions(
CMD_NAME,
"Usage: " CMD_NAME " [ <options...> ]\n",
WEAVE_VERSION_STRING "\n" COPYRIGHT_STRING,
"Resign a weave certificate using a new CA certificate/key."
);
static OptionSet *gCmdOptionSets[] =
{
&gCmdOptions,
&gHelpOptions,
NULL
};
static const char *gInCertFileName = NULL;
static const char *gOutCertFileName = NULL;
static const char *gCACertFileName = NULL;
static const char *gCAKeyFileName = NULL;
static const EVP_MD *gSigHashAlgo = NULL;
static bool gSelfSign = false;
bool Cmd_ResignCert(int argc, char *argv[])
{
bool res = true;
FILE *outCertFile = NULL;
X509 *caCert = NULL;
X509 *cert = NULL;
EVP_PKEY *caKey = NULL;
CertFormat inCertFmt;
bool outCertFileCreated = false;
if (argc == 1)
{
gHelpOptions.PrintBriefUsage(stderr);
ExitNow(res = true);
}
if (!ParseArgs(CMD_NAME, argc, argv, gCmdOptionSets))
{
ExitNow(res = true);
}
if (gInCertFileName == NULL)
{
fprintf(stderr, "Please specify certificate to be resigned using --cert option.\n");
ExitNow(res = false);
}
if (gOutCertFileName == NULL)
{
fprintf(stderr, "Please specify the file name for the new certificate using the --out option.\n");
ExitNow(res = false);
}
if (gCACertFileName == NULL && !gSelfSign)
{
fprintf(stderr, "Please specify a CA certificate to be used to sign the new certificate (using\n"
"the --ca-cert option) or --self to generate a self-signed certificate.\n");
ExitNow(res = false);
}
else if (gCACertFileName != NULL && gSelfSign)
{
fprintf(stderr, "Please specify only one of --ca-cert and --self.\n");
ExitNow(res = false);
}
if (gCAKeyFileName == NULL)
{
fprintf(stderr, "Please specify the CA key file name using the --ca-key option.\n");
ExitNow(res = false);
}
if (gSigHashAlgo == NULL)
{
fprintf(stderr, "Please specify a signature hash algorithm using either the --sha1 or --sha256 options.\n");
ExitNow(res = false);
}
if (access(gOutCertFileName, R_OK) == 0)
{
fprintf(stderr, "weave: ERROR: Output certificate file already exists (%s)\n"
"To replace the file, please remove it and re-run the command.\n",
gOutCertFileName);
ExitNow(res = false);
}
outCertFile = fopen(gOutCertFileName, "w+");
if (outCertFile == NULL)
{
fprintf(stderr, "weave: ERROR: Unable to create output certificate file (%s)\n"
"%s.\n",
gOutCertFileName,
strerror(errno));
ExitNow(res = false);
}
outCertFileCreated = true;
if (!InitOpenSSL())
ExitNow(res = false);
if (!ReadCert(gInCertFileName, cert, inCertFmt))
ExitNow(res = false);
if (!gSelfSign)
{
if (!ReadCertPEM(gCACertFileName, caCert))
ExitNow(res = false);
}
else
caCert = cert;
if (!ReadPrivateKey(gCAKeyFileName, "Enter password for private key:", caKey))
ExitNow(res = false);
if (!ResignCert(cert, caCert, caKey, gSigHashAlgo))
ExitNow(res = false);
if (!WriteCert(cert, outCertFile, gOutCertFileName, inCertFmt))
ExitNow(res = false);
res = (fclose(outCertFile) != EOF);
outCertFile = NULL;
if (!res)
{
fprintf(stderr, "weave: ERROR: Unable to write output certificate file (%s)\n"
"%s.\n",
gOutCertFileName,
strerror(errno));
ExitNow();
}
exit:
if (cert != NULL)
X509_free(cert);
if (caCert != NULL && caCert != cert)
X509_free(caCert);
if (caKey != NULL)
EVP_PKEY_free(caKey);
if (outCertFile != NULL)
fclose(outCertFile);
if (gOutCertFileName != NULL && outCertFileCreated && !res)
unlink(gOutCertFileName);
return res;
}
bool HandleOption(const char *progName, OptionSet *optSet, int id, const char *name, const char *arg)
{
switch (id)
{
case 'c':
gInCertFileName = arg;
break;
case 'o':
gOutCertFileName = arg;
break;
case 'C':
gCACertFileName = arg;
break;
case 'K':
gCAKeyFileName = arg;
break;
case 's':
gSelfSign = true;
break;
case '1':
gSigHashAlgo = EVP_sha1();
break;
case '2':
gSigHashAlgo = EVP_sha256();
break;
default:
PrintArgError("%s: INTERNAL ERROR: Unhandled option: %s\n", progName, name);
return false;
}
return true;
}