1*1c25dff2Stb /* $OpenBSD: ts.c,v 1.29 2024/08/26 18:40:50 tb Exp $ */ 2dab3f910Sjsing /* Written by Zoltan Glozik (zglozik@stones.com) for the OpenSSL 3dab3f910Sjsing * project 2002. 4dab3f910Sjsing */ 5dab3f910Sjsing /* ==================================================================== 6dab3f910Sjsing * Copyright (c) 2001 The OpenSSL Project. All rights reserved. 7dab3f910Sjsing * 8dab3f910Sjsing * Redistribution and use in source and binary forms, with or without 9dab3f910Sjsing * modification, are permitted provided that the following conditions 10dab3f910Sjsing * are met: 11dab3f910Sjsing * 12dab3f910Sjsing * 1. Redistributions of source code must retain the above copyright 13dab3f910Sjsing * notice, this list of conditions and the following disclaimer. 14dab3f910Sjsing * 15dab3f910Sjsing * 2. Redistributions in binary form must reproduce the above copyright 16dab3f910Sjsing * notice, this list of conditions and the following disclaimer in 17dab3f910Sjsing * the documentation and/or other materials provided with the 18dab3f910Sjsing * distribution. 19dab3f910Sjsing * 20dab3f910Sjsing * 3. All advertising materials mentioning features or use of this 21dab3f910Sjsing * software must display the following acknowledgment: 22dab3f910Sjsing * "This product includes software developed by the OpenSSL Project 23dab3f910Sjsing * for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)" 24dab3f910Sjsing * 25dab3f910Sjsing * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to 26dab3f910Sjsing * endorse or promote products derived from this software without 27dab3f910Sjsing * prior written permission. For written permission, please contact 28dab3f910Sjsing * licensing@OpenSSL.org. 29dab3f910Sjsing * 30dab3f910Sjsing * 5. Products derived from this software may not be called "OpenSSL" 31dab3f910Sjsing * nor may "OpenSSL" appear in their names without prior written 32dab3f910Sjsing * permission of the OpenSSL Project. 33dab3f910Sjsing * 34dab3f910Sjsing * 6. Redistributions of any form whatsoever must retain the following 35dab3f910Sjsing * acknowledgment: 36dab3f910Sjsing * "This product includes software developed by the OpenSSL Project 37dab3f910Sjsing * for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)" 38dab3f910Sjsing * 39dab3f910Sjsing * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY 40dab3f910Sjsing * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 41dab3f910Sjsing * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 42dab3f910Sjsing * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OpenSSL PROJECT OR 43dab3f910Sjsing * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 44dab3f910Sjsing * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 45dab3f910Sjsing * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 46dab3f910Sjsing * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 47dab3f910Sjsing * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 48dab3f910Sjsing * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 49dab3f910Sjsing * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 50dab3f910Sjsing * OF THE POSSIBILITY OF SUCH DAMAGE. 51dab3f910Sjsing * ==================================================================== 52dab3f910Sjsing * 53dab3f910Sjsing * This product includes cryptographic software written by Eric Young 54dab3f910Sjsing * (eay@cryptsoft.com). This product includes software written by Tim 55dab3f910Sjsing * Hudson (tjh@cryptsoft.com). 56dab3f910Sjsing * 57dab3f910Sjsing */ 58dab3f910Sjsing 59dab3f910Sjsing #include <stdio.h> 60dab3f910Sjsing #include <stdlib.h> 61dab3f910Sjsing #include <string.h> 62dab3f910Sjsing 63dab3f910Sjsing #include "apps.h" 64dab3f910Sjsing 65dab3f910Sjsing #include <openssl/bio.h> 66dab3f910Sjsing #include <openssl/bn.h> 67dab3f910Sjsing #include <openssl/err.h> 68dab3f910Sjsing #include <openssl/pem.h> 69dab3f910Sjsing #include <openssl/ts.h> 70dab3f910Sjsing 71dab3f910Sjsing /* Length of the nonce of the request in bits (must be a multiple of 8). */ 72dab3f910Sjsing #define NONCE_LENGTH 64 73dab3f910Sjsing 74dab3f910Sjsing /* Macro definitions for the configuration file. */ 75dab3f910Sjsing #define ENV_OID_FILE "oid_file" 76dab3f910Sjsing 77dab3f910Sjsing /* Local function declarations. */ 78dab3f910Sjsing 79dab3f910Sjsing static ASN1_OBJECT *txt2obj(const char *oid); 80dab3f910Sjsing static CONF *load_config_file(const char *configfile); 81dab3f910Sjsing 82dab3f910Sjsing /* Query related functions. */ 83e04fe004Sinoguchi static int query_command(const char *data, char *digest, const EVP_MD *md, 84e04fe004Sinoguchi const char *policy, int no_nonce, int cert, const char *in, const char *out, 85e04fe004Sinoguchi int text); 86dab3f910Sjsing static BIO *BIO_open_with_default(const char *file, const char *mode, 87dab3f910Sjsing FILE *default_fp); 88dab3f910Sjsing static TS_REQ *create_query(BIO *data_bio, char *digest, const EVP_MD *md, 89dab3f910Sjsing const char *policy, int no_nonce, int cert); 90e04fe004Sinoguchi static int create_digest(BIO *input, char *digest, const EVP_MD *md, 91e04fe004Sinoguchi unsigned char **md_value); 92dab3f910Sjsing static ASN1_INTEGER *create_nonce(int bits); 93dab3f910Sjsing 94dab3f910Sjsing /* Reply related functions. */ 95e04fe004Sinoguchi static int reply_command(CONF *conf, char *section, char *queryfile, 96e04fe004Sinoguchi char *passin, char *inkey, char *signer, char *chain, const char *policy, 97e04fe004Sinoguchi char *in, int token_in, char *out, int token_out, int text); 98dab3f910Sjsing static TS_RESP *read_PKCS7(BIO *in_bio); 995284dfeaSbcook static TS_RESP *create_response(CONF *conf, const char *section, 100e04fe004Sinoguchi char *queryfile, char *passin, char *inkey, char *signer, char *chain, 101e04fe004Sinoguchi const char *policy); 102dab3f910Sjsing static ASN1_INTEGER *serial_cb(TS_RESP_CTX *ctx, void *data); 103dab3f910Sjsing static ASN1_INTEGER *next_serial(const char *serialfile); 104dab3f910Sjsing static int save_ts_serial(const char *serialfile, ASN1_INTEGER *serial); 105dab3f910Sjsing 106dab3f910Sjsing /* Verify related functions. */ 107e04fe004Sinoguchi static int verify_command(char *data, char *digest, char *queryfile, char *in, 108e04fe004Sinoguchi int token_in, char *ca_path, char *ca_file, char *untrusted); 109dab3f910Sjsing static TS_VERIFY_CTX *create_verify_ctx(char *data, char *digest, 110e04fe004Sinoguchi char *queryfile, char *ca_path, char *ca_file, char *untrusted); 111dab3f910Sjsing static X509_STORE *create_cert_store(char *ca_path, char *ca_file); 112dab3f910Sjsing static int verify_cb(int ok, X509_STORE_CTX *ctx); 113dab3f910Sjsing 114d572171bSinoguchi enum mode { 115d572171bSinoguchi CMD_NONE, CMD_QUERY, CMD_REPLY, CMD_VERIFY 116d572171bSinoguchi }; 117d572171bSinoguchi 118d572171bSinoguchi static struct { 119d572171bSinoguchi char *ca_file; 120d572171bSinoguchi char *ca_path; 121d572171bSinoguchi int cert; 122d572171bSinoguchi char *chain; 123d572171bSinoguchi char *configfile; 124d572171bSinoguchi char *data; 125d572171bSinoguchi char *digest; 126d572171bSinoguchi char *in; 127d572171bSinoguchi char *inkey; 128d572171bSinoguchi const EVP_MD *md; 129d572171bSinoguchi int mode; 130d572171bSinoguchi int no_nonce; 131d572171bSinoguchi char *out; 132d572171bSinoguchi char *passin; 133d572171bSinoguchi char *policy; 134d572171bSinoguchi char *queryfile; 135d572171bSinoguchi char *section; 136d572171bSinoguchi char *signer; 137d572171bSinoguchi int text; 138d572171bSinoguchi int token_in; 139d572171bSinoguchi int token_out; 140d572171bSinoguchi char *untrusted; 141e7718adaStb } cfg; 142d572171bSinoguchi 143d572171bSinoguchi static int 144d572171bSinoguchi ts_opt_md(int argc, char **argv, int *argsused) 145d572171bSinoguchi { 146d572171bSinoguchi char *name = argv[0]; 147d572171bSinoguchi 148d572171bSinoguchi if (*name++ != '-') 149d572171bSinoguchi return (1); 150d572171bSinoguchi 151e7718adaStb if ((cfg.md = EVP_get_digestbyname(name)) == NULL) 152d572171bSinoguchi return (1); 153d572171bSinoguchi 154d572171bSinoguchi *argsused = 1; 155d572171bSinoguchi return (0); 156d572171bSinoguchi } 157d572171bSinoguchi 158d572171bSinoguchi static int 159d572171bSinoguchi ts_opt_query(void) 160d572171bSinoguchi { 161e7718adaStb if (cfg.mode != CMD_NONE) 162d572171bSinoguchi return (1); 163e7718adaStb cfg.mode = CMD_QUERY; 164d572171bSinoguchi return (0); 165d572171bSinoguchi } 166d572171bSinoguchi 167d572171bSinoguchi static int 168d572171bSinoguchi ts_opt_reply(void) 169d572171bSinoguchi { 170e7718adaStb if (cfg.mode != CMD_NONE) 171d572171bSinoguchi return (1); 172e7718adaStb cfg.mode = CMD_REPLY; 173d572171bSinoguchi return (0); 174d572171bSinoguchi } 175d572171bSinoguchi 176d572171bSinoguchi static int 177d572171bSinoguchi ts_opt_verify(void) 178d572171bSinoguchi { 179e7718adaStb if (cfg.mode != CMD_NONE) 180d572171bSinoguchi return (1); 181e7718adaStb cfg.mode = CMD_VERIFY; 182d572171bSinoguchi return (0); 183d572171bSinoguchi } 184d572171bSinoguchi 185d572171bSinoguchi static const struct option ts_options[] = { 186d572171bSinoguchi { 187d572171bSinoguchi .name = "CAfile", 188d572171bSinoguchi .argname = "file", 189d572171bSinoguchi .desc = "Certificate Authority file", 190d572171bSinoguchi .type = OPTION_ARG, 191e7718adaStb .opt.arg = &cfg.ca_file, 192d572171bSinoguchi }, 193d572171bSinoguchi { 194d572171bSinoguchi .name = "CApath", 195d572171bSinoguchi .argname = "path", 196d572171bSinoguchi .desc = "Certificate Authority path", 197d572171bSinoguchi .type = OPTION_ARG, 198e7718adaStb .opt.arg = &cfg.ca_path, 199d572171bSinoguchi }, 200d572171bSinoguchi { 201d572171bSinoguchi .name = "cert", 202d572171bSinoguchi .desc = "Include signing certificate in the response", 203d572171bSinoguchi .type = OPTION_FLAG, 204e7718adaStb .opt.flag = &cfg.cert, 205d572171bSinoguchi }, 206d572171bSinoguchi { 207d572171bSinoguchi .name = "chain", 208d572171bSinoguchi .argname = "file", 209d572171bSinoguchi .desc = "PEM certificates that will be included in the response", 210d572171bSinoguchi .type = OPTION_ARG, 211e7718adaStb .opt.arg = &cfg.chain, 212d572171bSinoguchi }, 213d572171bSinoguchi { 214d572171bSinoguchi .name = "config", 215d572171bSinoguchi .argname = "file", 216d572171bSinoguchi .desc = "Specify an alternative configuration file", 217d572171bSinoguchi .type = OPTION_ARG, 218e7718adaStb .opt.arg = &cfg.configfile, 219d572171bSinoguchi }, 220d572171bSinoguchi { 221d572171bSinoguchi .name = "data", 222d572171bSinoguchi .argname = "file", 223d572171bSinoguchi .desc = "Data file for which the time stamp request needs to be created", 224d572171bSinoguchi .type = OPTION_ARG, 225e7718adaStb .opt.arg = &cfg.data, 226d572171bSinoguchi }, 227d572171bSinoguchi { 228d572171bSinoguchi .name = "digest", 229d572171bSinoguchi .argname = "arg", 230d572171bSinoguchi .desc = "Specify the message imprint explicitly without the data file", 231d572171bSinoguchi .type = OPTION_ARG, 232e7718adaStb .opt.arg = &cfg.digest, 233d572171bSinoguchi }, 234d572171bSinoguchi { 235d572171bSinoguchi .name = "in", 236d572171bSinoguchi .argname = "file", 237d572171bSinoguchi .desc = "Input file", 238d572171bSinoguchi .type = OPTION_ARG, 239e7718adaStb .opt.arg = &cfg.in, 240d572171bSinoguchi }, 241d572171bSinoguchi { 242d572171bSinoguchi .name = "inkey", 243d572171bSinoguchi .argname = "file", 244d572171bSinoguchi .desc = "Input key file", 245d572171bSinoguchi .type = OPTION_ARG, 246e7718adaStb .opt.arg = &cfg.inkey, 247d572171bSinoguchi }, 248d572171bSinoguchi { 249d572171bSinoguchi .name = "no_nonce", 250d572171bSinoguchi .desc = "Specify no nonce in the request", 251d572171bSinoguchi .type = OPTION_FLAG, 252e7718adaStb .opt.flag = &cfg.no_nonce, 253d572171bSinoguchi }, 254d572171bSinoguchi { 255d572171bSinoguchi .name = "out", 256d572171bSinoguchi .argname = "file", 257d572171bSinoguchi .desc = "Output file", 258d572171bSinoguchi .type = OPTION_ARG, 259e7718adaStb .opt.arg = &cfg.out, 260d572171bSinoguchi }, 261d572171bSinoguchi { 262d572171bSinoguchi .name = "passin", 263d572171bSinoguchi .argname = "src", 264d572171bSinoguchi .desc = "Private key password source", 265d572171bSinoguchi .type = OPTION_ARG, 266e7718adaStb .opt.arg = &cfg.passin, 267d572171bSinoguchi }, 268d572171bSinoguchi { 269d572171bSinoguchi .name = "policy", 270d572171bSinoguchi .argname = "object_id", 271d572171bSinoguchi .desc = "Policy for the TSA to use when creating the time stamp token", 272d572171bSinoguchi .type = OPTION_ARG, 273e7718adaStb .opt.arg = &cfg.policy, 274d572171bSinoguchi }, 275d572171bSinoguchi { 276d572171bSinoguchi .name = "query", 277d572171bSinoguchi .desc = "Create and print a time stamp request", 278d572171bSinoguchi .type = OPTION_FUNC, 279d572171bSinoguchi .opt.func = ts_opt_query, 280d572171bSinoguchi }, 281d572171bSinoguchi { 282d572171bSinoguchi .name = "queryfile", 283d572171bSinoguchi .argname = "file", 284d572171bSinoguchi .desc = "File containing a DER-encoded time stamp request", 285d572171bSinoguchi .type = OPTION_ARG, 286e7718adaStb .opt.arg = &cfg.queryfile, 287d572171bSinoguchi }, 288d572171bSinoguchi { 289d572171bSinoguchi .name = "reply", 290d572171bSinoguchi .desc = "Create a time stamp response", 291d572171bSinoguchi .type = OPTION_FUNC, 292d572171bSinoguchi .opt.func = ts_opt_reply, 293d572171bSinoguchi }, 294d572171bSinoguchi { 295d572171bSinoguchi .name = "section", 296d572171bSinoguchi .argname = "arg", 297d572171bSinoguchi .desc = "TSA section containing the settings for response generation", 298d572171bSinoguchi .type = OPTION_ARG, 299e7718adaStb .opt.arg = &cfg.section, 300d572171bSinoguchi }, 301d572171bSinoguchi { 302d572171bSinoguchi .name = "signer", 303d572171bSinoguchi .argname = "file", 304d572171bSinoguchi .desc = "Signer certificate file", 305d572171bSinoguchi .type = OPTION_ARG, 306e7718adaStb .opt.arg = &cfg.signer, 307d572171bSinoguchi }, 308d572171bSinoguchi { 309d572171bSinoguchi .name = "text", 310d572171bSinoguchi .desc = "Output in human-readable text format", 311d572171bSinoguchi .type = OPTION_FLAG, 312e7718adaStb .opt.flag = &cfg.text, 313d572171bSinoguchi }, 314d572171bSinoguchi { 315d572171bSinoguchi .name = "token_in", 316d572171bSinoguchi .desc = "Input is a DER-encoded time stamp token", 317d572171bSinoguchi .type = OPTION_FLAG, 318e7718adaStb .opt.flag = &cfg.token_in, 319d572171bSinoguchi }, 320d572171bSinoguchi { 321d572171bSinoguchi .name = "token_out", 322d572171bSinoguchi .desc = "Output is a DER-encoded time stamp token", 323d572171bSinoguchi .type = OPTION_FLAG, 324e7718adaStb .opt.flag = &cfg.token_out, 325d572171bSinoguchi }, 326d572171bSinoguchi { 327d572171bSinoguchi .name = "untrusted", 328d572171bSinoguchi .argname = "file", 329d572171bSinoguchi .desc = "File containing untrusted certificates", 330d572171bSinoguchi .type = OPTION_ARG, 331e7718adaStb .opt.arg = &cfg.untrusted, 332d572171bSinoguchi }, 333d572171bSinoguchi { 334d572171bSinoguchi .name = "verify", 335d572171bSinoguchi .desc = "Verify a time stamp response", 336d572171bSinoguchi .type = OPTION_FUNC, 337d572171bSinoguchi .opt.func = ts_opt_verify, 338d572171bSinoguchi }, 339d572171bSinoguchi { 340d572171bSinoguchi .name = NULL, 341d572171bSinoguchi .desc = "", 342d572171bSinoguchi .type = OPTION_ARGV_FUNC, 343d572171bSinoguchi .opt.argvfunc = ts_opt_md, 344d572171bSinoguchi }, 345d572171bSinoguchi { NULL }, 346d572171bSinoguchi }; 347d572171bSinoguchi 348d572171bSinoguchi static void 349d572171bSinoguchi ts_usage(void) 350d572171bSinoguchi { 351d572171bSinoguchi fprintf(stderr, "usage:\n" 352d572171bSinoguchi "ts -query [-md4 | -md5 | -ripemd160 | -sha1] [-cert]\n" 353d572171bSinoguchi " [-config configfile] [-data file_to_hash]\n" 354d572171bSinoguchi " [-digest digest_bytes] [-in request.tsq] [-no_nonce]\n" 355d572171bSinoguchi " [-out request.tsq] [-policy object_id] [-text]\n"); 356d572171bSinoguchi fprintf(stderr, "\n" 357d572171bSinoguchi "ts -reply [-chain certs_file.pem] [-config configfile]\n" 358d572171bSinoguchi " [-in response.tsr] [-inkey private.pem] [-out response.tsr]\n" 359d572171bSinoguchi " [-passin arg] [-policy object_id] [-queryfile request.tsq]\n" 360d572171bSinoguchi " [-section tsa_section] [-signer tsa_cert.pem] [-text]\n" 361d572171bSinoguchi " [-token_in] [-token_out]\n"); 362d572171bSinoguchi fprintf(stderr, "\n" 363d572171bSinoguchi "ts -verify [-CAfile trusted_certs.pem]\n" 364d572171bSinoguchi " [-CApath trusted_cert_path] [-data file_to_hash]\n" 365d572171bSinoguchi " [-digest digest_bytes] [-in response.tsr]\n" 366d572171bSinoguchi " [-queryfile request.tsq] [-token_in]\n" 367d572171bSinoguchi " [-untrusted cert_file.pem]\n"); 368d572171bSinoguchi fprintf(stderr, "\n"); 369d572171bSinoguchi options_usage(ts_options); 370d572171bSinoguchi fprintf(stderr, "\n"); 371d572171bSinoguchi } 372d572171bSinoguchi 373dab3f910Sjsing int 374dab3f910Sjsing ts_main(int argc, char **argv) 375dab3f910Sjsing { 376dab3f910Sjsing int ret = 1; 377dab3f910Sjsing CONF *conf = NULL; 378dab3f910Sjsing char *password = NULL; /* Password itself. */ 379dab3f910Sjsing 38051811eadSderaadt if (pledge("stdio cpath wpath rpath tty", NULL) == -1) { 3819bc487adSdoug perror("pledge"); 382e370f0eeSdoug exit(1); 383e370f0eeSdoug } 3849bc487adSdoug 385e7718adaStb memset(&cfg, 0, sizeof(cfg)); 386e7718adaStb cfg.mode = CMD_NONE; 387d572171bSinoguchi 388d572171bSinoguchi if (options_parse(argc, argv, ts_options, NULL, NULL) != 0) 389dab3f910Sjsing goto usage; 390dab3f910Sjsing 391dab3f910Sjsing /* Get the password if required. */ 392e7718adaStb if (cfg.mode == CMD_REPLY && cfg.passin != NULL && 393e7718adaStb !app_passwd(bio_err, cfg.passin, NULL, &password, NULL)) { 394dab3f910Sjsing BIO_printf(bio_err, "Error getting password.\n"); 395dab3f910Sjsing goto cleanup; 396dab3f910Sjsing } 397dab3f910Sjsing /* 398dab3f910Sjsing * Check consistency of parameters and execute the appropriate 399dab3f910Sjsing * function. 400dab3f910Sjsing */ 401e7718adaStb switch (cfg.mode) { 402dab3f910Sjsing case CMD_NONE: 403dab3f910Sjsing goto usage; 404dab3f910Sjsing case CMD_QUERY: 405dab3f910Sjsing /* 406dab3f910Sjsing * Data file and message imprint cannot be specified at the 407dab3f910Sjsing * same time. 408dab3f910Sjsing */ 409e7718adaStb ret = cfg.data != NULL && cfg.digest != NULL; 410dab3f910Sjsing if (ret) 411dab3f910Sjsing goto usage; 412dab3f910Sjsing /* Load the config file for possible policy OIDs. */ 413e7718adaStb conf = load_config_file(cfg.configfile); 414e7718adaStb ret = !query_command(cfg.data, cfg.digest, 415e7718adaStb cfg.md, cfg.policy, cfg.no_nonce, 416e7718adaStb cfg.cert, cfg.in, cfg.out, 417e7718adaStb cfg.text); 418dab3f910Sjsing break; 419dab3f910Sjsing case CMD_REPLY: 420e7718adaStb conf = load_config_file(cfg.configfile); 421e7718adaStb if (cfg.in == NULL) { 422e7718adaStb ret = !(cfg.queryfile != NULL && conf != NULL && 423e7718adaStb !cfg.token_in); 424dab3f910Sjsing if (ret) 425dab3f910Sjsing goto usage; 426dab3f910Sjsing } else { 427dab3f910Sjsing /* 'in' and 'queryfile' are exclusive. */ 428e7718adaStb ret = !(cfg.queryfile == NULL); 429dab3f910Sjsing if (ret) 430dab3f910Sjsing goto usage; 431dab3f910Sjsing } 432dab3f910Sjsing 433e7718adaStb ret = !reply_command(conf, cfg.section, 434e7718adaStb cfg.queryfile, password, cfg.inkey, 435e7718adaStb cfg.signer, cfg.chain, cfg.policy, 436e7718adaStb cfg.in, cfg.token_in, cfg.out, 437e7718adaStb cfg.token_out, cfg.text); 438dab3f910Sjsing break; 439dab3f910Sjsing case CMD_VERIFY: 440e7718adaStb ret = !(((cfg.queryfile != NULL && cfg.data == NULL && 441e7718adaStb cfg.digest == NULL) || 442e7718adaStb (cfg.queryfile == NULL && cfg.data != NULL && 443e7718adaStb cfg.digest == NULL) || 444e7718adaStb (cfg.queryfile == NULL && cfg.data == NULL && 445e7718adaStb cfg.digest != NULL)) && 446e7718adaStb cfg.in != NULL); 447dab3f910Sjsing if (ret) 448dab3f910Sjsing goto usage; 449dab3f910Sjsing 450e7718adaStb ret = !verify_command(cfg.data, cfg.digest, 451e7718adaStb cfg.queryfile, cfg.in, cfg.token_in, 452e7718adaStb cfg.ca_path, cfg.ca_file, cfg.untrusted); 453dab3f910Sjsing } 454dab3f910Sjsing 455dab3f910Sjsing goto cleanup; 456dab3f910Sjsing 457dab3f910Sjsing usage: 458d572171bSinoguchi ts_usage(); 459dab3f910Sjsing 460dab3f910Sjsing cleanup: 461dab3f910Sjsing /* Clean up. */ 462dab3f910Sjsing NCONF_free(conf); 463dab3f910Sjsing free(password); 464dab3f910Sjsing OBJ_cleanup(); 465dab3f910Sjsing 466dab3f910Sjsing return (ret); 467dab3f910Sjsing } 468dab3f910Sjsing 469dab3f910Sjsing /* 470dab3f910Sjsing * Configuration file-related function definitions. 471dab3f910Sjsing */ 472dab3f910Sjsing 473dab3f910Sjsing static ASN1_OBJECT * 474dab3f910Sjsing txt2obj(const char *oid) 475dab3f910Sjsing { 476dab3f910Sjsing ASN1_OBJECT *oid_obj = NULL; 477dab3f910Sjsing 47830f6ef3aSinoguchi if ((oid_obj = OBJ_txt2obj(oid, 0)) == NULL) 479dab3f910Sjsing BIO_printf(bio_err, "cannot convert %s to OID\n", oid); 480dab3f910Sjsing 481dab3f910Sjsing return oid_obj; 482dab3f910Sjsing } 483dab3f910Sjsing 484dab3f910Sjsing static CONF * 485dab3f910Sjsing load_config_file(const char *configfile) 486dab3f910Sjsing { 487dab3f910Sjsing CONF *conf = NULL; 488dab3f910Sjsing long errorline = -1; 489dab3f910Sjsing 49030f6ef3aSinoguchi if (configfile == NULL) 491dab3f910Sjsing configfile = getenv("OPENSSL_CONF"); 492dab3f910Sjsing 49330f6ef3aSinoguchi if (configfile != NULL && 49430f6ef3aSinoguchi ((conf = NCONF_new(NULL)) == NULL || 495dab3f910Sjsing NCONF_load(conf, configfile, &errorline) <= 0)) { 496dab3f910Sjsing if (errorline <= 0) 497dab3f910Sjsing BIO_printf(bio_err, "error loading the config file " 498dab3f910Sjsing "'%s'\n", configfile); 499dab3f910Sjsing else 500dab3f910Sjsing BIO_printf(bio_err, "error on line %ld of config file " 501dab3f910Sjsing "'%s'\n", errorline, configfile); 502dab3f910Sjsing } 503dab3f910Sjsing if (conf != NULL) { 504dab3f910Sjsing const char *p; 505dab3f910Sjsing 506dab3f910Sjsing BIO_printf(bio_err, "Using configuration from %s\n", 507dab3f910Sjsing configfile); 508dab3f910Sjsing p = NCONF_get_string(conf, NULL, ENV_OID_FILE); 509dab3f910Sjsing if (p != NULL) { 510dab3f910Sjsing BIO *oid_bio = BIO_new_file(p, "r"); 51130f6ef3aSinoguchi if (oid_bio == NULL) 512dab3f910Sjsing ERR_print_errors(bio_err); 513dab3f910Sjsing else { 514dab3f910Sjsing OBJ_create_objects(oid_bio); 515dab3f910Sjsing BIO_free_all(oid_bio); 516dab3f910Sjsing } 517dab3f910Sjsing } else 518dab3f910Sjsing ERR_clear_error(); 519dab3f910Sjsing if (!add_oid_section(bio_err, conf)) 520dab3f910Sjsing ERR_print_errors(bio_err); 521dab3f910Sjsing } 522dab3f910Sjsing return conf; 523dab3f910Sjsing } 524dab3f910Sjsing 525dab3f910Sjsing /* 526dab3f910Sjsing * Query-related method definitions. 527dab3f910Sjsing */ 528dab3f910Sjsing 529dab3f910Sjsing static int 530dab3f910Sjsing query_command(const char *data, char *digest, const EVP_MD *md, 531e04fe004Sinoguchi const char *policy, int no_nonce, int cert, const char *in, const char *out, 532e04fe004Sinoguchi int text) 533dab3f910Sjsing { 534dab3f910Sjsing int ret = 0; 535dab3f910Sjsing TS_REQ *query = NULL; 536dab3f910Sjsing BIO *in_bio = NULL; 537dab3f910Sjsing BIO *data_bio = NULL; 538dab3f910Sjsing BIO *out_bio = NULL; 539dab3f910Sjsing 540dab3f910Sjsing /* Build query object either from file or from scratch. */ 541dab3f910Sjsing if (in != NULL) { 542dab3f910Sjsing if ((in_bio = BIO_new_file(in, "rb")) == NULL) 543dab3f910Sjsing goto end; 544dab3f910Sjsing query = d2i_TS_REQ_bio(in_bio, NULL); 545dab3f910Sjsing } else { 546dab3f910Sjsing /* Open the file if no explicit digest bytes were specified. */ 54730f6ef3aSinoguchi if (digest == NULL && 54830f6ef3aSinoguchi (data_bio = BIO_open_with_default(data, "rb", stdin)) == NULL) 549dab3f910Sjsing goto end; 550dab3f910Sjsing /* Creating the query object. */ 551dab3f910Sjsing query = create_query(data_bio, digest, md, 552dab3f910Sjsing policy, no_nonce, cert); 553dab3f910Sjsing /* Saving the random number generator state. */ 554dab3f910Sjsing } 555dab3f910Sjsing if (query == NULL) 556dab3f910Sjsing goto end; 557dab3f910Sjsing 558dab3f910Sjsing /* Write query either in ASN.1 or in text format. */ 559dab3f910Sjsing if ((out_bio = BIO_open_with_default(out, "wb", stdout)) == NULL) 560dab3f910Sjsing goto end; 561dab3f910Sjsing if (text) { 562dab3f910Sjsing /* Text output. */ 563dab3f910Sjsing if (!TS_REQ_print_bio(out_bio, query)) 564dab3f910Sjsing goto end; 565dab3f910Sjsing } else { 566dab3f910Sjsing /* ASN.1 output. */ 567dab3f910Sjsing if (!i2d_TS_REQ_bio(out_bio, query)) 568dab3f910Sjsing goto end; 569dab3f910Sjsing } 570dab3f910Sjsing 571dab3f910Sjsing ret = 1; 572dab3f910Sjsing 573dab3f910Sjsing end: 574dab3f910Sjsing ERR_print_errors(bio_err); 575dab3f910Sjsing 576dab3f910Sjsing /* Clean up. */ 577dab3f910Sjsing BIO_free_all(in_bio); 578dab3f910Sjsing BIO_free_all(data_bio); 579dab3f910Sjsing BIO_free_all(out_bio); 580dab3f910Sjsing TS_REQ_free(query); 581dab3f910Sjsing 582dab3f910Sjsing return ret; 583dab3f910Sjsing } 584dab3f910Sjsing 585dab3f910Sjsing static BIO * 586dab3f910Sjsing BIO_open_with_default(const char *file, const char *mode, FILE *default_fp) 587dab3f910Sjsing { 588dab3f910Sjsing return file == NULL ? BIO_new_fp(default_fp, BIO_NOCLOSE) : 589dab3f910Sjsing BIO_new_file(file, mode); 590dab3f910Sjsing } 591dab3f910Sjsing 592dab3f910Sjsing static TS_REQ * 593e04fe004Sinoguchi create_query(BIO *data_bio, char *digest, const EVP_MD *md, const char *policy, 594e04fe004Sinoguchi int no_nonce, int cert) 595dab3f910Sjsing { 596dab3f910Sjsing int ret = 0; 597dab3f910Sjsing TS_REQ *ts_req = NULL; 598dab3f910Sjsing int len; 599dab3f910Sjsing TS_MSG_IMPRINT *msg_imprint = NULL; 600dab3f910Sjsing X509_ALGOR *algo = NULL; 601dab3f910Sjsing unsigned char *data = NULL; 602c3585e1cStb ASN1_OBJECT *md_obj = NULL, *policy_obj = NULL; 603dab3f910Sjsing ASN1_INTEGER *nonce_asn1 = NULL; 604dab3f910Sjsing 605dab3f910Sjsing /* Setting default message digest. */ 60630f6ef3aSinoguchi if (md == NULL && (md = EVP_get_digestbyname("sha1")) == NULL) 607dab3f910Sjsing goto err; 608dab3f910Sjsing 609dab3f910Sjsing /* Creating request object. */ 61030f6ef3aSinoguchi if ((ts_req = TS_REQ_new()) == NULL) 611dab3f910Sjsing goto err; 612dab3f910Sjsing 613dab3f910Sjsing /* Setting version. */ 614dab3f910Sjsing if (!TS_REQ_set_version(ts_req, 1)) 615dab3f910Sjsing goto err; 616dab3f910Sjsing 617dab3f910Sjsing /* Creating and adding MSG_IMPRINT object. */ 61830f6ef3aSinoguchi if ((msg_imprint = TS_MSG_IMPRINT_new()) == NULL) 619dab3f910Sjsing goto err; 620dab3f910Sjsing 621dab3f910Sjsing /* Adding algorithm. */ 62230f6ef3aSinoguchi if ((algo = X509_ALGOR_new()) == NULL) 623dab3f910Sjsing goto err; 624c3585e1cStb if ((md_obj = OBJ_nid2obj(EVP_MD_type(md))) == NULL) 625dab3f910Sjsing goto err; 626c3585e1cStb /* 627c3585e1cStb * This does not use X509_ALGOR_set_md() for historical reasons. 628c3585e1cStb * See the comment in PKCS7_SIGNER_INFO_set() for details. 629c3585e1cStb */ 630c3585e1cStb if (!X509_ALGOR_set0(algo, md_obj, V_ASN1_NULL, NULL)) 631dab3f910Sjsing goto err; 632dab3f910Sjsing if (!TS_MSG_IMPRINT_set_algo(msg_imprint, algo)) 633dab3f910Sjsing goto err; 634dab3f910Sjsing 635dab3f910Sjsing /* Adding message digest. */ 636dab3f910Sjsing if ((len = create_digest(data_bio, digest, md, &data)) == 0) 637dab3f910Sjsing goto err; 638dab3f910Sjsing if (!TS_MSG_IMPRINT_set_msg(msg_imprint, data, len)) 639dab3f910Sjsing goto err; 640dab3f910Sjsing 641dab3f910Sjsing if (!TS_REQ_set_msg_imprint(ts_req, msg_imprint)) 642dab3f910Sjsing goto err; 643dab3f910Sjsing 644dab3f910Sjsing /* Setting policy if requested. */ 64530f6ef3aSinoguchi if (policy != NULL && (policy_obj = txt2obj(policy)) == NULL) 646dab3f910Sjsing goto err; 64730f6ef3aSinoguchi if (policy_obj != NULL && !TS_REQ_set_policy_id(ts_req, policy_obj)) 648dab3f910Sjsing goto err; 649dab3f910Sjsing 650dab3f910Sjsing /* Setting nonce if requested. */ 65130f6ef3aSinoguchi if (!no_nonce && (nonce_asn1 = create_nonce(NONCE_LENGTH)) == NULL) 652dab3f910Sjsing goto err; 65330f6ef3aSinoguchi if (nonce_asn1 != NULL && !TS_REQ_set_nonce(ts_req, nonce_asn1)) 654dab3f910Sjsing goto err; 655dab3f910Sjsing 656dab3f910Sjsing /* Setting certificate request flag if requested. */ 657dab3f910Sjsing if (!TS_REQ_set_cert_req(ts_req, cert)) 658dab3f910Sjsing goto err; 659dab3f910Sjsing 660dab3f910Sjsing ret = 1; 661dab3f910Sjsing 662dab3f910Sjsing err: 663dab3f910Sjsing if (!ret) { 664dab3f910Sjsing TS_REQ_free(ts_req); 665dab3f910Sjsing ts_req = NULL; 666dab3f910Sjsing BIO_printf(bio_err, "could not create query\n"); 667dab3f910Sjsing } 668dab3f910Sjsing TS_MSG_IMPRINT_free(msg_imprint); 669dab3f910Sjsing X509_ALGOR_free(algo); 670dab3f910Sjsing free(data); 671dab3f910Sjsing ASN1_OBJECT_free(policy_obj); 672dab3f910Sjsing ASN1_INTEGER_free(nonce_asn1); 673dab3f910Sjsing 674dab3f910Sjsing return ts_req; 675dab3f910Sjsing } 676dab3f910Sjsing 677dab3f910Sjsing static int 678dab3f910Sjsing create_digest(BIO *input, char *digest, const EVP_MD *md, 6795a216ca0Stb unsigned char **out_md_value) 680dab3f910Sjsing { 681814fd41bSinoguchi EVP_MD_CTX *md_ctx = NULL; 6825a216ca0Stb unsigned char *md_value = NULL; 6835a216ca0Stb int md_value_len; 6845a216ca0Stb int ret = 0; 685dab3f910Sjsing 686dab3f910Sjsing md_value_len = EVP_MD_size(md); 687dab3f910Sjsing if (md_value_len < 0) 688dab3f910Sjsing goto err; 689814fd41bSinoguchi 69030f6ef3aSinoguchi if (input != NULL) { 691dab3f910Sjsing /* Digest must be computed from an input file. */ 692dab3f910Sjsing unsigned char buffer[4096]; 693dab3f910Sjsing int length; 694dab3f910Sjsing 6955a216ca0Stb md_value = malloc(md_value_len); 6965a216ca0Stb if (md_value == NULL) 697dab3f910Sjsing goto err; 698dab3f910Sjsing 699387397abStb if ((md_ctx = EVP_MD_CTX_new()) == NULL) 700387397abStb goto err; 701387397abStb 702814fd41bSinoguchi if (!EVP_DigestInit(md_ctx, md)) 703814fd41bSinoguchi goto err; 704814fd41bSinoguchi 705dab3f910Sjsing while ((length = BIO_read(input, buffer, sizeof(buffer))) > 0) { 706814fd41bSinoguchi if (!EVP_DigestUpdate(md_ctx, buffer, length)) 707814fd41bSinoguchi goto err; 708dab3f910Sjsing } 709814fd41bSinoguchi 7105a216ca0Stb if (!EVP_DigestFinal(md_ctx, md_value, NULL)) 711814fd41bSinoguchi goto err; 712dab3f910Sjsing } else { 713dab3f910Sjsing /* Digest bytes are specified with digest. */ 714dab3f910Sjsing long digest_len; 715814fd41bSinoguchi 7165a216ca0Stb md_value = string_to_hex(digest, &digest_len); 7175a216ca0Stb if (md_value == NULL || md_value_len != digest_len) { 718dab3f910Sjsing BIO_printf(bio_err, "bad digest, %d bytes " 719dab3f910Sjsing "must be specified\n", md_value_len); 720dab3f910Sjsing goto err; 721dab3f910Sjsing } 722dab3f910Sjsing } 723dab3f910Sjsing 7245a216ca0Stb *out_md_value = md_value; 7255a216ca0Stb md_value = NULL; 7265a216ca0Stb 7275a216ca0Stb ret = md_value_len; 728814fd41bSinoguchi 729dab3f910Sjsing err: 7305a216ca0Stb free(md_value); 731814fd41bSinoguchi EVP_MD_CTX_free(md_ctx); 7325a216ca0Stb 7335a216ca0Stb return ret; 734dab3f910Sjsing } 735dab3f910Sjsing 736dab3f910Sjsing static ASN1_INTEGER * 737dab3f910Sjsing create_nonce(int bits) 738dab3f910Sjsing { 739dab3f910Sjsing unsigned char buf[20]; 740dab3f910Sjsing ASN1_INTEGER *nonce = NULL; 741dab3f910Sjsing int len = (bits - 1) / 8 + 1; 742dab3f910Sjsing int i; 743dab3f910Sjsing 744dab3f910Sjsing /* Generating random byte sequence. */ 745dab3f910Sjsing if (len > (int) sizeof(buf)) 746dab3f910Sjsing goto err; 747fd6ab616Sjsing arc4random_buf(buf, len); 748dab3f910Sjsing 749dab3f910Sjsing /* Find the first non-zero byte and creating ASN1_INTEGER object. */ 750dab3f910Sjsing for (i = 0; i < len && !buf[i]; ++i) 751dab3f910Sjsing ; 75230f6ef3aSinoguchi if ((nonce = ASN1_INTEGER_new()) == NULL) 753dab3f910Sjsing goto err; 754dab3f910Sjsing free(nonce->data); 755dab3f910Sjsing /* Allocate at least one byte. */ 756dab3f910Sjsing nonce->length = len - i; 75730f6ef3aSinoguchi if ((nonce->data = malloc(nonce->length + 1)) == NULL) 758dab3f910Sjsing goto err; 759dab3f910Sjsing memcpy(nonce->data, buf + i, nonce->length); 760dab3f910Sjsing 761dab3f910Sjsing return nonce; 762dab3f910Sjsing 763dab3f910Sjsing err: 764dab3f910Sjsing BIO_printf(bio_err, "could not create nonce\n"); 765dab3f910Sjsing ASN1_INTEGER_free(nonce); 766dab3f910Sjsing return NULL; 767dab3f910Sjsing } 768e04fe004Sinoguchi 769dab3f910Sjsing /* 770dab3f910Sjsing * Reply-related method definitions. 771dab3f910Sjsing */ 772dab3f910Sjsing 773dab3f910Sjsing static int 774e04fe004Sinoguchi reply_command(CONF *conf, char *section, char *queryfile, char *passin, 775e04fe004Sinoguchi char *inkey, char *signer, char *chain, const char *policy, char *in, 776e04fe004Sinoguchi int token_in, char *out, int token_out, int text) 777dab3f910Sjsing { 778dab3f910Sjsing int ret = 0; 779dab3f910Sjsing TS_RESP *response = NULL; 780dab3f910Sjsing BIO *in_bio = NULL; 781dab3f910Sjsing BIO *query_bio = NULL; 782dab3f910Sjsing BIO *inkey_bio = NULL; 783dab3f910Sjsing BIO *signer_bio = NULL; 784dab3f910Sjsing BIO *out_bio = NULL; 785dab3f910Sjsing 786dab3f910Sjsing /* Build response object either from response or query. */ 787dab3f910Sjsing if (in != NULL) { 788dab3f910Sjsing if ((in_bio = BIO_new_file(in, "rb")) == NULL) 789dab3f910Sjsing goto end; 790dab3f910Sjsing if (token_in) { 791dab3f910Sjsing /* 792dab3f910Sjsing * We have a ContentInfo (PKCS7) object, add 793dab3f910Sjsing * 'granted' status info around it. 794dab3f910Sjsing */ 795dab3f910Sjsing response = read_PKCS7(in_bio); 796dab3f910Sjsing } else { 797dab3f910Sjsing /* We have a ready-made TS_RESP object. */ 798dab3f910Sjsing response = d2i_TS_RESP_bio(in_bio, NULL); 799dab3f910Sjsing } 800dab3f910Sjsing } else { 80130f6ef3aSinoguchi response = create_response(conf, section, queryfile, passin, 80230f6ef3aSinoguchi inkey, signer, chain, policy); 80330f6ef3aSinoguchi if (response != NULL) 804dab3f910Sjsing BIO_printf(bio_err, "Response has been generated.\n"); 805dab3f910Sjsing else 806dab3f910Sjsing BIO_printf(bio_err, "Response is not generated.\n"); 807dab3f910Sjsing } 808dab3f910Sjsing if (response == NULL) 809dab3f910Sjsing goto end; 810dab3f910Sjsing 811dab3f910Sjsing /* Write response either in ASN.1 or text format. */ 812dab3f910Sjsing if ((out_bio = BIO_open_with_default(out, "wb", stdout)) == NULL) 813dab3f910Sjsing goto end; 814dab3f910Sjsing if (text) { 815dab3f910Sjsing /* Text output. */ 816dab3f910Sjsing if (token_out) { 817dab3f910Sjsing TS_TST_INFO *tst_info = TS_RESP_get_tst_info(response); 818dab3f910Sjsing if (!TS_TST_INFO_print_bio(out_bio, tst_info)) 819dab3f910Sjsing goto end; 820dab3f910Sjsing } else { 821dab3f910Sjsing if (!TS_RESP_print_bio(out_bio, response)) 822dab3f910Sjsing goto end; 823dab3f910Sjsing } 824dab3f910Sjsing } else { 825dab3f910Sjsing /* ASN.1 DER output. */ 826dab3f910Sjsing if (token_out) { 827dab3f910Sjsing PKCS7 *token = TS_RESP_get_token(response); 828dab3f910Sjsing if (!i2d_PKCS7_bio(out_bio, token)) 829dab3f910Sjsing goto end; 830dab3f910Sjsing } else { 831dab3f910Sjsing if (!i2d_TS_RESP_bio(out_bio, response)) 832dab3f910Sjsing goto end; 833dab3f910Sjsing } 834dab3f910Sjsing } 835dab3f910Sjsing 836dab3f910Sjsing ret = 1; 837dab3f910Sjsing 838dab3f910Sjsing end: 839dab3f910Sjsing ERR_print_errors(bio_err); 840dab3f910Sjsing 841dab3f910Sjsing /* Clean up. */ 842dab3f910Sjsing BIO_free_all(in_bio); 843dab3f910Sjsing BIO_free_all(query_bio); 844dab3f910Sjsing BIO_free_all(inkey_bio); 845dab3f910Sjsing BIO_free_all(signer_bio); 846dab3f910Sjsing BIO_free_all(out_bio); 847dab3f910Sjsing TS_RESP_free(response); 848dab3f910Sjsing 849dab3f910Sjsing return ret; 850dab3f910Sjsing } 851dab3f910Sjsing 852dab3f910Sjsing /* Reads a PKCS7 token and adds default 'granted' status info to it. */ 853dab3f910Sjsing static TS_RESP * 854dab3f910Sjsing read_PKCS7(BIO *in_bio) 855dab3f910Sjsing { 856dab3f910Sjsing int ret = 0; 857dab3f910Sjsing PKCS7 *token = NULL; 858dab3f910Sjsing TS_TST_INFO *tst_info = NULL; 859dab3f910Sjsing TS_RESP *resp = NULL; 860dab3f910Sjsing TS_STATUS_INFO *si = NULL; 861dab3f910Sjsing 862dab3f910Sjsing /* Read PKCS7 object and extract the signed time stamp info. */ 86330f6ef3aSinoguchi if ((token = d2i_PKCS7_bio(in_bio, NULL)) == NULL) 864dab3f910Sjsing goto end; 86530f6ef3aSinoguchi if ((tst_info = PKCS7_to_TS_TST_INFO(token)) == NULL) 866dab3f910Sjsing goto end; 867dab3f910Sjsing 868dab3f910Sjsing /* Creating response object. */ 86930f6ef3aSinoguchi if ((resp = TS_RESP_new()) == NULL) 870dab3f910Sjsing goto end; 871dab3f910Sjsing 872dab3f910Sjsing /* Create granted status info. */ 87330f6ef3aSinoguchi if ((si = TS_STATUS_INFO_new()) == NULL) 874dab3f910Sjsing goto end; 87554d91a7fStb if (!TS_STATUS_INFO_set_status(si, TS_STATUS_GRANTED)) 876dab3f910Sjsing goto end; 877dab3f910Sjsing if (!TS_RESP_set_status_info(resp, si)) 878dab3f910Sjsing goto end; 879dab3f910Sjsing 880dab3f910Sjsing /* Setting encapsulated token. */ 881dab3f910Sjsing TS_RESP_set_tst_info(resp, token, tst_info); 882dab3f910Sjsing token = NULL; /* Ownership is lost. */ 883dab3f910Sjsing tst_info = NULL; /* Ownership is lost. */ 884dab3f910Sjsing 885dab3f910Sjsing ret = 1; 886dab3f910Sjsing end: 887dab3f910Sjsing PKCS7_free(token); 888dab3f910Sjsing TS_TST_INFO_free(tst_info); 889dab3f910Sjsing if (!ret) { 890dab3f910Sjsing TS_RESP_free(resp); 891dab3f910Sjsing resp = NULL; 892dab3f910Sjsing } 893dab3f910Sjsing TS_STATUS_INFO_free(si); 894dab3f910Sjsing return resp; 895dab3f910Sjsing } 896dab3f910Sjsing 897dab3f910Sjsing static TS_RESP * 898e04fe004Sinoguchi create_response(CONF *conf, const char *section, char *queryfile, char *passin, 899e04fe004Sinoguchi char *inkey, char *signer, char *chain, const char *policy) 900dab3f910Sjsing { 901dab3f910Sjsing int ret = 0; 902dab3f910Sjsing TS_RESP *response = NULL; 903dab3f910Sjsing BIO *query_bio = NULL; 904dab3f910Sjsing TS_RESP_CTX *resp_ctx = NULL; 905dab3f910Sjsing 90630f6ef3aSinoguchi if ((query_bio = BIO_new_file(queryfile, "rb")) == NULL) 907dab3f910Sjsing goto end; 908dab3f910Sjsing 909dab3f910Sjsing /* Getting TSA configuration section. */ 91030f6ef3aSinoguchi if ((section = TS_CONF_get_tsa_section(conf, section)) == NULL) 911dab3f910Sjsing goto end; 912dab3f910Sjsing 913dab3f910Sjsing /* Setting up response generation context. */ 91430f6ef3aSinoguchi if ((resp_ctx = TS_RESP_CTX_new()) == NULL) 915dab3f910Sjsing goto end; 916dab3f910Sjsing 917dab3f910Sjsing /* Setting serial number provider callback. */ 918dab3f910Sjsing if (!TS_CONF_set_serial(conf, section, serial_cb, resp_ctx)) 919dab3f910Sjsing goto end; 920dab3f910Sjsing 921dab3f910Sjsing /* Setting TSA signer certificate. */ 922dab3f910Sjsing if (!TS_CONF_set_signer_cert(conf, section, signer, resp_ctx)) 923dab3f910Sjsing goto end; 924dab3f910Sjsing 925dab3f910Sjsing /* Setting TSA signer certificate chain. */ 926dab3f910Sjsing if (!TS_CONF_set_certs(conf, section, chain, resp_ctx)) 927dab3f910Sjsing goto end; 928dab3f910Sjsing 929dab3f910Sjsing /* Setting TSA signer private key. */ 930dab3f910Sjsing if (!TS_CONF_set_signer_key(conf, section, inkey, passin, resp_ctx)) 931dab3f910Sjsing goto end; 932dab3f910Sjsing 933dab3f910Sjsing /* Setting default policy OID. */ 934dab3f910Sjsing if (!TS_CONF_set_def_policy(conf, section, policy, resp_ctx)) 935dab3f910Sjsing goto end; 936dab3f910Sjsing 937dab3f910Sjsing /* Setting acceptable policy OIDs. */ 938dab3f910Sjsing if (!TS_CONF_set_policies(conf, section, resp_ctx)) 939dab3f910Sjsing goto end; 940dab3f910Sjsing 941dab3f910Sjsing /* Setting the acceptable one-way hash algorithms. */ 942dab3f910Sjsing if (!TS_CONF_set_digests(conf, section, resp_ctx)) 943dab3f910Sjsing goto end; 944dab3f910Sjsing 945dab3f910Sjsing /* Setting guaranteed time stamp accuracy. */ 946dab3f910Sjsing if (!TS_CONF_set_accuracy(conf, section, resp_ctx)) 947dab3f910Sjsing goto end; 948dab3f910Sjsing 949dab3f910Sjsing /* Setting the precision of the time. */ 950dab3f910Sjsing if (!TS_CONF_set_clock_precision_digits(conf, section, resp_ctx)) 951dab3f910Sjsing goto end; 952dab3f910Sjsing 953*1c25dff2Stb /* Setting the ordering flag if requested. */ 954dab3f910Sjsing if (!TS_CONF_set_ordering(conf, section, resp_ctx)) 955dab3f910Sjsing goto end; 956dab3f910Sjsing 957dab3f910Sjsing /* Setting the TSA name required flag if requested. */ 958dab3f910Sjsing if (!TS_CONF_set_tsa_name(conf, section, resp_ctx)) 959dab3f910Sjsing goto end; 960dab3f910Sjsing 961dab3f910Sjsing /* Setting the ESS cert id chain flag if requested. */ 962dab3f910Sjsing if (!TS_CONF_set_ess_cert_id_chain(conf, section, resp_ctx)) 963dab3f910Sjsing goto end; 964dab3f910Sjsing 965dab3f910Sjsing /* Creating the response. */ 96630f6ef3aSinoguchi if ((response = TS_RESP_create_response(resp_ctx, query_bio)) == NULL) 967dab3f910Sjsing goto end; 968dab3f910Sjsing 969dab3f910Sjsing ret = 1; 970dab3f910Sjsing end: 971dab3f910Sjsing if (!ret) { 972dab3f910Sjsing TS_RESP_free(response); 973dab3f910Sjsing response = NULL; 974dab3f910Sjsing } 975dab3f910Sjsing TS_RESP_CTX_free(resp_ctx); 976dab3f910Sjsing BIO_free_all(query_bio); 977dab3f910Sjsing 978dab3f910Sjsing return response; 979dab3f910Sjsing } 980dab3f910Sjsing 981dab3f910Sjsing static ASN1_INTEGER * 982dab3f910Sjsing serial_cb(TS_RESP_CTX *ctx, void *data) 983dab3f910Sjsing { 984dab3f910Sjsing const char *serial_file = (const char *) data; 985dab3f910Sjsing ASN1_INTEGER *serial = next_serial(serial_file); 986dab3f910Sjsing 98730f6ef3aSinoguchi if (serial == NULL) { 988dab3f910Sjsing TS_RESP_CTX_set_status_info(ctx, TS_STATUS_REJECTION, 989dab3f910Sjsing "Error during serial number " 990dab3f910Sjsing "generation."); 991dab3f910Sjsing TS_RESP_CTX_add_failure_info(ctx, 992dab3f910Sjsing TS_INFO_ADD_INFO_NOT_AVAILABLE); 993dab3f910Sjsing } else 994dab3f910Sjsing save_ts_serial(serial_file, serial); 995dab3f910Sjsing 996dab3f910Sjsing return serial; 997dab3f910Sjsing } 998dab3f910Sjsing 999dab3f910Sjsing static ASN1_INTEGER * 1000dab3f910Sjsing next_serial(const char *serialfile) 1001dab3f910Sjsing { 1002dab3f910Sjsing int ret = 0; 1003dab3f910Sjsing BIO *in = NULL; 1004dab3f910Sjsing ASN1_INTEGER *serial = NULL; 1005dab3f910Sjsing BIGNUM *bn = NULL; 1006dab3f910Sjsing 100730f6ef3aSinoguchi if ((serial = ASN1_INTEGER_new()) == NULL) 1008dab3f910Sjsing goto err; 1009dab3f910Sjsing 101030f6ef3aSinoguchi if ((in = BIO_new_file(serialfile, "r")) == NULL) { 1011dab3f910Sjsing ERR_clear_error(); 1012dab3f910Sjsing BIO_printf(bio_err, "Warning: could not open file %s for " 1013dab3f910Sjsing "reading, using serial number: 1\n", serialfile); 1014dab3f910Sjsing if (!ASN1_INTEGER_set(serial, 1)) 1015dab3f910Sjsing goto err; 1016dab3f910Sjsing } else { 1017dab3f910Sjsing char buf[1024]; 1018dab3f910Sjsing if (!a2i_ASN1_INTEGER(in, serial, buf, sizeof(buf))) { 1019dab3f910Sjsing BIO_printf(bio_err, "unable to load number from %s\n", 1020dab3f910Sjsing serialfile); 1021dab3f910Sjsing goto err; 1022dab3f910Sjsing } 102330f6ef3aSinoguchi if ((bn = ASN1_INTEGER_to_BN(serial, NULL)) == NULL) 1024dab3f910Sjsing goto err; 1025dab3f910Sjsing ASN1_INTEGER_free(serial); 1026dab3f910Sjsing serial = NULL; 1027dab3f910Sjsing if (!BN_add_word(bn, 1)) 1028dab3f910Sjsing goto err; 102930f6ef3aSinoguchi if ((serial = BN_to_ASN1_INTEGER(bn, NULL)) == NULL) 1030dab3f910Sjsing goto err; 1031dab3f910Sjsing } 1032dab3f910Sjsing ret = 1; 1033dab3f910Sjsing err: 1034dab3f910Sjsing if (!ret) { 1035dab3f910Sjsing ASN1_INTEGER_free(serial); 1036dab3f910Sjsing serial = NULL; 1037dab3f910Sjsing } 1038dab3f910Sjsing BIO_free_all(in); 1039dab3f910Sjsing BN_free(bn); 1040dab3f910Sjsing return serial; 1041dab3f910Sjsing } 1042dab3f910Sjsing 1043dab3f910Sjsing static int 1044dab3f910Sjsing save_ts_serial(const char *serialfile, ASN1_INTEGER *serial) 1045dab3f910Sjsing { 1046dab3f910Sjsing int ret = 0; 1047dab3f910Sjsing BIO *out = NULL; 1048dab3f910Sjsing 104930f6ef3aSinoguchi if ((out = BIO_new_file(serialfile, "w")) == NULL) 1050dab3f910Sjsing goto err; 1051dab3f910Sjsing if (i2a_ASN1_INTEGER(out, serial) <= 0) 1052dab3f910Sjsing goto err; 1053dab3f910Sjsing if (BIO_puts(out, "\n") <= 0) 1054dab3f910Sjsing goto err; 1055dab3f910Sjsing ret = 1; 1056dab3f910Sjsing err: 1057dab3f910Sjsing if (!ret) 1058dab3f910Sjsing BIO_printf(bio_err, "could not save serial number to %s\n", 1059dab3f910Sjsing serialfile); 1060dab3f910Sjsing BIO_free_all(out); 1061dab3f910Sjsing return ret; 1062dab3f910Sjsing } 1063dab3f910Sjsing 1064dab3f910Sjsing /* 1065dab3f910Sjsing * Verify-related method definitions. 1066dab3f910Sjsing */ 1067dab3f910Sjsing 1068dab3f910Sjsing static int 1069dab3f910Sjsing verify_command(char *data, char *digest, char *queryfile, char *in, 1070dab3f910Sjsing int token_in, char *ca_path, char *ca_file, char *untrusted) 1071dab3f910Sjsing { 1072dab3f910Sjsing BIO *in_bio = NULL; 1073dab3f910Sjsing PKCS7 *token = NULL; 1074dab3f910Sjsing TS_RESP *response = NULL; 1075dab3f910Sjsing TS_VERIFY_CTX *verify_ctx = NULL; 1076dab3f910Sjsing int ret = 0; 1077dab3f910Sjsing 1078dab3f910Sjsing /* Decode the token (PKCS7) or response (TS_RESP) files. */ 107930f6ef3aSinoguchi if ((in_bio = BIO_new_file(in, "rb")) == NULL) 1080dab3f910Sjsing goto end; 1081dab3f910Sjsing if (token_in) { 108230f6ef3aSinoguchi if ((token = d2i_PKCS7_bio(in_bio, NULL)) == NULL) 1083dab3f910Sjsing goto end; 1084dab3f910Sjsing } else { 108530f6ef3aSinoguchi if ((response = d2i_TS_RESP_bio(in_bio, NULL)) == NULL) 1086dab3f910Sjsing goto end; 1087dab3f910Sjsing } 1088dab3f910Sjsing 108930f6ef3aSinoguchi if ((verify_ctx = create_verify_ctx(data, digest, queryfile, 109030f6ef3aSinoguchi ca_path, ca_file, untrusted)) == NULL) 1091dab3f910Sjsing goto end; 1092dab3f910Sjsing 1093dab3f910Sjsing /* Checking the token or response against the request. */ 1094dab3f910Sjsing ret = token_in ? 1095dab3f910Sjsing TS_RESP_verify_token(verify_ctx, token) : 1096dab3f910Sjsing TS_RESP_verify_response(verify_ctx, response); 1097dab3f910Sjsing 1098dab3f910Sjsing end: 1099dab3f910Sjsing printf("Verification: "); 1100dab3f910Sjsing if (ret) 1101dab3f910Sjsing printf("OK\n"); 1102dab3f910Sjsing else { 1103dab3f910Sjsing printf("FAILED\n"); 1104dab3f910Sjsing /* Print errors, if there are any. */ 1105dab3f910Sjsing ERR_print_errors(bio_err); 1106dab3f910Sjsing } 1107dab3f910Sjsing 1108dab3f910Sjsing /* Clean up. */ 1109dab3f910Sjsing BIO_free_all(in_bio); 1110dab3f910Sjsing PKCS7_free(token); 1111dab3f910Sjsing TS_RESP_free(response); 1112dab3f910Sjsing TS_VERIFY_CTX_free(verify_ctx); 1113dab3f910Sjsing return ret; 1114dab3f910Sjsing } 1115dab3f910Sjsing 1116dab3f910Sjsing static TS_VERIFY_CTX * 1117dab3f910Sjsing create_verify_ctx(char *data, char *digest, char *queryfile, char *ca_path, 1118dab3f910Sjsing char *ca_file, char *untrusted) 1119dab3f910Sjsing { 1120dab3f910Sjsing TS_VERIFY_CTX *ctx = NULL; 1121dab3f910Sjsing BIO *input = NULL; 1122dab3f910Sjsing TS_REQ *request = NULL; 112354d91a7fStb X509_STORE *store; 112454d91a7fStb STACK_OF(X509) *certs; 1125dab3f910Sjsing int ret = 0; 1126dab3f910Sjsing 1127dab3f910Sjsing if (data != NULL || digest != NULL) { 112830f6ef3aSinoguchi if ((ctx = TS_VERIFY_CTX_new()) == NULL) 1129dab3f910Sjsing goto err; 113054d91a7fStb TS_VERIFY_CTX_set_flags(ctx, TS_VFY_VERSION | TS_VFY_SIGNER); 1131dab3f910Sjsing if (data != NULL) { 113254d91a7fStb BIO *data_bio; 113354d91a7fStb 113454d91a7fStb TS_VERIFY_CTX_add_flags(ctx, TS_VFY_DATA); 113554d91a7fStb if ((data_bio = BIO_new_file(data, "rb")) == NULL) 1136dab3f910Sjsing goto err; 113754d91a7fStb TS_VERIFY_CTX_set_data(ctx, data_bio); 1138dab3f910Sjsing } else if (digest != NULL) { 113954d91a7fStb unsigned char *imprint; 1140dab3f910Sjsing long imprint_len; 114154d91a7fStb 114254d91a7fStb TS_VERIFY_CTX_add_flags(ctx, TS_VFY_IMPRINT); 114354d91a7fStb if ((imprint = string_to_hex(digest, 114430f6ef3aSinoguchi &imprint_len)) == NULL) { 1145dab3f910Sjsing BIO_printf(bio_err, "invalid digest string\n"); 1146dab3f910Sjsing goto err; 1147dab3f910Sjsing } 114854d91a7fStb TS_VERIFY_CTX_set_imprint(ctx, imprint, imprint_len); 1149dab3f910Sjsing } 1150dab3f910Sjsing } else if (queryfile != NULL) { 1151dab3f910Sjsing /* 1152dab3f910Sjsing * The request has just to be read, decoded and converted to 1153dab3f910Sjsing * a verify context object. 1154dab3f910Sjsing */ 115530f6ef3aSinoguchi if ((input = BIO_new_file(queryfile, "rb")) == NULL) 1156dab3f910Sjsing goto err; 115730f6ef3aSinoguchi if ((request = d2i_TS_REQ_bio(input, NULL)) == NULL) 1158dab3f910Sjsing goto err; 115930f6ef3aSinoguchi if ((ctx = TS_REQ_to_TS_VERIFY_CTX(request, NULL)) == NULL) 1160dab3f910Sjsing goto err; 1161dab3f910Sjsing } else 1162dab3f910Sjsing return NULL; 1163dab3f910Sjsing 1164dab3f910Sjsing /* Add the signature verification flag and arguments. */ 116554d91a7fStb TS_VERIFY_CTX_add_flags(ctx, TS_VFY_SIGNATURE); 1166dab3f910Sjsing 1167dab3f910Sjsing /* Initialising the X509_STORE object. */ 116854d91a7fStb if ((store = create_cert_store(ca_path, ca_file)) == NULL) 1169dab3f910Sjsing goto err; 117054d91a7fStb TS_VERIFY_CTX_set_store(ctx, store); 1171dab3f910Sjsing 1172dab3f910Sjsing /* Loading untrusted certificates. */ 117354d91a7fStb if (untrusted != NULL) { 117454d91a7fStb if ((certs = TS_CONF_load_certs(untrusted)) == NULL) 1175dab3f910Sjsing goto err; 117654d91a7fStb TS_VERIFY_CTX_set_certs(ctx, certs); 117754d91a7fStb } 1178dab3f910Sjsing 1179dab3f910Sjsing ret = 1; 1180dab3f910Sjsing err: 1181dab3f910Sjsing if (!ret) { 1182dab3f910Sjsing TS_VERIFY_CTX_free(ctx); 1183dab3f910Sjsing ctx = NULL; 1184dab3f910Sjsing } 1185dab3f910Sjsing BIO_free_all(input); 1186dab3f910Sjsing TS_REQ_free(request); 1187dab3f910Sjsing return ctx; 1188dab3f910Sjsing } 1189dab3f910Sjsing 1190dab3f910Sjsing static X509_STORE * 1191dab3f910Sjsing create_cert_store(char *ca_path, char *ca_file) 1192dab3f910Sjsing { 1193dab3f910Sjsing X509_STORE *cert_ctx = NULL; 1194dab3f910Sjsing X509_LOOKUP *lookup = NULL; 1195dab3f910Sjsing int i; 1196dab3f910Sjsing 1197dab3f910Sjsing /* Creating the X509_STORE object. */ 119879564beeSinoguchi if ((cert_ctx = X509_STORE_new()) == NULL) 119979564beeSinoguchi goto err; 1200dab3f910Sjsing 1201dab3f910Sjsing /* Setting the callback for certificate chain verification. */ 1202dab3f910Sjsing X509_STORE_set_verify_cb(cert_ctx, verify_cb); 1203dab3f910Sjsing 1204dab3f910Sjsing /* Adding a trusted certificate directory source. */ 120530f6ef3aSinoguchi if (ca_path != NULL) { 1206dab3f910Sjsing lookup = X509_STORE_add_lookup(cert_ctx, 1207dab3f910Sjsing X509_LOOKUP_hash_dir()); 1208dab3f910Sjsing if (lookup == NULL) { 1209dab3f910Sjsing BIO_printf(bio_err, "memory allocation failure\n"); 1210dab3f910Sjsing goto err; 1211dab3f910Sjsing } 1212dab3f910Sjsing i = X509_LOOKUP_add_dir(lookup, ca_path, X509_FILETYPE_PEM); 1213dab3f910Sjsing if (!i) { 1214dab3f910Sjsing BIO_printf(bio_err, "Error loading directory %s\n", 1215dab3f910Sjsing ca_path); 1216dab3f910Sjsing goto err; 1217dab3f910Sjsing } 1218dab3f910Sjsing } 1219dab3f910Sjsing /* Adding a trusted certificate file source. */ 122030f6ef3aSinoguchi if (ca_file != NULL) { 1221dab3f910Sjsing lookup = X509_STORE_add_lookup(cert_ctx, X509_LOOKUP_file()); 1222dab3f910Sjsing if (lookup == NULL) { 1223dab3f910Sjsing BIO_printf(bio_err, "memory allocation failure\n"); 1224dab3f910Sjsing goto err; 1225dab3f910Sjsing } 1226dab3f910Sjsing i = X509_LOOKUP_load_file(lookup, ca_file, X509_FILETYPE_PEM); 1227dab3f910Sjsing if (!i) { 1228dab3f910Sjsing BIO_printf(bio_err, "Error loading file %s\n", ca_file); 1229dab3f910Sjsing goto err; 1230dab3f910Sjsing } 1231dab3f910Sjsing } 1232dab3f910Sjsing return cert_ctx; 1233dab3f910Sjsing err: 1234dab3f910Sjsing X509_STORE_free(cert_ctx); 1235dab3f910Sjsing return NULL; 1236dab3f910Sjsing } 1237dab3f910Sjsing 1238dab3f910Sjsing static int 1239dab3f910Sjsing verify_cb(int ok, X509_STORE_CTX *ctx) 1240dab3f910Sjsing { 1241dab3f910Sjsing /* 1242dab3f910Sjsing char buf[256]; 1243dab3f910Sjsing 1244dab3f910Sjsing if (!ok) 1245dab3f910Sjsing { 1246dab3f910Sjsing X509_NAME_oneline(X509_get_subject_name(ctx->current_cert), 1247dab3f910Sjsing buf, sizeof(buf)); 1248dab3f910Sjsing printf("%s\n", buf); 1249dab3f910Sjsing printf("error %d at %d depth lookup: %s\n", 1250dab3f910Sjsing ctx->error, ctx->error_depth, 1251dab3f910Sjsing X509_verify_cert_error_string(ctx->error)); 1252dab3f910Sjsing } 1253dab3f910Sjsing */ 1254dab3f910Sjsing 1255dab3f910Sjsing return ok; 1256dab3f910Sjsing } 1257