* This file is part of the openHiTLS project.
*
* openHiTLS is licensed under the Mulan PSL v2.
* You can use this software according to the terms and conditions of the Mulan PSL v2.
* You may obtain a copy of Mulan PSL v2 at:
*
* http://license.coscl.org.cn/MulanPSL2
*
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
* See the Mulan PSL v2 for more details.
*/
#include "app_crl.h"
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
#include <limits.h>
#include "bsl_list.h"
#include "bsl_print.h"
#include "bsl_sal.h"
#include "bsl_types.h"
#include "hitls_pki_errno.h"
#include "crypt_eal_pkey.h"
#include "crypt_eal_rand.h"
#include "app_opt.h"
#include "app_errno.h"
#include "app_print.h"
#include "app_conf.h"
#include "app_utils.h"
#define MAX_CRLFILE_SIZE (256 * 1024)
#define DEFAULT_CERT_SIZE 1024U
typedef enum OptionChoice {
HITLS_APP_OPT_CRL_ERR = -1,
HITLS_APP_OPT_CRL_EOF = 0,
HITLS_APP_OPT_CRL_HELP = 1,
HITLS_APP_OPT_CRL_IN,
HITLS_APP_OPT_CRL_NOOUT,
HITLS_APP_OPT_CRL_OUT,
HITLS_APP_OPT_CRL_NEXTUPDATE,
HITLS_APP_OPT_CRL_CAFILE,
HITLS_APP_OPT_CRL_INFORM,
HITLS_APP_OPT_CRL_OUTFORM,
HITLS_APP_OPT_CRL_ISSUER,
HITLS_APP_OPT_CRL_HASH,
HITLS_APP_OPT_CRL_TEXT,
} HITLSOptType;
static const HITLS_CmdOption g_crlOpts[] = {
{"help", HITLS_APP_OPT_CRL_HELP, HITLS_APP_OPT_VALUETYPE_NO_VALUE, "Display this function summary"},
{"in", HITLS_APP_OPT_CRL_IN, HITLS_APP_OPT_VALUETYPE_IN_FILE, "Input file"},
{"noout", HITLS_APP_OPT_CRL_NOOUT, HITLS_APP_OPT_VALUETYPE_NO_VALUE, "No CRL output "},
{"out", HITLS_APP_OPT_CRL_OUT, HITLS_APP_OPT_VALUETYPE_OUT_FILE, "Output file"},
{"nextupdate", HITLS_APP_OPT_CRL_NEXTUPDATE, HITLS_APP_OPT_VALUETYPE_NO_VALUE, "Print CRL nextupdate"},
{"CAfile", HITLS_APP_OPT_CRL_CAFILE, HITLS_APP_OPT_VALUETYPE_IN_FILE, "Verify CRL using CAFile"},
{"inform", HITLS_APP_OPT_CRL_INFORM, HITLS_APP_OPT_VALUETYPE_FMT_PEMDER, "Input crl file format"},
{"outform", HITLS_APP_OPT_CRL_OUTFORM, HITLS_APP_OPT_VALUETYPE_FMT_PEMDER, "Output crl file format"},
{"issuer", HITLS_APP_OPT_CRL_ISSUER, HITLS_APP_OPT_VALUETYPE_NO_VALUE, "Print issuer DN"},
{"hash", HITLS_APP_OPT_CRL_HASH, HITLS_APP_OPT_VALUETYPE_NO_VALUE, "Print issuer DN hash"},
{"text", HITLS_APP_OPT_CRL_TEXT, HITLS_APP_OPT_VALUETYPE_NO_VALUE, "Print CRL in text"},
{NULL, 0, 0, NULL}
};
typedef struct {
BSL_ParseFormat inform;
BSL_ParseFormat outform;
char *infile;
char *cafile;
char *outfile;
bool noout;
bool nextupdate;
bool issuer;
bool hash;
bool text;
BSL_UIO *uio;
} CrlInfo;
static int32_t DecodeCertFile(uint8_t *infileBuf, uint64_t infileBufLen, HITLS_X509_Cert **tmp)
{
uint32_t bufLen = (uint32_t)infileBufLen;
if ((uint64_t)bufLen != infileBufLen) {
return HITLS_APP_DECODE_FAIL;
}
BSL_Buffer encode = {infileBuf, bufLen};
return HITLS_X509_CertParseBuff(BSL_FORMAT_UNKNOWN, &encode, tmp);
}
static int32_t VerifyCrlFile(const char *caFile, const HITLS_X509_Crl *crl)
{
BSL_UIO *readUio = HITLS_APP_UioOpen(caFile, 'r', caFile != NULL ? 1 : 0);
if (readUio == NULL) {
AppPrintError("Failed to open the file <%s>, No such file or directory\n", caFile);
return HITLS_APP_UIO_FAIL;
}
uint8_t *caFileBuf = NULL;
uint64_t caFileBufLen = 0;
int32_t ret = HITLS_APP_OptReadUio(readUio, &caFileBuf, &caFileBufLen, MAX_CRLFILE_SIZE);
BSL_UIO_Free(readUio);
if (ret != HITLS_APP_SUCCESS || caFileBuf == NULL || caFileBufLen == 0) {
BSL_SAL_FREE(caFileBuf);
AppPrintError("Failed to read CAfile from <%s>\n", caFile);
return HITLS_APP_UIO_FAIL;
}
HITLS_X509_Cert *cert = NULL;
ret = DecodeCertFile(caFileBuf, caFileBufLen, &cert);
BSL_SAL_FREE(caFileBuf);
if (ret != HITLS_APP_SUCCESS) {
HITLS_X509_CertFree(cert);
AppPrintError("Failed to decode the CAfile <%s>\n", caFile);
return HITLS_APP_DECODE_FAIL;
}
CRYPT_EAL_PkeyCtx *pubKey = NULL;
ret = HITLS_X509_CertCtrl(cert, HITLS_X509_GET_PUBKEY, &pubKey, sizeof(CRYPT_EAL_PkeyCtx *));
HITLS_X509_CertFree(cert);
if (pubKey == NULL) {
AppPrintError("Failed to getting CRL issuer certificate\n");
return HITLS_APP_X509_FAIL;
}
ret = HITLS_X509_CrlVerify(pubKey, crl);
CRYPT_EAL_PkeyFreeCtx((CRYPT_EAL_PkeyCtx *)pubKey);
if (ret != HITLS_PKI_SUCCESS) {
AppPrintError("The verification result: failed\n");
return HITLS_APP_CERT_VERIFY_FAIL;
}
AppPrintError("The verification result: OK\n");
return HITLS_APP_SUCCESS;
}
static int32_t OutCrlFileInfo(BSL_UIO *uio, HITLS_X509_Crl *crl, uint32_t format)
{
BSL_Buffer encode = {0};
int32_t ret = HITLS_X509_CrlGenBuff(format, crl, &encode);
if (ret != HITLS_PKI_SUCCESS) {
AppPrintError("Failed to convert the CRL.\n");
return HITLS_APP_ENCODE_FAIL;
}
ret = HITLS_APP_OptWriteUio(uio, encode.data, encode.dataLen, HITLS_APP_FORMAT_PEM);
BSL_SAL_FREE(encode.data);
if (ret != HITLS_APP_SUCCESS) {
AppPrintError("Failed to print the CRL content\n");
}
return ret;
}
static int32_t PrintNextUpdate(BSL_UIO *uio, HITLS_X509_Crl *crl)
{
BSL_TIME time = {0};
int32_t ret = HITLS_X509_CrlCtrl(crl, HITLS_X509_GET_AFTER_TIME, &time, sizeof(BSL_TIME));
if (ret != HITLS_PKI_SUCCESS && ret != HITLS_X509_ERR_CRL_NEXTUPDATE_UNEXIST) {
AppPrintError("Failed to get character string\n");
return HITLS_APP_X509_FAIL;
}
ret = HITLS_PKI_PrintCtrl(HITLS_PKI_PRINT_NEXTUPDATE, &time, sizeof(BSL_TIME), uio);
if (ret != HITLS_PKI_SUCCESS) {
AppPrintError("Failed to get print string\n");
return HITLS_APP_X509_FAIL;
}
return HITLS_APP_SUCCESS;
}
static int32_t PrintIssuer(BSL_UIO *uio, HITLS_X509_Crl *crl)
{
BslList *issuer = NULL;
int32_t ret = HITLS_X509_CrlCtrl(crl, HITLS_X509_GET_ISSUER_DN, &issuer, sizeof(BslList *));
if (ret != HITLS_PKI_SUCCESS) {
AppPrintError("Failed to get CRL issuer name, errCode=%d.\n", ret);
return HITLS_APP_X509_FAIL;
}
ret = BSL_PRINT_Fmt(0, uio, "Issuer=");
if (ret != 0) {
AppPrintError("Failed to print CRL issuer name, errCode=%d.\n", ret);
return HITLS_APP_BSL_FAIL;
}
ret = HITLS_PKI_PrintCtrl(HITLS_PKI_PRINT_DNNAME, issuer, sizeof(BslList), uio);
if (ret != HITLS_PKI_SUCCESS) {
AppPrintError("Failed to print CRL issuer, errCode=%d.\n", ret);
return HITLS_APP_X509_FAIL;
}
return HITLS_APP_SUCCESS;
}
static int32_t PrintIssuerHash(BSL_UIO *uio, HITLS_X509_Crl *crl)
{
BslList *issuer = NULL;
int32_t ret = HITLS_X509_CrlCtrl(crl, HITLS_X509_GET_ISSUER_DN, &issuer, sizeof(BslList *));
if (ret != HITLS_PKI_SUCCESS) {
AppPrintError("Failed to get CRL issuer name for hash, errCode=%d.\n", ret);
return HITLS_APP_X509_FAIL;
}
ret = BSL_PRINT_Fmt(0, uio, "Issuer Hash=");
if (ret != 0) {
AppPrintError("Failed to print CRL issuer hash prefix, errCode=%d.\n", ret);
return HITLS_APP_BSL_FAIL;
}
ret = HITLS_PKI_PrintCtrl(HITLS_PKI_PRINT_DNNAME_HASH, issuer, sizeof(BslList), uio);
if (ret != HITLS_PKI_SUCCESS) {
AppPrintError("Failed to print CRL issuer hash, errCode=%d.\n", ret);
return HITLS_APP_X509_FAIL;
}
return HITLS_APP_SUCCESS;
}
static int32_t PrintText(BSL_UIO *uio, HITLS_X509_Crl *crl)
{
int32_t ret = HITLS_PKI_PrintCtrl(HITLS_PKI_PRINT_CRL, crl, sizeof(HITLS_X509_Crl *), uio);
if (ret != HITLS_PKI_SUCCESS) {
AppPrintError("Failed to print CRL text, errCode=%d.\n", ret);
return HITLS_APP_X509_FAIL;
}
return HITLS_APP_SUCCESS;
}
static int32_t OptParse(CrlInfo *outInfo)
{
HITLSOptType optType;
int ret = HITLS_APP_SUCCESS;
while ((optType = HITLS_APP_OptNext()) != HITLS_APP_OPT_CRL_EOF) {
switch (optType) {
case HITLS_APP_OPT_CRL_EOF:
case HITLS_APP_OPT_CRL_ERR:
ret = HITLS_APP_OPT_UNKOWN;
AppPrintError("crl: Use -help for summary.\n");
return ret;
case HITLS_APP_OPT_CRL_HELP:
ret = HITLS_APP_HELP;
(void)HITLS_APP_OptHelpPrint(g_crlOpts);
return ret;
case HITLS_APP_OPT_CRL_OUT:
outInfo->outfile = HITLS_APP_OptGetValueStr();
if (outInfo->outfile == NULL || strlen(outInfo->outfile) >= PATH_MAX) {
AppPrintError("The length of outfile error, range is (0, 4096).\n");
return HITLS_APP_OPT_VALUE_INVALID;
}
break;
case HITLS_APP_OPT_CRL_NOOUT:
outInfo->noout = true;
break;
case HITLS_APP_OPT_CRL_IN:
outInfo->infile = HITLS_APP_OptGetValueStr();
if (outInfo->infile == NULL || strlen(outInfo->infile) >= PATH_MAX) {
AppPrintError("The length of input file error, range is (0, 4096).\n");
return HITLS_APP_OPT_VALUE_INVALID;
}
break;
case HITLS_APP_OPT_CRL_CAFILE:
outInfo->cafile = HITLS_APP_OptGetValueStr();
if (outInfo->cafile == NULL || strlen(outInfo->cafile) >= PATH_MAX) {
AppPrintError("The length of CA file error, range is (0, 4096).\n");
return HITLS_APP_OPT_VALUE_INVALID;
}
break;
case HITLS_APP_OPT_CRL_NEXTUPDATE:
outInfo->nextupdate = true;
break;
case HITLS_APP_OPT_CRL_INFORM:
if (HITLS_APP_OptGetFormatType(HITLS_APP_OptGetValueStr(), HITLS_APP_OPT_VALUETYPE_FMT_PEMDER,
&outInfo->inform) != HITLS_APP_SUCCESS) {
AppPrintError("The informat of crl file error.\n");
return HITLS_APP_OPT_VALUE_INVALID;
}
break;
case HITLS_APP_OPT_CRL_OUTFORM:
if (HITLS_APP_OptGetFormatType(HITLS_APP_OptGetValueStr(), HITLS_APP_OPT_VALUETYPE_FMT_PEMDER,
&outInfo->outform) != HITLS_APP_SUCCESS) {
AppPrintError("The format of crl file error.\n");
return HITLS_APP_OPT_VALUE_INVALID;
}
break;
case HITLS_APP_OPT_CRL_ISSUER:
outInfo->issuer = true;
break;
case HITLS_APP_OPT_CRL_HASH:
outInfo->hash = true;
break;
case HITLS_APP_OPT_CRL_TEXT:
outInfo->text = true;
break;
default:
return HITLS_APP_OPT_UNKOWN;
}
}
return HITLS_APP_SUCCESS;
}
int32_t HITLS_CrlMain(int argc, char *argv[])
{
CrlInfo crlInfo = {0, BSL_FORMAT_PEM, NULL, NULL, NULL, false, false, false, false, false, NULL};
HITLS_X509_Crl *crl = NULL;
int32_t mainRet = HITLS_APP_OptBegin(argc, argv, g_crlOpts);
if (mainRet != HITLS_APP_SUCCESS) {
AppPrintError("error in opt begin.\n");
goto end;
}
mainRet = OptParse(&crlInfo);
if (mainRet != HITLS_APP_SUCCESS) {
goto end;
}
int unParseParamNum = HITLS_APP_GetRestOptNum();
if (unParseParamNum != 0) {
AppPrintError("Extra arguments given.\n");
AppPrintError("crl: Use -help for summary.\n");
mainRet = HITLS_APP_OPT_UNKOWN;
goto end;
}
crl = HITLS_APP_LoadCrl(crlInfo.infile, crlInfo.inform);
if (crl == NULL) {
AppPrintError("Failed to load CRL.\n");
mainRet = HITLS_APP_DECODE_FAIL;
goto end;
}
crlInfo.uio = HITLS_APP_UioOpen(crlInfo.outfile, 'w', crlInfo.outfile != NULL ? 1 : 0);
if (crlInfo.uio == NULL) {
AppPrintError("Failed to open the standard output.");
mainRet = HITLS_APP_UIO_FAIL;
goto end;
}
if (crlInfo.nextupdate) {
mainRet = PrintNextUpdate(crlInfo.uio, crl);
if (mainRet != HITLS_APP_SUCCESS) {
goto end;
}
}
if (crlInfo.cafile != NULL) {
mainRet = VerifyCrlFile(crlInfo.cafile, crl);
if (mainRet != HITLS_APP_SUCCESS) {
goto end;
}
}
if (crlInfo.issuer) {
mainRet = PrintIssuer(crlInfo.uio, crl);
if (mainRet != HITLS_APP_SUCCESS) {
goto end;
}
}
if (crlInfo.hash) {
mainRet = PrintIssuerHash(crlInfo.uio, crl);
if (mainRet != HITLS_APP_SUCCESS) {
goto end;
}
}
if (crlInfo.text) {
mainRet = PrintText(crlInfo.uio, crl);
if (mainRet != HITLS_APP_SUCCESS) {
goto end;
}
}
if (!crlInfo.noout) {
mainRet = OutCrlFileInfo(crlInfo.uio, crl, crlInfo.outform);
}
end:
HITLS_X509_CrlFree(crl);
BSL_UIO_Free(crlInfo.uio);
HITLS_APP_OptEnd();
return mainRet;
}