diff --git a/crypto/openssl/apps/dgst.c b/crypto/openssl/apps/dgst.c index 0fb1f45200a0..b002de18c94b 100644 --- a/crypto/openssl/apps/dgst.c +++ b/crypto/openssl/apps/dgst.c @@ -1,745 +1,744 @@ /* * Copyright 1995-2025 The OpenSSL Project Authors. All Rights Reserved. * * Licensed under the Apache License 2.0 (the "License"). You may not use * this file except in compliance with the License. You can obtain a copy * in the file LICENSE in the source distribution or at * https://www.openssl.org/source/license.html */ #include #include #include #include "apps.h" #include "progs.h" #include #include #include #include #include #include #include #include #undef BUFSIZE #define BUFSIZE 1024*8 static int do_fp_oneshot_sign(BIO *out, EVP_MD_CTX *ctx, BIO *in, int sep, int binout, EVP_PKEY *key, unsigned char *sigin, int siglen, const char *sig_name, const char *file); int do_fp(BIO *out, unsigned char *buf, BIO *bp, int sep, int binout, int xoflen, EVP_PKEY *key, unsigned char *sigin, int siglen, const char *sig_name, const char *md_name, const char *file); static void show_digests(const OBJ_NAME *name, void *bio_); struct doall_dgst_digests { BIO *bio; int n; }; typedef enum OPTION_choice { OPT_COMMON, OPT_LIST, OPT_C, OPT_R, OPT_OUT, OPT_SIGN, OPT_PASSIN, OPT_VERIFY, OPT_PRVERIFY, OPT_SIGNATURE, OPT_KEYFORM, OPT_ENGINE, OPT_ENGINE_IMPL, OPT_HEX, OPT_BINARY, OPT_DEBUG, OPT_FIPS_FINGERPRINT, OPT_HMAC, OPT_MAC, OPT_SIGOPT, OPT_MACOPT, OPT_XOFLEN, OPT_DIGEST, OPT_R_ENUM, OPT_PROV_ENUM } OPTION_CHOICE; const OPTIONS dgst_options[] = { {OPT_HELP_STR, 1, '-', "Usage: %s [options] [file...]\n"}, OPT_SECTION("General"), {"help", OPT_HELP, '-', "Display this summary"}, {"list", OPT_LIST, '-', "List digests"}, #ifndef OPENSSL_NO_ENGINE {"engine", OPT_ENGINE, 's', "Use engine e, possibly a hardware device"}, {"engine_impl", OPT_ENGINE_IMPL, '-', "Also use engine given by -engine for digest operations"}, #endif {"passin", OPT_PASSIN, 's', "Input file pass phrase source"}, OPT_SECTION("Output"), {"c", OPT_C, '-', "Print the digest with separating colons"}, {"r", OPT_R, '-', "Print the digest in coreutils format"}, {"out", OPT_OUT, '>', "Output to filename rather than stdout"}, {"keyform", OPT_KEYFORM, 'f', "Key file format (ENGINE, other values ignored)"}, {"hex", OPT_HEX, '-', "Print as hex dump"}, {"binary", OPT_BINARY, '-', "Print in binary form"}, {"xoflen", OPT_XOFLEN, 'p', "Output length for XOF algorithms. To obtain the maximum security strength set this to 32 (or greater) for SHAKE128, and 64 (or greater) for SHAKE256"}, {"d", OPT_DEBUG, '-', "Print debug info"}, {"debug", OPT_DEBUG, '-', "Print debug info"}, OPT_SECTION("Signing"), {"sign", OPT_SIGN, 's', "Sign digest using private key"}, {"verify", OPT_VERIFY, 's', "Verify a signature using public key"}, {"prverify", OPT_PRVERIFY, 's', "Verify a signature using private key"}, {"sigopt", OPT_SIGOPT, 's', "Signature parameter in n:v form"}, {"signature", OPT_SIGNATURE, '<', "File with signature to verify"}, {"hmac", OPT_HMAC, 's', "Create hashed MAC with key"}, {"mac", OPT_MAC, 's', "Create MAC (not necessarily HMAC)"}, {"macopt", OPT_MACOPT, 's', "MAC algorithm parameters in n:v form or key"}, {"", OPT_DIGEST, '-', "Any supported digest"}, {"fips-fingerprint", OPT_FIPS_FINGERPRINT, '-', "Compute HMAC with the key used in OpenSSL-FIPS fingerprint"}, OPT_R_OPTIONS, OPT_PROV_OPTIONS, OPT_PARAMETERS(), {"file", 0, 0, "Files to digest (optional; default is stdin)"}, {NULL} }; int dgst_main(int argc, char **argv) { BIO *in = NULL, *inp = NULL, *bmd = NULL, *out = NULL; ENGINE *e = NULL, *impl = NULL; EVP_PKEY *sigkey = NULL; STACK_OF(OPENSSL_STRING) *sigopts = NULL, *macopts = NULL; char *hmac_key = NULL; char *mac_name = NULL, *digestname = NULL; char *passinarg = NULL, *passin = NULL; EVP_MD *md = NULL; const char *outfile = NULL, *keyfile = NULL, *prog = NULL; const char *sigfile = NULL; const char *md_name = NULL; OPTION_CHOICE o; int separator = 0, debug = 0, keyform = FORMAT_UNDEF, siglen = 0; int i, ret = EXIT_FAILURE, out_bin = -1, want_pub = 0, do_verify = 0; int xoflen = 0; unsigned char *buf = NULL, *sigbuf = NULL; int engine_impl = 0; struct doall_dgst_digests dec; EVP_MD_CTX *signctx = NULL; int oneshot_sign = 0; buf = app_malloc(BUFSIZE, "I/O buffer"); md = (EVP_MD *)EVP_get_digestbyname(argv[0]); if (md != NULL) digestname = argv[0]; opt_set_unknown_name("digest"); prog = opt_init(argc, argv, dgst_options); while ((o = opt_next()) != OPT_EOF) { switch (o) { case OPT_EOF: case OPT_ERR: opthelp: BIO_printf(bio_err, "%s: Use -help for summary.\n", prog); goto end; case OPT_HELP: opt_help(dgst_options); ret = EXIT_SUCCESS; goto end; case OPT_LIST: BIO_printf(bio_out, "Supported digests:\n"); dec.bio = bio_out; dec.n = 0; OBJ_NAME_do_all_sorted(OBJ_NAME_TYPE_MD_METH, show_digests, &dec); BIO_printf(bio_out, "\n"); ret = EXIT_SUCCESS; goto end; case OPT_C: separator = 1; break; case OPT_R: separator = 2; break; case OPT_R_CASES: if (!opt_rand(o)) goto end; break; case OPT_OUT: outfile = opt_arg(); break; case OPT_SIGN: keyfile = opt_arg(); break; case OPT_PASSIN: passinarg = opt_arg(); break; case OPT_VERIFY: keyfile = opt_arg(); want_pub = do_verify = 1; break; case OPT_PRVERIFY: keyfile = opt_arg(); do_verify = 1; break; case OPT_SIGNATURE: sigfile = opt_arg(); break; case OPT_KEYFORM: if (!opt_format(opt_arg(), OPT_FMT_ANY, &keyform)) goto opthelp; break; case OPT_ENGINE: e = setup_engine(opt_arg(), 0); break; case OPT_ENGINE_IMPL: engine_impl = 1; break; case OPT_HEX: out_bin = 0; break; case OPT_BINARY: out_bin = 1; break; case OPT_XOFLEN: xoflen = atoi(opt_arg()); break; case OPT_DEBUG: debug = 1; break; case OPT_FIPS_FINGERPRINT: hmac_key = "etaonrishdlcupfm"; break; case OPT_HMAC: hmac_key = opt_arg(); break; case OPT_MAC: mac_name = opt_arg(); break; case OPT_SIGOPT: if (!sigopts) sigopts = sk_OPENSSL_STRING_new_null(); if (!sigopts || !sk_OPENSSL_STRING_push(sigopts, opt_arg())) goto opthelp; break; case OPT_MACOPT: if (!macopts) macopts = sk_OPENSSL_STRING_new_null(); if (!macopts || !sk_OPENSSL_STRING_push(macopts, opt_arg())) goto opthelp; break; case OPT_DIGEST: digestname = opt_unknown(); break; case OPT_PROV_CASES: if (!opt_provider(o)) goto end; break; } } /* Remaining args are files to digest. */ argc = opt_num_rest(); argv = opt_rest(); if (keyfile != NULL && argc > 1) { BIO_printf(bio_err, "%s: Can only sign or verify one file.\n", prog); goto end; } if (!app_RAND_load()) goto end; if (digestname != NULL) { if (!opt_md(digestname, &md)) goto opthelp; } if (do_verify && sigfile == NULL) { BIO_printf(bio_err, "No signature to verify: use the -signature option\n"); goto end; } if (engine_impl) impl = e; in = BIO_new(BIO_s_file()); bmd = BIO_new(BIO_f_md()); if (in == NULL || bmd == NULL) goto end; if (debug) { BIO_set_callback_ex(in, BIO_debug_callback_ex); /* needed for windows 3.1 */ BIO_set_callback_arg(in, (char *)bio_err); } if (!app_passwd(passinarg, NULL, &passin, NULL)) { BIO_printf(bio_err, "Error getting password\n"); goto end; } if (out_bin == -1) { if (keyfile != NULL) out_bin = 1; else out_bin = 0; } out = bio_open_default(outfile, 'w', out_bin ? FORMAT_BINARY : FORMAT_TEXT); if (out == NULL) goto end; if ((!(mac_name == NULL) + !(keyfile == NULL) + !(hmac_key == NULL)) > 1) { BIO_printf(bio_err, "MAC and signing key cannot both be specified\n"); goto end; } if (keyfile != NULL) { if (want_pub) sigkey = load_pubkey(keyfile, keyform, 0, NULL, e, "public key"); else sigkey = load_key(keyfile, keyform, 0, passin, e, "private key"); if (sigkey == NULL) { /* * load_[pub]key() has already printed an appropriate message */ goto end; } { char def_md[80]; if (EVP_PKEY_get_default_digest_name(sigkey, def_md, sizeof(def_md)) == 2 && strcmp(def_md, "UNDEF") == 0) oneshot_sign = 1; signctx = EVP_MD_CTX_new(); if (signctx == NULL) goto end; } } if (mac_name != NULL) { EVP_PKEY_CTX *mac_ctx = NULL; if (!init_gen_str(&mac_ctx, mac_name, impl, 0, NULL, NULL)) goto end; if (macopts != NULL) { for (i = 0; i < sk_OPENSSL_STRING_num(macopts); i++) { char *macopt = sk_OPENSSL_STRING_value(macopts, i); if (pkey_ctrl_string(mac_ctx, macopt) <= 0) { EVP_PKEY_CTX_free(mac_ctx); BIO_printf(bio_err, "MAC parameter error \"%s\"\n", macopt); goto end; } } } sigkey = app_keygen(mac_ctx, mac_name, 0, 0 /* not verbose */); /* Verbose output would make external-tests gost-engine fail */ EVP_PKEY_CTX_free(mac_ctx); if (sigkey == NULL) goto end; } if (hmac_key != NULL) { if (md == NULL) { md = (EVP_MD *)EVP_sha256(); digestname = SN_sha256; } sigkey = EVP_PKEY_new_raw_private_key(EVP_PKEY_HMAC, impl, (unsigned char *)hmac_key, strlen(hmac_key)); if (sigkey == NULL) goto end; } if (sigkey != NULL) { EVP_MD_CTX *mctx = NULL; EVP_PKEY_CTX *pctx = NULL; int res; if (oneshot_sign) { mctx = signctx; } else if (BIO_get_md_ctx(bmd, &mctx) <= 0) { BIO_printf(bio_err, "Error getting context\n"); goto end; } if (do_verify) if (impl == NULL) res = EVP_DigestVerifyInit_ex(mctx, &pctx, digestname, app_get0_libctx(), app_get0_propq(), sigkey, NULL); else res = EVP_DigestVerifyInit(mctx, &pctx, md, impl, sigkey); else if (impl == NULL) res = EVP_DigestSignInit_ex(mctx, &pctx, digestname, app_get0_libctx(), app_get0_propq(), sigkey, NULL); else res = EVP_DigestSignInit(mctx, &pctx, md, impl, sigkey); if (res == 0) { BIO_printf(bio_err, "Error setting context\n"); goto end; } if (sigopts != NULL) { for (i = 0; i < sk_OPENSSL_STRING_num(sigopts); i++) { char *sigopt = sk_OPENSSL_STRING_value(sigopts, i); if (pkey_ctrl_string(pctx, sigopt) <= 0) { BIO_printf(bio_err, "Signature parameter error \"%s\"\n", sigopt); goto end; } } } } /* we use md as a filter, reading from 'in' */ else { EVP_MD_CTX *mctx = NULL; if (oneshot_sign) { BIO_printf(bio_err, "Oneshot algorithms don't use a digest\n"); goto end; } if (BIO_get_md_ctx(bmd, &mctx) <= 0) { BIO_printf(bio_err, "Error getting context\n"); goto end; } if (md == NULL) md = (EVP_MD *)EVP_sha256(); if (!EVP_DigestInit_ex(mctx, md, impl)) { BIO_printf(bio_err, "Error setting digest\n"); goto end; } } if (sigfile != NULL && sigkey != NULL) { BIO *sigbio = BIO_new_file(sigfile, "rb"); if (sigbio == NULL) { BIO_printf(bio_err, "Error opening signature file %s\n", sigfile); goto end; } siglen = EVP_PKEY_get_size(sigkey); sigbuf = app_malloc(siglen, "signature buffer"); siglen = BIO_read(sigbio, sigbuf, siglen); BIO_free(sigbio); if (siglen <= 0) { BIO_printf(bio_err, "Error reading signature file %s\n", sigfile); goto end; } } if (!oneshot_sign) { inp = BIO_push(bmd, in); if (md == NULL) { EVP_MD_CTX *tctx; BIO_get_md_ctx(bmd, &tctx); md = EVP_MD_CTX_get1_md(tctx); } if (md != NULL) md_name = EVP_MD_get0_name(md); } if (xoflen > 0) { if (!EVP_MD_xof(md)) { BIO_printf(bio_err, "Length can only be specified for XOF\n"); goto end; } /* * Signing using XOF is not supported by any algorithms currently since * each algorithm only calls EVP_DigestFinal_ex() in their sign_final * and verify_final methods. */ if (sigkey != NULL) { BIO_printf(bio_err, "Signing key cannot be specified for XOF\n"); goto end; } } if (argc == 0) { BIO_set_fp(in, stdin, BIO_NOCLOSE); if (oneshot_sign) ret = do_fp_oneshot_sign(out, signctx, in, separator, out_bin, sigkey, sigbuf, siglen, NULL, "stdin"); else ret = do_fp(out, buf, inp, separator, out_bin, xoflen, sigkey, sigbuf, siglen, NULL, md_name, "stdin"); } else { const char *sig_name = NULL; if (out_bin == 0) { if (sigkey != NULL) sig_name = EVP_PKEY_get0_type_name(sigkey); } ret = EXIT_SUCCESS; for (i = 0; i < argc; i++) { if (BIO_read_filename(in, argv[i]) <= 0) { perror(argv[i]); ret = EXIT_FAILURE; continue; } else { if (oneshot_sign) { if (do_fp_oneshot_sign(out, signctx, in, separator, out_bin, sigkey, sigbuf, siglen, sig_name, argv[i])) ret = EXIT_FAILURE; } else { if (do_fp(out, buf, inp, separator, out_bin, xoflen, sigkey, sigbuf, siglen, sig_name, md_name, argv[i])) ret = EXIT_FAILURE; } } (void)BIO_reset(bmd); } } end: if (ret != EXIT_SUCCESS) ERR_print_errors(bio_err); OPENSSL_clear_free(buf, BUFSIZE); BIO_free(in); OPENSSL_free(passin); BIO_free_all(out); EVP_MD_free(md); EVP_PKEY_free(sigkey); EVP_MD_CTX_free(signctx); sk_OPENSSL_STRING_free(sigopts); sk_OPENSSL_STRING_free(macopts); OPENSSL_free(sigbuf); BIO_free(bmd); release_engine(e); return ret; } static void show_digests(const OBJ_NAME *name, void *arg) { struct doall_dgst_digests *dec = (struct doall_dgst_digests *)arg; EVP_MD *md = NULL; /* Filter out signed digests (a.k.a signature algorithms) */ if (strstr(name->name, "rsa") != NULL || strstr(name->name, "RSA") != NULL) return; if (!islower((unsigned char)*name->name)) return; /* Filter out message digests that we cannot use */ md = EVP_MD_fetch(app_get0_libctx(), name->name, app_get0_propq()); if (md == NULL) { if (EVP_get_digestbyname(name->name) == NULL) return; } BIO_printf(dec->bio, "-%-25s", name->name); if (++dec->n == 3) { BIO_printf(dec->bio, "\n"); dec->n = 0; } else { BIO_printf(dec->bio, " "); } EVP_MD_free(md); } /* * The newline_escape_filename function performs newline escaping for any * filename that contains a newline. This function also takes a pointer * to backslash. The backslash pointer is a flag to indicating whether a newline * is present in the filename. If a newline is present, the backslash flag is * set and the output format will contain a backslash at the beginning of the * digest output. This output format is to replicate the output format found * in the '*sum' checksum programs. This aims to preserve backward * compatibility. */ static const char *newline_escape_filename(const char *file, int *backslash) { size_t i, e = 0, length = strlen(file), newline_count = 0, mem_len = 0; char *file_cpy = NULL; for (i = 0; i < length; i++) if (file[i] == '\n') newline_count++; mem_len = length + newline_count + 1; file_cpy = app_malloc(mem_len, file); i = 0; while (e < length) { const char c = file[e]; if (c == '\n') { file_cpy[i++] = '\\'; file_cpy[i++] = 'n'; *backslash = 1; } else { file_cpy[i++] = c; } e++; } file_cpy[i] = '\0'; return (const char*)file_cpy; } static void print_out(BIO *out, unsigned char *buf, size_t len, int sep, int binout, const char *sig_name, const char *md_name, const char *file) { int i, backslash = 0; if (binout) { BIO_write(out, buf, len); } else if (sep == 2) { file = newline_escape_filename(file, &backslash); if (backslash == 1) BIO_puts(out, "\\"); for (i = 0; i < (int)len; i++) BIO_printf(out, "%02x", buf[i]); BIO_printf(out, " *%s\n", file); OPENSSL_free((char *)file); } else { if (sig_name != NULL) { BIO_puts(out, sig_name); if (md_name != NULL) BIO_printf(out, "-%s", md_name); BIO_printf(out, "(%s)= ", file); } else if (md_name != NULL) { BIO_printf(out, "%s(%s)= ", md_name, file); } else { BIO_printf(out, "(%s)= ", file); } for (i = 0; i < (int)len; i++) { if (sep && (i != 0)) BIO_printf(out, ":"); BIO_printf(out, "%02x", buf[i]); } BIO_printf(out, "\n"); } } static void print_verify_result(BIO *out, int i) { if (i > 0) BIO_printf(out, "Verified OK\n"); else if (i == 0) BIO_printf(out, "Verification failure\n"); else BIO_printf(bio_err, "Error verifying data\n"); } int do_fp(BIO *out, unsigned char *buf, BIO *bp, int sep, int binout, int xoflen, EVP_PKEY *key, unsigned char *sigin, int siglen, const char *sig_name, const char *md_name, const char *file) { size_t len = BUFSIZE; int i, ret = EXIT_FAILURE; unsigned char *allocated_buf = NULL; while (BIO_pending(bp) || !BIO_eof(bp)) { i = BIO_read(bp, (char *)buf, BUFSIZE); if (i < 0) { BIO_printf(bio_err, "Read error in %s\n", file); goto end; } if (i == 0) break; } if (sigin != NULL) { EVP_MD_CTX *ctx; BIO_get_md_ctx(bp, &ctx); i = EVP_DigestVerifyFinal(ctx, sigin, (unsigned int)siglen); print_verify_result(out, i); if (i > 0) ret = EXIT_SUCCESS; goto end; } if (key != NULL) { EVP_MD_CTX *ctx; size_t tmplen; BIO_get_md_ctx(bp, &ctx); if (!EVP_DigestSignFinal(ctx, NULL, &tmplen)) { BIO_printf(bio_err, "Error getting maximum length of signed data\n"); goto end; } if (tmplen > BUFSIZE) { len = tmplen; allocated_buf = app_malloc(len, "Signature buffer"); buf = allocated_buf; } if (!EVP_DigestSignFinal(ctx, buf, &len)) { BIO_printf(bio_err, "Error signing data\n"); goto end; } } else if (xoflen > 0) { EVP_MD_CTX *ctx; len = xoflen; if (len > BUFSIZE) { allocated_buf = app_malloc(len, "Digest buffer"); buf = allocated_buf; } BIO_get_md_ctx(bp, &ctx); if (!EVP_DigestFinalXOF(ctx, buf, len)) { BIO_printf(bio_err, "Error Digesting Data\n"); goto end; } } else { len = BIO_gets(bp, (char *)buf, BUFSIZE); if ((int)len < 0) goto end; } print_out(out, buf, len, sep, binout, sig_name, md_name, file); ret = EXIT_SUCCESS; end: if (allocated_buf != NULL) OPENSSL_clear_free(allocated_buf, len); return ret; } /* * Some new algorithms only support one shot operations. * For these we need to buffer all input and then do the sign on the * total buffered input. These algorithms set a NULL digest name which is * then used inside EVP_DigestVerify() and EVP_DigestSign(). */ static int do_fp_oneshot_sign(BIO *out, EVP_MD_CTX *ctx, BIO *in, int sep, int binout, EVP_PKEY *key, unsigned char *sigin, int siglen, const char *sig_name, const char *file) { int res, ret = EXIT_FAILURE; size_t len = 0; - int buflen = 0; - int maxlen = 16 * 1024 * 1024; + size_t buflen = 0; + size_t maxlen = 16 * 1024 * 1024; uint8_t *buf = NULL, *sig = NULL; - buflen = bio_to_mem(&buf, maxlen, in); - if (buflen <= 0) { + if (!bio_to_mem(&buf, &buflen, maxlen, in)) { BIO_printf(bio_err, "Read error in %s\n", file); return ret; } if (sigin != NULL) { res = EVP_DigestVerify(ctx, sigin, siglen, buf, buflen); print_verify_result(out, res); if (res > 0) ret = EXIT_SUCCESS; goto end; } if (key != NULL) { if (EVP_DigestSign(ctx, NULL, &len, buf, buflen) != 1) { BIO_printf(bio_err, "Error getting maximum length of signed data\n"); goto end; } sig = app_malloc(len, "Signature buffer"); if (EVP_DigestSign(ctx, sig, &len, buf, buflen) != 1) { BIO_printf(bio_err, "Error signing data\n"); goto end; } print_out(out, sig, len, sep, binout, sig_name, NULL, file); ret = EXIT_SUCCESS; } else { BIO_printf(bio_err, "key must be set for one-shot algorithms\n"); goto end; } end: OPENSSL_free(sig); OPENSSL_clear_free(buf, buflen); return ret; } diff --git a/crypto/openssl/apps/include/apps.h b/crypto/openssl/apps/include/apps.h index 11381ea7da8c..026cbfe7aecd 100644 --- a/crypto/openssl/apps/include/apps.h +++ b/crypto/openssl/apps/include/apps.h @@ -1,356 +1,356 @@ /* * Copyright 1995-2025 The OpenSSL Project Authors. All Rights Reserved. * * Licensed under the Apache License 2.0 (the "License"). You may not use * this file except in compliance with the License. You can obtain a copy * in the file LICENSE in the source distribution or at * https://www.openssl.org/source/license.html */ #ifndef OSSL_APPS_H # define OSSL_APPS_H # include "internal/common.h" /* for HAS_PREFIX */ # include "internal/nelem.h" # include # include # include # ifndef OPENSSL_NO_POSIX_IO # include # include # endif # include # include # include # include # include # include # include # include # include # include # include "apps_ui.h" # include "opt.h" # include "fmt.h" # include "platform.h" # include "engine_loader.h" # include "app_libctx.h" /* * quick macro when you need to pass an unsigned char instead of a char. * this is true for some implementations of the is*() functions, for * example. */ # define _UC(c) ((unsigned char)(c)) void app_RAND_load_conf(CONF *c, const char *section); int app_RAND_write(void); int app_RAND_load(void); extern char *default_config_file; /* may be "" */ extern BIO *bio_in; extern BIO *bio_out; extern BIO *bio_err; extern const unsigned char tls13_aes128gcmsha256_id[]; extern const unsigned char tls13_aes256gcmsha384_id[]; extern BIO_ADDR *ourpeer; BIO *dup_bio_in(int format); BIO *dup_bio_out(int format); BIO *dup_bio_err(int format); BIO *bio_open_owner(const char *filename, int format, int private); BIO *bio_open_default(const char *filename, char mode, int format); BIO *bio_open_default_quiet(const char *filename, char mode, int format); int mem_bio_to_file(BIO *in, const char *filename, int format, int private); char *app_conf_try_string(const CONF *cnf, const char *group, const char *name); int app_conf_try_number(const CONF *conf, const char *group, const char *name, long *result); CONF *app_load_config_bio(BIO *in, const char *filename); # define app_load_config(filename) app_load_config_internal(filename, 0) # define app_load_config_quiet(filename) app_load_config_internal(filename, 1) CONF *app_load_config_internal(const char *filename, int quiet); CONF *app_load_config_verbose(const char *filename, int verbose); int app_load_modules(const CONF *config); CONF *app_load_config_modules(const char *configfile); void unbuffer(FILE *fp); void wait_for_async(SSL *s); # if defined(OPENSSL_SYS_MSDOS) int has_stdin_waiting(void); # endif void corrupt_signature(const ASN1_STRING *signature); /* Helpers for setting X509v3 certificate fields notBefore and notAfter */ int check_cert_time_string(const char *time, const char *desc); int set_cert_times(X509 *x, const char *startdate, const char *enddate, int days, int strict_compare_times); int set_crl_lastupdate(X509_CRL *crl, const char *lastupdate); int set_crl_nextupdate(X509_CRL *crl, const char *nextupdate, long days, long hours, long secs); typedef struct args_st { int size; int argc; char **argv; } ARGS; /* We need both wrap and the "real" function because libcrypto uses both. */ int wrap_password_callback(char *buf, int bufsiz, int verify, void *cb_data); /* progress callback for dsaparam, dhparam, req, genpkey, etc. */ int progress_cb(EVP_PKEY_CTX *ctx); void dump_cert_text(BIO *out, X509 *x); void print_name(BIO *out, const char *title, const X509_NAME *nm); void print_bignum_var(BIO *, const BIGNUM *, const char *, int, unsigned char *); void print_array(BIO *, const char *, int, const unsigned char *); int set_nameopt(const char *arg); unsigned long get_nameopt(void); int set_dateopt(unsigned long *dateopt, const char *arg); int set_cert_ex(unsigned long *flags, const char *arg); int set_name_ex(unsigned long *flags, const char *arg); int set_ext_copy(int *copy_type, const char *arg); int copy_extensions(X509 *x, X509_REQ *req, int copy_type); char *get_passwd(const char *pass, const char *desc); int app_passwd(const char *arg1, const char *arg2, char **pass1, char **pass2); int add_oid_section(CONF *conf); X509_REQ *load_csr(const char *file, int format, const char *desc); X509_REQ *load_csr_autofmt(const char *infile, int format, STACK_OF(OPENSSL_STRING) *vfyopts, const char *desc); X509 *load_cert_pass(const char *uri, int format, int maybe_stdin, const char *pass, const char *desc); # define load_cert(uri, format, desc) load_cert_pass(uri, format, 1, NULL, desc) X509_CRL *load_crl(const char *uri, int format, int maybe_stdin, const char *desc); void cleanse(char *str); void clear_free(char *str); EVP_PKEY *load_key(const char *uri, int format, int maybe_stdin, const char *pass, ENGINE *e, const char *desc); /* first try reading public key, on failure resort to loading private key */ EVP_PKEY *load_pubkey(const char *uri, int format, int maybe_stdin, const char *pass, ENGINE *e, const char *desc); EVP_PKEY *load_keyparams(const char *uri, int format, int maybe_stdin, const char *keytype, const char *desc); EVP_PKEY *load_keyparams_suppress(const char *uri, int format, int maybe_stdin, const char *keytype, const char *desc, int suppress_decode_errors); char *next_item(char *opt); /* in list separated by comma and/or space */ int load_cert_certs(const char *uri, X509 **pcert, STACK_OF(X509) **pcerts, int exclude_http, const char *pass, const char *desc, X509_VERIFY_PARAM *vpm); STACK_OF(X509) *load_certs_multifile(char *files, const char *pass, const char *desc, X509_VERIFY_PARAM *vpm); X509_STORE *load_certstore(char *input, const char *pass, const char *desc, X509_VERIFY_PARAM *vpm); int load_certs(const char *uri, int maybe_stdin, STACK_OF(X509) **certs, const char *pass, const char *desc); int load_crls(const char *uri, STACK_OF(X509_CRL) **crls, const char *pass, const char *desc); int load_key_certs_crls(const char *uri, int format, int maybe_stdin, const char *pass, const char *desc, int quiet, EVP_PKEY **ppkey, EVP_PKEY **ppubkey, EVP_PKEY **pparams, X509 **pcert, STACK_OF(X509) **pcerts, X509_CRL **pcrl, STACK_OF(X509_CRL) **pcrls); X509_STORE *setup_verify(const char *CAfile, int noCAfile, const char *CApath, int noCApath, const char *CAstore, int noCAstore); __owur int ctx_set_verify_locations(SSL_CTX *ctx, const char *CAfile, int noCAfile, const char *CApath, int noCApath, const char *CAstore, int noCAstore); # ifndef OPENSSL_NO_CT /* * Sets the file to load the Certificate Transparency log list from. * If path is NULL, loads from the default file path. * Returns 1 on success, 0 otherwise. */ __owur int ctx_set_ctlog_list_file(SSL_CTX *ctx, const char *path); # endif ENGINE *setup_engine_methods(const char *id, unsigned int methods, int debug); # define setup_engine(e, debug) setup_engine_methods(e, (unsigned int)-1, debug) void release_engine(ENGINE *e); int init_engine(ENGINE *e); int finish_engine(ENGINE *e); char *make_engine_uri(ENGINE *e, const char *key_id, const char *desc); int get_legacy_pkey_id(OSSL_LIB_CTX *libctx, const char *algname, ENGINE *e); const EVP_MD *get_digest_from_engine(const char *name); const EVP_CIPHER *get_cipher_from_engine(const char *name); # ifndef OPENSSL_NO_OCSP OCSP_RESPONSE *process_responder(OCSP_REQUEST *req, const char *host, const char *port, const char *path, const char *proxy, const char *no_proxy, int use_ssl, STACK_OF(CONF_VALUE) *headers, int req_timeout); # endif /* Functions defined in ca.c and also used in ocsp.c */ int unpack_revinfo(ASN1_TIME **prevtm, int *preason, ASN1_OBJECT **phold, ASN1_GENERALIZEDTIME **pinvtm, const char *str); # define DB_type 0 # define DB_exp_date 1 # define DB_rev_date 2 # define DB_serial 3 /* index - unique */ # define DB_file 4 # define DB_name 5 /* index - unique when active and not disabled */ # define DB_NUMBER 6 # define DB_TYPE_REV 'R' /* Revoked */ # define DB_TYPE_EXP 'E' /* Expired */ # define DB_TYPE_VAL 'V' /* Valid ; inserted with: ca ... -valid */ # define DB_TYPE_SUSP 'S' /* Suspended */ typedef struct db_attr_st { int unique_subject; } DB_ATTR; typedef struct ca_db_st { DB_ATTR attributes; TXT_DB *db; char *dbfname; # ifndef OPENSSL_NO_POSIX_IO struct stat dbst; # endif } CA_DB; extern int do_updatedb(CA_DB *db, time_t *now); void app_bail_out(char *fmt, ...); void *app_malloc(size_t sz, const char *what); /* load_serial, save_serial, and rotate_serial are also used for CRL numbers */ BIGNUM *load_serial(const char *serialfile, int *exists, int create, ASN1_INTEGER **retai); int save_serial(const char *serialfile, const char *suffix, const BIGNUM *serial, ASN1_INTEGER **retai); int rotate_serial(const char *serialfile, const char *new_suffix, const char *old_suffix); int rand_serial(BIGNUM *b, ASN1_INTEGER *ai); CA_DB *load_index(const char *dbfile, DB_ATTR *dbattr); int index_index(CA_DB *db); int save_index(const char *dbfile, const char *suffix, CA_DB *db); int rotate_index(const char *dbfile, const char *new_suffix, const char *old_suffix); void free_index(CA_DB *db); # define index_name_cmp_noconst(a, b) \ index_name_cmp((const OPENSSL_CSTRING *)CHECKED_PTR_OF(OPENSSL_STRING, a), \ (const OPENSSL_CSTRING *)CHECKED_PTR_OF(OPENSSL_STRING, b)) int index_name_cmp(const OPENSSL_CSTRING *a, const OPENSSL_CSTRING *b); int parse_yesno(const char *str, int def); X509_NAME *parse_name(const char *str, int chtype, int multirdn, const char *desc); void policies_print(X509_STORE_CTX *ctx); -int bio_to_mem(unsigned char **out, int maxlen, BIO *in); +int bio_to_mem(unsigned char **out, size_t *outlen, size_t maxlen, BIO *in); int pkey_ctrl_string(EVP_PKEY_CTX *ctx, const char *value); int x509_ctrl_string(X509 *x, const char *value); int x509_req_ctrl_string(X509_REQ *x, const char *value); int init_gen_str(EVP_PKEY_CTX **pctx, const char *algname, ENGINE *e, int do_param, OSSL_LIB_CTX *libctx, const char *propq); int cert_matches_key(const X509 *cert, const EVP_PKEY *pkey); int do_X509_sign(X509 *x, int force_v1, EVP_PKEY *pkey, const char *md, STACK_OF(OPENSSL_STRING) *sigopts, X509V3_CTX *ext_ctx); int do_X509_verify(X509 *x, EVP_PKEY *pkey, STACK_OF(OPENSSL_STRING) *vfyopts); int do_X509_REQ_sign(X509_REQ *x, EVP_PKEY *pkey, const char *md, STACK_OF(OPENSSL_STRING) *sigopts); int do_X509_REQ_verify(X509_REQ *x, EVP_PKEY *pkey, STACK_OF(OPENSSL_STRING) *vfyopts); int do_X509_CRL_sign(X509_CRL *x, EVP_PKEY *pkey, const char *md, STACK_OF(OPENSSL_STRING) *sigopts); extern char *psk_key; unsigned char *next_protos_parse(size_t *outlen, const char *in); int check_cert_attributes(BIO *bio, X509 *x, const char *checkhost, const char *checkemail, const char *checkip, int print); void store_setup_crl_download(X509_STORE *st); typedef struct app_http_tls_info_st { const char *server; const char *port; int use_proxy; long timeout; SSL_CTX *ssl_ctx; } APP_HTTP_TLS_INFO; BIO *app_http_tls_cb(BIO *hbio, /* APP_HTTP_TLS_INFO */ void *arg, int connect, int detail); void APP_HTTP_TLS_INFO_free(APP_HTTP_TLS_INFO *info); # ifndef OPENSSL_NO_SOCK ASN1_VALUE *app_http_get_asn1(const char *url, const char *proxy, const char *no_proxy, SSL_CTX *ssl_ctx, const STACK_OF(CONF_VALUE) *headers, long timeout, const char *expected_content_type, const ASN1_ITEM *it); ASN1_VALUE *app_http_post_asn1(const char *host, const char *port, const char *path, const char *proxy, const char *no_proxy, SSL_CTX *ctx, const STACK_OF(CONF_VALUE) *headers, const char *content_type, ASN1_VALUE *req, const ASN1_ITEM *req_it, const char *expected_content_type, long timeout, const ASN1_ITEM *rsp_it); # endif # define EXT_COPY_NONE 0 # define EXT_COPY_ADD 1 # define EXT_COPY_ALL 2 # define NETSCAPE_CERT_HDR "certificate" # define APP_PASS_LEN 1024 /* * IETF RFC 5280 says serial number must be <= 20 bytes. Use 159 bits * so that the first bit will never be one, so that the DER encoding * rules won't force a leading octet. */ # define SERIAL_RAND_BITS 159 int app_isdir(const char *); int app_access(const char *, int flag); int fileno_stdin(void); int fileno_stdout(void); int raw_read_stdin(void *, int); int raw_write_stdout(const void *, int); # define TM_START 0 # define TM_STOP 1 double app_tminterval(int stop, int usertime); void make_uppercase(char *string); typedef struct verify_options_st { int depth; int quiet; int error; int return_error; } VERIFY_CB_ARGS; extern VERIFY_CB_ARGS verify_args; OSSL_PARAM *app_params_new_from_opts(STACK_OF(OPENSSL_STRING) *opts, const OSSL_PARAM *paramdefs); void app_params_free(OSSL_PARAM *params); int app_provider_load(OSSL_LIB_CTX *libctx, const char *provider_name); void app_providers_cleanup(void); EVP_PKEY *app_keygen(EVP_PKEY_CTX *ctx, const char *alg, int bits, int verbose); EVP_PKEY *app_paramgen(EVP_PKEY_CTX *ctx, const char *alg); #endif diff --git a/crypto/openssl/apps/lib/apps.c b/crypto/openssl/apps/lib/apps.c index 1b9c9e3e9a19..6e0f756524d8 100644 --- a/crypto/openssl/apps/lib/apps.c +++ b/crypto/openssl/apps/lib/apps.c @@ -1,3501 +1,3502 @@ /* * Copyright 1995-2025 The OpenSSL Project Authors. All Rights Reserved. * * Licensed under the Apache License 2.0 (the "License"). You may not use * this file except in compliance with the License. You can obtain a copy * in the file LICENSE in the source distribution or at * https://www.openssl.org/source/license.html */ #if !defined(_POSIX_C_SOURCE) && defined(OPENSSL_SYS_VMS) /* * On VMS, you need to define this to get the declaration of fileno(). The * value 2 is to make sure no function defined in POSIX-2 is left undefined. */ # define _POSIX_C_SOURCE 2 #endif #ifndef OPENSSL_NO_ENGINE /* We need to use some deprecated APIs */ # define OPENSSL_SUPPRESS_DEPRECATED # include #endif #include #include #include #include #ifndef OPENSSL_NO_POSIX_IO # include # include #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "s_apps.h" #include "apps.h" #include "internal/sockets.h" /* for openssl_fdset() */ +#include "internal/numbers.h" /* for LONG_MAX */ #include "internal/e_os.h" #ifdef _WIN32 static int WIN32_rename(const char *from, const char *to); # define rename(from, to) WIN32_rename((from), (to)) #endif #if defined(OPENSSL_SYS_WINDOWS) || defined(OPENSSL_SYS_MSDOS) # include #endif #if defined(OPENSSL_SYS_MSDOS) && !defined(_WIN32) || defined(__BORLANDC__) # define _kbhit kbhit #endif static BIO *bio_open_default_(const char *filename, char mode, int format, int quiet); #define PASS_SOURCE_SIZE_MAX 4 DEFINE_STACK_OF(CONF) typedef struct { const char *name; unsigned long flag; unsigned long mask; } NAME_EX_TBL; static int set_table_opts(unsigned long *flags, const char *arg, const NAME_EX_TBL *in_tbl); static int set_multi_opts(unsigned long *flags, const char *arg, const NAME_EX_TBL *in_tbl); int app_init(long mesgwin); #ifndef APP_INIT int app_init(long mesgwin) { return 1; } #endif int ctx_set_verify_locations(SSL_CTX *ctx, const char *CAfile, int noCAfile, const char *CApath, int noCApath, const char *CAstore, int noCAstore) { if (CAfile == NULL && CApath == NULL && CAstore == NULL) { if (!noCAfile && SSL_CTX_set_default_verify_file(ctx) <= 0) return 0; if (!noCApath && SSL_CTX_set_default_verify_dir(ctx) <= 0) return 0; if (!noCAstore && SSL_CTX_set_default_verify_store(ctx) <= 0) return 0; return 1; } if (CAfile != NULL && !SSL_CTX_load_verify_file(ctx, CAfile)) return 0; if (CApath != NULL && !SSL_CTX_load_verify_dir(ctx, CApath)) return 0; if (CAstore != NULL && !SSL_CTX_load_verify_store(ctx, CAstore)) return 0; return 1; } #ifndef OPENSSL_NO_CT int ctx_set_ctlog_list_file(SSL_CTX *ctx, const char *path) { if (path == NULL) return SSL_CTX_set_default_ctlog_list_file(ctx); return SSL_CTX_set_ctlog_list_file(ctx, path); } #endif static unsigned long nmflag = 0; static char nmflag_set = 0; int set_nameopt(const char *arg) { int ret = set_name_ex(&nmflag, arg); if (ret) nmflag_set = 1; return ret; } unsigned long get_nameopt(void) { return nmflag_set ? nmflag : XN_FLAG_SEP_CPLUS_SPC | XN_FLAG_FN_SN | ASN1_STRFLGS_ESC_CTRL | ASN1_STRFLGS_UTF8_CONVERT | ASN1_STRFLGS_DUMP_UNKNOWN | ASN1_STRFLGS_DUMP_DER; } void dump_cert_text(BIO *out, X509 *x) { print_name(out, "subject=", X509_get_subject_name(x)); print_name(out, "issuer=", X509_get_issuer_name(x)); } int wrap_password_callback(char *buf, int bufsiz, int verify, void *userdata) { return password_callback(buf, bufsiz, verify, (PW_CB_DATA *)userdata); } static char *app_get_pass(const char *arg, int keepbio); char *get_passwd(const char *pass, const char *desc) { char *result = NULL; if (desc == NULL) desc = ""; if (!app_passwd(pass, NULL, &result, NULL)) BIO_printf(bio_err, "Error getting password for %s\n", desc); if (pass != NULL && result == NULL) { BIO_printf(bio_err, "Trying plain input string (better precede with 'pass:')\n"); result = OPENSSL_strdup(pass); if (result == NULL) BIO_printf(bio_err, "Out of memory getting password for %s\n", desc); } return result; } int app_passwd(const char *arg1, const char *arg2, char **pass1, char **pass2) { int same = arg1 != NULL && arg2 != NULL && strcmp(arg1, arg2) == 0; if (arg1 != NULL) { *pass1 = app_get_pass(arg1, same); if (*pass1 == NULL) return 0; } else if (pass1 != NULL) { *pass1 = NULL; } if (arg2 != NULL) { *pass2 = app_get_pass(arg2, same ? 2 : 0); if (*pass2 == NULL) return 0; } else if (pass2 != NULL) { *pass2 = NULL; } return 1; } static char *app_get_pass(const char *arg, int keepbio) { static BIO *pwdbio = NULL; char *tmp, tpass[APP_PASS_LEN]; int i; /* PASS_SOURCE_SIZE_MAX = max number of chars before ':' in below strings */ if (CHECK_AND_SKIP_PREFIX(arg, "pass:")) return OPENSSL_strdup(arg); if (CHECK_AND_SKIP_PREFIX(arg, "env:")) { tmp = getenv(arg); if (tmp == NULL) { BIO_printf(bio_err, "No environment variable %s\n", arg); return NULL; } return OPENSSL_strdup(tmp); } if (!keepbio || pwdbio == NULL) { if (CHECK_AND_SKIP_PREFIX(arg, "file:")) { pwdbio = BIO_new_file(arg, "r"); if (pwdbio == NULL) { BIO_printf(bio_err, "Can't open file %s\n", arg); return NULL; } #if !defined(_WIN32) /* * Under _WIN32, which covers even Win64 and CE, file * descriptors referenced by BIO_s_fd are not inherited * by child process and therefore below is not an option. * It could have been an option if bss_fd.c was operating * on real Windows descriptors, such as those obtained * with CreateFile. */ } else if (CHECK_AND_SKIP_PREFIX(arg, "fd:")) { BIO *btmp; i = atoi(arg); if (i >= 0) pwdbio = BIO_new_fd(i, BIO_NOCLOSE); if ((i < 0) || pwdbio == NULL) { BIO_printf(bio_err, "Can't access file descriptor %s\n", arg); return NULL; } /* * Can't do BIO_gets on an fd BIO so add a buffering BIO */ btmp = BIO_new(BIO_f_buffer()); if (btmp == NULL) { BIO_free_all(pwdbio); pwdbio = NULL; BIO_printf(bio_err, "Out of memory\n"); return NULL; } pwdbio = BIO_push(btmp, pwdbio); #endif } else if (strcmp(arg, "stdin") == 0) { unbuffer(stdin); pwdbio = dup_bio_in(FORMAT_TEXT); if (pwdbio == NULL) { BIO_printf(bio_err, "Can't open BIO for stdin\n"); return NULL; } } else { /* argument syntax error; do not reveal too much about arg */ tmp = strchr(arg, ':'); if (tmp == NULL || tmp - arg > PASS_SOURCE_SIZE_MAX) BIO_printf(bio_err, "Invalid password argument, missing ':' within the first %d chars\n", PASS_SOURCE_SIZE_MAX + 1); else BIO_printf(bio_err, "Invalid password argument, starting with \"%.*s\"\n", (int)(tmp - arg + 1), arg); return NULL; } } i = BIO_gets(pwdbio, tpass, APP_PASS_LEN); if (keepbio != 1) { BIO_free_all(pwdbio); pwdbio = NULL; } if (i <= 0) { BIO_printf(bio_err, "Error reading password from BIO\n"); return NULL; } tmp = strchr(tpass, '\n'); if (tmp != NULL) *tmp = 0; return OPENSSL_strdup(tpass); } char *app_conf_try_string(const CONF *conf, const char *group, const char *name) { char *res; ERR_set_mark(); res = NCONF_get_string(conf, group, name); if (res == NULL) ERR_pop_to_mark(); else ERR_clear_last_mark(); return res; } int app_conf_try_number(const CONF *conf, const char *group, const char *name, long *result) { int ok; ERR_set_mark(); ok = NCONF_get_number(conf, group, name, result); if (!ok) ERR_pop_to_mark(); else ERR_clear_last_mark(); return ok; } CONF *app_load_config_bio(BIO *in, const char *filename) { long errorline = -1; CONF *conf; int i; conf = NCONF_new_ex(app_get0_libctx(), NULL); i = NCONF_load_bio(conf, in, &errorline); if (i > 0) return conf; if (errorline <= 0) { BIO_printf(bio_err, "%s: Can't load ", opt_getprog()); } else { BIO_printf(bio_err, "%s: Error on line %ld of ", opt_getprog(), errorline); } if (filename != NULL) BIO_printf(bio_err, "config file \"%s\"\n", filename); else BIO_printf(bio_err, "config input"); NCONF_free(conf); return NULL; } CONF *app_load_config_verbose(const char *filename, int verbose) { if (verbose) { if (*filename == '\0') BIO_printf(bio_err, "No configuration used\n"); else BIO_printf(bio_err, "Using configuration from %s\n", filename); } return app_load_config_internal(filename, 0); } CONF *app_load_config_internal(const char *filename, int quiet) { BIO *in; CONF *conf; if (filename == NULL || *filename != '\0') { if ((in = bio_open_default_(filename, 'r', FORMAT_TEXT, quiet)) == NULL) return NULL; conf = app_load_config_bio(in, filename); BIO_free(in); } else { /* Return empty config if filename is empty string. */ conf = NCONF_new_ex(app_get0_libctx(), NULL); } return conf; } int app_load_modules(const CONF *config) { CONF *to_free = NULL; if (config == NULL) config = to_free = app_load_config_quiet(default_config_file); if (config == NULL) return 1; if (CONF_modules_load(config, NULL, 0) <= 0) { BIO_printf(bio_err, "Error configuring OpenSSL modules\n"); ERR_print_errors(bio_err); NCONF_free(to_free); return 0; } NCONF_free(to_free); return 1; } int add_oid_section(CONF *conf) { char *p; STACK_OF(CONF_VALUE) *sktmp; CONF_VALUE *cnf; int i; if ((p = app_conf_try_string(conf, NULL, "oid_section")) == NULL) return 1; if ((sktmp = NCONF_get_section(conf, p)) == NULL) { BIO_printf(bio_err, "problem loading oid section %s\n", p); return 0; } for (i = 0; i < sk_CONF_VALUE_num(sktmp); i++) { cnf = sk_CONF_VALUE_value(sktmp, i); if (OBJ_create(cnf->value, cnf->name, cnf->name) == NID_undef) { BIO_printf(bio_err, "problem creating object %s=%s\n", cnf->name, cnf->value); return 0; } } return 1; } CONF *app_load_config_modules(const char *configfile) { CONF *conf = NULL; if (configfile != NULL) { if ((conf = app_load_config_verbose(configfile, 1)) == NULL) return NULL; if (configfile != default_config_file && !app_load_modules(conf)) { NCONF_free(conf); conf = NULL; } } return conf; } #define IS_HTTP(uri) ((uri) != NULL && HAS_PREFIX(uri, OSSL_HTTP_PREFIX)) #define IS_HTTPS(uri) ((uri) != NULL && HAS_PREFIX(uri, OSSL_HTTPS_PREFIX)) X509 *load_cert_pass(const char *uri, int format, int maybe_stdin, const char *pass, const char *desc) { X509 *cert = NULL; if (desc == NULL) desc = "certificate"; if (IS_HTTPS(uri)) { BIO_printf(bio_err, "Loading %s over HTTPS is unsupported\n", desc); } else if (IS_HTTP(uri)) { cert = X509_load_http(uri, NULL, NULL, 0 /* timeout */); if (cert == NULL) { ERR_print_errors(bio_err); BIO_printf(bio_err, "Unable to load %s from %s\n", desc, uri); } } else { (void)load_key_certs_crls(uri, format, maybe_stdin, pass, desc, 0, NULL, NULL, NULL, &cert, NULL, NULL, NULL); } return cert; } X509_CRL *load_crl(const char *uri, int format, int maybe_stdin, const char *desc) { X509_CRL *crl = NULL; if (desc == NULL) desc = "CRL"; if (IS_HTTPS(uri)) { BIO_printf(bio_err, "Loading %s over HTTPS is unsupported\n", desc); } else if (IS_HTTP(uri)) { crl = X509_CRL_load_http(uri, NULL, NULL, 0 /* timeout */); if (crl == NULL) { ERR_print_errors(bio_err); BIO_printf(bio_err, "Unable to load %s from %s\n", desc, uri); } } else { (void)load_key_certs_crls(uri, format, maybe_stdin, NULL, desc, 0, NULL, NULL, NULL, NULL, NULL, &crl, NULL); } return crl; } /* Could be simplified if OSSL_STORE supported CSRs, see FR #15725 */ X509_REQ *load_csr(const char *file, int format, const char *desc) { X509_REQ *req = NULL; BIO *in; if (format == FORMAT_UNDEF) format = FORMAT_PEM; in = bio_open_default(file, 'r', format); if (in == NULL) goto end; if (format == FORMAT_ASN1) req = d2i_X509_REQ_bio(in, NULL); else if (format == FORMAT_PEM) req = PEM_read_bio_X509_REQ(in, NULL, NULL, NULL); else print_format_error(format, OPT_FMT_PEMDER); end: if (req == NULL) { ERR_print_errors(bio_err); if (desc != NULL) BIO_printf(bio_err, "Unable to load %s\n", desc); } BIO_free(in); return req; } /* Better extend OSSL_STORE to support CSRs, see FR #15725 */ X509_REQ *load_csr_autofmt(const char *infile, int format, STACK_OF(OPENSSL_STRING) *vfyopts, const char *desc) { X509_REQ *csr; if (format != FORMAT_UNDEF) { csr = load_csr(infile, format, desc); } else { /* try PEM, then DER */ BIO *bio_bak = bio_err; bio_err = NULL; /* do not show errors on more than one try */ csr = load_csr(infile, FORMAT_PEM, NULL /* desc */); bio_err = bio_bak; if (csr == NULL) { ERR_clear_error(); csr = load_csr(infile, FORMAT_ASN1, NULL /* desc */); } if (csr == NULL) { BIO_printf(bio_err, "error: unable to load %s from file '%s'\n", desc, infile); } } if (csr != NULL) { EVP_PKEY *pkey = X509_REQ_get0_pubkey(csr); int ret = do_X509_REQ_verify(csr, pkey, vfyopts); if (pkey == NULL || ret < 0) BIO_puts(bio_err, "Warning: error while verifying CSR self-signature\n"); else if (ret == 0) BIO_puts(bio_err, "Warning: CSR self-signature does not match the contents\n"); return csr; } return csr; } void cleanse(char *str) { if (str != NULL) OPENSSL_cleanse(str, strlen(str)); } void clear_free(char *str) { if (str != NULL) OPENSSL_clear_free(str, strlen(str)); } EVP_PKEY *load_key(const char *uri, int format, int may_stdin, const char *pass, ENGINE *e, const char *desc) { EVP_PKEY *pkey = NULL; char *allocated_uri = NULL; if (desc == NULL) desc = "private key"; if (format == FORMAT_ENGINE) uri = allocated_uri = make_engine_uri(e, uri, desc); (void)load_key_certs_crls(uri, format, may_stdin, pass, desc, 0, &pkey, NULL, NULL, NULL, NULL, NULL, NULL); OPENSSL_free(allocated_uri); return pkey; } /* first try reading public key, on failure resort to loading private key */ EVP_PKEY *load_pubkey(const char *uri, int format, int maybe_stdin, const char *pass, ENGINE *e, const char *desc) { EVP_PKEY *pkey = NULL; char *allocated_uri = NULL; if (desc == NULL) desc = "public key"; if (format == FORMAT_ENGINE) uri = allocated_uri = make_engine_uri(e, uri, desc); (void)load_key_certs_crls(uri, format, maybe_stdin, pass, desc, 1, NULL, &pkey, NULL, NULL, NULL, NULL, NULL); if (pkey == NULL) (void)load_key_certs_crls(uri, format, maybe_stdin, pass, desc, 0, &pkey, NULL, NULL, NULL, NULL, NULL, NULL); OPENSSL_free(allocated_uri); return pkey; } EVP_PKEY *load_keyparams_suppress(const char *uri, int format, int maybe_stdin, const char *keytype, const char *desc, int suppress_decode_errors) { EVP_PKEY *params = NULL; if (desc == NULL) desc = "key parameters"; (void)load_key_certs_crls(uri, format, maybe_stdin, NULL, desc, suppress_decode_errors, NULL, NULL, ¶ms, NULL, NULL, NULL, NULL); if (params != NULL && keytype != NULL && !EVP_PKEY_is_a(params, keytype)) { ERR_print_errors(bio_err); BIO_printf(bio_err, "Unable to load %s from %s (unexpected parameters type)\n", desc, uri); EVP_PKEY_free(params); params = NULL; } return params; } EVP_PKEY *load_keyparams(const char *uri, int format, int maybe_stdin, const char *keytype, const char *desc) { return load_keyparams_suppress(uri, format, maybe_stdin, keytype, desc, 0); } void app_bail_out(char *fmt, ...) { va_list args; va_start(args, fmt); BIO_vprintf(bio_err, fmt, args); va_end(args); ERR_print_errors(bio_err); exit(EXIT_FAILURE); } void *app_malloc(size_t sz, const char *what) { void *vp = OPENSSL_malloc(sz); if (vp == NULL) app_bail_out("%s: Could not allocate %zu bytes for %s\n", opt_getprog(), sz, what); return vp; } char *next_item(char *opt) /* in list separated by comma and/or space */ { /* advance to separator (comma or whitespace), if any */ while (*opt != ',' && !isspace(_UC(*opt)) && *opt != '\0') opt++; if (*opt != '\0') { /* terminate current item */ *opt++ = '\0'; /* skip over any whitespace after separator */ while (isspace(_UC(*opt))) opt++; } return *opt == '\0' ? NULL : opt; /* NULL indicates end of input */ } static void warn_cert_msg(const char *uri, X509 *cert, const char *msg) { char *subj = X509_NAME_oneline(X509_get_subject_name(cert), NULL, 0); BIO_printf(bio_err, "Warning: certificate from '%s' with subject '%s' %s\n", uri, subj, msg); OPENSSL_free(subj); } static void warn_cert(const char *uri, X509 *cert, int warn_EE, X509_VERIFY_PARAM *vpm) { uint32_t ex_flags = X509_get_extension_flags(cert); int res = X509_cmp_timeframe(vpm, X509_get0_notBefore(cert), X509_get0_notAfter(cert)); if (res != 0) warn_cert_msg(uri, cert, res > 0 ? "has expired" : "not yet valid"); if (warn_EE && (ex_flags & EXFLAG_V1) == 0 && (ex_flags & EXFLAG_CA) == 0) warn_cert_msg(uri, cert, "is not a CA cert"); } static void warn_certs(const char *uri, STACK_OF(X509) *certs, int warn_EE, X509_VERIFY_PARAM *vpm) { int i; for (i = 0; i < sk_X509_num(certs); i++) warn_cert(uri, sk_X509_value(certs, i), warn_EE, vpm); } int load_cert_certs(const char *uri, X509 **pcert, STACK_OF(X509) **pcerts, int exclude_http, const char *pass, const char *desc, X509_VERIFY_PARAM *vpm) { int ret = 0; char *pass_string; if (desc == NULL) desc = pcerts == NULL ? "certificate" : "certificates"; if (exclude_http && (HAS_CASE_PREFIX(uri, "http://") || HAS_CASE_PREFIX(uri, "https://"))) { BIO_printf(bio_err, "error: HTTP retrieval not allowed for %s\n", desc); return ret; } pass_string = get_passwd(pass, desc); ret = load_key_certs_crls(uri, FORMAT_UNDEF, 0, pass_string, desc, 0, NULL, NULL, NULL, pcert, pcerts, NULL, NULL); clear_free(pass_string); if (ret) { if (pcert != NULL) warn_cert(uri, *pcert, 0, vpm); if (pcerts != NULL) warn_certs(uri, *pcerts, 1, vpm); } else { if (pcerts != NULL) { OSSL_STACK_OF_X509_free(*pcerts); *pcerts = NULL; } } return ret; } STACK_OF(X509) *load_certs_multifile(char *files, const char *pass, const char *desc, X509_VERIFY_PARAM *vpm) { STACK_OF(X509) *certs = NULL; STACK_OF(X509) *result = sk_X509_new_null(); if (files == NULL) goto err; if (result == NULL) goto oom; while (files != NULL) { char *next = next_item(files); if (!load_cert_certs(files, NULL, &certs, 0, pass, desc, vpm)) goto err; if (!X509_add_certs(result, certs, X509_ADD_FLAG_UP_REF | X509_ADD_FLAG_NO_DUP)) goto oom; OSSL_STACK_OF_X509_free(certs); certs = NULL; files = next; } return result; oom: BIO_printf(bio_err, "out of memory\n"); err: OSSL_STACK_OF_X509_free(certs); OSSL_STACK_OF_X509_free(result); return NULL; } static X509_STORE *sk_X509_to_store(X509_STORE *store /* may be NULL */, const STACK_OF(X509) *certs /* may NULL */) { int i; if (store == NULL) store = X509_STORE_new(); if (store == NULL) return NULL; for (i = 0; i < sk_X509_num(certs); i++) { if (!X509_STORE_add_cert(store, sk_X509_value(certs, i))) { X509_STORE_free(store); return NULL; } } return store; } /* * Create cert store structure with certificates read from given file(s). * Returns pointer to created X509_STORE on success, NULL on error. */ X509_STORE *load_certstore(char *input, const char *pass, const char *desc, X509_VERIFY_PARAM *vpm) { X509_STORE *store = NULL; STACK_OF(X509) *certs = NULL; while (input != NULL) { char *next = next_item(input); int ok; if (!load_cert_certs(input, NULL, &certs, 1, pass, desc, vpm)) { X509_STORE_free(store); return NULL; } ok = (store = sk_X509_to_store(store, certs)) != NULL; OSSL_STACK_OF_X509_free(certs); certs = NULL; if (!ok) return NULL; input = next; } return store; } /* * Initialize or extend, if *certs != NULL, a certificate stack. * The caller is responsible for freeing *certs if its value is left not NULL. */ int load_certs(const char *uri, int maybe_stdin, STACK_OF(X509) **certs, const char *pass, const char *desc) { int ret, was_NULL = *certs == NULL; if (desc == NULL) desc = "certificates"; ret = load_key_certs_crls(uri, FORMAT_UNDEF, maybe_stdin, pass, desc, 0, NULL, NULL, NULL, NULL, certs, NULL, NULL); if (!ret && was_NULL) { OSSL_STACK_OF_X509_free(*certs); *certs = NULL; } return ret; } /* * Initialize or extend, if *crls != NULL, a certificate stack. * The caller is responsible for freeing *crls if its value is left not NULL. */ int load_crls(const char *uri, STACK_OF(X509_CRL) **crls, const char *pass, const char *desc) { int ret, was_NULL = *crls == NULL; if (desc == NULL) desc = "CRLs"; ret = load_key_certs_crls(uri, FORMAT_UNDEF, 0, pass, desc, 0, NULL, NULL, NULL, NULL, NULL, NULL, crls); if (!ret && was_NULL) { sk_X509_CRL_pop_free(*crls, X509_CRL_free); *crls = NULL; } return ret; } static const char *format2string(int format) { switch (format) { case FORMAT_PEM: return "PEM"; case FORMAT_ASN1: return "DER"; case FORMAT_PVK: return "PVK"; case FORMAT_MSBLOB: return "MSBLOB"; } return NULL; } /* Set type expectation, but set to 0 if objects of multiple types expected. */ #define SET_EXPECT(val) \ (expect = expect < 0 ? (val) : (expect == (val) ? (val) : 0)) #define SET_EXPECT1(pvar, val) \ if ((pvar) != NULL) { \ *(pvar) = NULL; \ SET_EXPECT(val); \ } /* Provide (error msg) text for some of the credential types to be loaded. */ #define FAIL_NAME \ (ppkey != NULL ? "private key" : ppubkey != NULL ? "public key" : \ pparams != NULL ? "key parameters" : \ pcert != NULL ? "certificate" : pcerts != NULL ? "certificates" : \ pcrl != NULL ? "CRL" : pcrls != NULL ? "CRLs" : NULL) /* * Load those types of credentials for which the result pointer is not NULL. * Reads from stdin if 'uri' is NULL and 'maybe_stdin' is nonzero. * 'format' parameter may be FORMAT_PEM, FORMAT_ASN1, or 0 for no hint. * desc may contain more detail on the credential(s) to be loaded for error msg * For non-NULL ppkey, pcert, and pcrl the first suitable value found is loaded. * If pcerts is non-NULL and *pcerts == NULL then a new cert list is allocated. * If pcerts is non-NULL then all available certificates are appended to *pcerts * except any certificate assigned to *pcert. * If pcrls is non-NULL and *pcrls == NULL then a new list of CRLs is allocated. * If pcrls is non-NULL then all available CRLs are appended to *pcerts * except any CRL assigned to *pcrl. * In any case (also on error) the caller is responsible for freeing all members * of *pcerts and *pcrls (as far as they are not NULL). */ int load_key_certs_crls(const char *uri, int format, int maybe_stdin, const char *pass, const char *desc, int quiet, EVP_PKEY **ppkey, EVP_PKEY **ppubkey, EVP_PKEY **pparams, X509 **pcert, STACK_OF(X509) **pcerts, X509_CRL **pcrl, STACK_OF(X509_CRL) **pcrls) { PW_CB_DATA uidata; OSSL_STORE_CTX *ctx = NULL; OSSL_LIB_CTX *libctx = app_get0_libctx(); const char *propq = app_get0_propq(); int ncerts = 0, ncrls = 0, expect = -1; const char *failed = FAIL_NAME; const char *input_type; OSSL_PARAM itp[2]; const OSSL_PARAM *params = NULL; /* 'failed' describes type of credential to load for potential error msg */ if (failed == NULL) { if (!quiet) BIO_printf(bio_err, "Internal error: nothing was requested to load from %s\n", uri != NULL ? uri : ""); return 0; } /* suppress any extraneous errors left over from failed parse attempts */ ERR_set_mark(); SET_EXPECT1(ppkey, OSSL_STORE_INFO_PKEY); SET_EXPECT1(ppubkey, OSSL_STORE_INFO_PUBKEY); SET_EXPECT1(pparams, OSSL_STORE_INFO_PARAMS); SET_EXPECT1(pcert, OSSL_STORE_INFO_CERT); /* * Up to here, the follwing holds. * If just one of the ppkey, ppubkey, pparams, and pcert function parameters * is nonzero, expect > 0 indicates which type of credential is expected. * If expect == 0, more than one of them is nonzero (multiple types expected). */ if (pcerts != NULL) { if (*pcerts == NULL && (*pcerts = sk_X509_new_null()) == NULL) { if (!quiet) BIO_printf(bio_err, "Out of memory loading"); goto end; } /* * Adapt the 'expect' variable: * set to OSSL_STORE_INFO_CERT if no other type is expected so far, * otherwise set to 0 (indicating that multiple types are expected). */ SET_EXPECT(OSSL_STORE_INFO_CERT); } SET_EXPECT1(pcrl, OSSL_STORE_INFO_CRL); if (pcrls != NULL) { if (*pcrls == NULL && (*pcrls = sk_X509_CRL_new_null()) == NULL) { if (!quiet) BIO_printf(bio_err, "Out of memory loading"); goto end; } /* * Adapt the 'expect' variable: * set to OSSL_STORE_INFO_CRL if no other type is expected so far, * otherwise set to 0 (indicating that multiple types are expected). */ SET_EXPECT(OSSL_STORE_INFO_CRL); } uidata.password = pass; uidata.prompt_info = uri; if ((input_type = format2string(format)) != NULL) { itp[0] = OSSL_PARAM_construct_utf8_string(OSSL_STORE_PARAM_INPUT_TYPE, (char *)input_type, 0); itp[1] = OSSL_PARAM_construct_end(); params = itp; } if (uri == NULL) { BIO *bio; if (!maybe_stdin) { if (!quiet) BIO_printf(bio_err, "No filename or uri specified for loading\n"); goto end; } uri = ""; unbuffer(stdin); bio = BIO_new_fp(stdin, 0); if (bio != NULL) { ctx = OSSL_STORE_attach(bio, "file", libctx, propq, get_ui_method(), &uidata, params, NULL, NULL); BIO_free(bio); } } else { ctx = OSSL_STORE_open_ex(uri, libctx, propq, get_ui_method(), &uidata, params, NULL, NULL); } if (ctx == NULL) { if (!quiet) BIO_printf(bio_err, "Could not open file or uri for loading"); goto end; } /* expect == 0 means here multiple types of credentials are to be loaded */ if (expect > 0 && !OSSL_STORE_expect(ctx, expect)) { if (!quiet) BIO_printf(bio_err, "Internal error trying to load"); goto end; } failed = NULL; /* from here, failed != NULL only if actually an error has been detected */ while ((ppkey != NULL || ppubkey != NULL || pparams != NULL || pcert != NULL || pcerts != NULL || pcrl != NULL || pcrls != NULL) && !OSSL_STORE_eof(ctx)) { OSSL_STORE_INFO *info = OSSL_STORE_load(ctx); int type, ok = 1; /* * This can happen (for example) if we attempt to load a file with * multiple different types of things in it - but the thing we just * tried to load wasn't one of the ones we wanted, e.g. if we're trying * to load a certificate but the file has both the private key and the * certificate in it. We just retry until eof. */ if (info == NULL) { continue; } type = OSSL_STORE_INFO_get_type(info); switch (type) { case OSSL_STORE_INFO_PKEY: if (ppkey != NULL) { ok = (*ppkey = OSSL_STORE_INFO_get1_PKEY(info)) != NULL; if (ok) ppkey = NULL; break; } /* * An EVP_PKEY with private parts also holds the public parts, * so if the caller asked for a public key, and we got a private * key, we can still pass it back. */ /* fall through */ case OSSL_STORE_INFO_PUBKEY: if (ppubkey != NULL) { ok = (*ppubkey = OSSL_STORE_INFO_get1_PUBKEY(info)) != NULL; if (ok) ppubkey = NULL; } break; case OSSL_STORE_INFO_PARAMS: if (pparams != NULL) { ok = (*pparams = OSSL_STORE_INFO_get1_PARAMS(info)) != NULL; if (ok) pparams = NULL; } break; case OSSL_STORE_INFO_CERT: if (pcert != NULL) { ok = (*pcert = OSSL_STORE_INFO_get1_CERT(info)) != NULL; if (ok) pcert = NULL; } else if (pcerts != NULL) { ok = X509_add_cert(*pcerts, OSSL_STORE_INFO_get1_CERT(info), X509_ADD_FLAG_DEFAULT); } ncerts += ok; break; case OSSL_STORE_INFO_CRL: if (pcrl != NULL) { ok = (*pcrl = OSSL_STORE_INFO_get1_CRL(info)) != NULL; if (ok) pcrl = NULL; } else if (pcrls != NULL) { ok = sk_X509_CRL_push(*pcrls, OSSL_STORE_INFO_get1_CRL(info)); } ncrls += ok; break; default: /* skip any other type; ok stays == 1 */ break; } OSSL_STORE_INFO_free(info); if (!ok) { failed = OSSL_STORE_INFO_type_string(type); if (!quiet) BIO_printf(bio_err, "Error reading"); break; } } end: OSSL_STORE_close(ctx); /* see if any of the requested types of credentials was not found */ if (failed == NULL) { if (ncerts > 0) pcerts = NULL; if (ncrls > 0) pcrls = NULL; failed = FAIL_NAME; if (failed != NULL && !quiet) BIO_printf(bio_err, "Could not find"); } if (failed != NULL && !quiet) { unsigned long err = ERR_peek_last_error(); /* continue the error message with the type of credential affected */ if (desc != NULL && strstr(desc, failed) != NULL) { BIO_printf(bio_err, " %s", desc); } else { BIO_printf(bio_err, " %s", failed); if (desc != NULL) BIO_printf(bio_err, " of %s", desc); } if (uri != NULL) BIO_printf(bio_err, " from %s", uri); if (ERR_SYSTEM_ERROR(err)) { /* provide more readable diagnostic output */ BIO_printf(bio_err, ": %s", strerror(ERR_GET_REASON(err))); ERR_pop_to_mark(); ERR_set_mark(); } BIO_printf(bio_err, "\n"); ERR_print_errors(bio_err); } if (quiet || failed == NULL) /* clear any suppressed or spurious errors */ ERR_pop_to_mark(); else ERR_clear_last_mark(); return failed == NULL; } #define X509V3_EXT_UNKNOWN_MASK (0xfL << 16) #define X509V3_EXT_DEFAULT 0 /* Return error for unknown exts */ #define X509V3_EXT_ERROR_UNKNOWN (1L << 16) /* Print error for unknown exts */ #define X509V3_EXT_PARSE_UNKNOWN (2L << 16) /* ASN1 parse unknown extensions */ #define X509V3_EXT_DUMP_UNKNOWN (3L << 16) /* BIO_dump unknown extensions */ #define X509_FLAG_CA (X509_FLAG_NO_ISSUER | X509_FLAG_NO_PUBKEY | \ X509_FLAG_NO_HEADER | X509_FLAG_NO_VERSION) int set_cert_ex(unsigned long *flags, const char *arg) { static const NAME_EX_TBL cert_tbl[] = { {"compatible", X509_FLAG_COMPAT, 0xffffffffl}, {"ca_default", X509_FLAG_CA, 0xffffffffl}, {"no_header", X509_FLAG_NO_HEADER, 0}, {"no_version", X509_FLAG_NO_VERSION, 0}, {"no_serial", X509_FLAG_NO_SERIAL, 0}, {"no_signame", X509_FLAG_NO_SIGNAME, 0}, {"no_validity", X509_FLAG_NO_VALIDITY, 0}, {"no_subject", X509_FLAG_NO_SUBJECT, 0}, {"no_issuer", X509_FLAG_NO_ISSUER, 0}, {"no_pubkey", X509_FLAG_NO_PUBKEY, 0}, {"no_extensions", X509_FLAG_NO_EXTENSIONS, 0}, {"no_sigdump", X509_FLAG_NO_SIGDUMP, 0}, {"no_aux", X509_FLAG_NO_AUX, 0}, {"no_attributes", X509_FLAG_NO_ATTRIBUTES, 0}, {"ext_default", X509V3_EXT_DEFAULT, X509V3_EXT_UNKNOWN_MASK}, {"ext_error", X509V3_EXT_ERROR_UNKNOWN, X509V3_EXT_UNKNOWN_MASK}, {"ext_parse", X509V3_EXT_PARSE_UNKNOWN, X509V3_EXT_UNKNOWN_MASK}, {"ext_dump", X509V3_EXT_DUMP_UNKNOWN, X509V3_EXT_UNKNOWN_MASK}, {NULL, 0, 0} }; return set_multi_opts(flags, arg, cert_tbl); } int set_name_ex(unsigned long *flags, const char *arg) { static const NAME_EX_TBL ex_tbl[] = { {"esc_2253", ASN1_STRFLGS_ESC_2253, 0}, {"esc_2254", ASN1_STRFLGS_ESC_2254, 0}, {"esc_ctrl", ASN1_STRFLGS_ESC_CTRL, 0}, {"esc_msb", ASN1_STRFLGS_ESC_MSB, 0}, {"use_quote", ASN1_STRFLGS_ESC_QUOTE, 0}, {"utf8", ASN1_STRFLGS_UTF8_CONVERT, 0}, {"ignore_type", ASN1_STRFLGS_IGNORE_TYPE, 0}, {"show_type", ASN1_STRFLGS_SHOW_TYPE, 0}, {"dump_all", ASN1_STRFLGS_DUMP_ALL, 0}, {"dump_nostr", ASN1_STRFLGS_DUMP_UNKNOWN, 0}, {"dump_der", ASN1_STRFLGS_DUMP_DER, 0}, {"compat", XN_FLAG_COMPAT, 0xffffffffL}, {"sep_comma_plus", XN_FLAG_SEP_COMMA_PLUS, XN_FLAG_SEP_MASK}, {"sep_comma_plus_space", XN_FLAG_SEP_CPLUS_SPC, XN_FLAG_SEP_MASK}, {"sep_semi_plus_space", XN_FLAG_SEP_SPLUS_SPC, XN_FLAG_SEP_MASK}, {"sep_multiline", XN_FLAG_SEP_MULTILINE, XN_FLAG_SEP_MASK}, {"dn_rev", XN_FLAG_DN_REV, 0}, {"nofname", XN_FLAG_FN_NONE, XN_FLAG_FN_MASK}, {"sname", XN_FLAG_FN_SN, XN_FLAG_FN_MASK}, {"lname", XN_FLAG_FN_LN, XN_FLAG_FN_MASK}, {"align", XN_FLAG_FN_ALIGN, 0}, {"oid", XN_FLAG_FN_OID, XN_FLAG_FN_MASK}, {"space_eq", XN_FLAG_SPC_EQ, 0}, {"dump_unknown", XN_FLAG_DUMP_UNKNOWN_FIELDS, 0}, {"RFC2253", XN_FLAG_RFC2253, 0xffffffffL}, {"oneline", XN_FLAG_ONELINE, 0xffffffffL}, {"multiline", XN_FLAG_MULTILINE, 0xffffffffL}, {"ca_default", XN_FLAG_MULTILINE, 0xffffffffL}, {NULL, 0, 0} }; if (set_multi_opts(flags, arg, ex_tbl) == 0) return 0; if (*flags != XN_FLAG_COMPAT && (*flags & XN_FLAG_SEP_MASK) == 0) *flags |= XN_FLAG_SEP_CPLUS_SPC; return 1; } int set_dateopt(unsigned long *dateopt, const char *arg) { if (OPENSSL_strcasecmp(arg, "rfc_822") == 0) *dateopt = ASN1_DTFLGS_RFC822; else if (OPENSSL_strcasecmp(arg, "iso_8601") == 0) *dateopt = ASN1_DTFLGS_ISO8601; else return 0; return 1; } int set_ext_copy(int *copy_type, const char *arg) { if (OPENSSL_strcasecmp(arg, "none") == 0) *copy_type = EXT_COPY_NONE; else if (OPENSSL_strcasecmp(arg, "copy") == 0) *copy_type = EXT_COPY_ADD; else if (OPENSSL_strcasecmp(arg, "copyall") == 0) *copy_type = EXT_COPY_ALL; else return 0; return 1; } int copy_extensions(X509 *x, X509_REQ *req, int copy_type) { STACK_OF(X509_EXTENSION) *exts; int i, ret = 0; if (x == NULL || req == NULL) return 0; if (copy_type == EXT_COPY_NONE) return 1; exts = X509_REQ_get_extensions(req); for (i = 0; i < sk_X509_EXTENSION_num(exts); i++) { X509_EXTENSION *ext = sk_X509_EXTENSION_value(exts, i); ASN1_OBJECT *obj = X509_EXTENSION_get_object(ext); int idx = X509_get_ext_by_OBJ(x, obj, -1); /* Does extension exist in target? */ if (idx != -1) { /* If normal copy don't override existing extension */ if (copy_type == EXT_COPY_ADD) continue; /* Delete all extensions of same type */ do { X509_EXTENSION_free(X509_delete_ext(x, idx)); idx = X509_get_ext_by_OBJ(x, obj, -1); } while (idx != -1); } if (!X509_add_ext(x, ext, -1)) goto end; } ret = 1; end: sk_X509_EXTENSION_pop_free(exts, X509_EXTENSION_free); return ret; } static int set_multi_opts(unsigned long *flags, const char *arg, const NAME_EX_TBL *in_tbl) { STACK_OF(CONF_VALUE) *vals; CONF_VALUE *val; int i, ret = 1; if (!arg) return 0; vals = X509V3_parse_list(arg); for (i = 0; i < sk_CONF_VALUE_num(vals); i++) { val = sk_CONF_VALUE_value(vals, i); if (!set_table_opts(flags, val->name, in_tbl)) ret = 0; } sk_CONF_VALUE_pop_free(vals, X509V3_conf_free); return ret; } static int set_table_opts(unsigned long *flags, const char *arg, const NAME_EX_TBL *in_tbl) { char c; const NAME_EX_TBL *ptbl; c = arg[0]; if (c == '-') { c = 0; arg++; } else if (c == '+') { c = 1; arg++; } else { c = 1; } for (ptbl = in_tbl; ptbl->name; ptbl++) { if (OPENSSL_strcasecmp(arg, ptbl->name) == 0) { *flags &= ~ptbl->mask; if (c) *flags |= ptbl->flag; else *flags &= ~ptbl->flag; return 1; } } return 0; } void print_name(BIO *out, const char *title, const X509_NAME *nm) { char *buf; char mline = 0; int indent = 0; unsigned long lflags = get_nameopt(); if (out == NULL) return; if (title != NULL) BIO_puts(out, title); if ((lflags & XN_FLAG_SEP_MASK) == XN_FLAG_SEP_MULTILINE) { mline = 1; indent = 4; } if (lflags == XN_FLAG_COMPAT) { buf = X509_NAME_oneline(nm, 0, 0); BIO_puts(out, buf); BIO_puts(out, "\n"); OPENSSL_free(buf); } else { if (mline) BIO_puts(out, "\n"); X509_NAME_print_ex(out, nm, indent, lflags); BIO_puts(out, "\n"); } } void print_bignum_var(BIO *out, const BIGNUM *in, const char *var, int len, unsigned char *buffer) { BIO_printf(out, " static unsigned char %s_%d[] = {", var, len); if (BN_is_zero(in)) { BIO_printf(out, "\n 0x00"); } else { int i, l; l = BN_bn2bin(in, buffer); for (i = 0; i < l; i++) { BIO_printf(out, (i % 10) == 0 ? "\n " : " "); if (i < l - 1) BIO_printf(out, "0x%02X,", buffer[i]); else BIO_printf(out, "0x%02X", buffer[i]); } } BIO_printf(out, "\n };\n"); } void print_array(BIO *out, const char *title, int len, const unsigned char *d) { int i; BIO_printf(out, "unsigned char %s[%d] = {", title, len); for (i = 0; i < len; i++) { if ((i % 10) == 0) BIO_printf(out, "\n "); if (i < len - 1) BIO_printf(out, "0x%02X, ", d[i]); else BIO_printf(out, "0x%02X", d[i]); } BIO_printf(out, "\n};\n"); } X509_STORE *setup_verify(const char *CAfile, int noCAfile, const char *CApath, int noCApath, const char *CAstore, int noCAstore) { X509_STORE *store = X509_STORE_new(); X509_LOOKUP *lookup; OSSL_LIB_CTX *libctx = app_get0_libctx(); const char *propq = app_get0_propq(); if (store == NULL) goto end; if (CAfile != NULL || !noCAfile) { lookup = X509_STORE_add_lookup(store, X509_LOOKUP_file()); if (lookup == NULL) goto end; if (CAfile != NULL) { if (X509_LOOKUP_load_file_ex(lookup, CAfile, X509_FILETYPE_PEM, libctx, propq) <= 0) { ERR_clear_error(); if (X509_LOOKUP_load_file_ex(lookup, CAfile, X509_FILETYPE_ASN1, libctx, propq) <= 0) { BIO_printf(bio_err, "Error loading file %s\n", CAfile); goto end; } } } else { X509_LOOKUP_load_file_ex(lookup, NULL, X509_FILETYPE_DEFAULT, libctx, propq); } } if (CApath != NULL || !noCApath) { lookup = X509_STORE_add_lookup(store, X509_LOOKUP_hash_dir()); if (lookup == NULL) goto end; if (CApath != NULL) { if (X509_LOOKUP_add_dir(lookup, CApath, X509_FILETYPE_PEM) <= 0) { BIO_printf(bio_err, "Error loading directory %s\n", CApath); goto end; } } else { X509_LOOKUP_add_dir(lookup, NULL, X509_FILETYPE_DEFAULT); } } if (CAstore != NULL || !noCAstore) { lookup = X509_STORE_add_lookup(store, X509_LOOKUP_store()); if (lookup == NULL) goto end; if (!X509_LOOKUP_add_store_ex(lookup, CAstore, libctx, propq)) { if (CAstore != NULL) BIO_printf(bio_err, "Error loading store URI %s\n", CAstore); goto end; } } ERR_clear_error(); return store; end: ERR_print_errors(bio_err); X509_STORE_free(store); return NULL; } static unsigned long index_serial_hash(const OPENSSL_CSTRING *a) { const char *n; n = a[DB_serial]; while (*n == '0') n++; return OPENSSL_LH_strhash(n); } static int index_serial_cmp(const OPENSSL_CSTRING *a, const OPENSSL_CSTRING *b) { const char *aa, *bb; for (aa = a[DB_serial]; *aa == '0'; aa++) ; for (bb = b[DB_serial]; *bb == '0'; bb++) ; return strcmp(aa, bb); } static int index_name_qual(char **a) { return (a[0][0] == 'V'); } static unsigned long index_name_hash(const OPENSSL_CSTRING *a) { return OPENSSL_LH_strhash(a[DB_name]); } int index_name_cmp(const OPENSSL_CSTRING *a, const OPENSSL_CSTRING *b) { return strcmp(a[DB_name], b[DB_name]); } static IMPLEMENT_LHASH_HASH_FN(index_serial, OPENSSL_CSTRING) static IMPLEMENT_LHASH_COMP_FN(index_serial, OPENSSL_CSTRING) static IMPLEMENT_LHASH_HASH_FN(index_name, OPENSSL_CSTRING) static IMPLEMENT_LHASH_COMP_FN(index_name, OPENSSL_CSTRING) #undef BSIZE #define BSIZE 256 BIGNUM *load_serial(const char *serialfile, int *exists, int create, ASN1_INTEGER **retai) { BIO *in = NULL; BIGNUM *ret = NULL; char buf[1024]; ASN1_INTEGER *ai = NULL; ai = ASN1_INTEGER_new(); if (ai == NULL) goto err; in = BIO_new_file(serialfile, "r"); if (exists != NULL) *exists = in != NULL; if (in == NULL) { if (!create) { perror(serialfile); goto err; } ERR_clear_error(); ret = BN_new(); if (ret == NULL) { BIO_printf(bio_err, "Out of memory\n"); } else if (!rand_serial(ret, ai)) { BIO_printf(bio_err, "Error creating random number to store in %s\n", serialfile); BN_free(ret); ret = NULL; } } else { if (!a2i_ASN1_INTEGER(in, ai, buf, 1024)) { BIO_printf(bio_err, "Unable to load number from %s\n", serialfile); goto err; } ret = ASN1_INTEGER_to_BN(ai, NULL); if (ret == NULL) { BIO_printf(bio_err, "Error converting number from bin to BIGNUM\n"); goto err; } } if (ret != NULL && retai != NULL) { *retai = ai; ai = NULL; } err: if (ret == NULL) ERR_print_errors(bio_err); BIO_free(in); ASN1_INTEGER_free(ai); return ret; } int save_serial(const char *serialfile, const char *suffix, const BIGNUM *serial, ASN1_INTEGER **retai) { char buf[1][BSIZE]; BIO *out = NULL; int ret = 0; ASN1_INTEGER *ai = NULL; int j; if (suffix == NULL) j = strlen(serialfile); else j = strlen(serialfile) + strlen(suffix) + 1; if (j >= BSIZE) { BIO_printf(bio_err, "File name too long\n"); goto err; } if (suffix == NULL) { OPENSSL_strlcpy(buf[0], serialfile, BSIZE); } else { #ifndef OPENSSL_SYS_VMS BIO_snprintf(buf[0], sizeof(buf[0]), "%s.%s", serialfile, suffix); #else BIO_snprintf(buf[0], sizeof(buf[0]), "%s-%s", serialfile, suffix); #endif } out = BIO_new_file(buf[0], "w"); if (out == NULL) { goto err; } if ((ai = BN_to_ASN1_INTEGER(serial, NULL)) == NULL) { BIO_printf(bio_err, "error converting serial to ASN.1 format\n"); goto err; } i2a_ASN1_INTEGER(out, ai); BIO_puts(out, "\n"); ret = 1; if (retai) { *retai = ai; ai = NULL; } err: if (!ret) ERR_print_errors(bio_err); BIO_free_all(out); ASN1_INTEGER_free(ai); return ret; } int rotate_serial(const char *serialfile, const char *new_suffix, const char *old_suffix) { char buf[2][BSIZE]; int i, j; i = strlen(serialfile) + strlen(old_suffix); j = strlen(serialfile) + strlen(new_suffix); if (i > j) j = i; if (j + 1 >= BSIZE) { BIO_printf(bio_err, "File name too long\n"); goto err; } #ifndef OPENSSL_SYS_VMS BIO_snprintf(buf[0], sizeof(buf[0]), "%s.%s", serialfile, new_suffix); BIO_snprintf(buf[1], sizeof(buf[1]), "%s.%s", serialfile, old_suffix); #else BIO_snprintf(buf[0], sizeof(buf[0]), "%s-%s", serialfile, new_suffix); BIO_snprintf(buf[1], sizeof(buf[1]), "%s-%s", serialfile, old_suffix); #endif if (rename(serialfile, buf[1]) < 0 && errno != ENOENT #ifdef ENOTDIR && errno != ENOTDIR #endif ) { BIO_printf(bio_err, "Unable to rename %s to %s\n", serialfile, buf[1]); perror("reason"); goto err; } if (rename(buf[0], serialfile) < 0) { BIO_printf(bio_err, "Unable to rename %s to %s\n", buf[0], serialfile); perror("reason"); rename(buf[1], serialfile); goto err; } return 1; err: ERR_print_errors(bio_err); return 0; } int rand_serial(BIGNUM *b, ASN1_INTEGER *ai) { BIGNUM *btmp; int ret = 0; btmp = b == NULL ? BN_new() : b; if (btmp == NULL) return 0; if (!BN_rand(btmp, SERIAL_RAND_BITS, BN_RAND_TOP_ANY, BN_RAND_BOTTOM_ANY)) goto error; if (ai && !BN_to_ASN1_INTEGER(btmp, ai)) goto error; ret = 1; error: if (btmp != b) BN_free(btmp); return ret; } CA_DB *load_index(const char *dbfile, DB_ATTR *db_attr) { CA_DB *retdb = NULL; TXT_DB *tmpdb = NULL; BIO *in; CONF *dbattr_conf = NULL; char buf[BSIZE]; #ifndef OPENSSL_NO_POSIX_IO FILE *dbfp; struct stat dbst; #endif in = BIO_new_file(dbfile, "r"); if (in == NULL) goto err; #ifndef OPENSSL_NO_POSIX_IO BIO_get_fp(in, &dbfp); if (fstat(fileno(dbfp), &dbst) == -1) { ERR_raise_data(ERR_LIB_SYS, errno, "calling fstat(%s)", dbfile); goto err; } #endif if ((tmpdb = TXT_DB_read(in, DB_NUMBER)) == NULL) goto err; #ifndef OPENSSL_SYS_VMS BIO_snprintf(buf, sizeof(buf), "%s.attr", dbfile); #else BIO_snprintf(buf, sizeof(buf), "%s-attr", dbfile); #endif dbattr_conf = app_load_config_quiet(buf); retdb = app_malloc(sizeof(*retdb), "new DB"); retdb->db = tmpdb; tmpdb = NULL; if (db_attr) retdb->attributes = *db_attr; else retdb->attributes.unique_subject = 1; if (dbattr_conf != NULL) { char *p = app_conf_try_string(dbattr_conf, NULL, "unique_subject"); if (p != NULL) retdb->attributes.unique_subject = parse_yesno(p, 1); } retdb->dbfname = OPENSSL_strdup(dbfile); if (retdb->dbfname == NULL) goto err; #ifndef OPENSSL_NO_POSIX_IO retdb->dbst = dbst; #endif err: ERR_print_errors(bio_err); NCONF_free(dbattr_conf); TXT_DB_free(tmpdb); BIO_free_all(in); return retdb; } /* * Returns > 0 on success, <= 0 on error */ int index_index(CA_DB *db) { if (!TXT_DB_create_index(db->db, DB_serial, NULL, LHASH_HASH_FN(index_serial), LHASH_COMP_FN(index_serial))) { BIO_printf(bio_err, "Error creating serial number index:(%ld,%ld,%ld)\n", db->db->error, db->db->arg1, db->db->arg2); goto err; } if (db->attributes.unique_subject && !TXT_DB_create_index(db->db, DB_name, index_name_qual, LHASH_HASH_FN(index_name), LHASH_COMP_FN(index_name))) { BIO_printf(bio_err, "Error creating name index:(%ld,%ld,%ld)\n", db->db->error, db->db->arg1, db->db->arg2); goto err; } return 1; err: ERR_print_errors(bio_err); return 0; } int save_index(const char *dbfile, const char *suffix, CA_DB *db) { char buf[3][BSIZE]; BIO *out; int j; j = strlen(dbfile) + strlen(suffix); if (j + 6 >= BSIZE) { BIO_printf(bio_err, "File name too long\n"); goto err; } #ifndef OPENSSL_SYS_VMS BIO_snprintf(buf[2], sizeof(buf[2]), "%s.attr", dbfile); BIO_snprintf(buf[1], sizeof(buf[1]), "%s.attr.%s", dbfile, suffix); BIO_snprintf(buf[0], sizeof(buf[0]), "%s.%s", dbfile, suffix); #else BIO_snprintf(buf[2], sizeof(buf[2]), "%s-attr", dbfile); BIO_snprintf(buf[1], sizeof(buf[1]), "%s-attr-%s", dbfile, suffix); BIO_snprintf(buf[0], sizeof(buf[0]), "%s-%s", dbfile, suffix); #endif out = BIO_new_file(buf[0], "w"); if (out == NULL) { perror(dbfile); BIO_printf(bio_err, "Unable to open '%s'\n", dbfile); goto err; } j = TXT_DB_write(out, db->db); BIO_free(out); if (j <= 0) goto err; out = BIO_new_file(buf[1], "w"); if (out == NULL) { perror(buf[2]); BIO_printf(bio_err, "Unable to open '%s'\n", buf[2]); goto err; } BIO_printf(out, "unique_subject = %s\n", db->attributes.unique_subject ? "yes" : "no"); BIO_free(out); return 1; err: ERR_print_errors(bio_err); return 0; } int rotate_index(const char *dbfile, const char *new_suffix, const char *old_suffix) { char buf[5][BSIZE]; int i, j; i = strlen(dbfile) + strlen(old_suffix); j = strlen(dbfile) + strlen(new_suffix); if (i > j) j = i; if (j + 6 >= BSIZE) { BIO_printf(bio_err, "File name too long\n"); goto err; } #ifndef OPENSSL_SYS_VMS BIO_snprintf(buf[4], sizeof(buf[4]), "%s.attr", dbfile); BIO_snprintf(buf[3], sizeof(buf[3]), "%s.attr.%s", dbfile, old_suffix); BIO_snprintf(buf[2], sizeof(buf[2]), "%s.attr.%s", dbfile, new_suffix); BIO_snprintf(buf[1], sizeof(buf[1]), "%s.%s", dbfile, old_suffix); BIO_snprintf(buf[0], sizeof(buf[0]), "%s.%s", dbfile, new_suffix); #else BIO_snprintf(buf[4], sizeof(buf[4]), "%s-attr", dbfile); BIO_snprintf(buf[3], sizeof(buf[3]), "%s-attr-%s", dbfile, old_suffix); BIO_snprintf(buf[2], sizeof(buf[2]), "%s-attr-%s", dbfile, new_suffix); BIO_snprintf(buf[1], sizeof(buf[1]), "%s-%s", dbfile, old_suffix); BIO_snprintf(buf[0], sizeof(buf[0]), "%s-%s", dbfile, new_suffix); #endif if (rename(dbfile, buf[1]) < 0 && errno != ENOENT #ifdef ENOTDIR && errno != ENOTDIR #endif ) { BIO_printf(bio_err, "Unable to rename %s to %s\n", dbfile, buf[1]); perror("reason"); goto err; } if (rename(buf[0], dbfile) < 0) { BIO_printf(bio_err, "Unable to rename %s to %s\n", buf[0], dbfile); perror("reason"); rename(buf[1], dbfile); goto err; } if (rename(buf[4], buf[3]) < 0 && errno != ENOENT #ifdef ENOTDIR && errno != ENOTDIR #endif ) { BIO_printf(bio_err, "Unable to rename %s to %s\n", buf[4], buf[3]); perror("reason"); rename(dbfile, buf[0]); rename(buf[1], dbfile); goto err; } if (rename(buf[2], buf[4]) < 0) { BIO_printf(bio_err, "Unable to rename %s to %s\n", buf[2], buf[4]); perror("reason"); rename(buf[3], buf[4]); rename(dbfile, buf[0]); rename(buf[1], dbfile); goto err; } return 1; err: ERR_print_errors(bio_err); return 0; } void free_index(CA_DB *db) { if (db) { TXT_DB_free(db->db); OPENSSL_free(db->dbfname); OPENSSL_free(db); } } int parse_yesno(const char *str, int def) { if (str) { switch (*str) { case 'f': /* false */ case 'F': /* FALSE */ case 'n': /* no */ case 'N': /* NO */ case '0': /* 0 */ return 0; case 't': /* true */ case 'T': /* TRUE */ case 'y': /* yes */ case 'Y': /* YES */ case '1': /* 1 */ return 1; } } return def; } /* * name is expected to be in the format /type0=value0/type1=value1/type2=... * where + can be used instead of / to form multi-valued RDNs if canmulti * and characters may be escaped by \ */ X509_NAME *parse_name(const char *cp, int chtype, int canmulti, const char *desc) { int nextismulti = 0; char *work; X509_NAME *n; if (*cp++ != '/') { BIO_printf(bio_err, "%s: %s name is expected to be in the format " "/type0=value0/type1=value1/type2=... where characters may " "be escaped by \\. This name is not in that format: '%s'\n", opt_getprog(), desc, --cp); return NULL; } n = X509_NAME_new(); if (n == NULL) { BIO_printf(bio_err, "%s: Out of memory\n", opt_getprog()); return NULL; } work = OPENSSL_strdup(cp); if (work == NULL) { BIO_printf(bio_err, "%s: Error copying %s name input\n", opt_getprog(), desc); goto err; } while (*cp != '\0') { char *bp = work; char *typestr = bp; unsigned char *valstr; int nid; int ismulti = nextismulti; nextismulti = 0; /* Collect the type */ while (*cp != '\0' && *cp != '=') *bp++ = *cp++; *bp++ = '\0'; if (*cp == '\0') { BIO_printf(bio_err, "%s: Missing '=' after RDN type string '%s' in %s name string\n", opt_getprog(), typestr, desc); goto err; } ++cp; /* Collect the value. */ valstr = (unsigned char *)bp; for (; *cp != '\0' && *cp != '/'; *bp++ = *cp++) { /* unescaped '+' symbol string signals further member of multiRDN */ if (canmulti && *cp == '+') { nextismulti = 1; break; } if (*cp == '\\' && *++cp == '\0') { BIO_printf(bio_err, "%s: Escape character at end of %s name string\n", opt_getprog(), desc); goto err; } } *bp++ = '\0'; /* If not at EOS (must be + or /), move forward. */ if (*cp != '\0') ++cp; /* Parse */ nid = OBJ_txt2nid(typestr); if (nid == NID_undef) { BIO_printf(bio_err, "%s warning: Skipping unknown %s name attribute \"%s\"\n", opt_getprog(), desc, typestr); if (ismulti) BIO_printf(bio_err, "%s hint: a '+' in a value string needs be escaped using '\\' else a new member of a multi-valued RDN is expected\n", opt_getprog()); continue; } if (*valstr == '\0') { BIO_printf(bio_err, "%s warning: No value provided for %s name attribute \"%s\", skipped\n", opt_getprog(), desc, typestr); continue; } if (!X509_NAME_add_entry_by_NID(n, nid, chtype, valstr, strlen((char *)valstr), -1, ismulti ? -1 : 0)) { ERR_print_errors(bio_err); BIO_printf(bio_err, "%s: Error adding %s name attribute \"/%s=%s\"\n", opt_getprog(), desc, typestr, valstr); goto err; } } OPENSSL_free(work); return n; err: X509_NAME_free(n); OPENSSL_free(work); return NULL; } /* - * Read whole contents of a BIO into an allocated memory buffer and return - * it. + * Read whole contents of a BIO into an allocated memory buffer. + * The return value is one on success, zero on error. + * If `maxlen` is non-zero, at most `maxlen` bytes are returned, or else, if + * the input is longer than `maxlen`, an error is returned. + * If `maxlen` is zero, the limit is effectively `SIZE_MAX`. */ - -int bio_to_mem(unsigned char **out, int maxlen, BIO *in) +int bio_to_mem(unsigned char **out, size_t *outlen, size_t maxlen, BIO *in) { + unsigned char tbuf[4096]; BIO *mem; - int len, ret; - unsigned char tbuf[1024]; + BUF_MEM *bufm; + size_t sz = 0; + int len; mem = BIO_new(BIO_s_mem()); if (mem == NULL) - return -1; + return 0; for (;;) { - if ((maxlen != -1) && maxlen < 1024) - len = maxlen; - else - len = 1024; - len = BIO_read(in, tbuf, len); - if (len < 0) { - BIO_free(mem); - return -1; - } - if (len == 0) + if ((len = BIO_read(in, tbuf, 4096)) == 0) break; - if (BIO_write(mem, tbuf, len) != len) { + if (len < 0 + || BIO_write(mem, tbuf, len) != len + || sz > SIZE_MAX - len + || ((sz += len) > maxlen && maxlen != 0)) { BIO_free(mem); - return -1; + return 0; } - if (maxlen != -1) - maxlen -= len; - - if (maxlen == 0) - break; } - ret = BIO_get_mem_data(mem, (char **)out); - BIO_set_flags(mem, BIO_FLAGS_MEM_RDONLY); + + /* So BIO_free orphans BUF_MEM */ + (void)BIO_set_close(mem, BIO_NOCLOSE); + BIO_get_mem_ptr(mem, &bufm); BIO_free(mem); - return ret; + *out = (unsigned char *)bufm->data; + *outlen = bufm->length; + /* Tell BUF_MEM to orphan data */ + bufm->data = NULL; + BUF_MEM_free(bufm); + return 1; } int pkey_ctrl_string(EVP_PKEY_CTX *ctx, const char *value) { int rv = 0; char *stmp, *vtmp = NULL; stmp = OPENSSL_strdup(value); if (stmp == NULL) return -1; vtmp = strchr(stmp, ':'); if (vtmp == NULL) goto err; *vtmp = 0; vtmp++; rv = EVP_PKEY_CTX_ctrl_str(ctx, stmp, vtmp); err: OPENSSL_free(stmp); return rv; } static void nodes_print(const char *name, STACK_OF(X509_POLICY_NODE) *nodes) { X509_POLICY_NODE *node; int i; BIO_printf(bio_err, "%s Policies:", name); if (nodes) { BIO_puts(bio_err, "\n"); for (i = 0; i < sk_X509_POLICY_NODE_num(nodes); i++) { node = sk_X509_POLICY_NODE_value(nodes, i); X509_POLICY_NODE_print(bio_err, node, 2); } } else { BIO_puts(bio_err, " \n"); } } void policies_print(X509_STORE_CTX *ctx) { X509_POLICY_TREE *tree; int explicit_policy; tree = X509_STORE_CTX_get0_policy_tree(ctx); explicit_policy = X509_STORE_CTX_get_explicit_policy(ctx); BIO_printf(bio_err, "Require explicit Policy: %s\n", explicit_policy ? "True" : "False"); nodes_print("Authority", X509_policy_tree_get0_policies(tree)); nodes_print("User", X509_policy_tree_get0_user_policies(tree)); } /*- * next_protos_parse parses a comma separated list of strings into a string * in a format suitable for passing to SSL_CTX_set_next_protos_advertised. * outlen: (output) set to the length of the resulting buffer on success. * err: (maybe NULL) on failure, an error message line is written to this BIO. * in: a NUL terminated string like "abc,def,ghi" * * returns: a malloc'd buffer or NULL on failure. */ unsigned char *next_protos_parse(size_t *outlen, const char *in) { size_t len; unsigned char *out; size_t i, start = 0; size_t skipped = 0; len = strlen(in); if (len == 0 || len >= 65535) return NULL; out = app_malloc(len + 1, "NPN buffer"); for (i = 0; i <= len; ++i) { if (i == len || in[i] == ',') { /* * Zero-length ALPN elements are invalid on the wire, we could be * strict and reject the entire string, but just ignoring extra * commas seems harmless and more friendly. * * Every comma we skip in this way puts the input buffer another * byte ahead of the output buffer, so all stores into the output * buffer need to be decremented by the number commas skipped. */ if (i == start) { ++start; ++skipped; continue; } if (i - start > 255) { OPENSSL_free(out); return NULL; } out[start - skipped] = (unsigned char)(i - start); start = i + 1; } else { out[i + 1 - skipped] = in[i]; } } if (len <= skipped) { OPENSSL_free(out); return NULL; } *outlen = len + 1 - skipped; return out; } int check_cert_attributes(BIO *bio, X509 *x, const char *checkhost, const char *checkemail, const char *checkip, int print) { int valid_host = 0; int valid_mail = 0; int valid_ip = 0; int ret = 1; if (x == NULL) return 0; if (checkhost != NULL) { valid_host = X509_check_host(x, checkhost, 0, 0, NULL); if (print) BIO_printf(bio, "Hostname %s does%s match certificate\n", checkhost, valid_host == 1 ? "" : " NOT"); ret = ret && valid_host > 0; } if (checkemail != NULL) { valid_mail = X509_check_email(x, checkemail, 0, 0); if (print) BIO_printf(bio, "Email %s does%s match certificate\n", checkemail, valid_mail ? "" : " NOT"); ret = ret && valid_mail > 0; } if (checkip != NULL) { valid_ip = X509_check_ip_asc(x, checkip, 0); if (print) BIO_printf(bio, "IP %s does%s match certificate\n", checkip, valid_ip ? "" : " NOT"); ret = ret && valid_ip > 0; } return ret; } static int do_pkey_ctx_init(EVP_PKEY_CTX *pkctx, STACK_OF(OPENSSL_STRING) *opts) { int i; if (opts == NULL) return 1; for (i = 0; i < sk_OPENSSL_STRING_num(opts); i++) { char *opt = sk_OPENSSL_STRING_value(opts, i); if (pkey_ctrl_string(pkctx, opt) <= 0) { BIO_printf(bio_err, "parameter error \"%s\"\n", opt); ERR_print_errors(bio_err); return 0; } } return 1; } static int do_x509_init(X509 *x, STACK_OF(OPENSSL_STRING) *opts) { int i; if (opts == NULL) return 1; for (i = 0; i < sk_OPENSSL_STRING_num(opts); i++) { char *opt = sk_OPENSSL_STRING_value(opts, i); if (x509_ctrl_string(x, opt) <= 0) { BIO_printf(bio_err, "parameter error \"%s\"\n", opt); ERR_print_errors(bio_err); return 0; } } return 1; } static int do_x509_req_init(X509_REQ *x, STACK_OF(OPENSSL_STRING) *opts) { int i; if (opts == NULL) return 1; for (i = 0; i < sk_OPENSSL_STRING_num(opts); i++) { char *opt = sk_OPENSSL_STRING_value(opts, i); if (x509_req_ctrl_string(x, opt) <= 0) { BIO_printf(bio_err, "parameter error \"%s\"\n", opt); ERR_print_errors(bio_err); return 0; } } return 1; } static int do_sign_init(EVP_MD_CTX *ctx, EVP_PKEY *pkey, const char *md, STACK_OF(OPENSSL_STRING) *sigopts) { EVP_PKEY_CTX *pkctx = NULL; char def_md[80]; if (ctx == NULL) return 0; /* * EVP_PKEY_get_default_digest_name() returns 2 if the digest is mandatory * for this algorithm. */ if (EVP_PKEY_get_default_digest_name(pkey, def_md, sizeof(def_md)) == 2 && strcmp(def_md, "UNDEF") == 0) { /* The signing algorithm requires there to be no digest */ md = NULL; } return EVP_DigestSignInit_ex(ctx, &pkctx, md, app_get0_libctx(), app_get0_propq(), pkey, NULL) && do_pkey_ctx_init(pkctx, sigopts); } static int adapt_keyid_ext(X509 *cert, X509V3_CTX *ext_ctx, const char *name, const char *value, int add_default) { const STACK_OF(X509_EXTENSION) *exts = X509_get0_extensions(cert); X509_EXTENSION *new_ext = X509V3_EXT_nconf(NULL, ext_ctx, name, value); int idx, rv = 0; if (new_ext == NULL) return rv; idx = X509v3_get_ext_by_OBJ(exts, X509_EXTENSION_get_object(new_ext), -1); if (idx >= 0) { X509_EXTENSION *found_ext = X509v3_get_ext(exts, idx); ASN1_OCTET_STRING *encoded = X509_EXTENSION_get_data(found_ext); int disabled = ASN1_STRING_length(encoded) <= 2; /* indicating "none" */ if (disabled) { X509_delete_ext(cert, idx); X509_EXTENSION_free(found_ext); } /* else keep existing key identifier, which might be outdated */ rv = 1; } else { rv = !add_default || X509_add_ext(cert, new_ext, -1); } X509_EXTENSION_free(new_ext); return rv; } int cert_matches_key(const X509 *cert, const EVP_PKEY *pkey) { int match; ERR_set_mark(); match = X509_check_private_key(cert, pkey); ERR_pop_to_mark(); return match; } /* Ensure RFC 5280 compliance, adapt keyIDs as needed, and sign the cert info */ int do_X509_sign(X509 *cert, int force_v1, EVP_PKEY *pkey, const char *md, STACK_OF(OPENSSL_STRING) *sigopts, X509V3_CTX *ext_ctx) { EVP_MD_CTX *mctx = EVP_MD_CTX_new(); int self_sign; int rv = 0; if (!force_v1) { if (!X509_set_version(cert, X509_VERSION_3)) goto end; /* * Add default SKID before AKID such that AKID can make use of it * in case the certificate is self-signed */ /* Prevent X509_V_ERR_MISSING_SUBJECT_KEY_IDENTIFIER */ if (!adapt_keyid_ext(cert, ext_ctx, "subjectKeyIdentifier", "hash", 1)) goto end; /* Prevent X509_V_ERR_MISSING_AUTHORITY_KEY_IDENTIFIER */ self_sign = cert_matches_key(cert, pkey); if (!adapt_keyid_ext(cert, ext_ctx, "authorityKeyIdentifier", "keyid, issuer", !self_sign)) goto end; } /* May add further measures for ensuring RFC 5280 compliance, see #19805 */ if (mctx != NULL && do_sign_init(mctx, pkey, md, sigopts) > 0) rv = (X509_sign_ctx(cert, mctx) > 0); end: EVP_MD_CTX_free(mctx); return rv; } /* Sign the certificate request info */ int do_X509_REQ_sign(X509_REQ *x, EVP_PKEY *pkey, const char *md, STACK_OF(OPENSSL_STRING) *sigopts) { int rv = 0; EVP_MD_CTX *mctx = EVP_MD_CTX_new(); if (do_sign_init(mctx, pkey, md, sigopts) > 0) rv = (X509_REQ_sign_ctx(x, mctx) > 0); EVP_MD_CTX_free(mctx); return rv; } /* Sign the CRL info */ int do_X509_CRL_sign(X509_CRL *x, EVP_PKEY *pkey, const char *md, STACK_OF(OPENSSL_STRING) *sigopts) { int rv = 0; EVP_MD_CTX *mctx = EVP_MD_CTX_new(); if (do_sign_init(mctx, pkey, md, sigopts) > 0) rv = (X509_CRL_sign_ctx(x, mctx) > 0); EVP_MD_CTX_free(mctx); return rv; } /* * do_X509_verify returns 1 if the signature is valid, * 0 if the signature check fails, or -1 if error occurs. */ int do_X509_verify(X509 *x, EVP_PKEY *pkey, STACK_OF(OPENSSL_STRING) *vfyopts) { int rv = 0; if (do_x509_init(x, vfyopts) > 0) rv = X509_verify(x, pkey); else rv = -1; return rv; } /* * do_X509_REQ_verify returns 1 if the signature is valid, * 0 if the signature check fails, or -1 if error occurs. */ int do_X509_REQ_verify(X509_REQ *x, EVP_PKEY *pkey, STACK_OF(OPENSSL_STRING) *vfyopts) { int rv = 0; if (do_x509_req_init(x, vfyopts) > 0) rv = X509_REQ_verify_ex(x, pkey, app_get0_libctx(), app_get0_propq()); else rv = -1; return rv; } /* Get first http URL from a DIST_POINT structure */ static const char *get_dp_url(DIST_POINT *dp) { GENERAL_NAMES *gens; GENERAL_NAME *gen; int i, gtype; ASN1_STRING *uri; if (!dp->distpoint || dp->distpoint->type != 0) return NULL; gens = dp->distpoint->name.fullname; for (i = 0; i < sk_GENERAL_NAME_num(gens); i++) { gen = sk_GENERAL_NAME_value(gens, i); uri = GENERAL_NAME_get0_value(gen, >ype); if (gtype == GEN_URI && ASN1_STRING_length(uri) > 6) { const char *uptr = (const char *)ASN1_STRING_get0_data(uri); if (IS_HTTP(uptr)) /* can/should not use HTTPS here */ return uptr; } } return NULL; } /* * Look through a CRLDP structure and attempt to find an http URL to * downloads a CRL from. */ static X509_CRL *load_crl_crldp(STACK_OF(DIST_POINT) *crldp) { int i; const char *urlptr = NULL; for (i = 0; i < sk_DIST_POINT_num(crldp); i++) { DIST_POINT *dp = sk_DIST_POINT_value(crldp, i); urlptr = get_dp_url(dp); if (urlptr != NULL) return load_crl(urlptr, FORMAT_UNDEF, 0, "CRL via CDP"); } return NULL; } /* * Example of downloading CRLs from CRLDP: * not usable for real world as it always downloads and doesn't cache anything. */ static STACK_OF(X509_CRL) *crls_http_cb(const X509_STORE_CTX *ctx, const X509_NAME *nm) { X509 *x; STACK_OF(X509_CRL) *crls = NULL; X509_CRL *crl; STACK_OF(DIST_POINT) *crldp; crls = sk_X509_CRL_new_null(); if (!crls) return NULL; x = X509_STORE_CTX_get_current_cert(ctx); crldp = X509_get_ext_d2i(x, NID_crl_distribution_points, NULL, NULL); crl = load_crl_crldp(crldp); sk_DIST_POINT_pop_free(crldp, DIST_POINT_free); if (crl == NULL || !sk_X509_CRL_push(crls, crl)) goto error; /* Try to download delta CRL */ crldp = X509_get_ext_d2i(x, NID_freshest_crl, NULL, NULL); crl = load_crl_crldp(crldp); sk_DIST_POINT_pop_free(crldp, DIST_POINT_free); if (crl != NULL && !sk_X509_CRL_push(crls, crl)) goto error; return crls; error: X509_CRL_free(crl); sk_X509_CRL_free(crls); return NULL; } void store_setup_crl_download(X509_STORE *st) { X509_STORE_set_lookup_crls_cb(st, crls_http_cb); } #if !defined(OPENSSL_NO_SOCK) && !defined(OPENSSL_NO_HTTP) static const char *tls_error_hint(void) { unsigned long err = ERR_peek_error(); if (ERR_GET_LIB(err) != ERR_LIB_SSL) err = ERR_peek_last_error(); if (ERR_GET_LIB(err) != ERR_LIB_SSL) return NULL; /* likely no TLS error */ switch (ERR_GET_REASON(err)) { case SSL_R_WRONG_VERSION_NUMBER: return "The server does not support (a suitable version of) TLS"; case SSL_R_UNKNOWN_PROTOCOL: return "The server does not support HTTPS"; case SSL_R_CERTIFICATE_VERIFY_FAILED: return "Cannot authenticate server via its TLS certificate, likely due to mismatch with our trusted TLS certs or missing revocation status"; case SSL_AD_REASON_OFFSET + TLS1_AD_UNKNOWN_CA: return "Server did not accept our TLS certificate, likely due to mismatch with server's trust anchor or missing revocation status"; case SSL_AD_REASON_OFFSET + SSL3_AD_HANDSHAKE_FAILURE: return "TLS handshake failure. Possibly the server requires our TLS certificate but did not receive it"; default: return NULL; /* no hint available for TLS error */ } } static BIO *http_tls_shutdown(BIO *bio) { if (bio != NULL) { BIO *cbio; const char *hint = tls_error_hint(); if (hint != NULL) BIO_printf(bio_err, "%s\n", hint); (void)ERR_set_mark(); BIO_ssl_shutdown(bio); cbio = BIO_pop(bio); /* connect+HTTP BIO */ BIO_free(bio); /* SSL BIO */ (void)ERR_pop_to_mark(); /* hide SSL_R_READ_BIO_NOT_SET etc. */ bio = cbio; } return bio; } /* HTTP callback function that supports TLS connection also via HTTPS proxy */ BIO *app_http_tls_cb(BIO *bio, void *arg, int connect, int detail) { APP_HTTP_TLS_INFO *info = (APP_HTTP_TLS_INFO *)arg; SSL_CTX *ssl_ctx = info->ssl_ctx; if (ssl_ctx == NULL) /* not using TLS */ return bio; if (connect) { SSL *ssl; BIO *sbio = NULL; X509_STORE *ts = SSL_CTX_get_cert_store(ssl_ctx); X509_VERIFY_PARAM *vpm = X509_STORE_get0_param(ts); const char *host = vpm == NULL ? NULL : X509_VERIFY_PARAM_get0_host(vpm, 0 /* first hostname */); /* adapt after fixing callback design flaw, see #17088 */ if ((info->use_proxy && !OSSL_HTTP_proxy_connect(bio, info->server, info->port, NULL, NULL, /* no proxy credentials */ info->timeout, bio_err, opt_getprog())) || (sbio = BIO_new(BIO_f_ssl())) == NULL) { return NULL; } if ((ssl = SSL_new(ssl_ctx)) == NULL) { BIO_free(sbio); return NULL; } if (vpm != NULL) SSL_set_tlsext_host_name(ssl, host /* may be NULL */); SSL_set_connect_state(ssl); BIO_set_ssl(sbio, ssl, BIO_CLOSE); bio = BIO_push(sbio, bio); } else { /* disconnect from TLS */ bio = http_tls_shutdown(bio); } return bio; } void APP_HTTP_TLS_INFO_free(APP_HTTP_TLS_INFO *info) { if (info != NULL) { SSL_CTX_free(info->ssl_ctx); OPENSSL_free(info); } } ASN1_VALUE *app_http_get_asn1(const char *url, const char *proxy, const char *no_proxy, SSL_CTX *ssl_ctx, const STACK_OF(CONF_VALUE) *headers, long timeout, const char *expected_content_type, const ASN1_ITEM *it) { APP_HTTP_TLS_INFO info; char *server; char *port; int use_ssl; BIO *mem; ASN1_VALUE *resp = NULL; if (url == NULL || it == NULL) { ERR_raise(ERR_LIB_HTTP, ERR_R_PASSED_NULL_PARAMETER); return NULL; } if (!OSSL_HTTP_parse_url(url, &use_ssl, NULL /* userinfo */, &server, &port, NULL /* port_num, */, NULL, NULL, NULL)) return NULL; if (use_ssl && ssl_ctx == NULL) { ERR_raise_data(ERR_LIB_HTTP, ERR_R_PASSED_NULL_PARAMETER, "missing SSL_CTX"); goto end; } if (!use_ssl && ssl_ctx != NULL) { ERR_raise_data(ERR_LIB_HTTP, ERR_R_PASSED_INVALID_ARGUMENT, "SSL_CTX given but use_ssl == 0"); goto end; } info.server = server; info.port = port; info.use_proxy = /* workaround for callback design flaw, see #17088 */ OSSL_HTTP_adapt_proxy(proxy, no_proxy, server, use_ssl) != NULL; info.timeout = timeout; info.ssl_ctx = ssl_ctx; mem = OSSL_HTTP_get(url, proxy, no_proxy, NULL /* bio */, NULL /* rbio */, app_http_tls_cb, &info, 0 /* buf_size */, headers, expected_content_type, 1 /* expect_asn1 */, OSSL_HTTP_DEFAULT_MAX_RESP_LEN, timeout); resp = ASN1_item_d2i_bio(it, mem, NULL); BIO_free(mem); end: OPENSSL_free(server); OPENSSL_free(port); return resp; } ASN1_VALUE *app_http_post_asn1(const char *host, const char *port, const char *path, const char *proxy, const char *no_proxy, SSL_CTX *ssl_ctx, const STACK_OF(CONF_VALUE) *headers, const char *content_type, ASN1_VALUE *req, const ASN1_ITEM *req_it, const char *expected_content_type, long timeout, const ASN1_ITEM *rsp_it) { int use_ssl = ssl_ctx != NULL; APP_HTTP_TLS_INFO info; BIO *rsp, *req_mem = ASN1_item_i2d_mem_bio(req_it, req); ASN1_VALUE *res; if (req_mem == NULL) return NULL; info.server = host; info.port = port; info.use_proxy = /* workaround for callback design flaw, see #17088 */ OSSL_HTTP_adapt_proxy(proxy, no_proxy, host, use_ssl) != NULL; info.timeout = timeout; info.ssl_ctx = ssl_ctx; rsp = OSSL_HTTP_transfer(NULL, host, port, path, use_ssl, proxy, no_proxy, NULL /* bio */, NULL /* rbio */, app_http_tls_cb, &info, 0 /* buf_size */, headers, content_type, req_mem, expected_content_type, 1 /* expect_asn1 */, OSSL_HTTP_DEFAULT_MAX_RESP_LEN, timeout, 0 /* keep_alive */); BIO_free(req_mem); res = ASN1_item_d2i_bio(rsp_it, rsp, NULL); BIO_free(rsp); return res; } #endif /* * Platform-specific sections */ #if defined(_WIN32) # ifdef fileno # undef fileno # define fileno(a) (int)_fileno(a) # endif # include # include static int WIN32_rename(const char *from, const char *to) { TCHAR *tfrom = NULL, *tto; DWORD err; int ret = 0; if (sizeof(TCHAR) == 1) { tfrom = (TCHAR *)from; tto = (TCHAR *)to; } else { /* UNICODE path */ size_t i, flen = strlen(from) + 1, tlen = strlen(to) + 1; tfrom = malloc(sizeof(*tfrom) * (flen + tlen)); if (tfrom == NULL) goto err; tto = tfrom + flen; # if !defined(_WIN32_WCE) || _WIN32_WCE >= 101 if (!MultiByteToWideChar(CP_ACP, 0, from, flen, (WCHAR *)tfrom, flen)) # endif for (i = 0; i < flen; i++) tfrom[i] = (TCHAR)from[i]; # if !defined(_WIN32_WCE) || _WIN32_WCE >= 101 if (!MultiByteToWideChar(CP_ACP, 0, to, tlen, (WCHAR *)tto, tlen)) # endif for (i = 0; i < tlen; i++) tto[i] = (TCHAR)to[i]; } if (MoveFile(tfrom, tto)) goto ok; err = GetLastError(); if (err == ERROR_ALREADY_EXISTS || err == ERROR_FILE_EXISTS) { if (DeleteFile(tto) && MoveFile(tfrom, tto)) goto ok; err = GetLastError(); } if (err == ERROR_FILE_NOT_FOUND || err == ERROR_PATH_NOT_FOUND) errno = ENOENT; else if (err == ERROR_ACCESS_DENIED) errno = EACCES; else errno = EINVAL; /* we could map more codes... */ err: ret = -1; ok: if (tfrom != NULL && tfrom != (TCHAR *)from) free(tfrom); return ret; } #endif /* app_tminterval section */ #if defined(_WIN32) double app_tminterval(int stop, int usertime) { FILETIME now; double ret = 0; static ULARGE_INTEGER tmstart; static int warning = 1; int use_GetSystemTime = 1; # ifdef _WIN32_WINNT static HANDLE proc = NULL; if (proc == NULL) { if (check_winnt()) proc = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, GetCurrentProcessId()); if (proc == NULL) proc = (HANDLE) - 1; } if (usertime && proc != (HANDLE) - 1) { FILETIME junk; GetProcessTimes(proc, &junk, &junk, &junk, &now); use_GetSystemTime = 0; } # endif if (use_GetSystemTime) { SYSTEMTIME systime; if (usertime && warning) { BIO_printf(bio_err, "To get meaningful results, run " "this program on idle system.\n"); warning = 0; } GetSystemTime(&systime); SystemTimeToFileTime(&systime, &now); } if (stop == TM_START) { tmstart.u.LowPart = now.dwLowDateTime; tmstart.u.HighPart = now.dwHighDateTime; } else { ULARGE_INTEGER tmstop; tmstop.u.LowPart = now.dwLowDateTime; tmstop.u.HighPart = now.dwHighDateTime; ret = (__int64)(tmstop.QuadPart - tmstart.QuadPart) * 1e-7; } return ret; } #elif defined(OPENSSL_SYS_VXWORKS) # include double app_tminterval(int stop, int usertime) { double ret = 0; # ifdef CLOCK_REALTIME static struct timespec tmstart; struct timespec now; # else static unsigned long tmstart; unsigned long now; # endif static int warning = 1; if (usertime && warning) { BIO_printf(bio_err, "To get meaningful results, run " "this program on idle system.\n"); warning = 0; } # ifdef CLOCK_REALTIME clock_gettime(CLOCK_REALTIME, &now); if (stop == TM_START) tmstart = now; else ret = ((now.tv_sec + now.tv_nsec * 1e-9) - (tmstart.tv_sec + tmstart.tv_nsec * 1e-9)); # else now = tickGet(); if (stop == TM_START) tmstart = now; else ret = (now - tmstart) / (double)sysClkRateGet(); # endif return ret; } #elif defined(_SC_CLK_TCK) /* by means of unistd.h */ # include double app_tminterval(int stop, int usertime) { double ret = 0; struct tms rus; clock_t now = times(&rus); static clock_t tmstart; if (usertime) now = rus.tms_utime; if (stop == TM_START) { tmstart = now; } else { long int tck = sysconf(_SC_CLK_TCK); ret = (now - tmstart) / (double)tck; } return ret; } #else # include # include double app_tminterval(int stop, int usertime) { double ret = 0; struct rusage rus; struct timeval now; static struct timeval tmstart; if (usertime) getrusage(RUSAGE_SELF, &rus), now = rus.ru_utime; else gettimeofday(&now, NULL); if (stop == TM_START) tmstart = now; else ret = ((now.tv_sec + now.tv_usec * 1e-6) - (tmstart.tv_sec + tmstart.tv_usec * 1e-6)); return ret; } #endif int app_access(const char *name, int flag) { #ifdef _WIN32 return _access(name, flag); #else return access(name, flag); #endif } int app_isdir(const char *name) { return opt_isdir(name); } /* raw_read|write section */ #if defined(__VMS) # include "vms_term_sock.h" static int stdin_sock = -1; static void close_stdin_sock(void) { TerminalSocket(TERM_SOCK_DELETE, &stdin_sock); } int fileno_stdin(void) { if (stdin_sock == -1) { TerminalSocket(TERM_SOCK_CREATE, &stdin_sock); atexit(close_stdin_sock); } return stdin_sock; } #else int fileno_stdin(void) { return fileno(stdin); } #endif int fileno_stdout(void) { return fileno(stdout); } #if defined(_WIN32) && defined(STD_INPUT_HANDLE) int raw_read_stdin(void *buf, int siz) { DWORD n; if (ReadFile(GetStdHandle(STD_INPUT_HANDLE), buf, siz, &n, NULL)) return n; else return -1; } #elif defined(__VMS) # include int raw_read_stdin(void *buf, int siz) { return recv(fileno_stdin(), buf, siz, 0); } #else int raw_read_stdin(void *buf, int siz) { return read(fileno_stdin(), buf, siz); } #endif #if defined(_WIN32) && defined(STD_OUTPUT_HANDLE) int raw_write_stdout(const void *buf, int siz) { DWORD n; if (WriteFile(GetStdHandle(STD_OUTPUT_HANDLE), buf, siz, &n, NULL)) return n; else return -1; } #elif defined(OPENSSL_SYS_TANDEM) && defined(OPENSSL_THREADS) \ && defined(_SPT_MODEL_) int raw_write_stdout(const void *buf, int siz) { return write(fileno(stdout), (void *)buf, siz); } #else int raw_write_stdout(const void *buf, int siz) { return write(fileno_stdout(), buf, siz); } #endif /* * Centralized handling of input and output files with format specification * The format is meant to show what the input and output is supposed to be, * and is therefore a show of intent more than anything else. However, it * does impact behavior on some platforms, such as differentiating between * text and binary input/output on non-Unix platforms */ BIO *dup_bio_in(int format) { return BIO_new_fp(stdin, BIO_NOCLOSE | (FMT_istext(format) ? BIO_FP_TEXT : 0)); } BIO *dup_bio_out(int format) { BIO *b = BIO_new_fp(stdout, BIO_NOCLOSE | (FMT_istext(format) ? BIO_FP_TEXT : 0)); void *prefix = NULL; if (b == NULL) return NULL; #ifdef OPENSSL_SYS_VMS if (FMT_istext(format)) b = BIO_push(BIO_new(BIO_f_linebuffer()), b); #endif if (FMT_istext(format) && (prefix = getenv("HARNESS_OSSL_PREFIX")) != NULL) { b = BIO_push(BIO_new(BIO_f_prefix()), b); BIO_set_prefix(b, prefix); } return b; } BIO *dup_bio_err(int format) { BIO *b = BIO_new_fp(stderr, BIO_NOCLOSE | (FMT_istext(format) ? BIO_FP_TEXT : 0)); #ifdef OPENSSL_SYS_VMS if (b != NULL && FMT_istext(format)) b = BIO_push(BIO_new(BIO_f_linebuffer()), b); #endif return b; } void unbuffer(FILE *fp) { /* * On VMS, setbuf() will only take 32-bit pointers, and a compilation * with /POINTER_SIZE=64 will give off a MAYLOSEDATA2 warning here. * However, we trust that the C RTL will never give us a FILE pointer * above the first 4 GB of memory, so we simply turn off the warning * temporarily. */ #if defined(OPENSSL_SYS_VMS) && defined(__DECC) # pragma environment save # pragma message disable maylosedata2 #endif setbuf(fp, NULL); #if defined(OPENSSL_SYS_VMS) && defined(__DECC) # pragma environment restore #endif } static const char *modestr(char mode, int format) { OPENSSL_assert(mode == 'a' || mode == 'r' || mode == 'w'); switch (mode) { case 'a': return FMT_istext(format) ? "a" : "ab"; case 'r': return FMT_istext(format) ? "r" : "rb"; case 'w': return FMT_istext(format) ? "w" : "wb"; } /* The assert above should make sure we never reach this point */ return NULL; } static const char *modeverb(char mode) { switch (mode) { case 'a': return "appending"; case 'r': return "reading"; case 'w': return "writing"; } return "(doing something)"; } /* * Open a file for writing, owner-read-only. */ BIO *bio_open_owner(const char *filename, int format, int private) { FILE *fp = NULL; BIO *b = NULL; int textmode, bflags; #ifndef OPENSSL_NO_POSIX_IO int fd = -1, mode; #endif if (!private || filename == NULL || strcmp(filename, "-") == 0) return bio_open_default(filename, 'w', format); textmode = FMT_istext(format); #ifndef OPENSSL_NO_POSIX_IO mode = O_WRONLY; # ifdef O_CREAT mode |= O_CREAT; # endif # ifdef O_TRUNC mode |= O_TRUNC; # endif if (!textmode) { # ifdef O_BINARY mode |= O_BINARY; # elif defined(_O_BINARY) mode |= _O_BINARY; # endif } # ifdef OPENSSL_SYS_VMS /* * VMS doesn't have O_BINARY, it just doesn't make sense. But, * it still needs to know that we're going binary, or fdopen() * will fail with "invalid argument"... so we tell VMS what the * context is. */ if (!textmode) fd = open(filename, mode, 0600, "ctx=bin"); else # endif fd = open(filename, mode, 0600); if (fd < 0) goto err; fp = fdopen(fd, modestr('w', format)); #else /* OPENSSL_NO_POSIX_IO */ /* Have stdio but not Posix IO, do the best we can */ fp = fopen(filename, modestr('w', format)); #endif /* OPENSSL_NO_POSIX_IO */ if (fp == NULL) goto err; bflags = BIO_CLOSE; if (textmode) bflags |= BIO_FP_TEXT; b = BIO_new_fp(fp, bflags); if (b != NULL) return b; err: BIO_printf(bio_err, "%s: Can't open \"%s\" for writing, %s\n", opt_getprog(), filename, strerror(errno)); ERR_print_errors(bio_err); /* If we have fp, then fdopen took over fd, so don't close both. */ if (fp != NULL) fclose(fp); #ifndef OPENSSL_NO_POSIX_IO else if (fd >= 0) close(fd); #endif return NULL; } static BIO *bio_open_default_(const char *filename, char mode, int format, int quiet) { BIO *ret; if (filename == NULL || strcmp(filename, "-") == 0) { ret = mode == 'r' ? dup_bio_in(format) : dup_bio_out(format); if (quiet) { ERR_clear_error(); return ret; } if (ret != NULL) return ret; BIO_printf(bio_err, "Can't open %s, %s\n", mode == 'r' ? "stdin" : "stdout", strerror(errno)); } else { ret = BIO_new_file(filename, modestr(mode, format)); if (quiet) { ERR_clear_error(); return ret; } if (ret != NULL) return ret; BIO_printf(bio_err, "Can't open \"%s\" for %s, %s\n", filename, modeverb(mode), strerror(errno)); } ERR_print_errors(bio_err); return NULL; } BIO *bio_open_default(const char *filename, char mode, int format) { return bio_open_default_(filename, mode, format, 0); } BIO *bio_open_default_quiet(const char *filename, char mode, int format) { return bio_open_default_(filename, mode, format, 1); } int mem_bio_to_file(BIO *in, const char *filename, int format, int private) { int rv = 0, ret = 0; BIO *out = NULL; BUF_MEM *mem_buffer = NULL; rv = BIO_get_mem_ptr(in, &mem_buffer); if (rv <= 0) { BIO_puts(bio_err, "Error reading mem buffer\n"); goto end; } out = bio_open_owner(filename, format, private); if (out == NULL) goto end; rv = BIO_write(out, mem_buffer->data, mem_buffer->length); if (rv < 0 || (size_t)rv != mem_buffer->length) BIO_printf(bio_err, "Error writing to output file: '%s'\n", filename); else ret = 1; end: if (!ret) ERR_print_errors(bio_err); BIO_free_all(out); return ret; } void wait_for_async(SSL *s) { /* On Windows select only works for sockets, so we simply don't wait */ #ifndef OPENSSL_SYS_WINDOWS int width = 0; fd_set asyncfds; OSSL_ASYNC_FD *fds; size_t numfds; size_t i; if (!SSL_get_all_async_fds(s, NULL, &numfds)) return; if (numfds == 0) return; fds = app_malloc(sizeof(OSSL_ASYNC_FD) * numfds, "allocate async fds"); if (!SSL_get_all_async_fds(s, fds, &numfds)) { OPENSSL_free(fds); return; } FD_ZERO(&asyncfds); for (i = 0; i < numfds; i++) { if (width <= (int)fds[i]) width = (int)fds[i] + 1; openssl_fdset((int)fds[i], &asyncfds); } select(width, (void *)&asyncfds, NULL, NULL, NULL); OPENSSL_free(fds); #endif } /* if OPENSSL_SYS_WINDOWS is defined then so is OPENSSL_SYS_MSDOS */ #if defined(OPENSSL_SYS_MSDOS) int has_stdin_waiting(void) { # if defined(OPENSSL_SYS_WINDOWS) HANDLE inhand = GetStdHandle(STD_INPUT_HANDLE); DWORD events = 0; INPUT_RECORD inputrec; DWORD insize = 1; BOOL peeked; if (inhand == INVALID_HANDLE_VALUE) { return 0; } peeked = PeekConsoleInput(inhand, &inputrec, insize, &events); if (!peeked) { /* Probably redirected input? _kbhit() does not work in this case */ if (!feof(stdin)) { return 1; } return 0; } # endif return _kbhit(); } #endif /* Corrupt a signature by modifying final byte */ void corrupt_signature(const ASN1_STRING *signature) { unsigned char *s = signature->data; s[signature->length - 1] ^= 0x1; } int check_cert_time_string(const char *time, const char *desc) { if (time == NULL || strcmp(time, "today") == 0 || ASN1_TIME_set_string_X509(NULL, time)) return 1; BIO_printf(bio_err, "%s is invalid, it should be \"today\" or have format [CC]YYMMDDHHMMSSZ\n", desc); return 0; } int set_cert_times(X509 *x, const char *startdate, const char *enddate, int days, int strict_compare_times) { if (!check_cert_time_string(startdate, "start date")) return 0; if (!check_cert_time_string(enddate, "end date")) return 0; if (startdate == NULL || strcmp(startdate, "today") == 0) { if (X509_gmtime_adj(X509_getm_notBefore(x), 0) == NULL) { BIO_printf(bio_err, "Error setting notBefore certificate field\n"); return 0; } } else { if (!ASN1_TIME_set_string_X509(X509_getm_notBefore(x), startdate)) { BIO_printf(bio_err, "Error setting notBefore certificate field\n"); return 0; } } if (enddate != NULL && strcmp(enddate, "today") == 0) { enddate = NULL; days = 0; } if (enddate == NULL) { if (X509_time_adj_ex(X509_getm_notAfter(x), days, 0, NULL) == NULL) { BIO_printf(bio_err, "Error setting notAfter certificate field\n"); return 0; } } else if (!ASN1_TIME_set_string_X509(X509_getm_notAfter(x), enddate)) { BIO_printf(bio_err, "Error setting notAfter certificate field\n"); return 0; } if (ASN1_TIME_compare(X509_get0_notAfter(x), X509_get0_notBefore(x)) < 0) { BIO_printf(bio_err, "%s: end date before start date\n", strict_compare_times ? "Error" : "Warning"); if (strict_compare_times) return 0; } return 1; } int set_crl_lastupdate(X509_CRL *crl, const char *lastupdate) { int ret = 0; ASN1_TIME *tm = ASN1_TIME_new(); if (tm == NULL) goto end; if (lastupdate == NULL) { if (X509_gmtime_adj(tm, 0) == NULL) goto end; } else { if (!ASN1_TIME_set_string_X509(tm, lastupdate)) goto end; } if (!X509_CRL_set1_lastUpdate(crl, tm)) goto end; ret = 1; end: ASN1_TIME_free(tm); return ret; } int set_crl_nextupdate(X509_CRL *crl, const char *nextupdate, long days, long hours, long secs) { int ret = 0; ASN1_TIME *tm = ASN1_TIME_new(); if (tm == NULL) goto end; if (nextupdate == NULL) { if (X509_time_adj_ex(tm, days, hours * 60 * 60 + secs, NULL) == NULL) goto end; } else { if (!ASN1_TIME_set_string_X509(tm, nextupdate)) goto end; } if (!X509_CRL_set1_nextUpdate(crl, tm)) goto end; ret = 1; end: ASN1_TIME_free(tm); return ret; } void make_uppercase(char *string) { int i; for (i = 0; string[i] != '\0'; i++) string[i] = toupper((unsigned char)string[i]); } OSSL_PARAM *app_params_new_from_opts(STACK_OF(OPENSSL_STRING) *opts, const OSSL_PARAM *paramdefs) { OSSL_PARAM *params = NULL; size_t sz = (size_t)sk_OPENSSL_STRING_num(opts); size_t params_n; char *opt = "", *stmp, *vtmp = NULL; int found = 1; if (opts == NULL) return NULL; params = OPENSSL_zalloc(sizeof(OSSL_PARAM) * (sz + 1)); if (params == NULL) return NULL; for (params_n = 0; params_n < sz; params_n++) { opt = sk_OPENSSL_STRING_value(opts, (int)params_n); if ((stmp = OPENSSL_strdup(opt)) == NULL || (vtmp = strchr(stmp, ':')) == NULL) goto err; /* Replace ':' with 0 to terminate the string pointed to by stmp */ *vtmp = 0; /* Skip over the separator so that vmtp points to the value */ vtmp++; if (!OSSL_PARAM_allocate_from_text(¶ms[params_n], paramdefs, stmp, vtmp, strlen(vtmp), &found)) goto err; OPENSSL_free(stmp); } params[params_n] = OSSL_PARAM_construct_end(); return params; err: OPENSSL_free(stmp); BIO_printf(bio_err, "Parameter %s '%s'\n", found ? "error" : "unknown", opt); ERR_print_errors(bio_err); app_params_free(params); return NULL; } void app_params_free(OSSL_PARAM *params) { int i; if (params != NULL) { for (i = 0; params[i].key != NULL; ++i) OPENSSL_free(params[i].data); OPENSSL_free(params); } } EVP_PKEY *app_keygen(EVP_PKEY_CTX *ctx, const char *alg, int bits, int verbose) { EVP_PKEY *res = NULL; if (verbose && alg != NULL) { BIO_printf(bio_err, "Generating %s key", alg); if (bits > 0) BIO_printf(bio_err, " with %d bits\n", bits); else BIO_printf(bio_err, "\n"); } if (!RAND_status()) BIO_printf(bio_err, "Warning: generating random key material may take a long time\n" "if the system has a poor entropy source\n"); if (EVP_PKEY_keygen(ctx, &res) <= 0) BIO_printf(bio_err, "%s: Error generating %s key\n", opt_getprog(), alg != NULL ? alg : "asymmetric"); return res; } EVP_PKEY *app_paramgen(EVP_PKEY_CTX *ctx, const char *alg) { EVP_PKEY *res = NULL; if (!RAND_status()) BIO_printf(bio_err, "Warning: generating random key parameters may take a long time\n" "if the system has a poor entropy source\n"); if (EVP_PKEY_paramgen(ctx, &res) <= 0) BIO_printf(bio_err, "%s: Generating %s key parameters failed\n", opt_getprog(), alg != NULL ? alg : "asymmetric"); return res; } /* * Return non-zero if the legacy path is still an option. * This decision is based on the global command line operations and the * behaviour thus far. */ int opt_legacy_okay(void) { int provider_options = opt_provider_option_given(); int libctx = app_get0_libctx() != NULL || app_get0_propq() != NULL; /* * Having a provider option specified or a custom library context or * property query, is a sure sign we're not using legacy. */ if (provider_options || libctx) return 0; return 1; } diff --git a/crypto/openssl/apps/pkeyutl.c b/crypto/openssl/apps/pkeyutl.c index 79ad4c6f29f5..5ddf1c02c09b 100644 --- a/crypto/openssl/apps/pkeyutl.c +++ b/crypto/openssl/apps/pkeyutl.c @@ -1,926 +1,926 @@ /* * Copyright 2006-2025 The OpenSSL Project Authors. All Rights Reserved. * * Licensed under the Apache License 2.0 (the "License"). You may not use * this file except in compliance with the License. You can obtain a copy * in the file LICENSE in the source distribution or at * https://www.openssl.org/source/license.html */ #include "apps.h" #include "progs.h" #include #include #include #include #include #define KEY_NONE 0 #define KEY_PRIVKEY 1 #define KEY_PUBKEY 2 #define KEY_CERT 3 static EVP_PKEY *get_pkey(const char *kdfalg, const char *keyfile, int keyform, int key_type, char *passinarg, int pkey_op, ENGINE *e); static EVP_PKEY_CTX *init_ctx(const char *kdfalg, int *pkeysize, int pkey_op, ENGINE *e, const int engine_impl, int rawin, EVP_PKEY *pkey /* ownership is passed to ctx */, EVP_MD_CTX *mctx, const char *digestname, const char *kemop, OSSL_LIB_CTX *libctx, const char *propq); static int setup_peer(EVP_PKEY_CTX *ctx, int peerform, const char *file, ENGINE *e); static int do_keyop(EVP_PKEY_CTX *ctx, int pkey_op, unsigned char *out, size_t *poutlen, const unsigned char *in, size_t inlen, unsigned char *secret, size_t *psecretlen); static int do_raw_keyop(int pkey_op, EVP_MD_CTX *mctx, EVP_PKEY *pkey, BIO *in, - int filesize, unsigned char *sig, int siglen, + int filesize, unsigned char *sig, size_t siglen, unsigned char **out, size_t *poutlen); static int only_nomd(EVP_PKEY *pkey) { #define MADE_UP_MAX_MD_NAME_LEN 100 char defname[MADE_UP_MAX_MD_NAME_LEN]; int deftype; deftype = EVP_PKEY_get_default_digest_name(pkey, defname, sizeof(defname)); return deftype == 2 /* Mandatory */ && strcmp(defname, "UNDEF") == 0; } typedef enum OPTION_choice { OPT_COMMON, OPT_ENGINE, OPT_ENGINE_IMPL, OPT_IN, OPT_OUT, OPT_PUBIN, OPT_CERTIN, OPT_ASN1PARSE, OPT_HEXDUMP, OPT_SIGN, OPT_VERIFY, OPT_VERIFYRECOVER, OPT_REV, OPT_ENCRYPT, OPT_DECRYPT, OPT_DERIVE, OPT_SIGFILE, OPT_INKEY, OPT_PEERKEY, OPT_PASSIN, OPT_PEERFORM, OPT_KEYFORM, OPT_PKEYOPT, OPT_PKEYOPT_PASSIN, OPT_KDF, OPT_KDFLEN, OPT_R_ENUM, OPT_PROV_ENUM, OPT_DECAP, OPT_ENCAP, OPT_SECOUT, OPT_KEMOP, OPT_CONFIG, OPT_RAWIN, OPT_DIGEST } OPTION_CHOICE; const OPTIONS pkeyutl_options[] = { OPT_SECTION("General"), {"help", OPT_HELP, '-', "Display this summary"}, #ifndef OPENSSL_NO_ENGINE {"engine", OPT_ENGINE, 's', "Use engine, possibly a hardware device"}, {"engine_impl", OPT_ENGINE_IMPL, '-', "Also use engine given by -engine for crypto operations"}, #endif {"sign", OPT_SIGN, '-', "Sign input data with private key"}, {"verify", OPT_VERIFY, '-', "Verify with public key"}, {"encrypt", OPT_ENCRYPT, '-', "Encrypt input data with public key"}, {"decrypt", OPT_DECRYPT, '-', "Decrypt input data with private key"}, {"derive", OPT_DERIVE, '-', "Derive shared secret from own and peer (EC)DH keys"}, {"decap", OPT_DECAP, '-', "Decapsulate shared secret"}, {"encap", OPT_ENCAP, '-', "Encapsulate shared secret"}, OPT_CONFIG_OPTION, OPT_SECTION("Input"), {"in", OPT_IN, '<', "Input file - default stdin"}, {"inkey", OPT_INKEY, 's', "Input key, by default private key"}, {"pubin", OPT_PUBIN, '-', "Input key is a public key"}, {"passin", OPT_PASSIN, 's', "Input file pass phrase source"}, {"peerkey", OPT_PEERKEY, 's', "Peer key file used in key derivation"}, {"peerform", OPT_PEERFORM, 'E', "Peer key format (DER/PEM/P12/ENGINE)"}, {"certin", OPT_CERTIN, '-', "Input is a cert with a public key"}, {"rev", OPT_REV, '-', "Reverse the order of the input buffer"}, {"sigfile", OPT_SIGFILE, '<', "Signature file (verify operation only)"}, {"keyform", OPT_KEYFORM, 'E', "Private key format (ENGINE, other values ignored)"}, OPT_SECTION("Output"), {"out", OPT_OUT, '>', "Output file - default stdout"}, {"secret", OPT_SECOUT, '>', "File to store secret on encapsulation"}, {"asn1parse", OPT_ASN1PARSE, '-', "parse the output as ASN.1 data to check its DER encoding and print errors"}, {"hexdump", OPT_HEXDUMP, '-', "Hex dump output"}, {"verifyrecover", OPT_VERIFYRECOVER, '-', "Verify RSA signature, recovering original signature input data"}, OPT_SECTION("Signing/Derivation/Encapsulation"), {"rawin", OPT_RAWIN, '-', "Indicate that the signature/verification input data is not yet hashed"}, {"digest", OPT_DIGEST, 's', "The digest algorithm to use for signing/verifying raw input data. Implies -rawin"}, {"pkeyopt", OPT_PKEYOPT, 's', "Public key options as opt:value"}, {"pkeyopt_passin", OPT_PKEYOPT_PASSIN, 's', "Public key option that is read as a passphrase argument opt:passphrase"}, {"kdf", OPT_KDF, 's', "Use KDF algorithm"}, {"kdflen", OPT_KDFLEN, 'p', "KDF algorithm output length"}, {"kemop", OPT_KEMOP, 's', "KEM operation specific to the key algorithm"}, OPT_R_OPTIONS, OPT_PROV_OPTIONS, {NULL} }; int pkeyutl_main(int argc, char **argv) { CONF *conf = NULL; BIO *in = NULL, *out = NULL, *secout = NULL; ENGINE *e = NULL; EVP_PKEY_CTX *ctx = NULL; EVP_PKEY *pkey = NULL; char *infile = NULL, *outfile = NULL, *secoutfile = NULL, *sigfile = NULL, *passinarg = NULL; char hexdump = 0, asn1parse = 0, rev = 0, *prog; unsigned char *buf_in = NULL, *buf_out = NULL, *sig = NULL, *secret = NULL; OPTION_CHOICE o; - int buf_inlen = 0, siglen = -1; + size_t buf_inlen = 0, siglen = 0; int keyform = FORMAT_UNDEF, peerform = FORMAT_UNDEF; int keysize = -1, pkey_op = EVP_PKEY_OP_SIGN, key_type = KEY_PRIVKEY; int engine_impl = 0; int ret = 1, rv = -1; size_t buf_outlen = 0, secretlen = 0; const char *inkey = NULL; const char *peerkey = NULL; const char *kdfalg = NULL, *digestname = NULL, *kemop = NULL; int kdflen = 0; STACK_OF(OPENSSL_STRING) *pkeyopts = NULL; STACK_OF(OPENSSL_STRING) *pkeyopts_passin = NULL; int rawin = 0; EVP_MD_CTX *mctx = NULL; EVP_MD *md = NULL; int filesize = -1; OSSL_LIB_CTX *libctx = app_get0_libctx(); prog = opt_init(argc, argv, pkeyutl_options); while ((o = opt_next()) != OPT_EOF) { switch (o) { case OPT_EOF: case OPT_ERR: opthelp: BIO_printf(bio_err, "%s: Use -help for summary.\n", prog); goto end; case OPT_HELP: opt_help(pkeyutl_options); ret = 0; goto end; case OPT_IN: infile = opt_arg(); break; case OPT_OUT: outfile = opt_arg(); break; case OPT_SECOUT: secoutfile = opt_arg(); break; case OPT_SIGFILE: sigfile = opt_arg(); break; case OPT_ENGINE_IMPL: engine_impl = 1; break; case OPT_INKEY: inkey = opt_arg(); break; case OPT_PEERKEY: peerkey = opt_arg(); break; case OPT_PASSIN: passinarg = opt_arg(); break; case OPT_PEERFORM: if (!opt_format(opt_arg(), OPT_FMT_ANY, &peerform)) goto opthelp; break; case OPT_KEYFORM: if (!opt_format(opt_arg(), OPT_FMT_ANY, &keyform)) goto opthelp; break; case OPT_R_CASES: if (!opt_rand(o)) goto end; break; case OPT_CONFIG: conf = app_load_config_modules(opt_arg()); if (conf == NULL) goto end; break; case OPT_PROV_CASES: if (!opt_provider(o)) goto end; break; case OPT_ENGINE: e = setup_engine(opt_arg(), 0); break; case OPT_PUBIN: key_type = KEY_PUBKEY; break; case OPT_CERTIN: key_type = KEY_CERT; break; case OPT_ASN1PARSE: asn1parse = 1; break; case OPT_HEXDUMP: hexdump = 1; break; case OPT_SIGN: pkey_op = EVP_PKEY_OP_SIGN; break; case OPT_VERIFY: pkey_op = EVP_PKEY_OP_VERIFY; break; case OPT_VERIFYRECOVER: pkey_op = EVP_PKEY_OP_VERIFYRECOVER; break; case OPT_ENCRYPT: pkey_op = EVP_PKEY_OP_ENCRYPT; break; case OPT_DECRYPT: pkey_op = EVP_PKEY_OP_DECRYPT; break; case OPT_DERIVE: pkey_op = EVP_PKEY_OP_DERIVE; break; case OPT_DECAP: pkey_op = EVP_PKEY_OP_DECAPSULATE; break; case OPT_ENCAP: key_type = KEY_PUBKEY; pkey_op = EVP_PKEY_OP_ENCAPSULATE; break; case OPT_KEMOP: kemop = opt_arg(); break; case OPT_KDF: pkey_op = EVP_PKEY_OP_DERIVE; key_type = KEY_NONE; kdfalg = opt_arg(); break; case OPT_KDFLEN: kdflen = atoi(opt_arg()); break; case OPT_REV: rev = 1; break; case OPT_PKEYOPT: if ((pkeyopts == NULL && (pkeyopts = sk_OPENSSL_STRING_new_null()) == NULL) || sk_OPENSSL_STRING_push(pkeyopts, opt_arg()) == 0) { BIO_puts(bio_err, "out of memory\n"); goto end; } break; case OPT_PKEYOPT_PASSIN: if ((pkeyopts_passin == NULL && (pkeyopts_passin = sk_OPENSSL_STRING_new_null()) == NULL) || sk_OPENSSL_STRING_push(pkeyopts_passin, opt_arg()) == 0) { BIO_puts(bio_err, "out of memory\n"); goto end; } break; case OPT_RAWIN: rawin = 1; break; case OPT_DIGEST: digestname = opt_arg(); break; } } /* No extra arguments. */ if (!opt_check_rest_arg(NULL)) goto opthelp; if (!app_RAND_load()) goto end; if (digestname != NULL) rawin = 1; if (kdfalg != NULL) { if (kdflen == 0) { BIO_printf(bio_err, "%s: no KDF length given (-kdflen parameter).\n", prog); goto opthelp; } } else if (inkey == NULL) { BIO_printf(bio_err, "%s: no private key given (-inkey parameter).\n", prog); goto opthelp; } else if (peerkey != NULL && pkey_op != EVP_PKEY_OP_DERIVE) { BIO_printf(bio_err, "%s: -peerkey option not allowed without -derive.\n", prog); goto opthelp; } else if (peerkey == NULL && pkey_op == EVP_PKEY_OP_DERIVE) { BIO_printf(bio_err, "%s: missing -peerkey option for -derive operation.\n", prog); goto opthelp; } pkey = get_pkey(kdfalg, inkey, keyform, key_type, passinarg, pkey_op, e); if (key_type != KEY_NONE && pkey == NULL) { BIO_printf(bio_err, "%s: Error loading key\n", prog); goto end; } if (pkey_op == EVP_PKEY_OP_VERIFYRECOVER && !EVP_PKEY_is_a(pkey, "RSA")) { BIO_printf(bio_err, "%s: -verifyrecover can be used only with RSA\n", prog); goto end; } if (pkey_op == EVP_PKEY_OP_SIGN || pkey_op == EVP_PKEY_OP_VERIFY) { if (only_nomd(pkey)) { if (digestname != NULL) { const char *alg = EVP_PKEY_get0_type_name(pkey); BIO_printf(bio_err, "%s: -digest (prehash) is not supported with %s\n", prog, alg != NULL ? alg : "(unknown key type)"); goto end; } rawin = 1; } } else if (digestname != NULL || rawin) { BIO_printf(bio_err, "%s: -digest and -rawin can only be used with -sign or -verify\n", prog); goto opthelp; } if (rawin && rev) { BIO_printf(bio_err, "%s: -rev cannot be used with raw input\n", prog); goto opthelp; } if (rawin) { if ((mctx = EVP_MD_CTX_new()) == NULL) { BIO_printf(bio_err, "Error: out of memory\n"); goto end; } } ctx = init_ctx(kdfalg, &keysize, pkey_op, e, engine_impl, rawin, pkey, mctx, digestname, kemop, libctx, app_get0_propq()); if (ctx == NULL) { BIO_printf(bio_err, "%s: Error initializing context\n", prog); goto end; } if (peerkey != NULL && !setup_peer(ctx, peerform, peerkey, e)) { BIO_printf(bio_err, "%s: Error setting up peer key\n", prog); goto end; } if (pkeyopts != NULL) { int num = sk_OPENSSL_STRING_num(pkeyopts); int i; for (i = 0; i < num; ++i) { const char *opt = sk_OPENSSL_STRING_value(pkeyopts, i); if (pkey_ctrl_string(ctx, opt) <= 0) { BIO_printf(bio_err, "%s: Can't set parameter \"%s\":\n", prog, opt); goto end; } } } if (pkeyopts_passin != NULL) { int num = sk_OPENSSL_STRING_num(pkeyopts_passin); int i; for (i = 0; i < num; i++) { char *opt = sk_OPENSSL_STRING_value(pkeyopts_passin, i); char *passin = strchr(opt, ':'); char *passwd; if (passin == NULL) { /* Get password interactively */ char passwd_buf[4096]; int r; BIO_snprintf(passwd_buf, sizeof(passwd_buf), "Enter %s: ", opt); r = EVP_read_pw_string(passwd_buf, sizeof(passwd_buf) - 1, passwd_buf, 0); if (r < 0) { if (r == -2) BIO_puts(bio_err, "user abort\n"); else BIO_puts(bio_err, "entry failed\n"); goto end; } passwd = OPENSSL_strdup(passwd_buf); if (passwd == NULL) { BIO_puts(bio_err, "out of memory\n"); goto end; } } else { /* * Get password as a passin argument: First split option name * and passphrase argument into two strings */ *passin = 0; passin++; if (app_passwd(passin, NULL, &passwd, NULL) == 0) { BIO_printf(bio_err, "failed to get '%s'\n", opt); goto end; } } if (EVP_PKEY_CTX_ctrl_str(ctx, opt, passwd) <= 0) { BIO_printf(bio_err, "%s: Can't set parameter \"%s\":\n", prog, opt); OPENSSL_free(passwd); goto end; } OPENSSL_free(passwd); } } if (sigfile != NULL && (pkey_op != EVP_PKEY_OP_VERIFY)) { BIO_printf(bio_err, "%s: Signature file specified for non verify\n", prog); goto end; } if (sigfile == NULL && (pkey_op == EVP_PKEY_OP_VERIFY)) { BIO_printf(bio_err, "%s: No signature file specified for verify\n", prog); goto end; } if (pkey_op != EVP_PKEY_OP_DERIVE && pkey_op != EVP_PKEY_OP_ENCAPSULATE) { in = bio_open_default(infile, 'r', FORMAT_BINARY); if (infile != NULL) { struct stat st; if (stat(infile, &st) == 0 && st.st_size <= INT_MAX) filesize = (int)st.st_size; } if (in == NULL) goto end; } if (pkey_op == EVP_PKEY_OP_DECAPSULATE && outfile != NULL) { if (secoutfile != NULL) { BIO_printf(bio_err, "%s: Decapsulation produces only a shared " "secret and no output. The '-out' option " "is not applicable.\n", prog); goto end; } if ((out = bio_open_owner(outfile, 'w', FORMAT_BINARY)) == NULL) goto end; } else { out = bio_open_default(outfile, 'w', FORMAT_BINARY); if (out == NULL) goto end; } if (pkey_op == EVP_PKEY_OP_ENCAPSULATE || pkey_op == EVP_PKEY_OP_DECAPSULATE) { if (secoutfile == NULL && pkey_op == EVP_PKEY_OP_ENCAPSULATE) { BIO_printf(bio_err, "KEM-based shared-secret derivation requires " "the '-secret ' option\n"); goto end; } /* For backwards compatibility, default decap secrets to the output */ if (secoutfile != NULL && (secout = bio_open_owner(secoutfile, 'w', FORMAT_BINARY)) == NULL) goto end; } if (sigfile != NULL) { BIO *sigbio = BIO_new_file(sigfile, "rb"); + size_t maxsiglen = 16 * 1024 * 1024; if (sigbio == NULL) { BIO_printf(bio_err, "Can't open signature file %s\n", sigfile); goto end; } - siglen = bio_to_mem(&sig, keysize * 10, sigbio); - BIO_free(sigbio); - if (siglen < 0) { + if (!bio_to_mem(&sig, &siglen, maxsiglen, sigbio)) { + BIO_free(sigbio); BIO_printf(bio_err, "Error reading signature data\n"); goto end; } + BIO_free(sigbio); } /* Raw input data is handled elsewhere */ if (in != NULL && !rawin) { /* Read the input data */ - buf_inlen = bio_to_mem(&buf_in, -1, in); - if (buf_inlen < 0) { + if (!bio_to_mem(&buf_in, &buf_inlen, 0, in)) { BIO_printf(bio_err, "Error reading input Data\n"); goto end; } if (rev) { size_t i; unsigned char ctmp; - size_t l = (size_t)buf_inlen; + size_t l = buf_inlen; for (i = 0; i < l / 2; i++) { ctmp = buf_in[i]; buf_in[i] = buf_in[l - 1 - i]; buf_in[l - 1 - i] = ctmp; } } } /* Sanity check the input if the input is not raw */ if (!rawin && (pkey_op == EVP_PKEY_OP_SIGN || pkey_op == EVP_PKEY_OP_VERIFY)) { if (buf_inlen > EVP_MAX_MD_SIZE) { BIO_printf(bio_err, - "Error: The non-raw input data length %d is too long - max supported hashed size is %d\n", + "Error: The non-raw input data length %zd is too long - " + "max supported hashed size is %d\n", buf_inlen, EVP_MAX_MD_SIZE); goto end; } } if (pkey_op == EVP_PKEY_OP_VERIFY) { if (rawin) { rv = do_raw_keyop(pkey_op, mctx, pkey, in, filesize, sig, siglen, NULL, 0); } else { - rv = EVP_PKEY_verify(ctx, sig, (size_t)siglen, - buf_in, (size_t)buf_inlen); + rv = EVP_PKEY_verify(ctx, sig, siglen, buf_in, buf_inlen); } if (rv == 1) { BIO_puts(out, "Signature Verified Successfully\n"); ret = 0; } else { BIO_puts(out, "Signature Verification Failure\n"); } goto end; } if (rawin) { /* rawin allocates the buffer in do_raw_keyop() */ rv = do_raw_keyop(pkey_op, mctx, pkey, in, filesize, NULL, 0, &buf_out, (size_t *)&buf_outlen); } else { if (kdflen != 0) { buf_outlen = kdflen; rv = 1; } else { - rv = do_keyop(ctx, pkey_op, NULL, (size_t *)&buf_outlen, - buf_in, (size_t)buf_inlen, NULL, (size_t *)&secretlen); + rv = do_keyop(ctx, pkey_op, NULL, &buf_outlen, + buf_in, buf_inlen, NULL, &secretlen); } if (rv > 0 && (secretlen > 0 || (pkey_op != EVP_PKEY_OP_ENCAPSULATE && pkey_op != EVP_PKEY_OP_DECAPSULATE)) && (buf_outlen > 0 || pkey_op == EVP_PKEY_OP_DECAPSULATE)) { if (buf_outlen > 0) buf_out = app_malloc(buf_outlen, "buffer output"); if (secretlen > 0) secret = app_malloc(secretlen, "secret output"); rv = do_keyop(ctx, pkey_op, - buf_out, (size_t *)&buf_outlen, - buf_in, (size_t)buf_inlen, secret, (size_t *)&secretlen); + buf_out, &buf_outlen, + buf_in, buf_inlen, secret, &secretlen); } } if (rv <= 0) { if (pkey_op != EVP_PKEY_OP_DERIVE) { BIO_puts(bio_err, "Public Key operation error\n"); } else { BIO_puts(bio_err, "Key derivation failed\n"); } goto end; } ret = 0; if (asn1parse) { if (!ASN1_parse_dump(out, buf_out, buf_outlen, 1, -1)) ERR_print_errors(bio_err); /* but still return success */ } else if (hexdump) { BIO_dump(out, (char *)buf_out, buf_outlen); } else { BIO_write(out, buf_out, buf_outlen); } /* Backwards compatible decap output fallback */ if (secretlen > 0) BIO_write(secout ? secout : out, secret, secretlen); end: if (ret != 0) ERR_print_errors(bio_err); EVP_MD_CTX_free(mctx); EVP_PKEY_CTX_free(ctx); EVP_PKEY_free(pkey); EVP_MD_free(md); release_engine(e); BIO_free(in); BIO_free_all(out); BIO_free_all(secout); OPENSSL_free(buf_in); OPENSSL_free(buf_out); OPENSSL_free(sig); OPENSSL_free(secret); sk_OPENSSL_STRING_free(pkeyopts); sk_OPENSSL_STRING_free(pkeyopts_passin); NCONF_free(conf); return ret; } static EVP_PKEY *get_pkey(const char *kdfalg, const char *keyfile, int keyform, int key_type, char *passinarg, int pkey_op, ENGINE *e) { EVP_PKEY *pkey = NULL; char *passin = NULL; X509 *x; if (((pkey_op == EVP_PKEY_OP_SIGN) || (pkey_op == EVP_PKEY_OP_DECRYPT) || (pkey_op == EVP_PKEY_OP_DERIVE)) && (key_type != KEY_PRIVKEY && kdfalg == NULL)) { BIO_printf(bio_err, "A private key is needed for this operation\n"); return NULL; } if (!app_passwd(passinarg, NULL, &passin, NULL)) { BIO_printf(bio_err, "Error getting password\n"); return NULL; } switch (key_type) { case KEY_PRIVKEY: pkey = load_key(keyfile, keyform, 0, passin, e, "private key"); break; case KEY_PUBKEY: pkey = load_pubkey(keyfile, keyform, 0, NULL, e, "public key"); break; case KEY_CERT: x = load_cert(keyfile, keyform, "Certificate"); if (x) { pkey = X509_get_pubkey(x); X509_free(x); } break; case KEY_NONE: break; } OPENSSL_free(passin); return pkey; } static EVP_PKEY_CTX *init_ctx(const char *kdfalg, int *pkeysize, int pkey_op, ENGINE *e, const int engine_impl, int rawin, EVP_PKEY *pkey /* ownership is passed to ctx */, EVP_MD_CTX *mctx, const char *digestname, const char *kemop, OSSL_LIB_CTX *libctx, const char *propq) { EVP_PKEY_CTX *ctx = NULL; ENGINE *impl = NULL; int rv = -1; #ifndef OPENSSL_NO_ENGINE if (engine_impl) impl = e; #endif if (kdfalg != NULL) { int kdfnid = OBJ_sn2nid(kdfalg); if (kdfnid == NID_undef) { kdfnid = OBJ_ln2nid(kdfalg); if (kdfnid == NID_undef) { BIO_printf(bio_err, "The given KDF \"%s\" is unknown.\n", kdfalg); return NULL; } } if (impl != NULL) ctx = EVP_PKEY_CTX_new_id(kdfnid, impl); else ctx = EVP_PKEY_CTX_new_from_name(libctx, kdfalg, propq); } else { if (pkey == NULL) return NULL; *pkeysize = EVP_PKEY_get_size(pkey); if (impl != NULL) ctx = EVP_PKEY_CTX_new(pkey, impl); else ctx = EVP_PKEY_CTX_new_from_pkey(libctx, pkey, propq); } if (ctx == NULL) return NULL; if (rawin) { EVP_MD_CTX_set_pkey_ctx(mctx, ctx); switch (pkey_op) { case EVP_PKEY_OP_SIGN: rv = EVP_DigestSignInit_ex(mctx, NULL, digestname, libctx, propq, pkey, NULL); break; case EVP_PKEY_OP_VERIFY: rv = EVP_DigestVerifyInit_ex(mctx, NULL, digestname, libctx, propq, pkey, NULL); break; } } else { switch (pkey_op) { case EVP_PKEY_OP_SIGN: rv = EVP_PKEY_sign_init(ctx); break; case EVP_PKEY_OP_VERIFY: rv = EVP_PKEY_verify_init(ctx); break; case EVP_PKEY_OP_VERIFYRECOVER: rv = EVP_PKEY_verify_recover_init(ctx); break; case EVP_PKEY_OP_ENCRYPT: rv = EVP_PKEY_encrypt_init(ctx); break; case EVP_PKEY_OP_DECRYPT: rv = EVP_PKEY_decrypt_init(ctx); break; case EVP_PKEY_OP_DERIVE: rv = EVP_PKEY_derive_init(ctx); break; case EVP_PKEY_OP_ENCAPSULATE: rv = EVP_PKEY_encapsulate_init(ctx, NULL); if (rv > 0 && kemop != NULL) rv = EVP_PKEY_CTX_set_kem_op(ctx, kemop); break; case EVP_PKEY_OP_DECAPSULATE: rv = EVP_PKEY_decapsulate_init(ctx, NULL); if (rv > 0 && kemop != NULL) rv = EVP_PKEY_CTX_set_kem_op(ctx, kemop); break; } } if (rv <= 0) { EVP_PKEY_CTX_free(ctx); ctx = NULL; } return ctx; } static int setup_peer(EVP_PKEY_CTX *ctx, int peerform, const char *file, ENGINE *e) { EVP_PKEY *pkey = EVP_PKEY_CTX_get0_pkey(ctx); EVP_PKEY *peer = NULL; ENGINE *engine = NULL; int ret = 1; if (peerform == FORMAT_ENGINE) engine = e; peer = load_pubkey(file, peerform, 0, NULL, engine, "peer key"); if (peer == NULL) { BIO_printf(bio_err, "Error reading peer key %s\n", file); return 0; } if (strcmp(EVP_PKEY_get0_type_name(peer), EVP_PKEY_get0_type_name(pkey)) != 0) { BIO_printf(bio_err, "Type of peer public key: %s does not match type of private key: %s\n", EVP_PKEY_get0_type_name(peer), EVP_PKEY_get0_type_name(pkey)); ret = 0; } else { ret = EVP_PKEY_derive_set_peer(ctx, peer) > 0; } EVP_PKEY_free(peer); return ret; } static int do_keyop(EVP_PKEY_CTX *ctx, int pkey_op, unsigned char *out, size_t *poutlen, const unsigned char *in, size_t inlen, unsigned char *secret, size_t *pseclen) { int rv = 0; switch (pkey_op) { case EVP_PKEY_OP_VERIFYRECOVER: rv = EVP_PKEY_verify_recover(ctx, out, poutlen, in, inlen); break; case EVP_PKEY_OP_SIGN: rv = EVP_PKEY_sign(ctx, out, poutlen, in, inlen); break; case EVP_PKEY_OP_ENCRYPT: rv = EVP_PKEY_encrypt(ctx, out, poutlen, in, inlen); break; case EVP_PKEY_OP_DECRYPT: rv = EVP_PKEY_decrypt(ctx, out, poutlen, in, inlen); break; case EVP_PKEY_OP_DERIVE: rv = EVP_PKEY_derive(ctx, out, poutlen); break; case EVP_PKEY_OP_ENCAPSULATE: rv = EVP_PKEY_encapsulate(ctx, out, poutlen, secret, pseclen); break; case EVP_PKEY_OP_DECAPSULATE: rv = EVP_PKEY_decapsulate(ctx, secret, pseclen, in, inlen); break; } return rv; } #define TBUF_MAXSIZE 2048 static int do_raw_keyop(int pkey_op, EVP_MD_CTX *mctx, EVP_PKEY *pkey, BIO *in, - int filesize, unsigned char *sig, int siglen, + int filesize, unsigned char *sig, size_t siglen, unsigned char **out, size_t *poutlen) { int rv = 0; unsigned char tbuf[TBUF_MAXSIZE]; unsigned char *mbuf = NULL; int buf_len = 0; /* Some algorithms only support oneshot digests */ if (only_nomd(pkey)) { if (filesize < 0) { BIO_printf(bio_err, "Error: unable to determine file size for oneshot operation\n"); goto end; } mbuf = app_malloc(filesize, "oneshot sign/verify buffer"); switch (pkey_op) { case EVP_PKEY_OP_VERIFY: buf_len = BIO_read(in, mbuf, filesize); if (buf_len != filesize) { BIO_printf(bio_err, "Error reading raw input data\n"); goto end; } - rv = EVP_DigestVerify(mctx, sig, (size_t)siglen, mbuf, buf_len); + rv = EVP_DigestVerify(mctx, sig, siglen, mbuf, buf_len); break; case EVP_PKEY_OP_SIGN: buf_len = BIO_read(in, mbuf, filesize); if (buf_len != filesize) { BIO_printf(bio_err, "Error reading raw input data\n"); goto end; } rv = EVP_DigestSign(mctx, NULL, poutlen, mbuf, buf_len); if (rv == 1 && out != NULL) { *out = app_malloc(*poutlen, "buffer output"); rv = EVP_DigestSign(mctx, *out, poutlen, mbuf, buf_len); } break; } goto end; } switch (pkey_op) { case EVP_PKEY_OP_VERIFY: for (;;) { buf_len = BIO_read(in, tbuf, TBUF_MAXSIZE); if (buf_len == 0) break; if (buf_len < 0) { BIO_printf(bio_err, "Error reading raw input data\n"); goto end; } rv = EVP_DigestVerifyUpdate(mctx, tbuf, (size_t)buf_len); if (rv != 1) { BIO_printf(bio_err, "Error verifying raw input data\n"); goto end; } } - rv = EVP_DigestVerifyFinal(mctx, sig, (size_t)siglen); + rv = EVP_DigestVerifyFinal(mctx, sig, siglen); break; case EVP_PKEY_OP_SIGN: for (;;) { buf_len = BIO_read(in, tbuf, TBUF_MAXSIZE); if (buf_len == 0) break; if (buf_len < 0) { BIO_printf(bio_err, "Error reading raw input data\n"); goto end; } rv = EVP_DigestSignUpdate(mctx, tbuf, (size_t)buf_len); if (rv != 1) { BIO_printf(bio_err, "Error signing raw input data\n"); goto end; } } rv = EVP_DigestSignFinal(mctx, NULL, poutlen); if (rv == 1 && out != NULL) { *out = app_malloc(*poutlen, "buffer output"); rv = EVP_DigestSignFinal(mctx, *out, poutlen); } break; } end: OPENSSL_free(mbuf); return rv; } diff --git a/crypto/openssl/apps/s_client.c b/crypto/openssl/apps/s_client.c index ffb4597a197c..5c7f4a93c9fa 100644 --- a/crypto/openssl/apps/s_client.c +++ b/crypto/openssl/apps/s_client.c @@ -1,4088 +1,4089 @@ /* * Copyright 1995-2025 The OpenSSL Project Authors. All Rights Reserved. * Copyright 2005 Nokia. All rights reserved. * * Licensed under the Apache License 2.0 (the "License"). You may not use * this file except in compliance with the License. You can obtain a copy * in the file LICENSE in the source distribution or at * https://www.openssl.org/source/license.html */ #include "internal/e_os.h" #include #include #include #include #include #include #include "internal/nelem.h" #include "internal/sockets.h" /* for openssl_fdset() */ #ifndef OPENSSL_NO_SOCK /* * With IPv6, it looks like Digital has mixed up the proper order of * recursive header file inclusion, resulting in the compiler complaining * that u_int isn't defined, but only if _POSIX_C_SOURCE is defined, which is * needed to have fileno() declared correctly... So let's define u_int */ #if defined(OPENSSL_SYS_VMS_DECC) && !defined(__U_INT) # define __U_INT typedef unsigned int u_int; #endif #include "apps.h" #include "progs.h" #include #include #include #include #include #include #include #include #include #ifndef OPENSSL_NO_CT # include #endif #include "s_apps.h" #include "timeouts.h" #include "internal/sockets.h" #if defined(__has_feature) # if __has_feature(memory_sanitizer) # include # endif #endif #undef BUFSIZZ #define BUFSIZZ 1024*16 #define S_CLIENT_IRC_READ_TIMEOUT 8 #define USER_DATA_MODE_NONE 0 #define USER_DATA_MODE_BASIC 1 #define USER_DATA_MODE_ADVANCED 2 #define USER_DATA_PROCESS_BAD_ARGUMENT 0 #define USER_DATA_PROCESS_SHUT 1 #define USER_DATA_PROCESS_RESTART 2 #define USER_DATA_PROCESS_NO_DATA 3 #define USER_DATA_PROCESS_CONTINUE 4 struct user_data_st { /* SSL connection we are processing commands for */ SSL *con; /* Buffer where we are storing data supplied by the user */ char *buf; /* Allocated size of the buffer */ size_t bufmax; /* Amount of the buffer actually used */ size_t buflen; /* Current location in the buffer where we will read from next*/ size_t bufoff; /* The mode we are using for processing commands */ int mode; /* Whether FIN has ben sent on the stream */ int isfin; }; static void user_data_init(struct user_data_st *user_data, SSL *con, char *buf, size_t bufmax, int mode); static int user_data_add(struct user_data_st *user_data, size_t i); static int user_data_process(struct user_data_st *user_data, size_t *len, size_t *off); static int user_data_has_data(struct user_data_st *user_data); static char *prog; static int c_debug = 0; static int c_showcerts = 0; static char *keymatexportlabel = NULL; static int keymatexportlen = 20; static BIO *bio_c_out = NULL; static int c_quiet = 0; static char *sess_out = NULL; static SSL_SESSION *psksess = NULL; static void print_stuff(BIO *berr, SSL *con, int full); #ifndef OPENSSL_NO_OCSP static int ocsp_resp_cb(SSL *s, void *arg); #endif static int ldap_ExtendedResponse_parse(const char *buf, long rem); static int is_dNS_name(const char *host); static const unsigned char cert_type_rpk[] = { TLSEXT_cert_type_rpk, TLSEXT_cert_type_x509 }; static int enable_server_rpk = 0; static int saved_errno; static void save_errno(void) { saved_errno = errno; errno = 0; } static int restore_errno(void) { int ret = errno; errno = saved_errno; return ret; } /* Default PSK identity and key */ static char *psk_identity = "Client_identity"; #ifndef OPENSSL_NO_PSK static unsigned int psk_client_cb(SSL *ssl, const char *hint, char *identity, unsigned int max_identity_len, unsigned char *psk, unsigned int max_psk_len) { int ret; long key_len; unsigned char *key; if (c_debug) BIO_printf(bio_c_out, "psk_client_cb\n"); if (!hint) { /* no ServerKeyExchange message */ if (c_debug) BIO_printf(bio_c_out, "NULL received PSK identity hint, continuing anyway\n"); } else if (c_debug) { BIO_printf(bio_c_out, "Received PSK identity hint '%s'\n", hint); } /* * lookup PSK identity and PSK key based on the given identity hint here */ ret = BIO_snprintf(identity, max_identity_len, "%s", psk_identity); if (ret < 0 || (unsigned int)ret > max_identity_len) goto out_err; if (c_debug) BIO_printf(bio_c_out, "created identity '%s' len=%d\n", identity, ret); /* convert the PSK key to binary */ key = OPENSSL_hexstr2buf(psk_key, &key_len); if (key == NULL) { BIO_printf(bio_err, "Could not convert PSK key '%s' to buffer\n", psk_key); return 0; } if (max_psk_len > INT_MAX || key_len > (long)max_psk_len) { BIO_printf(bio_err, "psk buffer of callback is too small (%d) for key (%ld)\n", max_psk_len, key_len); OPENSSL_free(key); return 0; } memcpy(psk, key, key_len); OPENSSL_free(key); if (c_debug) BIO_printf(bio_c_out, "created PSK len=%ld\n", key_len); return key_len; out_err: if (c_debug) BIO_printf(bio_err, "Error in PSK client callback\n"); return 0; } #endif const unsigned char tls13_aes128gcmsha256_id[] = { 0x13, 0x01 }; const unsigned char tls13_aes256gcmsha384_id[] = { 0x13, 0x02 }; static int psk_use_session_cb(SSL *s, const EVP_MD *md, const unsigned char **id, size_t *idlen, SSL_SESSION **sess) { SSL_SESSION *usesess = NULL; const SSL_CIPHER *cipher = NULL; if (psksess != NULL) { if (!SSL_SESSION_up_ref(psksess)) goto err; usesess = psksess; } else { long key_len; unsigned char *key = OPENSSL_hexstr2buf(psk_key, &key_len); if (key == NULL) { BIO_printf(bio_err, "Could not convert PSK key '%s' to buffer\n", psk_key); return 0; } /* We default to SHA-256 */ cipher = SSL_CIPHER_find(s, tls13_aes128gcmsha256_id); if (cipher == NULL) { BIO_printf(bio_err, "Error finding suitable ciphersuite\n"); OPENSSL_free(key); return 0; } usesess = SSL_SESSION_new(); if (usesess == NULL || !SSL_SESSION_set1_master_key(usesess, key, key_len) || !SSL_SESSION_set_cipher(usesess, cipher) || !SSL_SESSION_set_protocol_version(usesess, TLS1_3_VERSION)) { OPENSSL_free(key); goto err; } OPENSSL_free(key); } cipher = SSL_SESSION_get0_cipher(usesess); if (cipher == NULL) goto err; if (md != NULL && SSL_CIPHER_get_handshake_digest(cipher) != md) { /* PSK not usable, ignore it */ *id = NULL; *idlen = 0; *sess = NULL; SSL_SESSION_free(usesess); } else { *sess = usesess; *id = (unsigned char *)psk_identity; *idlen = strlen(psk_identity); } return 1; err: SSL_SESSION_free(usesess); return 0; } /* This is a context that we pass to callbacks */ typedef struct tlsextctx_st { BIO *biodebug; int ack; } tlsextctx; static int ssl_servername_cb(SSL *s, int *ad, void *arg) { tlsextctx *p = (tlsextctx *) arg; const char *hn = SSL_get_servername(s, TLSEXT_NAMETYPE_host_name); if (SSL_get_servername_type(s) != -1) p->ack = !SSL_session_reused(s) && hn != NULL; else BIO_printf(bio_err, "Can't use SSL_get_servername\n"); return SSL_TLSEXT_ERR_OK; } #ifndef OPENSSL_NO_NEXTPROTONEG /* This the context that we pass to next_proto_cb */ typedef struct tlsextnextprotoctx_st { unsigned char *data; size_t len; int status; } tlsextnextprotoctx; static tlsextnextprotoctx next_proto; static int next_proto_cb(SSL *s, unsigned char **out, unsigned char *outlen, const unsigned char *in, unsigned int inlen, void *arg) { tlsextnextprotoctx *ctx = arg; if (!c_quiet) { /* We can assume that |in| is syntactically valid. */ unsigned i; BIO_printf(bio_c_out, "Protocols advertised by server: "); for (i = 0; i < inlen;) { if (i) BIO_write(bio_c_out, ", ", 2); BIO_write(bio_c_out, &in[i + 1], in[i]); i += in[i] + 1; } BIO_write(bio_c_out, "\n", 1); } ctx->status = SSL_select_next_proto(out, outlen, in, inlen, ctx->data, ctx->len); return SSL_TLSEXT_ERR_OK; } #endif /* ndef OPENSSL_NO_NEXTPROTONEG */ static int serverinfo_cli_parse_cb(SSL *s, unsigned int ext_type, const unsigned char *in, size_t inlen, int *al, void *arg) { char pem_name[100]; unsigned char ext_buf[4 + 65536]; /* Reconstruct the type/len fields prior to extension data */ inlen &= 0xffff; /* for formal memcmpy correctness */ ext_buf[0] = (unsigned char)(ext_type >> 8); ext_buf[1] = (unsigned char)(ext_type); ext_buf[2] = (unsigned char)(inlen >> 8); ext_buf[3] = (unsigned char)(inlen); memcpy(ext_buf + 4, in, inlen); BIO_snprintf(pem_name, sizeof(pem_name), "SERVERINFO FOR EXTENSION %d", ext_type); PEM_write_bio(bio_c_out, pem_name, "", ext_buf, 4 + inlen); return 1; } /* * Hex decoder that tolerates optional whitespace. Returns number of bytes * produced, advances inptr to end of input string. */ static ossl_ssize_t hexdecode(const char **inptr, void *result) { unsigned char **out = (unsigned char **)result; const char *in = *inptr; unsigned char *ret = app_malloc(strlen(in) / 2, "hexdecode"); unsigned char *cp = ret; uint8_t byte; int nibble = 0; if (ret == NULL) return -1; for (byte = 0; *in; ++in) { int x; if (isspace(_UC(*in))) continue; x = OPENSSL_hexchar2int(*in); if (x < 0) { OPENSSL_free(ret); return 0; } byte |= (char)x; if ((nibble ^= 1) == 0) { *cp++ = byte; byte = 0; } else { byte <<= 4; } } if (nibble != 0) { OPENSSL_free(ret); return 0; } *inptr = in; return cp - (*out = ret); } /* * Decode unsigned 0..255, returns 1 on success, <= 0 on failure. Advances * inptr to next field skipping leading whitespace. */ static ossl_ssize_t checked_uint8(const char **inptr, void *out) { uint8_t *result = (uint8_t *)out; const char *in = *inptr; char *endp; long v; int e; save_errno(); v = strtol(in, &endp, 10); e = restore_errno(); if (((v == LONG_MIN || v == LONG_MAX) && e == ERANGE) || endp == in || !isspace(_UC(*endp)) || v != (*result = (uint8_t) v)) { return -1; } for (in = endp; isspace(_UC(*in)); ++in) continue; *inptr = in; return 1; } struct tlsa_field { void *var; const char *name; ossl_ssize_t (*parser)(const char **, void *); }; static int tlsa_import_rr(SSL *con, const char *rrdata) { /* Not necessary to re-init these values; the "parsers" do that. */ static uint8_t usage; static uint8_t selector; static uint8_t mtype; static unsigned char *data; static struct tlsa_field tlsa_fields[] = { { &usage, "usage", checked_uint8 }, { &selector, "selector", checked_uint8 }, { &mtype, "mtype", checked_uint8 }, { &data, "data", hexdecode }, { NULL, } }; struct tlsa_field *f; int ret; const char *cp = rrdata; ossl_ssize_t len = 0; for (f = tlsa_fields; f->var; ++f) { /* Returns number of bytes produced, advances cp to next field */ if ((len = f->parser(&cp, f->var)) <= 0) { BIO_printf(bio_err, "%s: warning: bad TLSA %s field in: %s\n", prog, f->name, rrdata); return 0; } } /* The data field is last, so len is its length */ ret = SSL_dane_tlsa_add(con, usage, selector, mtype, data, len); OPENSSL_free(data); if (ret == 0) { ERR_print_errors(bio_err); BIO_printf(bio_err, "%s: warning: unusable TLSA rrdata: %s\n", prog, rrdata); return 0; } if (ret < 0) { ERR_print_errors(bio_err); BIO_printf(bio_err, "%s: warning: error loading TLSA rrdata: %s\n", prog, rrdata); return 0; } return ret; } static int tlsa_import_rrset(SSL *con, STACK_OF(OPENSSL_STRING) *rrset) { int num = sk_OPENSSL_STRING_num(rrset); int count = 0; int i; for (i = 0; i < num; ++i) { char *rrdata = sk_OPENSSL_STRING_value(rrset, i); if (tlsa_import_rr(con, rrdata) > 0) ++count; } return count > 0; } typedef enum OPTION_choice { OPT_COMMON, OPT_4, OPT_6, OPT_HOST, OPT_PORT, OPT_CONNECT, OPT_BIND, OPT_UNIX, OPT_XMPPHOST, OPT_VERIFY, OPT_NAMEOPT, OPT_CERT, OPT_CRL, OPT_CRL_DOWNLOAD, OPT_SESS_OUT, OPT_SESS_IN, OPT_CERTFORM, OPT_CRLFORM, OPT_VERIFY_RET_ERROR, OPT_VERIFY_QUIET, OPT_BRIEF, OPT_PREXIT, OPT_NO_INTERACTIVE, OPT_CRLF, OPT_QUIET, OPT_NBIO, OPT_SSL_CLIENT_ENGINE, OPT_IGN_EOF, OPT_NO_IGN_EOF, OPT_DEBUG, OPT_TLSEXTDEBUG, OPT_STATUS, OPT_WDEBUG, OPT_MSG, OPT_MSGFILE, OPT_ENGINE, OPT_TRACE, OPT_SECURITY_DEBUG, OPT_SECURITY_DEBUG_VERBOSE, OPT_SHOWCERTS, OPT_NBIO_TEST, OPT_STATE, OPT_PSK_IDENTITY, OPT_PSK, OPT_PSK_SESS, #ifndef OPENSSL_NO_SRP OPT_SRPUSER, OPT_SRPPASS, OPT_SRP_STRENGTH, OPT_SRP_LATEUSER, OPT_SRP_MOREGROUPS, #endif OPT_SSL3, OPT_SSL_CONFIG, OPT_TLS1_3, OPT_TLS1_2, OPT_TLS1_1, OPT_TLS1, OPT_DTLS, OPT_DTLS1, OPT_DTLS1_2, OPT_QUIC, OPT_SCTP, OPT_TIMEOUT, OPT_MTU, OPT_KEYFORM, OPT_PASS, OPT_CERT_CHAIN, OPT_KEY, OPT_RECONNECT, OPT_BUILD_CHAIN, OPT_NEXTPROTONEG, OPT_ALPN, OPT_CAPATH, OPT_NOCAPATH, OPT_CHAINCAPATH, OPT_VERIFYCAPATH, OPT_CAFILE, OPT_NOCAFILE, OPT_CHAINCAFILE, OPT_VERIFYCAFILE, OPT_CASTORE, OPT_NOCASTORE, OPT_CHAINCASTORE, OPT_VERIFYCASTORE, OPT_SERVERINFO, OPT_STARTTLS, OPT_SERVERNAME, OPT_NOSERVERNAME, OPT_ASYNC, OPT_USE_SRTP, OPT_KEYMATEXPORT, OPT_KEYMATEXPORTLEN, OPT_PROTOHOST, OPT_MAXFRAGLEN, OPT_MAX_SEND_FRAG, OPT_SPLIT_SEND_FRAG, OPT_MAX_PIPELINES, OPT_READ_BUF, OPT_KEYLOG_FILE, OPT_EARLY_DATA, OPT_REQCAFILE, OPT_TFO, OPT_V_ENUM, OPT_X_ENUM, OPT_S_ENUM, OPT_IGNORE_UNEXPECTED_EOF, OPT_FALLBACKSCSV, OPT_NOCMDS, OPT_ADV, OPT_PROXY, OPT_PROXY_USER, OPT_PROXY_PASS, OPT_DANE_TLSA_DOMAIN, #ifndef OPENSSL_NO_CT OPT_CT, OPT_NOCT, OPT_CTLOG_FILE, #endif OPT_DANE_TLSA_RRDATA, OPT_DANE_EE_NO_NAME, OPT_ENABLE_PHA, OPT_ENABLE_SERVER_RPK, OPT_ENABLE_CLIENT_RPK, OPT_SCTP_LABEL_BUG, OPT_KTLS, OPT_R_ENUM, OPT_PROV_ENUM } OPTION_CHOICE; const OPTIONS s_client_options[] = { {OPT_HELP_STR, 1, '-', "Usage: %s [options] [host:port]\n"}, OPT_SECTION("General"), {"help", OPT_HELP, '-', "Display this summary"}, #ifndef OPENSSL_NO_ENGINE {"engine", OPT_ENGINE, 's', "Use engine, possibly a hardware device"}, {"ssl_client_engine", OPT_SSL_CLIENT_ENGINE, 's', "Specify engine to be used for client certificate operations"}, #endif {"ssl_config", OPT_SSL_CONFIG, 's', "Use specified section for SSL_CTX configuration"}, #ifndef OPENSSL_NO_CT {"ct", OPT_CT, '-', "Request and parse SCTs (also enables OCSP stapling)"}, {"noct", OPT_NOCT, '-', "Do not request or parse SCTs (default)"}, {"ctlogfile", OPT_CTLOG_FILE, '<', "CT log list CONF file"}, #endif OPT_SECTION("Network"), {"host", OPT_HOST, 's', "Use -connect instead"}, {"port", OPT_PORT, 'p', "Use -connect instead"}, {"connect", OPT_CONNECT, 's', "TCP/IP where to connect; default: " PORT ")"}, {"bind", OPT_BIND, 's', "bind local address for connection"}, {"proxy", OPT_PROXY, 's', "Connect to via specified proxy to the real server"}, {"proxy_user", OPT_PROXY_USER, 's', "UserID for proxy authentication"}, {"proxy_pass", OPT_PROXY_PASS, 's', "Proxy authentication password source"}, #ifdef AF_UNIX {"unix", OPT_UNIX, 's', "Connect over the specified Unix-domain socket"}, #endif {"4", OPT_4, '-', "Use IPv4 only"}, #ifdef AF_INET6 {"6", OPT_6, '-', "Use IPv6 only"}, #endif {"maxfraglen", OPT_MAXFRAGLEN, 'p', "Enable Maximum Fragment Length Negotiation (len values: 512, 1024, 2048 and 4096)"}, {"max_send_frag", OPT_MAX_SEND_FRAG, 'p', "Maximum Size of send frames "}, {"split_send_frag", OPT_SPLIT_SEND_FRAG, 'p', "Size used to split data for encrypt pipelines"}, {"max_pipelines", OPT_MAX_PIPELINES, 'p', "Maximum number of encrypt/decrypt pipelines to be used"}, {"read_buf", OPT_READ_BUF, 'p', "Default read buffer size to be used for connections"}, {"fallback_scsv", OPT_FALLBACKSCSV, '-', "Send the fallback SCSV"}, OPT_SECTION("Identity"), {"cert", OPT_CERT, '<', "Client certificate file to use"}, {"certform", OPT_CERTFORM, 'F', "Client certificate file format (PEM/DER/P12); has no effect"}, {"cert_chain", OPT_CERT_CHAIN, '<', "Client certificate chain file (in PEM format)"}, {"build_chain", OPT_BUILD_CHAIN, '-', "Build client certificate chain"}, {"key", OPT_KEY, 's', "Private key file to use; default: -cert file"}, {"keyform", OPT_KEYFORM, 'E', "Key format (ENGINE, other values ignored)"}, {"pass", OPT_PASS, 's', "Private key and cert file pass phrase source"}, {"verify", OPT_VERIFY, 'p', "Turn on peer certificate verification"}, {"nameopt", OPT_NAMEOPT, 's', "Certificate subject/issuer name printing options"}, {"CApath", OPT_CAPATH, '/', "PEM format directory of CA's"}, {"CAfile", OPT_CAFILE, '<', "PEM format file of CA's"}, {"CAstore", OPT_CASTORE, ':', "URI to store of CA's"}, {"no-CAfile", OPT_NOCAFILE, '-', "Do not load the default certificates file"}, {"no-CApath", OPT_NOCAPATH, '-', "Do not load certificates from the default certificates directory"}, {"no-CAstore", OPT_NOCASTORE, '-', "Do not load certificates from the default certificates store"}, {"requestCAfile", OPT_REQCAFILE, '<', "PEM format file of CA names to send to the server"}, #if defined(TCP_FASTOPEN) && !defined(OPENSSL_NO_TFO) {"tfo", OPT_TFO, '-', "Connect using TCP Fast Open"}, #endif {"dane_tlsa_domain", OPT_DANE_TLSA_DOMAIN, 's', "DANE TLSA base domain"}, {"dane_tlsa_rrdata", OPT_DANE_TLSA_RRDATA, 's', "DANE TLSA rrdata presentation form"}, {"dane_ee_no_namechecks", OPT_DANE_EE_NO_NAME, '-', "Disable name checks when matching DANE-EE(3) TLSA records"}, {"psk_identity", OPT_PSK_IDENTITY, 's', "PSK identity"}, {"psk", OPT_PSK, 's', "PSK in hex (without 0x)"}, {"psk_session", OPT_PSK_SESS, '<', "File to read PSK SSL session from"}, {"name", OPT_PROTOHOST, 's', "Hostname to use for \"-starttls lmtp\", \"-starttls smtp\" or \"-starttls xmpp[-server]\""}, OPT_SECTION("Session"), {"reconnect", OPT_RECONNECT, '-', "Drop and re-make the connection with the same Session-ID"}, {"sess_out", OPT_SESS_OUT, '>', "File to write SSL session to"}, {"sess_in", OPT_SESS_IN, '<', "File to read SSL session from"}, OPT_SECTION("Input/Output"), {"crlf", OPT_CRLF, '-', "Convert LF from terminal into CRLF"}, {"quiet", OPT_QUIET, '-', "No s_client output"}, {"ign_eof", OPT_IGN_EOF, '-', "Ignore input eof (default when -quiet)"}, {"no_ign_eof", OPT_NO_IGN_EOF, '-', "Don't ignore input eof"}, {"starttls", OPT_STARTTLS, 's', "Use the appropriate STARTTLS command before starting TLS"}, {"xmpphost", OPT_XMPPHOST, 's', "Alias of -name option for \"-starttls xmpp[-server]\""}, {"brief", OPT_BRIEF, '-', "Restrict output to brief summary of connection parameters"}, {"prexit", OPT_PREXIT, '-', "Print session information when the program exits"}, {"no-interactive", OPT_NO_INTERACTIVE, '-', "Don't run the client in the interactive mode"}, OPT_SECTION("Debug"), {"showcerts", OPT_SHOWCERTS, '-', "Show all certificates sent by the server"}, {"debug", OPT_DEBUG, '-', "Extra output"}, {"msg", OPT_MSG, '-', "Show protocol messages"}, {"msgfile", OPT_MSGFILE, '>', "File to send output of -msg or -trace, instead of stdout"}, {"nbio_test", OPT_NBIO_TEST, '-', "More ssl protocol testing"}, {"state", OPT_STATE, '-', "Print the ssl states"}, {"keymatexport", OPT_KEYMATEXPORT, 's', "Export keying material using label"}, {"keymatexportlen", OPT_KEYMATEXPORTLEN, 'p', "Export len bytes of keying material; default 20"}, {"security_debug", OPT_SECURITY_DEBUG, '-', "Enable security debug messages"}, {"security_debug_verbose", OPT_SECURITY_DEBUG_VERBOSE, '-', "Output more security debug output"}, #ifndef OPENSSL_NO_SSL_TRACE {"trace", OPT_TRACE, '-', "Show trace output of protocol messages"}, #endif #ifdef WATT32 {"wdebug", OPT_WDEBUG, '-', "WATT-32 tcp debugging"}, #endif {"keylogfile", OPT_KEYLOG_FILE, '>', "Write TLS secrets to file"}, {"nocommands", OPT_NOCMDS, '-', "Do not use interactive command letters"}, {"adv", OPT_ADV, '-', "Advanced command mode"}, {"servername", OPT_SERVERNAME, 's', "Set TLS extension servername (SNI) in ClientHello (default)"}, {"noservername", OPT_NOSERVERNAME, '-', "Do not send the server name (SNI) extension in the ClientHello"}, {"tlsextdebug", OPT_TLSEXTDEBUG, '-', "Hex dump of all TLS extensions received"}, {"ignore_unexpected_eof", OPT_IGNORE_UNEXPECTED_EOF, '-', "Do not treat lack of close_notify from a peer as an error"}, #ifndef OPENSSL_NO_OCSP {"status", OPT_STATUS, '-', "Request certificate status from server"}, #endif {"serverinfo", OPT_SERVERINFO, 's', "types Send empty ClientHello extensions (comma-separated numbers)"}, {"alpn", OPT_ALPN, 's', "Enable ALPN extension, considering named protocols supported (comma-separated list)"}, {"async", OPT_ASYNC, '-', "Support asynchronous operation"}, {"nbio", OPT_NBIO, '-', "Use non-blocking IO"}, OPT_SECTION("Protocol and version"), #ifndef OPENSSL_NO_SSL3 {"ssl3", OPT_SSL3, '-', "Just use SSLv3"}, #endif #ifndef OPENSSL_NO_TLS1 {"tls1", OPT_TLS1, '-', "Just use TLSv1"}, #endif #ifndef OPENSSL_NO_TLS1_1 {"tls1_1", OPT_TLS1_1, '-', "Just use TLSv1.1"}, #endif #ifndef OPENSSL_NO_TLS1_2 {"tls1_2", OPT_TLS1_2, '-', "Just use TLSv1.2"}, #endif #ifndef OPENSSL_NO_TLS1_3 {"tls1_3", OPT_TLS1_3, '-', "Just use TLSv1.3"}, #endif #ifndef OPENSSL_NO_DTLS {"dtls", OPT_DTLS, '-', "Use any version of DTLS"}, {"quic", OPT_QUIC, '-', "Use QUIC"}, {"timeout", OPT_TIMEOUT, '-', "Enable send/receive timeout on DTLS connections"}, {"mtu", OPT_MTU, 'p', "Set the link layer MTU"}, #endif #ifndef OPENSSL_NO_DTLS1 {"dtls1", OPT_DTLS1, '-', "Just use DTLSv1"}, #endif #ifndef OPENSSL_NO_DTLS1_2 {"dtls1_2", OPT_DTLS1_2, '-', "Just use DTLSv1.2"}, #endif #ifndef OPENSSL_NO_SCTP {"sctp", OPT_SCTP, '-', "Use SCTP"}, {"sctp_label_bug", OPT_SCTP_LABEL_BUG, '-', "Enable SCTP label length bug"}, #endif #ifndef OPENSSL_NO_NEXTPROTONEG {"nextprotoneg", OPT_NEXTPROTONEG, 's', "Enable NPN extension, considering named protocols supported (comma-separated list)"}, #endif {"early_data", OPT_EARLY_DATA, '<', "File to send as early data"}, {"enable_pha", OPT_ENABLE_PHA, '-', "Enable post-handshake-authentication"}, {"enable_server_rpk", OPT_ENABLE_SERVER_RPK, '-', "Enable raw public keys (RFC7250) from the server"}, {"enable_client_rpk", OPT_ENABLE_CLIENT_RPK, '-', "Enable raw public keys (RFC7250) from the client"}, #ifndef OPENSSL_NO_SRTP {"use_srtp", OPT_USE_SRTP, 's', "Offer SRTP key management with a colon-separated profile list"}, #endif #ifndef OPENSSL_NO_SRP {"srpuser", OPT_SRPUSER, 's', "(deprecated) SRP authentication for 'user'"}, {"srppass", OPT_SRPPASS, 's', "(deprecated) Password for 'user'"}, {"srp_lateuser", OPT_SRP_LATEUSER, '-', "(deprecated) SRP username into second ClientHello message"}, {"srp_moregroups", OPT_SRP_MOREGROUPS, '-', "(deprecated) Tolerate other than the known g N values."}, {"srp_strength", OPT_SRP_STRENGTH, 'p', "(deprecated) Minimal length in bits for N"}, #endif #ifndef OPENSSL_NO_KTLS {"ktls", OPT_KTLS, '-', "Enable Kernel TLS for sending and receiving"}, #endif OPT_R_OPTIONS, OPT_S_OPTIONS, OPT_V_OPTIONS, {"CRL", OPT_CRL, '<', "CRL file to use"}, {"crl_download", OPT_CRL_DOWNLOAD, '-', "Download CRL from distribution points"}, {"CRLform", OPT_CRLFORM, 'F', "CRL format (PEM or DER); default PEM"}, {"verify_return_error", OPT_VERIFY_RET_ERROR, '-', "Close connection on verification error"}, {"verify_quiet", OPT_VERIFY_QUIET, '-', "Restrict verify output to errors"}, {"chainCAfile", OPT_CHAINCAFILE, '<', "CA file for certificate chain (PEM format)"}, {"chainCApath", OPT_CHAINCAPATH, '/', "Use dir as certificate store path to build CA certificate chain"}, {"chainCAstore", OPT_CHAINCASTORE, ':', "CA store URI for certificate chain"}, {"verifyCAfile", OPT_VERIFYCAFILE, '<', "CA file for certificate verification (PEM format)"}, {"verifyCApath", OPT_VERIFYCAPATH, '/', "Use dir as certificate store path to verify CA certificate"}, {"verifyCAstore", OPT_VERIFYCASTORE, ':', "CA store URI for certificate verification"}, OPT_X_OPTIONS, OPT_PROV_OPTIONS, OPT_PARAMETERS(), {"host:port", 0, 0, "Where to connect; same as -connect option"}, {NULL} }; typedef enum PROTOCOL_choice { PROTO_OFF, PROTO_SMTP, PROTO_POP3, PROTO_IMAP, PROTO_FTP, PROTO_TELNET, PROTO_XMPP, PROTO_XMPP_SERVER, PROTO_IRC, PROTO_MYSQL, PROTO_POSTGRES, PROTO_LMTP, PROTO_NNTP, PROTO_SIEVE, PROTO_LDAP } PROTOCOL_CHOICE; static const OPT_PAIR services[] = { {"smtp", PROTO_SMTP}, {"pop3", PROTO_POP3}, {"imap", PROTO_IMAP}, {"ftp", PROTO_FTP}, {"xmpp", PROTO_XMPP}, {"xmpp-server", PROTO_XMPP_SERVER}, {"telnet", PROTO_TELNET}, {"irc", PROTO_IRC}, {"mysql", PROTO_MYSQL}, {"postgres", PROTO_POSTGRES}, {"lmtp", PROTO_LMTP}, {"nntp", PROTO_NNTP}, {"sieve", PROTO_SIEVE}, {"ldap", PROTO_LDAP}, {NULL, 0} }; #define IS_INET_FLAG(o) \ (o == OPT_4 || o == OPT_6 || o == OPT_HOST || o == OPT_PORT || o == OPT_CONNECT) #define IS_UNIX_FLAG(o) (o == OPT_UNIX) #define IS_PROT_FLAG(o) \ (o == OPT_SSL3 || o == OPT_TLS1 || o == OPT_TLS1_1 || o == OPT_TLS1_2 \ || o == OPT_TLS1_3 || o == OPT_DTLS || o == OPT_DTLS1 || o == OPT_DTLS1_2 \ || o == OPT_QUIC) /* Free |*dest| and optionally set it to a copy of |source|. */ static void freeandcopy(char **dest, const char *source) { OPENSSL_free(*dest); *dest = NULL; if (source != NULL) *dest = OPENSSL_strdup(source); } static int new_session_cb(SSL *s, SSL_SESSION *sess) { if (sess_out != NULL) { BIO *stmp = BIO_new_file(sess_out, "w"); if (stmp == NULL) { BIO_printf(bio_err, "Error writing session file %s\n", sess_out); } else { PEM_write_bio_SSL_SESSION(stmp, sess); BIO_free(stmp); } } /* * Session data gets dumped on connection for TLSv1.2 and below, and on * arrival of the NewSessionTicket for TLSv1.3. */ if (SSL_version(s) == TLS1_3_VERSION) { BIO_printf(bio_c_out, "---\nPost-Handshake New Session Ticket arrived:\n"); SSL_SESSION_print(bio_c_out, sess); BIO_printf(bio_c_out, "---\n"); } /* * We always return a "fail" response so that the session gets freed again * because we haven't used the reference. */ return 0; } int s_client_main(int argc, char **argv) { BIO *sbio; EVP_PKEY *key = NULL; SSL *con = NULL; SSL_CTX *ctx = NULL; STACK_OF(X509) *chain = NULL; X509 *cert = NULL; X509_VERIFY_PARAM *vpm = NULL; SSL_EXCERT *exc = NULL; SSL_CONF_CTX *cctx = NULL; STACK_OF(OPENSSL_STRING) *ssl_args = NULL; char *dane_tlsa_domain = NULL; STACK_OF(OPENSSL_STRING) *dane_tlsa_rrset = NULL; int dane_ee_no_name = 0; STACK_OF(X509_CRL) *crls = NULL; const SSL_METHOD *meth = TLS_client_method(); const char *CApath = NULL, *CAfile = NULL, *CAstore = NULL; char *cbuf = NULL, *sbuf = NULL, *mbuf = NULL; char *proxystr = NULL, *proxyuser = NULL; char *proxypassarg = NULL, *proxypass = NULL; char *connectstr = NULL, *bindstr = NULL; char *cert_file = NULL, *key_file = NULL, *chain_file = NULL; char *chCApath = NULL, *chCAfile = NULL, *chCAstore = NULL, *host = NULL; char *thost = NULL, *tport = NULL; char *port = NULL; char *bindhost = NULL, *bindport = NULL; char *passarg = NULL, *pass = NULL; char *vfyCApath = NULL, *vfyCAfile = NULL, *vfyCAstore = NULL; char *ReqCAfile = NULL; char *sess_in = NULL, *crl_file = NULL, *p; const char *protohost = NULL; struct timeval timeout, *timeoutp; fd_set readfds, writefds; int noCApath = 0, noCAfile = 0, noCAstore = 0; int build_chain = 0, cert_format = FORMAT_UNDEF; size_t cbuf_len, cbuf_off; int key_format = FORMAT_UNDEF, crlf = 0, full_log = 1, mbuf_len = 0; int prexit = 0; int nointeractive = 0; int sdebug = 0; int reconnect = 0, verify = SSL_VERIFY_NONE, vpmtouched = 0; int ret = 1, in_init = 1, i, nbio_test = 0, sock = -1, k, width, state = 0; int sbuf_len, sbuf_off, cmdmode = USER_DATA_MODE_BASIC; int socket_family = AF_UNSPEC, socket_type = SOCK_STREAM, protocol = 0; int starttls_proto = PROTO_OFF, crl_format = FORMAT_UNDEF, crl_download = 0; int write_tty, read_tty, write_ssl, read_ssl, tty_on, ssl_pending; int first_loop; #if !defined(OPENSSL_SYS_WINDOWS) && !defined(OPENSSL_SYS_MSDOS) int at_eof = 0; #endif int read_buf_len = 0; int fallback_scsv = 0; OPTION_CHOICE o; #ifndef OPENSSL_NO_DTLS int enable_timeouts = 0; long socket_mtu = 0; #endif #ifndef OPENSSL_NO_ENGINE ENGINE *ssl_client_engine = NULL; #endif ENGINE *e = NULL; #if defined(OPENSSL_SYS_WINDOWS) || defined(OPENSSL_SYS_MSDOS) struct timeval tv; #endif const char *servername = NULL; char *sname_alloc = NULL; int noservername = 0; const char *alpn_in = NULL; tlsextctx tlsextcbp = { NULL, 0 }; const char *ssl_config = NULL; #define MAX_SI_TYPES 100 unsigned short serverinfo_types[MAX_SI_TYPES]; int serverinfo_count = 0, start = 0, len; #ifndef OPENSSL_NO_NEXTPROTONEG const char *next_proto_neg_in = NULL; #endif #ifndef OPENSSL_NO_SRP char *srppass = NULL; int srp_lateuser = 0; SRP_ARG srp_arg = { NULL, NULL, 0, 0, 0, 1024 }; #endif #ifndef OPENSSL_NO_SRTP char *srtp_profiles = NULL; #endif #ifndef OPENSSL_NO_CT char *ctlog_file = NULL; int ct_validation = 0; #endif int min_version = 0, max_version = 0, prot_opt = 0, no_prot_opt = 0; int async = 0; unsigned int max_send_fragment = 0; unsigned int split_send_fragment = 0, max_pipelines = 0; enum { use_inet, use_unix, use_unknown } connect_type = use_unknown; int count4or6 = 0; uint8_t maxfraglen = 0; int c_nbio = 0, c_msg = 0, c_ign_eof = 0, c_brief = 0; int c_tlsextdebug = 0; #ifndef OPENSSL_NO_OCSP int c_status_req = 0; #endif BIO *bio_c_msg = NULL; const char *keylog_file = NULL, *early_data_file = NULL; int isdtls = 0, isquic = 0; char *psksessf = NULL; int enable_pha = 0; int enable_client_rpk = 0; #ifndef OPENSSL_NO_SCTP int sctp_label_bug = 0; #endif int ignore_unexpected_eof = 0; #ifndef OPENSSL_NO_KTLS int enable_ktls = 0; #endif int tfo = 0; int is_infinite; BIO_ADDR *peer_addr = NULL; struct user_data_st user_data; FD_ZERO(&readfds); FD_ZERO(&writefds); /* Known false-positive of MemorySanitizer. */ #if defined(__has_feature) # if __has_feature(memory_sanitizer) __msan_unpoison(&readfds, sizeof(readfds)); __msan_unpoison(&writefds, sizeof(writefds)); # endif #endif c_quiet = 0; c_debug = 0; c_showcerts = 0; c_nbio = 0; port = OPENSSL_strdup(PORT); vpm = X509_VERIFY_PARAM_new(); cctx = SSL_CONF_CTX_new(); if (port == NULL || vpm == NULL || cctx == NULL) { BIO_printf(bio_err, "%s: out of memory\n", opt_getprog()); goto end; } cbuf = app_malloc(BUFSIZZ, "cbuf"); sbuf = app_malloc(BUFSIZZ, "sbuf"); mbuf = app_malloc(BUFSIZZ, "mbuf"); SSL_CONF_CTX_set_flags(cctx, SSL_CONF_FLAG_CLIENT | SSL_CONF_FLAG_CMDLINE); prog = opt_init(argc, argv, s_client_options); while ((o = opt_next()) != OPT_EOF) { /* Check for intermixing flags. */ if (connect_type == use_unix && IS_INET_FLAG(o)) { BIO_printf(bio_err, "%s: Intermixed protocol flags (unix and internet domains)\n", prog); goto end; } if (connect_type == use_inet && IS_UNIX_FLAG(o)) { BIO_printf(bio_err, "%s: Intermixed protocol flags (internet and unix domains)\n", prog); goto end; } if (IS_PROT_FLAG(o) && ++prot_opt > 1) { BIO_printf(bio_err, "Cannot supply multiple protocol flags\n"); goto end; } if (IS_NO_PROT_FLAG(o)) no_prot_opt++; if (prot_opt == 1 && no_prot_opt) { BIO_printf(bio_err, "Cannot supply both a protocol flag and '-no_'\n"); goto end; } switch (o) { case OPT_EOF: case OPT_ERR: opthelp: BIO_printf(bio_err, "%s: Use -help for summary.\n", prog); goto end; case OPT_HELP: opt_help(s_client_options); ret = 0; goto end; case OPT_4: connect_type = use_inet; socket_family = AF_INET; count4or6++; break; #ifdef AF_INET6 case OPT_6: connect_type = use_inet; socket_family = AF_INET6; count4or6++; break; #endif case OPT_HOST: connect_type = use_inet; freeandcopy(&host, opt_arg()); break; case OPT_PORT: connect_type = use_inet; freeandcopy(&port, opt_arg()); break; case OPT_CONNECT: connect_type = use_inet; freeandcopy(&connectstr, opt_arg()); break; case OPT_BIND: freeandcopy(&bindstr, opt_arg()); break; case OPT_PROXY: proxystr = opt_arg(); break; case OPT_PROXY_USER: proxyuser = opt_arg(); break; case OPT_PROXY_PASS: proxypassarg = opt_arg(); break; #ifdef AF_UNIX case OPT_UNIX: connect_type = use_unix; socket_family = AF_UNIX; freeandcopy(&host, opt_arg()); break; #endif case OPT_XMPPHOST: /* fall through, since this is an alias */ case OPT_PROTOHOST: protohost = opt_arg(); break; case OPT_VERIFY: verify = SSL_VERIFY_PEER; verify_args.depth = atoi(opt_arg()); if (!c_quiet) BIO_printf(bio_err, "verify depth is %d\n", verify_args.depth); break; case OPT_CERT: cert_file = opt_arg(); break; case OPT_NAMEOPT: if (!set_nameopt(opt_arg())) goto end; break; case OPT_CRL: crl_file = opt_arg(); break; case OPT_CRL_DOWNLOAD: crl_download = 1; break; case OPT_SESS_OUT: sess_out = opt_arg(); break; case OPT_SESS_IN: sess_in = opt_arg(); break; case OPT_CERTFORM: if (!opt_format(opt_arg(), OPT_FMT_ANY, &cert_format)) goto opthelp; break; case OPT_CRLFORM: if (!opt_format(opt_arg(), OPT_FMT_PEMDER, &crl_format)) goto opthelp; break; case OPT_VERIFY_RET_ERROR: verify = SSL_VERIFY_PEER; verify_args.return_error = 1; break; case OPT_VERIFY_QUIET: verify_args.quiet = 1; break; case OPT_BRIEF: c_brief = verify_args.quiet = c_quiet = 1; break; case OPT_S_CASES: if (ssl_args == NULL) ssl_args = sk_OPENSSL_STRING_new_null(); if (ssl_args == NULL || !sk_OPENSSL_STRING_push(ssl_args, opt_flag()) || !sk_OPENSSL_STRING_push(ssl_args, opt_arg())) { BIO_printf(bio_err, "%s: Memory allocation failure\n", prog); goto end; } break; case OPT_V_CASES: if (!opt_verify(o, vpm)) goto end; vpmtouched++; break; case OPT_X_CASES: if (!args_excert(o, &exc)) goto end; break; case OPT_IGNORE_UNEXPECTED_EOF: ignore_unexpected_eof = 1; break; case OPT_PREXIT: prexit = 1; break; case OPT_NO_INTERACTIVE: nointeractive = 1; break; case OPT_CRLF: crlf = 1; break; case OPT_QUIET: c_quiet = c_ign_eof = 1; break; case OPT_NBIO: c_nbio = 1; break; case OPT_NOCMDS: cmdmode = USER_DATA_MODE_NONE; break; case OPT_ADV: cmdmode = USER_DATA_MODE_ADVANCED; break; case OPT_ENGINE: e = setup_engine(opt_arg(), 1); break; case OPT_SSL_CLIENT_ENGINE: #ifndef OPENSSL_NO_ENGINE ssl_client_engine = setup_engine(opt_arg(), 0); if (ssl_client_engine == NULL) { BIO_printf(bio_err, "Error getting client auth engine\n"); goto opthelp; } #endif break; case OPT_R_CASES: if (!opt_rand(o)) goto end; break; case OPT_PROV_CASES: if (!opt_provider(o)) goto end; break; case OPT_IGN_EOF: c_ign_eof = 1; break; case OPT_NO_IGN_EOF: c_ign_eof = 0; break; case OPT_DEBUG: c_debug = 1; break; case OPT_TLSEXTDEBUG: c_tlsextdebug = 1; break; case OPT_STATUS: #ifndef OPENSSL_NO_OCSP c_status_req = 1; #endif break; case OPT_WDEBUG: #ifdef WATT32 dbug_init(); #endif break; case OPT_MSG: c_msg = 1; break; case OPT_MSGFILE: bio_c_msg = BIO_new_file(opt_arg(), "w"); if (bio_c_msg == NULL) { BIO_printf(bio_err, "Error writing file %s\n", opt_arg()); goto end; } break; case OPT_TRACE: #ifndef OPENSSL_NO_SSL_TRACE c_msg = 2; #endif break; case OPT_SECURITY_DEBUG: sdebug = 1; break; case OPT_SECURITY_DEBUG_VERBOSE: sdebug = 2; break; case OPT_SHOWCERTS: c_showcerts = 1; break; case OPT_NBIO_TEST: nbio_test = 1; break; case OPT_STATE: state = 1; break; case OPT_PSK_IDENTITY: psk_identity = opt_arg(); break; case OPT_PSK: for (p = psk_key = opt_arg(); *p; p++) { if (isxdigit(_UC(*p))) continue; BIO_printf(bio_err, "Not a hex number '%s'\n", psk_key); goto end; } break; case OPT_PSK_SESS: psksessf = opt_arg(); break; #ifndef OPENSSL_NO_SRP case OPT_SRPUSER: srp_arg.srplogin = opt_arg(); if (min_version < TLS1_VERSION) min_version = TLS1_VERSION; break; case OPT_SRPPASS: srppass = opt_arg(); if (min_version < TLS1_VERSION) min_version = TLS1_VERSION; break; case OPT_SRP_STRENGTH: srp_arg.strength = atoi(opt_arg()); BIO_printf(bio_err, "SRP minimal length for N is %d\n", srp_arg.strength); if (min_version < TLS1_VERSION) min_version = TLS1_VERSION; break; case OPT_SRP_LATEUSER: srp_lateuser = 1; if (min_version < TLS1_VERSION) min_version = TLS1_VERSION; break; case OPT_SRP_MOREGROUPS: srp_arg.amp = 1; if (min_version < TLS1_VERSION) min_version = TLS1_VERSION; break; #endif case OPT_SSL_CONFIG: ssl_config = opt_arg(); break; case OPT_SSL3: min_version = SSL3_VERSION; max_version = SSL3_VERSION; socket_type = SOCK_STREAM; #ifndef OPENSSL_NO_DTLS isdtls = 0; #endif isquic = 0; break; case OPT_TLS1_3: min_version = TLS1_3_VERSION; max_version = TLS1_3_VERSION; socket_type = SOCK_STREAM; #ifndef OPENSSL_NO_DTLS isdtls = 0; #endif isquic = 0; break; case OPT_TLS1_2: min_version = TLS1_2_VERSION; max_version = TLS1_2_VERSION; socket_type = SOCK_STREAM; #ifndef OPENSSL_NO_DTLS isdtls = 0; #endif isquic = 0; break; case OPT_TLS1_1: min_version = TLS1_1_VERSION; max_version = TLS1_1_VERSION; socket_type = SOCK_STREAM; #ifndef OPENSSL_NO_DTLS isdtls = 0; #endif isquic = 0; break; case OPT_TLS1: min_version = TLS1_VERSION; max_version = TLS1_VERSION; socket_type = SOCK_STREAM; #ifndef OPENSSL_NO_DTLS isdtls = 0; #endif isquic = 0; break; case OPT_DTLS: #ifndef OPENSSL_NO_DTLS meth = DTLS_client_method(); socket_type = SOCK_DGRAM; isdtls = 1; isquic = 0; #endif break; case OPT_DTLS1: #ifndef OPENSSL_NO_DTLS1 meth = DTLS_client_method(); min_version = DTLS1_VERSION; max_version = DTLS1_VERSION; socket_type = SOCK_DGRAM; isdtls = 1; isquic = 0; #endif break; case OPT_DTLS1_2: #ifndef OPENSSL_NO_DTLS1_2 meth = DTLS_client_method(); min_version = DTLS1_2_VERSION; max_version = DTLS1_2_VERSION; socket_type = SOCK_DGRAM; isdtls = 1; isquic = 0; #endif break; case OPT_QUIC: #ifndef OPENSSL_NO_QUIC meth = OSSL_QUIC_client_method(); min_version = 0; max_version = 0; socket_type = SOCK_DGRAM; # ifndef OPENSSL_NO_DTLS isdtls = 0; # endif isquic = 1; #endif break; case OPT_SCTP: #ifndef OPENSSL_NO_SCTP protocol = IPPROTO_SCTP; #endif break; case OPT_SCTP_LABEL_BUG: #ifndef OPENSSL_NO_SCTP sctp_label_bug = 1; #endif break; case OPT_TIMEOUT: #ifndef OPENSSL_NO_DTLS enable_timeouts = 1; #endif break; case OPT_MTU: #ifndef OPENSSL_NO_DTLS socket_mtu = atol(opt_arg()); #endif break; case OPT_FALLBACKSCSV: fallback_scsv = 1; break; case OPT_KEYFORM: if (!opt_format(opt_arg(), OPT_FMT_ANY, &key_format)) goto opthelp; break; case OPT_PASS: passarg = opt_arg(); break; case OPT_CERT_CHAIN: chain_file = opt_arg(); break; case OPT_KEY: key_file = opt_arg(); break; case OPT_RECONNECT: reconnect = 5; break; case OPT_CAPATH: CApath = opt_arg(); break; case OPT_NOCAPATH: noCApath = 1; break; case OPT_CHAINCAPATH: chCApath = opt_arg(); break; case OPT_VERIFYCAPATH: vfyCApath = opt_arg(); break; case OPT_BUILD_CHAIN: build_chain = 1; break; case OPT_REQCAFILE: ReqCAfile = opt_arg(); break; case OPT_CAFILE: CAfile = opt_arg(); break; case OPT_NOCAFILE: noCAfile = 1; break; #ifndef OPENSSL_NO_CT case OPT_NOCT: ct_validation = 0; break; case OPT_CT: ct_validation = 1; break; case OPT_CTLOG_FILE: ctlog_file = opt_arg(); break; #endif case OPT_CHAINCAFILE: chCAfile = opt_arg(); break; case OPT_VERIFYCAFILE: vfyCAfile = opt_arg(); break; case OPT_CASTORE: CAstore = opt_arg(); break; case OPT_NOCASTORE: noCAstore = 1; break; case OPT_CHAINCASTORE: chCAstore = opt_arg(); break; case OPT_VERIFYCASTORE: vfyCAstore = opt_arg(); break; case OPT_DANE_TLSA_DOMAIN: dane_tlsa_domain = opt_arg(); break; case OPT_DANE_TLSA_RRDATA: if (dane_tlsa_rrset == NULL) dane_tlsa_rrset = sk_OPENSSL_STRING_new_null(); if (dane_tlsa_rrset == NULL || !sk_OPENSSL_STRING_push(dane_tlsa_rrset, opt_arg())) { BIO_printf(bio_err, "%s: Memory allocation failure\n", prog); goto end; } break; case OPT_DANE_EE_NO_NAME: dane_ee_no_name = 1; break; case OPT_NEXTPROTONEG: #ifndef OPENSSL_NO_NEXTPROTONEG next_proto_neg_in = opt_arg(); #endif break; case OPT_ALPN: alpn_in = opt_arg(); break; case OPT_SERVERINFO: p = opt_arg(); len = strlen(p); for (start = 0, i = 0; i <= len; ++i) { if (i == len || p[i] == ',') { serverinfo_types[serverinfo_count] = atoi(p + start); if (++serverinfo_count == MAX_SI_TYPES) break; start = i + 1; } } break; case OPT_STARTTLS: if (!opt_pair(opt_arg(), services, &starttls_proto)) goto end; break; case OPT_TFO: tfo = 1; break; case OPT_SERVERNAME: servername = opt_arg(); break; case OPT_NOSERVERNAME: noservername = 1; break; case OPT_USE_SRTP: #ifndef OPENSSL_NO_SRTP srtp_profiles = opt_arg(); #endif break; case OPT_KEYMATEXPORT: keymatexportlabel = opt_arg(); break; case OPT_KEYMATEXPORTLEN: keymatexportlen = atoi(opt_arg()); break; case OPT_ASYNC: async = 1; break; case OPT_MAXFRAGLEN: len = atoi(opt_arg()); switch (len) { case 512: maxfraglen = TLSEXT_max_fragment_length_512; break; case 1024: maxfraglen = TLSEXT_max_fragment_length_1024; break; case 2048: maxfraglen = TLSEXT_max_fragment_length_2048; break; case 4096: maxfraglen = TLSEXT_max_fragment_length_4096; break; default: BIO_printf(bio_err, "%s: Max Fragment Len %u is out of permitted values", prog, len); goto opthelp; } break; case OPT_MAX_SEND_FRAG: max_send_fragment = atoi(opt_arg()); break; case OPT_SPLIT_SEND_FRAG: split_send_fragment = atoi(opt_arg()); break; case OPT_MAX_PIPELINES: max_pipelines = atoi(opt_arg()); break; case OPT_READ_BUF: read_buf_len = atoi(opt_arg()); break; case OPT_KEYLOG_FILE: keylog_file = opt_arg(); break; case OPT_EARLY_DATA: early_data_file = opt_arg(); break; case OPT_ENABLE_PHA: enable_pha = 1; break; case OPT_KTLS: #ifndef OPENSSL_NO_KTLS enable_ktls = 1; #endif break; case OPT_ENABLE_SERVER_RPK: enable_server_rpk = 1; break; case OPT_ENABLE_CLIENT_RPK: enable_client_rpk = 1; break; } } /* Optional argument is connect string if -connect not used. */ if (opt_num_rest() == 1) { /* Don't allow -connect and a separate argument. */ if (connectstr != NULL) { BIO_printf(bio_err, "%s: cannot provide both -connect option and target parameter\n", prog); goto opthelp; } connect_type = use_inet; freeandcopy(&connectstr, *opt_rest()); } else if (!opt_check_rest_arg(NULL)) { goto opthelp; } if (!app_RAND_load()) goto end; if (c_ign_eof) cmdmode = USER_DATA_MODE_NONE; if (count4or6 >= 2) { BIO_printf(bio_err, "%s: Can't use both -4 and -6\n", prog); goto opthelp; } if (noservername) { if (servername != NULL) { BIO_printf(bio_err, "%s: Can't use -servername and -noservername together\n", prog); goto opthelp; } if (dane_tlsa_domain != NULL) { BIO_printf(bio_err, "%s: Can't use -dane_tlsa_domain and -noservername together\n", prog); goto opthelp; } } #ifndef OPENSSL_NO_NEXTPROTONEG if (min_version == TLS1_3_VERSION && next_proto_neg_in != NULL) { BIO_printf(bio_err, "Cannot supply -nextprotoneg with TLSv1.3\n"); goto opthelp; } #endif if (connectstr != NULL) { int res; char *tmp_host = host, *tmp_port = port; res = BIO_parse_hostserv(connectstr, &host, &port, BIO_PARSE_PRIO_HOST); if (tmp_host != host) OPENSSL_free(tmp_host); if (tmp_port != port) OPENSSL_free(tmp_port); if (!res) { BIO_printf(bio_err, "%s: -connect argument or target parameter malformed or ambiguous\n", prog); goto end; } } if (proxystr != NULL) { #ifndef OPENSSL_NO_HTTP int res; char *tmp_host = host, *tmp_port = port; if (host == NULL || port == NULL) { BIO_printf(bio_err, "%s: -proxy requires use of -connect or target parameter\n", prog); goto opthelp; } if (servername == NULL && !noservername) { servername = sname_alloc = OPENSSL_strdup(host); if (sname_alloc == NULL) { BIO_printf(bio_err, "%s: out of memory\n", prog); goto end; } } /* Retain the original target host:port for use in the HTTP proxy connect string */ thost = OPENSSL_strdup(host); tport = OPENSSL_strdup(port); if (thost == NULL || tport == NULL) { BIO_printf(bio_err, "%s: out of memory\n", prog); goto end; } res = BIO_parse_hostserv(proxystr, &host, &port, BIO_PARSE_PRIO_HOST); if (tmp_host != host) OPENSSL_free(tmp_host); if (tmp_port != port) OPENSSL_free(tmp_port); if (!res) { BIO_printf(bio_err, "%s: -proxy argument malformed or ambiguous\n", prog); goto end; } #else BIO_printf(bio_err, "%s: -proxy not supported in no-http build\n", prog); goto end; #endif } if (bindstr != NULL) { int res; res = BIO_parse_hostserv(bindstr, &bindhost, &bindport, BIO_PARSE_PRIO_HOST); if (!res) { BIO_printf(bio_err, "%s: -bind argument parameter malformed or ambiguous\n", prog); goto end; } } #ifdef AF_UNIX if (socket_family == AF_UNIX && socket_type != SOCK_STREAM) { BIO_printf(bio_err, "Can't use unix sockets and datagrams together\n"); goto end; } #endif #ifndef OPENSSL_NO_SCTP if (protocol == IPPROTO_SCTP) { if (socket_type != SOCK_DGRAM) { BIO_printf(bio_err, "Can't use -sctp without DTLS\n"); goto end; } /* SCTP is unusual. It uses DTLS over a SOCK_STREAM protocol */ socket_type = SOCK_STREAM; } #endif #if !defined(OPENSSL_NO_NEXTPROTONEG) next_proto.status = -1; if (next_proto_neg_in) { next_proto.data = next_protos_parse(&next_proto.len, next_proto_neg_in); if (next_proto.data == NULL) { BIO_printf(bio_err, "Error parsing -nextprotoneg argument\n"); goto end; } } else next_proto.data = NULL; #endif if (!app_passwd(passarg, NULL, &pass, NULL)) { BIO_printf(bio_err, "Error getting private key password\n"); goto end; } if (!app_passwd(proxypassarg, NULL, &proxypass, NULL)) { BIO_printf(bio_err, "Error getting proxy password\n"); goto end; } if (proxypass != NULL && proxyuser == NULL) { BIO_printf(bio_err, "Error: Must specify proxy_user with proxy_pass\n"); goto end; } if (key_file == NULL) key_file = cert_file; if (key_file != NULL) { key = load_key(key_file, key_format, 0, pass, e, "client certificate private key"); if (key == NULL) goto end; } if (cert_file != NULL) { cert = load_cert_pass(cert_file, cert_format, 1, pass, "client certificate"); if (cert == NULL) goto end; } if (chain_file != NULL) { if (!load_certs(chain_file, 0, &chain, pass, "client certificate chain")) goto end; } if (crl_file != NULL) { X509_CRL *crl; crl = load_crl(crl_file, crl_format, 0, "CRL"); if (crl == NULL) goto end; crls = sk_X509_CRL_new_null(); if (crls == NULL || !sk_X509_CRL_push(crls, crl)) { BIO_puts(bio_err, "Error adding CRL\n"); ERR_print_errors(bio_err); X509_CRL_free(crl); goto end; } } if (!load_excert(&exc)) goto end; if (bio_c_out == NULL) { if (c_quiet && !c_debug) { bio_c_out = BIO_new(BIO_s_null()); if (c_msg && bio_c_msg == NULL) { bio_c_msg = dup_bio_out(FORMAT_TEXT); if (bio_c_msg == NULL) { BIO_printf(bio_err, "Out of memory\n"); goto end; } } } else { bio_c_out = dup_bio_out(FORMAT_TEXT); } if (bio_c_out == NULL) { BIO_printf(bio_err, "Unable to create BIO\n"); goto end; } } #ifndef OPENSSL_NO_SRP if (!app_passwd(srppass, NULL, &srp_arg.srppassin, NULL)) { BIO_printf(bio_err, "Error getting password\n"); goto end; } #endif ctx = SSL_CTX_new_ex(app_get0_libctx(), app_get0_propq(), meth); if (ctx == NULL) { ERR_print_errors(bio_err); goto end; } SSL_CTX_clear_mode(ctx, SSL_MODE_AUTO_RETRY); if (sdebug) ssl_ctx_security_debug(ctx, sdebug); if (!config_ctx(cctx, ssl_args, ctx)) goto end; if (ssl_config != NULL) { if (SSL_CTX_config(ctx, ssl_config) == 0) { BIO_printf(bio_err, "Error using configuration \"%s\"\n", ssl_config); ERR_print_errors(bio_err); goto end; } } #ifndef OPENSSL_NO_SCTP if (protocol == IPPROTO_SCTP && sctp_label_bug == 1) SSL_CTX_set_mode(ctx, SSL_MODE_DTLS_SCTP_LABEL_LENGTH_BUG); #endif if (min_version != 0 && SSL_CTX_set_min_proto_version(ctx, min_version) == 0) goto end; if (max_version != 0 && SSL_CTX_set_max_proto_version(ctx, max_version) == 0) goto end; if (ignore_unexpected_eof) SSL_CTX_set_options(ctx, SSL_OP_IGNORE_UNEXPECTED_EOF); #ifndef OPENSSL_NO_KTLS if (enable_ktls) SSL_CTX_set_options(ctx, SSL_OP_ENABLE_KTLS); #endif if (vpmtouched && !SSL_CTX_set1_param(ctx, vpm)) { BIO_printf(bio_err, "Error setting verify params\n"); ERR_print_errors(bio_err); goto end; } if (async) { SSL_CTX_set_mode(ctx, SSL_MODE_ASYNC); } if (max_send_fragment > 0 && !SSL_CTX_set_max_send_fragment(ctx, max_send_fragment)) { BIO_printf(bio_err, "%s: Max send fragment size %u is out of permitted range\n", prog, max_send_fragment); goto end; } if (split_send_fragment > 0 && !SSL_CTX_set_split_send_fragment(ctx, split_send_fragment)) { BIO_printf(bio_err, "%s: Split send fragment size %u is out of permitted range\n", prog, split_send_fragment); goto end; } if (max_pipelines > 0 && !SSL_CTX_set_max_pipelines(ctx, max_pipelines)) { BIO_printf(bio_err, "%s: Max pipelines %u is out of permitted range\n", prog, max_pipelines); goto end; } if (read_buf_len > 0) { SSL_CTX_set_default_read_buffer_len(ctx, read_buf_len); } if (maxfraglen > 0 && !SSL_CTX_set_tlsext_max_fragment_length(ctx, maxfraglen)) { BIO_printf(bio_err, "%s: Max Fragment Length code %u is out of permitted values" "\n", prog, maxfraglen); goto end; } if (!ssl_load_stores(ctx, vfyCApath, vfyCAfile, vfyCAstore, chCApath, chCAfile, chCAstore, crls, crl_download)) { BIO_printf(bio_err, "Error loading store locations\n"); ERR_print_errors(bio_err); goto end; } if (ReqCAfile != NULL) { STACK_OF(X509_NAME) *nm = sk_X509_NAME_new_null(); if (nm == NULL || !SSL_add_file_cert_subjects_to_stack(nm, ReqCAfile)) { sk_X509_NAME_pop_free(nm, X509_NAME_free); BIO_printf(bio_err, "Error loading CA names\n"); ERR_print_errors(bio_err); goto end; } SSL_CTX_set0_CA_list(ctx, nm); } #ifndef OPENSSL_NO_ENGINE if (ssl_client_engine) { if (!SSL_CTX_set_client_cert_engine(ctx, ssl_client_engine)) { BIO_puts(bio_err, "Error setting client auth engine\n"); ERR_print_errors(bio_err); release_engine(ssl_client_engine); goto end; } release_engine(ssl_client_engine); } #endif #ifndef OPENSSL_NO_PSK if (psk_key != NULL) { if (c_debug) BIO_printf(bio_c_out, "PSK key given, setting client callback\n"); SSL_CTX_set_psk_client_callback(ctx, psk_client_cb); } #endif if (psksessf != NULL) { BIO *stmp = BIO_new_file(psksessf, "r"); if (stmp == NULL) { BIO_printf(bio_err, "Can't open PSK session file %s\n", psksessf); ERR_print_errors(bio_err); goto end; } psksess = PEM_read_bio_SSL_SESSION(stmp, NULL, 0, NULL); BIO_free(stmp); if (psksess == NULL) { BIO_printf(bio_err, "Can't read PSK session file %s\n", psksessf); ERR_print_errors(bio_err); goto end; } } if (psk_key != NULL || psksess != NULL) SSL_CTX_set_psk_use_session_callback(ctx, psk_use_session_cb); #ifndef OPENSSL_NO_SRTP if (srtp_profiles != NULL) { /* Returns 0 on success! */ if (SSL_CTX_set_tlsext_use_srtp(ctx, srtp_profiles) != 0) { BIO_printf(bio_err, "Error setting SRTP profile\n"); ERR_print_errors(bio_err); goto end; } } #endif if (exc != NULL) ssl_ctx_set_excert(ctx, exc); #if !defined(OPENSSL_NO_NEXTPROTONEG) if (next_proto.data != NULL) SSL_CTX_set_next_proto_select_cb(ctx, next_proto_cb, &next_proto); #endif if (alpn_in) { size_t alpn_len; unsigned char *alpn = next_protos_parse(&alpn_len, alpn_in); if (alpn == NULL) { BIO_printf(bio_err, "Error parsing -alpn argument\n"); goto end; } /* Returns 0 on success! */ if (SSL_CTX_set_alpn_protos(ctx, alpn, alpn_len) != 0) { BIO_printf(bio_err, "Error setting ALPN\n"); goto end; } OPENSSL_free(alpn); } for (i = 0; i < serverinfo_count; i++) { if (!SSL_CTX_add_client_custom_ext(ctx, serverinfo_types[i], NULL, NULL, NULL, serverinfo_cli_parse_cb, NULL)) { BIO_printf(bio_err, "Warning: Unable to add custom extension %u, skipping\n", serverinfo_types[i]); } } if (state) SSL_CTX_set_info_callback(ctx, apps_ssl_info_callback); #ifndef OPENSSL_NO_CT /* Enable SCT processing, without early connection termination */ if (ct_validation && !SSL_CTX_enable_ct(ctx, SSL_CT_VALIDATION_PERMISSIVE)) { ERR_print_errors(bio_err); goto end; } if (!ctx_set_ctlog_list_file(ctx, ctlog_file)) { if (ct_validation) { ERR_print_errors(bio_err); goto end; } /* * If CT validation is not enabled, the log list isn't needed so don't * show errors or abort. We try to load it regardless because then we * can show the names of the logs any SCTs came from (SCTs may be seen * even with validation disabled). */ ERR_clear_error(); } #endif SSL_CTX_set_verify(ctx, verify, verify_callback); if (!ctx_set_verify_locations(ctx, CAfile, noCAfile, CApath, noCApath, CAstore, noCAstore)) { ERR_print_errors(bio_err); goto end; } ssl_ctx_add_crls(ctx, crls, crl_download); if (!set_cert_key_stuff(ctx, cert, key, chain, build_chain)) goto end; if (!noservername) { tlsextcbp.biodebug = bio_err; SSL_CTX_set_tlsext_servername_callback(ctx, ssl_servername_cb); SSL_CTX_set_tlsext_servername_arg(ctx, &tlsextcbp); } #ifndef OPENSSL_NO_SRP if (srp_arg.srplogin != NULL && !set_up_srp_arg(ctx, &srp_arg, srp_lateuser, c_msg, c_debug)) goto end; # endif if (dane_tlsa_domain != NULL) { if (SSL_CTX_dane_enable(ctx) <= 0) { BIO_printf(bio_err, "%s: Error enabling DANE TLSA authentication.\n", prog); ERR_print_errors(bio_err); goto end; } } /* * In TLSv1.3 NewSessionTicket messages arrive after the handshake and can * come at any time. Therefore, we use a callback to write out the session * when we know about it. This approach works for < TLSv1.3 as well. */ SSL_CTX_set_session_cache_mode(ctx, SSL_SESS_CACHE_CLIENT | SSL_SESS_CACHE_NO_INTERNAL_STORE); SSL_CTX_sess_set_new_cb(ctx, new_session_cb); if (set_keylog_file(ctx, keylog_file)) goto end; con = SSL_new(ctx); if (con == NULL) goto end; if (enable_pha) SSL_set_post_handshake_auth(con, 1); if (enable_client_rpk) if (!SSL_set1_client_cert_type(con, cert_type_rpk, sizeof(cert_type_rpk))) { BIO_printf(bio_err, "Error setting client certificate types\n"); goto end; } if (enable_server_rpk) { if (!SSL_set1_server_cert_type(con, cert_type_rpk, sizeof(cert_type_rpk))) { BIO_printf(bio_err, "Error setting server certificate types\n"); goto end; } } if (sess_in != NULL) { SSL_SESSION *sess; BIO *stmp = BIO_new_file(sess_in, "r"); if (stmp == NULL) { BIO_printf(bio_err, "Can't open session file %s\n", sess_in); ERR_print_errors(bio_err); goto end; } sess = PEM_read_bio_SSL_SESSION(stmp, NULL, 0, NULL); BIO_free(stmp); if (sess == NULL) { BIO_printf(bio_err, "Can't open session file %s\n", sess_in); ERR_print_errors(bio_err); goto end; } if (!SSL_set_session(con, sess)) { BIO_printf(bio_err, "Can't set session\n"); ERR_print_errors(bio_err); goto end; } SSL_SESSION_free(sess); } if (fallback_scsv) SSL_set_mode(con, SSL_MODE_SEND_FALLBACK_SCSV); if (!noservername && (servername != NULL || dane_tlsa_domain == NULL)) { if (servername == NULL) { if (host == NULL || is_dNS_name(host)) servername = (host == NULL) ? "localhost" : host; } if (servername != NULL && !SSL_set_tlsext_host_name(con, servername)) { BIO_printf(bio_err, "Unable to set TLS servername extension.\n"); ERR_print_errors(bio_err); goto end; } } if (dane_tlsa_domain != NULL) { if (SSL_dane_enable(con, dane_tlsa_domain) <= 0) { BIO_printf(bio_err, "%s: Error enabling DANE TLSA " "authentication.\n", prog); ERR_print_errors(bio_err); goto end; } if (dane_tlsa_rrset == NULL) { BIO_printf(bio_err, "%s: DANE TLSA authentication requires at " "least one -dane_tlsa_rrdata option.\n", prog); goto end; } if (tlsa_import_rrset(con, dane_tlsa_rrset) <= 0) { BIO_printf(bio_err, "%s: Failed to import any TLSA " "records.\n", prog); goto end; } if (dane_ee_no_name) SSL_dane_set_flags(con, DANE_FLAG_NO_DANE_EE_NAMECHECKS); } else if (dane_tlsa_rrset != NULL) { BIO_printf(bio_err, "%s: DANE TLSA authentication requires the " "-dane_tlsa_domain option.\n", prog); goto end; } #ifndef OPENSSL_NO_DTLS if (isdtls && tfo) { BIO_printf(bio_err, "%s: DTLS does not support the -tfo option\n", prog); goto end; } #endif #ifndef OPENSSL_NO_QUIC if (isquic && tfo) { BIO_printf(bio_err, "%s: QUIC does not support the -tfo option\n", prog); goto end; } if (isquic && alpn_in == NULL) { BIO_printf(bio_err, "%s: QUIC requires ALPN to be specified (e.g. \"h3\" for HTTP/3) via the -alpn option\n", prog); goto end; } #endif if (tfo) BIO_printf(bio_c_out, "Connecting via TFO\n"); re_start: /* peer_addr might be set from previous connections */ BIO_ADDR_free(peer_addr); peer_addr = NULL; if (init_client(&sock, host, port, bindhost, bindport, socket_family, socket_type, protocol, tfo, !isquic, &peer_addr) == 0) { BIO_printf(bio_err, "connect:errno=%d\n", get_last_socket_error()); BIO_closesocket(sock); goto end; } BIO_printf(bio_c_out, "CONNECTED(%08X)\n", sock); /* * QUIC always uses a non-blocking socket - and we have to switch on * non-blocking mode at the SSL level */ if (c_nbio || isquic) { if (!BIO_socket_nbio(sock, 1)) { ERR_print_errors(bio_err); goto end; } if (c_nbio) { if (isquic && !SSL_set_blocking_mode(con, 0)) goto end; BIO_printf(bio_c_out, "Turned on non blocking io\n"); } } #ifndef OPENSSL_NO_DTLS if (isdtls) { union BIO_sock_info_u peer_info; #ifndef OPENSSL_NO_SCTP if (protocol == IPPROTO_SCTP) sbio = BIO_new_dgram_sctp(sock, BIO_NOCLOSE); else #endif sbio = BIO_new_dgram(sock, BIO_NOCLOSE); if (sbio == NULL || (peer_info.addr = BIO_ADDR_new()) == NULL) { BIO_printf(bio_err, "memory allocation failure\n"); BIO_free(sbio); BIO_closesocket(sock); goto end; } if (!BIO_sock_info(sock, BIO_SOCK_INFO_ADDRESS, &peer_info)) { BIO_printf(bio_err, "getsockname:errno=%d\n", get_last_socket_error()); BIO_free(sbio); BIO_ADDR_free(peer_info.addr); BIO_closesocket(sock); goto end; } (void)BIO_ctrl_set_connected(sbio, peer_info.addr); BIO_ADDR_free(peer_info.addr); peer_info.addr = NULL; if (enable_timeouts) { timeout.tv_sec = 0; timeout.tv_usec = DGRAM_RCV_TIMEOUT; BIO_ctrl(sbio, BIO_CTRL_DGRAM_SET_RECV_TIMEOUT, 0, &timeout); timeout.tv_sec = 0; timeout.tv_usec = DGRAM_SND_TIMEOUT; BIO_ctrl(sbio, BIO_CTRL_DGRAM_SET_SEND_TIMEOUT, 0, &timeout); } if (socket_mtu) { if (socket_mtu < DTLS_get_link_min_mtu(con)) { BIO_printf(bio_err, "MTU too small. Must be at least %ld\n", DTLS_get_link_min_mtu(con)); BIO_free(sbio); goto shut; } SSL_set_options(con, SSL_OP_NO_QUERY_MTU); if (!DTLS_set_link_mtu(con, socket_mtu)) { BIO_printf(bio_err, "Failed to set MTU\n"); BIO_free(sbio); goto shut; } } else { /* want to do MTU discovery */ BIO_ctrl(sbio, BIO_CTRL_DGRAM_MTU_DISCOVER, 0, NULL); } } else #endif /* OPENSSL_NO_DTLS */ #ifndef OPENSSL_NO_QUIC if (isquic) { sbio = BIO_new_dgram(sock, BIO_NOCLOSE); if (!SSL_set1_initial_peer_addr(con, peer_addr)) { BIO_printf(bio_err, "Failed to set the initial peer address\n"); goto shut; } } else #endif sbio = BIO_new_socket(sock, BIO_NOCLOSE); if (sbio == NULL) { BIO_printf(bio_err, "Unable to create BIO\n"); ERR_print_errors(bio_err); BIO_closesocket(sock); goto end; } /* Now that we're using a BIO... */ if (tfo) { (void)BIO_set_conn_address(sbio, peer_addr); (void)BIO_set_tfo(sbio, 1); } if (nbio_test) { BIO *test; test = BIO_new(BIO_f_nbio_test()); if (test == NULL) { BIO_printf(bio_err, "Unable to create BIO\n"); BIO_free(sbio); goto shut; } sbio = BIO_push(test, sbio); } if (c_debug) { BIO_set_callback_ex(sbio, bio_dump_callback); BIO_set_callback_arg(sbio, (char *)bio_c_out); } if (c_msg) { #ifndef OPENSSL_NO_SSL_TRACE if (c_msg == 2) SSL_set_msg_callback(con, SSL_trace); else #endif SSL_set_msg_callback(con, msg_cb); SSL_set_msg_callback_arg(con, bio_c_msg ? bio_c_msg : bio_c_out); } if (c_tlsextdebug) { SSL_set_tlsext_debug_callback(con, tlsext_cb); SSL_set_tlsext_debug_arg(con, bio_c_out); } #ifndef OPENSSL_NO_OCSP if (c_status_req) { SSL_set_tlsext_status_type(con, TLSEXT_STATUSTYPE_ocsp); SSL_CTX_set_tlsext_status_cb(ctx, ocsp_resp_cb); SSL_CTX_set_tlsext_status_arg(ctx, bio_c_out); } #endif SSL_set_bio(con, sbio, sbio); SSL_set_connect_state(con); /* ok, lets connect */ if (fileno_stdin() > SSL_get_fd(con)) width = fileno_stdin() + 1; else width = SSL_get_fd(con) + 1; read_tty = 1; write_tty = 0; tty_on = 0; read_ssl = 1; write_ssl = 1; first_loop = 1; cbuf_len = 0; cbuf_off = 0; sbuf_len = 0; sbuf_off = 0; #ifndef OPENSSL_NO_HTTP if (proxystr != NULL) { /* Here we must use the connect string target host & port */ if (!OSSL_HTTP_proxy_connect(sbio, thost, tport, proxyuser, proxypass, 0 /* no timeout */, bio_err, prog)) goto shut; } #endif switch ((PROTOCOL_CHOICE) starttls_proto) { case PROTO_OFF: break; case PROTO_LMTP: case PROTO_SMTP: { /* * This is an ugly hack that does a lot of assumptions. We do * have to handle multi-line responses which may come in a single * packet or not. We therefore have to use BIO_gets() which does * need a buffering BIO. So during the initial chitchat we do * push a buffering BIO into the chain that is removed again * later on to not disturb the rest of the s_client operation. */ int foundit = 0; BIO *fbio = BIO_new(BIO_f_buffer()); if (fbio == NULL) { BIO_printf(bio_err, "Unable to create BIO\n"); goto shut; } BIO_push(fbio, sbio); /* Wait for multi-line response to end from LMTP or SMTP */ do { mbuf_len = BIO_gets(fbio, mbuf, BUFSIZZ); } while (mbuf_len > 3 && mbuf[3] == '-'); if (protohost == NULL) protohost = "mail.example.com"; if (starttls_proto == (int)PROTO_LMTP) BIO_printf(fbio, "LHLO %s\r\n", protohost); else BIO_printf(fbio, "EHLO %s\r\n", protohost); (void)BIO_flush(fbio); /* * Wait for multi-line response to end LHLO LMTP or EHLO SMTP * response. */ do { mbuf_len = BIO_gets(fbio, mbuf, BUFSIZZ); if (strstr(mbuf, "STARTTLS")) foundit = 1; } while (mbuf_len > 3 && mbuf[3] == '-'); (void)BIO_flush(fbio); BIO_pop(fbio); BIO_free(fbio); if (!foundit) BIO_printf(bio_err, "Didn't find STARTTLS in server response," " trying anyway...\n"); BIO_printf(sbio, "STARTTLS\r\n"); BIO_read(sbio, sbuf, BUFSIZZ); } break; case PROTO_POP3: { BIO_read(sbio, mbuf, BUFSIZZ); BIO_printf(sbio, "STLS\r\n"); mbuf_len = BIO_read(sbio, sbuf, BUFSIZZ); if (mbuf_len < 0) { BIO_printf(bio_err, "BIO_read failed\n"); goto end; } } break; case PROTO_IMAP: { int foundit = 0; BIO *fbio = BIO_new(BIO_f_buffer()); if (fbio == NULL) { BIO_printf(bio_err, "Unable to create BIO\n"); goto shut; } BIO_push(fbio, sbio); BIO_gets(fbio, mbuf, BUFSIZZ); /* STARTTLS command requires CAPABILITY... */ BIO_printf(fbio, ". CAPABILITY\r\n"); (void)BIO_flush(fbio); /* wait for multi-line CAPABILITY response */ do { mbuf_len = BIO_gets(fbio, mbuf, BUFSIZZ); if (strstr(mbuf, "STARTTLS")) foundit = 1; } while (mbuf_len > 3 && mbuf[0] != '.'); (void)BIO_flush(fbio); BIO_pop(fbio); BIO_free(fbio); if (!foundit) BIO_printf(bio_err, "Didn't find STARTTLS in server response," " trying anyway...\n"); BIO_printf(sbio, ". STARTTLS\r\n"); BIO_read(sbio, sbuf, BUFSIZZ); } break; case PROTO_FTP: { BIO *fbio = BIO_new(BIO_f_buffer()); if (fbio == NULL) { BIO_printf(bio_err, "Unable to create BIO\n"); goto shut; } BIO_push(fbio, sbio); /* wait for multi-line response to end from FTP */ do { mbuf_len = BIO_gets(fbio, mbuf, BUFSIZZ); } while (mbuf_len > 3 && (!isdigit((unsigned char)mbuf[0]) || !isdigit((unsigned char)mbuf[1]) || !isdigit((unsigned char)mbuf[2]) || mbuf[3] != ' ')); (void)BIO_flush(fbio); BIO_pop(fbio); BIO_free(fbio); BIO_printf(sbio, "AUTH TLS\r\n"); BIO_read(sbio, sbuf, BUFSIZZ); } break; case PROTO_XMPP: case PROTO_XMPP_SERVER: { int seen = 0; BIO_printf(sbio, "", starttls_proto == PROTO_XMPP ? "client" : "server", protohost ? protohost : host); seen = BIO_read(sbio, mbuf, BUFSIZZ); if (seen < 0) { BIO_printf(bio_err, "BIO_read failed\n"); goto end; } mbuf[seen] = '\0'; while (!strstr (mbuf, ""); seen = BIO_read(sbio, sbuf, BUFSIZZ); if (seen < 0) { BIO_printf(bio_err, "BIO_read failed\n"); goto shut; } sbuf[seen] = '\0'; if (!strstr(sbuf, "= bytes) { BIO_printf(bio_err, "Cannot confirm server version. "); goto shut; } else if (packet[pos++] == '\0') { break; } } /* make sure we have at least 15 bytes left in the packet */ if (pos + 15 > bytes) { BIO_printf(bio_err, "MySQL server handshake packet is broken.\n"); goto shut; } pos += 12; /* skip over conn id[4] + SALT[8] */ if (packet[pos++] != '\0') { /* verify filler */ BIO_printf(bio_err, "MySQL packet is broken.\n"); goto shut; } /* capability flags[2] */ if (!((packet[pos] + (packet[pos + 1] << 8)) & ssl_flg)) { BIO_printf(bio_err, "MySQL server does not support SSL.\n"); goto shut; } /* Sending SSL Handshake packet. */ BIO_write(sbio, ssl_req, sizeof(ssl_req)); (void)BIO_flush(sbio); } break; case PROTO_POSTGRES: { static const unsigned char ssl_request[] = { /* Length SSLRequest */ 0, 0, 0, 8, 4, 210, 22, 47 }; int bytes; /* Send SSLRequest packet */ BIO_write(sbio, ssl_request, 8); (void)BIO_flush(sbio); /* Reply will be a single S if SSL is enabled */ bytes = BIO_read(sbio, sbuf, BUFSIZZ); if (bytes != 1 || sbuf[0] != 'S') goto shut; } break; case PROTO_NNTP: { int foundit = 0; BIO *fbio = BIO_new(BIO_f_buffer()); if (fbio == NULL) { BIO_printf(bio_err, "Unable to create BIO\n"); goto end; } BIO_push(fbio, sbio); BIO_gets(fbio, mbuf, BUFSIZZ); /* STARTTLS command requires CAPABILITIES... */ BIO_printf(fbio, "CAPABILITIES\r\n"); (void)BIO_flush(fbio); BIO_gets(fbio, mbuf, BUFSIZZ); /* no point in trying to parse the CAPABILITIES response if there is none */ if (strstr(mbuf, "101") != NULL) { /* wait for multi-line CAPABILITIES response */ do { mbuf_len = BIO_gets(fbio, mbuf, BUFSIZZ); if (strstr(mbuf, "STARTTLS")) foundit = 1; } while (mbuf_len > 1 && mbuf[0] != '.'); } (void)BIO_flush(fbio); BIO_pop(fbio); BIO_free(fbio); if (!foundit) BIO_printf(bio_err, "Didn't find STARTTLS in server response," " trying anyway...\n"); BIO_printf(sbio, "STARTTLS\r\n"); mbuf_len = BIO_read(sbio, mbuf, BUFSIZZ); if (mbuf_len < 0) { BIO_printf(bio_err, "BIO_read failed\n"); goto end; } mbuf[mbuf_len] = '\0'; if (strstr(mbuf, "382") == NULL) { BIO_printf(bio_err, "STARTTLS failed: %s", mbuf); goto shut; } } break; case PROTO_SIEVE: { int foundit = 0; BIO *fbio = BIO_new(BIO_f_buffer()); if (fbio == NULL) { BIO_printf(bio_err, "Unable to create BIO\n"); goto end; } BIO_push(fbio, sbio); /* wait for multi-line response to end from Sieve */ do { mbuf_len = BIO_gets(fbio, mbuf, BUFSIZZ); /* * According to RFC 5804 § 1.7, capability * is case-insensitive, make it uppercase */ if (mbuf_len > 1 && mbuf[0] == '"') { make_uppercase(mbuf); if (HAS_PREFIX(mbuf, "\"STARTTLS\"")) foundit = 1; } } while (mbuf_len > 1 && mbuf[0] == '"'); (void)BIO_flush(fbio); BIO_pop(fbio); BIO_free(fbio); if (!foundit) BIO_printf(bio_err, "Didn't find STARTTLS in server response," " trying anyway...\n"); BIO_printf(sbio, "STARTTLS\r\n"); mbuf_len = BIO_read(sbio, mbuf, BUFSIZZ); if (mbuf_len < 0) { BIO_printf(bio_err, "BIO_read failed\n"); goto end; } mbuf[mbuf_len] = '\0'; if (mbuf_len < 2) { BIO_printf(bio_err, "STARTTLS failed: %s", mbuf); goto shut; } /* * According to RFC 5804 § 2.2, response codes are case- * insensitive, make it uppercase but preserve the response. */ strncpy(sbuf, mbuf, 2); make_uppercase(sbuf); if (!HAS_PREFIX(sbuf, "OK")) { BIO_printf(bio_err, "STARTTLS not supported: %s", mbuf); goto shut; } } break; case PROTO_LDAP: { /* StartTLS Operation according to RFC 4511 */ static char ldap_tls_genconf[] = "asn1=SEQUENCE:LDAPMessage\n" "[LDAPMessage]\n" "messageID=INTEGER:1\n" "extendedReq=EXPLICIT:23A,IMPLICIT:0C," "FORMAT:ASCII,OCT:1.3.6.1.4.1.1466.20037\n"; long errline = -1; char *genstr = NULL; int result = -1; ASN1_TYPE *atyp = NULL; BIO *ldapbio = BIO_new(BIO_s_mem()); CONF *cnf = NCONF_new(NULL); if (ldapbio == NULL || cnf == NULL) { BIO_free(ldapbio); NCONF_free(cnf); goto end; } BIO_puts(ldapbio, ldap_tls_genconf); if (NCONF_load_bio(cnf, ldapbio, &errline) <= 0) { BIO_free(ldapbio); NCONF_free(cnf); if (errline <= 0) { BIO_printf(bio_err, "NCONF_load_bio failed\n"); goto end; } else { BIO_printf(bio_err, "Error on line %ld\n", errline); goto end; } } BIO_free(ldapbio); genstr = NCONF_get_string(cnf, "default", "asn1"); if (genstr == NULL) { NCONF_free(cnf); BIO_printf(bio_err, "NCONF_get_string failed\n"); goto end; } atyp = ASN1_generate_nconf(genstr, cnf); - if (atyp == NULL) { + if (atyp == NULL || atyp->type != V_ASN1_SEQUENCE) { NCONF_free(cnf); + ASN1_TYPE_free(atyp); BIO_printf(bio_err, "ASN1_generate_nconf failed\n"); goto end; } NCONF_free(cnf); /* Send SSLRequest packet */ BIO_write(sbio, atyp->value.sequence->data, atyp->value.sequence->length); (void)BIO_flush(sbio); ASN1_TYPE_free(atyp); mbuf_len = BIO_read(sbio, mbuf, BUFSIZZ); if (mbuf_len < 0) { BIO_printf(bio_err, "BIO_read failed\n"); goto end; } result = ldap_ExtendedResponse_parse(mbuf, mbuf_len); if (result < 0) { BIO_printf(bio_err, "ldap_ExtendedResponse_parse failed\n"); goto shut; } else if (result > 0) { BIO_printf(bio_err, "STARTTLS failed, LDAP Result Code: %i\n", result); goto shut; } mbuf_len = 0; } break; } if (early_data_file != NULL && ((SSL_get0_session(con) != NULL && SSL_SESSION_get_max_early_data(SSL_get0_session(con)) > 0) || (psksess != NULL && SSL_SESSION_get_max_early_data(psksess) > 0))) { BIO *edfile = BIO_new_file(early_data_file, "r"); size_t readbytes, writtenbytes; int finish = 0; if (edfile == NULL) { BIO_printf(bio_err, "Cannot open early data file\n"); goto shut; } while (!finish) { if (!BIO_read_ex(edfile, cbuf, BUFSIZZ, &readbytes)) finish = 1; while (!SSL_write_early_data(con, cbuf, readbytes, &writtenbytes)) { switch (SSL_get_error(con, 0)) { case SSL_ERROR_WANT_WRITE: case SSL_ERROR_WANT_ASYNC: case SSL_ERROR_WANT_READ: /* Just keep trying - busy waiting */ continue; default: BIO_printf(bio_err, "Error writing early data\n"); BIO_free(edfile); ERR_print_errors(bio_err); goto shut; } } } BIO_free(edfile); } user_data_init(&user_data, con, cbuf, BUFSIZZ, cmdmode); for (;;) { FD_ZERO(&readfds); FD_ZERO(&writefds); if ((isdtls || isquic) && SSL_get_event_timeout(con, &timeout, &is_infinite) && !is_infinite) timeoutp = &timeout; else timeoutp = NULL; if (!SSL_is_init_finished(con) && SSL_total_renegotiations(con) == 0 && SSL_get_key_update_type(con) == SSL_KEY_UPDATE_NONE) { in_init = 1; tty_on = 0; } else { tty_on = 1; if (in_init) { in_init = 0; if (c_brief) { BIO_puts(bio_err, "CONNECTION ESTABLISHED\n"); print_ssl_summary(con); } print_stuff(bio_c_out, con, full_log); if (full_log > 0) full_log--; if (starttls_proto) { BIO_write(bio_err, mbuf, mbuf_len); /* We don't need to know any more */ if (!reconnect) starttls_proto = PROTO_OFF; } if (reconnect) { reconnect--; BIO_printf(bio_c_out, "drop connection and then reconnect\n"); do_ssl_shutdown(con); SSL_set_connect_state(con); BIO_closesocket(SSL_get_fd(con)); goto re_start; } } } if (!write_ssl) { do { switch (user_data_process(&user_data, &cbuf_len, &cbuf_off)) { default: BIO_printf(bio_err, "ERROR\n"); /* fall through */ case USER_DATA_PROCESS_SHUT: ret = 0; goto shut; case USER_DATA_PROCESS_RESTART: goto re_start; case USER_DATA_PROCESS_NO_DATA: break; case USER_DATA_PROCESS_CONTINUE: write_ssl = 1; break; } } while (!write_ssl && cbuf_len == 0 && user_data_has_data(&user_data)); if (cbuf_len > 0) { read_tty = 0; timeout.tv_sec = 0; timeout.tv_usec = 0; } else { read_tty = 1; } } ssl_pending = read_ssl && SSL_has_pending(con); if (!ssl_pending) { #if !defined(OPENSSL_SYS_WINDOWS) && !defined(OPENSSL_SYS_MSDOS) if (tty_on) { /* * Note that select() returns when read _would not block_, * and EOF satisfies that. To avoid a CPU-hogging loop, * set the flag so we exit. */ if (read_tty && !at_eof) openssl_fdset(fileno_stdin(), &readfds); #if !defined(OPENSSL_SYS_VMS) if (write_tty) openssl_fdset(fileno_stdout(), &writefds); #endif } /* * Note that for QUIC we never actually check FD_ISSET() for the * underlying network fds. We just rely on select waking up when * they become readable/writeable and then SSL_handle_events() doing * the right thing. */ if ((!isquic && read_ssl) || (isquic && SSL_net_read_desired(con))) openssl_fdset(SSL_get_fd(con), &readfds); if ((!isquic && write_ssl) || (isquic && (first_loop || SSL_net_write_desired(con)))) openssl_fdset(SSL_get_fd(con), &writefds); #else if (!tty_on || !write_tty) { if ((!isquic && read_ssl) || (isquic && SSL_net_read_desired(con))) openssl_fdset(SSL_get_fd(con), &readfds); if ((!isquic && write_ssl) || (isquic && (first_loop || SSL_net_write_desired(con)))) openssl_fdset(SSL_get_fd(con), &writefds); } #endif /* * Note: under VMS with SOCKETSHR the second parameter is * currently of type (int *) whereas under other systems it is * (void *) if you don't have a cast it will choke the compiler: * if you do have a cast then you can either go for (int *) or * (void *). */ #if defined(OPENSSL_SYS_WINDOWS) || defined(OPENSSL_SYS_MSDOS) /* * Under Windows/DOS we make the assumption that we can always * write to the tty: therefore, if we need to write to the tty we * just fall through. Otherwise we timeout the select every * second and see if there are any keypresses. Note: this is a * hack, in a proper Windows application we wouldn't do this. */ i = 0; if (!write_tty) { if (read_tty) { tv.tv_sec = 1; tv.tv_usec = 0; i = select(width, (void *)&readfds, (void *)&writefds, NULL, &tv); if (!i && (!has_stdin_waiting() || !read_tty)) continue; } else i = select(width, (void *)&readfds, (void *)&writefds, NULL, timeoutp); } #else i = select(width, (void *)&readfds, (void *)&writefds, NULL, timeoutp); #endif if (i < 0) { BIO_printf(bio_err, "bad select %d\n", get_last_socket_error()); goto shut; } } if (timeoutp != NULL) { SSL_handle_events(con); if (isdtls && !FD_ISSET(SSL_get_fd(con), &readfds) && !FD_ISSET(SSL_get_fd(con), &writefds)) BIO_printf(bio_err, "TIMEOUT occurred\n"); } if (!ssl_pending && ((!isquic && FD_ISSET(SSL_get_fd(con), &writefds)) || (isquic && (cbuf_len > 0 || first_loop)))) { k = SSL_write(con, &(cbuf[cbuf_off]), (unsigned int)cbuf_len); switch (SSL_get_error(con, k)) { case SSL_ERROR_NONE: cbuf_off += k; cbuf_len -= k; if (k <= 0) goto end; /* we have done a write(con,NULL,0); */ if (cbuf_len == 0) { read_tty = 1; write_ssl = 0; } else { /* if (cbuf_len > 0) */ read_tty = 0; write_ssl = 1; } break; case SSL_ERROR_WANT_WRITE: BIO_printf(bio_c_out, "write W BLOCK\n"); write_ssl = 1; read_tty = 0; break; case SSL_ERROR_WANT_ASYNC: BIO_printf(bio_c_out, "write A BLOCK\n"); wait_for_async(con); write_ssl = 1; read_tty = 0; break; case SSL_ERROR_WANT_READ: BIO_printf(bio_c_out, "write R BLOCK\n"); write_tty = 0; read_ssl = 1; write_ssl = 0; break; case SSL_ERROR_WANT_X509_LOOKUP: BIO_printf(bio_c_out, "write X BLOCK\n"); break; case SSL_ERROR_ZERO_RETURN: if (cbuf_len != 0) { BIO_printf(bio_c_out, "shutdown\n"); ret = 0; goto shut; } else { read_tty = 1; write_ssl = 0; break; } case SSL_ERROR_SYSCALL: if ((k != 0) || (cbuf_len != 0)) { int sockerr = get_last_socket_error(); if (!tfo || sockerr != EISCONN) { BIO_printf(bio_err, "write:errno=%d\n", sockerr); goto shut; } } else { read_tty = 1; write_ssl = 0; } break; case SSL_ERROR_WANT_ASYNC_JOB: /* This shouldn't ever happen in s_client - treat as an error */ case SSL_ERROR_SSL: ERR_print_errors(bio_err); goto shut; } } #if defined(OPENSSL_SYS_WINDOWS) || defined(OPENSSL_SYS_MSDOS) || defined(OPENSSL_SYS_VMS) /* Assume Windows/DOS/BeOS can always write */ else if (!ssl_pending && write_tty) #else else if (!ssl_pending && FD_ISSET(fileno_stdout(), &writefds)) #endif { #ifdef CHARSET_EBCDIC ascii2ebcdic(&(sbuf[sbuf_off]), &(sbuf[sbuf_off]), sbuf_len); #endif i = raw_write_stdout(&(sbuf[sbuf_off]), sbuf_len); if (i <= 0) { BIO_printf(bio_c_out, "DONE\n"); ret = 0; goto shut; } sbuf_len -= i; sbuf_off += i; if (sbuf_len <= 0) { read_ssl = 1; write_tty = 0; } } else if (ssl_pending || (!isquic && FD_ISSET(SSL_get_fd(con), &readfds))) { #ifdef RENEG { static int iiii; if (++iiii == 52) { SSL_renegotiate(con); iiii = 0; } } #endif k = SSL_read(con, sbuf, BUFSIZZ); switch (SSL_get_error(con, k)) { case SSL_ERROR_NONE: if (k <= 0) goto end; sbuf_off = 0; sbuf_len = k; read_ssl = 0; write_tty = 1; break; case SSL_ERROR_WANT_ASYNC: BIO_printf(bio_c_out, "read A BLOCK\n"); wait_for_async(con); write_tty = 0; read_ssl = 1; if ((read_tty == 0) && (write_ssl == 0)) write_ssl = 1; break; case SSL_ERROR_WANT_WRITE: BIO_printf(bio_c_out, "read W BLOCK\n"); write_ssl = 1; read_tty = 0; break; case SSL_ERROR_WANT_READ: BIO_printf(bio_c_out, "read R BLOCK\n"); write_tty = 0; read_ssl = 1; if ((read_tty == 0) && (write_ssl == 0)) write_ssl = 1; break; case SSL_ERROR_WANT_X509_LOOKUP: BIO_printf(bio_c_out, "read X BLOCK\n"); break; case SSL_ERROR_SYSCALL: ret = get_last_socket_error(); if (c_brief) BIO_puts(bio_err, "CONNECTION CLOSED BY SERVER\n"); else BIO_printf(bio_err, "read:errno=%d\n", ret); goto shut; case SSL_ERROR_ZERO_RETURN: BIO_printf(bio_c_out, "closed\n"); ret = 0; goto shut; case SSL_ERROR_WANT_ASYNC_JOB: /* This shouldn't ever happen in s_client. Treat as an error */ case SSL_ERROR_SSL: ERR_print_errors(bio_err); goto shut; } } /* don't wait for client input in the non-interactive mode */ else if (nointeractive) { ret = 0; goto shut; } /* OPENSSL_SYS_MSDOS includes OPENSSL_SYS_WINDOWS */ #if defined(OPENSSL_SYS_MSDOS) else if (has_stdin_waiting()) #else else if (FD_ISSET(fileno_stdin(), &readfds)) #endif { if (crlf) { int j, lf_num; i = raw_read_stdin(cbuf, BUFSIZZ / 2); lf_num = 0; /* both loops are skipped when i <= 0 */ for (j = 0; j < i; j++) if (cbuf[j] == '\n') lf_num++; for (j = i - 1; j >= 0; j--) { cbuf[j + lf_num] = cbuf[j]; if (cbuf[j] == '\n') { lf_num--; i++; cbuf[j + lf_num] = '\r'; } } assert(lf_num == 0); } else i = raw_read_stdin(cbuf, BUFSIZZ); #if !defined(OPENSSL_SYS_WINDOWS) && !defined(OPENSSL_SYS_MSDOS) if (i == 0) at_eof = 1; #endif if (!c_ign_eof && i <= 0) { BIO_printf(bio_err, "DONE\n"); ret = 0; goto shut; } if (i > 0 && !user_data_add(&user_data, i)) { ret = 0; goto shut; } read_tty = 0; } first_loop = 0; } shut: if (in_init) print_stuff(bio_c_out, con, full_log); do_ssl_shutdown(con); /* * If we ended with an alert being sent, but still with data in the * network buffer to be read, then calling BIO_closesocket() will * result in a TCP-RST being sent. On some platforms (notably * Windows) then this will result in the peer immediately abandoning * the connection including any buffered alert data before it has * had a chance to be read. Shutting down the sending side first, * and then closing the socket sends TCP-FIN first followed by * TCP-RST. This seems to allow the peer to read the alert data. */ shutdown(SSL_get_fd(con), 1); /* SHUT_WR */ /* * We just said we have nothing else to say, but it doesn't mean that * the other side has nothing. It's even recommended to consume incoming * data. [In testing context this ensures that alerts are passed on...] */ timeout.tv_sec = 0; timeout.tv_usec = 500000; /* some extreme round-trip */ do { FD_ZERO(&readfds); openssl_fdset(sock, &readfds); } while (select(sock + 1, &readfds, NULL, NULL, &timeout) > 0 && BIO_read(sbio, sbuf, BUFSIZZ) > 0); BIO_closesocket(SSL_get_fd(con)); end: if (con != NULL) { if (prexit != 0) print_stuff(bio_c_out, con, 1); SSL_free(con); } SSL_SESSION_free(psksess); #if !defined(OPENSSL_NO_NEXTPROTONEG) OPENSSL_free(next_proto.data); #endif SSL_CTX_free(ctx); set_keylog_file(NULL, NULL); X509_free(cert); sk_X509_CRL_pop_free(crls, X509_CRL_free); EVP_PKEY_free(key); OSSL_STACK_OF_X509_free(chain); OPENSSL_free(pass); #ifndef OPENSSL_NO_SRP OPENSSL_free(srp_arg.srppassin); #endif OPENSSL_free(sname_alloc); BIO_ADDR_free(peer_addr); OPENSSL_free(connectstr); OPENSSL_free(bindstr); OPENSSL_free(bindhost); OPENSSL_free(bindport); OPENSSL_free(host); OPENSSL_free(port); OPENSSL_free(thost); OPENSSL_free(tport); X509_VERIFY_PARAM_free(vpm); ssl_excert_free(exc); sk_OPENSSL_STRING_free(ssl_args); sk_OPENSSL_STRING_free(dane_tlsa_rrset); SSL_CONF_CTX_free(cctx); OPENSSL_clear_free(cbuf, BUFSIZZ); OPENSSL_clear_free(sbuf, BUFSIZZ); OPENSSL_clear_free(mbuf, BUFSIZZ); clear_free(proxypass); release_engine(e); BIO_free(bio_c_out); bio_c_out = NULL; BIO_free(bio_c_msg); bio_c_msg = NULL; return ret; } static char *ec_curve_name(EVP_PKEY *pkey) { char *curve = 0; size_t namelen; if (EVP_PKEY_get_group_name(pkey, NULL, 0, &namelen)) { curve = OPENSSL_malloc(++namelen); if (!EVP_PKEY_get_group_name(pkey, curve, namelen, 0)) { OPENSSL_free(curve); curve = NULL; } } return (curve); } static void print_cert_key_info(BIO *bio, X509 *cert) { EVP_PKEY *pkey = X509_get0_pubkey(cert); char *curve = NULL; const char *keyalg; if (pkey == NULL) return; keyalg = EVP_PKEY_get0_type_name(pkey); if (keyalg == NULL) keyalg = OBJ_nid2ln(EVP_PKEY_get_base_id(pkey)); if (EVP_PKEY_id(pkey) == EVP_PKEY_EC) curve = ec_curve_name(pkey); if (curve != NULL) BIO_printf(bio, " a:PKEY: %s, (%s); sigalg: %s\n", keyalg, curve, OBJ_nid2ln(X509_get_signature_nid(cert))); else BIO_printf(bio, " a:PKEY: %s, %d (bit); sigalg: %s\n", keyalg, EVP_PKEY_get_bits(pkey), OBJ_nid2ln(X509_get_signature_nid(cert))); OPENSSL_free(curve); } static void print_stuff(BIO *bio, SSL *s, int full) { X509 *peer = NULL; STACK_OF(X509) *sk; const SSL_CIPHER *c; int i, istls13 = (SSL_version(s) == TLS1_3_VERSION); long verify_result; #ifndef OPENSSL_NO_COMP const COMP_METHOD *comp, *expansion; #endif unsigned char *exportedkeymat; #ifndef OPENSSL_NO_CT const SSL_CTX *ctx = SSL_get_SSL_CTX(s); #endif if (full) { int got_a_chain = 0; sk = SSL_get_peer_cert_chain(s); if (sk != NULL) { got_a_chain = 1; BIO_printf(bio, "---\nCertificate chain\n"); for (i = 0; i < sk_X509_num(sk); i++) { X509 *chain_cert = sk_X509_value(sk, i); BIO_printf(bio, "%2d s:", i); X509_NAME_print_ex(bio, X509_get_subject_name(chain_cert), 0, get_nameopt()); BIO_puts(bio, "\n"); BIO_printf(bio, " i:"); X509_NAME_print_ex(bio, X509_get_issuer_name(chain_cert), 0, get_nameopt()); BIO_puts(bio, "\n"); print_cert_key_info(bio, chain_cert); BIO_printf(bio, " v:NotBefore: "); ASN1_TIME_print(bio, X509_get0_notBefore(chain_cert)); BIO_printf(bio, "; NotAfter: "); ASN1_TIME_print(bio, X509_get0_notAfter(chain_cert)); BIO_puts(bio, "\n"); if (c_showcerts) PEM_write_bio_X509(bio, chain_cert); } } BIO_printf(bio, "---\n"); peer = SSL_get0_peer_certificate(s); if (peer != NULL) { BIO_printf(bio, "Server certificate\n"); /* Redundant if we showed the whole chain */ if (!(c_showcerts && got_a_chain)) PEM_write_bio_X509(bio, peer); dump_cert_text(bio, peer); } else { BIO_printf(bio, "no peer certificate available\n"); } /* Only display RPK information if configured */ if (SSL_get_negotiated_client_cert_type(s) == TLSEXT_cert_type_rpk) BIO_printf(bio, "Client-to-server raw public key negotiated\n"); if (SSL_get_negotiated_server_cert_type(s) == TLSEXT_cert_type_rpk) BIO_printf(bio, "Server-to-client raw public key negotiated\n"); if (enable_server_rpk) { EVP_PKEY *peer_rpk = SSL_get0_peer_rpk(s); if (peer_rpk != NULL) { BIO_printf(bio, "Server raw public key\n"); EVP_PKEY_print_public(bio, peer_rpk, 2, NULL); } else { BIO_printf(bio, "no peer rpk available\n"); } } print_ca_names(bio, s); ssl_print_sigalgs(bio, s); ssl_print_tmp_key(bio, s); #ifndef OPENSSL_NO_CT /* * When the SSL session is anonymous, or resumed via an abbreviated * handshake, no SCTs are provided as part of the handshake. While in * a resumed session SCTs may be present in the session's certificate, * no callbacks are invoked to revalidate these, and in any case that * set of SCTs may be incomplete. Thus it makes little sense to * attempt to display SCTs from a resumed session's certificate, and of * course none are associated with an anonymous peer. */ if (peer != NULL && !SSL_session_reused(s) && SSL_ct_is_enabled(s)) { const STACK_OF(SCT) *scts = SSL_get0_peer_scts(s); int sct_count = scts != NULL ? sk_SCT_num(scts) : 0; BIO_printf(bio, "---\nSCTs present (%i)\n", sct_count); if (sct_count > 0) { const CTLOG_STORE *log_store = SSL_CTX_get0_ctlog_store(ctx); BIO_printf(bio, "---\n"); for (i = 0; i < sct_count; ++i) { SCT *sct = sk_SCT_value(scts, i); BIO_printf(bio, "SCT validation status: %s\n", SCT_validation_status_string(sct)); SCT_print(sct, bio, 0, log_store); if (i < sct_count - 1) BIO_printf(bio, "\n---\n"); } BIO_printf(bio, "\n"); } } #endif BIO_printf(bio, "---\nSSL handshake has read %ju bytes " "and written %ju bytes\n", BIO_number_read(SSL_get_rbio(s)), BIO_number_written(SSL_get_wbio(s))); } print_verify_detail(s, bio); BIO_printf(bio, (SSL_session_reused(s) ? "---\nReused, " : "---\nNew, ")); c = SSL_get_current_cipher(s); BIO_printf(bio, "%s, Cipher is %s\n", SSL_CIPHER_get_version(c), SSL_CIPHER_get_name(c)); BIO_printf(bio, "Protocol: %s\n", SSL_get_version(s)); if (peer != NULL) { EVP_PKEY *pktmp; pktmp = X509_get0_pubkey(peer); BIO_printf(bio, "Server public key is %d bit\n", EVP_PKEY_get_bits(pktmp)); } ssl_print_secure_renegotiation_notes(bio, s); #ifndef OPENSSL_NO_COMP comp = SSL_get_current_compression(s); expansion = SSL_get_current_expansion(s); BIO_printf(bio, "Compression: %s\n", comp ? SSL_COMP_get_name(comp) : "NONE"); BIO_printf(bio, "Expansion: %s\n", expansion ? SSL_COMP_get_name(expansion) : "NONE"); #endif #ifndef OPENSSL_NO_KTLS if (BIO_get_ktls_send(SSL_get_wbio(s))) BIO_printf(bio_err, "Using Kernel TLS for sending\n"); if (BIO_get_ktls_recv(SSL_get_rbio(s))) BIO_printf(bio_err, "Using Kernel TLS for receiving\n"); #endif if (OSSL_TRACE_ENABLED(TLS)) { /* Print out local port of connection: useful for debugging */ int sock; union BIO_sock_info_u info; sock = SSL_get_fd(s); if ((info.addr = BIO_ADDR_new()) != NULL && BIO_sock_info(sock, BIO_SOCK_INFO_ADDRESS, &info)) { BIO_printf(bio_c_out, "LOCAL PORT is %u\n", ntohs(BIO_ADDR_rawport(info.addr))); } BIO_ADDR_free(info.addr); } #if !defined(OPENSSL_NO_NEXTPROTONEG) if (next_proto.status != -1) { const unsigned char *proto; unsigned int proto_len; SSL_get0_next_proto_negotiated(s, &proto, &proto_len); BIO_printf(bio, "Next protocol: (%d) ", next_proto.status); BIO_write(bio, proto, proto_len); BIO_write(bio, "\n", 1); } #endif { const unsigned char *proto; unsigned int proto_len; SSL_get0_alpn_selected(s, &proto, &proto_len); if (proto_len > 0) { BIO_printf(bio, "ALPN protocol: "); BIO_write(bio, proto, proto_len); BIO_write(bio, "\n", 1); } else BIO_printf(bio, "No ALPN negotiated\n"); } #ifndef OPENSSL_NO_SRTP { SRTP_PROTECTION_PROFILE *srtp_profile = SSL_get_selected_srtp_profile(s); if (srtp_profile) BIO_printf(bio, "SRTP Extension negotiated, profile=%s\n", srtp_profile->name); } #endif if (istls13) { switch (SSL_get_early_data_status(s)) { case SSL_EARLY_DATA_NOT_SENT: BIO_printf(bio, "Early data was not sent\n"); break; case SSL_EARLY_DATA_REJECTED: BIO_printf(bio, "Early data was rejected\n"); break; case SSL_EARLY_DATA_ACCEPTED: BIO_printf(bio, "Early data was accepted\n"); break; } /* * We also print the verify results when we dump session information, * but in TLSv1.3 we may not get that right away (or at all) depending * on when we get a NewSessionTicket. Therefore, we print it now as well. */ verify_result = SSL_get_verify_result(s); BIO_printf(bio, "Verify return code: %ld (%s)\n", verify_result, X509_verify_cert_error_string(verify_result)); } else { /* In TLSv1.3 we do this on arrival of a NewSessionTicket */ SSL_SESSION_print(bio, SSL_get_session(s)); } if (SSL_get_session(s) != NULL && keymatexportlabel != NULL) { BIO_printf(bio, "Keying material exporter:\n"); BIO_printf(bio, " Label: '%s'\n", keymatexportlabel); BIO_printf(bio, " Length: %i bytes\n", keymatexportlen); exportedkeymat = app_malloc(keymatexportlen, "export key"); if (SSL_export_keying_material(s, exportedkeymat, keymatexportlen, keymatexportlabel, strlen(keymatexportlabel), NULL, 0, 0) <= 0) { BIO_printf(bio, " Error\n"); } else { BIO_printf(bio, " Keying material: "); for (i = 0; i < keymatexportlen; i++) BIO_printf(bio, "%02X", exportedkeymat[i]); BIO_printf(bio, "\n"); } OPENSSL_free(exportedkeymat); } BIO_printf(bio, "---\n"); /* flush, or debugging output gets mixed with http response */ (void)BIO_flush(bio); } # ifndef OPENSSL_NO_OCSP static int ocsp_resp_cb(SSL *s, void *arg) { const unsigned char *p; int len; OCSP_RESPONSE *rsp; len = SSL_get_tlsext_status_ocsp_resp(s, &p); BIO_puts(arg, "OCSP response: "); if (p == NULL) { BIO_puts(arg, "no response sent\n"); return 1; } rsp = d2i_OCSP_RESPONSE(NULL, &p, len); if (rsp == NULL) { BIO_puts(arg, "response parse error\n"); BIO_dump_indent(arg, (char *)p, len, 4); return 0; } BIO_puts(arg, "\n======================================\n"); OCSP_RESPONSE_print(arg, rsp, 0); BIO_puts(arg, "======================================\n"); OCSP_RESPONSE_free(rsp); return 1; } # endif static int ldap_ExtendedResponse_parse(const char *buf, long rem) { const unsigned char *cur, *end; long len; int tag, xclass, inf, ret = -1; cur = (const unsigned char *)buf; end = cur + rem; /* * From RFC 4511: * * LDAPMessage ::= SEQUENCE { * messageID MessageID, * protocolOp CHOICE { * ... * extendedResp ExtendedResponse, * ... }, * controls [0] Controls OPTIONAL } * * ExtendedResponse ::= [APPLICATION 24] SEQUENCE { * COMPONENTS OF LDAPResult, * responseName [10] LDAPOID OPTIONAL, * responseValue [11] OCTET STRING OPTIONAL } * * LDAPResult ::= SEQUENCE { * resultCode ENUMERATED { * success (0), * ... * other (80), * ... }, * matchedDN LDAPDN, * diagnosticMessage LDAPString, * referral [3] Referral OPTIONAL } */ /* pull SEQUENCE */ inf = ASN1_get_object(&cur, &len, &tag, &xclass, rem); if (inf != V_ASN1_CONSTRUCTED || tag != V_ASN1_SEQUENCE || (rem = end - cur, len > rem)) { BIO_printf(bio_err, "Unexpected LDAP response\n"); goto end; } rem = len; /* ensure that we don't overstep the SEQUENCE */ /* pull MessageID */ inf = ASN1_get_object(&cur, &len, &tag, &xclass, rem); if (inf != V_ASN1_UNIVERSAL || tag != V_ASN1_INTEGER || (rem = end - cur, len > rem)) { BIO_printf(bio_err, "No MessageID\n"); goto end; } cur += len; /* shall we check for MessageId match or just skip? */ /* pull [APPLICATION 24] */ rem = end - cur; inf = ASN1_get_object(&cur, &len, &tag, &xclass, rem); if (inf != V_ASN1_CONSTRUCTED || xclass != V_ASN1_APPLICATION || tag != 24) { BIO_printf(bio_err, "Not ExtendedResponse\n"); goto end; } /* pull resultCode */ rem = end - cur; inf = ASN1_get_object(&cur, &len, &tag, &xclass, rem); if (inf != V_ASN1_UNIVERSAL || tag != V_ASN1_ENUMERATED || len == 0 || (rem = end - cur, len > rem)) { BIO_printf(bio_err, "Not LDAPResult\n"); goto end; } /* len should always be one, but just in case... */ for (ret = 0, inf = 0; inf < len; inf++) { ret <<= 8; ret |= cur[inf]; } /* There is more data, but we don't care... */ end: return ret; } /* * Host dNS Name verifier: used for checking that the hostname is in dNS format * before setting it as SNI */ static int is_dNS_name(const char *host) { const size_t MAX_LABEL_LENGTH = 63; size_t i; int isdnsname = 0; size_t length = strlen(host); size_t label_length = 0; int all_numeric = 1; /* * Deviation from strict DNS name syntax, also check names with '_' * Check DNS name syntax, any '-' or '.' must be internal, * and on either side of each '.' we can't have a '-' or '.'. * * If the name has just one label, we don't consider it a DNS name. */ for (i = 0; i < length && label_length < MAX_LABEL_LENGTH; ++i) { char c = host[i]; if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_') { label_length += 1; all_numeric = 0; continue; } if (c >= '0' && c <= '9') { label_length += 1; continue; } /* Dot and hyphen cannot be first or last. */ if (i > 0 && i < length - 1) { if (c == '-') { label_length += 1; continue; } /* * Next to a dot the preceding and following characters must not be * another dot or a hyphen. Otherwise, record that the name is * plausible, since it has two or more labels. */ if (c == '.' && host[i + 1] != '.' && host[i - 1] != '-' && host[i + 1] != '-') { label_length = 0; isdnsname = 1; continue; } } isdnsname = 0; break; } /* dNS name must not be all numeric and labels must be shorter than 64 characters. */ isdnsname &= !all_numeric && !(label_length == MAX_LABEL_LENGTH); return isdnsname; } static void user_data_init(struct user_data_st *user_data, SSL *con, char *buf, size_t bufmax, int mode) { user_data->con = con; user_data->buf = buf; user_data->bufmax = bufmax; user_data->buflen = 0; user_data->bufoff = 0; user_data->mode = mode; user_data->isfin = 0; } static int user_data_add(struct user_data_st *user_data, size_t i) { if (user_data->buflen != 0 || i > user_data->bufmax) return 0; user_data->buflen = i; user_data->bufoff = 0; return 1; } #define USER_COMMAND_HELP 0 #define USER_COMMAND_QUIT 1 #define USER_COMMAND_RECONNECT 2 #define USER_COMMAND_RENEGOTIATE 3 #define USER_COMMAND_KEY_UPDATE 4 #define USER_COMMAND_FIN 5 static int user_data_execute(struct user_data_st *user_data, int cmd, char *arg) { switch (cmd) { case USER_COMMAND_HELP: /* This only ever occurs in advanced mode, so just emit advanced help */ BIO_printf(bio_err, "Enter text to send to the peer followed by \n"); BIO_printf(bio_err, "To issue a command insert {cmd} or {cmd:arg} anywhere in the text\n"); BIO_printf(bio_err, "Entering {{ will send { to the peer\n"); BIO_printf(bio_err, "The following commands are available\n"); BIO_printf(bio_err, " {help}: Get this help text\n"); BIO_printf(bio_err, " {quit}: Close the connection to the peer\n"); BIO_printf(bio_err, " {reconnect}: Reconnect to the peer\n"); if (SSL_is_quic(user_data->con)) { BIO_printf(bio_err, " {fin}: Send FIN on the stream. No further writing is possible\n"); } else if(SSL_version(user_data->con) == TLS1_3_VERSION) { BIO_printf(bio_err, " {keyup:req|noreq}: Send a Key Update message\n"); BIO_printf(bio_err, " Arguments:\n"); BIO_printf(bio_err, " req = peer update requested (default)\n"); BIO_printf(bio_err, " noreq = peer update not requested\n"); } else { BIO_printf(bio_err, " {reneg}: Attempt to renegotiate\n"); } BIO_printf(bio_err, "\n"); return USER_DATA_PROCESS_NO_DATA; case USER_COMMAND_QUIT: BIO_printf(bio_err, "DONE\n"); return USER_DATA_PROCESS_SHUT; case USER_COMMAND_RECONNECT: BIO_printf(bio_err, "RECONNECTING\n"); do_ssl_shutdown(user_data->con); SSL_set_connect_state(user_data->con); BIO_closesocket(SSL_get_fd(user_data->con)); return USER_DATA_PROCESS_RESTART; case USER_COMMAND_RENEGOTIATE: BIO_printf(bio_err, "RENEGOTIATING\n"); if (!SSL_renegotiate(user_data->con)) break; return USER_DATA_PROCESS_CONTINUE; case USER_COMMAND_KEY_UPDATE: { int updatetype; if (OPENSSL_strcasecmp(arg, "req") == 0) updatetype = SSL_KEY_UPDATE_REQUESTED; else if (OPENSSL_strcasecmp(arg, "noreq") == 0) updatetype = SSL_KEY_UPDATE_NOT_REQUESTED; else return USER_DATA_PROCESS_BAD_ARGUMENT; BIO_printf(bio_err, "KEYUPDATE\n"); if (!SSL_key_update(user_data->con, updatetype)) break; return USER_DATA_PROCESS_CONTINUE; } case USER_COMMAND_FIN: if (!SSL_stream_conclude(user_data->con, 0)) break; user_data->isfin = 1; return USER_DATA_PROCESS_NO_DATA; default: break; } BIO_printf(bio_err, "ERROR\n"); ERR_print_errors(bio_err); return USER_DATA_PROCESS_SHUT; } static int user_data_process(struct user_data_st *user_data, size_t *len, size_t *off) { char *buf_start = user_data->buf + user_data->bufoff; size_t outlen = user_data->buflen; if (user_data->buflen == 0) { *len = 0; *off = 0; return USER_DATA_PROCESS_NO_DATA; } if (user_data->mode == USER_DATA_MODE_BASIC) { switch (buf_start[0]) { case 'Q': user_data->buflen = user_data->bufoff = *len = *off = 0; return user_data_execute(user_data, USER_COMMAND_QUIT, NULL); case 'C': user_data->buflen = user_data->bufoff = *len = *off = 0; return user_data_execute(user_data, USER_COMMAND_RECONNECT, NULL); case 'R': user_data->buflen = user_data->bufoff = *len = *off = 0; return user_data_execute(user_data, USER_COMMAND_RENEGOTIATE, NULL); case 'K': case 'k': user_data->buflen = user_data->bufoff = *len = *off = 0; return user_data_execute(user_data, USER_COMMAND_KEY_UPDATE, buf_start[0] == 'K' ? "req" : "noreq"); default: break; } } else if (user_data->mode == USER_DATA_MODE_ADVANCED) { char *cmd_start = buf_start; cmd_start[outlen] = '\0'; for (;;) { cmd_start = strchr(cmd_start, '{'); if (cmd_start == buf_start && *(cmd_start + 1) == '{') { /* The "{" is escaped, so skip it */ cmd_start += 2; buf_start++; user_data->bufoff++; user_data->buflen--; outlen--; continue; } break; } if (cmd_start == buf_start) { /* Command detected */ char *cmd_end = strchr(cmd_start, '}'); char *arg_start; int cmd = -1, ret = USER_DATA_PROCESS_NO_DATA; size_t oldoff; if (cmd_end == NULL) { /* Malformed command */ cmd_start[outlen - 1] = '\0'; BIO_printf(bio_err, "ERROR PROCESSING COMMAND. REST OF LINE IGNORED: %s\n", cmd_start); user_data->buflen = user_data->bufoff = *len = *off = 0; return USER_DATA_PROCESS_NO_DATA; } *cmd_end = '\0'; arg_start = strchr(cmd_start, ':'); if (arg_start != NULL) { *arg_start = '\0'; arg_start++; } /* Skip over the { */ cmd_start++; /* * Now we have cmd_start pointing to a NUL terminated string for * the command, and arg_start either being NULL or pointing to a * NUL terminated string for the argument. */ if (OPENSSL_strcasecmp(cmd_start, "help") == 0) { cmd = USER_COMMAND_HELP; } else if (OPENSSL_strcasecmp(cmd_start, "quit") == 0) { cmd = USER_COMMAND_QUIT; } else if (OPENSSL_strcasecmp(cmd_start, "reconnect") == 0) { cmd = USER_COMMAND_RECONNECT; } else if(SSL_is_quic(user_data->con)) { if (OPENSSL_strcasecmp(cmd_start, "fin") == 0) cmd = USER_COMMAND_FIN; } if (SSL_version(user_data->con) == TLS1_3_VERSION) { if (OPENSSL_strcasecmp(cmd_start, "keyup") == 0) { cmd = USER_COMMAND_KEY_UPDATE; if (arg_start == NULL) arg_start = "req"; } } else { /* (D)TLSv1.2 or below */ if (OPENSSL_strcasecmp(cmd_start, "reneg") == 0) cmd = USER_COMMAND_RENEGOTIATE; } if (cmd == -1) { BIO_printf(bio_err, "UNRECOGNISED COMMAND (IGNORED): %s\n", cmd_start); } else { ret = user_data_execute(user_data, cmd, arg_start); if (ret == USER_DATA_PROCESS_BAD_ARGUMENT) { BIO_printf(bio_err, "BAD ARGUMENT (COMMAND IGNORED): %s\n", arg_start); ret = USER_DATA_PROCESS_NO_DATA; } } oldoff = user_data->bufoff; user_data->bufoff = (cmd_end - user_data->buf) + 1; user_data->buflen -= user_data->bufoff - oldoff; if (user_data->buf + 1 == cmd_start && user_data->buflen == 1 && user_data->buf[user_data->bufoff] == '\n') { /* * This command was the only thing on the whole line. We * suppress the final `\n` */ user_data->bufoff = 0; user_data->buflen = 0; } *len = *off = 0; return ret; } else if (cmd_start != NULL) { /* * There is a command on this line, but its not at the start. Output * the start of the line, and we'll process the command next time * we call this function */ outlen = cmd_start - buf_start; } } if (user_data->isfin) { user_data->buflen = user_data->bufoff = *len = *off = 0; return USER_DATA_PROCESS_NO_DATA; } #ifdef CHARSET_EBCDIC ebcdic2ascii(buf_start, buf_start, outlen); #endif *len = outlen; *off = user_data->bufoff; user_data->buflen -= outlen; if (user_data->buflen == 0) user_data->bufoff = 0; else user_data->bufoff += outlen; return USER_DATA_PROCESS_CONTINUE; } static int user_data_has_data(struct user_data_st *user_data) { return user_data->buflen > 0; } #endif /* OPENSSL_NO_SOCK */ diff --git a/crypto/openssl/crypto/asn1/a_strex.c b/crypto/openssl/crypto/asn1/a_strex.c index 4b031a73add5..cfe2644f572c 100644 --- a/crypto/openssl/crypto/asn1/a_strex.c +++ b/crypto/openssl/crypto/asn1/a_strex.c @@ -1,628 +1,630 @@ /* * Copyright 2000-2024 The OpenSSL Project Authors. All Rights Reserved. * * Licensed under the Apache License 2.0 (the "License"). You may not use * this file except in compliance with the License. You can obtain a copy * in the file LICENSE in the source distribution or at * https://www.openssl.org/source/license.html */ #include #include #include "internal/cryptlib.h" #include "internal/sizes.h" #include "crypto/asn1.h" #include #include #include #include "charmap.h" /* * ASN1_STRING_print_ex() and X509_NAME_print_ex(). Enhanced string and name * printing routines handling multibyte characters, RFC2253 and a host of * other options. */ #define CHARTYPE_BS_ESC (ASN1_STRFLGS_ESC_2253 | CHARTYPE_FIRST_ESC_2253 | CHARTYPE_LAST_ESC_2253) #define ESC_FLAGS (ASN1_STRFLGS_ESC_2253 | \ ASN1_STRFLGS_ESC_2254 | \ ASN1_STRFLGS_ESC_QUOTE | \ ASN1_STRFLGS_ESC_CTRL | \ ASN1_STRFLGS_ESC_MSB) /* * Three IO functions for sending data to memory, a BIO and a FILE * pointer. */ static int send_bio_chars(void *arg, const void *buf, int len) { if (!arg) return 1; if (BIO_write(arg, buf, len) != len) return 0; return 1; } #ifndef OPENSSL_NO_STDIO static int send_fp_chars(void *arg, const void *buf, int len) { if (!arg) return 1; if (fwrite(buf, 1, len, arg) != (unsigned int)len) return 0; return 1; } #endif typedef int char_io (void *arg, const void *buf, int len); /* * This function handles display of strings, one character at a time. It is * passed an unsigned long for each character because it could come from 2 or * even 4 byte forms. */ static int do_esc_char(unsigned long c, unsigned short flags, char *do_quotes, char_io *io_ch, void *arg) { unsigned short chflgs; unsigned char chtmp; char tmphex[HEX_SIZE(long) + 3]; if (c > 0xffffffffL) return -1; if (c > 0xffff) { BIO_snprintf(tmphex, sizeof(tmphex), "\\W%08lX", c); if (!io_ch(arg, tmphex, 10)) return -1; return 10; } if (c > 0xff) { BIO_snprintf(tmphex, sizeof(tmphex), "\\U%04lX", c); if (!io_ch(arg, tmphex, 6)) return -1; return 6; } chtmp = (unsigned char)c; if (chtmp > 0x7f) chflgs = flags & ASN1_STRFLGS_ESC_MSB; else chflgs = char_type[chtmp] & flags; if (chflgs & CHARTYPE_BS_ESC) { /* If we don't escape with quotes, signal we need quotes */ if (chflgs & ASN1_STRFLGS_ESC_QUOTE) { if (do_quotes) *do_quotes = 1; if (!io_ch(arg, &chtmp, 1)) return -1; return 1; } if (!io_ch(arg, "\\", 1)) return -1; if (!io_ch(arg, &chtmp, 1)) return -1; return 2; } if (chflgs & (ASN1_STRFLGS_ESC_CTRL | ASN1_STRFLGS_ESC_MSB | ASN1_STRFLGS_ESC_2254)) { BIO_snprintf(tmphex, 11, "\\%02X", chtmp); if (!io_ch(arg, tmphex, 3)) return -1; return 3; } /* * If we get this far and do any escaping at all must escape the escape * character itself: backslash. */ if (chtmp == '\\' && (flags & ESC_FLAGS)) { if (!io_ch(arg, "\\\\", 2)) return -1; return 2; } if (!io_ch(arg, &chtmp, 1)) return -1; return 1; } #define BUF_TYPE_WIDTH_MASK 0x7 #define BUF_TYPE_CONVUTF8 0x8 /* * This function sends each character in a buffer to do_esc_char(). It * interprets the content formats and converts to or from UTF8 as * appropriate. */ static int do_buf(unsigned char *buf, int buflen, int type, unsigned short flags, char *quotes, char_io *io_ch, void *arg) { int i, outlen, len, charwidth; unsigned short orflags; unsigned char *p, *q; unsigned long c; p = buf; q = buf + buflen; outlen = 0; charwidth = type & BUF_TYPE_WIDTH_MASK; switch (charwidth) { case 4: if (buflen & 3) { ERR_raise(ERR_LIB_ASN1, ASN1_R_INVALID_UNIVERSALSTRING_LENGTH); return -1; } break; case 2: if (buflen & 1) { ERR_raise(ERR_LIB_ASN1, ASN1_R_INVALID_BMPSTRING_LENGTH); return -1; } break; default: break; } while (p != q) { if (p == buf && flags & ASN1_STRFLGS_ESC_2253) orflags = CHARTYPE_FIRST_ESC_2253; else orflags = 0; switch (charwidth) { case 4: c = ((unsigned long)*p++) << 24; c |= ((unsigned long)*p++) << 16; c |= ((unsigned long)*p++) << 8; c |= *p++; break; case 2: c = ((unsigned long)*p++) << 8; c |= *p++; break; case 1: c = *p++; break; case 0: i = UTF8_getc(p, buflen, &c); if (i < 0) return -1; /* Invalid UTF8String */ buflen -= i; p += i; break; default: return -1; /* invalid width */ } if (p == q && flags & ASN1_STRFLGS_ESC_2253) orflags = CHARTYPE_LAST_ESC_2253; if (type & BUF_TYPE_CONVUTF8) { unsigned char utfbuf[6]; - int utflen; - utflen = UTF8_putc(utfbuf, sizeof(utfbuf), c); + int utflen = UTF8_putc(utfbuf, sizeof(utfbuf), c); + + if (utflen < 0) + return -1; /* error happened with UTF8 */ for (i = 0; i < utflen; i++) { /* * We don't need to worry about setting orflags correctly * because if utflen==1 its value will be correct anyway * otherwise each character will be > 0x7f and so the * character will never be escaped on first and last. */ len = do_esc_char(utfbuf[i], flags | orflags, quotes, io_ch, arg); if (len < 0) return -1; outlen += len; } } else { len = do_esc_char(c, flags | orflags, quotes, io_ch, arg); if (len < 0) return -1; outlen += len; } } return outlen; } /* This function hex dumps a buffer of characters */ static int do_hex_dump(char_io *io_ch, void *arg, unsigned char *buf, int buflen) { unsigned char *p, *q; char hextmp[2]; if (arg) { p = buf; q = buf + buflen; while (p != q) { ossl_to_hex(hextmp, *p); if (!io_ch(arg, hextmp, 2)) return -1; p++; } } return buflen << 1; } /* * "dump" a string. This is done when the type is unknown, or the flags * request it. We can either dump the content octets or the entire DER * encoding. This uses the RFC2253 #01234 format. */ static int do_dump(unsigned long lflags, char_io *io_ch, void *arg, const ASN1_STRING *str) { /* * Placing the ASN1_STRING in a temp ASN1_TYPE allows the DER encoding to * readily obtained */ ASN1_TYPE t; unsigned char *der_buf, *p; int outlen, der_len; if (!io_ch(arg, "#", 1)) return -1; /* If we don't dump DER encoding just dump content octets */ if (!(lflags & ASN1_STRFLGS_DUMP_DER)) { outlen = do_hex_dump(io_ch, arg, str->data, str->length); if (outlen < 0) return -1; return outlen + 1; } t.type = str->type; t.value.ptr = (char *)str; der_len = i2d_ASN1_TYPE(&t, NULL); if (der_len <= 0) return -1; if ((der_buf = OPENSSL_malloc(der_len)) == NULL) return -1; p = der_buf; i2d_ASN1_TYPE(&t, &p); outlen = do_hex_dump(io_ch, arg, der_buf, der_len); OPENSSL_free(der_buf); if (outlen < 0) return -1; return outlen + 1; } /* * Lookup table to convert tags to character widths, 0 = UTF8 encoded, -1 is * used for non string types otherwise it is the number of bytes per * character */ static const signed char tag2nbyte[] = { -1, -1, -1, -1, -1, /* 0-4 */ -1, -1, -1, -1, -1, /* 5-9 */ -1, -1, /* 10-11 */ 0, /* 12 V_ASN1_UTF8STRING */ -1, -1, -1, -1, -1, /* 13-17 */ 1, /* 18 V_ASN1_NUMERICSTRING */ 1, /* 19 V_ASN1_PRINTABLESTRING */ 1, /* 20 V_ASN1_T61STRING */ -1, /* 21 */ 1, /* 22 V_ASN1_IA5STRING */ 1, /* 23 V_ASN1_UTCTIME */ 1, /* 24 V_ASN1_GENERALIZEDTIME */ -1, /* 25 */ 1, /* 26 V_ASN1_ISO64STRING */ -1, /* 27 */ 4, /* 28 V_ASN1_UNIVERSALSTRING */ -1, /* 29 */ 2 /* 30 V_ASN1_BMPSTRING */ }; /* * This is the main function, print out an ASN1_STRING taking note of various * escape and display options. Returns number of characters written or -1 if * an error occurred. */ static int do_print_ex(char_io *io_ch, void *arg, unsigned long lflags, const ASN1_STRING *str) { int outlen, len; int type; char quotes; unsigned short flags; quotes = 0; /* Keep a copy of escape flags */ flags = (unsigned short)(lflags & ESC_FLAGS); type = str->type; outlen = 0; if (lflags & ASN1_STRFLGS_SHOW_TYPE) { const char *tagname; tagname = ASN1_tag2str(type); /* We can directly cast here as tagname will never be too large. */ outlen += (int)strlen(tagname); if (!io_ch(arg, tagname, outlen) || !io_ch(arg, ":", 1)) return -1; outlen++; } /* Decide what to do with type, either dump content or display it */ /* Dump everything */ if (lflags & ASN1_STRFLGS_DUMP_ALL) type = -1; /* Ignore the string type */ else if (lflags & ASN1_STRFLGS_IGNORE_TYPE) type = 1; else { /* Else determine width based on type */ if ((type > 0) && (type < 31)) type = tag2nbyte[type]; else type = -1; if ((type == -1) && !(lflags & ASN1_STRFLGS_DUMP_UNKNOWN)) type = 1; } if (type == -1) { len = do_dump(lflags, io_ch, arg, str); if (len < 0 || len > INT_MAX - outlen) return -1; outlen += len; return outlen; } if (lflags & ASN1_STRFLGS_UTF8_CONVERT) { /* * Note: if string is UTF8 and we want to convert to UTF8 then we * just interpret it as 1 byte per character to avoid converting * twice. */ if (!type) type = 1; else type |= BUF_TYPE_CONVUTF8; } len = do_buf(str->data, str->length, type, flags, "es, io_ch, NULL); if (len < 0 || len > INT_MAX - 2 - outlen) return -1; outlen += len; if (quotes) outlen += 2; if (!arg) return outlen; if (quotes && !io_ch(arg, "\"", 1)) return -1; if (do_buf(str->data, str->length, type, flags, NULL, io_ch, arg) < 0) return -1; if (quotes && !io_ch(arg, "\"", 1)) return -1; return outlen; } /* Used for line indenting: print 'indent' spaces */ static int do_indent(char_io *io_ch, void *arg, int indent) { int i; for (i = 0; i < indent; i++) if (!io_ch(arg, " ", 1)) return 0; return 1; } #define FN_WIDTH_LN 25 #define FN_WIDTH_SN 10 static int do_name_ex(char_io *io_ch, void *arg, const X509_NAME *n, int indent, unsigned long flags) { int i, prev = -1, orflags, cnt; int fn_opt, fn_nid; ASN1_OBJECT *fn; const ASN1_STRING *val; const X509_NAME_ENTRY *ent; char objtmp[80]; const char *objbuf; int outlen, len; char *sep_dn, *sep_mv, *sep_eq; int sep_dn_len, sep_mv_len, sep_eq_len; if (indent < 0) indent = 0; outlen = indent; if (!do_indent(io_ch, arg, indent)) return -1; switch (flags & XN_FLAG_SEP_MASK) { case XN_FLAG_SEP_MULTILINE: sep_dn = "\n"; sep_dn_len = 1; sep_mv = " + "; sep_mv_len = 3; break; case XN_FLAG_SEP_COMMA_PLUS: sep_dn = ","; sep_dn_len = 1; sep_mv = "+"; sep_mv_len = 1; indent = 0; break; case XN_FLAG_SEP_CPLUS_SPC: sep_dn = ", "; sep_dn_len = 2; sep_mv = " + "; sep_mv_len = 3; indent = 0; break; case XN_FLAG_SEP_SPLUS_SPC: sep_dn = "; "; sep_dn_len = 2; sep_mv = " + "; sep_mv_len = 3; indent = 0; break; default: return -1; } if (flags & XN_FLAG_SPC_EQ) { sep_eq = " = "; sep_eq_len = 3; } else { sep_eq = "="; sep_eq_len = 1; } fn_opt = flags & XN_FLAG_FN_MASK; cnt = X509_NAME_entry_count(n); for (i = 0; i < cnt; i++) { if (flags & XN_FLAG_DN_REV) ent = X509_NAME_get_entry(n, cnt - i - 1); else ent = X509_NAME_get_entry(n, i); if (prev != -1) { if (prev == X509_NAME_ENTRY_set(ent)) { if (!io_ch(arg, sep_mv, sep_mv_len)) return -1; outlen += sep_mv_len; } else { if (!io_ch(arg, sep_dn, sep_dn_len)) return -1; outlen += sep_dn_len; if (!do_indent(io_ch, arg, indent)) return -1; outlen += indent; } } prev = X509_NAME_ENTRY_set(ent); fn = X509_NAME_ENTRY_get_object(ent); val = X509_NAME_ENTRY_get_data(ent); fn_nid = OBJ_obj2nid(fn); if (fn_opt != XN_FLAG_FN_NONE) { int objlen, fld_len; if ((fn_opt == XN_FLAG_FN_OID) || (fn_nid == NID_undef)) { OBJ_obj2txt(objtmp, sizeof(objtmp), fn, 1); fld_len = 0; /* XXX: what should this be? */ objbuf = objtmp; } else { if (fn_opt == XN_FLAG_FN_SN) { fld_len = FN_WIDTH_SN; objbuf = OBJ_nid2sn(fn_nid); } else if (fn_opt == XN_FLAG_FN_LN) { fld_len = FN_WIDTH_LN; objbuf = OBJ_nid2ln(fn_nid); } else { fld_len = 0; /* XXX: what should this be? */ objbuf = ""; } } objlen = strlen(objbuf); if (!io_ch(arg, objbuf, objlen)) return -1; if ((objlen < fld_len) && (flags & XN_FLAG_FN_ALIGN)) { if (!do_indent(io_ch, arg, fld_len - objlen)) return -1; outlen += fld_len - objlen; } if (!io_ch(arg, sep_eq, sep_eq_len)) return -1; outlen += objlen + sep_eq_len; } /* * If the field name is unknown then fix up the DER dump flag. We * might want to limit this further so it will DER dump on anything * other than a few 'standard' fields. */ if ((fn_nid == NID_undef) && (flags & XN_FLAG_DUMP_UNKNOWN_FIELDS)) orflags = ASN1_STRFLGS_DUMP_ALL; else orflags = 0; len = do_print_ex(io_ch, arg, flags | orflags, val); if (len < 0) return -1; outlen += len; } return outlen; } /* Wrappers round the main functions */ int X509_NAME_print_ex(BIO *out, const X509_NAME *nm, int indent, unsigned long flags) { if (flags == XN_FLAG_COMPAT) return X509_NAME_print(out, nm, indent); return do_name_ex(send_bio_chars, out, nm, indent, flags); } #ifndef OPENSSL_NO_STDIO int X509_NAME_print_ex_fp(FILE *fp, const X509_NAME *nm, int indent, unsigned long flags) { if (flags == XN_FLAG_COMPAT) { BIO *btmp; int ret; btmp = BIO_new_fp(fp, BIO_NOCLOSE); if (!btmp) return -1; ret = X509_NAME_print(btmp, nm, indent); BIO_free(btmp); return ret; } return do_name_ex(send_fp_chars, fp, nm, indent, flags); } #endif int ASN1_STRING_print_ex(BIO *out, const ASN1_STRING *str, unsigned long flags) { return do_print_ex(send_bio_chars, out, flags, str); } #ifndef OPENSSL_NO_STDIO int ASN1_STRING_print_ex_fp(FILE *fp, const ASN1_STRING *str, unsigned long flags) { return do_print_ex(send_fp_chars, fp, flags, str); } #endif /* * Utility function: convert any string type to UTF8, returns number of bytes * in output string or a negative error code */ int ASN1_STRING_to_UTF8(unsigned char **out, const ASN1_STRING *in) { ASN1_STRING stmp, *str = &stmp; int mbflag, type, ret; if (!in) return -1; type = in->type; if ((type < 0) || (type > 30)) return -1; mbflag = tag2nbyte[type]; if (mbflag == -1) return -1; mbflag |= MBSTRING_FLAG; stmp.data = NULL; stmp.length = 0; stmp.flags = 0; ret = ASN1_mbstring_copy(&str, in->data, in->length, mbflag, B_ASN1_UTF8STRING); if (ret < 0) return ret; *out = stmp.data; return stmp.length; } diff --git a/crypto/openssl/crypto/asn1/evp_asn1.c b/crypto/openssl/crypto/asn1/evp_asn1.c index 13d8ed3893ab..6aca0117bc11 100644 --- a/crypto/openssl/crypto/asn1/evp_asn1.c +++ b/crypto/openssl/crypto/asn1/evp_asn1.c @@ -1,187 +1,207 @@ /* * Copyright 1995-2021 The OpenSSL Project Authors. All Rights Reserved. * * Licensed under the Apache License 2.0 (the "License"). You may not use * this file except in compliance with the License. You can obtain a copy * in the file LICENSE in the source distribution or at * https://www.openssl.org/source/license.html */ #include #include "internal/cryptlib.h" #include #include #include "crypto/asn1.h" int ASN1_TYPE_set_octetstring(ASN1_TYPE *a, unsigned char *data, int len) { ASN1_STRING *os; if ((os = ASN1_OCTET_STRING_new()) == NULL) return 0; if (!ASN1_OCTET_STRING_set(os, data, len)) { ASN1_OCTET_STRING_free(os); return 0; } ASN1_TYPE_set(a, V_ASN1_OCTET_STRING, os); return 1; } /* int max_len: for returned value * if passing NULL in data, nothing is copied but the necessary length * for it is returned. */ int ASN1_TYPE_get_octetstring(const ASN1_TYPE *a, unsigned char *data, int max_len) { int ret, num; const unsigned char *p; if ((a->type != V_ASN1_OCTET_STRING) || (a->value.octet_string == NULL)) { ERR_raise(ERR_LIB_ASN1, ASN1_R_DATA_IS_WRONG); return -1; } p = ASN1_STRING_get0_data(a->value.octet_string); ret = ASN1_STRING_length(a->value.octet_string); if (ret < max_len) num = ret; else num = max_len; if (num > 0 && data != NULL) memcpy(data, p, num); return ret; } static ossl_inline void asn1_type_init_oct(ASN1_OCTET_STRING *oct, unsigned char *data, int len) { oct->data = data; oct->type = V_ASN1_OCTET_STRING; oct->length = len; oct->flags = 0; } +/* + * This function copies 'anum' to 'num' and the data of 'oct' to 'data'. + * If the length of 'data' > 'max_len', copies only the first 'max_len' + * bytes, but returns the full length of 'oct'; this allows distinguishing + * whether all the data was copied. + */ static int asn1_type_get_int_oct(ASN1_OCTET_STRING *oct, int32_t anum, long *num, unsigned char *data, int max_len) { int ret = ASN1_STRING_length(oct), n; if (num != NULL) *num = anum; if (max_len > ret) n = ret; else n = max_len; if (data != NULL) memcpy(data, ASN1_STRING_get0_data(oct), n); return ret; } typedef struct { int32_t num; ASN1_OCTET_STRING *oct; } asn1_int_oct; ASN1_SEQUENCE(asn1_int_oct) = { ASN1_EMBED(asn1_int_oct, num, INT32), ASN1_SIMPLE(asn1_int_oct, oct, ASN1_OCTET_STRING) } static_ASN1_SEQUENCE_END(asn1_int_oct) DECLARE_ASN1_ITEM(asn1_int_oct) int ASN1_TYPE_set_int_octetstring(ASN1_TYPE *a, long num, unsigned char *data, int len) { asn1_int_oct atmp; ASN1_OCTET_STRING oct; atmp.num = num; atmp.oct = &oct; asn1_type_init_oct(&oct, data, len); if (ASN1_TYPE_pack_sequence(ASN1_ITEM_rptr(asn1_int_oct), &atmp, &a)) return 1; return 0; } +/* + * This function decodes an int-octet sequence and copies the integer to 'num' + * and the data of octet to 'data'. + * If the length of 'data' > 'max_len', copies only the first 'max_len' + * bytes, but returns the full length of 'oct'; this allows distinguishing + * whether all the data was copied. + */ int ASN1_TYPE_get_int_octetstring(const ASN1_TYPE *a, long *num, unsigned char *data, int max_len) { asn1_int_oct *atmp = NULL; int ret = -1; if ((a->type != V_ASN1_SEQUENCE) || (a->value.sequence == NULL)) { goto err; } atmp = ASN1_TYPE_unpack_sequence(ASN1_ITEM_rptr(asn1_int_oct), a); if (atmp == NULL) goto err; ret = asn1_type_get_int_oct(atmp->oct, atmp->num, num, data, max_len); if (ret == -1) { err: ERR_raise(ERR_LIB_ASN1, ASN1_R_DATA_IS_WRONG); } M_ASN1_free_of(atmp, asn1_int_oct); return ret; } typedef struct { ASN1_OCTET_STRING *oct; int32_t num; } asn1_oct_int; /* * Defined in RFC 5084 - * Section 2. "Content-Authenticated Encryption Algorithms" */ ASN1_SEQUENCE(asn1_oct_int) = { ASN1_SIMPLE(asn1_oct_int, oct, ASN1_OCTET_STRING), ASN1_EMBED(asn1_oct_int, num, INT32) } static_ASN1_SEQUENCE_END(asn1_oct_int) DECLARE_ASN1_ITEM(asn1_oct_int) int ossl_asn1_type_set_octetstring_int(ASN1_TYPE *a, long num, unsigned char *data, int len) { asn1_oct_int atmp; ASN1_OCTET_STRING oct; atmp.num = num; atmp.oct = &oct; asn1_type_init_oct(&oct, data, len); if (ASN1_TYPE_pack_sequence(ASN1_ITEM_rptr(asn1_oct_int), &atmp, &a)) return 1; return 0; } +/* + * This function decodes an octet-int sequence and copies the data of octet + * to 'data' and the integer to 'num'. + * If the length of 'data' > 'max_len', copies only the first 'max_len' + * bytes, but returns the full length of 'oct'; this allows distinguishing + * whether all the data was copied. + */ int ossl_asn1_type_get_octetstring_int(const ASN1_TYPE *a, long *num, unsigned char *data, int max_len) { asn1_oct_int *atmp = NULL; int ret = -1; if ((a->type != V_ASN1_SEQUENCE) || (a->value.sequence == NULL)) goto err; atmp = ASN1_TYPE_unpack_sequence(ASN1_ITEM_rptr(asn1_oct_int), a); if (atmp == NULL) goto err; ret = asn1_type_get_int_oct(atmp->oct, atmp->num, num, data, max_len); if (ret == -1) { err: ERR_raise(ERR_LIB_ASN1, ASN1_R_DATA_IS_WRONG); } M_ASN1_free_of(atmp, asn1_oct_int); return ret; } diff --git a/crypto/openssl/crypto/bio/bf_lbuf.c b/crypto/openssl/crypto/bio/bf_lbuf.c index eed3dc4633e0..ce7123171aa3 100644 --- a/crypto/openssl/crypto/bio/bf_lbuf.c +++ b/crypto/openssl/crypto/bio/bf_lbuf.c @@ -1,315 +1,335 @@ /* * Copyright 1995-2023 The OpenSSL Project Authors. All Rights Reserved. * * Licensed under the Apache License 2.0 (the "License"). You may not use * this file except in compliance with the License. You can obtain a copy * in the file LICENSE in the source distribution or at * https://www.openssl.org/source/license.html */ #include #include #include "bio_local.h" #include "internal/cryptlib.h" #include static int linebuffer_write(BIO *h, const char *buf, int num); static int linebuffer_read(BIO *h, char *buf, int size); static int linebuffer_puts(BIO *h, const char *str); static int linebuffer_gets(BIO *h, char *str, int size); static long linebuffer_ctrl(BIO *h, int cmd, long arg1, void *arg2); static int linebuffer_new(BIO *h); static int linebuffer_free(BIO *data); static long linebuffer_callback_ctrl(BIO *h, int cmd, BIO_info_cb *fp); /* A 10k maximum should be enough for most purposes */ #define DEFAULT_LINEBUFFER_SIZE 1024*10 /* #define DEBUG */ static const BIO_METHOD methods_linebuffer = { BIO_TYPE_LINEBUFFER, "linebuffer", bwrite_conv, linebuffer_write, bread_conv, linebuffer_read, linebuffer_puts, linebuffer_gets, linebuffer_ctrl, linebuffer_new, linebuffer_free, linebuffer_callback_ctrl, }; const BIO_METHOD *BIO_f_linebuffer(void) { return &methods_linebuffer; } typedef struct bio_linebuffer_ctx_struct { char *obuf; /* the output char array */ int obuf_size; /* how big is the output buffer */ int obuf_len; /* how many bytes are in it */ } BIO_LINEBUFFER_CTX; static int linebuffer_new(BIO *bi) { BIO_LINEBUFFER_CTX *ctx; if ((ctx = OPENSSL_malloc(sizeof(*ctx))) == NULL) return 0; ctx->obuf = OPENSSL_malloc(DEFAULT_LINEBUFFER_SIZE); if (ctx->obuf == NULL) { OPENSSL_free(ctx); return 0; } ctx->obuf_size = DEFAULT_LINEBUFFER_SIZE; ctx->obuf_len = 0; bi->init = 1; bi->ptr = (char *)ctx; bi->flags = 0; return 1; } static int linebuffer_free(BIO *a) { BIO_LINEBUFFER_CTX *b; if (a == NULL) return 0; b = (BIO_LINEBUFFER_CTX *)a->ptr; OPENSSL_free(b->obuf); OPENSSL_free(a->ptr); a->ptr = NULL; a->init = 0; a->flags = 0; return 1; } static int linebuffer_read(BIO *b, char *out, int outl) { int ret = 0; if (out == NULL) return 0; if (b->next_bio == NULL) return 0; ret = BIO_read(b->next_bio, out, outl); BIO_clear_retry_flags(b); BIO_copy_next_retry(b); return ret; } static int linebuffer_write(BIO *b, const char *in, int inl) { int i, num = 0, foundnl; BIO_LINEBUFFER_CTX *ctx; if ((in == NULL) || (inl <= 0)) return 0; ctx = (BIO_LINEBUFFER_CTX *)b->ptr; if ((ctx == NULL) || (b->next_bio == NULL)) return 0; BIO_clear_retry_flags(b); do { const char *p; char c; for (p = in, c = '\0'; p < in + inl && (c = *p) != '\n'; p++) ; if (c == '\n') { p++; foundnl = 1; } else foundnl = 0; /* * If a NL was found and we already have text in the save buffer, * concatenate them and write */ while ((foundnl || p - in > ctx->obuf_size - ctx->obuf_len) && ctx->obuf_len > 0) { int orig_olen = ctx->obuf_len; i = ctx->obuf_size - ctx->obuf_len; if (p - in > 0) { if (i >= p - in) { memcpy(&(ctx->obuf[ctx->obuf_len]), in, p - in); ctx->obuf_len += p - in; inl -= p - in; num += p - in; in = p; } else { memcpy(&(ctx->obuf[ctx->obuf_len]), in, i); ctx->obuf_len += i; inl -= i; in += i; num += i; } } i = BIO_write(b->next_bio, ctx->obuf, ctx->obuf_len); if (i <= 0) { ctx->obuf_len = orig_olen; BIO_copy_next_retry(b); if (i < 0) return ((num > 0) ? num : i); if (i == 0) return num; } if (i < ctx->obuf_len) memmove(ctx->obuf, ctx->obuf + i, ctx->obuf_len - i); ctx->obuf_len -= i; } /* * Now that the save buffer is emptied, let's write the input buffer * if a NL was found and there is anything to write. */ if ((foundnl || p - in > ctx->obuf_size) && p - in > 0) { i = BIO_write(b->next_bio, in, p - in); if (i <= 0) { BIO_copy_next_retry(b); if (i < 0) return ((num > 0) ? num : i); if (i == 0) return num; } num += i; in += i; inl -= i; } } while (foundnl && inl > 0); /* * We've written as much as we can. The rest of the input buffer, if - * any, is text that doesn't and with a NL and therefore needs to be - * saved for the next trip. + * any, is text that doesn't end with a NL and therefore we need to try + * free up some space in our obuf so we can make forward progress. */ - if (inl > 0) { - memcpy(&(ctx->obuf[ctx->obuf_len]), in, inl); - ctx->obuf_len += inl; - num += inl; + while (inl > 0) { + size_t avail = (size_t)ctx->obuf_size - (size_t)ctx->obuf_len; + size_t to_copy; + + if (avail == 0) { + /* Flush buffered data to make room */ + i = BIO_write(b->next_bio, ctx->obuf, ctx->obuf_len); + if (i <= 0) { + BIO_copy_next_retry(b); + return num > 0 ? num : i; + } + if (i < ctx->obuf_len) + memmove(ctx->obuf, ctx->obuf + i, ctx->obuf_len - i); + ctx->obuf_len -= i; + continue; + } + + to_copy = inl > (int)avail ? avail : (size_t)inl; + memcpy(&(ctx->obuf[ctx->obuf_len]), in, to_copy); + ctx->obuf_len += (int)to_copy; + in += to_copy; + inl -= (int)to_copy; + num += (int)to_copy; } + return num; } static long linebuffer_ctrl(BIO *b, int cmd, long num, void *ptr) { BIO *dbio; BIO_LINEBUFFER_CTX *ctx; long ret = 1; char *p; int r; int obs; ctx = (BIO_LINEBUFFER_CTX *)b->ptr; switch (cmd) { case BIO_CTRL_RESET: ctx->obuf_len = 0; if (b->next_bio == NULL) return 0; ret = BIO_ctrl(b->next_bio, cmd, num, ptr); break; case BIO_CTRL_INFO: ret = (long)ctx->obuf_len; break; case BIO_CTRL_WPENDING: ret = (long)ctx->obuf_len; if (ret == 0) { if (b->next_bio == NULL) return 0; ret = BIO_ctrl(b->next_bio, cmd, num, ptr); } break; case BIO_C_SET_BUFF_SIZE: if (num > INT_MAX) return 0; obs = (int)num; p = ctx->obuf; if ((obs > DEFAULT_LINEBUFFER_SIZE) && (obs != ctx->obuf_size)) { p = OPENSSL_malloc((size_t)obs); if (p == NULL) return 0; } if (ctx->obuf != p) { if (ctx->obuf_len > obs) { ctx->obuf_len = obs; } memcpy(p, ctx->obuf, ctx->obuf_len); OPENSSL_free(ctx->obuf); ctx->obuf = p; ctx->obuf_size = obs; } break; case BIO_C_DO_STATE_MACHINE: if (b->next_bio == NULL) return 0; BIO_clear_retry_flags(b); ret = BIO_ctrl(b->next_bio, cmd, num, ptr); BIO_copy_next_retry(b); break; case BIO_CTRL_FLUSH: if (b->next_bio == NULL) return 0; if (ctx->obuf_len <= 0) { ret = BIO_ctrl(b->next_bio, cmd, num, ptr); BIO_copy_next_retry(b); break; } for (;;) { BIO_clear_retry_flags(b); if (ctx->obuf_len > 0) { r = BIO_write(b->next_bio, ctx->obuf, ctx->obuf_len); BIO_copy_next_retry(b); if (r <= 0) return (long)r; if (r < ctx->obuf_len) memmove(ctx->obuf, ctx->obuf + r, ctx->obuf_len - r); ctx->obuf_len -= r; } else { ctx->obuf_len = 0; break; } } ret = BIO_ctrl(b->next_bio, cmd, num, ptr); BIO_copy_next_retry(b); break; case BIO_CTRL_DUP: dbio = (BIO *)ptr; if (BIO_set_write_buffer_size(dbio, ctx->obuf_size) <= 0) ret = 0; break; default: if (b->next_bio == NULL) return 0; ret = BIO_ctrl(b->next_bio, cmd, num, ptr); break; } return ret; } static long linebuffer_callback_ctrl(BIO *b, int cmd, BIO_info_cb *fp) { if (b->next_bio == NULL) return 0; return BIO_callback_ctrl(b->next_bio, cmd, fp); } static int linebuffer_gets(BIO *b, char *buf, int size) { if (b->next_bio == NULL) return 0; return BIO_gets(b->next_bio, buf, size); } static int linebuffer_puts(BIO *b, const char *str) { return linebuffer_write(b, str, strlen(str)); } diff --git a/crypto/openssl/crypto/evp/evp_lib.c b/crypto/openssl/crypto/evp/evp_lib.c index 32ada929e1be..a4aa302a0ffa 100644 --- a/crypto/openssl/crypto/evp/evp_lib.c +++ b/crypto/openssl/crypto/evp/evp_lib.c @@ -1,1493 +1,1492 @@ /* * Copyright 1995-2025 The OpenSSL Project Authors. All Rights Reserved. * * Licensed under the Apache License 2.0 (the "License"). You may not use * this file except in compliance with the License. You can obtain a copy * in the file LICENSE in the source distribution or at * https://www.openssl.org/source/license.html */ /* * EVP _meth_ APIs are deprecated for public use, but still ok for * internal use. */ #include "internal/deprecated.h" #include #include #include "internal/cryptlib.h" #include #include #include #include #include #include #include #include #include "crypto/evp.h" #include "crypto/cryptlib.h" #include "internal/provider.h" #include "evp_local.h" #if !defined(FIPS_MODULE) # include "crypto/asn1.h" int EVP_CIPHER_param_to_asn1(EVP_CIPHER_CTX *c, ASN1_TYPE *type) { return evp_cipher_param_to_asn1_ex(c, type, NULL); } int EVP_CIPHER_asn1_to_param(EVP_CIPHER_CTX *c, ASN1_TYPE *type) { return evp_cipher_asn1_to_param_ex(c, type, NULL); } int EVP_CIPHER_get_asn1_iv(EVP_CIPHER_CTX *ctx, ASN1_TYPE *type) { int i = 0; unsigned int l; if (type != NULL) { unsigned char iv[EVP_MAX_IV_LENGTH]; l = EVP_CIPHER_CTX_get_iv_length(ctx); if (!ossl_assert(l <= sizeof(iv))) return -1; i = ASN1_TYPE_get_octetstring(type, iv, l); if (i != (int)l) return -1; if (!EVP_CipherInit_ex(ctx, NULL, NULL, NULL, iv, -1)) return -1; } return i; } int EVP_CIPHER_set_asn1_iv(EVP_CIPHER_CTX *c, ASN1_TYPE *type) { int i = 0; unsigned int j; unsigned char *oiv = NULL; if (type != NULL) { oiv = (unsigned char *)EVP_CIPHER_CTX_original_iv(c); j = EVP_CIPHER_CTX_get_iv_length(c); OPENSSL_assert(j <= sizeof(c->iv)); i = ASN1_TYPE_set_octetstring(type, oiv, j); } return i; } int evp_cipher_param_to_asn1_ex(EVP_CIPHER_CTX *c, ASN1_TYPE *type, evp_cipher_aead_asn1_params *asn1_params) { int ret = -1; /* Assume the worst */ const EVP_CIPHER *cipher; if (c == NULL || c->cipher == NULL) goto err; cipher = c->cipher; /* * For legacy implementations, we detect custom AlgorithmIdentifier * parameter handling by checking if the function pointer * cipher->set_asn1_parameters is set. We know that this pointer * is NULL for provided implementations. * * Otherwise, for any implementation, we check the flag * EVP_CIPH_FLAG_CUSTOM_ASN1. If it isn't set, we apply * default AI parameter extraction. * * Otherwise, for provided implementations, we convert |type| to * a DER encoded blob and pass to the implementation in OSSL_PARAM * form. * * If none of the above applies, this operation is unsupported. */ if (cipher->set_asn1_parameters != NULL) { ret = cipher->set_asn1_parameters(c, type); } else if ((EVP_CIPHER_get_flags(cipher) & EVP_CIPH_FLAG_CUSTOM_ASN1) == 0) { switch (EVP_CIPHER_get_mode(cipher)) { case EVP_CIPH_WRAP_MODE: if (EVP_CIPHER_is_a(cipher, SN_id_smime_alg_CMS3DESwrap)) ASN1_TYPE_set(type, V_ASN1_NULL, NULL); ret = 1; break; case EVP_CIPH_GCM_MODE: ret = evp_cipher_set_asn1_aead_params(c, type, asn1_params); break; case EVP_CIPH_CCM_MODE: case EVP_CIPH_XTS_MODE: case EVP_CIPH_OCB_MODE: ret = -2; break; default: ret = EVP_CIPHER_set_asn1_iv(c, type); } } else if (cipher->prov != NULL) { /* We cheat, there's no need for an object ID for this use */ X509_ALGOR alg; alg.algorithm = NULL; alg.parameter = type; ret = EVP_CIPHER_CTX_get_algor_params(c, &alg); } else { ret = -2; } err: if (ret == -2) ERR_raise(ERR_LIB_EVP, EVP_R_UNSUPPORTED_CIPHER); else if (ret <= 0) ERR_raise(ERR_LIB_EVP, EVP_R_CIPHER_PARAMETER_ERROR); if (ret < -1) ret = -1; return ret; } int evp_cipher_asn1_to_param_ex(EVP_CIPHER_CTX *c, ASN1_TYPE *type, evp_cipher_aead_asn1_params *asn1_params) { int ret = -1; /* Assume the worst */ const EVP_CIPHER *cipher; if (c == NULL || c->cipher == NULL) goto err; cipher = c->cipher; /* * For legacy implementations, we detect custom AlgorithmIdentifier * parameter handling by checking if there the function pointer * cipher->get_asn1_parameters is set. We know that this pointer * is NULL for provided implementations. * * Otherwise, for any implementation, we check the flag * EVP_CIPH_FLAG_CUSTOM_ASN1. If it isn't set, we apply * default AI parameter creation. * * Otherwise, for provided implementations, we get the AI parameter * in DER encoded form from the implementation by requesting the * appropriate OSSL_PARAM and converting the result to a ASN1_TYPE. * * If none of the above applies, this operation is unsupported. */ if (cipher->get_asn1_parameters != NULL) { ret = cipher->get_asn1_parameters(c, type); } else if ((EVP_CIPHER_get_flags(cipher) & EVP_CIPH_FLAG_CUSTOM_ASN1) == 0) { switch (EVP_CIPHER_get_mode(cipher)) { case EVP_CIPH_WRAP_MODE: ret = 1; break; case EVP_CIPH_GCM_MODE: ret = evp_cipher_get_asn1_aead_params(c, type, asn1_params); break; case EVP_CIPH_CCM_MODE: case EVP_CIPH_XTS_MODE: case EVP_CIPH_OCB_MODE: ret = -2; break; default: ret = EVP_CIPHER_get_asn1_iv(c, type) >= 0 ? 1 : -1; } } else if (cipher->prov != NULL) { /* We cheat, there's no need for an object ID for this use */ X509_ALGOR alg; alg.algorithm = NULL; alg.parameter = type; ret = EVP_CIPHER_CTX_set_algor_params(c, &alg); } else { ret = -2; } err: if (ret == -2) ERR_raise(ERR_LIB_EVP, EVP_R_UNSUPPORTED_CIPHER); else if (ret <= 0) ERR_raise(ERR_LIB_EVP, EVP_R_CIPHER_PARAMETER_ERROR); if (ret < -1) ret = -1; return ret; } int evp_cipher_get_asn1_aead_params(EVP_CIPHER_CTX *c, ASN1_TYPE *type, evp_cipher_aead_asn1_params *asn1_params) { int i = 0; long tl; unsigned char iv[EVP_MAX_IV_LENGTH]; if (type == NULL || asn1_params == NULL) return 0; - i = ossl_asn1_type_get_octetstring_int(type, &tl, NULL, EVP_MAX_IV_LENGTH); - if (i <= 0) + i = ossl_asn1_type_get_octetstring_int(type, &tl, iv, EVP_MAX_IV_LENGTH); + if (i <= 0 || i > EVP_MAX_IV_LENGTH) return -1; - ossl_asn1_type_get_octetstring_int(type, &tl, iv, i); memcpy(asn1_params->iv, iv, i); asn1_params->iv_len = i; return i; } int evp_cipher_set_asn1_aead_params(EVP_CIPHER_CTX *c, ASN1_TYPE *type, evp_cipher_aead_asn1_params *asn1_params) { if (type == NULL || asn1_params == NULL) return 0; return ossl_asn1_type_set_octetstring_int(type, asn1_params->tag_len, asn1_params->iv, asn1_params->iv_len); } #endif /* !defined(FIPS_MODULE) */ /* Convert the various cipher NIDs and dummies to a proper OID NID */ int EVP_CIPHER_get_type(const EVP_CIPHER *cipher) { int nid; nid = EVP_CIPHER_get_nid(cipher); switch (nid) { case NID_rc2_cbc: case NID_rc2_64_cbc: case NID_rc2_40_cbc: return NID_rc2_cbc; case NID_rc4: case NID_rc4_40: return NID_rc4; case NID_aes_128_cfb128: case NID_aes_128_cfb8: case NID_aes_128_cfb1: return NID_aes_128_cfb128; case NID_aes_192_cfb128: case NID_aes_192_cfb8: case NID_aes_192_cfb1: return NID_aes_192_cfb128; case NID_aes_256_cfb128: case NID_aes_256_cfb8: case NID_aes_256_cfb1: return NID_aes_256_cfb128; case NID_des_cfb64: case NID_des_cfb8: case NID_des_cfb1: return NID_des_cfb64; case NID_des_ede3_cfb64: case NID_des_ede3_cfb8: case NID_des_ede3_cfb1: return NID_des_cfb64; default: #ifdef FIPS_MODULE return NID_undef; #else { /* Check it has an OID and it is valid */ ASN1_OBJECT *otmp = OBJ_nid2obj(nid); if (OBJ_get0_data(otmp) == NULL) nid = NID_undef; ASN1_OBJECT_free(otmp); return nid; } #endif } } int evp_cipher_cache_constants(EVP_CIPHER *cipher) { int ok, aead = 0, custom_iv = 0, cts = 0, multiblock = 0, randkey = 0; size_t ivlen = 0; size_t blksz = 0; size_t keylen = 0; unsigned int mode = 0; OSSL_PARAM params[10]; params[0] = OSSL_PARAM_construct_size_t(OSSL_CIPHER_PARAM_BLOCK_SIZE, &blksz); params[1] = OSSL_PARAM_construct_size_t(OSSL_CIPHER_PARAM_IVLEN, &ivlen); params[2] = OSSL_PARAM_construct_size_t(OSSL_CIPHER_PARAM_KEYLEN, &keylen); params[3] = OSSL_PARAM_construct_uint(OSSL_CIPHER_PARAM_MODE, &mode); params[4] = OSSL_PARAM_construct_int(OSSL_CIPHER_PARAM_AEAD, &aead); params[5] = OSSL_PARAM_construct_int(OSSL_CIPHER_PARAM_CUSTOM_IV, &custom_iv); params[6] = OSSL_PARAM_construct_int(OSSL_CIPHER_PARAM_CTS, &cts); params[7] = OSSL_PARAM_construct_int(OSSL_CIPHER_PARAM_TLS1_MULTIBLOCK, &multiblock); params[8] = OSSL_PARAM_construct_int(OSSL_CIPHER_PARAM_HAS_RAND_KEY, &randkey); params[9] = OSSL_PARAM_construct_end(); ok = evp_do_ciph_getparams(cipher, params) > 0; if (ok) { cipher->block_size = blksz; cipher->iv_len = ivlen; cipher->key_len = keylen; cipher->flags = mode; if (aead) cipher->flags |= EVP_CIPH_FLAG_AEAD_CIPHER; if (custom_iv) cipher->flags |= EVP_CIPH_CUSTOM_IV; if (cts) cipher->flags |= EVP_CIPH_FLAG_CTS; if (multiblock) cipher->flags |= EVP_CIPH_FLAG_TLS1_1_MULTIBLOCK; if (cipher->ccipher != NULL) cipher->flags |= EVP_CIPH_FLAG_CUSTOM_CIPHER; if (randkey) cipher->flags |= EVP_CIPH_RAND_KEY; if (OSSL_PARAM_locate_const(EVP_CIPHER_gettable_ctx_params(cipher), OSSL_CIPHER_PARAM_ALGORITHM_ID_PARAMS)) cipher->flags |= EVP_CIPH_FLAG_CUSTOM_ASN1; } return ok; } int EVP_CIPHER_get_block_size(const EVP_CIPHER *cipher) { return (cipher == NULL) ? 0 : cipher->block_size; } int EVP_CIPHER_CTX_get_block_size(const EVP_CIPHER_CTX *ctx) { return (ctx == NULL) ? 0 : EVP_CIPHER_get_block_size(ctx->cipher); } int EVP_CIPHER_impl_ctx_size(const EVP_CIPHER *e) { return e->ctx_size; } int EVP_Cipher(EVP_CIPHER_CTX *ctx, unsigned char *out, const unsigned char *in, unsigned int inl) { if (ctx == NULL || ctx->cipher == NULL) return 0; if (ctx->cipher->prov != NULL) { /* * If the provided implementation has a ccipher function, we use it, * and translate its return value like this: 0 => -1, 1 => outlen * * Otherwise, we call the cupdate function if in != NULL, or cfinal * if in == NULL. Regardless of which, we return what we got. */ int ret = -1; size_t outl = 0; size_t blocksize = EVP_CIPHER_CTX_get_block_size(ctx); if (blocksize == 0) return 0; if (ctx->cipher->ccipher != NULL) ret = ctx->cipher->ccipher(ctx->algctx, out, &outl, inl + (blocksize == 1 ? 0 : blocksize), in, (size_t)inl) ? (int)outl : -1; else if (in != NULL) ret = ctx->cipher->cupdate(ctx->algctx, out, &outl, inl + (blocksize == 1 ? 0 : blocksize), in, (size_t)inl); else ret = ctx->cipher->cfinal(ctx->algctx, out, &outl, blocksize == 1 ? 0 : blocksize); return ret; } return ctx->cipher->do_cipher(ctx, out, in, inl); } #ifndef OPENSSL_NO_DEPRECATED_3_0 const EVP_CIPHER *EVP_CIPHER_CTX_cipher(const EVP_CIPHER_CTX *ctx) { if (ctx == NULL) return NULL; return ctx->cipher; } #endif const EVP_CIPHER *EVP_CIPHER_CTX_get0_cipher(const EVP_CIPHER_CTX *ctx) { if (ctx == NULL) return NULL; return ctx->cipher; } EVP_CIPHER *EVP_CIPHER_CTX_get1_cipher(EVP_CIPHER_CTX *ctx) { EVP_CIPHER *cipher; if (ctx == NULL || ctx->cipher == NULL) return NULL; cipher = (EVP_CIPHER *)ctx->cipher; if (!EVP_CIPHER_up_ref(cipher)) return NULL; return cipher; } int EVP_CIPHER_CTX_is_encrypting(const EVP_CIPHER_CTX *ctx) { return ctx->encrypt; } unsigned long EVP_CIPHER_get_flags(const EVP_CIPHER *cipher) { return cipher == NULL ? 0 : cipher->flags; } void *EVP_CIPHER_CTX_get_app_data(const EVP_CIPHER_CTX *ctx) { return ctx->app_data; } void EVP_CIPHER_CTX_set_app_data(EVP_CIPHER_CTX *ctx, void *data) { ctx->app_data = data; } void *EVP_CIPHER_CTX_get_cipher_data(const EVP_CIPHER_CTX *ctx) { return ctx->cipher_data; } void *EVP_CIPHER_CTX_set_cipher_data(EVP_CIPHER_CTX *ctx, void *cipher_data) { void *old_cipher_data; old_cipher_data = ctx->cipher_data; ctx->cipher_data = cipher_data; return old_cipher_data; } int EVP_CIPHER_get_iv_length(const EVP_CIPHER *cipher) { return (cipher == NULL) ? 0 : cipher->iv_len; } int EVP_CIPHER_CTX_get_iv_length(const EVP_CIPHER_CTX *ctx) { if (ctx->cipher == NULL) return 0; if (ctx->iv_len < 0) { int rv, len = EVP_CIPHER_get_iv_length(ctx->cipher); size_t v = len; OSSL_PARAM params[2] = { OSSL_PARAM_END, OSSL_PARAM_END }; if (ctx->cipher->get_ctx_params != NULL) { params[0] = OSSL_PARAM_construct_size_t(OSSL_CIPHER_PARAM_IVLEN, &v); rv = evp_do_ciph_ctx_getparams(ctx->cipher, ctx->algctx, params); if (rv > 0) { if (OSSL_PARAM_modified(params) && !OSSL_PARAM_get_int(params, &len)) return -1; } else if (rv != EVP_CTRL_RET_UNSUPPORTED) { return -1; } } /* Code below to be removed when legacy support is dropped. */ else if ((EVP_CIPHER_get_flags(ctx->cipher) & EVP_CIPH_CUSTOM_IV_LENGTH) != 0) { rv = EVP_CIPHER_CTX_ctrl((EVP_CIPHER_CTX *)ctx, EVP_CTRL_GET_IVLEN, 0, &len); if (rv <= 0) return -1; } /*- * Casting away the const is annoying but required here. We need to * cache the result for performance reasons. */ ((EVP_CIPHER_CTX *)ctx)->iv_len = len; } return ctx->iv_len; } int EVP_CIPHER_CTX_get_tag_length(const EVP_CIPHER_CTX *ctx) { int ret; size_t v = 0; OSSL_PARAM params[2] = { OSSL_PARAM_END, OSSL_PARAM_END }; params[0] = OSSL_PARAM_construct_size_t(OSSL_CIPHER_PARAM_AEAD_TAGLEN, &v); ret = evp_do_ciph_ctx_getparams(ctx->cipher, ctx->algctx, params); return ret == 1 ? (int)v : 0; } #ifndef OPENSSL_NO_DEPRECATED_3_0 const unsigned char *EVP_CIPHER_CTX_original_iv(const EVP_CIPHER_CTX *ctx) { int ok; const unsigned char *v = ctx->oiv; OSSL_PARAM params[2] = { OSSL_PARAM_END, OSSL_PARAM_END }; params[0] = OSSL_PARAM_construct_octet_ptr(OSSL_CIPHER_PARAM_IV, (void **)&v, sizeof(ctx->oiv)); ok = evp_do_ciph_ctx_getparams(ctx->cipher, ctx->algctx, params); return ok != 0 ? v : NULL; } /* * OSSL_PARAM_OCTET_PTR gets us the pointer to the running IV in the provider */ const unsigned char *EVP_CIPHER_CTX_iv(const EVP_CIPHER_CTX *ctx) { int ok; const unsigned char *v = ctx->iv; OSSL_PARAM params[2] = { OSSL_PARAM_END, OSSL_PARAM_END }; params[0] = OSSL_PARAM_construct_octet_ptr(OSSL_CIPHER_PARAM_UPDATED_IV, (void **)&v, sizeof(ctx->iv)); ok = evp_do_ciph_ctx_getparams(ctx->cipher, ctx->algctx, params); return ok != 0 ? v : NULL; } unsigned char *EVP_CIPHER_CTX_iv_noconst(EVP_CIPHER_CTX *ctx) { int ok; unsigned char *v = ctx->iv; OSSL_PARAM params[2] = { OSSL_PARAM_END, OSSL_PARAM_END }; params[0] = OSSL_PARAM_construct_octet_ptr(OSSL_CIPHER_PARAM_UPDATED_IV, (void **)&v, sizeof(ctx->iv)); ok = evp_do_ciph_ctx_getparams(ctx->cipher, ctx->algctx, params); return ok != 0 ? v : NULL; } #endif /* OPENSSL_NO_DEPRECATED_3_0_0 */ int EVP_CIPHER_CTX_get_updated_iv(EVP_CIPHER_CTX *ctx, void *buf, size_t len) { OSSL_PARAM params[2] = { OSSL_PARAM_END, OSSL_PARAM_END }; params[0] = OSSL_PARAM_construct_octet_string(OSSL_CIPHER_PARAM_UPDATED_IV, buf, len); return evp_do_ciph_ctx_getparams(ctx->cipher, ctx->algctx, params) > 0; } int EVP_CIPHER_CTX_get_original_iv(EVP_CIPHER_CTX *ctx, void *buf, size_t len) { OSSL_PARAM params[2] = { OSSL_PARAM_END, OSSL_PARAM_END }; params[0] = OSSL_PARAM_construct_octet_string(OSSL_CIPHER_PARAM_IV, buf, len); return evp_do_ciph_ctx_getparams(ctx->cipher, ctx->algctx, params) > 0; } unsigned char *EVP_CIPHER_CTX_buf_noconst(EVP_CIPHER_CTX *ctx) { return ctx->buf; } int EVP_CIPHER_CTX_get_num(const EVP_CIPHER_CTX *ctx) { int ok; unsigned int v = (unsigned int)ctx->num; OSSL_PARAM params[2] = { OSSL_PARAM_END, OSSL_PARAM_END }; params[0] = OSSL_PARAM_construct_uint(OSSL_CIPHER_PARAM_NUM, &v); ok = evp_do_ciph_ctx_getparams(ctx->cipher, ctx->algctx, params); return ok != 0 ? (int)v : EVP_CTRL_RET_UNSUPPORTED; } int EVP_CIPHER_CTX_set_num(EVP_CIPHER_CTX *ctx, int num) { int ok; unsigned int n = (unsigned int)num; OSSL_PARAM params[2] = { OSSL_PARAM_END, OSSL_PARAM_END }; params[0] = OSSL_PARAM_construct_uint(OSSL_CIPHER_PARAM_NUM, &n); ok = evp_do_ciph_ctx_setparams(ctx->cipher, ctx->algctx, params); if (ok != 0) ctx->num = (int)n; return ok != 0; } int EVP_CIPHER_get_key_length(const EVP_CIPHER *cipher) { return cipher->key_len; } int EVP_CIPHER_CTX_get_key_length(const EVP_CIPHER_CTX *ctx) { if (ctx->cipher == NULL) return 0; if (ctx->key_len <= 0 && ctx->cipher->prov != NULL) { int ok; OSSL_PARAM params[2] = { OSSL_PARAM_END, OSSL_PARAM_END }; size_t len; params[0] = OSSL_PARAM_construct_size_t(OSSL_CIPHER_PARAM_KEYLEN, &len); ok = evp_do_ciph_ctx_getparams(ctx->cipher, ctx->algctx, params); if (ok <= 0) return EVP_CTRL_RET_UNSUPPORTED; /*- * The if branch should never be taken since EVP_MAX_KEY_LENGTH is * less than INT_MAX but best to be safe. * * Casting away the const is annoying but required here. We need to * cache the result for performance reasons. */ if (!OSSL_PARAM_get_int(params, &((EVP_CIPHER_CTX *)ctx)->key_len)) return -1; ((EVP_CIPHER_CTX *)ctx)->key_len = (int)len; } return ctx->key_len; } int EVP_CIPHER_get_nid(const EVP_CIPHER *cipher) { return (cipher == NULL) ? NID_undef : cipher->nid; } int EVP_CIPHER_CTX_get_nid(const EVP_CIPHER_CTX *ctx) { return EVP_CIPHER_get_nid(ctx->cipher); } int EVP_CIPHER_is_a(const EVP_CIPHER *cipher, const char *name) { if (cipher == NULL) return 0; if (cipher->prov != NULL) return evp_is_a(cipher->prov, cipher->name_id, NULL, name); return evp_is_a(NULL, 0, EVP_CIPHER_get0_name(cipher), name); } int evp_cipher_get_number(const EVP_CIPHER *cipher) { return cipher->name_id; } const char *EVP_CIPHER_get0_name(const EVP_CIPHER *cipher) { if (cipher->type_name != NULL) return cipher->type_name; #ifndef FIPS_MODULE return OBJ_nid2sn(EVP_CIPHER_get_nid(cipher)); #else return NULL; #endif } const char *EVP_CIPHER_get0_description(const EVP_CIPHER *cipher) { if (cipher->description != NULL) return cipher->description; #ifndef FIPS_MODULE return OBJ_nid2ln(EVP_CIPHER_get_nid(cipher)); #else return NULL; #endif } int EVP_CIPHER_names_do_all(const EVP_CIPHER *cipher, void (*fn)(const char *name, void *data), void *data) { if (cipher->prov != NULL) return evp_names_do_all(cipher->prov, cipher->name_id, fn, data); return 1; } const OSSL_PROVIDER *EVP_CIPHER_get0_provider(const EVP_CIPHER *cipher) { return cipher->prov; } int EVP_CIPHER_get_mode(const EVP_CIPHER *cipher) { return EVP_CIPHER_get_flags(cipher) & EVP_CIPH_MODE; } int EVP_MD_is_a(const EVP_MD *md, const char *name) { if (md == NULL) return 0; if (md->prov != NULL) return evp_is_a(md->prov, md->name_id, NULL, name); return evp_is_a(NULL, 0, EVP_MD_get0_name(md), name); } int evp_md_get_number(const EVP_MD *md) { return md->name_id; } const char *EVP_MD_get0_description(const EVP_MD *md) { if (md->description != NULL) return md->description; #ifndef FIPS_MODULE return OBJ_nid2ln(EVP_MD_nid(md)); #else return NULL; #endif } const char *EVP_MD_get0_name(const EVP_MD *md) { if (md == NULL) return NULL; if (md->type_name != NULL) return md->type_name; #ifndef FIPS_MODULE return OBJ_nid2sn(EVP_MD_nid(md)); #else return NULL; #endif } int EVP_MD_names_do_all(const EVP_MD *md, void (*fn)(const char *name, void *data), void *data) { if (md->prov != NULL) return evp_names_do_all(md->prov, md->name_id, fn, data); return 1; } const OSSL_PROVIDER *EVP_MD_get0_provider(const EVP_MD *md) { return md->prov; } int EVP_MD_get_type(const EVP_MD *md) { return md->type; } int EVP_MD_get_pkey_type(const EVP_MD *md) { return md->pkey_type; } int EVP_MD_get_block_size(const EVP_MD *md) { if (md == NULL) { ERR_raise(ERR_LIB_EVP, EVP_R_MESSAGE_DIGEST_IS_NULL); return -1; } return md->block_size; } int EVP_MD_get_size(const EVP_MD *md) { if (md == NULL) { ERR_raise(ERR_LIB_EVP, EVP_R_MESSAGE_DIGEST_IS_NULL); return -1; } return md->md_size; } int EVP_MD_xof(const EVP_MD *md) { return md != NULL && ((EVP_MD_get_flags(md) & EVP_MD_FLAG_XOF) != 0); } unsigned long EVP_MD_get_flags(const EVP_MD *md) { return md->flags; } EVP_MD *EVP_MD_meth_new(int md_type, int pkey_type) { EVP_MD *md = evp_md_new(); if (md != NULL) { md->type = md_type; md->pkey_type = pkey_type; md->origin = EVP_ORIG_METH; } return md; } EVP_MD *EVP_MD_meth_dup(const EVP_MD *md) { EVP_MD *to = NULL; /* * Non-legacy EVP_MDs can't be duplicated like this. * Use EVP_MD_up_ref() instead. */ if (md->prov != NULL) return NULL; if ((to = EVP_MD_meth_new(md->type, md->pkey_type)) != NULL) { CRYPTO_REF_COUNT refcnt = to->refcnt; memcpy(to, md, sizeof(*to)); to->refcnt = refcnt; to->origin = EVP_ORIG_METH; } return to; } void evp_md_free_int(EVP_MD *md) { OPENSSL_free(md->type_name); ossl_provider_free(md->prov); CRYPTO_FREE_REF(&md->refcnt); OPENSSL_free(md); } void EVP_MD_meth_free(EVP_MD *md) { if (md == NULL || md->origin != EVP_ORIG_METH) return; evp_md_free_int(md); } int EVP_MD_meth_set_input_blocksize(EVP_MD *md, int blocksize) { if (md->block_size != 0) return 0; md->block_size = blocksize; return 1; } int EVP_MD_meth_set_result_size(EVP_MD *md, int resultsize) { if (md->md_size != 0) return 0; md->md_size = resultsize; return 1; } int EVP_MD_meth_set_app_datasize(EVP_MD *md, int datasize) { if (md->ctx_size != 0) return 0; md->ctx_size = datasize; return 1; } int EVP_MD_meth_set_flags(EVP_MD *md, unsigned long flags) { if (md->flags != 0) return 0; md->flags = flags; return 1; } int EVP_MD_meth_set_init(EVP_MD *md, int (*init)(EVP_MD_CTX *ctx)) { if (md->init != NULL) return 0; md->init = init; return 1; } int EVP_MD_meth_set_update(EVP_MD *md, int (*update)(EVP_MD_CTX *ctx, const void *data, size_t count)) { if (md->update != NULL) return 0; md->update = update; return 1; } int EVP_MD_meth_set_final(EVP_MD *md, int (*final)(EVP_MD_CTX *ctx, unsigned char *md)) { if (md->final != NULL) return 0; md->final = final; return 1; } int EVP_MD_meth_set_copy(EVP_MD *md, int (*copy)(EVP_MD_CTX *to, const EVP_MD_CTX *from)) { if (md->copy != NULL) return 0; md->copy = copy; return 1; } int EVP_MD_meth_set_cleanup(EVP_MD *md, int (*cleanup)(EVP_MD_CTX *ctx)) { if (md->cleanup != NULL) return 0; md->cleanup = cleanup; return 1; } int EVP_MD_meth_set_ctrl(EVP_MD *md, int (*ctrl)(EVP_MD_CTX *ctx, int cmd, int p1, void *p2)) { if (md->md_ctrl != NULL) return 0; md->md_ctrl = ctrl; return 1; } int EVP_MD_meth_get_input_blocksize(const EVP_MD *md) { return md->block_size; } int EVP_MD_meth_get_result_size(const EVP_MD *md) { return md->md_size; } int EVP_MD_meth_get_app_datasize(const EVP_MD *md) { return md->ctx_size; } unsigned long EVP_MD_meth_get_flags(const EVP_MD *md) { return md->flags; } int (*EVP_MD_meth_get_init(const EVP_MD *md))(EVP_MD_CTX *ctx) { return md->init; } int (*EVP_MD_meth_get_update(const EVP_MD *md))(EVP_MD_CTX *ctx, const void *data, size_t count) { return md->update; } int (*EVP_MD_meth_get_final(const EVP_MD *md))(EVP_MD_CTX *ctx, unsigned char *md) { return md->final; } int (*EVP_MD_meth_get_copy(const EVP_MD *md))(EVP_MD_CTX *to, const EVP_MD_CTX *from) { return md->copy; } int (*EVP_MD_meth_get_cleanup(const EVP_MD *md))(EVP_MD_CTX *ctx) { return md->cleanup; } int (*EVP_MD_meth_get_ctrl(const EVP_MD *md))(EVP_MD_CTX *ctx, int cmd, int p1, void *p2) { return md->md_ctrl; } #ifndef OPENSSL_NO_DEPRECATED_3_0 const EVP_MD *EVP_MD_CTX_md(const EVP_MD_CTX *ctx) { if (ctx == NULL) return NULL; return ctx->reqdigest; } #endif const EVP_MD *EVP_MD_CTX_get0_md(const EVP_MD_CTX *ctx) { if (ctx == NULL) return NULL; return ctx->reqdigest; } EVP_MD *EVP_MD_CTX_get1_md(EVP_MD_CTX *ctx) { EVP_MD *md; if (ctx == NULL) return NULL; md = (EVP_MD *)ctx->reqdigest; if (md == NULL || !EVP_MD_up_ref(md)) return NULL; return md; } int EVP_MD_CTX_get_size_ex(const EVP_MD_CTX *ctx) { EVP_MD_CTX *c = (EVP_MD_CTX *)ctx; const OSSL_PARAM *gettables; gettables = EVP_MD_CTX_gettable_params(c); if (gettables != NULL && OSSL_PARAM_locate_const(gettables, OSSL_DIGEST_PARAM_SIZE) != NULL) { OSSL_PARAM params[2] = { OSSL_PARAM_END, OSSL_PARAM_END }; size_t sz = 0; /* * For XOF's EVP_MD_get_size() returns 0 * So try to get the xoflen instead. This will return -1 if the * xof length has not been set. */ params[0] = OSSL_PARAM_construct_size_t(OSSL_DIGEST_PARAM_SIZE, &sz); if (EVP_MD_CTX_get_params(c, params) != 1 || sz == SIZE_MAX || sz == 0) return -1; return sz; } /* Normal digests have a constant fixed size output */ return EVP_MD_get_size(EVP_MD_CTX_get0_md(ctx)); } EVP_PKEY_CTX *EVP_MD_CTX_get_pkey_ctx(const EVP_MD_CTX *ctx) { return ctx->pctx; } #if !defined(FIPS_MODULE) void EVP_MD_CTX_set_pkey_ctx(EVP_MD_CTX *ctx, EVP_PKEY_CTX *pctx) { /* * it's reasonable to set NULL pctx (a.k.a clear the ctx->pctx), so * we have to deal with the cleanup job here. */ if (!EVP_MD_CTX_test_flags(ctx, EVP_MD_CTX_FLAG_KEEP_PKEY_CTX)) EVP_PKEY_CTX_free(ctx->pctx); ctx->pctx = pctx; if (pctx != NULL) { /* make sure pctx is not freed when destroying EVP_MD_CTX */ EVP_MD_CTX_set_flags(ctx, EVP_MD_CTX_FLAG_KEEP_PKEY_CTX); } else { EVP_MD_CTX_clear_flags(ctx, EVP_MD_CTX_FLAG_KEEP_PKEY_CTX); } } #endif /* !defined(FIPS_MODULE) */ void *EVP_MD_CTX_get0_md_data(const EVP_MD_CTX *ctx) { return ctx->md_data; } int (*EVP_MD_CTX_update_fn(EVP_MD_CTX *ctx))(EVP_MD_CTX *ctx, const void *data, size_t count) { return ctx->update; } void EVP_MD_CTX_set_update_fn(EVP_MD_CTX *ctx, int (*update) (EVP_MD_CTX *ctx, const void *data, size_t count)) { ctx->update = update; } void EVP_MD_CTX_set_flags(EVP_MD_CTX *ctx, int flags) { ctx->flags |= flags; } void EVP_MD_CTX_clear_flags(EVP_MD_CTX *ctx, int flags) { ctx->flags &= ~flags; } int EVP_MD_CTX_test_flags(const EVP_MD_CTX *ctx, int flags) { return (ctx->flags & flags); } static int evp_cipher_ctx_enable_use_bits(EVP_CIPHER_CTX *ctx, unsigned int enable) { OSSL_PARAM params[] = { OSSL_PARAM_END, OSSL_PARAM_END }; params[0] = OSSL_PARAM_construct_uint(OSSL_CIPHER_PARAM_USE_BITS, &enable); return EVP_CIPHER_CTX_set_params(ctx, params); } void EVP_CIPHER_CTX_set_flags(EVP_CIPHER_CTX *ctx, int flags) { int oldflags = ctx->flags; ctx->flags |= flags; if (((oldflags ^ ctx->flags) & EVP_CIPH_FLAG_LENGTH_BITS) != 0) evp_cipher_ctx_enable_use_bits(ctx, 1); } void EVP_CIPHER_CTX_clear_flags(EVP_CIPHER_CTX *ctx, int flags) { int oldflags = ctx->flags; ctx->flags &= ~flags; if (((oldflags ^ ctx->flags) & EVP_CIPH_FLAG_LENGTH_BITS) != 0) evp_cipher_ctx_enable_use_bits(ctx, 0); } int EVP_CIPHER_CTX_test_flags(const EVP_CIPHER_CTX *ctx, int flags) { return (ctx->flags & flags); } #if !defined(FIPS_MODULE) int EVP_PKEY_CTX_set_group_name(EVP_PKEY_CTX *ctx, const char *name) { OSSL_PARAM params[] = { OSSL_PARAM_END, OSSL_PARAM_END }; if (ctx == NULL || !EVP_PKEY_CTX_IS_GEN_OP(ctx)) { ERR_raise(ERR_LIB_EVP, EVP_R_COMMAND_NOT_SUPPORTED); /* Uses the same return values as EVP_PKEY_CTX_ctrl */ return -2; } if (name == NULL) return -1; params[0] = OSSL_PARAM_construct_utf8_string(OSSL_PKEY_PARAM_GROUP_NAME, (char *)name, 0); return EVP_PKEY_CTX_set_params(ctx, params); } int EVP_PKEY_CTX_get_group_name(EVP_PKEY_CTX *ctx, char *name, size_t namelen) { OSSL_PARAM params[] = { OSSL_PARAM_END, OSSL_PARAM_END }; OSSL_PARAM *p = params; if (ctx == NULL || !EVP_PKEY_CTX_IS_GEN_OP(ctx)) { /* There is no legacy support for this */ ERR_raise(ERR_LIB_EVP, EVP_R_COMMAND_NOT_SUPPORTED); /* Uses the same return values as EVP_PKEY_CTX_ctrl */ return -2; } if (name == NULL) return -1; *p++ = OSSL_PARAM_construct_utf8_string(OSSL_PKEY_PARAM_GROUP_NAME, name, namelen); if (!EVP_PKEY_CTX_get_params(ctx, params)) return -1; return 1; } #endif /* !FIPS_MODULE */ /* * evp_pkey_keygen() abstracts from the explicit use of B * while providing a generic way of generating a new asymmetric key pair * of algorithm type I (e.g., C or C). * The library context I and property query I * are used when fetching algorithms from providers. * The I specify algorithm-specific parameters * such as the RSA modulus size or the name of an EC curve. */ static EVP_PKEY *evp_pkey_keygen(OSSL_LIB_CTX *libctx, const char *name, const char *propq, const OSSL_PARAM *params) { EVP_PKEY *pkey = NULL; EVP_PKEY_CTX *ctx = EVP_PKEY_CTX_new_from_name(libctx, name, propq); if (ctx != NULL && EVP_PKEY_keygen_init(ctx) > 0 && EVP_PKEY_CTX_set_params(ctx, params)) (void)EVP_PKEY_generate(ctx, &pkey); EVP_PKEY_CTX_free(ctx); return pkey; } EVP_PKEY *EVP_PKEY_Q_keygen(OSSL_LIB_CTX *libctx, const char *propq, const char *type, ...) { va_list args; size_t bits; char *name; OSSL_PARAM params[] = { OSSL_PARAM_END, OSSL_PARAM_END }; EVP_PKEY *ret = NULL; va_start(args, type); if (OPENSSL_strcasecmp(type, "RSA") == 0) { bits = va_arg(args, size_t); params[0] = OSSL_PARAM_construct_size_t(OSSL_PKEY_PARAM_RSA_BITS, &bits); } else if (OPENSSL_strcasecmp(type, "EC") == 0) { name = va_arg(args, char *); params[0] = OSSL_PARAM_construct_utf8_string(OSSL_PKEY_PARAM_GROUP_NAME, name, 0); } ret = evp_pkey_keygen(libctx, type, propq, params); va_end(args); return ret; } #if !defined(FIPS_MODULE) int EVP_CIPHER_CTX_set_algor_params(EVP_CIPHER_CTX *ctx, const X509_ALGOR *alg) { int ret = -1; /* Assume the worst */ unsigned char *der = NULL; int derl = -1; if ((derl = i2d_ASN1_TYPE(alg->parameter, &der)) >= 0) { const char *k_old = OSSL_CIPHER_PARAM_ALGORITHM_ID_PARAMS_OLD; const char *k_new = OSSL_CIPHER_PARAM_ALGORITHM_ID_PARAMS; OSSL_PARAM params[3]; /* * Passing the same data with both the old (deprecated) and the * new AlgID parameters OSSL_PARAM key. */ params[0] = OSSL_PARAM_construct_octet_string(k_old, der, (size_t)derl); params[1] = OSSL_PARAM_construct_octet_string(k_new, der, (size_t)derl); params[2] = OSSL_PARAM_construct_end(); ret = EVP_CIPHER_CTX_set_params(ctx, params); } OPENSSL_free(der); return ret; } int EVP_CIPHER_CTX_get_algor_params(EVP_CIPHER_CTX *ctx, X509_ALGOR *alg) { int ret = -1; /* Assume the worst */ unsigned char *der = NULL; size_t derl; ASN1_TYPE *type = NULL; int i = -1; const char *k_old = OSSL_CIPHER_PARAM_ALGORITHM_ID_PARAMS_OLD; const char *k_new = OSSL_CIPHER_PARAM_ALGORITHM_ID_PARAMS; const char *derk; OSSL_PARAM params[3]; /* * We make two passes, the first to get the appropriate buffer size, * and the second to get the actual value. * Also, using both the old (deprecated) and the new AlgID parameters * OSSL_PARAM key, and using whichever the provider responds to. * Should the provider respond on both, the new key takes priority. */ params[0] = OSSL_PARAM_construct_octet_string(k_old, NULL, 0); params[1] = OSSL_PARAM_construct_octet_string(k_new, NULL, 0); params[2] = OSSL_PARAM_construct_end(); if (!EVP_CIPHER_CTX_get_params(ctx, params)) goto err; /* ... but, we should get a return size too! */ if (OSSL_PARAM_modified(¶ms[0]) && params[0].return_size != 0) i = 0; if (OSSL_PARAM_modified(¶ms[1]) && params[1].return_size != 0) i = 1; if (i < 0) goto err; /* * If alg->parameter is non-NULL, it will be changed by d2i_ASN1_TYPE() * below. If it is NULL, the d2i_ASN1_TYPE() call will allocate new * space for it. Either way, alg->parameter can be safely assigned * with type after the d2i_ASN1_TYPE() call, with the safety that it * will be ok. */ type = alg->parameter; derk = params[i].key; derl = params[i].return_size; if ((der = OPENSSL_malloc(derl)) != NULL) { unsigned char *derp = der; params[i] = OSSL_PARAM_construct_octet_string(derk, der, derl); if (EVP_CIPHER_CTX_get_params(ctx, params) && OSSL_PARAM_modified(¶ms[i]) && d2i_ASN1_TYPE(&type, (const unsigned char **)&derp, (int)derl) != NULL) { /* * Don't free alg->parameter, see comment further up. * Worst case, alg->parameter gets assigned its own value. */ alg->parameter = type; ret = 1; } } err: OPENSSL_free(der); return ret; } int EVP_CIPHER_CTX_get_algor(EVP_CIPHER_CTX *ctx, X509_ALGOR **alg) { int ret = -1; /* Assume the worst */ OSSL_PARAM params[2]; size_t aid_len = 0; const char *k_aid = OSSL_SIGNATURE_PARAM_ALGORITHM_ID; params[0] = OSSL_PARAM_construct_octet_string(k_aid, NULL, 0); params[1] = OSSL_PARAM_construct_end(); if (EVP_CIPHER_CTX_get_params(ctx, params) <= 0) goto err; if (OSSL_PARAM_modified(¶ms[0])) aid_len = params[0].return_size; if (aid_len == 0) { ERR_raise(ERR_LIB_EVP, EVP_R_GETTING_ALGORITHMIDENTIFIER_NOT_SUPPORTED); ret = -2; goto err; } if (alg != NULL) { unsigned char *aid = NULL; const unsigned char *pp = NULL; if ((aid = OPENSSL_malloc(aid_len)) != NULL) { params[0] = OSSL_PARAM_construct_octet_string(k_aid, aid, aid_len); pp = aid; if (EVP_CIPHER_CTX_get_params(ctx, params) && OSSL_PARAM_modified(¶ms[0]) && d2i_X509_ALGOR(alg, &pp, aid_len) != NULL) ret = 1; } OPENSSL_free(aid); } err: return ret; } int EVP_PKEY_CTX_set_algor_params(EVP_PKEY_CTX *ctx, const X509_ALGOR *alg) { int ret = -1; /* Assume the worst */ unsigned char *der = NULL; int derl = -1; if ((derl = i2d_ASN1_TYPE(alg->parameter, &der)) >= 0) { const char *k = OSSL_PKEY_PARAM_ALGORITHM_ID_PARAMS; OSSL_PARAM params[2]; /* * Passing the same data with both the old (deprecated) and the * new AlgID parameters OSSL_PARAM key. */ params[0] = OSSL_PARAM_construct_octet_string(k, der, (size_t)derl); params[1] = OSSL_PARAM_construct_end(); ret = EVP_PKEY_CTX_set_params(ctx, params); } OPENSSL_free(der); return ret; } int EVP_PKEY_CTX_get_algor_params(EVP_PKEY_CTX *ctx, X509_ALGOR *alg) { int ret = -1; /* Assume the worst */ OSSL_PARAM params[2]; unsigned char *der = NULL; size_t derl; ASN1_TYPE *type = NULL; const char *k = OSSL_PKEY_PARAM_ALGORITHM_ID_PARAMS; /* * We make two passes, the first to get the appropriate buffer size, * and the second to get the actual value. * Also, using both the old (deprecated) and the new AlgID parameters * OSSL_PARAM key, and using whichever the provider responds to. * Should the provider respond on both, the new key takes priority. */ params[0] = OSSL_PARAM_construct_octet_string(k, NULL, 0); params[1] = OSSL_PARAM_construct_end(); if (!EVP_PKEY_CTX_get_params(ctx, params)) goto err; /* * If alg->parameter is non-NULL, it will be changed by d2i_ASN1_TYPE() * below. If it is NULL, the d2i_ASN1_TYPE() call will allocate new * space for it. Either way, alg->parameter can be safely assigned * with type after the d2i_ASN1_TYPE() call, with the safety that it * will be ok. */ type = alg->parameter; derl = params[0].return_size; if (OSSL_PARAM_modified(¶ms[0]) /* ... but, we should get a return size too! */ && derl != 0 && (der = OPENSSL_malloc(derl)) != NULL) { unsigned char *derp = der; params[0] = OSSL_PARAM_construct_octet_string(k, der, derl); if (EVP_PKEY_CTX_get_params(ctx, params) && OSSL_PARAM_modified(¶ms[0]) && d2i_ASN1_TYPE(&type, (const unsigned char **)&derp, derl) != NULL) { /* * Don't free alg->parameter, see comment further up. * Worst case, alg->parameter gets assigned its own value. */ alg->parameter = type; ret = 1; } } err: OPENSSL_free(der); return ret; } int EVP_PKEY_CTX_get_algor(EVP_PKEY_CTX *ctx, X509_ALGOR **alg) { int ret = -1; /* Assume the worst */ OSSL_PARAM params[2]; size_t aid_len = 0; const char *k_aid = OSSL_SIGNATURE_PARAM_ALGORITHM_ID; params[0] = OSSL_PARAM_construct_octet_string(k_aid, NULL, 0); params[1] = OSSL_PARAM_construct_end(); if (EVP_PKEY_CTX_get_params(ctx, params) <= 0) goto err; if (OSSL_PARAM_modified(¶ms[0])) aid_len = params[0].return_size; if (aid_len == 0) { ERR_raise(ERR_LIB_EVP, EVP_R_GETTING_ALGORITHMIDENTIFIER_NOT_SUPPORTED); ret = -2; goto err; } if (alg != NULL) { unsigned char *aid = NULL; const unsigned char *pp = NULL; if ((aid = OPENSSL_malloc(aid_len)) != NULL) { params[0] = OSSL_PARAM_construct_octet_string(k_aid, aid, aid_len); pp = aid; if (EVP_PKEY_CTX_get_params(ctx, params) && OSSL_PARAM_modified(¶ms[0]) && d2i_X509_ALGOR(alg, &pp, aid_len) != NULL) ret = 1; } OPENSSL_free(aid); } err: return ret; } #endif /* !defined(FIPS_MODULE) */ diff --git a/crypto/openssl/crypto/modes/ocb128.c b/crypto/openssl/crypto/modes/ocb128.c index 1ae807c100d0..6fe266981005 100644 --- a/crypto/openssl/crypto/modes/ocb128.c +++ b/crypto/openssl/crypto/modes/ocb128.c @@ -1,558 +1,564 @@ /* * Copyright 2014-2020 The OpenSSL Project Authors. All Rights Reserved. * * Licensed under the Apache License 2.0 (the "License"). You may not use * this file except in compliance with the License. You can obtain a copy * in the file LICENSE in the source distribution or at * https://www.openssl.org/source/license.html */ #include #include #include #include "crypto/modes.h" #ifndef OPENSSL_NO_OCB /* * Calculate the number of binary trailing zero's in any given number */ static u32 ocb_ntz(u64 n) { u32 cnt = 0; /* * We do a right-to-left simple sequential search. This is surprisingly * efficient as the distribution of trailing zeros is not uniform, * e.g. the number of possible inputs with no trailing zeros is equal to * the number with 1 or more; the number with exactly 1 is equal to the * number with 2 or more, etc. Checking the last two bits covers 75% of * all numbers. Checking the last three covers 87.5% */ while (!(n & 1)) { n >>= 1; cnt++; } return cnt; } /* * Shift a block of 16 bytes left by shift bits */ static void ocb_block_lshift(const unsigned char *in, size_t shift, unsigned char *out) { int i; unsigned char carry = 0, carry_next; for (i = 15; i >= 0; i--) { carry_next = in[i] >> (8 - shift); out[i] = (in[i] << shift) | carry; carry = carry_next; } } /* * Perform a "double" operation as per OCB spec */ static void ocb_double(OCB_BLOCK *in, OCB_BLOCK *out) { unsigned char mask; /* * Calculate the mask based on the most significant bit. There are more * efficient ways to do this - but this way is constant time */ mask = in->c[0] & 0x80; mask >>= 7; mask = (0 - mask) & 0x87; ocb_block_lshift(in->c, 1, out->c); out->c[15] ^= mask; } /* * Perform an xor on in1 and in2 - each of len bytes. Store result in out */ static void ocb_block_xor(const unsigned char *in1, const unsigned char *in2, size_t len, unsigned char *out) { size_t i; for (i = 0; i < len; i++) { out[i] = in1[i] ^ in2[i]; } } /* * Lookup L_index in our lookup table. If we haven't already got it we need to * calculate it */ static OCB_BLOCK *ocb_lookup_l(OCB128_CONTEXT *ctx, size_t idx) { size_t l_index = ctx->l_index; if (idx <= l_index) { return ctx->l + idx; } /* We don't have it - so calculate it */ if (idx >= ctx->max_l_index) { void *tmp_ptr; /* * Each additional entry allows to process almost double as * much data, so that in linear world the table will need to * be expanded with smaller and smaller increments. Originally * it was doubling in size, which was a waste. Growing it * linearly is not formally optimal, but is simpler to implement. * We grow table by minimally required 4*n that would accommodate * the index. */ ctx->max_l_index += (idx - ctx->max_l_index + 4) & ~3; tmp_ptr = OPENSSL_realloc(ctx->l, ctx->max_l_index * sizeof(OCB_BLOCK)); if (tmp_ptr == NULL) /* prevent ctx->l from being clobbered */ return NULL; ctx->l = tmp_ptr; } while (l_index < idx) { ocb_double(ctx->l + l_index, ctx->l + l_index + 1); l_index++; } ctx->l_index = l_index; return ctx->l + idx; } /* * Create a new OCB128_CONTEXT */ OCB128_CONTEXT *CRYPTO_ocb128_new(void *keyenc, void *keydec, block128_f encrypt, block128_f decrypt, ocb128_f stream) { OCB128_CONTEXT *octx; int ret; if ((octx = OPENSSL_malloc(sizeof(*octx))) != NULL) { ret = CRYPTO_ocb128_init(octx, keyenc, keydec, encrypt, decrypt, stream); if (ret) return octx; OPENSSL_free(octx); } return NULL; } /* * Initialise an existing OCB128_CONTEXT */ int CRYPTO_ocb128_init(OCB128_CONTEXT *ctx, void *keyenc, void *keydec, block128_f encrypt, block128_f decrypt, ocb128_f stream) { memset(ctx, 0, sizeof(*ctx)); ctx->l_index = 0; ctx->max_l_index = 5; if ((ctx->l = OPENSSL_malloc(ctx->max_l_index * 16)) == NULL) return 0; /* * We set both the encryption and decryption key schedules - decryption * needs both. Don't really need decryption schedule if only doing * encryption - but it simplifies things to take it anyway */ ctx->encrypt = encrypt; ctx->decrypt = decrypt; ctx->stream = stream; ctx->keyenc = keyenc; ctx->keydec = keydec; /* L_* = ENCIPHER(K, zeros(128)) */ ctx->encrypt(ctx->l_star.c, ctx->l_star.c, ctx->keyenc); /* L_$ = double(L_*) */ ocb_double(&ctx->l_star, &ctx->l_dollar); /* L_0 = double(L_$) */ ocb_double(&ctx->l_dollar, ctx->l); /* L_{i} = double(L_{i-1}) */ ocb_double(ctx->l, ctx->l+1); ocb_double(ctx->l+1, ctx->l+2); ocb_double(ctx->l+2, ctx->l+3); ocb_double(ctx->l+3, ctx->l+4); ctx->l_index = 4; /* enough to process up to 496 bytes */ return 1; } /* * Copy an OCB128_CONTEXT object */ int CRYPTO_ocb128_copy_ctx(OCB128_CONTEXT *dest, OCB128_CONTEXT *src, void *keyenc, void *keydec) { memcpy(dest, src, sizeof(OCB128_CONTEXT)); if (keyenc) dest->keyenc = keyenc; if (keydec) dest->keydec = keydec; if (src->l) { if ((dest->l = OPENSSL_malloc(src->max_l_index * 16)) == NULL) return 0; memcpy(dest->l, src->l, (src->l_index + 1) * 16); } return 1; } /* * Set the IV to be used for this operation. Must be 1 - 15 bytes. */ int CRYPTO_ocb128_setiv(OCB128_CONTEXT *ctx, const unsigned char *iv, size_t len, size_t taglen) { unsigned char ktop[16], tmp[16], mask; unsigned char stretch[24], nonce[16]; size_t bottom, shift; /* * Spec says IV is 120 bits or fewer - it allows non byte aligned lengths. * We don't support this at this stage */ if ((len > 15) || (len < 1) || (taglen > 16) || (taglen < 1)) { return -1; } /* Reset nonce-dependent variables */ memset(&ctx->sess, 0, sizeof(ctx->sess)); /* Nonce = num2str(TAGLEN mod 128,7) || zeros(120-bitlen(N)) || 1 || N */ nonce[0] = ((taglen * 8) % 128) << 1; memset(nonce + 1, 0, 15); memcpy(nonce + 16 - len, iv, len); nonce[15 - len] |= 1; /* Ktop = ENCIPHER(K, Nonce[1..122] || zeros(6)) */ memcpy(tmp, nonce, 16); tmp[15] &= 0xc0; ctx->encrypt(tmp, ktop, ctx->keyenc); /* Stretch = Ktop || (Ktop[1..64] xor Ktop[9..72]) */ memcpy(stretch, ktop, 16); ocb_block_xor(ktop, ktop + 1, 8, stretch + 16); /* bottom = str2num(Nonce[123..128]) */ bottom = nonce[15] & 0x3f; /* Offset_0 = Stretch[1+bottom..128+bottom] */ shift = bottom % 8; ocb_block_lshift(stretch + (bottom / 8), shift, ctx->sess.offset.c); mask = 0xff; mask <<= 8 - shift; ctx->sess.offset.c[15] |= (*(stretch + (bottom / 8) + 16) & mask) >> (8 - shift); return 1; } /* * Provide any AAD. This can be called multiple times. Only the final time can * have a partial block */ int CRYPTO_ocb128_aad(OCB128_CONTEXT *ctx, const unsigned char *aad, size_t len) { u64 i, all_num_blocks; size_t num_blocks, last_len; OCB_BLOCK tmp; /* Calculate the number of blocks of AAD provided now, and so far */ num_blocks = len / 16; all_num_blocks = num_blocks + ctx->sess.blocks_hashed; /* Loop through all full blocks of AAD */ for (i = ctx->sess.blocks_hashed + 1; i <= all_num_blocks; i++) { OCB_BLOCK *lookup; /* Offset_i = Offset_{i-1} xor L_{ntz(i)} */ lookup = ocb_lookup_l(ctx, ocb_ntz(i)); if (lookup == NULL) return 0; ocb_block16_xor(&ctx->sess.offset_aad, lookup, &ctx->sess.offset_aad); memcpy(tmp.c, aad, 16); aad += 16; /* Sum_i = Sum_{i-1} xor ENCIPHER(K, A_i xor Offset_i) */ ocb_block16_xor(&ctx->sess.offset_aad, &tmp, &tmp); ctx->encrypt(tmp.c, tmp.c, ctx->keyenc); ocb_block16_xor(&tmp, &ctx->sess.sum, &ctx->sess.sum); } /* * Check if we have any partial blocks left over. This is only valid in the * last call to this function */ last_len = len % 16; if (last_len > 0) { /* Offset_* = Offset_m xor L_* */ ocb_block16_xor(&ctx->sess.offset_aad, &ctx->l_star, &ctx->sess.offset_aad); /* CipherInput = (A_* || 1 || zeros(127-bitlen(A_*))) xor Offset_* */ memset(tmp.c, 0, 16); memcpy(tmp.c, aad, last_len); tmp.c[last_len] = 0x80; ocb_block16_xor(&ctx->sess.offset_aad, &tmp, &tmp); /* Sum = Sum_m xor ENCIPHER(K, CipherInput) */ ctx->encrypt(tmp.c, tmp.c, ctx->keyenc); ocb_block16_xor(&tmp, &ctx->sess.sum, &ctx->sess.sum); } ctx->sess.blocks_hashed = all_num_blocks; return 1; } /* * Provide any data to be encrypted. This can be called multiple times. Only * the final time can have a partial block */ int CRYPTO_ocb128_encrypt(OCB128_CONTEXT *ctx, const unsigned char *in, unsigned char *out, size_t len) { u64 i, all_num_blocks; size_t num_blocks, last_len; /* * Calculate the number of blocks of data to be encrypted provided now, and * so far */ num_blocks = len / 16; all_num_blocks = num_blocks + ctx->sess.blocks_processed; if (num_blocks && all_num_blocks == (size_t)all_num_blocks && ctx->stream != NULL) { - size_t max_idx = 0, top = (size_t)all_num_blocks; + size_t max_idx = 0, top = (size_t)all_num_blocks, processed_bytes = 0; /* * See how many L_{i} entries we need to process data at hand * and pre-compute missing entries in the table [if any]... */ while (top >>= 1) max_idx++; if (ocb_lookup_l(ctx, max_idx) == NULL) return 0; ctx->stream(in, out, num_blocks, ctx->keyenc, (size_t)ctx->sess.blocks_processed + 1, ctx->sess.offset.c, (const unsigned char (*)[16])ctx->l, ctx->sess.checksum.c); + processed_bytes = num_blocks * 16; + in += processed_bytes; + out += processed_bytes; } else { /* Loop through all full blocks to be encrypted */ for (i = ctx->sess.blocks_processed + 1; i <= all_num_blocks; i++) { OCB_BLOCK *lookup; OCB_BLOCK tmp; /* Offset_i = Offset_{i-1} xor L_{ntz(i)} */ lookup = ocb_lookup_l(ctx, ocb_ntz(i)); if (lookup == NULL) return 0; ocb_block16_xor(&ctx->sess.offset, lookup, &ctx->sess.offset); memcpy(tmp.c, in, 16); in += 16; /* Checksum_i = Checksum_{i-1} xor P_i */ ocb_block16_xor(&tmp, &ctx->sess.checksum, &ctx->sess.checksum); /* C_i = Offset_i xor ENCIPHER(K, P_i xor Offset_i) */ ocb_block16_xor(&ctx->sess.offset, &tmp, &tmp); ctx->encrypt(tmp.c, tmp.c, ctx->keyenc); ocb_block16_xor(&ctx->sess.offset, &tmp, &tmp); memcpy(out, tmp.c, 16); out += 16; } } /* * Check if we have any partial blocks left over. This is only valid in the * last call to this function */ last_len = len % 16; if (last_len > 0) { OCB_BLOCK pad; /* Offset_* = Offset_m xor L_* */ ocb_block16_xor(&ctx->sess.offset, &ctx->l_star, &ctx->sess.offset); /* Pad = ENCIPHER(K, Offset_*) */ ctx->encrypt(ctx->sess.offset.c, pad.c, ctx->keyenc); /* C_* = P_* xor Pad[1..bitlen(P_*)] */ ocb_block_xor(in, pad.c, last_len, out); /* Checksum_* = Checksum_m xor (P_* || 1 || zeros(127-bitlen(P_*))) */ memset(pad.c, 0, 16); /* borrow pad */ memcpy(pad.c, in, last_len); pad.c[last_len] = 0x80; ocb_block16_xor(&pad, &ctx->sess.checksum, &ctx->sess.checksum); } ctx->sess.blocks_processed = all_num_blocks; return 1; } /* * Provide any data to be decrypted. This can be called multiple times. Only * the final time can have a partial block */ int CRYPTO_ocb128_decrypt(OCB128_CONTEXT *ctx, const unsigned char *in, unsigned char *out, size_t len) { u64 i, all_num_blocks; size_t num_blocks, last_len; /* * Calculate the number of blocks of data to be decrypted provided now, and * so far */ num_blocks = len / 16; all_num_blocks = num_blocks + ctx->sess.blocks_processed; if (num_blocks && all_num_blocks == (size_t)all_num_blocks && ctx->stream != NULL) { - size_t max_idx = 0, top = (size_t)all_num_blocks; + size_t max_idx = 0, top = (size_t)all_num_blocks, processed_bytes = 0; /* * See how many L_{i} entries we need to process data at hand * and pre-compute missing entries in the table [if any]... */ while (top >>= 1) max_idx++; if (ocb_lookup_l(ctx, max_idx) == NULL) return 0; ctx->stream(in, out, num_blocks, ctx->keydec, (size_t)ctx->sess.blocks_processed + 1, ctx->sess.offset.c, (const unsigned char (*)[16])ctx->l, ctx->sess.checksum.c); + processed_bytes = num_blocks * 16; + in += processed_bytes; + out += processed_bytes; } else { OCB_BLOCK tmp; /* Loop through all full blocks to be decrypted */ for (i = ctx->sess.blocks_processed + 1; i <= all_num_blocks; i++) { /* Offset_i = Offset_{i-1} xor L_{ntz(i)} */ OCB_BLOCK *lookup = ocb_lookup_l(ctx, ocb_ntz(i)); if (lookup == NULL) return 0; ocb_block16_xor(&ctx->sess.offset, lookup, &ctx->sess.offset); memcpy(tmp.c, in, 16); in += 16; /* P_i = Offset_i xor DECIPHER(K, C_i xor Offset_i) */ ocb_block16_xor(&ctx->sess.offset, &tmp, &tmp); ctx->decrypt(tmp.c, tmp.c, ctx->keydec); ocb_block16_xor(&ctx->sess.offset, &tmp, &tmp); /* Checksum_i = Checksum_{i-1} xor P_i */ ocb_block16_xor(&tmp, &ctx->sess.checksum, &ctx->sess.checksum); memcpy(out, tmp.c, 16); out += 16; } } /* * Check if we have any partial blocks left over. This is only valid in the * last call to this function */ last_len = len % 16; if (last_len > 0) { OCB_BLOCK pad; /* Offset_* = Offset_m xor L_* */ ocb_block16_xor(&ctx->sess.offset, &ctx->l_star, &ctx->sess.offset); /* Pad = ENCIPHER(K, Offset_*) */ ctx->encrypt(ctx->sess.offset.c, pad.c, ctx->keyenc); /* P_* = C_* xor Pad[1..bitlen(C_*)] */ ocb_block_xor(in, pad.c, last_len, out); /* Checksum_* = Checksum_m xor (P_* || 1 || zeros(127-bitlen(P_*))) */ memset(pad.c, 0, 16); /* borrow pad */ memcpy(pad.c, out, last_len); pad.c[last_len] = 0x80; ocb_block16_xor(&pad, &ctx->sess.checksum, &ctx->sess.checksum); } ctx->sess.blocks_processed = all_num_blocks; return 1; } static int ocb_finish(OCB128_CONTEXT *ctx, unsigned char *tag, size_t len, int write) { OCB_BLOCK tmp; if (len > 16 || len < 1) { return -1; } /* * Tag = ENCIPHER(K, Checksum_* xor Offset_* xor L_$) xor HASH(K,A) */ ocb_block16_xor(&ctx->sess.checksum, &ctx->sess.offset, &tmp); ocb_block16_xor(&ctx->l_dollar, &tmp, &tmp); ctx->encrypt(tmp.c, tmp.c, ctx->keyenc); ocb_block16_xor(&tmp, &ctx->sess.sum, &tmp); if (write) { memcpy(tag, &tmp, len); return 1; } else { return CRYPTO_memcmp(&tmp, tag, len); } } /* * Calculate the tag and verify it against the supplied tag */ int CRYPTO_ocb128_finish(OCB128_CONTEXT *ctx, const unsigned char *tag, size_t len) { return ocb_finish(ctx, (unsigned char*)tag, len, 0); } /* * Retrieve the calculated tag */ int CRYPTO_ocb128_tag(OCB128_CONTEXT *ctx, unsigned char *tag, size_t len) { return ocb_finish(ctx, tag, len, 1); } /* * Release all resources */ void CRYPTO_ocb128_cleanup(OCB128_CONTEXT *ctx) { if (ctx) { OPENSSL_clear_free(ctx->l, ctx->max_l_index * 16); OPENSSL_cleanse(ctx, sizeof(*ctx)); } } #endif /* OPENSSL_NO_OCB */ diff --git a/crypto/openssl/crypto/pkcs12/p12_decr.c b/crypto/openssl/crypto/pkcs12/p12_decr.c index 3fa9c9c8ccec..ce603878c0d6 100644 --- a/crypto/openssl/crypto/pkcs12/p12_decr.c +++ b/crypto/openssl/crypto/pkcs12/p12_decr.c @@ -1,218 +1,223 @@ /* * Copyright 1999-2024 The OpenSSL Project Authors. All Rights Reserved. * * Licensed under the Apache License 2.0 (the "License"). You may not use * this file except in compliance with the License. You can obtain a copy * in the file LICENSE in the source distribution or at * https://www.openssl.org/source/license.html */ #include #include "internal/cryptlib.h" #include #include /* * Encrypt/Decrypt a buffer based on password and algor, result in a * OPENSSL_malloc'ed buffer */ unsigned char *PKCS12_pbe_crypt_ex(const X509_ALGOR *algor, const char *pass, int passlen, const unsigned char *in, int inlen, unsigned char **data, int *datalen, int en_de, OSSL_LIB_CTX *libctx, const char *propq) { unsigned char *out = NULL; int outlen, i; EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new(); int max_out_len, mac_len = 0; int block_size; if (ctx == NULL) { ERR_raise(ERR_LIB_PKCS12, ERR_R_EVP_LIB); goto err; } /* Process data */ if (!EVP_PBE_CipherInit_ex(algor->algorithm, pass, passlen, algor->parameter, ctx, en_de, libctx, propq)) goto err; /* * GOST algorithm specifics: * OMAC algorithm calculate and encrypt MAC of the encrypted objects * It's appended to encrypted text on encrypting * MAC should be processed on decrypting separately from plain text */ block_size = EVP_CIPHER_CTX_get_block_size(ctx); if (block_size == 0) { ERR_raise(ERR_LIB_PKCS12, ERR_R_PASSED_NULL_PARAMETER); goto err; } max_out_len = inlen + block_size; if ((EVP_CIPHER_get_flags(EVP_CIPHER_CTX_get0_cipher(ctx)) & EVP_CIPH_FLAG_CIPHER_WITH_MAC) != 0) { if (EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_TLS1_AAD, 0, &mac_len) < 0) { ERR_raise(ERR_LIB_PKCS12, ERR_R_INTERNAL_ERROR); goto err; } if (EVP_CIPHER_CTX_is_encrypting(ctx)) { max_out_len += mac_len; } else { if (inlen < mac_len) { ERR_raise(ERR_LIB_PKCS12, PKCS12_R_UNSUPPORTED_PKCS12_MODE); goto err; } inlen -= mac_len; if (EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_SET_TAG, (int)mac_len, (unsigned char *)in+inlen) < 0) { ERR_raise(ERR_LIB_PKCS12, ERR_R_INTERNAL_ERROR); goto err; } } } if ((out = OPENSSL_malloc(max_out_len)) == NULL) goto err; if (!EVP_CipherUpdate(ctx, out, &i, in, inlen)) { OPENSSL_free(out); out = NULL; ERR_raise(ERR_LIB_PKCS12, ERR_R_EVP_LIB); goto err; } outlen = i; if (!EVP_CipherFinal_ex(ctx, out + i, &i)) { OPENSSL_free(out); out = NULL; ERR_raise_data(ERR_LIB_PKCS12, PKCS12_R_PKCS12_CIPHERFINAL_ERROR, passlen == 0 ? "empty password" : "maybe wrong password"); goto err; } outlen += i; if ((EVP_CIPHER_get_flags(EVP_CIPHER_CTX_get0_cipher(ctx)) & EVP_CIPH_FLAG_CIPHER_WITH_MAC) != 0) { if (EVP_CIPHER_CTX_is_encrypting(ctx)) { if (EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_GET_TAG, (int)mac_len, out+outlen) < 0) { OPENSSL_free(out); out = NULL; ERR_raise(ERR_LIB_PKCS12, ERR_R_INTERNAL_ERROR); goto err; } outlen += mac_len; } } if (datalen) *datalen = outlen; if (data) *data = out; err: EVP_CIPHER_CTX_free(ctx); return out; } unsigned char *PKCS12_pbe_crypt(const X509_ALGOR *algor, const char *pass, int passlen, const unsigned char *in, int inlen, unsigned char **data, int *datalen, int en_de) { return PKCS12_pbe_crypt_ex(algor, pass, passlen, in, inlen, data, datalen, en_de, NULL, NULL); } /* * Decrypt an OCTET STRING and decode ASN1 structure if zbuf set zero buffer * after use. */ void *PKCS12_item_decrypt_d2i_ex(const X509_ALGOR *algor, const ASN1_ITEM *it, const char *pass, int passlen, const ASN1_OCTET_STRING *oct, int zbuf, OSSL_LIB_CTX *libctx, const char *propq) { unsigned char *out = NULL; const unsigned char *p; void *ret; int outlen = 0; + if (oct == NULL) { + ERR_raise(ERR_LIB_PKCS12, ERR_R_PASSED_NULL_PARAMETER); + return NULL; + } + if (!PKCS12_pbe_crypt_ex(algor, pass, passlen, oct->data, oct->length, &out, &outlen, 0, libctx, propq)) return NULL; p = out; OSSL_TRACE_BEGIN(PKCS12_DECRYPT) { BIO_printf(trc_out, "\n"); BIO_dump(trc_out, out, outlen); BIO_printf(trc_out, "\n"); } OSSL_TRACE_END(PKCS12_DECRYPT); ret = ASN1_item_d2i(NULL, &p, outlen, it); if (zbuf) OPENSSL_cleanse(out, outlen); if (!ret) ERR_raise(ERR_LIB_PKCS12, PKCS12_R_DECODE_ERROR); OPENSSL_free(out); return ret; } void *PKCS12_item_decrypt_d2i(const X509_ALGOR *algor, const ASN1_ITEM *it, const char *pass, int passlen, const ASN1_OCTET_STRING *oct, int zbuf) { return PKCS12_item_decrypt_d2i_ex(algor, it, pass, passlen, oct, zbuf, NULL, NULL); } /* * Encode ASN1 structure and encrypt, return OCTET STRING if zbuf set zero * encoding. */ ASN1_OCTET_STRING *PKCS12_item_i2d_encrypt_ex(X509_ALGOR *algor, const ASN1_ITEM *it, const char *pass, int passlen, void *obj, int zbuf, OSSL_LIB_CTX *ctx, const char *propq) { ASN1_OCTET_STRING *oct = NULL; unsigned char *in = NULL; int inlen; if ((oct = ASN1_OCTET_STRING_new()) == NULL) { ERR_raise(ERR_LIB_PKCS12, ERR_R_ASN1_LIB); goto err; } inlen = ASN1_item_i2d(obj, &in, it); if (!in) { ERR_raise(ERR_LIB_PKCS12, PKCS12_R_ENCODE_ERROR); goto err; } if (!PKCS12_pbe_crypt_ex(algor, pass, passlen, in, inlen, &oct->data, &oct->length, 1, ctx, propq)) { ERR_raise(ERR_LIB_PKCS12, PKCS12_R_ENCRYPT_ERROR); OPENSSL_free(in); goto err; } if (zbuf) OPENSSL_cleanse(in, inlen); OPENSSL_free(in); return oct; err: ASN1_OCTET_STRING_free(oct); return NULL; } ASN1_OCTET_STRING *PKCS12_item_i2d_encrypt(X509_ALGOR *algor, const ASN1_ITEM *it, const char *pass, int passlen, void *obj, int zbuf) { return PKCS12_item_i2d_encrypt_ex(algor, it, pass, passlen, obj, zbuf, NULL, NULL); } diff --git a/crypto/openssl/crypto/pkcs12/p12_kiss.c b/crypto/openssl/crypto/pkcs12/p12_kiss.c index 0901dc94085f..a813450f588c 100644 --- a/crypto/openssl/crypto/pkcs12/p12_kiss.c +++ b/crypto/openssl/crypto/pkcs12/p12_kiss.c @@ -1,268 +1,274 @@ /* * Copyright 1999-2023 The OpenSSL Project Authors. All Rights Reserved. * * Licensed under the Apache License 2.0 (the "License"). You may not use * this file except in compliance with the License. You can obtain a copy * in the file LICENSE in the source distribution or at * https://www.openssl.org/source/license.html */ #include #include "internal/cryptlib.h" #include #include "crypto/x509.h" /* for ossl_x509_add_cert_new() */ /* Simplified PKCS#12 routines */ static int parse_pk12(PKCS12 *p12, const char *pass, int passlen, EVP_PKEY **pkey, STACK_OF(X509) *ocerts); static int parse_bags(const STACK_OF(PKCS12_SAFEBAG) *bags, const char *pass, int passlen, EVP_PKEY **pkey, STACK_OF(X509) *ocerts, OSSL_LIB_CTX *libctx, const char *propq); static int parse_bag(PKCS12_SAFEBAG *bag, const char *pass, int passlen, EVP_PKEY **pkey, STACK_OF(X509) *ocerts, OSSL_LIB_CTX *libctx, const char *propq); /* * Parse and decrypt a PKCS#12 structure returning user key, user cert and * other (CA) certs. Note either ca should be NULL, *ca should be NULL, or it * should point to a valid STACK structure. pkey and/or cert may be NULL; * if non-NULL the variables they point to can be passed uninitialised. */ int PKCS12_parse(PKCS12 *p12, const char *pass, EVP_PKEY **pkey, X509 **cert, STACK_OF(X509) **ca) { STACK_OF(X509) *ocerts = NULL; X509 *x = NULL; if (pkey != NULL) *pkey = NULL; if (cert != NULL) *cert = NULL; /* Check for NULL PKCS12 structure */ if (p12 == NULL) { ERR_raise(ERR_LIB_PKCS12, PKCS12_R_INVALID_NULL_PKCS12_POINTER); return 0; } /* Check the mac */ if (PKCS12_mac_present(p12)) { /* * If password is zero length or NULL then try verifying both cases to * determine which password is correct. The reason for this is that under * PKCS#12 password based encryption no password and a zero length * password are two different things... */ if (pass == NULL || *pass == '\0') { if (PKCS12_verify_mac(p12, NULL, 0)) pass = NULL; else if (PKCS12_verify_mac(p12, "", 0)) pass = ""; else { ERR_raise(ERR_LIB_PKCS12, PKCS12_R_MAC_VERIFY_FAILURE); goto err; } } else if (!PKCS12_verify_mac(p12, pass, -1)) { ERR_raise(ERR_LIB_PKCS12, PKCS12_R_MAC_VERIFY_FAILURE); goto err; } } else if (pass == NULL || *pass == '\0') { pass = NULL; } /* If needed, allocate stack for other certificates */ if ((cert != NULL || ca != NULL) && (ocerts = sk_X509_new_null()) == NULL) { ERR_raise(ERR_LIB_PKCS12, ERR_R_CRYPTO_LIB); goto err; } if (!parse_pk12(p12, pass, -1, pkey, ocerts)) { int err = ERR_peek_last_error(); if (ERR_GET_LIB(err) != ERR_LIB_EVP && ERR_GET_REASON(err) != EVP_R_UNSUPPORTED_ALGORITHM) ERR_raise(ERR_LIB_PKCS12, PKCS12_R_PARSE_ERROR); goto err; } /* Split the certs in ocerts over *cert and *ca as far as requested */ while ((x = sk_X509_shift(ocerts)) != NULL) { if (pkey != NULL && *pkey != NULL && cert != NULL && *cert == NULL) { int match; ERR_set_mark(); match = X509_check_private_key(x, *pkey); ERR_pop_to_mark(); if (match) { *cert = x; continue; } } if (ca != NULL) { if (!ossl_x509_add_cert_new(ca, x, X509_ADD_FLAG_DEFAULT)) goto err; continue; } X509_free(x); } sk_X509_free(ocerts); return 1; err: if (pkey != NULL) { EVP_PKEY_free(*pkey); *pkey = NULL; } if (cert != NULL) { X509_free(*cert); *cert = NULL; } X509_free(x); OSSL_STACK_OF_X509_free(ocerts); return 0; } /* Parse the outer PKCS#12 structure */ /* pkey and/or ocerts may be NULL */ static int parse_pk12(PKCS12 *p12, const char *pass, int passlen, EVP_PKEY **pkey, STACK_OF(X509) *ocerts) { STACK_OF(PKCS7) *asafes; STACK_OF(PKCS12_SAFEBAG) *bags; int i, bagnid; PKCS7 *p7; if ((asafes = PKCS12_unpack_authsafes(p12)) == NULL) return 0; for (i = 0; i < sk_PKCS7_num(asafes); i++) { p7 = sk_PKCS7_value(asafes, i); bagnid = OBJ_obj2nid(p7->type); if (bagnid == NID_pkcs7_data) { bags = PKCS12_unpack_p7data(p7); } else if (bagnid == NID_pkcs7_encrypted) { bags = PKCS12_unpack_p7encdata(p7, pass, passlen); } else continue; if (!bags) { sk_PKCS7_pop_free(asafes, PKCS7_free); return 0; } if (!parse_bags(bags, pass, passlen, pkey, ocerts, p7->ctx.libctx, p7->ctx.propq)) { sk_PKCS12_SAFEBAG_pop_free(bags, PKCS12_SAFEBAG_free); sk_PKCS7_pop_free(asafes, PKCS7_free); return 0; } sk_PKCS12_SAFEBAG_pop_free(bags, PKCS12_SAFEBAG_free); } sk_PKCS7_pop_free(asafes, PKCS7_free); return 1; } /* pkey and/or ocerts may be NULL */ static int parse_bags(const STACK_OF(PKCS12_SAFEBAG) *bags, const char *pass, int passlen, EVP_PKEY **pkey, STACK_OF(X509) *ocerts, OSSL_LIB_CTX *libctx, const char *propq) { int i; for (i = 0; i < sk_PKCS12_SAFEBAG_num(bags); i++) { if (!parse_bag(sk_PKCS12_SAFEBAG_value(bags, i), pass, passlen, pkey, ocerts, libctx, propq)) return 0; } return 1; } /* pkey and/or ocerts may be NULL */ static int parse_bag(PKCS12_SAFEBAG *bag, const char *pass, int passlen, EVP_PKEY **pkey, STACK_OF(X509) *ocerts, OSSL_LIB_CTX *libctx, const char *propq) { PKCS8_PRIV_KEY_INFO *p8; X509 *x509; const ASN1_TYPE *attrib; ASN1_BMPSTRING *fname = NULL; ASN1_OCTET_STRING *lkid = NULL; - if ((attrib = PKCS12_SAFEBAG_get0_attr(bag, NID_friendlyName))) + if ((attrib = PKCS12_SAFEBAG_get0_attr(bag, NID_friendlyName))) { + if (attrib->type != V_ASN1_BMPSTRING) + return 0; fname = attrib->value.bmpstring; + } - if ((attrib = PKCS12_SAFEBAG_get0_attr(bag, NID_localKeyID))) + if ((attrib = PKCS12_SAFEBAG_get0_attr(bag, NID_localKeyID))) { + if (attrib->type != V_ASN1_OCTET_STRING) + return 0; lkid = attrib->value.octet_string; + } switch (PKCS12_SAFEBAG_get_nid(bag)) { case NID_keyBag: if (pkey == NULL || *pkey != NULL) return 1; *pkey = EVP_PKCS82PKEY_ex(PKCS12_SAFEBAG_get0_p8inf(bag), libctx, propq); if (*pkey == NULL) return 0; break; case NID_pkcs8ShroudedKeyBag: if (pkey == NULL || *pkey != NULL) return 1; if ((p8 = PKCS12_decrypt_skey_ex(bag, pass, passlen, libctx, propq)) == NULL) return 0; *pkey = EVP_PKCS82PKEY_ex(p8, libctx, propq); PKCS8_PRIV_KEY_INFO_free(p8); if (!(*pkey)) return 0; break; case NID_certBag: if (ocerts == NULL || PKCS12_SAFEBAG_get_bag_nid(bag) != NID_x509Certificate) return 1; if ((x509 = PKCS12_SAFEBAG_get1_cert_ex(bag, libctx, propq)) == NULL) return 0; if (lkid && !X509_keyid_set1(x509, lkid->data, lkid->length)) { X509_free(x509); return 0; } if (fname) { int len, r; unsigned char *data; len = ASN1_STRING_to_UTF8(&data, fname); if (len >= 0) { r = X509_alias_set1(x509, data, len); OPENSSL_free(data); if (!r) { X509_free(x509); return 0; } } } if (!sk_X509_push(ocerts, x509)) { X509_free(x509); return 0; } break; case NID_safeContentsBag: return parse_bags(PKCS12_SAFEBAG_get0_safes(bag), pass, passlen, pkey, ocerts, libctx, propq); default: return 1; } return 1; } diff --git a/crypto/openssl/crypto/pkcs12/p12_mutl.c b/crypto/openssl/crypto/pkcs12/p12_mutl.c index b43c82f0ed29..f6c7be483de8 100644 --- a/crypto/openssl/crypto/pkcs12/p12_mutl.c +++ b/crypto/openssl/crypto/pkcs12/p12_mutl.c @@ -1,529 +1,543 @@ /* * Copyright 1999-2024 The OpenSSL Project Authors. All Rights Reserved. * * Licensed under the Apache License 2.0 (the "License"). You may not use * this file except in compliance with the License. You can obtain a copy * in the file LICENSE in the source distribution or at * https://www.openssl.org/source/license.html */ /* * HMAC low level APIs are deprecated for public use, but still ok for internal * use. */ #include "internal/deprecated.h" #include #include "internal/cryptlib.h" #include "crypto/evp.h" #include #include #include #include #include "p12_local.h" static int pkcs12_pbmac1_pbkdf2_key_gen(const char *pass, int passlen, unsigned char *salt, int saltlen, int id, int iter, int keylen, unsigned char *out, const EVP_MD *md_type); int PKCS12_mac_present(const PKCS12 *p12) { return p12->mac ? 1 : 0; } void PKCS12_get0_mac(const ASN1_OCTET_STRING **pmac, const X509_ALGOR **pmacalg, const ASN1_OCTET_STRING **psalt, const ASN1_INTEGER **piter, const PKCS12 *p12) { if (p12->mac) { X509_SIG_get0(p12->mac->dinfo, pmacalg, pmac); if (psalt) *psalt = p12->mac->salt; if (piter) *piter = p12->mac->iter; } else { if (pmac) *pmac = NULL; if (pmacalg) *pmacalg = NULL; if (psalt) *psalt = NULL; if (piter) *piter = NULL; } } #define TK26_MAC_KEY_LEN 32 static int pkcs12_gen_gost_mac_key(const char *pass, int passlen, const unsigned char *salt, int saltlen, int iter, int keylen, unsigned char *key, const EVP_MD *digest) { unsigned char out[96]; if (keylen != TK26_MAC_KEY_LEN) { return 0; } if (!PKCS5_PBKDF2_HMAC(pass, passlen, salt, saltlen, iter, digest, sizeof(out), out)) { return 0; } memcpy(key, out + sizeof(out) - TK26_MAC_KEY_LEN, TK26_MAC_KEY_LEN); OPENSSL_cleanse(out, sizeof(out)); return 1; } PBKDF2PARAM *PBMAC1_get1_pbkdf2_param(const X509_ALGOR *macalg) { PBMAC1PARAM *param = NULL; PBKDF2PARAM *pbkdf2_param = NULL; const ASN1_OBJECT *kdf_oid; param = ASN1_TYPE_unpack_sequence(ASN1_ITEM_rptr(PBMAC1PARAM), macalg->parameter); if (param == NULL) { ERR_raise(ERR_LIB_PKCS12, ERR_R_PASSED_INVALID_ARGUMENT); return NULL; } X509_ALGOR_get0(&kdf_oid, NULL, NULL, param->keyDerivationFunc); if (OBJ_obj2nid(kdf_oid) != NID_id_pbkdf2) { ERR_raise(ERR_LIB_PKCS12, ERR_R_PASSED_INVALID_ARGUMENT); PBMAC1PARAM_free(param); return NULL; } pbkdf2_param = ASN1_TYPE_unpack_sequence(ASN1_ITEM_rptr(PBKDF2PARAM), param->keyDerivationFunc->parameter); PBMAC1PARAM_free(param); return pbkdf2_param; } static int PBMAC1_PBKDF2_HMAC(OSSL_LIB_CTX *ctx, const char *propq, const char *pass, int passlen, const X509_ALGOR *macalg, unsigned char *key) { PBKDF2PARAM *pbkdf2_param = NULL; const ASN1_OBJECT *kdf_hmac_oid; int kdf_hmac_nid; int ret = -1; int keylen = 0; EVP_MD *kdf_md = NULL; const ASN1_OCTET_STRING *pbkdf2_salt = NULL; pbkdf2_param = PBMAC1_get1_pbkdf2_param(macalg); if (pbkdf2_param == NULL) { ERR_raise(ERR_LIB_PKCS12, ERR_R_UNSUPPORTED); goto err; } - keylen = ASN1_INTEGER_get(pbkdf2_param->keylength); - pbkdf2_salt = pbkdf2_param->salt->value.octet_string; if (pbkdf2_param->prf == NULL) { kdf_hmac_nid = NID_hmacWithSHA1; } else { X509_ALGOR_get0(&kdf_hmac_oid, NULL, NULL, pbkdf2_param->prf); kdf_hmac_nid = OBJ_obj2nid(kdf_hmac_oid); } kdf_md = EVP_MD_fetch(ctx, OBJ_nid2sn(ossl_hmac2mdnid(kdf_hmac_nid)), propq); if (kdf_md == NULL) { ERR_raise(ERR_LIB_PKCS12, ERR_R_FETCH_FAILED); goto err; } + /* Validate salt is an OCTET STRING choice */ + if (pbkdf2_param->salt == NULL + || pbkdf2_param->salt->type != V_ASN1_OCTET_STRING) { + ERR_raise(ERR_LIB_PKCS12, PKCS12_R_PARSE_ERROR); + goto err; + } + pbkdf2_salt = pbkdf2_param->salt->value.octet_string; + + /* RFC 9579 specifies missing key length as invalid */ + if (pbkdf2_param->keylength != NULL) + keylen = ASN1_INTEGER_get(pbkdf2_param->keylength); + if (keylen <= 0 || keylen > EVP_MAX_MD_SIZE) { + ERR_raise(ERR_LIB_PKCS12, PKCS12_R_PARSE_ERROR); + goto err; + } + if (PKCS5_PBKDF2_HMAC(pass, passlen, pbkdf2_salt->data, pbkdf2_salt->length, ASN1_INTEGER_get(pbkdf2_param->iter), kdf_md, keylen, key) <= 0) { ERR_raise(ERR_LIB_PKCS12, ERR_R_INTERNAL_ERROR); goto err; } ret = keylen; err: EVP_MD_free(kdf_md); PBKDF2PARAM_free(pbkdf2_param); return ret; } /* Generate a MAC, also used for verification */ static int pkcs12_gen_mac(PKCS12 *p12, const char *pass, int passlen, unsigned char *mac, unsigned int *maclen, int pbmac1_md_nid, int pbmac1_kdf_nid, int (*pkcs12_key_gen)(const char *pass, int passlen, unsigned char *salt, int slen, int id, int iter, int n, unsigned char *out, const EVP_MD *md_type)) { int ret = 0; const EVP_MD *md; EVP_MD *md_fetch; HMAC_CTX *hmac = NULL; unsigned char key[EVP_MAX_MD_SIZE], *salt; int saltlen, iter; char md_name[80]; int keylen = 0; int md_nid = NID_undef; const X509_ALGOR *macalg; const ASN1_OBJECT *macoid; if (!PKCS7_type_is_data(p12->authsafes)) { ERR_raise(ERR_LIB_PKCS12, PKCS12_R_CONTENT_TYPE_NOT_DATA); return 0; } if (p12->authsafes->d.data == NULL) { ERR_raise(ERR_LIB_PKCS12, PKCS12_R_DECODE_ERROR); return 0; } salt = p12->mac->salt->data; saltlen = p12->mac->salt->length; if (p12->mac->iter == NULL) iter = 1; else iter = ASN1_INTEGER_get(p12->mac->iter); X509_SIG_get0(p12->mac->dinfo, &macalg, NULL); X509_ALGOR_get0(&macoid, NULL, NULL, macalg); if (OBJ_obj2nid(macoid) == NID_pbmac1) { if (OBJ_obj2txt(md_name, sizeof(md_name), OBJ_nid2obj(pbmac1_md_nid), 0) < 0) return 0; } else { if (OBJ_obj2txt(md_name, sizeof(md_name), macoid, 0) < 0) return 0; } (void)ERR_set_mark(); md = md_fetch = EVP_MD_fetch(p12->authsafes->ctx.libctx, md_name, p12->authsafes->ctx.propq); if (md == NULL) md = EVP_get_digestbynid(OBJ_obj2nid(macoid)); if (md == NULL) { (void)ERR_clear_last_mark(); ERR_raise(ERR_LIB_PKCS12, PKCS12_R_UNKNOWN_DIGEST_ALGORITHM); return 0; } (void)ERR_pop_to_mark(); keylen = EVP_MD_get_size(md); md_nid = EVP_MD_get_type(md); if (keylen <= 0) goto err; /* For PBMAC1 we use a special keygen callback if not provided (e.g. on verification) */ if (pbmac1_md_nid != NID_undef && pkcs12_key_gen == NULL) { keylen = PBMAC1_PBKDF2_HMAC(p12->authsafes->ctx.libctx, p12->authsafes->ctx.propq, pass, passlen, macalg, key); if (keylen < 0) goto err; } else if ((md_nid == NID_id_GostR3411_94 || md_nid == NID_id_GostR3411_2012_256 || md_nid == NID_id_GostR3411_2012_512) && ossl_safe_getenv("LEGACY_GOST_PKCS12") == NULL) { keylen = TK26_MAC_KEY_LEN; if (!pkcs12_gen_gost_mac_key(pass, passlen, salt, saltlen, iter, keylen, key, md)) { ERR_raise(ERR_LIB_PKCS12, PKCS12_R_KEY_GEN_ERROR); goto err; } } else { EVP_MD *hmac_md = (EVP_MD *)md; int fetched = 0; if (pbmac1_kdf_nid != NID_undef) { char hmac_md_name[128]; if (OBJ_obj2txt(hmac_md_name, sizeof(hmac_md_name), OBJ_nid2obj(pbmac1_kdf_nid), 0) < 0) goto err; hmac_md = EVP_MD_fetch(NULL, hmac_md_name, NULL); if (hmac_md == NULL) goto err; fetched = 1; } if (pkcs12_key_gen != NULL) { int res = (*pkcs12_key_gen)(pass, passlen, salt, saltlen, PKCS12_MAC_ID, iter, keylen, key, hmac_md); if (fetched) EVP_MD_free(hmac_md); if (res != 1) { ERR_raise(ERR_LIB_PKCS12, PKCS12_R_KEY_GEN_ERROR); goto err; } } else { if (fetched) EVP_MD_free(hmac_md); /* Default to UTF-8 password */ if (!PKCS12_key_gen_utf8_ex(pass, passlen, salt, saltlen, PKCS12_MAC_ID, iter, keylen, key, md, p12->authsafes->ctx.libctx, p12->authsafes->ctx.propq)) { ERR_raise(ERR_LIB_PKCS12, PKCS12_R_KEY_GEN_ERROR); goto err; } } } if ((hmac = HMAC_CTX_new()) == NULL || !HMAC_Init_ex(hmac, key, keylen, md, NULL) || !HMAC_Update(hmac, p12->authsafes->d.data->data, p12->authsafes->d.data->length) || !HMAC_Final(hmac, mac, maclen)) { goto err; } ret = 1; err: OPENSSL_cleanse(key, sizeof(key)); HMAC_CTX_free(hmac); EVP_MD_free(md_fetch); return ret; } int PKCS12_gen_mac(PKCS12 *p12, const char *pass, int passlen, unsigned char *mac, unsigned int *maclen) { return pkcs12_gen_mac(p12, pass, passlen, mac, maclen, NID_undef, NID_undef, NULL); } /* Verify the mac */ int PKCS12_verify_mac(PKCS12 *p12, const char *pass, int passlen) { unsigned char mac[EVP_MAX_MD_SIZE]; unsigned int maclen; const ASN1_OCTET_STRING *macoct; const X509_ALGOR *macalg; const ASN1_OBJECT *macoid; if (p12->mac == NULL) { ERR_raise(ERR_LIB_PKCS12, PKCS12_R_MAC_ABSENT); return 0; } X509_SIG_get0(p12->mac->dinfo, &macalg, NULL); X509_ALGOR_get0(&macoid, NULL, NULL, macalg); if (OBJ_obj2nid(macoid) == NID_pbmac1) { PBMAC1PARAM *param = NULL; const ASN1_OBJECT *hmac_oid; int md_nid = NID_undef; param = ASN1_TYPE_unpack_sequence(ASN1_ITEM_rptr(PBMAC1PARAM), macalg->parameter); if (param == NULL) { ERR_raise(ERR_LIB_PKCS12, ERR_R_UNSUPPORTED); return 0; } X509_ALGOR_get0(&hmac_oid, NULL, NULL, param->messageAuthScheme); md_nid = ossl_hmac2mdnid(OBJ_obj2nid(hmac_oid)); if (!pkcs12_gen_mac(p12, pass, passlen, mac, &maclen, md_nid, NID_undef, NULL)) { ERR_raise(ERR_LIB_PKCS12, PKCS12_R_MAC_GENERATION_ERROR); PBMAC1PARAM_free(param); return 0; } PBMAC1PARAM_free(param); } else { if (!pkcs12_gen_mac(p12, pass, passlen, mac, &maclen, NID_undef, NID_undef, NULL)) { ERR_raise(ERR_LIB_PKCS12, PKCS12_R_MAC_GENERATION_ERROR); return 0; } } X509_SIG_get0(p12->mac->dinfo, NULL, &macoct); if ((maclen != (unsigned int)ASN1_STRING_length(macoct)) || CRYPTO_memcmp(mac, ASN1_STRING_get0_data(macoct), maclen) != 0) return 0; return 1; } /* Set a mac */ int PKCS12_set_mac(PKCS12 *p12, const char *pass, int passlen, unsigned char *salt, int saltlen, int iter, const EVP_MD *md_type) { unsigned char mac[EVP_MAX_MD_SIZE]; unsigned int maclen; ASN1_OCTET_STRING *macoct; if (md_type == NULL) /* No need to do a fetch as the md_type is used only to get a NID */ md_type = EVP_sha256(); if (!iter) iter = PKCS12_DEFAULT_ITER; if (PKCS12_setup_mac(p12, iter, salt, saltlen, md_type) == PKCS12_ERROR) { ERR_raise(ERR_LIB_PKCS12, PKCS12_R_MAC_SETUP_ERROR); return 0; } /* * Note that output mac is forced to UTF-8... */ if (!pkcs12_gen_mac(p12, pass, passlen, mac, &maclen, NID_undef, NID_undef, NULL)) { ERR_raise(ERR_LIB_PKCS12, PKCS12_R_MAC_GENERATION_ERROR); return 0; } X509_SIG_getm(p12->mac->dinfo, NULL, &macoct); if (!ASN1_OCTET_STRING_set(macoct, mac, maclen)) { ERR_raise(ERR_LIB_PKCS12, PKCS12_R_MAC_STRING_SET_ERROR); return 0; } return 1; } static int pkcs12_pbmac1_pbkdf2_key_gen(const char *pass, int passlen, unsigned char *salt, int saltlen, int id, int iter, int keylen, unsigned char *out, const EVP_MD *md_type) { return PKCS5_PBKDF2_HMAC(pass, passlen, salt, saltlen, iter, md_type, keylen, out); } static int pkcs12_setup_mac(PKCS12 *p12, int iter, unsigned char *salt, int saltlen, int nid) { X509_ALGOR *macalg; PKCS12_MAC_DATA_free(p12->mac); p12->mac = NULL; if ((p12->mac = PKCS12_MAC_DATA_new()) == NULL) return PKCS12_ERROR; if (iter > 1) { if ((p12->mac->iter = ASN1_INTEGER_new()) == NULL) { ERR_raise(ERR_LIB_PKCS12, ERR_R_ASN1_LIB); return 0; } if (!ASN1_INTEGER_set(p12->mac->iter, iter)) { ERR_raise(ERR_LIB_PKCS12, ERR_R_ASN1_LIB); return 0; } } if (saltlen == 0) saltlen = PKCS12_SALT_LEN; else if (saltlen < 0) return 0; if ((p12->mac->salt->data = OPENSSL_malloc(saltlen)) == NULL) return 0; p12->mac->salt->length = saltlen; if (salt == NULL) { if (RAND_bytes_ex(p12->authsafes->ctx.libctx, p12->mac->salt->data, (size_t)saltlen, 0) <= 0) return 0; } else { memcpy(p12->mac->salt->data, salt, saltlen); } X509_SIG_getm(p12->mac->dinfo, &macalg, NULL); if (!X509_ALGOR_set0(macalg, OBJ_nid2obj(nid), V_ASN1_NULL, NULL)) { ERR_raise(ERR_LIB_PKCS12, ERR_R_ASN1_LIB); return 0; } return 1; } /* Set up a mac structure */ int PKCS12_setup_mac(PKCS12 *p12, int iter, unsigned char *salt, int saltlen, const EVP_MD *md_type) { return pkcs12_setup_mac(p12, iter, salt, saltlen, EVP_MD_get_type(md_type)); } int PKCS12_set_pbmac1_pbkdf2(PKCS12 *p12, const char *pass, int passlen, unsigned char *salt, int saltlen, int iter, const EVP_MD *md_type, const char *prf_md_name) { unsigned char mac[EVP_MAX_MD_SIZE]; unsigned int maclen; ASN1_OCTET_STRING *macoct; X509_ALGOR *alg = NULL; int ret = 0; int prf_md_nid = NID_undef, prf_nid = NID_undef, hmac_nid; unsigned char *known_salt = NULL; int keylen = 0; PBMAC1PARAM *param = NULL; X509_ALGOR *hmac_alg = NULL, *macalg = NULL; if (md_type == NULL) /* No need to do a fetch as the md_type is used only to get a NID */ md_type = EVP_sha256(); if (prf_md_name == NULL) prf_md_nid = EVP_MD_get_type(md_type); else prf_md_nid = OBJ_txt2nid(prf_md_name); if (iter == 0) iter = PKCS12_DEFAULT_ITER; keylen = EVP_MD_get_size(md_type); prf_nid = ossl_md2hmacnid(prf_md_nid); hmac_nid = ossl_md2hmacnid(EVP_MD_get_type(md_type)); if (prf_nid == NID_undef || hmac_nid == NID_undef) { ERR_raise(ERR_LIB_PKCS12, PKCS12_R_UNKNOWN_DIGEST_ALGORITHM); goto err; } if (salt == NULL) { known_salt = OPENSSL_malloc(saltlen); if (known_salt == NULL) goto err; if (RAND_bytes_ex(NULL, known_salt, saltlen, 0) <= 0) { ERR_raise(ERR_LIB_PKCS12, ERR_R_RAND_LIB); goto err; } } param = PBMAC1PARAM_new(); hmac_alg = X509_ALGOR_new(); alg = PKCS5_pbkdf2_set(iter, salt ? salt : known_salt, saltlen, prf_nid, keylen); if (param == NULL || hmac_alg == NULL || alg == NULL) goto err; if (pkcs12_setup_mac(p12, iter, salt ? salt : known_salt, saltlen, NID_pbmac1) == PKCS12_ERROR) { ERR_raise(ERR_LIB_PKCS12, PKCS12_R_MAC_SETUP_ERROR); goto err; } if (!X509_ALGOR_set0(hmac_alg, OBJ_nid2obj(hmac_nid), V_ASN1_NULL, NULL)) { ERR_raise(ERR_LIB_PKCS12, PKCS12_R_MAC_SETUP_ERROR); goto err; } X509_ALGOR_free(param->keyDerivationFunc); X509_ALGOR_free(param->messageAuthScheme); param->keyDerivationFunc = alg; param->messageAuthScheme = hmac_alg; X509_SIG_getm(p12->mac->dinfo, &macalg, &macoct); if (!ASN1_TYPE_pack_sequence(ASN1_ITEM_rptr(PBMAC1PARAM), param, &macalg->parameter)) goto err; /* * Note that output mac is forced to UTF-8... */ if (!pkcs12_gen_mac(p12, pass, passlen, mac, &maclen, EVP_MD_get_type(md_type), prf_md_nid, pkcs12_pbmac1_pbkdf2_key_gen)) { ERR_raise(ERR_LIB_PKCS12, PKCS12_R_MAC_GENERATION_ERROR); goto err; } if (!ASN1_OCTET_STRING_set(macoct, mac, maclen)) { ERR_raise(ERR_LIB_PKCS12, PKCS12_R_MAC_STRING_SET_ERROR); goto err; } ret = 1; err: PBMAC1PARAM_free(param); OPENSSL_free(known_salt); return ret; } diff --git a/crypto/openssl/crypto/pkcs12/p12_utl.c b/crypto/openssl/crypto/pkcs12/p12_utl.c index a96623f19fba..082706d5f522 100644 --- a/crypto/openssl/crypto/pkcs12/p12_utl.c +++ b/crypto/openssl/crypto/pkcs12/p12_utl.c @@ -1,265 +1,270 @@ /* * Copyright 1999-2023 The OpenSSL Project Authors. All Rights Reserved. * * Licensed under the Apache License 2.0 (the "License"). You may not use * this file except in compliance with the License. You can obtain a copy * in the file LICENSE in the source distribution or at * https://www.openssl.org/source/license.html */ #include #include "internal/cryptlib.h" #include #include "p12_local.h" #include "crypto/pkcs7/pk7_local.h" /* Cheap and nasty Unicode stuff */ unsigned char *OPENSSL_asc2uni(const char *asc, int asclen, unsigned char **uni, int *unilen) { int ulen, i; unsigned char *unitmp; if (asclen == -1) asclen = strlen(asc); if (asclen < 0) return NULL; ulen = asclen * 2 + 2; if ((unitmp = OPENSSL_malloc(ulen)) == NULL) return NULL; for (i = 0; i < ulen - 2; i += 2) { unitmp[i] = 0; unitmp[i + 1] = asc[i >> 1]; } /* Make result double null terminated */ unitmp[ulen - 2] = 0; unitmp[ulen - 1] = 0; if (unilen) *unilen = ulen; if (uni) *uni = unitmp; return unitmp; } char *OPENSSL_uni2asc(const unsigned char *uni, int unilen) { int asclen, i; char *asctmp; /* string must contain an even number of bytes */ if (unilen & 1) return NULL; if (unilen < 0) return NULL; asclen = unilen / 2; /* If no terminating zero allow for one */ if (!unilen || uni[unilen - 1]) asclen++; uni++; if ((asctmp = OPENSSL_malloc(asclen)) == NULL) return NULL; for (i = 0; i < unilen; i += 2) asctmp[i >> 1] = uni[i]; asctmp[asclen - 1] = 0; return asctmp; } /* * OPENSSL_{utf82uni|uni2utf8} perform conversion between UTF-8 and * PKCS#12 BMPString format, which is specified as big-endian UTF-16. * One should keep in mind that even though BMPString is passed as * unsigned char *, it's not the kind of string you can exercise e.g. * strlen on. Caller also has to keep in mind that its length is * expressed not in number of UTF-16 characters, but in number of * bytes the string occupies, and treat it, the length, accordingly. */ unsigned char *OPENSSL_utf82uni(const char *asc, int asclen, unsigned char **uni, int *unilen) { int ulen, i, j; unsigned char *unitmp, *ret; unsigned long utf32chr = 0; if (asclen == -1) asclen = strlen(asc); for (ulen = 0, i = 0; i < asclen; i += j) { j = UTF8_getc((const unsigned char *)asc+i, asclen-i, &utf32chr); /* * Following condition is somewhat opportunistic is sense that * decoding failure is used as *indirect* indication that input * string might in fact be extended ASCII/ANSI/ISO-8859-X. The * fallback is taken in hope that it would allow to process * files created with previous OpenSSL version, which used the * naive OPENSSL_asc2uni all along. It might be worth noting * that probability of false positive depends on language. In * cases covered by ISO Latin 1 probability is very low, because * any printable non-ASCII alphabet letter followed by another * or any ASCII character will trigger failure and fallback. * In other cases situation can be intensified by the fact that * English letters are not part of alternative keyboard layout, * but even then there should be plenty of pairs that trigger * decoding failure... */ if (j < 0) return OPENSSL_asc2uni(asc, asclen, uni, unilen); if (utf32chr > 0x10FFFF) /* UTF-16 cap */ return NULL; if (utf32chr >= 0x10000) /* pair of UTF-16 characters */ ulen += 2*2; else /* or just one */ ulen += 2; } ulen += 2; /* for trailing UTF16 zero */ if ((ret = OPENSSL_malloc(ulen)) == NULL) return NULL; /* re-run the loop writing down UTF-16 characters in big-endian order */ for (unitmp = ret, i = 0; i < asclen; i += j) { j = UTF8_getc((const unsigned char *)asc+i, asclen-i, &utf32chr); if (utf32chr >= 0x10000) { /* pair if UTF-16 characters */ unsigned int hi, lo; utf32chr -= 0x10000; hi = 0xD800 + (utf32chr>>10); lo = 0xDC00 + (utf32chr&0x3ff); *unitmp++ = (unsigned char)(hi>>8); *unitmp++ = (unsigned char)(hi); *unitmp++ = (unsigned char)(lo>>8); *unitmp++ = (unsigned char)(lo); } else { /* or just one */ *unitmp++ = (unsigned char)(utf32chr>>8); *unitmp++ = (unsigned char)(utf32chr); } } /* Make result double null terminated */ *unitmp++ = 0; *unitmp++ = 0; if (unilen) *unilen = ulen; if (uni) *uni = ret; return ret; } static int bmp_to_utf8(char *str, const unsigned char *utf16, int len) { unsigned long utf32chr; if (len == 0) return 0; if (len < 2) return -1; /* pull UTF-16 character in big-endian order */ utf32chr = (utf16[0]<<8) | utf16[1]; if (utf32chr >= 0xD800 && utf32chr < 0xE000) { /* two chars */ unsigned int lo; if (len < 4) return -1; utf32chr -= 0xD800; utf32chr <<= 10; lo = (utf16[2]<<8) | utf16[3]; if (lo < 0xDC00 || lo >= 0xE000) return -1; utf32chr |= lo-0xDC00; utf32chr += 0x10000; } return UTF8_putc((unsigned char *)str, len > 4 ? 4 : len, utf32chr); } char *OPENSSL_uni2utf8(const unsigned char *uni, int unilen) { int asclen, i, j; char *asctmp; /* string must contain an even number of bytes */ if (unilen & 1) return NULL; for (asclen = 0, i = 0; i < unilen; ) { j = bmp_to_utf8(NULL, uni+i, unilen-i); /* * falling back to OPENSSL_uni2asc makes lesser sense [than * falling back to OPENSSL_asc2uni in OPENSSL_utf82uni above], * it's done rather to maintain symmetry... */ if (j < 0) return OPENSSL_uni2asc(uni, unilen); if (j == 4) i += 4; else i += 2; asclen += j; } /* If no terminating zero allow for one */ if (!unilen || (uni[unilen-2]||uni[unilen - 1])) asclen++; if ((asctmp = OPENSSL_malloc(asclen)) == NULL) return NULL; /* re-run the loop emitting UTF-8 string */ for (asclen = 0, i = 0; i < unilen; ) { j = bmp_to_utf8(asctmp+asclen, uni+i, unilen-i); + /* when UTF8_putc fails */ + if (j < 0) { + OPENSSL_free(asctmp); + return NULL; + } if (j == 4) i += 4; else i += 2; asclen += j; } /* If no terminating zero write one */ if (!unilen || (uni[unilen-2]||uni[unilen - 1])) asctmp[asclen] = '\0'; return asctmp; } int i2d_PKCS12_bio(BIO *bp, const PKCS12 *p12) { return ASN1_item_i2d_bio(ASN1_ITEM_rptr(PKCS12), bp, p12); } #ifndef OPENSSL_NO_STDIO int i2d_PKCS12_fp(FILE *fp, const PKCS12 *p12) { return ASN1_item_i2d_fp(ASN1_ITEM_rptr(PKCS12), fp, p12); } #endif PKCS12 *d2i_PKCS12_bio(BIO *bp, PKCS12 **p12) { OSSL_LIB_CTX *libctx = NULL; const char *propq = NULL; const PKCS7_CTX *p7ctx = NULL; if (p12 != NULL) { p7ctx = ossl_pkcs12_get0_pkcs7ctx(*p12); if (p7ctx != NULL) { libctx = ossl_pkcs7_ctx_get0_libctx(p7ctx); propq = ossl_pkcs7_ctx_get0_propq(p7ctx); } } return ASN1_item_d2i_bio_ex(ASN1_ITEM_rptr(PKCS12), bp, p12, libctx, propq); } #ifndef OPENSSL_NO_STDIO PKCS12 *d2i_PKCS12_fp(FILE *fp, PKCS12 **p12) { OSSL_LIB_CTX *libctx = NULL; const char *propq = NULL; const PKCS7_CTX *p7ctx = NULL; if (p12 != NULL) { p7ctx = ossl_pkcs12_get0_pkcs7ctx(*p12); if (p7ctx != NULL) { libctx = ossl_pkcs7_ctx_get0_libctx(p7ctx); propq = ossl_pkcs7_ctx_get0_propq(p7ctx); } } return ASN1_item_d2i_fp_ex(ASN1_ITEM_rptr(PKCS12), fp, p12, libctx, propq); } #endif diff --git a/crypto/openssl/crypto/pkcs7/pk7_doit.c b/crypto/openssl/crypto/pkcs7/pk7_doit.c index 6173e4608b8a..39809a0fdb96 100644 --- a/crypto/openssl/crypto/pkcs7/pk7_doit.c +++ b/crypto/openssl/crypto/pkcs7/pk7_doit.c @@ -1,1298 +1,1300 @@ /* * Copyright 1995-2025 The OpenSSL Project Authors. All Rights Reserved. * * Licensed under the Apache License 2.0 (the "License"). You may not use * this file except in compliance with the License. You can obtain a copy * in the file LICENSE in the source distribution or at * https://www.openssl.org/source/license.html */ #include #include #include #include #include #include #include "internal/cryptlib.h" #include "internal/sizes.h" #include "crypto/evp.h" #include "pk7_local.h" static int add_attribute(STACK_OF(X509_ATTRIBUTE) **sk, int nid, int atrtype, void *value); static ASN1_TYPE *get_attribute(const STACK_OF(X509_ATTRIBUTE) *sk, int nid); int PKCS7_type_is_other(PKCS7 *p7) { int isOther = 1; int nid = OBJ_obj2nid(p7->type); switch (nid) { case NID_pkcs7_data: case NID_pkcs7_signed: case NID_pkcs7_enveloped: case NID_pkcs7_signedAndEnveloped: case NID_pkcs7_digest: case NID_pkcs7_encrypted: isOther = 0; break; default: isOther = 1; } return isOther; } ASN1_OCTET_STRING *PKCS7_get_octet_string(PKCS7 *p7) { if (PKCS7_type_is_data(p7)) return p7->d.data; if (PKCS7_type_is_other(p7) && p7->d.other && (p7->d.other->type == V_ASN1_OCTET_STRING)) return p7->d.other->value.octet_string; return NULL; } static ASN1_OCTET_STRING *pkcs7_get1_data(PKCS7 *p7) { ASN1_OCTET_STRING *os = PKCS7_get_octet_string(p7); if (os != NULL) { /* Edge case for MIME content, see RFC 5652 section-5.2.1 */ ASN1_OCTET_STRING *osdup = ASN1_OCTET_STRING_dup(os); if (osdup != NULL && (os->flags & ASN1_STRING_FLAG_NDEF)) /* ASN1_STRING_FLAG_NDEF flag is currently used by openssl-smime */ ASN1_STRING_set0(osdup, NULL, 0); return osdup; } /* General case for PKCS#7 content, see RFC 2315 section-7 */ if (PKCS7_type_is_other(p7) && (p7->d.other != NULL) && (p7->d.other->type == V_ASN1_SEQUENCE) && (p7->d.other->value.sequence != NULL) && (p7->d.other->value.sequence->length > 0)) { const unsigned char *data = p7->d.other->value.sequence->data; long len; int inf, tag, class; os = ASN1_OCTET_STRING_new(); if (os == NULL) return NULL; inf = ASN1_get_object(&data, &len, &tag, &class, p7->d.other->value.sequence->length); if (inf != V_ASN1_CONSTRUCTED || tag != V_ASN1_SEQUENCE || !ASN1_OCTET_STRING_set(os, data, len)) { ASN1_OCTET_STRING_free(os); os = NULL; } } return os; } static int pkcs7_bio_add_digest(BIO **pbio, X509_ALGOR *alg, const PKCS7_CTX *ctx) { BIO *btmp; char name[OSSL_MAX_NAME_SIZE]; EVP_MD *fetched = NULL; const EVP_MD *md; if ((btmp = BIO_new(BIO_f_md())) == NULL) { ERR_raise(ERR_LIB_PKCS7, ERR_R_BIO_LIB); goto err; } OBJ_obj2txt(name, sizeof(name), alg->algorithm, 0); (void)ERR_set_mark(); fetched = EVP_MD_fetch(ossl_pkcs7_ctx_get0_libctx(ctx), name, ossl_pkcs7_ctx_get0_propq(ctx)); if (fetched != NULL) md = fetched; else md = EVP_get_digestbyname(name); if (md == NULL) { (void)ERR_clear_last_mark(); ERR_raise(ERR_LIB_PKCS7, PKCS7_R_UNKNOWN_DIGEST_TYPE); goto err; } (void)ERR_pop_to_mark(); if (BIO_set_md(btmp, md) <= 0) { ERR_raise(ERR_LIB_PKCS7, ERR_R_BIO_LIB); EVP_MD_free(fetched); goto err; } EVP_MD_free(fetched); if (*pbio == NULL) *pbio = btmp; else if (!BIO_push(*pbio, btmp)) { ERR_raise(ERR_LIB_PKCS7, ERR_R_BIO_LIB); goto err; } btmp = NULL; return 1; err: BIO_free(btmp); return 0; } static int pkcs7_encode_rinfo(PKCS7_RECIP_INFO *ri, unsigned char *key, int keylen) { EVP_PKEY_CTX *pctx = NULL; EVP_PKEY *pkey = NULL; unsigned char *ek = NULL; int ret = 0; size_t eklen; const PKCS7_CTX *ctx = ri->ctx; pkey = X509_get0_pubkey(ri->cert); if (pkey == NULL) return 0; pctx = EVP_PKEY_CTX_new_from_pkey(ossl_pkcs7_ctx_get0_libctx(ctx), pkey, ossl_pkcs7_ctx_get0_propq(ctx)); if (pctx == NULL) return 0; if (EVP_PKEY_encrypt_init(pctx) <= 0) goto err; if (EVP_PKEY_encrypt(pctx, NULL, &eklen, key, keylen) <= 0) goto err; ek = OPENSSL_malloc(eklen); if (ek == NULL) goto err; if (EVP_PKEY_encrypt(pctx, ek, &eklen, key, keylen) <= 0) goto err; ASN1_STRING_set0(ri->enc_key, ek, eklen); ek = NULL; ret = 1; err: EVP_PKEY_CTX_free(pctx); OPENSSL_free(ek); return ret; } static int pkcs7_decrypt_rinfo(unsigned char **pek, int *peklen, PKCS7_RECIP_INFO *ri, EVP_PKEY *pkey, size_t fixlen) { EVP_PKEY_CTX *pctx = NULL; unsigned char *ek = NULL; size_t eklen; int ret = -1; const PKCS7_CTX *ctx = ri->ctx; pctx = EVP_PKEY_CTX_new_from_pkey(ossl_pkcs7_ctx_get0_libctx(ctx), pkey, ossl_pkcs7_ctx_get0_propq(ctx)); if (pctx == NULL) return -1; if (EVP_PKEY_decrypt_init(pctx) <= 0) goto err; if (EVP_PKEY_is_a(pkey, "RSA")) /* upper layer pkcs7 code incorrectly assumes that a successful RSA * decryption means that the key matches ciphertext (which never * was the case, implicit rejection or not), so to make it work * disable implicit rejection for RSA keys */ EVP_PKEY_CTX_ctrl_str(pctx, "rsa_pkcs1_implicit_rejection", "0"); ret = evp_pkey_decrypt_alloc(pctx, &ek, &eklen, fixlen, ri->enc_key->data, ri->enc_key->length); if (ret <= 0) goto err; ret = 1; OPENSSL_clear_free(*pek, *peklen); *pek = ek; *peklen = eklen; err: EVP_PKEY_CTX_free(pctx); if (!ret) OPENSSL_free(ek); return ret; } BIO *PKCS7_dataInit(PKCS7 *p7, BIO *bio) { int i; BIO *out = NULL, *btmp = NULL; X509_ALGOR *xa = NULL; EVP_CIPHER *fetched_cipher = NULL; const EVP_CIPHER *cipher; const EVP_CIPHER *evp_cipher = NULL; STACK_OF(X509_ALGOR) *md_sk = NULL; STACK_OF(PKCS7_RECIP_INFO) *rsk = NULL; X509_ALGOR *xalg = NULL; PKCS7_RECIP_INFO *ri = NULL; ASN1_OCTET_STRING *os = NULL; const PKCS7_CTX *p7_ctx; OSSL_LIB_CTX *libctx; const char *propq; if (p7 == NULL) { ERR_raise(ERR_LIB_PKCS7, PKCS7_R_INVALID_NULL_POINTER); return NULL; } p7_ctx = ossl_pkcs7_get0_ctx(p7); libctx = ossl_pkcs7_ctx_get0_libctx(p7_ctx); propq = ossl_pkcs7_ctx_get0_propq(p7_ctx); /* * The content field in the PKCS7 ContentInfo is optional, but that really * only applies to inner content (precisely, detached signatures). * * When reading content, missing outer content is therefore treated as an * error. * * When creating content, PKCS7_content_new() must be called before * calling this method, so a NULL p7->d is always an error. */ if (p7->d.ptr == NULL) { ERR_raise(ERR_LIB_PKCS7, PKCS7_R_NO_CONTENT); return NULL; } i = OBJ_obj2nid(p7->type); p7->state = PKCS7_S_HEADER; switch (i) { case NID_pkcs7_signed: md_sk = p7->d.sign->md_algs; os = pkcs7_get1_data(p7->d.sign->contents); break; case NID_pkcs7_signedAndEnveloped: rsk = p7->d.signed_and_enveloped->recipientinfo; md_sk = p7->d.signed_and_enveloped->md_algs; xalg = p7->d.signed_and_enveloped->enc_data->algorithm; evp_cipher = p7->d.signed_and_enveloped->enc_data->cipher; if (evp_cipher == NULL) { ERR_raise(ERR_LIB_PKCS7, PKCS7_R_CIPHER_NOT_INITIALIZED); goto err; } break; case NID_pkcs7_enveloped: rsk = p7->d.enveloped->recipientinfo; xalg = p7->d.enveloped->enc_data->algorithm; evp_cipher = p7->d.enveloped->enc_data->cipher; if (evp_cipher == NULL) { ERR_raise(ERR_LIB_PKCS7, PKCS7_R_CIPHER_NOT_INITIALIZED); goto err; } break; case NID_pkcs7_digest: xa = p7->d.digest->md; os = pkcs7_get1_data(p7->d.digest->contents); break; case NID_pkcs7_data: break; default: ERR_raise(ERR_LIB_PKCS7, PKCS7_R_UNSUPPORTED_CONTENT_TYPE); goto err; } for (i = 0; i < sk_X509_ALGOR_num(md_sk); i++) if (!pkcs7_bio_add_digest(&out, sk_X509_ALGOR_value(md_sk, i), p7_ctx)) goto err; if (xa && !pkcs7_bio_add_digest(&out, xa, p7_ctx)) goto err; if (evp_cipher != NULL) { unsigned char key[EVP_MAX_KEY_LENGTH]; unsigned char iv[EVP_MAX_IV_LENGTH]; int keylen, ivlen; EVP_CIPHER_CTX *ctx; if ((btmp = BIO_new(BIO_f_cipher())) == NULL) { ERR_raise(ERR_LIB_PKCS7, ERR_R_BIO_LIB); goto err; } BIO_get_cipher_ctx(btmp, &ctx); keylen = EVP_CIPHER_get_key_length(evp_cipher); ivlen = EVP_CIPHER_get_iv_length(evp_cipher); xalg->algorithm = OBJ_nid2obj(EVP_CIPHER_get_type(evp_cipher)); if (ivlen > 0) if (RAND_bytes_ex(libctx, iv, ivlen, 0) <= 0) goto err; (void)ERR_set_mark(); fetched_cipher = EVP_CIPHER_fetch(libctx, EVP_CIPHER_get0_name(evp_cipher), propq); (void)ERR_pop_to_mark(); if (fetched_cipher != NULL) cipher = fetched_cipher; else cipher = evp_cipher; if (EVP_CipherInit_ex(ctx, cipher, NULL, NULL, NULL, 1) <= 0) goto err; EVP_CIPHER_free(fetched_cipher); fetched_cipher = NULL; if (EVP_CIPHER_CTX_rand_key(ctx, key) <= 0) goto err; if (EVP_CipherInit_ex(ctx, NULL, NULL, key, iv, 1) <= 0) goto err; if (ivlen > 0) { if (xalg->parameter == NULL) { xalg->parameter = ASN1_TYPE_new(); if (xalg->parameter == NULL) goto err; } if (EVP_CIPHER_param_to_asn1(ctx, xalg->parameter) <= 0) { ASN1_TYPE_free(xalg->parameter); xalg->parameter = NULL; goto err; } } /* Lets do the pub key stuff :-) */ for (i = 0; i < sk_PKCS7_RECIP_INFO_num(rsk); i++) { ri = sk_PKCS7_RECIP_INFO_value(rsk, i); if (pkcs7_encode_rinfo(ri, key, keylen) <= 0) goto err; } OPENSSL_cleanse(key, keylen); if (out == NULL) out = btmp; else BIO_push(out, btmp); btmp = NULL; } if (bio == NULL) { if (PKCS7_is_detached(p7)) { bio = BIO_new(BIO_s_null()); } else if (os != NULL && os->length > 0) { /* * bio needs a copy of os->data instead of a pointer because * the data will be used after os has been freed */ bio = BIO_new(BIO_s_mem()); if (bio != NULL) { BIO_set_mem_eof_return(bio, 0); if (BIO_write(bio, os->data, os->length) != os->length) { BIO_free_all(bio); bio = NULL; } } } else { bio = BIO_new(BIO_s_mem()); if (bio == NULL) goto err; BIO_set_mem_eof_return(bio, 0); } if (bio == NULL) goto err; } if (out) BIO_push(out, bio); else out = bio; ASN1_OCTET_STRING_free(os); return out; err: ASN1_OCTET_STRING_free(os); EVP_CIPHER_free(fetched_cipher); BIO_free_all(out); BIO_free_all(btmp); return NULL; } static int pkcs7_cmp_ri(PKCS7_RECIP_INFO *ri, X509 *pcert) { int ret; ret = X509_NAME_cmp(ri->issuer_and_serial->issuer, X509_get_issuer_name(pcert)); if (ret) return ret; return ASN1_INTEGER_cmp(X509_get0_serialNumber(pcert), ri->issuer_and_serial->serial); } /* int */ BIO *PKCS7_dataDecode(PKCS7 *p7, EVP_PKEY *pkey, BIO *in_bio, X509 *pcert) { int i, len; BIO *out = NULL, *btmp = NULL, *etmp = NULL, *bio = NULL; X509_ALGOR *xa; ASN1_OCTET_STRING *data_body = NULL; EVP_MD *evp_md = NULL; const EVP_MD *md; EVP_CIPHER *evp_cipher = NULL; const EVP_CIPHER *cipher = NULL; EVP_CIPHER_CTX *evp_ctx = NULL; X509_ALGOR *enc_alg = NULL; STACK_OF(X509_ALGOR) *md_sk = NULL; STACK_OF(PKCS7_RECIP_INFO) *rsk = NULL; PKCS7_RECIP_INFO *ri = NULL; unsigned char *ek = NULL, *tkey = NULL; int eklen = 0, tkeylen = 0; char name[OSSL_MAX_NAME_SIZE]; const PKCS7_CTX *p7_ctx; OSSL_LIB_CTX *libctx; const char *propq; if (p7 == NULL) { ERR_raise(ERR_LIB_PKCS7, PKCS7_R_INVALID_NULL_POINTER); return NULL; } p7_ctx = ossl_pkcs7_get0_ctx(p7); libctx = ossl_pkcs7_ctx_get0_libctx(p7_ctx); propq = ossl_pkcs7_ctx_get0_propq(p7_ctx); if (p7->d.ptr == NULL) { ERR_raise(ERR_LIB_PKCS7, PKCS7_R_NO_CONTENT); return NULL; } i = OBJ_obj2nid(p7->type); p7->state = PKCS7_S_HEADER; switch (i) { case NID_pkcs7_signed: /* * p7->d.sign->contents is a PKCS7 structure consisting of a contentType * field and optional content. * data_body is NULL if that structure has no (=detached) content * or if the contentType is wrong (i.e., not "data"). */ data_body = PKCS7_get_octet_string(p7->d.sign->contents); if (!PKCS7_is_detached(p7) && data_body == NULL) { ERR_raise(ERR_LIB_PKCS7, PKCS7_R_INVALID_SIGNED_DATA_TYPE); goto err; } md_sk = p7->d.sign->md_algs; break; case NID_pkcs7_signedAndEnveloped: rsk = p7->d.signed_and_enveloped->recipientinfo; md_sk = p7->d.signed_and_enveloped->md_algs; /* data_body is NULL if the optional EncryptedContent is missing. */ data_body = p7->d.signed_and_enveloped->enc_data->enc_data; enc_alg = p7->d.signed_and_enveloped->enc_data->algorithm; OBJ_obj2txt(name, sizeof(name), enc_alg->algorithm, 0); (void)ERR_set_mark(); evp_cipher = EVP_CIPHER_fetch(libctx, name, propq); if (evp_cipher != NULL) cipher = evp_cipher; else cipher = EVP_get_cipherbyname(name); if (cipher == NULL) { (void)ERR_clear_last_mark(); ERR_raise(ERR_LIB_PKCS7, PKCS7_R_UNSUPPORTED_CIPHER_TYPE); goto err; } (void)ERR_pop_to_mark(); break; case NID_pkcs7_enveloped: rsk = p7->d.enveloped->recipientinfo; enc_alg = p7->d.enveloped->enc_data->algorithm; /* data_body is NULL if the optional EncryptedContent is missing. */ data_body = p7->d.enveloped->enc_data->enc_data; OBJ_obj2txt(name, sizeof(name), enc_alg->algorithm, 0); (void)ERR_set_mark(); evp_cipher = EVP_CIPHER_fetch(libctx, name, propq); if (evp_cipher != NULL) cipher = evp_cipher; else cipher = EVP_get_cipherbyname(name); if (cipher == NULL) { (void)ERR_clear_last_mark(); ERR_raise(ERR_LIB_PKCS7, PKCS7_R_UNSUPPORTED_CIPHER_TYPE); goto err; } (void)ERR_pop_to_mark(); break; default: ERR_raise(ERR_LIB_PKCS7, PKCS7_R_UNSUPPORTED_CONTENT_TYPE); goto err; } /* Detached content must be supplied via in_bio instead. */ if (data_body == NULL && in_bio == NULL) { ERR_raise(ERR_LIB_PKCS7, PKCS7_R_NO_CONTENT); goto err; } /* We will be checking the signature */ if (md_sk != NULL) { for (i = 0; i < sk_X509_ALGOR_num(md_sk); i++) { xa = sk_X509_ALGOR_value(md_sk, i); if ((btmp = BIO_new(BIO_f_md())) == NULL) { ERR_raise(ERR_LIB_PKCS7, ERR_R_BIO_LIB); goto err; } OBJ_obj2txt(name, sizeof(name), xa->algorithm, 0); (void)ERR_set_mark(); evp_md = EVP_MD_fetch(libctx, name, propq); if (evp_md != NULL) md = evp_md; else md = EVP_get_digestbyname(name); if (md == NULL) { (void)ERR_clear_last_mark(); ERR_raise(ERR_LIB_PKCS7, PKCS7_R_UNKNOWN_DIGEST_TYPE); goto err; } (void)ERR_pop_to_mark(); if (BIO_set_md(btmp, md) <= 0) { EVP_MD_free(evp_md); ERR_raise(ERR_LIB_PKCS7, ERR_R_BIO_LIB); goto err; } EVP_MD_free(evp_md); if (out == NULL) out = btmp; else BIO_push(out, btmp); btmp = NULL; } } if (cipher != NULL) { if ((etmp = BIO_new(BIO_f_cipher())) == NULL) { ERR_raise(ERR_LIB_PKCS7, ERR_R_BIO_LIB); goto err; } /* * It was encrypted, we need to decrypt the secret key with the * private key */ /* * Find the recipientInfo which matches the passed certificate (if * any) */ if (pcert) { for (i = 0; i < sk_PKCS7_RECIP_INFO_num(rsk); i++) { ri = sk_PKCS7_RECIP_INFO_value(rsk, i); if (!pkcs7_cmp_ri(ri, pcert)) break; ri = NULL; } if (ri == NULL) { ERR_raise(ERR_LIB_PKCS7, PKCS7_R_NO_RECIPIENT_MATCHES_CERTIFICATE); goto err; } } /* If we haven't got a certificate try each ri in turn */ if (pcert == NULL) { /* * Always attempt to decrypt all rinfo even after success as a * defence against MMA timing attacks. */ for (i = 0; i < sk_PKCS7_RECIP_INFO_num(rsk); i++) { ri = sk_PKCS7_RECIP_INFO_value(rsk, i); ri->ctx = p7_ctx; if (pkcs7_decrypt_rinfo(&ek, &eklen, ri, pkey, EVP_CIPHER_get_key_length(cipher)) < 0) goto err; ERR_clear_error(); } } else { ri->ctx = p7_ctx; /* Only exit on fatal errors, not decrypt failure */ if (pkcs7_decrypt_rinfo(&ek, &eklen, ri, pkey, 0) < 0) goto err; ERR_clear_error(); } evp_ctx = NULL; BIO_get_cipher_ctx(etmp, &evp_ctx); if (EVP_CipherInit_ex(evp_ctx, cipher, NULL, NULL, NULL, 0) <= 0) goto err; if (EVP_CIPHER_asn1_to_param(evp_ctx, enc_alg->parameter) <= 0) goto err; /* Generate random key as MMA defence */ len = EVP_CIPHER_CTX_get_key_length(evp_ctx); if (len <= 0) goto err; tkeylen = (size_t)len; tkey = OPENSSL_malloc(tkeylen); if (tkey == NULL) goto err; if (EVP_CIPHER_CTX_rand_key(evp_ctx, tkey) <= 0) goto err; if (ek == NULL) { ek = tkey; eklen = tkeylen; tkey = NULL; } if (eklen != EVP_CIPHER_CTX_get_key_length(evp_ctx)) { /* * Some S/MIME clients don't use the same key and effective key * length. The key length is determined by the size of the * decrypted RSA key. */ if (EVP_CIPHER_CTX_set_key_length(evp_ctx, eklen) <= 0) { /* Use random key as MMA defence */ OPENSSL_clear_free(ek, eklen); ek = tkey; eklen = tkeylen; tkey = NULL; } } /* Clear errors so we don't leak information useful in MMA */ ERR_clear_error(); if (EVP_CipherInit_ex(evp_ctx, NULL, NULL, ek, NULL, 0) <= 0) goto err; OPENSSL_clear_free(ek, eklen); ek = NULL; OPENSSL_clear_free(tkey, tkeylen); tkey = NULL; if (out == NULL) out = etmp; else BIO_push(out, etmp); etmp = NULL; } if (in_bio != NULL) { bio = in_bio; } else { if (data_body->length > 0) bio = BIO_new_mem_buf(data_body->data, data_body->length); else { bio = BIO_new(BIO_s_mem()); if (bio == NULL) goto err; BIO_set_mem_eof_return(bio, 0); } if (bio == NULL) goto err; } BIO_push(out, bio); bio = NULL; EVP_CIPHER_free(evp_cipher); return out; err: EVP_CIPHER_free(evp_cipher); OPENSSL_clear_free(ek, eklen); OPENSSL_clear_free(tkey, tkeylen); BIO_free_all(out); BIO_free_all(btmp); BIO_free_all(etmp); BIO_free_all(bio); return NULL; } static BIO *PKCS7_find_digest(EVP_MD_CTX **pmd, BIO *bio, int nid) { for (;;) { bio = BIO_find_type(bio, BIO_TYPE_MD); if (bio == NULL) { ERR_raise(ERR_LIB_PKCS7, PKCS7_R_UNABLE_TO_FIND_MESSAGE_DIGEST); return NULL; } BIO_get_md_ctx(bio, pmd); if (*pmd == NULL) { ERR_raise(ERR_LIB_PKCS7, ERR_R_INTERNAL_ERROR); return NULL; } if (EVP_MD_CTX_get_type(*pmd) == nid) return bio; bio = BIO_next(bio); } return NULL; } static int do_pkcs7_signed_attrib(PKCS7_SIGNER_INFO *si, EVP_MD_CTX *mctx) { unsigned char md_data[EVP_MAX_MD_SIZE]; unsigned int md_len; /* Add signing time if not already present */ if (!PKCS7_get_signed_attribute(si, NID_pkcs9_signingTime)) { if (!PKCS7_add0_attrib_signing_time(si, NULL)) { ERR_raise(ERR_LIB_PKCS7, ERR_R_PKCS7_LIB); return 0; } } /* Add digest */ if (!EVP_DigestFinal_ex(mctx, md_data, &md_len)) { ERR_raise(ERR_LIB_PKCS7, ERR_R_EVP_LIB); return 0; } if (!PKCS7_add1_attrib_digest(si, md_data, md_len)) { ERR_raise(ERR_LIB_PKCS7, ERR_R_PKCS7_LIB); return 0; } /* Now sign the attributes */ if (!PKCS7_SIGNER_INFO_sign(si)) return 0; return 1; } int PKCS7_dataFinal(PKCS7 *p7, BIO *bio) { int ret = 0; int i, j; BIO *btmp; PKCS7_SIGNER_INFO *si; EVP_MD_CTX *mdc, *ctx_tmp; STACK_OF(X509_ATTRIBUTE) *sk; STACK_OF(PKCS7_SIGNER_INFO) *si_sk = NULL; ASN1_OCTET_STRING *os = NULL; const PKCS7_CTX *p7_ctx; if (p7 == NULL) { ERR_raise(ERR_LIB_PKCS7, PKCS7_R_INVALID_NULL_POINTER); return 0; } p7_ctx = ossl_pkcs7_get0_ctx(p7); if (p7->d.ptr == NULL) { ERR_raise(ERR_LIB_PKCS7, PKCS7_R_NO_CONTENT); return 0; } ctx_tmp = EVP_MD_CTX_new(); if (ctx_tmp == NULL) { ERR_raise(ERR_LIB_PKCS7, ERR_R_EVP_LIB); return 0; } i = OBJ_obj2nid(p7->type); p7->state = PKCS7_S_HEADER; switch (i) { case NID_pkcs7_data: os = p7->d.data; break; case NID_pkcs7_signedAndEnveloped: /* XXXXXXXXXXXXXXXX */ si_sk = p7->d.signed_and_enveloped->signer_info; os = p7->d.signed_and_enveloped->enc_data->enc_data; if (os == NULL) { os = ASN1_OCTET_STRING_new(); if (os == NULL) { ERR_raise(ERR_LIB_PKCS7, ERR_R_ASN1_LIB); goto err; } p7->d.signed_and_enveloped->enc_data->enc_data = os; } break; case NID_pkcs7_enveloped: /* XXXXXXXXXXXXXXXX */ os = p7->d.enveloped->enc_data->enc_data; if (os == NULL) { os = ASN1_OCTET_STRING_new(); if (os == NULL) { ERR_raise(ERR_LIB_PKCS7, ERR_R_ASN1_LIB); goto err; } p7->d.enveloped->enc_data->enc_data = os; } break; case NID_pkcs7_signed: si_sk = p7->d.sign->signer_info; os = PKCS7_get_octet_string(p7->d.sign->contents); /* If detached data then the content is excluded */ if (PKCS7_type_is_data(p7->d.sign->contents) && p7->detached) { ASN1_OCTET_STRING_free(os); os = NULL; p7->d.sign->contents->d.data = NULL; } break; case NID_pkcs7_digest: os = PKCS7_get_octet_string(p7->d.digest->contents); /* If detached data then the content is excluded */ if (PKCS7_type_is_data(p7->d.digest->contents) && p7->detached) { ASN1_OCTET_STRING_free(os); os = NULL; p7->d.digest->contents->d.data = NULL; } break; default: ERR_raise(ERR_LIB_PKCS7, PKCS7_R_UNSUPPORTED_CONTENT_TYPE); goto err; } if (si_sk != NULL) { for (i = 0; i < sk_PKCS7_SIGNER_INFO_num(si_sk); i++) { si = sk_PKCS7_SIGNER_INFO_value(si_sk, i); if (si->pkey == NULL) continue; j = OBJ_obj2nid(si->digest_alg->algorithm); btmp = bio; btmp = PKCS7_find_digest(&mdc, btmp, j); if (btmp == NULL) goto err; /* * We now have the EVP_MD_CTX, lets do the signing. */ if (!EVP_MD_CTX_copy_ex(ctx_tmp, mdc)) goto err; sk = si->auth_attr; /* * If there are attributes, we add the digest attribute and only * sign the attributes */ if (sk_X509_ATTRIBUTE_num(sk) > 0) { if (!do_pkcs7_signed_attrib(si, ctx_tmp)) goto err; } else { unsigned char *abuf = NULL; unsigned int abuflen = EVP_PKEY_get_size(si->pkey); if (abuflen == 0 || (abuf = OPENSSL_malloc(abuflen)) == NULL) goto err; if (!EVP_SignFinal_ex(ctx_tmp, abuf, &abuflen, si->pkey, ossl_pkcs7_ctx_get0_libctx(p7_ctx), ossl_pkcs7_ctx_get0_propq(p7_ctx))) { OPENSSL_free(abuf); ERR_raise(ERR_LIB_PKCS7, ERR_R_EVP_LIB); goto err; } ASN1_STRING_set0(si->enc_digest, abuf, abuflen); } } } else if (i == NID_pkcs7_digest) { unsigned char md_data[EVP_MAX_MD_SIZE]; unsigned int md_len; if (!PKCS7_find_digest(&mdc, bio, OBJ_obj2nid(p7->d.digest->md->algorithm))) goto err; if (!EVP_DigestFinal_ex(mdc, md_data, &md_len)) goto err; if (!ASN1_OCTET_STRING_set(p7->d.digest->digest, md_data, md_len)) goto err; } if (!PKCS7_is_detached(p7)) { /* * NOTE(emilia): I think we only reach os == NULL here because detached * digested data support is broken. */ if (os == NULL) goto err; if (!(os->flags & ASN1_STRING_FLAG_NDEF)) { char *cont; long contlen; btmp = BIO_find_type(bio, BIO_TYPE_MEM); if (btmp == NULL) { ERR_raise(ERR_LIB_PKCS7, PKCS7_R_UNABLE_TO_FIND_MEM_BIO); goto err; } contlen = BIO_get_mem_data(btmp, &cont); /* * Mark the BIO read only then we can use its copy of the data * instead of making an extra copy. */ BIO_set_flags(btmp, BIO_FLAGS_MEM_RDONLY); BIO_set_mem_eof_return(btmp, 0); ASN1_STRING_set0(os, (unsigned char *)cont, contlen); } } ret = 1; err: EVP_MD_CTX_free(ctx_tmp); return ret; } int PKCS7_SIGNER_INFO_sign(PKCS7_SIGNER_INFO *si) { EVP_MD_CTX *mctx; EVP_PKEY_CTX *pctx = NULL; unsigned char *abuf = NULL; int alen; size_t siglen; const EVP_MD *md = NULL; const PKCS7_CTX *ctx = si->ctx; md = EVP_get_digestbyobj(si->digest_alg->algorithm); if (md == NULL) return 0; mctx = EVP_MD_CTX_new(); if (mctx == NULL) { ERR_raise(ERR_LIB_PKCS7, ERR_R_EVP_LIB); goto err; } if (EVP_DigestSignInit_ex(mctx, &pctx, EVP_MD_get0_name(md), ossl_pkcs7_ctx_get0_libctx(ctx), ossl_pkcs7_ctx_get0_propq(ctx), si->pkey, NULL) <= 0) goto err; alen = ASN1_item_i2d((ASN1_VALUE *)si->auth_attr, &abuf, ASN1_ITEM_rptr(PKCS7_ATTR_SIGN)); if (alen < 0 || abuf == NULL) goto err; if (EVP_DigestSignUpdate(mctx, abuf, alen) <= 0) goto err; OPENSSL_free(abuf); abuf = NULL; if (EVP_DigestSignFinal(mctx, NULL, &siglen) <= 0) goto err; abuf = OPENSSL_malloc(siglen); if (abuf == NULL) goto err; if (EVP_DigestSignFinal(mctx, abuf, &siglen) <= 0) goto err; EVP_MD_CTX_free(mctx); ASN1_STRING_set0(si->enc_digest, abuf, siglen); return 1; err: OPENSSL_free(abuf); EVP_MD_CTX_free(mctx); return 0; } /* This partly overlaps with PKCS7_verify(). It does not support flags. */ int PKCS7_dataVerify(X509_STORE *cert_store, X509_STORE_CTX *ctx, BIO *bio, PKCS7 *p7, PKCS7_SIGNER_INFO *si) { PKCS7_ISSUER_AND_SERIAL *ias; int ret = 0, i; STACK_OF(X509) *untrusted; STACK_OF(X509_CRL) *crls; X509 *signer; if (p7 == NULL) { ERR_raise(ERR_LIB_PKCS7, PKCS7_R_INVALID_NULL_POINTER); return 0; } if (p7->d.ptr == NULL) { ERR_raise(ERR_LIB_PKCS7, PKCS7_R_NO_CONTENT); return 0; } if (PKCS7_type_is_signed(p7)) { untrusted = p7->d.sign->cert; crls = p7->d.sign->crl; } else if (PKCS7_type_is_signedAndEnveloped(p7)) { untrusted = p7->d.signed_and_enveloped->cert; crls = p7->d.signed_and_enveloped->crl; } else { ERR_raise(ERR_LIB_PKCS7, PKCS7_R_WRONG_PKCS7_TYPE); goto err; } X509_STORE_CTX_set0_crls(ctx, crls); /* XXXXXXXXXXXXXXXXXXXXXXX */ ias = si->issuer_and_serial; signer = X509_find_by_issuer_and_serial(untrusted, ias->issuer, ias->serial); /* Were we able to find the signer certificate in passed to us? */ if (signer == NULL) { ERR_raise(ERR_LIB_PKCS7, PKCS7_R_UNABLE_TO_FIND_CERTIFICATE); goto err; } /* Lets verify */ if (!X509_STORE_CTX_init(ctx, cert_store, signer, untrusted)) { ERR_raise(ERR_LIB_PKCS7, ERR_R_X509_LIB); goto err; } X509_STORE_CTX_set_purpose(ctx, X509_PURPOSE_SMIME_SIGN); i = X509_verify_cert(ctx); if (i <= 0) { ERR_raise(ERR_LIB_PKCS7, ERR_R_X509_LIB); goto err; } return PKCS7_signatureVerify(bio, p7, si, signer); err: return ret; } int PKCS7_signatureVerify(BIO *bio, PKCS7 *p7, PKCS7_SIGNER_INFO *si, X509 *signer) { ASN1_OCTET_STRING *os; EVP_MD_CTX *mdc_tmp, *mdc; const EVP_MD *md; EVP_MD *fetched_md = NULL; int ret = 0, i; int md_type; STACK_OF(X509_ATTRIBUTE) *sk; BIO *btmp; EVP_PKEY *pkey; unsigned char *abuf = NULL; const PKCS7_CTX *ctx = ossl_pkcs7_get0_ctx(p7); OSSL_LIB_CTX *libctx = ossl_pkcs7_ctx_get0_libctx(ctx); const char *propq = ossl_pkcs7_ctx_get0_propq(ctx); mdc_tmp = EVP_MD_CTX_new(); if (mdc_tmp == NULL) { ERR_raise(ERR_LIB_PKCS7, ERR_R_EVP_LIB); goto err; } if (!PKCS7_type_is_signed(p7) && !PKCS7_type_is_signedAndEnveloped(p7)) { ERR_raise(ERR_LIB_PKCS7, PKCS7_R_WRONG_PKCS7_TYPE); goto err; } md_type = OBJ_obj2nid(si->digest_alg->algorithm); btmp = bio; for (;;) { if ((btmp == NULL) || ((btmp = BIO_find_type(btmp, BIO_TYPE_MD)) == NULL)) { ERR_raise(ERR_LIB_PKCS7, PKCS7_R_UNABLE_TO_FIND_MESSAGE_DIGEST); goto err; } BIO_get_md_ctx(btmp, &mdc); if (mdc == NULL) { ERR_raise(ERR_LIB_PKCS7, ERR_R_INTERNAL_ERROR); goto err; } if (EVP_MD_CTX_get_type(mdc) == md_type) break; /* * Workaround for some broken clients that put the signature OID * instead of the digest OID in digest_alg->algorithm */ if (EVP_MD_get_pkey_type(EVP_MD_CTX_get0_md(mdc)) == md_type) break; btmp = BIO_next(btmp); } /* * mdc is the digest ctx that we want, unless there are attributes, in * which case the digest is the signed attributes */ if (!EVP_MD_CTX_copy_ex(mdc_tmp, mdc)) goto err; sk = si->auth_attr; if ((sk != NULL) && (sk_X509_ATTRIBUTE_num(sk) != 0)) { unsigned char md_dat[EVP_MAX_MD_SIZE]; unsigned int md_len; int alen; ASN1_OCTET_STRING *message_digest; if (!EVP_DigestFinal_ex(mdc_tmp, md_dat, &md_len)) goto err; message_digest = PKCS7_digest_from_attributes(sk); if (!message_digest) { ERR_raise(ERR_LIB_PKCS7, PKCS7_R_UNABLE_TO_FIND_MESSAGE_DIGEST); goto err; } if ((message_digest->length != (int)md_len) || (memcmp(message_digest->data, md_dat, md_len))) { ERR_raise(ERR_LIB_PKCS7, PKCS7_R_DIGEST_FAILURE); ret = -1; goto err; } (void)ERR_set_mark(); fetched_md = EVP_MD_fetch(libctx, OBJ_nid2sn(md_type), propq); if (fetched_md != NULL) md = fetched_md; else md = EVP_get_digestbynid(md_type); if (md == NULL || !EVP_VerifyInit_ex(mdc_tmp, md, NULL)) { (void)ERR_clear_last_mark(); goto err; } (void)ERR_pop_to_mark(); alen = ASN1_item_i2d((ASN1_VALUE *)sk, &abuf, ASN1_ITEM_rptr(PKCS7_ATTR_VERIFY)); if (alen <= 0 || abuf == NULL) { ERR_raise(ERR_LIB_PKCS7, ERR_R_ASN1_LIB); ret = -1; goto err; } if (!EVP_VerifyUpdate(mdc_tmp, abuf, alen)) goto err; } os = si->enc_digest; pkey = X509_get0_pubkey(signer); if (pkey == NULL) { ret = -1; goto err; } i = EVP_VerifyFinal_ex(mdc_tmp, os->data, os->length, pkey, libctx, propq); if (i <= 0) { ERR_raise(ERR_LIB_PKCS7, PKCS7_R_SIGNATURE_FAILURE); ret = -1; goto err; } ret = 1; err: OPENSSL_free(abuf); EVP_MD_CTX_free(mdc_tmp); EVP_MD_free(fetched_md); return ret; } PKCS7_ISSUER_AND_SERIAL *PKCS7_get_issuer_and_serial(PKCS7 *p7, int idx) { STACK_OF(PKCS7_RECIP_INFO) *rsk; PKCS7_RECIP_INFO *ri; int i; i = OBJ_obj2nid(p7->type); if (i != NID_pkcs7_signedAndEnveloped) return NULL; if (p7->d.signed_and_enveloped == NULL) return NULL; rsk = p7->d.signed_and_enveloped->recipientinfo; if (rsk == NULL) return NULL; if (sk_PKCS7_RECIP_INFO_num(rsk) <= idx) return NULL; ri = sk_PKCS7_RECIP_INFO_value(rsk, idx); return ri->issuer_and_serial; } ASN1_TYPE *PKCS7_get_signed_attribute(const PKCS7_SIGNER_INFO *si, int nid) { return get_attribute(si->auth_attr, nid); } ASN1_TYPE *PKCS7_get_attribute(const PKCS7_SIGNER_INFO *si, int nid) { return get_attribute(si->unauth_attr, nid); } static ASN1_TYPE *get_attribute(const STACK_OF(X509_ATTRIBUTE) *sk, int nid) { int idx = X509at_get_attr_by_NID(sk, nid, -1); if (idx < 0) return NULL; return X509_ATTRIBUTE_get0_type(X509at_get_attr(sk, idx), 0); } ASN1_OCTET_STRING *PKCS7_digest_from_attributes(STACK_OF(X509_ATTRIBUTE) *sk) { ASN1_TYPE *astype; if ((astype = get_attribute(sk, NID_pkcs9_messageDigest)) == NULL) return NULL; + if (astype->type != V_ASN1_OCTET_STRING) + return NULL; return astype->value.octet_string; } int PKCS7_set_signed_attributes(PKCS7_SIGNER_INFO *p7si, STACK_OF(X509_ATTRIBUTE) *sk) { sk_X509_ATTRIBUTE_pop_free(p7si->auth_attr, X509_ATTRIBUTE_free); p7si->auth_attr = sk_X509_ATTRIBUTE_deep_copy(sk, X509_ATTRIBUTE_dup, X509_ATTRIBUTE_free); if (p7si->auth_attr == NULL) return 0; return 1; } int PKCS7_set_attributes(PKCS7_SIGNER_INFO *p7si, STACK_OF(X509_ATTRIBUTE) *sk) { sk_X509_ATTRIBUTE_pop_free(p7si->unauth_attr, X509_ATTRIBUTE_free); p7si->unauth_attr = sk_X509_ATTRIBUTE_deep_copy(sk, X509_ATTRIBUTE_dup, X509_ATTRIBUTE_free); if (p7si->unauth_attr == NULL) return 0; return 1; } int PKCS7_add_signed_attribute(PKCS7_SIGNER_INFO *p7si, int nid, int atrtype, void *value) { return add_attribute(&(p7si->auth_attr), nid, atrtype, value); } int PKCS7_add_attribute(PKCS7_SIGNER_INFO *p7si, int nid, int atrtype, void *value) { return add_attribute(&(p7si->unauth_attr), nid, atrtype, value); } static int add_attribute(STACK_OF(X509_ATTRIBUTE) **sk, int nid, int atrtype, void *value) { X509_ATTRIBUTE *attr = NULL; int i, n; if (*sk == NULL) { if ((*sk = sk_X509_ATTRIBUTE_new_null()) == NULL) return 0; } n = sk_X509_ATTRIBUTE_num(*sk); for (i = 0; i < n; i++) { attr = sk_X509_ATTRIBUTE_value(*sk, i); if (OBJ_obj2nid(X509_ATTRIBUTE_get0_object(attr)) == nid) goto end; } if (!sk_X509_ATTRIBUTE_push(*sk, NULL)) return 0; end: attr = X509_ATTRIBUTE_create(nid, atrtype, value); if (attr == NULL) { if (i == n) sk_X509_ATTRIBUTE_pop(*sk); return 0; } X509_ATTRIBUTE_free(sk_X509_ATTRIBUTE_value(*sk, i)); (void) sk_X509_ATTRIBUTE_set(*sk, i, attr); return 1; } diff --git a/crypto/openssl/crypto/ts/ts_rsp_verify.c b/crypto/openssl/crypto/ts/ts_rsp_verify.c index 0d9fa01e4337..60bc918d2d92 100644 --- a/crypto/openssl/crypto/ts/ts_rsp_verify.c +++ b/crypto/openssl/crypto/ts/ts_rsp_verify.c @@ -1,573 +1,573 @@ /* * Copyright 2006-2025 The OpenSSL Project Authors. All Rights Reserved. * * Licensed under the Apache License 2.0 (the "License"). You may not use * this file except in compliance with the License. You can obtain a copy * in the file LICENSE in the source distribution or at * https://www.openssl.org/source/license.html */ #include #include #include #include #include "internal/cryptlib.h" #include "internal/sizes.h" #include "crypto/ess.h" #include "ts_local.h" static int ts_verify_cert(X509_STORE *store, STACK_OF(X509) *untrusted, X509 *signer, STACK_OF(X509) **chain); static int ts_check_signing_certs(const PKCS7_SIGNER_INFO *si, const STACK_OF(X509) *chain); static int int_ts_RESP_verify_token(TS_VERIFY_CTX *ctx, PKCS7 *token, TS_TST_INFO *tst_info); static int ts_check_status_info(TS_RESP *response); static char *ts_get_status_text(STACK_OF(ASN1_UTF8STRING) *text); static int ts_check_policy(const ASN1_OBJECT *req_oid, const TS_TST_INFO *tst_info); static int ts_compute_imprint(BIO *data, TS_TST_INFO *tst_info, X509_ALGOR **md_alg, unsigned char **imprint, unsigned *imprint_len); static int ts_check_imprints(X509_ALGOR *algor_a, const unsigned char *imprint_a, unsigned len_a, TS_TST_INFO *tst_info); static int ts_check_nonces(const ASN1_INTEGER *a, TS_TST_INFO *tst_info); static int ts_check_signer_name(GENERAL_NAME *tsa_name, X509 *signer); static int ts_find_name(STACK_OF(GENERAL_NAME) *gen_names, GENERAL_NAME *name); /* * This must be large enough to hold all values in ts_status_text (with * comma separator) or all text fields in ts_failure_info (also with comma). */ #define TS_STATUS_BUF_SIZE 256 /* * Local mapping between response codes and descriptions. */ static const char *ts_status_text[] = { "granted", "grantedWithMods", "rejection", "waiting", "revocationWarning", "revocationNotification" }; #define TS_STATUS_TEXT_SIZE OSSL_NELEM(ts_status_text) static struct { int code; const char *text; } ts_failure_info[] = { {TS_INFO_BAD_ALG, "badAlg"}, {TS_INFO_BAD_REQUEST, "badRequest"}, {TS_INFO_BAD_DATA_FORMAT, "badDataFormat"}, {TS_INFO_TIME_NOT_AVAILABLE, "timeNotAvailable"}, {TS_INFO_UNACCEPTED_POLICY, "unacceptedPolicy"}, {TS_INFO_UNACCEPTED_EXTENSION, "unacceptedExtension"}, {TS_INFO_ADD_INFO_NOT_AVAILABLE, "addInfoNotAvailable"}, {TS_INFO_SYSTEM_FAILURE, "systemFailure"} }; /*- * This function carries out the following tasks: * - Checks if there is one and only one signer. * - Search for the signing certificate in 'certs' and in the response. * - Check the extended key usage and key usage fields of the signer * certificate (done by the path validation). * - Build and validate the certificate path. * - Check if the certificate path meets the requirements of the * SigningCertificate ESS signed attribute. * - Verify the signature value. * - Returns the signer certificate in 'signer', if 'signer' is not NULL. */ int TS_RESP_verify_signature(PKCS7 *token, STACK_OF(X509) *certs, X509_STORE *store, X509 **signer_out) { STACK_OF(PKCS7_SIGNER_INFO) *sinfos = NULL; PKCS7_SIGNER_INFO *si; STACK_OF(X509) *untrusted = NULL; STACK_OF(X509) *signers = NULL; X509 *signer; STACK_OF(X509) *chain = NULL; char buf[4096]; int i, j = 0, ret = 0; BIO *p7bio = NULL; /* Some sanity checks first. */ if (!token) { ERR_raise(ERR_LIB_TS, TS_R_INVALID_NULL_POINTER); goto err; } if (!PKCS7_type_is_signed(token)) { ERR_raise(ERR_LIB_TS, TS_R_WRONG_CONTENT_TYPE); goto err; } sinfos = PKCS7_get_signer_info(token); if (!sinfos || sk_PKCS7_SIGNER_INFO_num(sinfos) != 1) { ERR_raise(ERR_LIB_TS, TS_R_THERE_MUST_BE_ONE_SIGNER); goto err; } si = sk_PKCS7_SIGNER_INFO_value(sinfos, 0); if (PKCS7_get_detached(token)) { ERR_raise(ERR_LIB_TS, TS_R_NO_CONTENT); goto err; } /* * Get hold of the signer certificate, search only internal certificates * if it was requested. */ signers = PKCS7_get0_signers(token, certs, 0); if (!signers || sk_X509_num(signers) != 1) goto err; signer = sk_X509_value(signers, 0); untrusted = sk_X509_new_reserve(NULL, sk_X509_num(certs) + sk_X509_num(token->d.sign->cert)); if (untrusted == NULL || !X509_add_certs(untrusted, certs, 0) || !X509_add_certs(untrusted, token->d.sign->cert, 0)) goto err; if (!ts_verify_cert(store, untrusted, signer, &chain)) goto err; if (!ts_check_signing_certs(si, chain)) goto err; p7bio = PKCS7_dataInit(token, NULL); /* We now have to 'read' from p7bio to calculate digests etc. */ while ((i = BIO_read(p7bio, buf, sizeof(buf))) > 0) continue; j = PKCS7_signatureVerify(p7bio, token, si, signer); if (j <= 0) { ERR_raise(ERR_LIB_TS, TS_R_SIGNATURE_FAILURE); goto err; } if (signer_out) { if (!X509_up_ref(signer)) goto err; *signer_out = signer; } ret = 1; err: BIO_free_all(p7bio); sk_X509_free(untrusted); OSSL_STACK_OF_X509_free(chain); sk_X509_free(signers); return ret; } /* * The certificate chain is returned in chain. Caller is responsible for * freeing the vector. */ static int ts_verify_cert(X509_STORE *store, STACK_OF(X509) *untrusted, X509 *signer, STACK_OF(X509) **chain) { X509_STORE_CTX *cert_ctx = NULL; int i; int ret = 0; *chain = NULL; cert_ctx = X509_STORE_CTX_new(); if (cert_ctx == NULL) { ERR_raise(ERR_LIB_TS, ERR_R_X509_LIB); goto err; } if (!X509_STORE_CTX_init(cert_ctx, store, signer, untrusted)) goto end; X509_STORE_CTX_set_purpose(cert_ctx, X509_PURPOSE_TIMESTAMP_SIGN); i = X509_verify_cert(cert_ctx); if (i <= 0) { int j = X509_STORE_CTX_get_error(cert_ctx); ERR_raise_data(ERR_LIB_TS, TS_R_CERTIFICATE_VERIFY_ERROR, "Verify error:%s", X509_verify_cert_error_string(j)); goto err; } *chain = X509_STORE_CTX_get1_chain(cert_ctx); ret = 1; goto end; err: ret = 0; end: X509_STORE_CTX_free(cert_ctx); return ret; } static ESS_SIGNING_CERT *ossl_ess_get_signing_cert(const PKCS7_SIGNER_INFO *si) { ASN1_TYPE *attr; const unsigned char *p; attr = PKCS7_get_signed_attribute(si, NID_id_smime_aa_signingCertificate); - if (attr == NULL) + if (attr == NULL || attr->type != V_ASN1_SEQUENCE) return NULL; p = attr->value.sequence->data; return d2i_ESS_SIGNING_CERT(NULL, &p, attr->value.sequence->length); } static ESS_SIGNING_CERT_V2 *ossl_ess_get_signing_cert_v2(const PKCS7_SIGNER_INFO *si) { ASN1_TYPE *attr; const unsigned char *p; attr = PKCS7_get_signed_attribute(si, NID_id_smime_aa_signingCertificateV2); - if (attr == NULL) + if (attr == NULL || attr->type != V_ASN1_SEQUENCE) return NULL; p = attr->value.sequence->data; return d2i_ESS_SIGNING_CERT_V2(NULL, &p, attr->value.sequence->length); } static int ts_check_signing_certs(const PKCS7_SIGNER_INFO *si, const STACK_OF(X509) *chain) { ESS_SIGNING_CERT *ss = ossl_ess_get_signing_cert(si); ESS_SIGNING_CERT_V2 *ssv2 = ossl_ess_get_signing_cert_v2(si); int ret = OSSL_ESS_check_signing_certs(ss, ssv2, chain, 1) > 0; ESS_SIGNING_CERT_free(ss); ESS_SIGNING_CERT_V2_free(ssv2); return ret; } /*- * Verifies whether 'response' contains a valid response with regards * to the settings of the context: * - Gives an error message if the TS_TST_INFO is not present. * - Calls _TS_RESP_verify_token to verify the token content. */ int TS_RESP_verify_response(TS_VERIFY_CTX *ctx, TS_RESP *response) { PKCS7 *token = response->token; TS_TST_INFO *tst_info = response->tst_info; int ret = 0; if (!ts_check_status_info(response)) goto err; if (!int_ts_RESP_verify_token(ctx, token, tst_info)) goto err; ret = 1; err: return ret; } /* * Tries to extract a TS_TST_INFO structure from the PKCS7 token and * calls the internal int_TS_RESP_verify_token function for verifying it. */ int TS_RESP_verify_token(TS_VERIFY_CTX *ctx, PKCS7 *token) { TS_TST_INFO *tst_info = PKCS7_to_TS_TST_INFO(token); int ret = 0; if (tst_info) { ret = int_ts_RESP_verify_token(ctx, token, tst_info); TS_TST_INFO_free(tst_info); } return ret; } /*- * Verifies whether the 'token' contains a valid timestamp token * with regards to the settings of the context. Only those checks are * carried out that are specified in the context: * - Verifies the signature of the TS_TST_INFO. * - Checks the version number of the response. * - Check if the requested and returned policies math. * - Check if the message imprints are the same. * - Check if the nonces are the same. * - Check if the TSA name matches the signer. * - Check if the TSA name is the expected TSA. */ static int int_ts_RESP_verify_token(TS_VERIFY_CTX *ctx, PKCS7 *token, TS_TST_INFO *tst_info) { X509 *signer = NULL; GENERAL_NAME *tsa_name = tst_info->tsa; X509_ALGOR *md_alg = NULL; unsigned char *imprint = NULL; unsigned imprint_len = 0; int ret = 0; int flags = ctx->flags; /* Some options require us to also check the signature */ if (((flags & TS_VFY_SIGNER) && tsa_name != NULL) || (flags & TS_VFY_TSA_NAME)) { flags |= TS_VFY_SIGNATURE; } if ((flags & TS_VFY_SIGNATURE) && !TS_RESP_verify_signature(token, ctx->certs, ctx->store, &signer)) goto err; if ((flags & TS_VFY_VERSION) && TS_TST_INFO_get_version(tst_info) != 1) { ERR_raise(ERR_LIB_TS, TS_R_UNSUPPORTED_VERSION); goto err; } if ((flags & TS_VFY_POLICY) && !ts_check_policy(ctx->policy, tst_info)) goto err; if ((flags & TS_VFY_IMPRINT) && !ts_check_imprints(ctx->md_alg, ctx->imprint, ctx->imprint_len, tst_info)) goto err; if ((flags & TS_VFY_DATA) && (!ts_compute_imprint(ctx->data, tst_info, &md_alg, &imprint, &imprint_len) || !ts_check_imprints(md_alg, imprint, imprint_len, tst_info))) goto err; if ((flags & TS_VFY_NONCE) && !ts_check_nonces(ctx->nonce, tst_info)) goto err; if ((flags & TS_VFY_SIGNER) && tsa_name && !ts_check_signer_name(tsa_name, signer)) { ERR_raise(ERR_LIB_TS, TS_R_TSA_NAME_MISMATCH); goto err; } if ((flags & TS_VFY_TSA_NAME) && !ts_check_signer_name(ctx->tsa_name, signer)) { ERR_raise(ERR_LIB_TS, TS_R_TSA_UNTRUSTED); goto err; } ret = 1; err: X509_free(signer); X509_ALGOR_free(md_alg); OPENSSL_free(imprint); return ret; } static int ts_check_status_info(TS_RESP *response) { TS_STATUS_INFO *info = response->status_info; long status = ASN1_INTEGER_get(info->status); const char *status_text = NULL; char *embedded_status_text = NULL; char failure_text[TS_STATUS_BUF_SIZE] = ""; if (status == 0 || status == 1) return 1; /* There was an error, get the description in status_text. */ if (0 <= status && status < (long) OSSL_NELEM(ts_status_text)) status_text = ts_status_text[status]; else status_text = "unknown code"; if (sk_ASN1_UTF8STRING_num(info->text) > 0 && (embedded_status_text = ts_get_status_text(info->text)) == NULL) return 0; /* Fill in failure_text with the failure information. */ if (info->failure_info) { int i; int first = 1; for (i = 0; i < (int)OSSL_NELEM(ts_failure_info); ++i) { if (ASN1_BIT_STRING_get_bit(info->failure_info, ts_failure_info[i].code)) { if (!first) strcat(failure_text, ","); else first = 0; strcat(failure_text, ts_failure_info[i].text); } } } if (failure_text[0] == '\0') strcpy(failure_text, "unspecified"); ERR_raise_data(ERR_LIB_TS, TS_R_NO_TIME_STAMP_TOKEN, "status code: %s, status text: %s, failure codes: %s", status_text, embedded_status_text ? embedded_status_text : "unspecified", failure_text); OPENSSL_free(embedded_status_text); return 0; } static char *ts_get_status_text(STACK_OF(ASN1_UTF8STRING) *text) { return ossl_sk_ASN1_UTF8STRING2text(text, "/", TS_MAX_STATUS_LENGTH); } static int ts_check_policy(const ASN1_OBJECT *req_oid, const TS_TST_INFO *tst_info) { const ASN1_OBJECT *resp_oid = tst_info->policy_id; if (OBJ_cmp(req_oid, resp_oid) != 0) { ERR_raise(ERR_LIB_TS, TS_R_POLICY_MISMATCH); return 0; } return 1; } static int ts_compute_imprint(BIO *data, TS_TST_INFO *tst_info, X509_ALGOR **md_alg, unsigned char **imprint, unsigned *imprint_len) { TS_MSG_IMPRINT *msg_imprint = tst_info->msg_imprint; X509_ALGOR *md_alg_resp = msg_imprint->hash_algo; EVP_MD *md = NULL; EVP_MD_CTX *md_ctx = NULL; unsigned char buffer[4096]; char name[OSSL_MAX_NAME_SIZE]; int length; *md_alg = NULL; *imprint = NULL; if ((*md_alg = X509_ALGOR_dup(md_alg_resp)) == NULL) goto err; OBJ_obj2txt(name, sizeof(name), md_alg_resp->algorithm, 0); (void)ERR_set_mark(); md = EVP_MD_fetch(NULL, name, NULL); if (md == NULL) md = (EVP_MD *)EVP_get_digestbyname(name); if (md == NULL) { (void)ERR_clear_last_mark(); goto err; } (void)ERR_pop_to_mark(); length = EVP_MD_get_size(md); if (length <= 0) goto err; *imprint_len = length; if ((*imprint = OPENSSL_malloc(*imprint_len)) == NULL) goto err; md_ctx = EVP_MD_CTX_new(); if (md_ctx == NULL) { ERR_raise(ERR_LIB_TS, ERR_R_EVP_LIB); goto err; } if (!EVP_DigestInit(md_ctx, md)) goto err; EVP_MD_free(md); md = NULL; while ((length = BIO_read(data, buffer, sizeof(buffer))) > 0) { if (!EVP_DigestUpdate(md_ctx, buffer, length)) goto err; } if (!EVP_DigestFinal(md_ctx, *imprint, NULL)) goto err; EVP_MD_CTX_free(md_ctx); return 1; err: EVP_MD_CTX_free(md_ctx); EVP_MD_free(md); X509_ALGOR_free(*md_alg); *md_alg = NULL; OPENSSL_free(*imprint); *imprint_len = 0; *imprint = 0; return 0; } static int ts_check_imprints(X509_ALGOR *algor_a, const unsigned char *imprint_a, unsigned len_a, TS_TST_INFO *tst_info) { TS_MSG_IMPRINT *b = tst_info->msg_imprint; X509_ALGOR *algor_b = b->hash_algo; int ret = 0; if (algor_a) { if (OBJ_cmp(algor_a->algorithm, algor_b->algorithm)) goto err; /* The parameter must be NULL in both. */ if ((algor_a->parameter && ASN1_TYPE_get(algor_a->parameter) != V_ASN1_NULL) || (algor_b->parameter && ASN1_TYPE_get(algor_b->parameter) != V_ASN1_NULL)) goto err; } ret = len_a == (unsigned)ASN1_STRING_length(b->hashed_msg) && memcmp(imprint_a, ASN1_STRING_get0_data(b->hashed_msg), len_a) == 0; err: if (!ret) ERR_raise(ERR_LIB_TS, TS_R_MESSAGE_IMPRINT_MISMATCH); return ret; } static int ts_check_nonces(const ASN1_INTEGER *a, TS_TST_INFO *tst_info) { const ASN1_INTEGER *b = tst_info->nonce; if (!b) { ERR_raise(ERR_LIB_TS, TS_R_NONCE_NOT_RETURNED); return 0; } /* No error if a nonce is returned without being requested. */ if (ASN1_INTEGER_cmp(a, b) != 0) { ERR_raise(ERR_LIB_TS, TS_R_NONCE_MISMATCH); return 0; } return 1; } /* * Check if the specified TSA name matches either the subject or one of the * subject alternative names of the TSA certificate. */ static int ts_check_signer_name(GENERAL_NAME *tsa_name, X509 *signer) { STACK_OF(GENERAL_NAME) *gen_names = NULL; int idx = -1; int found = 0; if (tsa_name->type == GEN_DIRNAME && X509_name_cmp(tsa_name->d.dirn, X509_get_subject_name(signer)) == 0) return 1; gen_names = X509_get_ext_d2i(signer, NID_subject_alt_name, NULL, &idx); while (gen_names != NULL) { found = ts_find_name(gen_names, tsa_name) >= 0; if (found) break; /* * Get the next subject alternative name, although there should be no * more than one. */ GENERAL_NAMES_free(gen_names); gen_names = X509_get_ext_d2i(signer, NID_subject_alt_name, NULL, &idx); } GENERAL_NAMES_free(gen_names); return found; } /* Returns 1 if name is in gen_names, 0 otherwise. */ static int ts_find_name(STACK_OF(GENERAL_NAME) *gen_names, GENERAL_NAME *name) { int i, found; for (i = 0, found = 0; !found && i < sk_GENERAL_NAME_num(gen_names); ++i) { GENERAL_NAME *current = sk_GENERAL_NAME_value(gen_names, i); found = GENERAL_NAME_cmp(current, name) == 0; } return found ? i - 1 : -1; } diff --git a/crypto/openssl/ssl/quic/quic_impl.c b/crypto/openssl/ssl/quic/quic_impl.c index cec05d5bd37b..0c15ef05236c 100644 --- a/crypto/openssl/ssl/quic/quic_impl.c +++ b/crypto/openssl/ssl/quic/quic_impl.c @@ -1,5386 +1,5388 @@ /* * Copyright 2022-2025 The OpenSSL Project Authors. All Rights Reserved. * * Licensed under the Apache License 2.0 (the "License"). You may not use * this file except in compliance with the License. You can obtain a copy * in the file LICENSE in the source distribution or at * https://www.openssl.org/source/license.html */ #include #include #include #include #include "quic_local.h" #include "internal/hashfunc.h" #include "internal/ssl_unwrap.h" #include "internal/quic_tls.h" #include "internal/quic_rx_depack.h" #include "internal/quic_error.h" #include "internal/quic_engine.h" #include "internal/quic_port.h" #include "internal/quic_reactor_wait_ctx.h" #include "internal/time.h" typedef struct qctx_st QCTX; static void qc_cleanup(QUIC_CONNECTION *qc, int have_lock); static void aon_write_finish(QUIC_XSO *xso); static int create_channel(QUIC_CONNECTION *qc, SSL_CTX *ctx); static QUIC_XSO *create_xso_from_stream(QUIC_CONNECTION *qc, QUIC_STREAM *qs); static QUIC_CONNECTION *create_qc_from_incoming_conn(QUIC_LISTENER *ql, QUIC_CHANNEL *ch); static int qc_try_create_default_xso_for_write(QCTX *ctx); static int qc_wait_for_default_xso_for_read(QCTX *ctx, int peek); static void qctx_lock(QCTX *qctx); static void qctx_unlock(QCTX *qctx); static void qctx_lock_for_io(QCTX *ctx); static int quic_do_handshake(QCTX *ctx); static void qc_update_reject_policy(QUIC_CONNECTION *qc); static void qc_touch_default_xso(QUIC_CONNECTION *qc); static void qc_set_default_xso(QUIC_CONNECTION *qc, QUIC_XSO *xso, int touch); static void qc_set_default_xso_keep_ref(QUIC_CONNECTION *qc, QUIC_XSO *xso, int touch, QUIC_XSO **old_xso); static SSL *quic_conn_stream_new(QCTX *ctx, uint64_t flags, int need_lock); static int quic_validate_for_write(QUIC_XSO *xso, int *err); static int quic_mutation_allowed(QUIC_CONNECTION *qc, int req_active); static void qctx_maybe_autotick(QCTX *ctx); static int qctx_should_autotick(QCTX *ctx); /* * QCTX is a utility structure which provides information we commonly wish to * unwrap upon an API call being dispatched to us, namely: * * - a pointer to the QUIC_CONNECTION (regardless of whether a QCSO or QSSO * was passed); * - a pointer to any applicable QUIC_XSO (e.g. if a QSSO was passed, or if * a QCSO with a default stream was passed); * - whether a QSSO was passed (xso == NULL must not be used to determine this * because it may be non-NULL when a QCSO is passed if that QCSO has a * default stream); * - a pointer to a QUIC_LISTENER object, if one is relevant; * - whether we are in "I/O context", meaning that non-normal errors can * be reported via SSL_get_error() as well as via ERR. Functions such as * SSL_read(), SSL_write() and SSL_do_handshake() are "I/O context" * functions which are allowed to change the value returned by * SSL_get_error. However, other functions (including functions which call * SSL_do_handshake() implicitly) are not allowed to change the return value * of SSL_get_error. */ struct qctx_st { QUIC_OBJ *obj; QUIC_DOMAIN *qd; QUIC_LISTENER *ql; QUIC_CONNECTION *qc; QUIC_XSO *xso; int is_stream, is_listener, is_domain, in_io; }; QUIC_NEEDS_LOCK static void quic_set_last_error(QCTX *ctx, int last_error) { if (!ctx->in_io) return; if (ctx->is_stream && ctx->xso != NULL) ctx->xso->last_error = last_error; else if (!ctx->is_stream && ctx->qc != NULL) ctx->qc->last_error = last_error; } /* * Raise a 'normal' error, meaning one that can be reported via SSL_get_error() * rather than via ERR. Note that normal errors must always be raised while * holding a lock. */ QUIC_NEEDS_LOCK static int quic_raise_normal_error(QCTX *ctx, int err) { assert(ctx->in_io); quic_set_last_error(ctx, err); return 0; } /* * Raise a 'non-normal' error, meaning any error that is not reported via * SSL_get_error() and must be reported via ERR. * * qc should be provided if available. In exceptional circumstances when qc is * not known NULL may be passed. This should generally only happen when an * expect_...() function defined below fails, which generally indicates a * dispatch error or caller error. * * ctx should be NULL if the connection lock is not held. */ static int quic_raise_non_normal_error(QCTX *ctx, const char *file, int line, const char *func, int reason, const char *fmt, ...) { va_list args; if (ctx != NULL) { quic_set_last_error(ctx, SSL_ERROR_SSL); if (reason == SSL_R_PROTOCOL_IS_SHUTDOWN && ctx->qc != NULL) ossl_quic_channel_restore_err_state(ctx->qc->ch); } ERR_new(); ERR_set_debug(file, line, func); va_start(args, fmt); ERR_vset_error(ERR_LIB_SSL, reason, fmt, args); va_end(args); return 0; } #define QUIC_RAISE_NORMAL_ERROR(ctx, err) \ quic_raise_normal_error((ctx), (err)) #define QUIC_RAISE_NON_NORMAL_ERROR(ctx, reason, msg) \ quic_raise_non_normal_error((ctx), \ OPENSSL_FILE, OPENSSL_LINE, \ OPENSSL_FUNC, \ (reason), \ (msg)) /* * Flags for expect_quic_as: * * QCTX_C * The input SSL object may be a QCSO. * * QCTX_S * The input SSL object may be a QSSO or a QCSO with a default stream * attached. * * (Note this means there is no current way to require an SSL object with a * QUIC stream which is not a QCSO; a QCSO with a default stream attached * is always considered to satisfy QCTX_S.) * * QCTX_AUTO_S * The input SSL object may be a QSSO or a QCSO with a default stream * attached. If no default stream is currently attached to a QCSO, * one may be auto-created if possible. * * If QCTX_REMOTE_INIT is set, an auto-created default XSO is * initiated by the remote party (i.e., local party reads first). * * If it is not set, an auto-created default XSO is * initiated by the local party (i.e., local party writes first). * * QCTX_L * The input SSL object may be a QLSO. * * QCTX_LOCK * If and only if the function returns successfully, the ctx * is guaranteed to be locked. * * QCTX_IO * Begin an I/O context. If not set, begins a non-I/O context. * This determines whether SSL_get_error() is updated; the value it returns * is modified only by an I/O call. * * QCTX_NO_ERROR * Don't raise an error if the object type is wrong. Should not be used in * conjunction with any flags that may raise errors not related to a wrong * object type. */ #define QCTX_C (1U << 0) #define QCTX_S (1U << 1) #define QCTX_L (1U << 2) #define QCTX_AUTO_S (1U << 3) #define QCTX_REMOTE_INIT (1U << 4) #define QCTX_LOCK (1U << 5) #define QCTX_IO (1U << 6) #define QCTX_D (1U << 7) #define QCTX_NO_ERROR (1U << 8) /* * Called when expect_quic failed. Used to diagnose why such a call failed and * raise a reasonable error code based on the configured preconditions in flags. */ static int wrong_type(const SSL *s, uint32_t flags) { const uint32_t mask = QCTX_C | QCTX_S | QCTX_L | QCTX_D; int code = ERR_R_UNSUPPORTED; if ((flags & QCTX_NO_ERROR) != 0) return 1; else if ((flags & mask) == QCTX_D) code = SSL_R_DOMAIN_USE_ONLY; else if ((flags & mask) == QCTX_L) code = SSL_R_LISTENER_USE_ONLY; else if ((flags & mask) == QCTX_C) code = SSL_R_CONN_USE_ONLY; else if ((flags & mask) == QCTX_S || (flags & mask) == (QCTX_C | QCTX_S)) code = SSL_R_NO_STREAM; return QUIC_RAISE_NON_NORMAL_ERROR(NULL, code, NULL); } /* * Given a QDSO, QCSO, QSSO or QLSO, initialises a QCTX, determining the * contextually applicable QUIC_LISTENER, QUIC_CONNECTION and QUIC_XSO * pointers. * * After this returns 1, all fields of the passed QCTX are initialised. * Returns 0 on failure. This function is intended to be used to provide API * semantics and as such, it invokes QUIC_RAISE_NON_NORMAL_ERROR() on failure * unless the QCTX_NO_ERROR flag is set. * * The flags argument controls the preconditions and postconditions of this * function. See above for the different flags. * * The fields of a QCTX are initialised as follows depending on the identity of * the SSL object, and assuming the preconditions demanded by the flags field as * described above are met: * * QDSO QLSO QCSO QSSO * qd non-NULL maybe maybe maybe * ql NULL non-NULL maybe maybe * qc NULL NULL non-NULL non-NULL * xso NULL NULL maybe non-NULL * is_stream 0 0 0 1 * is_listener 0 1 0 0 * is_domain 1 0 0 0 * */ static int expect_quic_as(const SSL *s, QCTX *ctx, uint32_t flags) { int ok = 0, locked = 0, lock_requested = ((flags & QCTX_LOCK) != 0); QUIC_DOMAIN *qd; QUIC_LISTENER *ql; QUIC_CONNECTION *qc; QUIC_XSO *xso; if ((flags & QCTX_AUTO_S) != 0) flags |= QCTX_S; ctx->obj = NULL; ctx->qd = NULL; ctx->ql = NULL; ctx->qc = NULL; ctx->xso = NULL; ctx->is_stream = 0; ctx->is_listener = 0; ctx->is_domain = 0; ctx->in_io = ((flags & QCTX_IO) != 0); if (s == NULL) { QUIC_RAISE_NON_NORMAL_ERROR(NULL, ERR_R_PASSED_NULL_PARAMETER, NULL); goto err; } switch (s->type) { case SSL_TYPE_QUIC_DOMAIN: if ((flags & QCTX_D) == 0) { wrong_type(s, flags); goto err; } qd = (QUIC_DOMAIN *)s; ctx->obj = &qd->obj; ctx->qd = qd; ctx->is_domain = 1; break; case SSL_TYPE_QUIC_LISTENER: if ((flags & QCTX_L) == 0) { wrong_type(s, flags); goto err; } ql = (QUIC_LISTENER *)s; ctx->obj = &ql->obj; ctx->qd = ql->domain; ctx->ql = ql; ctx->is_listener = 1; break; case SSL_TYPE_QUIC_CONNECTION: qc = (QUIC_CONNECTION *)s; ctx->obj = &qc->obj; ctx->qd = qc->domain; ctx->ql = qc->listener; /* never changes, so can be read without lock */ ctx->qc = qc; if ((flags & QCTX_AUTO_S) != 0) { if ((flags & QCTX_IO) != 0) qctx_lock_for_io(ctx); else qctx_lock(ctx); locked = 1; } if ((flags & QCTX_AUTO_S) != 0 && qc->default_xso == NULL) { if (!quic_mutation_allowed(qc, /*req_active=*/0)) { QUIC_RAISE_NON_NORMAL_ERROR(ctx, SSL_R_PROTOCOL_IS_SHUTDOWN, NULL); goto err; } /* If we haven't finished the handshake, try to advance it. */ if (quic_do_handshake(ctx) < 1) /* ossl_quic_do_handshake raised error here */ goto err; if ((flags & QCTX_REMOTE_INIT) != 0) { if (!qc_wait_for_default_xso_for_read(ctx, /*peek=*/0)) goto err; } else { if (!qc_try_create_default_xso_for_write(ctx)) goto err; } } if ((flags & QCTX_C) == 0 && (qc->default_xso == NULL || (flags & QCTX_S) == 0)) { wrong_type(s, flags); goto err; } ctx->xso = qc->default_xso; break; case SSL_TYPE_QUIC_XSO: if ((flags & QCTX_S) == 0) { wrong_type(s, flags); goto err; } xso = (QUIC_XSO *)s; ctx->obj = &xso->obj; ctx->qd = xso->conn->domain; ctx->ql = xso->conn->listener; ctx->qc = xso->conn; ctx->xso = xso; ctx->is_stream = 1; break; default: QUIC_RAISE_NON_NORMAL_ERROR(NULL, ERR_R_INTERNAL_ERROR, NULL); goto err; } if (lock_requested && !locked) { if ((flags & QCTX_IO) != 0) qctx_lock_for_io(ctx); else qctx_lock(ctx); locked = 1; } ok = 1; err: if (locked && (!ok || !lock_requested)) qctx_unlock(ctx); return ok; } static int is_quic_c(const SSL *s, QCTX *ctx, int raiseerrs) { uint32_t flags = QCTX_C; if (!raiseerrs) flags |= QCTX_NO_ERROR; return expect_quic_as(s, ctx, flags); } /* Same as expect_quic_cs except that errors are not raised if raiseerrs == 0 */ static int is_quic_cs(const SSL *s, QCTX *ctx, int raiseerrs) { uint32_t flags = QCTX_C | QCTX_S; if (!raiseerrs) flags |= QCTX_NO_ERROR; return expect_quic_as(s, ctx, flags); } static int expect_quic_cs(const SSL *s, QCTX *ctx) { return expect_quic_as(s, ctx, QCTX_C | QCTX_S); } static int expect_quic_csl(const SSL *s, QCTX *ctx) { return expect_quic_as(s, ctx, QCTX_C | QCTX_S | QCTX_L); } static int expect_quic_csld(const SSL *s, QCTX *ctx) { return expect_quic_as(s, ctx, QCTX_C | QCTX_S | QCTX_L | QCTX_D); } #define expect_quic_any expect_quic_csld static int expect_quic_listener(const SSL *s, QCTX *ctx) { return expect_quic_as(s, ctx, QCTX_L); } static int expect_quic_domain(const SSL *s, QCTX *ctx) { return expect_quic_as(s, ctx, QCTX_D); } /* * Like expect_quic_cs(), but requires a QUIC_XSO be contextually available. In * other words, requires that the passed QSO be a QSSO or a QCSO with a default * stream. * * remote_init determines if we expect the default XSO to be remotely created or * not. If it is -1, do not instantiate a default XSO if one does not yet exist. * * Channel mutex is acquired and retained on success. */ QUIC_ACQUIRES_LOCK static int ossl_unused expect_quic_with_stream_lock(const SSL *s, int remote_init, int in_io, QCTX *ctx) { uint32_t flags = QCTX_S | QCTX_LOCK; if (remote_init >= 0) flags |= QCTX_AUTO_S; if (remote_init > 0) flags |= QCTX_REMOTE_INIT; if (in_io) flags |= QCTX_IO; return expect_quic_as(s, ctx, flags); } /* * Like expect_quic_cs(), but fails if called on a QUIC_XSO. ctx->xso may still * be non-NULL if the QCSO has a default stream. */ static int ossl_unused expect_quic_conn_only(const SSL *s, QCTX *ctx) { return expect_quic_as(s, ctx, QCTX_C); } /* * Ensures that the domain mutex is held for a method which touches channel * state. * * Precondition: Domain mutex is not held (unchecked) */ static void qctx_lock(QCTX *ctx) { #if defined(OPENSSL_THREADS) assert(ctx->obj != NULL); ossl_crypto_mutex_lock(ossl_quic_obj_get0_mutex(ctx->obj)); #endif } /* Precondition: Channel mutex is held (unchecked) */ QUIC_NEEDS_LOCK static void qctx_unlock(QCTX *ctx) { #if defined(OPENSSL_THREADS) assert(ctx->obj != NULL); ossl_crypto_mutex_unlock(ossl_quic_obj_get0_mutex(ctx->obj)); #endif } static void qctx_lock_for_io(QCTX *ctx) { qctx_lock(ctx); ctx->in_io = 1; /* * We are entering an I/O function so we must update the values returned by * SSL_get_error and SSL_want. Set no error. This will be overridden later * if a call to QUIC_RAISE_NORMAL_ERROR or QUIC_RAISE_NON_NORMAL_ERROR * occurs during the API call. */ quic_set_last_error(ctx, SSL_ERROR_NONE); } /* * This predicate is the criterion which should determine API call rejection for * *most* mutating API calls, particularly stream-related operations for send * parts. * * A call is rejected (this function returns 0) if shutdown is in progress * (stream flushing), or we are in a TERMINATING or TERMINATED state. If * req_active=1, the connection must be active (i.e., the IDLE state is also * rejected). */ static int quic_mutation_allowed(QUIC_CONNECTION *qc, int req_active) { if (qc->shutting_down || ossl_quic_channel_is_term_any(qc->ch)) return 0; if (req_active && !ossl_quic_channel_is_active(qc->ch)) return 0; return 1; } static int qctx_is_top_level(QCTX *ctx) { return ctx->obj->parent_obj == NULL; } static int qctx_blocking(QCTX *ctx) { return ossl_quic_obj_blocking(ctx->obj); } /* * Block until a predicate is met. * * Precondition: Must have a channel. * Precondition: Must hold channel lock (unchecked). */ QUIC_NEEDS_LOCK static int block_until_pred(QCTX *ctx, int (*pred)(void *arg), void *pred_arg, uint32_t flags) { QUIC_ENGINE *qeng; QUIC_REACTOR *rtor; qeng = ossl_quic_obj_get0_engine(ctx->obj); assert(qeng != NULL); /* * Any attempt to block auto-disables tick inhibition as otherwise we will * hang around forever. */ ossl_quic_engine_set_inhibit_tick(qeng, 0); rtor = ossl_quic_engine_get0_reactor(qeng); return ossl_quic_reactor_block_until_pred(rtor, pred, pred_arg, flags); } /* * QUIC Front-End I/O API: Initialization * ====================================== * * SSL_new => ossl_quic_new * ossl_quic_init * SSL_reset => ossl_quic_reset * SSL_clear => ossl_quic_clear * ossl_quic_deinit * SSL_free => ossl_quic_free * * SSL_set_options => ossl_quic_set_options * SSL_get_options => ossl_quic_get_options * SSL_clear_options => ossl_quic_clear_options * */ /* SSL_new */ SSL *ossl_quic_new(SSL_CTX *ctx) { QUIC_CONNECTION *qc = NULL; SSL_CONNECTION *sc = NULL; /* * QUIC_server_method should not be used with SSL_new. * It should only be used with SSL_new_listener. */ if (ctx->method == OSSL_QUIC_server_method()) { QUIC_RAISE_NON_NORMAL_ERROR(NULL, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED, NULL); return NULL; } qc = OPENSSL_zalloc(sizeof(*qc)); if (qc == NULL) { QUIC_RAISE_NON_NORMAL_ERROR(NULL, ERR_R_CRYPTO_LIB, NULL); return NULL; } /* Create the QUIC domain mutex. */ #if defined(OPENSSL_THREADS) if ((qc->mutex = ossl_crypto_mutex_new()) == NULL) { QUIC_RAISE_NON_NORMAL_ERROR(NULL, ERR_R_CRYPTO_LIB, NULL); goto err; } #endif /* Create the handshake layer. */ qc->tls = ossl_ssl_connection_new_int(ctx, &qc->obj.ssl, TLS_method()); if (qc->tls == NULL || (sc = SSL_CONNECTION_FROM_SSL(qc->tls)) == NULL) { QUIC_RAISE_NON_NORMAL_ERROR(NULL, ERR_R_INTERNAL_ERROR, NULL); goto err; } /* override the user_ssl of the inner connection */ sc->s3.flags |= TLS1_FLAGS_QUIC | TLS1_FLAGS_QUIC_INTERNAL; /* Restrict options derived from the SSL_CTX. */ sc->options &= OSSL_QUIC_PERMITTED_OPTIONS_CONN; sc->pha_enabled = 0; /* Determine mode of operation. */ #if !defined(OPENSSL_NO_QUIC_THREAD_ASSIST) qc->is_thread_assisted = ((ctx->domain_flags & SSL_DOMAIN_FLAG_THREAD_ASSISTED) != 0); #endif qc->as_server = 0; qc->as_server_state = qc->as_server; if (!create_channel(qc, ctx)) goto err; ossl_quic_channel_set_msg_callback(qc->ch, ctx->msg_callback, &qc->obj.ssl); ossl_quic_channel_set_msg_callback_arg(qc->ch, ctx->msg_callback_arg); /* Initialise the QUIC_CONNECTION's QUIC_OBJ base. */ if (!ossl_quic_obj_init(&qc->obj, ctx, SSL_TYPE_QUIC_CONNECTION, NULL, qc->engine, qc->port)) { QUIC_RAISE_NON_NORMAL_ERROR(NULL, ERR_R_INTERNAL_ERROR, NULL); goto err; } /* Initialise libssl APL-related state. */ qc->default_stream_mode = SSL_DEFAULT_STREAM_MODE_AUTO_BIDI; qc->default_ssl_mode = qc->obj.ssl.ctx->mode; qc->default_ssl_options = qc->obj.ssl.ctx->options & OSSL_QUIC_PERMITTED_OPTIONS; qc->incoming_stream_policy = SSL_INCOMING_STREAM_POLICY_AUTO; qc->last_error = SSL_ERROR_NONE; qc_update_reject_policy(qc); /* * We do not create the default XSO yet. The reason for this is that the * stream ID of the default XSO will depend on whether the stream is client * or server-initiated, which depends on who transmits first. Since we do * not know whether the application will be using a client-transmits-first * or server-transmits-first protocol, we defer default XSO creation until * the client calls SSL_read() or SSL_write(). If it calls SSL_read() first, * we take that as a cue that the client is expecting a server-initiated * stream, and vice versa if SSL_write() is called first. */ return &qc->obj.ssl; err: if (qc != NULL) { qc_cleanup(qc, /*have_lock=*/0); OPENSSL_free(qc); } return NULL; } QUIC_NEEDS_LOCK static void quic_unref_port_bios(QUIC_PORT *port) { BIO *b; b = ossl_quic_port_get_net_rbio(port); BIO_free_all(b); b = ossl_quic_port_get_net_wbio(port); BIO_free_all(b); } QUIC_NEEDS_LOCK static void qc_cleanup(QUIC_CONNECTION *qc, int have_lock) { SSL_free(qc->tls); qc->tls = NULL; ossl_quic_channel_free(qc->ch); qc->ch = NULL; if (qc->port != NULL && qc->listener == NULL && qc->pending == 0) { /* TODO */ quic_unref_port_bios(qc->port); ossl_quic_port_free(qc->port); qc->port = NULL; ossl_quic_engine_free(qc->engine); qc->engine = NULL; } #if defined(OPENSSL_THREADS) if (have_lock) /* tsan doesn't like freeing locked mutexes */ ossl_crypto_mutex_unlock(qc->mutex); if (qc->listener == NULL && qc->pending == 0) ossl_crypto_mutex_free(&qc->mutex); #endif } /* SSL_free */ QUIC_TAKES_LOCK static void quic_free_listener(QCTX *ctx) { quic_unref_port_bios(ctx->ql->port); ossl_quic_port_drop_incoming(ctx->ql->port); ossl_quic_port_free(ctx->ql->port); if (ctx->ql->domain == NULL) { ossl_quic_engine_free(ctx->ql->engine); #if defined(OPENSSL_THREADS) ossl_crypto_mutex_free(&ctx->ql->mutex); #endif } else { SSL_free(&ctx->ql->domain->obj.ssl); } } /* SSL_free */ QUIC_TAKES_LOCK static void quic_free_domain(QCTX *ctx) { ossl_quic_engine_free(ctx->qd->engine); #if defined(OPENSSL_THREADS) ossl_crypto_mutex_free(&ctx->qd->mutex); #endif } QUIC_TAKES_LOCK void ossl_quic_free(SSL *s) { QCTX ctx; int is_default; /* We should never be called on anything but a QSO. */ if (!expect_quic_any(s, &ctx)) return; if (ctx.is_domain) { quic_free_domain(&ctx); return; } if (ctx.is_listener) { quic_free_listener(&ctx); return; } qctx_lock(&ctx); if (ctx.is_stream) { /* * When a QSSO is freed, the XSO is freed immediately, because the XSO * itself only contains API personality layer data. However the * underlying QUIC_STREAM is not freed immediately but is instead marked * as deleted for later collection. */ assert(ctx.qc->num_xso > 0); --ctx.qc->num_xso; /* If a stream's send part has not been finished, auto-reset it. */ if (( ctx.xso->stream->send_state == QUIC_SSTREAM_STATE_READY || ctx.xso->stream->send_state == QUIC_SSTREAM_STATE_SEND) && !ossl_quic_sstream_get_final_size(ctx.xso->stream->sstream, NULL)) ossl_quic_stream_map_reset_stream_send_part(ossl_quic_channel_get_qsm(ctx.qc->ch), ctx.xso->stream, 0); /* Do STOP_SENDING for the receive part, if applicable. */ if ( ctx.xso->stream->recv_state == QUIC_RSTREAM_STATE_RECV || ctx.xso->stream->recv_state == QUIC_RSTREAM_STATE_SIZE_KNOWN) ossl_quic_stream_map_stop_sending_recv_part(ossl_quic_channel_get_qsm(ctx.qc->ch), ctx.xso->stream, 0); /* Update stream state. */ ctx.xso->stream->deleted = 1; ossl_quic_stream_map_update_state(ossl_quic_channel_get_qsm(ctx.qc->ch), ctx.xso->stream); is_default = (ctx.xso == ctx.qc->default_xso); qctx_unlock(&ctx); /* * Unref the connection in most cases; the XSO has a ref to the QC and * not vice versa. But for a default XSO, to avoid circular references, * the QC refs the XSO but the XSO does not ref the QC. If we are the * default XSO, we only get here when the QC is being torn down anyway, * so don't call SSL_free(qc) as we are already in it. */ if (!is_default) SSL_free(&ctx.qc->obj.ssl); /* Note: SSL_free calls OPENSSL_free(xso) for us */ return; } /* * Free the default XSO, if any. The QUIC_STREAM is not deleted at this * stage, but is freed during the channel free when the whole QSM is freed. */ if (ctx.qc->default_xso != NULL) { QUIC_XSO *xso = ctx.qc->default_xso; qctx_unlock(&ctx); SSL_free(&xso->obj.ssl); qctx_lock(&ctx); ctx.qc->default_xso = NULL; } /* Ensure we have no remaining XSOs. */ assert(ctx.qc->num_xso == 0); #if !defined(OPENSSL_NO_QUIC_THREAD_ASSIST) if (ctx.qc->is_thread_assisted && ctx.qc->started) { ossl_quic_thread_assist_wait_stopped(&ctx.qc->thread_assist); ossl_quic_thread_assist_cleanup(&ctx.qc->thread_assist); } #endif /* * Note: SSL_free (that called this function) calls OPENSSL_free(ctx.qc) for * us */ qc_cleanup(ctx.qc, /*have_lock=*/1); /* Note: SSL_free calls OPENSSL_free(qc) for us */ if (ctx.qc->listener != NULL) SSL_free(&ctx.qc->listener->obj.ssl); if (ctx.qc->domain != NULL) SSL_free(&ctx.qc->domain->obj.ssl); } /* SSL method init */ int ossl_quic_init(SSL *s) { /* Same op as SSL_clear, forward the call. */ return ossl_quic_clear(s); } /* SSL method deinit */ void ossl_quic_deinit(SSL *s) { /* No-op. */ } /* SSL_clear (ssl_reset method) */ int ossl_quic_reset(SSL *s) { QCTX ctx; if (!expect_quic_any(s, &ctx)) return 0; ERR_raise(ERR_LIB_SSL, ERR_R_UNSUPPORTED); return 0; } /* ssl_clear method (unused) */ int ossl_quic_clear(SSL *s) { QCTX ctx; if (!expect_quic_any(s, &ctx)) return 0; ERR_raise(ERR_LIB_SSL, ERR_R_UNSUPPORTED); return 0; } int ossl_quic_set_override_now_cb(SSL *s, OSSL_TIME (*now_cb)(void *arg), void *now_cb_arg) { QCTX ctx; if (!expect_quic_any(s, &ctx)) return 0; qctx_lock(&ctx); ossl_quic_engine_set_time_cb(ctx.obj->engine, now_cb, now_cb_arg); qctx_unlock(&ctx); return 1; } void ossl_quic_conn_force_assist_thread_wake(SSL *s) { QCTX ctx; if (!expect_quic_conn_only(s, &ctx)) return; #if !defined(OPENSSL_NO_QUIC_THREAD_ASSIST) if (ctx.qc->is_thread_assisted && ctx.qc->started) ossl_quic_thread_assist_notify_deadline_changed(&ctx.qc->thread_assist); #endif } QUIC_NEEDS_LOCK static void qc_touch_default_xso(QUIC_CONNECTION *qc) { qc->default_xso_created = 1; qc_update_reject_policy(qc); } /* * Changes default XSO. Allows caller to keep reference to the old default XSO * (if any). Reference to new XSO is transferred from caller. */ QUIC_NEEDS_LOCK static void qc_set_default_xso_keep_ref(QUIC_CONNECTION *qc, QUIC_XSO *xso, int touch, QUIC_XSO **old_xso) { int refs; *old_xso = NULL; if (qc->default_xso != xso) { *old_xso = qc->default_xso; /* transfer old XSO ref to caller */ qc->default_xso = xso; if (xso == NULL) { /* * Changing to not having a default XSO. XSO becomes standalone and * now has a ref to the QC. */ if (!ossl_assert(SSL_up_ref(&qc->obj.ssl))) return; } else { /* * Changing from not having a default XSO to having one. The new XSO * will have had a reference to the QC we need to drop to avoid a * circular reference. * * Currently we never change directly from one default XSO to * another, though this function would also still be correct if this * weren't the case. */ assert(*old_xso == NULL); CRYPTO_DOWN_REF(&qc->obj.ssl.references, &refs); assert(refs > 0); } } if (touch) qc_touch_default_xso(qc); } /* * Changes default XSO, releasing the reference to any previous default XSO. * Reference to new XSO is transferred from caller. */ QUIC_NEEDS_LOCK static void qc_set_default_xso(QUIC_CONNECTION *qc, QUIC_XSO *xso, int touch) { QUIC_XSO *old_xso = NULL; qc_set_default_xso_keep_ref(qc, xso, touch, &old_xso); if (old_xso != NULL) SSL_free(&old_xso->obj.ssl); } QUIC_NEEDS_LOCK static void xso_update_options(QUIC_XSO *xso) { int cleanse = ((xso->ssl_options & SSL_OP_CLEANSE_PLAINTEXT) != 0); if (xso->stream->rstream != NULL) ossl_quic_rstream_set_cleanse(xso->stream->rstream, cleanse); if (xso->stream->sstream != NULL) ossl_quic_sstream_set_cleanse(xso->stream->sstream, cleanse); } /* * SSL_set_options * --------------- * * Setting options on a QCSO * - configures the handshake-layer options; * - configures the default data-plane options for new streams; * - configures the data-plane options on the default XSO, if there is one. * * Setting options on a QSSO * - configures data-plane options for that stream only. */ QUIC_TAKES_LOCK static uint64_t quic_mask_or_options(SSL *ssl, uint64_t mask_value, uint64_t or_value) { QCTX ctx; uint64_t hs_mask_value, hs_or_value, ret; if (!expect_quic_cs(ssl, &ctx)) return 0; qctx_lock(&ctx); if (!ctx.is_stream) { /* * If we were called on the connection, we apply any handshake option * changes. */ hs_mask_value = (mask_value & OSSL_QUIC_PERMITTED_OPTIONS_CONN); hs_or_value = (or_value & OSSL_QUIC_PERMITTED_OPTIONS_CONN); SSL_clear_options(ctx.qc->tls, hs_mask_value); SSL_set_options(ctx.qc->tls, hs_or_value); /* Update defaults for new streams. */ ctx.qc->default_ssl_options = ((ctx.qc->default_ssl_options & ~mask_value) | or_value) & OSSL_QUIC_PERMITTED_OPTIONS; } ret = ctx.qc->default_ssl_options; if (ctx.xso != NULL) { ctx.xso->ssl_options = ((ctx.xso->ssl_options & ~mask_value) | or_value) & OSSL_QUIC_PERMITTED_OPTIONS_STREAM; xso_update_options(ctx.xso); if (ctx.is_stream) ret = ctx.xso->ssl_options; } qctx_unlock(&ctx); return ret; } uint64_t ossl_quic_set_options(SSL *ssl, uint64_t options) { return quic_mask_or_options(ssl, 0, options); } /* SSL_clear_options */ uint64_t ossl_quic_clear_options(SSL *ssl, uint64_t options) { return quic_mask_or_options(ssl, options, 0); } /* SSL_get_options */ uint64_t ossl_quic_get_options(const SSL *ssl) { return quic_mask_or_options((SSL *)ssl, 0, 0); } /* * QUIC Front-End I/O API: Network BIO Configuration * ================================================= * * Handling the different BIOs is difficult: * * - It is more or less a requirement that we use non-blocking network I/O; * we need to be able to have timeouts on recv() calls, and make best effort * (non blocking) send() and recv() calls. * * The only sensible way to do this is to configure the socket into * non-blocking mode. We could try to do select() before calling send() or * recv() to get a guarantee that the call will not block, but this will * probably run into issues with buggy OSes which generate spurious socket * readiness events. In any case, relying on this to work reliably does not * seem sane. * * Timeouts could be handled via setsockopt() socket timeout options, but * this depends on OS support and adds another syscall to every network I/O * operation. It also has obvious thread safety concerns if we want to move * to concurrent use of a single socket at some later date. * * Some OSes support a MSG_DONTWAIT flag which allows a single I/O option to * be made non-blocking. However some OSes (e.g. Windows) do not support * this, so we cannot rely on this. * * As such, we need to configure any FD in non-blocking mode. This may * confound users who pass a blocking socket to libssl. However, in practice * it would be extremely strange for a user of QUIC to pass an FD to us, * then also try and send receive traffic on the same socket(!). Thus the * impact of this should be limited, and can be documented. * * - We support both blocking and non-blocking operation in terms of the API * presented to the user. One prospect is to set the blocking mode based on * whether the socket passed to us was already in blocking mode. However, * Windows has no API for determining if a socket is in blocking mode (!), * therefore this cannot be done portably. Currently therefore we expose an * explicit API call to set this, and default to blocking mode. * * - We need to determine our initial destination UDP address. The "natural" * way for a user to do this is to set the peer variable on a BIO_dgram. * However, this has problems because BIO_dgram's peer variable is used for * both transmission and reception. This means it can be constantly being * changed to a malicious value (e.g. if some random unrelated entity on the * network starts sending traffic to us) on every read call. This is not a * direct issue because we use the 'stateless' BIO_sendmmsg and BIO_recvmmsg * calls only, which do not use this variable. However, we do need to let * the user specify the peer in a 'normal' manner. The compromise here is * that we grab the current peer value set at the time the write BIO is set * and do not read the value again. * * - We also need to support memory BIOs (e.g. BIO_dgram_pair) or custom BIOs. * Currently we do this by only supporting non-blocking mode. * */ /* * Determines what initial destination UDP address we should use, if possible. * If this fails the client must set the destination address manually, or use a * BIO which does not need a destination address. */ static int csm_analyse_init_peer_addr(BIO *net_wbio, BIO_ADDR *peer) { if (BIO_dgram_detect_peer_addr(net_wbio, peer) <= 0) return 0; return 1; } static int quic_set0_net_rbio(QUIC_OBJ *obj, BIO *net_rbio) { QUIC_PORT *port; BIO *old_rbio = NULL; port = ossl_quic_obj_get0_port(obj); old_rbio = ossl_quic_port_get_net_rbio(port); if (old_rbio == net_rbio) return 0; if (!ossl_quic_port_set_net_rbio(port, net_rbio)) return 0; BIO_free_all(old_rbio); if (net_rbio != NULL) BIO_set_nbio(net_rbio, 1); /* best effort autoconfig */ return 1; } static int quic_set0_net_wbio(QUIC_OBJ *obj, BIO *net_wbio) { QUIC_PORT *port; BIO *old_wbio = NULL; port = ossl_quic_obj_get0_port(obj); old_wbio = ossl_quic_port_get_net_wbio(port); if (old_wbio == net_wbio) return 0; if (!ossl_quic_port_set_net_wbio(port, net_wbio)) return 0; BIO_free_all(old_wbio); if (net_wbio != NULL) BIO_set_nbio(net_wbio, 1); /* best effort autoconfig */ return 1; } void ossl_quic_conn_set0_net_rbio(SSL *s, BIO *net_rbio) { QCTX ctx; if (!expect_quic_csl(s, &ctx)) return; /* Returns 0 if no change. */ if (!quic_set0_net_rbio(ctx.obj, net_rbio)) return; } void ossl_quic_conn_set0_net_wbio(SSL *s, BIO *net_wbio) { QCTX ctx; if (!expect_quic_csl(s, &ctx)) return; /* Returns 0 if no change. */ if (!quic_set0_net_wbio(ctx.obj, net_wbio)) return; } BIO *ossl_quic_conn_get_net_rbio(const SSL *s) { QCTX ctx; QUIC_PORT *port; if (!expect_quic_csl(s, &ctx)) return NULL; port = ossl_quic_obj_get0_port(ctx.obj); assert(port != NULL); return ossl_quic_port_get_net_rbio(port); } BIO *ossl_quic_conn_get_net_wbio(const SSL *s) { QCTX ctx; QUIC_PORT *port; if (!expect_quic_csl(s, &ctx)) return NULL; port = ossl_quic_obj_get0_port(ctx.obj); assert(port != NULL); return ossl_quic_port_get_net_wbio(port); } int ossl_quic_conn_get_blocking_mode(const SSL *s) { QCTX ctx; if (!expect_quic_csl(s, &ctx)) return 0; return qctx_blocking(&ctx); } QUIC_TAKES_LOCK int ossl_quic_conn_set_blocking_mode(SSL *s, int blocking) { int ret = 0; unsigned int mode; QCTX ctx; if (!expect_quic_csl(s, &ctx)) return 0; qctx_lock(&ctx); /* Sanity check - can we support the request given the current network BIO? */ if (blocking) { /* * If called directly on a top-level object (QCSO or QLSO), update our * information on network BIO capabilities. */ if (qctx_is_top_level(&ctx)) ossl_quic_engine_update_poll_descriptors(ctx.obj->engine, /*force=*/1); /* Cannot enable blocking mode if we do not have pollable FDs. */ if (!ossl_quic_obj_can_support_blocking(ctx.obj)) { ret = QUIC_RAISE_NON_NORMAL_ERROR(&ctx, ERR_R_UNSUPPORTED, NULL); goto out; } } mode = (blocking != 0) ? QUIC_BLOCKING_MODE_BLOCKING : QUIC_BLOCKING_MODE_NONBLOCKING; ossl_quic_obj_set_blocking_mode(ctx.obj, mode); ret = 1; out: qctx_unlock(&ctx); return ret; } int ossl_quic_conn_set_initial_peer_addr(SSL *s, const BIO_ADDR *peer_addr) { QCTX ctx; if (!expect_quic_cs(s, &ctx)) return 0; if (ctx.qc->started) return QUIC_RAISE_NON_NORMAL_ERROR(&ctx, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED, NULL); if (peer_addr == NULL) { BIO_ADDR_clear(&ctx.qc->init_peer_addr); return 1; } return BIO_ADDR_copy(&ctx.qc->init_peer_addr, peer_addr); } /* * QUIC Front-End I/O API: Asynchronous I/O Management * =================================================== * * (BIO/)SSL_handle_events => ossl_quic_handle_events * (BIO/)SSL_get_event_timeout => ossl_quic_get_event_timeout * (BIO/)SSL_get_poll_fd => ossl_quic_get_poll_fd * */ /* SSL_handle_events; performs QUIC I/O and timeout processing. */ QUIC_TAKES_LOCK int ossl_quic_handle_events(SSL *s) { QCTX ctx; if (!expect_quic_any(s, &ctx)) return 0; qctx_lock(&ctx); ossl_quic_reactor_tick(ossl_quic_obj_get0_reactor(ctx.obj), 0); qctx_unlock(&ctx); return 1; } /* * SSL_get_event_timeout. Get the time in milliseconds until the SSL object * should next have events handled by the application by calling * SSL_handle_events(). tv is set to 0 if the object should have events handled * immediately. If no timeout is currently active, *is_infinite is set to 1 and * the value of *tv is undefined. */ QUIC_TAKES_LOCK int ossl_quic_get_event_timeout(SSL *s, struct timeval *tv, int *is_infinite) { QCTX ctx; QUIC_REACTOR *reactor; OSSL_TIME deadline; OSSL_TIME basetime; if (!expect_quic_any(s, &ctx)) return 0; qctx_lock(&ctx); reactor = ossl_quic_obj_get0_reactor(ctx.obj); deadline = ossl_quic_reactor_get_tick_deadline(reactor); if (ossl_time_is_infinite(deadline)) { qctx_unlock(&ctx); *is_infinite = 1; /* * Robustness against faulty applications that don't check *is_infinite; * harmless long timeout. */ tv->tv_sec = 1000000; tv->tv_usec = 0; return 1; } basetime = ossl_quic_engine_get_time(ctx.obj->engine); qctx_unlock(&ctx); *tv = ossl_time_to_timeval(ossl_time_subtract(deadline, basetime)); *is_infinite = 0; return 1; } /* SSL_get_rpoll_descriptor */ int ossl_quic_get_rpoll_descriptor(SSL *s, BIO_POLL_DESCRIPTOR *desc) { QCTX ctx; QUIC_PORT *port = NULL; BIO *net_rbio; if (!expect_quic_csl(s, &ctx)) return 0; port = ossl_quic_obj_get0_port(ctx.obj); net_rbio = ossl_quic_port_get_net_rbio(port); if (desc == NULL || net_rbio == NULL) return QUIC_RAISE_NON_NORMAL_ERROR(&ctx, ERR_R_PASSED_INVALID_ARGUMENT, NULL); return BIO_get_rpoll_descriptor(net_rbio, desc); } /* SSL_get_wpoll_descriptor */ int ossl_quic_get_wpoll_descriptor(SSL *s, BIO_POLL_DESCRIPTOR *desc) { QCTX ctx; QUIC_PORT *port = NULL; BIO *net_wbio; if (!expect_quic_csl(s, &ctx)) return 0; port = ossl_quic_obj_get0_port(ctx.obj); net_wbio = ossl_quic_port_get_net_wbio(port); if (desc == NULL || net_wbio == NULL) return QUIC_RAISE_NON_NORMAL_ERROR(&ctx, ERR_R_PASSED_INVALID_ARGUMENT, NULL); return BIO_get_wpoll_descriptor(net_wbio, desc); } /* SSL_net_read_desired */ QUIC_TAKES_LOCK int ossl_quic_get_net_read_desired(SSL *s) { QCTX ctx; int ret; if (!expect_quic_csl(s, &ctx)) return 0; qctx_lock(&ctx); ret = ossl_quic_reactor_net_read_desired(ossl_quic_obj_get0_reactor(ctx.obj)); qctx_unlock(&ctx); return ret; } /* SSL_net_write_desired */ QUIC_TAKES_LOCK int ossl_quic_get_net_write_desired(SSL *s) { int ret; QCTX ctx; if (!expect_quic_csl(s, &ctx)) return 0; qctx_lock(&ctx); ret = ossl_quic_reactor_net_write_desired(ossl_quic_obj_get0_reactor(ctx.obj)); qctx_unlock(&ctx); return ret; } /* * QUIC Front-End I/O API: Connection Lifecycle Operations * ======================================================= * * SSL_do_handshake => ossl_quic_do_handshake * SSL_set_connect_state => ossl_quic_set_connect_state * SSL_set_accept_state => ossl_quic_set_accept_state * SSL_shutdown => ossl_quic_shutdown * SSL_ctrl => ossl_quic_ctrl * (BIO/)SSL_connect => ossl_quic_connect * (BIO/)SSL_accept => ossl_quic_accept * */ QUIC_NEEDS_LOCK static void qc_shutdown_flush_init(QUIC_CONNECTION *qc) { QUIC_STREAM_MAP *qsm; if (qc->shutting_down) return; qsm = ossl_quic_channel_get_qsm(qc->ch); ossl_quic_stream_map_begin_shutdown_flush(qsm); qc->shutting_down = 1; } /* Returns 1 if all shutdown-flush streams have been done with. */ QUIC_NEEDS_LOCK static int qc_shutdown_flush_finished(QUIC_CONNECTION *qc) { QUIC_STREAM_MAP *qsm = ossl_quic_channel_get_qsm(qc->ch); return qc->shutting_down && ossl_quic_stream_map_is_shutdown_flush_finished(qsm); } /* SSL_shutdown */ static int quic_shutdown_wait(void *arg) { QUIC_CONNECTION *qc = arg; return ossl_quic_channel_is_terminated(qc->ch); } /* Returns 1 if shutdown flush process has finished or is inapplicable. */ static int quic_shutdown_flush_wait(void *arg) { QUIC_CONNECTION *qc = arg; return ossl_quic_channel_is_term_any(qc->ch) || qc_shutdown_flush_finished(qc); } static int quic_shutdown_peer_wait(void *arg) { QUIC_CONNECTION *qc = arg; return ossl_quic_channel_is_term_any(qc->ch); } QUIC_TAKES_LOCK int ossl_quic_conn_shutdown(SSL *s, uint64_t flags, const SSL_SHUTDOWN_EX_ARGS *args, size_t args_len) { int ret; QCTX ctx; int stream_flush = ((flags & SSL_SHUTDOWN_FLAG_NO_STREAM_FLUSH) == 0); int no_block = ((flags & SSL_SHUTDOWN_FLAG_NO_BLOCK) != 0); int wait_peer = ((flags & SSL_SHUTDOWN_FLAG_WAIT_PEER) != 0); if (!expect_quic_cs(s, &ctx)) return -1; if (ctx.is_stream) { QUIC_RAISE_NON_NORMAL_ERROR(&ctx, SSL_R_CONN_USE_ONLY, NULL); return -1; } qctx_lock(&ctx); if (ossl_quic_channel_is_terminated(ctx.qc->ch)) { qctx_unlock(&ctx); return 1; } /* Phase 1: Stream Flushing */ if (!wait_peer && stream_flush) { qc_shutdown_flush_init(ctx.qc); if (!qc_shutdown_flush_finished(ctx.qc)) { if (!no_block && qctx_blocking(&ctx)) { ret = block_until_pred(&ctx, quic_shutdown_flush_wait, ctx.qc, 0); if (ret < 1) { ret = 0; goto err; } } else { qctx_maybe_autotick(&ctx); } } if (!qc_shutdown_flush_finished(ctx.qc)) { qctx_unlock(&ctx); return 0; /* ongoing */ } } /* Phase 2: Connection Closure */ if (wait_peer && !ossl_quic_channel_is_term_any(ctx.qc->ch)) { if (!no_block && qctx_blocking(&ctx)) { ret = block_until_pred(&ctx, quic_shutdown_peer_wait, ctx.qc, 0); if (ret < 1) { ret = 0; goto err; } } else { qctx_maybe_autotick(&ctx); } if (!ossl_quic_channel_is_term_any(ctx.qc->ch)) { ret = 0; /* peer hasn't closed yet - still not done */ goto err; } /* * We are at least terminating - go through the normal process of * waiting until we are in the TERMINATED state. */ } /* Block mutation ops regardless of if we did stream flush. */ ctx.qc->shutting_down = 1; /* * This call is a no-op if we are already terminating, so it doesn't * affect the wait_peer case. */ ossl_quic_channel_local_close(ctx.qc->ch, args != NULL ? args->quic_error_code : 0, args != NULL ? args->quic_reason : NULL); SSL_set_shutdown(ctx.qc->tls, SSL_SENT_SHUTDOWN); if (ossl_quic_channel_is_terminated(ctx.qc->ch)) { qctx_unlock(&ctx); return 1; } /* Phase 3: Terminating Wait Time */ if (!no_block && qctx_blocking(&ctx) && (flags & SSL_SHUTDOWN_FLAG_RAPID) == 0) { ret = block_until_pred(&ctx, quic_shutdown_wait, ctx.qc, 0); if (ret < 1) { ret = 0; goto err; } } else { qctx_maybe_autotick(&ctx); } ret = ossl_quic_channel_is_terminated(ctx.qc->ch); err: qctx_unlock(&ctx); return ret; } /* SSL_ctrl */ long ossl_quic_ctrl(SSL *s, int cmd, long larg, void *parg) { QCTX ctx; if (!expect_quic_csl(s, &ctx)) return 0; switch (cmd) { case SSL_CTRL_MODE: if (ctx.is_listener) return QUIC_RAISE_NON_NORMAL_ERROR(&ctx, ERR_R_UNSUPPORTED, NULL); /* If called on a QCSO, update the default mode. */ if (!ctx.is_stream) ctx.qc->default_ssl_mode |= (uint32_t)larg; /* * If we were called on a QSSO or have a default stream, we also update * that. */ if (ctx.xso != NULL) { /* Cannot enable EPW while AON write in progress. */ if (ctx.xso->aon_write_in_progress) larg &= ~SSL_MODE_ENABLE_PARTIAL_WRITE; ctx.xso->ssl_mode |= (uint32_t)larg; return ctx.xso->ssl_mode; } return ctx.qc->default_ssl_mode; case SSL_CTRL_CLEAR_MODE: if (ctx.is_listener) return QUIC_RAISE_NON_NORMAL_ERROR(&ctx, ERR_R_UNSUPPORTED, NULL); if (!ctx.is_stream) ctx.qc->default_ssl_mode &= ~(uint32_t)larg; if (ctx.xso != NULL) { ctx.xso->ssl_mode &= ~(uint32_t)larg; return ctx.xso->ssl_mode; } return ctx.qc->default_ssl_mode; case SSL_CTRL_SET_MSG_CALLBACK_ARG: if (ctx.is_listener) return QUIC_RAISE_NON_NORMAL_ERROR(&ctx, ERR_R_UNSUPPORTED, NULL); ossl_quic_channel_set_msg_callback_arg(ctx.qc->ch, parg); /* This ctrl also needs to be passed to the internal SSL object */ return SSL_ctrl(ctx.qc->tls, cmd, larg, parg); case DTLS_CTRL_GET_TIMEOUT: /* DTLSv1_get_timeout */ { int is_infinite; if (!ossl_quic_get_event_timeout(s, parg, &is_infinite)) return 0; return !is_infinite; } case DTLS_CTRL_HANDLE_TIMEOUT: /* DTLSv1_handle_timeout */ /* For legacy compatibility with DTLS calls. */ return ossl_quic_handle_events(s) == 1 ? 1 : -1; /* Mask ctrls we shouldn't support for QUIC. */ case SSL_CTRL_GET_READ_AHEAD: case SSL_CTRL_SET_READ_AHEAD: case SSL_CTRL_SET_MAX_SEND_FRAGMENT: case SSL_CTRL_SET_SPLIT_SEND_FRAGMENT: case SSL_CTRL_SET_MAX_PIPELINES: return 0; default: /* * Probably a TLS related ctrl. Send back to the frontend SSL_ctrl * implementation. Either SSL_ctrl will handle it itself by direct * access into handshake layer state, or failing that, it will be passed * to the handshake layer via the SSL_METHOD vtable. If the ctrl is not * supported by anything, the handshake layer's ctrl method will finally * return 0. */ if (ctx.is_listener) return QUIC_RAISE_NON_NORMAL_ERROR(&ctx, ERR_R_UNSUPPORTED, NULL); return ossl_ctrl_internal(&ctx.qc->obj.ssl, cmd, larg, parg, /*no_quic=*/1); } } /* SSL_set_connect_state */ int ossl_quic_set_connect_state(SSL *s, int raiseerrs) { QCTX ctx; if (!is_quic_c(s, &ctx, raiseerrs)) return 0; if (ctx.qc->as_server_state == 0) return 1; /* Cannot be changed after handshake started */ if (ctx.qc->started) { if (raiseerrs) QUIC_RAISE_NON_NORMAL_ERROR(NULL, SSL_R_INVALID_COMMAND, NULL); return 0; } ctx.qc->as_server_state = 0; return 1; } /* SSL_set_accept_state */ int ossl_quic_set_accept_state(SSL *s, int raiseerrs) { QCTX ctx; if (!is_quic_c(s, &ctx, raiseerrs)) return 0; if (ctx.qc->as_server_state == 1) return 1; /* Cannot be changed after handshake started */ if (ctx.qc->started) { if (raiseerrs) QUIC_RAISE_NON_NORMAL_ERROR(NULL, SSL_R_INVALID_COMMAND, NULL); return 0; } ctx.qc->as_server_state = 1; return 1; } /* SSL_do_handshake */ struct quic_handshake_wait_args { QUIC_CONNECTION *qc; }; static int tls_wants_non_io_retry(QUIC_CONNECTION *qc) { int want = SSL_want(qc->tls); if (want == SSL_X509_LOOKUP || want == SSL_CLIENT_HELLO_CB || want == SSL_RETRY_VERIFY) return 1; return 0; } static int quic_handshake_wait(void *arg) { struct quic_handshake_wait_args *args = arg; if (!quic_mutation_allowed(args->qc, /*req_active=*/1)) return -1; if (ossl_quic_channel_is_handshake_complete(args->qc->ch)) return 1; if (tls_wants_non_io_retry(args->qc)) return 1; return 0; } static int configure_channel(QUIC_CONNECTION *qc) { assert(qc->ch != NULL); if (!ossl_quic_channel_set_peer_addr(qc->ch, &qc->init_peer_addr)) return 0; return 1; } static int need_notifier_for_domain_flags(uint64_t domain_flags) { return (domain_flags & SSL_DOMAIN_FLAG_THREAD_ASSISTED) != 0 || ((domain_flags & SSL_DOMAIN_FLAG_MULTI_THREAD) != 0 && (domain_flags & SSL_DOMAIN_FLAG_BLOCKING) != 0); } QUIC_NEEDS_LOCK static int create_channel(QUIC_CONNECTION *qc, SSL_CTX *ctx) { QUIC_ENGINE_ARGS engine_args = {0}; QUIC_PORT_ARGS port_args = {0}; engine_args.libctx = ctx->libctx; engine_args.propq = ctx->propq; #if defined(OPENSSL_THREADS) engine_args.mutex = qc->mutex; #endif if (need_notifier_for_domain_flags(ctx->domain_flags)) engine_args.reactor_flags |= QUIC_REACTOR_FLAG_USE_NOTIFIER; qc->engine = ossl_quic_engine_new(&engine_args); if (qc->engine == NULL) { QUIC_RAISE_NON_NORMAL_ERROR(NULL, ERR_R_INTERNAL_ERROR, NULL); return 0; } port_args.channel_ctx = ctx; qc->port = ossl_quic_engine_create_port(qc->engine, &port_args); if (qc->port == NULL) { QUIC_RAISE_NON_NORMAL_ERROR(NULL, ERR_R_INTERNAL_ERROR, NULL); ossl_quic_engine_free(qc->engine); return 0; } qc->ch = ossl_quic_port_create_outgoing(qc->port, qc->tls); if (qc->ch == NULL) { QUIC_RAISE_NON_NORMAL_ERROR(NULL, ERR_R_INTERNAL_ERROR, NULL); ossl_quic_port_free(qc->port); ossl_quic_engine_free(qc->engine); return 0; } return 1; } /* * Configures a channel with the information we have accumulated via calls made * to us from the application prior to starting a handshake attempt. */ QUIC_NEEDS_LOCK static int ensure_channel_started(QCTX *ctx) { QUIC_CONNECTION *qc = ctx->qc; if (!qc->started) { if (!configure_channel(qc)) { QUIC_RAISE_NON_NORMAL_ERROR(ctx, ERR_R_INTERNAL_ERROR, "failed to configure channel"); return 0; } if (!ossl_quic_channel_start(qc->ch)) { ossl_quic_channel_restore_err_state(qc->ch); QUIC_RAISE_NON_NORMAL_ERROR(ctx, ERR_R_INTERNAL_ERROR, "failed to start channel"); return 0; } #if !defined(OPENSSL_NO_QUIC_THREAD_ASSIST) if (qc->is_thread_assisted) if (!ossl_quic_thread_assist_init_start(&qc->thread_assist, qc->ch)) { QUIC_RAISE_NON_NORMAL_ERROR(ctx, ERR_R_INTERNAL_ERROR, "failed to start assist thread"); return 0; } #endif } qc->started = 1; return 1; } QUIC_NEEDS_LOCK static int quic_do_handshake(QCTX *ctx) { int ret; QUIC_CONNECTION *qc = ctx->qc; QUIC_PORT *port; BIO *net_rbio, *net_wbio; if (ossl_quic_channel_is_handshake_complete(qc->ch)) /* Handshake already completed. */ return 1; if (!quic_mutation_allowed(qc, /*req_active=*/0)) return QUIC_RAISE_NON_NORMAL_ERROR(ctx, SSL_R_PROTOCOL_IS_SHUTDOWN, NULL); if (qc->as_server != qc->as_server_state) { QUIC_RAISE_NON_NORMAL_ERROR(ctx, ERR_R_PASSED_INVALID_ARGUMENT, NULL); return -1; /* Non-protocol error */ } port = ossl_quic_obj_get0_port(ctx->obj); net_rbio = ossl_quic_port_get_net_rbio(port); net_wbio = ossl_quic_port_get_net_wbio(port); if (net_rbio == NULL || net_wbio == NULL) { /* Need read and write BIOs. */ QUIC_RAISE_NON_NORMAL_ERROR(ctx, SSL_R_BIO_NOT_SET, NULL); return -1; /* Non-protocol error */ } if (!qc->started && ossl_quic_port_is_addressed_w(port) && BIO_ADDR_family(&qc->init_peer_addr) == AF_UNSPEC) { /* * We are trying to connect and are using addressed mode, which means we * need an initial peer address; if we do not have a peer address yet, * we should try to autodetect one. * * We do this as late as possible because some BIOs (e.g. BIO_s_connect) * may not be able to provide us with a peer address until they have * finished their own processing. They may not be able to perform this * processing until an application has finished configuring that BIO * (e.g. with setter calls), which might happen after SSL_set_bio is * called. */ if (!csm_analyse_init_peer_addr(net_wbio, &qc->init_peer_addr)) /* best effort */ BIO_ADDR_clear(&qc->init_peer_addr); else ossl_quic_channel_set_peer_addr(qc->ch, &qc->init_peer_addr); } if (!qc->started && ossl_quic_port_is_addressed_w(port) && BIO_ADDR_family(&qc->init_peer_addr) == AF_UNSPEC) { /* * If we still don't have a peer address in addressed mode, we can't do * anything. */ QUIC_RAISE_NON_NORMAL_ERROR(ctx, SSL_R_REMOTE_PEER_ADDRESS_NOT_SET, NULL); return -1; /* Non-protocol error */ } /* * Start connection process. Note we may come here multiple times in * non-blocking mode, which is fine. */ if (!ensure_channel_started(ctx)) /* raises on failure */ return -1; /* Non-protocol error */ if (ossl_quic_channel_is_handshake_complete(qc->ch)) /* The handshake is now done. */ return 1; if (!qctx_blocking(ctx)) { /* Try to advance the reactor. */ qctx_maybe_autotick(ctx); if (ossl_quic_channel_is_handshake_complete(qc->ch)) /* The handshake is now done. */ return 1; if (ossl_quic_channel_is_term_any(qc->ch)) { QUIC_RAISE_NON_NORMAL_ERROR(ctx, SSL_R_PROTOCOL_IS_SHUTDOWN, NULL); return 0; } else if (ossl_quic_obj_desires_blocking(&qc->obj)) { /* * As a special case when doing a handshake when blocking mode is * desired yet not available, see if the network BIOs have become * poll descriptor-enabled. This supports BIOs such as BIO_s_connect * which do late creation of socket FDs and therefore cannot expose * a poll descriptor until after a network BIO is set on the QCSO. */ ossl_quic_engine_update_poll_descriptors(qc->obj.engine, /*force=*/1); } } /* * We are either in blocking mode or just entered it due to the code above. */ if (qctx_blocking(ctx)) { /* In blocking mode, wait for the handshake to complete. */ struct quic_handshake_wait_args args; args.qc = qc; ret = block_until_pred(ctx, quic_handshake_wait, &args, 0); if (!quic_mutation_allowed(qc, /*req_active=*/1)) { QUIC_RAISE_NON_NORMAL_ERROR(ctx, SSL_R_PROTOCOL_IS_SHUTDOWN, NULL); return 0; /* Shutdown before completion */ } else if (ret <= 0) { QUIC_RAISE_NON_NORMAL_ERROR(ctx, ERR_R_INTERNAL_ERROR, NULL); return -1; /* Non-protocol error */ } if (tls_wants_non_io_retry(qc)) { QUIC_RAISE_NORMAL_ERROR(ctx, SSL_get_error(qc->tls, 0)); return -1; } assert(ossl_quic_channel_is_handshake_complete(qc->ch)); return 1; } if (tls_wants_non_io_retry(qc)) { QUIC_RAISE_NORMAL_ERROR(ctx, SSL_get_error(qc->tls, 0)); return -1; } /* * Otherwise, indicate that the handshake isn't done yet. * We can only get here in non-blocking mode. */ QUIC_RAISE_NORMAL_ERROR(ctx, SSL_ERROR_WANT_READ); return -1; /* Non-protocol error */ } QUIC_TAKES_LOCK int ossl_quic_do_handshake(SSL *s) { int ret; QCTX ctx; if (!expect_quic_cs(s, &ctx)) return 0; qctx_lock_for_io(&ctx); ret = quic_do_handshake(&ctx); qctx_unlock(&ctx); return ret; } /* SSL_connect */ int ossl_quic_connect(SSL *s) { /* Ensure we are in connect state (no-op if non-idle). */ if (!ossl_quic_set_connect_state(s, 1)) return -1; /* Begin or continue the handshake */ return ossl_quic_do_handshake(s); } /* SSL_accept */ int ossl_quic_accept(SSL *s) { /* Ensure we are in accept state (no-op if non-idle). */ if (!ossl_quic_set_accept_state(s, 1)) return -1; /* Begin or continue the handshake */ return ossl_quic_do_handshake(s); } /* * QUIC Front-End I/O API: Stream Lifecycle Operations * =================================================== * * SSL_stream_new => ossl_quic_conn_stream_new * */ /* * Try to create the default XSO if it doesn't already exist. Returns 1 if the * default XSO was created. Returns 0 if it was not (e.g. because it already * exists). Note that this is NOT an error condition. */ QUIC_NEEDS_LOCK static int qc_try_create_default_xso_for_write(QCTX *ctx) { uint64_t flags = 0; QUIC_CONNECTION *qc = ctx->qc; if (qc->default_xso_created || qc->default_stream_mode == SSL_DEFAULT_STREAM_MODE_NONE) /* * We only do this once. If the user detaches a previously created * default XSO we don't auto-create another one. */ return QUIC_RAISE_NON_NORMAL_ERROR(ctx, SSL_R_NO_STREAM, NULL); /* Create a locally-initiated stream. */ if (qc->default_stream_mode == SSL_DEFAULT_STREAM_MODE_AUTO_UNI) flags |= SSL_STREAM_FLAG_UNI; qc_set_default_xso(qc, (QUIC_XSO *)quic_conn_stream_new(ctx, flags, /*needs_lock=*/0), /*touch=*/0); if (qc->default_xso == NULL) return QUIC_RAISE_NON_NORMAL_ERROR(ctx, ERR_R_INTERNAL_ERROR, NULL); qc_touch_default_xso(qc); return 1; } struct quic_wait_for_stream_args { QUIC_CONNECTION *qc; QUIC_STREAM *qs; QCTX *ctx; uint64_t expect_id; }; QUIC_NEEDS_LOCK static int quic_wait_for_stream(void *arg) { struct quic_wait_for_stream_args *args = arg; if (!quic_mutation_allowed(args->qc, /*req_active=*/1)) { /* If connection is torn down due to an error while blocking, stop. */ QUIC_RAISE_NON_NORMAL_ERROR(args->ctx, SSL_R_PROTOCOL_IS_SHUTDOWN, NULL); return -1; } args->qs = ossl_quic_stream_map_get_by_id(ossl_quic_channel_get_qsm(args->qc->ch), args->expect_id | QUIC_STREAM_DIR_BIDI); if (args->qs == NULL) args->qs = ossl_quic_stream_map_get_by_id(ossl_quic_channel_get_qsm(args->qc->ch), args->expect_id | QUIC_STREAM_DIR_UNI); if (args->qs != NULL) return 1; /* stream now exists */ return 0; /* did not get a stream, keep trying */ } QUIC_NEEDS_LOCK static int qc_wait_for_default_xso_for_read(QCTX *ctx, int peek) { /* Called on a QCSO and we don't currently have a default stream. */ uint64_t expect_id; QUIC_CONNECTION *qc = ctx->qc; QUIC_STREAM *qs; int res; struct quic_wait_for_stream_args wargs; OSSL_RTT_INFO rtt_info; /* * If default stream functionality is disabled or we already detached * one, don't make another default stream and just fail. */ if (qc->default_xso_created || qc->default_stream_mode == SSL_DEFAULT_STREAM_MODE_NONE) return QUIC_RAISE_NON_NORMAL_ERROR(ctx, SSL_R_NO_STREAM, NULL); /* * The peer may have opened a stream since we last ticked. So tick and * see if the stream with ordinal 0 (remote, bidi/uni based on stream * mode) exists yet. QUIC stream IDs must be allocated in order, so the * first stream created by a peer must have an ordinal of 0. */ expect_id = qc->as_server ? QUIC_STREAM_INITIATOR_CLIENT : QUIC_STREAM_INITIATOR_SERVER; qs = ossl_quic_stream_map_get_by_id(ossl_quic_channel_get_qsm(qc->ch), expect_id | QUIC_STREAM_DIR_BIDI); if (qs == NULL) qs = ossl_quic_stream_map_get_by_id(ossl_quic_channel_get_qsm(qc->ch), expect_id | QUIC_STREAM_DIR_UNI); if (qs == NULL) { qctx_maybe_autotick(ctx); qs = ossl_quic_stream_map_get_by_id(ossl_quic_channel_get_qsm(qc->ch), expect_id); } if (qs == NULL) { if (peek) return 0; if (ossl_quic_channel_is_term_any(qc->ch)) { return QUIC_RAISE_NON_NORMAL_ERROR(ctx, SSL_R_PROTOCOL_IS_SHUTDOWN, NULL); } else if (!qctx_blocking(ctx)) { /* Non-blocking mode, so just bail immediately. */ return QUIC_RAISE_NORMAL_ERROR(ctx, SSL_ERROR_WANT_READ); } /* Block until we have a stream. */ wargs.qc = qc; wargs.qs = NULL; wargs.ctx = ctx; wargs.expect_id = expect_id; res = block_until_pred(ctx, quic_wait_for_stream, &wargs, 0); if (res == 0) return QUIC_RAISE_NON_NORMAL_ERROR(ctx, ERR_R_INTERNAL_ERROR, NULL); else if (res < 0 || wargs.qs == NULL) /* quic_wait_for_stream raised error here */ return 0; qs = wargs.qs; } /* * We now have qs != NULL. Remove it from the incoming stream queue so that * it isn't also returned by any future SSL_accept_stream calls. */ ossl_statm_get_rtt_info(ossl_quic_channel_get_statm(qc->ch), &rtt_info); ossl_quic_stream_map_remove_from_accept_queue(ossl_quic_channel_get_qsm(qc->ch), qs, rtt_info.smoothed_rtt); /* * Now make qs the default stream, creating the necessary XSO. */ qc_set_default_xso(qc, create_xso_from_stream(qc, qs), /*touch=*/0); if (qc->default_xso == NULL) return QUIC_RAISE_NON_NORMAL_ERROR(ctx, ERR_R_INTERNAL_ERROR, NULL); qc_touch_default_xso(qc); /* inhibits default XSO */ return 1; } QUIC_NEEDS_LOCK static QUIC_XSO *create_xso_from_stream(QUIC_CONNECTION *qc, QUIC_STREAM *qs) { QUIC_XSO *xso = NULL; if ((xso = OPENSSL_zalloc(sizeof(*xso))) == NULL) { QUIC_RAISE_NON_NORMAL_ERROR(NULL, ERR_R_CRYPTO_LIB, NULL); goto err; } if (!ossl_quic_obj_init(&xso->obj, qc->obj.ssl.ctx, SSL_TYPE_QUIC_XSO, &qc->obj.ssl, NULL, NULL)) { QUIC_RAISE_NON_NORMAL_ERROR(NULL, ERR_R_INTERNAL_ERROR, NULL); goto err; } /* XSO refs QC */ if (!SSL_up_ref(&qc->obj.ssl)) { QUIC_RAISE_NON_NORMAL_ERROR(NULL, ERR_R_SSL_LIB, NULL); goto err; } xso->conn = qc; xso->ssl_mode = qc->default_ssl_mode; xso->ssl_options = qc->default_ssl_options & OSSL_QUIC_PERMITTED_OPTIONS_STREAM; xso->last_error = SSL_ERROR_NONE; xso->stream = qs; ++qc->num_xso; xso_update_options(xso); return xso; err: OPENSSL_free(xso); return NULL; } struct quic_new_stream_wait_args { QUIC_CONNECTION *qc; int is_uni; }; static int quic_new_stream_wait(void *arg) { struct quic_new_stream_wait_args *args = arg; QUIC_CONNECTION *qc = args->qc; if (!quic_mutation_allowed(qc, /*req_active=*/1)) return -1; if (ossl_quic_channel_is_new_local_stream_admissible(qc->ch, args->is_uni)) return 1; return 0; } /* locking depends on need_lock */ static SSL *quic_conn_stream_new(QCTX *ctx, uint64_t flags, int need_lock) { int ret; QUIC_CONNECTION *qc = ctx->qc; QUIC_XSO *xso = NULL; QUIC_STREAM *qs = NULL; int is_uni = ((flags & SSL_STREAM_FLAG_UNI) != 0); int no_blocking = ((flags & SSL_STREAM_FLAG_NO_BLOCK) != 0); int advance = ((flags & SSL_STREAM_FLAG_ADVANCE) != 0); if (need_lock) qctx_lock(ctx); if (!quic_mutation_allowed(qc, /*req_active=*/0)) { QUIC_RAISE_NON_NORMAL_ERROR(ctx, SSL_R_PROTOCOL_IS_SHUTDOWN, NULL); goto err; } if (!advance && !ossl_quic_channel_is_new_local_stream_admissible(qc->ch, is_uni)) { struct quic_new_stream_wait_args args; /* * Stream count flow control currently doesn't permit this stream to be * opened. */ if (no_blocking || !qctx_blocking(ctx)) { QUIC_RAISE_NON_NORMAL_ERROR(ctx, SSL_R_STREAM_COUNT_LIMITED, NULL); goto err; } args.qc = qc; args.is_uni = is_uni; /* Blocking mode - wait until we can get a stream. */ ret = block_until_pred(ctx, quic_new_stream_wait, &args, 0); if (!quic_mutation_allowed(qc, /*req_active=*/1)) { QUIC_RAISE_NON_NORMAL_ERROR(ctx, SSL_R_PROTOCOL_IS_SHUTDOWN, NULL); goto err; /* Shutdown before completion */ } else if (ret <= 0) { QUIC_RAISE_NON_NORMAL_ERROR(ctx, ERR_R_INTERNAL_ERROR, NULL); goto err; /* Non-protocol error */ } } qs = ossl_quic_channel_new_stream_local(qc->ch, is_uni); if (qs == NULL) { QUIC_RAISE_NON_NORMAL_ERROR(ctx, ERR_R_INTERNAL_ERROR, NULL); goto err; } xso = create_xso_from_stream(qc, qs); if (xso == NULL) goto err; qc_touch_default_xso(qc); /* inhibits default XSO */ if (need_lock) qctx_unlock(ctx); return &xso->obj.ssl; err: OPENSSL_free(xso); ossl_quic_stream_map_release(ossl_quic_channel_get_qsm(qc->ch), qs); if (need_lock) qctx_unlock(ctx); return NULL; } QUIC_TAKES_LOCK SSL *ossl_quic_conn_stream_new(SSL *s, uint64_t flags) { QCTX ctx; if (!expect_quic_conn_only(s, &ctx)) return NULL; return quic_conn_stream_new(&ctx, flags, /*need_lock=*/1); } /* * QUIC Front-End I/O API: Steady-State Operations * =============================================== * * Here we dispatch calls to the steady-state front-end I/O API functions; that * is, the functions used during the established phase of a QUIC connection * (e.g. SSL_read, SSL_write). * * Each function must handle both blocking and non-blocking modes. As discussed * above, all QUIC I/O is implemented using non-blocking mode internally. * * SSL_get_error => partially implemented by ossl_quic_get_error * SSL_want => ossl_quic_want * (BIO/)SSL_read => ossl_quic_read * (BIO/)SSL_write => ossl_quic_write * SSL_pending => ossl_quic_pending * SSL_stream_conclude => ossl_quic_conn_stream_conclude * SSL_key_update => ossl_quic_key_update */ /* SSL_get_error */ int ossl_quic_get_error(const SSL *s, int i) { QCTX ctx; int net_error, last_error; /* SSL_get_errors() should not raise new errors */ if (!is_quic_cs(s, &ctx, 0 /* suppress errors */)) return SSL_ERROR_SSL; qctx_lock(&ctx); net_error = ossl_quic_channel_net_error(ctx.qc->ch); last_error = ctx.is_stream ? ctx.xso->last_error : ctx.qc->last_error; qctx_unlock(&ctx); if (net_error) return SSL_ERROR_SYSCALL; return last_error; } /* Converts a code returned by SSL_get_error to a code returned by SSL_want. */ static int error_to_want(int error) { switch (error) { case SSL_ERROR_WANT_CONNECT: /* never used - UDP is connectionless */ case SSL_ERROR_WANT_ACCEPT: /* never used - UDP is connectionless */ case SSL_ERROR_ZERO_RETURN: default: return SSL_NOTHING; case SSL_ERROR_WANT_READ: return SSL_READING; case SSL_ERROR_WANT_WRITE: return SSL_WRITING; case SSL_ERROR_WANT_RETRY_VERIFY: return SSL_RETRY_VERIFY; case SSL_ERROR_WANT_CLIENT_HELLO_CB: return SSL_CLIENT_HELLO_CB; case SSL_ERROR_WANT_X509_LOOKUP: return SSL_X509_LOOKUP; } } /* SSL_want */ int ossl_quic_want(const SSL *s) { QCTX ctx; int w; if (!expect_quic_cs(s, &ctx)) return SSL_NOTHING; qctx_lock(&ctx); w = error_to_want(ctx.is_stream ? ctx.xso->last_error : ctx.qc->last_error); qctx_unlock(&ctx); return w; } /* * SSL_write * --------- * * The set of functions below provide the implementation of the public SSL_write * function. We must handle: * * - both blocking and non-blocking operation at the application level, * depending on how we are configured; * * - SSL_MODE_ENABLE_PARTIAL_WRITE being on or off; * * - SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER. * */ QUIC_NEEDS_LOCK static void quic_post_write(QUIC_XSO *xso, int did_append, int did_append_all, uint64_t flags, int do_tick) { /* * We have appended at least one byte to the stream. * Potentially mark stream as active, depending on FC. */ if (did_append) ossl_quic_stream_map_update_state(ossl_quic_channel_get_qsm(xso->conn->ch), xso->stream); if (did_append_all && (flags & SSL_WRITE_FLAG_CONCLUDE) != 0) ossl_quic_sstream_fin(xso->stream->sstream); /* * Try and send. * * TODO(QUIC FUTURE): It is probably inefficient to try and do this * immediately, plus we should eventually consider Nagle's algorithm. */ if (do_tick) ossl_quic_reactor_tick(ossl_quic_channel_get_reactor(xso->conn->ch), 0); } struct quic_write_again_args { QUIC_XSO *xso; const unsigned char *buf; size_t len; size_t total_written; int err; uint64_t flags; }; /* * Absolute maximum write buffer size, enforced to prevent a rogue peer from * deliberately inducing DoS. This has been chosen based on the optimal buffer * size for an RTT of 500ms and a bandwidth of 100 Mb/s. */ #define MAX_WRITE_BUF_SIZE (6 * 1024 * 1024) /* * Ensure spare buffer space available (up until a limit, at least). */ QUIC_NEEDS_LOCK static int sstream_ensure_spare(QUIC_SSTREAM *sstream, uint64_t spare) { size_t cur_sz = ossl_quic_sstream_get_buffer_size(sstream); size_t avail = ossl_quic_sstream_get_buffer_avail(sstream); size_t spare_ = (spare > SIZE_MAX) ? SIZE_MAX : (size_t)spare; size_t new_sz, growth; if (spare_ <= avail || cur_sz == MAX_WRITE_BUF_SIZE) return 1; growth = spare_ - avail; if (cur_sz + growth > MAX_WRITE_BUF_SIZE) new_sz = MAX_WRITE_BUF_SIZE; else new_sz = cur_sz + growth; return ossl_quic_sstream_set_buffer_size(sstream, new_sz); } /* * Append to a QUIC_STREAM's QUIC_SSTREAM, ensuring buffer space is expanded * as needed according to flow control. */ QUIC_NEEDS_LOCK static int xso_sstream_append(QUIC_XSO *xso, const unsigned char *buf, size_t len, size_t *actual_written) { QUIC_SSTREAM *sstream = xso->stream->sstream; uint64_t cur = ossl_quic_sstream_get_cur_size(sstream); uint64_t cwm = ossl_quic_txfc_get_cwm(&xso->stream->txfc); uint64_t permitted = (cwm >= cur ? cwm - cur : 0); if (len > permitted) len = (size_t)permitted; if (!sstream_ensure_spare(sstream, len)) return 0; return ossl_quic_sstream_append(sstream, buf, len, actual_written); } QUIC_NEEDS_LOCK static int quic_write_again(void *arg) { struct quic_write_again_args *args = arg; size_t actual_written = 0; if (!quic_mutation_allowed(args->xso->conn, /*req_active=*/1)) /* If connection is torn down due to an error while blocking, stop. */ return -2; if (!quic_validate_for_write(args->xso, &args->err)) /* * Stream may have become invalid for write due to connection events * while we blocked. */ return -2; args->err = ERR_R_INTERNAL_ERROR; if (!xso_sstream_append(args->xso, args->buf, args->len, &actual_written)) return -2; quic_post_write(args->xso, actual_written > 0, args->len == actual_written, args->flags, 0); args->buf += actual_written; args->len -= actual_written; args->total_written += actual_written; if (args->len == 0) /* Written everything, done. */ return 1; /* Not written everything yet, keep trying. */ return 0; } QUIC_NEEDS_LOCK static int quic_write_blocking(QCTX *ctx, const void *buf, size_t len, uint64_t flags, size_t *written) { int res; QUIC_XSO *xso = ctx->xso; struct quic_write_again_args args; size_t actual_written = 0; /* First make a best effort to append as much of the data as possible. */ if (!xso_sstream_append(xso, buf, len, &actual_written)) { /* Stream already finished or allocation error. */ *written = 0; return QUIC_RAISE_NON_NORMAL_ERROR(ctx, ERR_R_INTERNAL_ERROR, NULL); } quic_post_write(xso, actual_written > 0, actual_written == len, flags, 1); /* * Record however much data we wrote */ *written = actual_written; if (actual_written == len) { /* Managed to append everything on the first try. */ return 1; } /* * We did not manage to append all of the data immediately, so the stream * buffer has probably filled up. This means we need to block until some of * it is freed up. */ args.xso = xso; args.buf = (const unsigned char *)buf + actual_written; args.len = len - actual_written; args.total_written = 0; args.err = ERR_R_INTERNAL_ERROR; args.flags = flags; res = block_until_pred(ctx, quic_write_again, &args, 0); if (res <= 0) { if (!quic_mutation_allowed(xso->conn, /*req_active=*/1)) return QUIC_RAISE_NON_NORMAL_ERROR(ctx, SSL_R_PROTOCOL_IS_SHUTDOWN, NULL); else return QUIC_RAISE_NON_NORMAL_ERROR(ctx, args.err, NULL); } /* * When waiting on extra buffer space to be available, args.total_written * holds the amount of remaining data we requested to write, which will be * something less than the len parameter passed in, however much we wrote * here, add it to the value that we wrote when we initially called * xso_sstream_append */ *written += args.total_written; return 1; } /* * Functions to manage All-or-Nothing (AON) (that is, non-ENABLE_PARTIAL_WRITE) * write semantics. */ static void aon_write_begin(QUIC_XSO *xso, const unsigned char *buf, size_t buf_len, size_t already_sent) { assert(!xso->aon_write_in_progress); xso->aon_write_in_progress = 1; xso->aon_buf_base = buf; xso->aon_buf_pos = already_sent; xso->aon_buf_len = buf_len; } static void aon_write_finish(QUIC_XSO *xso) { xso->aon_write_in_progress = 0; xso->aon_buf_base = NULL; xso->aon_buf_pos = 0; xso->aon_buf_len = 0; } QUIC_NEEDS_LOCK static int quic_write_nonblocking_aon(QCTX *ctx, const void *buf, size_t len, uint64_t flags, size_t *written) { QUIC_XSO *xso = ctx->xso; const void *actual_buf; size_t actual_len, actual_written = 0; int accept_moving_buffer = ((xso->ssl_mode & SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER) != 0); if (xso->aon_write_in_progress) { /* * We are in the middle of an AON write (i.e., a previous write did not * manage to append all data to the SSTREAM and we have Enable Partial * Write (EPW) mode disabled.) */ if ((!accept_moving_buffer && xso->aon_buf_base != buf) || len != xso->aon_buf_len) /* * Pointer must not have changed if we are not in accept moving * buffer mode. Length must never change. */ return QUIC_RAISE_NON_NORMAL_ERROR(ctx, SSL_R_BAD_WRITE_RETRY, NULL); actual_buf = (unsigned char *)buf + xso->aon_buf_pos; actual_len = len - xso->aon_buf_pos; assert(actual_len > 0); } else { actual_buf = buf; actual_len = len; } /* First make a best effort to append as much of the data as possible. */ if (!xso_sstream_append(xso, actual_buf, actual_len, &actual_written)) { /* Stream already finished or allocation error. */ *written = 0; return QUIC_RAISE_NON_NORMAL_ERROR(ctx, ERR_R_INTERNAL_ERROR, NULL); } quic_post_write(xso, actual_written > 0, actual_written == actual_len, flags, qctx_should_autotick(ctx)); if (actual_written == actual_len) { /* We have sent everything. */ if (xso->aon_write_in_progress) { /* * We have sent everything, and we were in the middle of an AON * write. The output write length is the total length of the AON * buffer, not however many bytes we managed to write to the stream * in this call. */ *written = xso->aon_buf_len; aon_write_finish(xso); } else { *written = actual_written; } return 1; } if (xso->aon_write_in_progress) { /* * AON write is in progress but we have not written everything yet. We * may have managed to send zero bytes, or some number of bytes less * than the total remaining which need to be appended during this * AON operation. */ xso->aon_buf_pos += actual_written; assert(xso->aon_buf_pos < xso->aon_buf_len); return QUIC_RAISE_NORMAL_ERROR(ctx, SSL_ERROR_WANT_WRITE); } /* * Not in an existing AON operation but partial write is not enabled, so we * need to begin a new AON operation. However we needn't bother if we didn't * actually append anything. */ if (actual_written > 0) aon_write_begin(xso, buf, len, actual_written); /* * AON - We do not publicly admit to having appended anything until AON * completes. */ *written = 0; return QUIC_RAISE_NORMAL_ERROR(ctx, SSL_ERROR_WANT_WRITE); } QUIC_NEEDS_LOCK static int quic_write_nonblocking_epw(QCTX *ctx, const void *buf, size_t len, uint64_t flags, size_t *written) { QUIC_XSO *xso = ctx->xso; /* Simple best effort operation. */ if (!xso_sstream_append(xso, buf, len, written)) { /* Stream already finished or allocation error. */ *written = 0; return QUIC_RAISE_NON_NORMAL_ERROR(ctx, ERR_R_INTERNAL_ERROR, NULL); } quic_post_write(xso, *written > 0, *written == len, flags, qctx_should_autotick(ctx)); if (*written == 0) /* SSL_write_ex returns 0 if it didn't write anything. */ return QUIC_RAISE_NORMAL_ERROR(ctx, SSL_ERROR_WANT_WRITE); return 1; } QUIC_NEEDS_LOCK static int quic_validate_for_write(QUIC_XSO *xso, int *err) { QUIC_STREAM_MAP *qsm; if (xso == NULL || xso->stream == NULL) { *err = ERR_R_INTERNAL_ERROR; return 0; } switch (xso->stream->send_state) { default: case QUIC_SSTREAM_STATE_NONE: *err = SSL_R_STREAM_RECV_ONLY; return 0; case QUIC_SSTREAM_STATE_READY: qsm = ossl_quic_channel_get_qsm(xso->conn->ch); if (!ossl_quic_stream_map_ensure_send_part_id(qsm, xso->stream)) { *err = ERR_R_INTERNAL_ERROR; return 0; } /* FALLTHROUGH */ case QUIC_SSTREAM_STATE_SEND: case QUIC_SSTREAM_STATE_DATA_SENT: if (ossl_quic_sstream_get_final_size(xso->stream->sstream, NULL)) { *err = SSL_R_STREAM_FINISHED; return 0; } return 1; case QUIC_SSTREAM_STATE_DATA_RECVD: *err = SSL_R_STREAM_FINISHED; return 0; case QUIC_SSTREAM_STATE_RESET_SENT: case QUIC_SSTREAM_STATE_RESET_RECVD: *err = SSL_R_STREAM_RESET; return 0; } } QUIC_TAKES_LOCK int ossl_quic_write_flags(SSL *s, const void *buf, size_t len, uint64_t flags, size_t *written) { int ret; QCTX ctx; int partial_write, err; *written = 0; if (len == 0) { /* Do not autocreate default XSO for zero-length writes. */ if (!expect_quic_cs(s, &ctx)) return 0; qctx_lock_for_io(&ctx); } else { if (!expect_quic_with_stream_lock(s, /*remote_init=*/0, /*io=*/1, &ctx)) return 0; } partial_write = ((ctx.xso != NULL) ? ((ctx.xso->ssl_mode & SSL_MODE_ENABLE_PARTIAL_WRITE) != 0) : 0); if ((flags & ~SSL_WRITE_FLAG_CONCLUDE) != 0) { ret = QUIC_RAISE_NON_NORMAL_ERROR(&ctx, SSL_R_UNSUPPORTED_WRITE_FLAG, NULL); goto out; } if (!quic_mutation_allowed(ctx.qc, /*req_active=*/0)) { ret = QUIC_RAISE_NON_NORMAL_ERROR(&ctx, SSL_R_PROTOCOL_IS_SHUTDOWN, NULL); goto out; } /* * If we haven't finished the handshake, try to advance it. * We don't accept writes until the handshake is completed. */ if (quic_do_handshake(&ctx) < 1) { ret = 0; goto out; } /* Ensure correct stream state, stream send part not concluded, etc. */ if (len > 0 && !quic_validate_for_write(ctx.xso, &err)) { ret = QUIC_RAISE_NON_NORMAL_ERROR(&ctx, err, NULL); goto out; } if (len == 0) { if ((flags & SSL_WRITE_FLAG_CONCLUDE) != 0) quic_post_write(ctx.xso, 0, 1, flags, qctx_should_autotick(&ctx)); ret = 1; goto out; } if (qctx_blocking(&ctx)) ret = quic_write_blocking(&ctx, buf, len, flags, written); else if (partial_write) ret = quic_write_nonblocking_epw(&ctx, buf, len, flags, written); else ret = quic_write_nonblocking_aon(&ctx, buf, len, flags, written); out: qctx_unlock(&ctx); return ret; } QUIC_TAKES_LOCK int ossl_quic_write(SSL *s, const void *buf, size_t len, size_t *written) { return ossl_quic_write_flags(s, buf, len, 0, written); } /* * SSL_read * -------- */ struct quic_read_again_args { QCTX *ctx; QUIC_STREAM *stream; void *buf; size_t len; size_t *bytes_read; int peek; }; QUIC_NEEDS_LOCK static int quic_validate_for_read(QUIC_XSO *xso, int *err, int *eos) { QUIC_STREAM_MAP *qsm; *eos = 0; if (xso == NULL || xso->stream == NULL) { *err = ERR_R_INTERNAL_ERROR; return 0; } switch (xso->stream->recv_state) { default: case QUIC_RSTREAM_STATE_NONE: *err = SSL_R_STREAM_SEND_ONLY; return 0; case QUIC_RSTREAM_STATE_RECV: case QUIC_RSTREAM_STATE_SIZE_KNOWN: case QUIC_RSTREAM_STATE_DATA_RECVD: return 1; case QUIC_RSTREAM_STATE_DATA_READ: *eos = 1; return 0; case QUIC_RSTREAM_STATE_RESET_RECVD: qsm = ossl_quic_channel_get_qsm(xso->conn->ch); ossl_quic_stream_map_notify_app_read_reset_recv_part(qsm, xso->stream); /* FALLTHROUGH */ case QUIC_RSTREAM_STATE_RESET_READ: *err = SSL_R_STREAM_RESET; return 0; } } QUIC_NEEDS_LOCK static int quic_read_actual(QCTX *ctx, QUIC_STREAM *stream, void *buf, size_t buf_len, size_t *bytes_read, int peek) { int is_fin = 0, err, eos; QUIC_CONNECTION *qc = ctx->qc; if (!quic_validate_for_read(ctx->xso, &err, &eos)) { if (eos) { ctx->xso->retired_fin = 1; return QUIC_RAISE_NORMAL_ERROR(ctx, SSL_ERROR_ZERO_RETURN); } else { return QUIC_RAISE_NON_NORMAL_ERROR(ctx, err, NULL); } } if (peek) { if (!ossl_quic_rstream_peek(stream->rstream, buf, buf_len, bytes_read, &is_fin)) return QUIC_RAISE_NON_NORMAL_ERROR(ctx, ERR_R_INTERNAL_ERROR, NULL); } else { if (!ossl_quic_rstream_read(stream->rstream, buf, buf_len, bytes_read, &is_fin)) return QUIC_RAISE_NON_NORMAL_ERROR(ctx, ERR_R_INTERNAL_ERROR, NULL); } if (!peek) { if (*bytes_read > 0) { /* * We have read at least one byte from the stream. Inform stream-level * RXFC of the retirement of controlled bytes. Update the active stream * status (the RXFC may now want to emit a frame granting more credit to * the peer). */ OSSL_RTT_INFO rtt_info; ossl_statm_get_rtt_info(ossl_quic_channel_get_statm(qc->ch), &rtt_info); if (!ossl_quic_rxfc_on_retire(&stream->rxfc, *bytes_read, rtt_info.smoothed_rtt)) return QUIC_RAISE_NON_NORMAL_ERROR(ctx, ERR_R_INTERNAL_ERROR, NULL); } if (is_fin && !peek) { QUIC_STREAM_MAP *qsm = ossl_quic_channel_get_qsm(ctx->qc->ch); ossl_quic_stream_map_notify_totally_read(qsm, ctx->xso->stream); } if (*bytes_read > 0) ossl_quic_stream_map_update_state(ossl_quic_channel_get_qsm(qc->ch), stream); } if (*bytes_read == 0 && is_fin) { ctx->xso->retired_fin = 1; return QUIC_RAISE_NORMAL_ERROR(ctx, SSL_ERROR_ZERO_RETURN); } return 1; } QUIC_NEEDS_LOCK static int quic_read_again(void *arg) { struct quic_read_again_args *args = arg; if (!quic_mutation_allowed(args->ctx->qc, /*req_active=*/1)) { /* If connection is torn down due to an error while blocking, stop. */ QUIC_RAISE_NON_NORMAL_ERROR(args->ctx, SSL_R_PROTOCOL_IS_SHUTDOWN, NULL); return -1; } if (!quic_read_actual(args->ctx, args->stream, args->buf, args->len, args->bytes_read, args->peek)) return -1; if (*args->bytes_read > 0) /* got at least one byte, the SSL_read op can finish now */ return 1; return 0; /* did not read anything, keep trying */ } QUIC_TAKES_LOCK static int quic_read(SSL *s, void *buf, size_t len, size_t *bytes_read, int peek) { int ret, res; QCTX ctx; struct quic_read_again_args args; *bytes_read = 0; if (!expect_quic_cs(s, &ctx)) return 0; qctx_lock_for_io(&ctx); /* If we haven't finished the handshake, try to advance it. */ if (quic_do_handshake(&ctx) < 1) { ret = 0; /* ossl_quic_do_handshake raised error here */ goto out; } if (ctx.xso == NULL) { /* * Called on a QCSO and we don't currently have a default stream. * * Wait until we get a stream initiated by the peer (blocking mode) or * fail if we don't have one yet (non-blocking mode). */ if (!qc_wait_for_default_xso_for_read(&ctx, /*peek=*/0)) { ret = 0; /* error already raised here */ goto out; } ctx.xso = ctx.qc->default_xso; } if (!quic_read_actual(&ctx, ctx.xso->stream, buf, len, bytes_read, peek)) { ret = 0; /* quic_read_actual raised error here */ goto out; } if (*bytes_read > 0) { /* * Even though we succeeded, tick the reactor here to ensure we are * handling other aspects of the QUIC connection. */ if (quic_mutation_allowed(ctx.qc, /*req_active=*/0)) qctx_maybe_autotick(&ctx); ret = 1; } else if (!quic_mutation_allowed(ctx.qc, /*req_active=*/0)) { ret = QUIC_RAISE_NON_NORMAL_ERROR(&ctx, SSL_R_PROTOCOL_IS_SHUTDOWN, NULL); goto out; } else if (qctx_blocking(&ctx)) { /* * We were not able to read anything immediately, so our stream * buffer is empty. This means we need to block until we get * at least one byte. */ args.ctx = &ctx; args.stream = ctx.xso->stream; args.buf = buf; args.len = len; args.bytes_read = bytes_read; args.peek = peek; res = block_until_pred(&ctx, quic_read_again, &args, 0); if (res == 0) { ret = QUIC_RAISE_NON_NORMAL_ERROR(&ctx, ERR_R_INTERNAL_ERROR, NULL); goto out; } else if (res < 0) { ret = 0; /* quic_read_again raised error here */ goto out; } ret = 1; } else { /* * We did not get any bytes and are not in blocking mode. * Tick to see if this delivers any more. */ qctx_maybe_autotick(&ctx); /* Try the read again. */ if (!quic_read_actual(&ctx, ctx.xso->stream, buf, len, bytes_read, peek)) { ret = 0; /* quic_read_actual raised error here */ goto out; } if (*bytes_read > 0) ret = 1; /* Succeeded this time. */ else ret = QUIC_RAISE_NORMAL_ERROR(&ctx, SSL_ERROR_WANT_READ); } out: qctx_unlock(&ctx); return ret; } int ossl_quic_read(SSL *s, void *buf, size_t len, size_t *bytes_read) { return quic_read(s, buf, len, bytes_read, 0); } int ossl_quic_peek(SSL *s, void *buf, size_t len, size_t *bytes_read) { return quic_read(s, buf, len, bytes_read, 1); } /* * SSL_pending * ----------- */ QUIC_TAKES_LOCK static size_t ossl_quic_pending_int(const SSL *s, int check_channel) { QCTX ctx; size_t avail = 0; if (!expect_quic_cs(s, &ctx)) return 0; qctx_lock(&ctx); if (!ctx.qc->started) goto out; if (ctx.xso == NULL) { /* No XSO yet, but there might be a default XSO eligible to be created. */ if (qc_wait_for_default_xso_for_read(&ctx, /*peek=*/1)) { ctx.xso = ctx.qc->default_xso; } else { QUIC_RAISE_NON_NORMAL_ERROR(&ctx, SSL_R_NO_STREAM, NULL); goto out; } } if (ctx.xso->stream == NULL) { QUIC_RAISE_NON_NORMAL_ERROR(&ctx, ERR_R_INTERNAL_ERROR, NULL); goto out; } if (check_channel) avail = ossl_quic_stream_recv_pending(ctx.xso->stream, /*include_fin=*/1) || ossl_quic_channel_has_pending(ctx.qc->ch) || ossl_quic_channel_is_term_any(ctx.qc->ch); else avail = ossl_quic_stream_recv_pending(ctx.xso->stream, /*include_fin=*/0); out: qctx_unlock(&ctx); return avail; } size_t ossl_quic_pending(const SSL *s) { return ossl_quic_pending_int(s, /*check_channel=*/0); } int ossl_quic_has_pending(const SSL *s) { /* Do we have app-side pending data or pending URXEs or RXEs? */ return ossl_quic_pending_int(s, /*check_channel=*/1) > 0; } /* * SSL_stream_conclude * ------------------- */ QUIC_TAKES_LOCK int ossl_quic_conn_stream_conclude(SSL *s) { QCTX ctx; QUIC_STREAM *qs; int err; int ret; if (!expect_quic_with_stream_lock(s, /*remote_init=*/0, /*io=*/0, &ctx)) return 0; qs = ctx.xso->stream; if (!quic_mutation_allowed(ctx.qc, /*req_active=*/1)) { ret = QUIC_RAISE_NON_NORMAL_ERROR(&ctx, SSL_R_PROTOCOL_IS_SHUTDOWN, NULL); qctx_unlock(&ctx); return ret; } if (!quic_validate_for_write(ctx.xso, &err)) { ret = QUIC_RAISE_NON_NORMAL_ERROR(&ctx, err, NULL); qctx_unlock(&ctx); return ret; } if (ossl_quic_sstream_get_final_size(qs->sstream, NULL)) { qctx_unlock(&ctx); return 1; } ossl_quic_sstream_fin(qs->sstream); quic_post_write(ctx.xso, 1, 0, 0, qctx_should_autotick(&ctx)); qctx_unlock(&ctx); return 1; } /* * SSL_inject_net_dgram * -------------------- */ QUIC_TAKES_LOCK int SSL_inject_net_dgram(SSL *s, const unsigned char *buf, size_t buf_len, const BIO_ADDR *peer, const BIO_ADDR *local) { int ret = 0; QCTX ctx; QUIC_DEMUX *demux; QUIC_PORT *port; if (!expect_quic_csl(s, &ctx)) return 0; qctx_lock(&ctx); port = ossl_quic_obj_get0_port(ctx.obj); if (port == NULL) { QUIC_RAISE_NON_NORMAL_ERROR(&ctx, ERR_R_UNSUPPORTED, NULL); goto err; } demux = ossl_quic_port_get0_demux(port); ret = ossl_quic_demux_inject(demux, buf, buf_len, peer, local); err: qctx_unlock(&ctx); return ret; } /* * SSL_get0_connection * ------------------- */ SSL *ossl_quic_get0_connection(SSL *s) { QCTX ctx; if (!expect_quic_cs(s, &ctx)) return NULL; return &ctx.qc->obj.ssl; } /* * SSL_get0_listener * ----------------- */ SSL *ossl_quic_get0_listener(SSL *s) { QCTX ctx; if (!expect_quic_csl(s, &ctx)) return NULL; return ctx.ql != NULL ? &ctx.ql->obj.ssl : NULL; } /* * SSL_get0_domain * --------------- */ SSL *ossl_quic_get0_domain(SSL *s) { QCTX ctx; if (!expect_quic_any(s, &ctx)) return NULL; return ctx.qd != NULL ? &ctx.qd->obj.ssl : NULL; } /* * SSL_get_domain_flags * -------------------- */ int ossl_quic_get_domain_flags(const SSL *ssl, uint64_t *domain_flags) { QCTX ctx; if (!expect_quic_any(ssl, &ctx)) return 0; if (domain_flags != NULL) *domain_flags = ctx.obj->domain_flags; return 1; } /* * SSL_get_stream_type * ------------------- */ int ossl_quic_get_stream_type(SSL *s) { QCTX ctx; if (!expect_quic_cs(s, &ctx)) return SSL_STREAM_TYPE_BIDI; if (ctx.xso == NULL) { /* * If deferred XSO creation has yet to occur, proceed according to the * default stream mode. If AUTO_BIDI or AUTO_UNI is set, we cannot know * what kind of stream will be created yet, so return BIDI on the basis * that at this time, the client still has the option of calling * SSL_read() or SSL_write() first. */ if (ctx.qc->default_xso_created || ctx.qc->default_stream_mode == SSL_DEFAULT_STREAM_MODE_NONE) return SSL_STREAM_TYPE_NONE; else return SSL_STREAM_TYPE_BIDI; } if (ossl_quic_stream_is_bidi(ctx.xso->stream)) return SSL_STREAM_TYPE_BIDI; if (ossl_quic_stream_is_server_init(ctx.xso->stream) != ctx.qc->as_server) return SSL_STREAM_TYPE_READ; else return SSL_STREAM_TYPE_WRITE; } /* * SSL_get_stream_id * ----------------- */ QUIC_TAKES_LOCK uint64_t ossl_quic_get_stream_id(SSL *s) { QCTX ctx; uint64_t id; if (!expect_quic_with_stream_lock(s, /*remote_init=*/-1, /*io=*/0, &ctx)) return UINT64_MAX; id = ctx.xso->stream->id; qctx_unlock(&ctx); return id; } /* * SSL_is_stream_local * ------------------- */ QUIC_TAKES_LOCK int ossl_quic_is_stream_local(SSL *s) { QCTX ctx; int is_local; if (!expect_quic_with_stream_lock(s, /*remote_init=*/-1, /*io=*/0, &ctx)) return -1; is_local = ossl_quic_stream_is_local_init(ctx.xso->stream); qctx_unlock(&ctx); return is_local; } /* * SSL_set_default_stream_mode * --------------------------- */ QUIC_TAKES_LOCK int ossl_quic_set_default_stream_mode(SSL *s, uint32_t mode) { QCTX ctx; if (!expect_quic_conn_only(s, &ctx)) return 0; qctx_lock(&ctx); if (ctx.qc->default_xso_created) { qctx_unlock(&ctx); return QUIC_RAISE_NON_NORMAL_ERROR(&ctx, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED, "too late to change default stream mode"); } switch (mode) { case SSL_DEFAULT_STREAM_MODE_NONE: case SSL_DEFAULT_STREAM_MODE_AUTO_BIDI: case SSL_DEFAULT_STREAM_MODE_AUTO_UNI: ctx.qc->default_stream_mode = mode; break; default: qctx_unlock(&ctx); return QUIC_RAISE_NON_NORMAL_ERROR(&ctx, ERR_R_PASSED_INVALID_ARGUMENT, "bad default stream type"); } qctx_unlock(&ctx); return 1; } /* * SSL_detach_stream * ----------------- */ QUIC_TAKES_LOCK SSL *ossl_quic_detach_stream(SSL *s) { QCTX ctx; QUIC_XSO *xso = NULL; if (!expect_quic_conn_only(s, &ctx)) return NULL; qctx_lock(&ctx); /* Calling this function inhibits default XSO autocreation. */ /* QC ref to any default XSO is transferred to us and to caller. */ qc_set_default_xso_keep_ref(ctx.qc, NULL, /*touch=*/1, &xso); qctx_unlock(&ctx); return xso != NULL ? &xso->obj.ssl : NULL; } /* * SSL_attach_stream * ----------------- */ QUIC_TAKES_LOCK int ossl_quic_attach_stream(SSL *conn, SSL *stream) { QCTX ctx; QUIC_XSO *xso; int nref; if (!expect_quic_conn_only(conn, &ctx)) return 0; if (stream == NULL || stream->type != SSL_TYPE_QUIC_XSO) return QUIC_RAISE_NON_NORMAL_ERROR(&ctx, ERR_R_PASSED_NULL_PARAMETER, "stream to attach must be a valid QUIC stream"); xso = (QUIC_XSO *)stream; qctx_lock(&ctx); if (ctx.qc->default_xso != NULL) { qctx_unlock(&ctx); return QUIC_RAISE_NON_NORMAL_ERROR(&ctx, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED, "connection already has a default stream"); } /* * It is a caller error for the XSO being attached as a default XSO to have * more than one ref. */ if (!CRYPTO_GET_REF(&xso->obj.ssl.references, &nref)) { qctx_unlock(&ctx); return QUIC_RAISE_NON_NORMAL_ERROR(&ctx, ERR_R_INTERNAL_ERROR, "ref"); } if (nref != 1) { qctx_unlock(&ctx); return QUIC_RAISE_NON_NORMAL_ERROR(&ctx, ERR_R_PASSED_INVALID_ARGUMENT, "stream being attached must have " "only 1 reference"); } /* Caller's reference to the XSO is transferred to us. */ /* Calling this function inhibits default XSO autocreation. */ qc_set_default_xso(ctx.qc, xso, /*touch=*/1); qctx_unlock(&ctx); return 1; } /* * SSL_set_incoming_stream_policy * ------------------------------ */ QUIC_NEEDS_LOCK static int qc_get_effective_incoming_stream_policy(QUIC_CONNECTION *qc) { switch (qc->incoming_stream_policy) { case SSL_INCOMING_STREAM_POLICY_AUTO: if ((qc->default_xso == NULL && !qc->default_xso_created) || qc->default_stream_mode == SSL_DEFAULT_STREAM_MODE_NONE) return SSL_INCOMING_STREAM_POLICY_ACCEPT; else return SSL_INCOMING_STREAM_POLICY_REJECT; default: return qc->incoming_stream_policy; } } QUIC_NEEDS_LOCK static void qc_update_reject_policy(QUIC_CONNECTION *qc) { int policy = qc_get_effective_incoming_stream_policy(qc); int enable_reject = (policy == SSL_INCOMING_STREAM_POLICY_REJECT); ossl_quic_channel_set_incoming_stream_auto_reject(qc->ch, enable_reject, qc->incoming_stream_aec); } QUIC_TAKES_LOCK int ossl_quic_set_incoming_stream_policy(SSL *s, int policy, uint64_t aec) { int ret = 1; QCTX ctx; if (!expect_quic_conn_only(s, &ctx)) return 0; qctx_lock(&ctx); switch (policy) { case SSL_INCOMING_STREAM_POLICY_AUTO: case SSL_INCOMING_STREAM_POLICY_ACCEPT: case SSL_INCOMING_STREAM_POLICY_REJECT: ctx.qc->incoming_stream_policy = policy; ctx.qc->incoming_stream_aec = aec; break; default: QUIC_RAISE_NON_NORMAL_ERROR(&ctx, ERR_R_PASSED_INVALID_ARGUMENT, NULL); ret = 0; break; } qc_update_reject_policy(ctx.qc); qctx_unlock(&ctx); return ret; } /* * SSL_get_value, SSL_set_value * ---------------------------- */ QUIC_TAKES_LOCK static int qc_getset_idle_timeout(QCTX *ctx, uint32_t class_, uint64_t *p_value_out, uint64_t *p_value_in) { int ret = 0; uint64_t value_out = 0, value_in; qctx_lock(ctx); switch (class_) { case SSL_VALUE_CLASS_FEATURE_REQUEST: value_out = ossl_quic_channel_get_max_idle_timeout_request(ctx->qc->ch); if (p_value_in != NULL) { value_in = *p_value_in; if (value_in > OSSL_QUIC_VLINT_MAX) { QUIC_RAISE_NON_NORMAL_ERROR(ctx, ERR_R_PASSED_INVALID_ARGUMENT, NULL); goto err; } if (ossl_quic_channel_have_generated_transport_params(ctx->qc->ch)) { QUIC_RAISE_NON_NORMAL_ERROR(ctx, SSL_R_FEATURE_NOT_RENEGOTIABLE, NULL); goto err; } ossl_quic_channel_set_max_idle_timeout_request(ctx->qc->ch, value_in); } break; case SSL_VALUE_CLASS_FEATURE_PEER_REQUEST: case SSL_VALUE_CLASS_FEATURE_NEGOTIATED: if (p_value_in != NULL) { QUIC_RAISE_NON_NORMAL_ERROR(ctx, SSL_R_UNSUPPORTED_CONFIG_VALUE_OP, NULL); goto err; } if (!ossl_quic_channel_is_handshake_complete(ctx->qc->ch)) { QUIC_RAISE_NON_NORMAL_ERROR(ctx, SSL_R_FEATURE_NEGOTIATION_NOT_COMPLETE, NULL); goto err; } value_out = (class_ == SSL_VALUE_CLASS_FEATURE_NEGOTIATED) ? ossl_quic_channel_get_max_idle_timeout_actual(ctx->qc->ch) : ossl_quic_channel_get_max_idle_timeout_peer_request(ctx->qc->ch); break; default: QUIC_RAISE_NON_NORMAL_ERROR(ctx, SSL_R_UNSUPPORTED_CONFIG_VALUE_CLASS, NULL); goto err; } ret = 1; err: qctx_unlock(ctx); if (ret && p_value_out != NULL) *p_value_out = value_out; return ret; } QUIC_TAKES_LOCK static int qc_get_stream_avail(QCTX *ctx, uint32_t class_, int is_uni, int is_remote, uint64_t *value) { int ret = 0; if (class_ != SSL_VALUE_CLASS_GENERIC) { QUIC_RAISE_NON_NORMAL_ERROR(ctx, SSL_R_UNSUPPORTED_CONFIG_VALUE_CLASS, NULL); return 0; } qctx_lock(ctx); *value = is_remote ? ossl_quic_channel_get_remote_stream_count_avail(ctx->qc->ch, is_uni) : ossl_quic_channel_get_local_stream_count_avail(ctx->qc->ch, is_uni); ret = 1; qctx_unlock(ctx); return ret; } QUIC_NEEDS_LOCK static int qctx_should_autotick(QCTX *ctx) { int event_handling_mode; QUIC_OBJ *obj = ctx->obj; for (; (event_handling_mode = obj->event_handling_mode) == SSL_VALUE_EVENT_HANDLING_MODE_INHERIT && obj->parent_obj != NULL; obj = obj->parent_obj); return event_handling_mode != SSL_VALUE_EVENT_HANDLING_MODE_EXPLICIT; } QUIC_NEEDS_LOCK static void qctx_maybe_autotick(QCTX *ctx) { if (!qctx_should_autotick(ctx)) return; ossl_quic_reactor_tick(ossl_quic_obj_get0_reactor(ctx->obj), 0); } QUIC_TAKES_LOCK static int qc_getset_event_handling(QCTX *ctx, uint32_t class_, uint64_t *p_value_out, uint64_t *p_value_in) { int ret = 0; uint64_t value_out = 0; qctx_lock(ctx); if (class_ != SSL_VALUE_CLASS_GENERIC) { QUIC_RAISE_NON_NORMAL_ERROR(ctx, SSL_R_UNSUPPORTED_CONFIG_VALUE_CLASS, NULL); goto err; } if (p_value_in != NULL) { switch (*p_value_in) { case SSL_VALUE_EVENT_HANDLING_MODE_INHERIT: case SSL_VALUE_EVENT_HANDLING_MODE_IMPLICIT: case SSL_VALUE_EVENT_HANDLING_MODE_EXPLICIT: break; default: QUIC_RAISE_NON_NORMAL_ERROR(ctx, ERR_R_PASSED_INVALID_ARGUMENT, NULL); goto err; } value_out = *p_value_in; ctx->obj->event_handling_mode = (int)value_out; } else { value_out = ctx->obj->event_handling_mode; } ret = 1; err: qctx_unlock(ctx); if (ret && p_value_out != NULL) *p_value_out = value_out; return ret; } QUIC_TAKES_LOCK static int qc_get_stream_write_buf_stat(QCTX *ctx, uint32_t class_, uint64_t *p_value_out, size_t (*getter)(QUIC_SSTREAM *sstream)) { int ret = 0; size_t value = 0; qctx_lock(ctx); if (class_ != SSL_VALUE_CLASS_GENERIC) { QUIC_RAISE_NON_NORMAL_ERROR(ctx, SSL_R_UNSUPPORTED_CONFIG_VALUE_CLASS, NULL); goto err; } if (ctx->xso == NULL) { QUIC_RAISE_NON_NORMAL_ERROR(ctx, SSL_R_NO_STREAM, NULL); goto err; } if (!ossl_quic_stream_has_send(ctx->xso->stream)) { QUIC_RAISE_NON_NORMAL_ERROR(ctx, SSL_R_STREAM_RECV_ONLY, NULL); goto err; } if (ossl_quic_stream_has_send_buffer(ctx->xso->stream)) value = getter(ctx->xso->stream->sstream); ret = 1; err: qctx_unlock(ctx); *p_value_out = (uint64_t)value; return ret; } QUIC_NEEDS_LOCK static int expect_quic_for_value(SSL *s, QCTX *ctx, uint32_t id) { switch (id) { case SSL_VALUE_EVENT_HANDLING_MODE: case SSL_VALUE_STREAM_WRITE_BUF_SIZE: case SSL_VALUE_STREAM_WRITE_BUF_USED: case SSL_VALUE_STREAM_WRITE_BUF_AVAIL: return expect_quic_cs(s, ctx); default: return expect_quic_conn_only(s, ctx); } } QUIC_TAKES_LOCK int ossl_quic_get_value_uint(SSL *s, uint32_t class_, uint32_t id, uint64_t *value) { QCTX ctx; if (!expect_quic_for_value(s, &ctx, id)) return 0; if (value == NULL) return QUIC_RAISE_NON_NORMAL_ERROR(&ctx, ERR_R_PASSED_INVALID_ARGUMENT, NULL); switch (id) { case SSL_VALUE_QUIC_IDLE_TIMEOUT: return qc_getset_idle_timeout(&ctx, class_, value, NULL); case SSL_VALUE_QUIC_STREAM_BIDI_LOCAL_AVAIL: return qc_get_stream_avail(&ctx, class_, /*uni=*/0, /*remote=*/0, value); case SSL_VALUE_QUIC_STREAM_BIDI_REMOTE_AVAIL: return qc_get_stream_avail(&ctx, class_, /*uni=*/0, /*remote=*/1, value); case SSL_VALUE_QUIC_STREAM_UNI_LOCAL_AVAIL: return qc_get_stream_avail(&ctx, class_, /*uni=*/1, /*remote=*/0, value); case SSL_VALUE_QUIC_STREAM_UNI_REMOTE_AVAIL: return qc_get_stream_avail(&ctx, class_, /*uni=*/1, /*remote=*/1, value); case SSL_VALUE_EVENT_HANDLING_MODE: return qc_getset_event_handling(&ctx, class_, value, NULL); case SSL_VALUE_STREAM_WRITE_BUF_SIZE: return qc_get_stream_write_buf_stat(&ctx, class_, value, ossl_quic_sstream_get_buffer_size); case SSL_VALUE_STREAM_WRITE_BUF_USED: return qc_get_stream_write_buf_stat(&ctx, class_, value, ossl_quic_sstream_get_buffer_used); case SSL_VALUE_STREAM_WRITE_BUF_AVAIL: return qc_get_stream_write_buf_stat(&ctx, class_, value, ossl_quic_sstream_get_buffer_avail); default: return QUIC_RAISE_NON_NORMAL_ERROR(&ctx, SSL_R_UNSUPPORTED_CONFIG_VALUE, NULL); } return 1; } QUIC_TAKES_LOCK int ossl_quic_set_value_uint(SSL *s, uint32_t class_, uint32_t id, uint64_t value) { QCTX ctx; if (!expect_quic_for_value(s, &ctx, id)) return 0; switch (id) { case SSL_VALUE_QUIC_IDLE_TIMEOUT: return qc_getset_idle_timeout(&ctx, class_, NULL, &value); case SSL_VALUE_EVENT_HANDLING_MODE: return qc_getset_event_handling(&ctx, class_, NULL, &value); default: return QUIC_RAISE_NON_NORMAL_ERROR(&ctx, SSL_R_UNSUPPORTED_CONFIG_VALUE, NULL); } return 1; } /* * SSL_accept_stream * ----------------- */ struct wait_for_incoming_stream_args { QCTX *ctx; QUIC_STREAM *qs; }; QUIC_NEEDS_LOCK static int wait_for_incoming_stream(void *arg) { struct wait_for_incoming_stream_args *args = arg; QUIC_CONNECTION *qc = args->ctx->qc; QUIC_STREAM_MAP *qsm = ossl_quic_channel_get_qsm(qc->ch); if (!quic_mutation_allowed(qc, /*req_active=*/1)) { /* If connection is torn down due to an error while blocking, stop. */ QUIC_RAISE_NON_NORMAL_ERROR(args->ctx, SSL_R_PROTOCOL_IS_SHUTDOWN, NULL); return -1; } args->qs = ossl_quic_stream_map_peek_accept_queue(qsm); if (args->qs != NULL) return 1; /* got a stream */ return 0; /* did not get a stream, keep trying */ } QUIC_TAKES_LOCK SSL *ossl_quic_accept_stream(SSL *s, uint64_t flags) { QCTX ctx; int ret; SSL *new_s = NULL; QUIC_STREAM_MAP *qsm; QUIC_STREAM *qs; QUIC_XSO *xso; OSSL_RTT_INFO rtt_info; if (!expect_quic_conn_only(s, &ctx)) return NULL; qctx_lock(&ctx); if (qc_get_effective_incoming_stream_policy(ctx.qc) == SSL_INCOMING_STREAM_POLICY_REJECT) { QUIC_RAISE_NON_NORMAL_ERROR(&ctx, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED, NULL); goto out; } qsm = ossl_quic_channel_get_qsm(ctx.qc->ch); qs = ossl_quic_stream_map_peek_accept_queue(qsm); if (qs == NULL) { if (qctx_blocking(&ctx) && (flags & SSL_ACCEPT_STREAM_NO_BLOCK) == 0) { struct wait_for_incoming_stream_args args; args.ctx = &ctx; args.qs = NULL; ret = block_until_pred(&ctx, wait_for_incoming_stream, &args, 0); if (ret == 0) { QUIC_RAISE_NON_NORMAL_ERROR(&ctx, ERR_R_INTERNAL_ERROR, NULL); goto out; } else if (ret < 0 || args.qs == NULL) { goto out; } qs = args.qs; } else { goto out; } } xso = create_xso_from_stream(ctx.qc, qs); if (xso == NULL) goto out; ossl_statm_get_rtt_info(ossl_quic_channel_get_statm(ctx.qc->ch), &rtt_info); ossl_quic_stream_map_remove_from_accept_queue(qsm, qs, rtt_info.smoothed_rtt); new_s = &xso->obj.ssl; /* Calling this function inhibits default XSO autocreation. */ qc_touch_default_xso(ctx.qc); /* inhibits default XSO */ out: qctx_unlock(&ctx); return new_s; } /* * SSL_get_accept_stream_queue_len * ------------------------------- */ QUIC_TAKES_LOCK size_t ossl_quic_get_accept_stream_queue_len(SSL *s) { QCTX ctx; size_t v; if (!expect_quic_conn_only(s, &ctx)) return 0; qctx_lock(&ctx); v = ossl_quic_stream_map_get_total_accept_queue_len(ossl_quic_channel_get_qsm(ctx.qc->ch)); qctx_unlock(&ctx); return v; } /* * SSL_stream_reset * ---------------- */ int ossl_quic_stream_reset(SSL *ssl, const SSL_STREAM_RESET_ARGS *args, size_t args_len) { QCTX ctx; QUIC_STREAM_MAP *qsm; QUIC_STREAM *qs; uint64_t error_code; int ok, err; if (!expect_quic_with_stream_lock(ssl, /*remote_init=*/0, /*io=*/0, &ctx)) return 0; qsm = ossl_quic_channel_get_qsm(ctx.qc->ch); qs = ctx.xso->stream; error_code = (args != NULL ? args->quic_error_code : 0); if (!quic_validate_for_write(ctx.xso, &err)) { ok = QUIC_RAISE_NON_NORMAL_ERROR(&ctx, err, NULL); goto err; } ok = ossl_quic_stream_map_reset_stream_send_part(qsm, qs, error_code); if (ok) ctx.xso->requested_reset = 1; err: qctx_unlock(&ctx); return ok; } /* * SSL_get_stream_read_state * ------------------------- */ static void quic_classify_stream(QUIC_CONNECTION *qc, QUIC_STREAM *qs, int is_write, int *state, uint64_t *app_error_code) { int local_init; uint64_t final_size; local_init = (ossl_quic_stream_is_server_init(qs) == qc->as_server); if (app_error_code != NULL) *app_error_code = UINT64_MAX; else app_error_code = &final_size; /* throw away value */ if (!ossl_quic_stream_is_bidi(qs) && local_init != is_write) { /* * Unidirectional stream and this direction of transmission doesn't * exist. */ *state = SSL_STREAM_STATE_WRONG_DIR; } else if (ossl_quic_channel_is_term_any(qc->ch)) { /* Connection already closed. */ *state = SSL_STREAM_STATE_CONN_CLOSED; } else if (!is_write && qs->recv_state == QUIC_RSTREAM_STATE_DATA_READ) { /* Application has read a FIN. */ *state = SSL_STREAM_STATE_FINISHED; } else if ((!is_write && qs->stop_sending) || (is_write && ossl_quic_stream_send_is_reset(qs))) { /* * Stream has been reset locally. FIN takes precedence over this for the * read case as the application need not care if the stream is reset * after a FIN has been successfully processed. */ *state = SSL_STREAM_STATE_RESET_LOCAL; *app_error_code = !is_write ? qs->stop_sending_aec : qs->reset_stream_aec; } else if ((!is_write && ossl_quic_stream_recv_is_reset(qs)) || (is_write && qs->peer_stop_sending)) { /* * Stream has been reset remotely. */ *state = SSL_STREAM_STATE_RESET_REMOTE; *app_error_code = !is_write ? qs->peer_reset_stream_aec : qs->peer_stop_sending_aec; } else if (is_write && ossl_quic_sstream_get_final_size(qs->sstream, &final_size)) { /* * Stream has been finished. Stream reset takes precedence over this for * the write case as peer may not have received all data. */ *state = SSL_STREAM_STATE_FINISHED; } else { /* Stream still healthy. */ *state = SSL_STREAM_STATE_OK; } } static int quic_get_stream_state(SSL *ssl, int is_write) { QCTX ctx; int state; if (!expect_quic_with_stream_lock(ssl, /*remote_init=*/-1, /*io=*/0, &ctx)) return SSL_STREAM_STATE_NONE; quic_classify_stream(ctx.qc, ctx.xso->stream, is_write, &state, NULL); qctx_unlock(&ctx); return state; } int ossl_quic_get_stream_read_state(SSL *ssl) { return quic_get_stream_state(ssl, /*is_write=*/0); } /* * SSL_get_stream_write_state * -------------------------- */ int ossl_quic_get_stream_write_state(SSL *ssl) { return quic_get_stream_state(ssl, /*is_write=*/1); } /* * SSL_get_stream_read_error_code * ------------------------------ */ static int quic_get_stream_error_code(SSL *ssl, int is_write, uint64_t *app_error_code) { QCTX ctx; int state; if (!expect_quic_with_stream_lock(ssl, /*remote_init=*/-1, /*io=*/0, &ctx)) return -1; quic_classify_stream(ctx.qc, ctx.xso->stream, /*is_write=*/0, &state, app_error_code); qctx_unlock(&ctx); switch (state) { case SSL_STREAM_STATE_FINISHED: return 0; case SSL_STREAM_STATE_RESET_LOCAL: case SSL_STREAM_STATE_RESET_REMOTE: return 1; default: return -1; } } int ossl_quic_get_stream_read_error_code(SSL *ssl, uint64_t *app_error_code) { return quic_get_stream_error_code(ssl, /*is_write=*/0, app_error_code); } /* * SSL_get_stream_write_error_code * ------------------------------- */ int ossl_quic_get_stream_write_error_code(SSL *ssl, uint64_t *app_error_code) { return quic_get_stream_error_code(ssl, /*is_write=*/1, app_error_code); } /* * Write buffer size mutation * -------------------------- */ int ossl_quic_set_write_buffer_size(SSL *ssl, size_t size) { int ret = 0; QCTX ctx; if (!expect_quic_with_stream_lock(ssl, /*remote_init=*/-1, /*io=*/0, &ctx)) return 0; if (!ossl_quic_stream_has_send(ctx.xso->stream)) { /* Called on a unidirectional receive-only stream - error. */ QUIC_RAISE_NON_NORMAL_ERROR(&ctx, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED, NULL); goto out; } if (!ossl_quic_stream_has_send_buffer(ctx.xso->stream)) { /* * If the stream has a send part but we have disposed of it because we * no longer need it, this is a no-op. */ ret = 1; goto out; } if (!ossl_quic_sstream_set_buffer_size(ctx.xso->stream->sstream, size)) { QUIC_RAISE_NON_NORMAL_ERROR(&ctx, ERR_R_INTERNAL_ERROR, NULL); goto out; } ret = 1; out: qctx_unlock(&ctx); return ret; } /* * SSL_get_conn_close_info * ----------------------- */ int ossl_quic_get_conn_close_info(SSL *ssl, SSL_CONN_CLOSE_INFO *info, size_t info_len) { QCTX ctx; const QUIC_TERMINATE_CAUSE *tc; if (!expect_quic_conn_only(ssl, &ctx)) return -1; tc = ossl_quic_channel_get_terminate_cause(ctx.qc->ch); if (tc == NULL) return 0; info->error_code = tc->error_code; info->frame_type = tc->frame_type; info->reason = tc->reason; info->reason_len = tc->reason_len; info->flags = 0; if (!tc->remote) info->flags |= SSL_CONN_CLOSE_FLAG_LOCAL; if (!tc->app) info->flags |= SSL_CONN_CLOSE_FLAG_TRANSPORT; return 1; } /* * SSL_key_update * -------------- */ int ossl_quic_key_update(SSL *ssl, int update_type) { QCTX ctx; if (!expect_quic_conn_only(ssl, &ctx)) return 0; switch (update_type) { case SSL_KEY_UPDATE_NOT_REQUESTED: /* * QUIC signals peer key update implicily by triggering a local * spontaneous TXKU. Silently upgrade this to SSL_KEY_UPDATE_REQUESTED. */ case SSL_KEY_UPDATE_REQUESTED: break; default: QUIC_RAISE_NON_NORMAL_ERROR(&ctx, ERR_R_PASSED_INVALID_ARGUMENT, NULL); return 0; } qctx_lock(&ctx); /* Attempt to perform a TXKU. */ if (!ossl_quic_channel_trigger_txku(ctx.qc->ch)) { QUIC_RAISE_NON_NORMAL_ERROR(&ctx, SSL_R_TOO_MANY_KEY_UPDATES, NULL); qctx_unlock(&ctx); return 0; } qctx_unlock(&ctx); return 1; } /* * SSL_get_key_update_type * ----------------------- */ int ossl_quic_get_key_update_type(const SSL *s) { /* * We always handle key updates immediately so a key update is never * pending. */ return SSL_KEY_UPDATE_NONE; } /** * @brief Allocates an SSL object for a user from a QUIC channel. * * This function creates a new QUIC_CONNECTION object based on an incoming * connection associated with the provided QUIC_LISTENER. If the connection * creation fails, the function returns NULL. Otherwise, it returns a pointer * to the SSL object associated with the newly created connection. * * Note: This function is a registered port callback made from * ossl_quic_new_listener and ossl_quic_new_listener_from, and allows for * pre-allocation of the user_ssl object when a channel is created, rather than * when it is accepted * * @param ch Pointer to the QUIC_CHANNEL representing the incoming connection. * @param arg Pointer to a QUIC_LISTENER used to create the connection. * * @return Pointer to the SSL object on success, or NULL on failure. */ static SSL *alloc_port_user_ssl(QUIC_CHANNEL *ch, void *arg) { QUIC_LISTENER *ql = arg; QUIC_CONNECTION *qc = create_qc_from_incoming_conn(ql, ch); return (qc == NULL) ? NULL : &qc->obj.ssl; } /* * QUIC Front-End I/O API: Listeners * ================================= */ /* * SSL_new_listener * ---------------- */ SSL *ossl_quic_new_listener(SSL_CTX *ctx, uint64_t flags) { QUIC_LISTENER *ql = NULL; QUIC_ENGINE_ARGS engine_args = {0}; QUIC_PORT_ARGS port_args = {0}; if ((ql = OPENSSL_zalloc(sizeof(*ql))) == NULL) { QUIC_RAISE_NON_NORMAL_ERROR(NULL, ERR_R_CRYPTO_LIB, NULL); goto err; } #if defined(OPENSSL_THREADS) if ((ql->mutex = ossl_crypto_mutex_new()) == NULL) { QUIC_RAISE_NON_NORMAL_ERROR(NULL, ERR_R_CRYPTO_LIB, NULL); goto err; } #endif engine_args.libctx = ctx->libctx; engine_args.propq = ctx->propq; #if defined(OPENSSL_THREADS) engine_args.mutex = ql->mutex; #endif if (need_notifier_for_domain_flags(ctx->domain_flags)) engine_args.reactor_flags |= QUIC_REACTOR_FLAG_USE_NOTIFIER; if ((ql->engine = ossl_quic_engine_new(&engine_args)) == NULL) { QUIC_RAISE_NON_NORMAL_ERROR(NULL, ERR_R_INTERNAL_ERROR, NULL); goto err; } port_args.channel_ctx = ctx; port_args.is_multi_conn = 1; port_args.get_conn_user_ssl = alloc_port_user_ssl; port_args.user_ssl_arg = ql; if ((flags & SSL_LISTENER_FLAG_NO_VALIDATE) == 0) port_args.do_addr_validation = 1; ql->port = ossl_quic_engine_create_port(ql->engine, &port_args); if (ql->port == NULL) { QUIC_RAISE_NON_NORMAL_ERROR(NULL, ERR_R_INTERNAL_ERROR, NULL); goto err; } /* TODO(QUIC FUTURE): Implement SSL_LISTENER_FLAG_NO_ACCEPT */ ossl_quic_port_set_allow_incoming(ql->port, 1); /* Initialise the QUIC_LISTENER's object header. */ if (!ossl_quic_obj_init(&ql->obj, ctx, SSL_TYPE_QUIC_LISTENER, NULL, ql->engine, ql->port)) goto err; return &ql->obj.ssl; err: if (ql != NULL) ossl_quic_engine_free(ql->engine); #if defined(OPENSSL_THREADS) ossl_crypto_mutex_free(&ql->mutex); #endif OPENSSL_free(ql); return NULL; } /* * SSL_new_listener_from * --------------------- */ SSL *ossl_quic_new_listener_from(SSL *ssl, uint64_t flags) { QCTX ctx; QUIC_LISTENER *ql = NULL; QUIC_PORT_ARGS port_args = {0}; if (!expect_quic_domain(ssl, &ctx)) return NULL; if (!SSL_up_ref(&ctx.qd->obj.ssl)) return NULL; qctx_lock(&ctx); if ((ql = OPENSSL_zalloc(sizeof(*ql))) == NULL) { QUIC_RAISE_NON_NORMAL_ERROR(NULL, ERR_R_CRYPTO_LIB, NULL); goto err; } port_args.channel_ctx = ssl->ctx; port_args.is_multi_conn = 1; port_args.get_conn_user_ssl = alloc_port_user_ssl; port_args.user_ssl_arg = ql; if ((flags & SSL_LISTENER_FLAG_NO_VALIDATE) == 0) port_args.do_addr_validation = 1; ql->port = ossl_quic_engine_create_port(ctx.qd->engine, &port_args); if (ql->port == NULL) { QUIC_RAISE_NON_NORMAL_ERROR(NULL, ERR_R_INTERNAL_ERROR, NULL); goto err; } ql->domain = ctx.qd; ql->engine = ctx.qd->engine; #if defined(OPENSSL_THREADS) ql->mutex = ctx.qd->mutex; #endif /* * TODO(QUIC FUTURE): Implement SSL_LISTENER_FLAG_NO_ACCEPT * Given that we have apis to create client SSL objects from * server SSL objects (see SSL_new_from_listener), we have aspirations * to enable a flag that allows for the creation of the latter, but not * be used to do accept any connections. This is a placeholder for the * implementation of that flag */ ossl_quic_port_set_allow_incoming(ql->port, 1); /* Initialise the QUIC_LISTENER's object header. */ if (!ossl_quic_obj_init(&ql->obj, ssl->ctx, SSL_TYPE_QUIC_LISTENER, &ctx.qd->obj.ssl, NULL, ql->port)) goto err; qctx_unlock(&ctx); return &ql->obj.ssl; err: if (ql != NULL) ossl_quic_port_free(ql->port); OPENSSL_free(ql); qctx_unlock(&ctx); SSL_free(&ctx.qd->obj.ssl); return NULL; } /* * SSL_new_from_listener * --------------------- * code here is derived from ossl_quic_new(). The `ssl` argument is * a listener object which already comes with QUIC port/engine. The newly * created QUIC connection object (QCSO) is going to share the port/engine * with listener (`ssl`). The `ssl` also becomes a parent of QCSO created * by this function. The caller uses QCSO instance to connect to * remote QUIC server. * * The QCSO created here requires us to also create a channel so we * can connect to remote server. */ SSL *ossl_quic_new_from_listener(SSL *ssl, uint64_t flags) { QCTX ctx; QUIC_CONNECTION *qc = NULL; QUIC_LISTENER *ql; SSL_CONNECTION *sc = NULL; if (flags != 0) return NULL; if (!expect_quic_listener(ssl, &ctx)) return NULL; if (!SSL_up_ref(&ctx.ql->obj.ssl)) return NULL; qctx_lock(&ctx); ql = ctx.ql; /* * listeners (server) contexts don't typically * allocate a token cache because they don't need * to store them, but here we are using a server side * ctx as a client, so we should allocate one now */ if (ssl->ctx->tokencache == NULL) if ((ssl->ctx->tokencache = ossl_quic_new_token_store()) == NULL) goto err; if ((qc = OPENSSL_zalloc(sizeof(*qc))) == NULL) { QUIC_RAISE_NON_NORMAL_ERROR(NULL, ERR_R_CRYPTO_LIB, NULL); goto err; } /* * NOTE: setting a listener here is needed so `qc_cleanup()` does the right * thing. Setting listener to ql avoids premature destruction of port in * qc_cleanup() */ qc->listener = ql; qc->engine = ql->engine; qc->port = ql->port; /* create channel */ #if defined(OPENSSL_THREADS) /* this is the engine mutex */ qc->mutex = ql->mutex; #endif #if !defined(OPENSSL_NO_QUIC_THREAD_ASSIST) qc->is_thread_assisted = ((ql->obj.domain_flags & SSL_DOMAIN_FLAG_THREAD_ASSISTED) != 0); #endif /* Create the handshake layer. */ qc->tls = ossl_ssl_connection_new_int(ql->obj.ssl.ctx, NULL, TLS_method()); if (qc->tls == NULL || (sc = SSL_CONNECTION_FROM_SSL(qc->tls)) == NULL) { QUIC_RAISE_NON_NORMAL_ERROR(NULL, ERR_R_INTERNAL_ERROR, NULL); goto err; } sc->s3.flags |= TLS1_FLAGS_QUIC | TLS1_FLAGS_QUIC_INTERNAL; qc->default_ssl_options = OSSL_QUIC_PERMITTED_OPTIONS; qc->last_error = SSL_ERROR_NONE; /* * This is QCSO, we don't expect to accept connections * on success the channel assumes ownership of tls, we need * to grab reference for qc. */ qc->ch = ossl_quic_port_create_outgoing(qc->port, qc->tls); ossl_quic_channel_set_msg_callback(qc->ch, ql->obj.ssl.ctx->msg_callback, &qc->obj.ssl); ossl_quic_channel_set_msg_callback_arg(qc->ch, ql->obj.ssl.ctx->msg_callback_arg); /* * We deliberately pass NULL for engine and port, because we don't want to * to turn QCSO we create here into an event leader, nor port leader. * Both those roles are occupied already by listener (`ssl`) we use * to create a new QCSO here. */ if (!ossl_quic_obj_init(&qc->obj, ql->obj.ssl.ctx, SSL_TYPE_QUIC_CONNECTION, &ql->obj.ssl, NULL, NULL)) { QUIC_RAISE_NON_NORMAL_ERROR(NULL, ERR_R_INTERNAL_ERROR, NULL); goto err; } /* Initialise libssl APL-related state. */ qc->default_stream_mode = SSL_DEFAULT_STREAM_MODE_AUTO_BIDI; qc->default_ssl_mode = qc->obj.ssl.ctx->mode; qc->default_ssl_options = qc->obj.ssl.ctx->options & OSSL_QUIC_PERMITTED_OPTIONS; qc->incoming_stream_policy = SSL_INCOMING_STREAM_POLICY_AUTO; qc->last_error = SSL_ERROR_NONE; qc_update_reject_policy(qc); qctx_unlock(&ctx); return &qc->obj.ssl; err: if (qc != NULL) { qc_cleanup(qc, /* have_lock= */ 0); OPENSSL_free(qc); } qctx_unlock(&ctx); SSL_free(&ctx.ql->obj.ssl); return NULL; } /* * SSL_listen * ---------- */ QUIC_NEEDS_LOCK static int ql_listen(QUIC_LISTENER *ql) { if (ql->listening) return 1; ossl_quic_port_set_allow_incoming(ql->port, 1); ql->listening = 1; return 1; } QUIC_TAKES_LOCK int ossl_quic_listen(SSL *ssl) { QCTX ctx; int ret; if (!expect_quic_listener(ssl, &ctx)) return 0; qctx_lock_for_io(&ctx); ret = ql_listen(ctx.ql); qctx_unlock(&ctx); return ret; } /* * SSL_accept_connection * --------------------- */ static int quic_accept_connection_wait(void *arg) { QUIC_PORT *port = arg; if (!ossl_quic_port_is_running(port)) return -1; if (ossl_quic_port_have_incoming(port)) return 1; return 0; } QUIC_TAKES_LOCK SSL *ossl_quic_accept_connection(SSL *ssl, uint64_t flags) { int ret; QCTX ctx; SSL *conn_ssl = NULL; SSL_CONNECTION *conn = NULL; QUIC_CHANNEL *new_ch = NULL; QUIC_CONNECTION *qc; int no_block = ((flags & SSL_ACCEPT_CONNECTION_NO_BLOCK) != 0); if (!expect_quic_listener(ssl, &ctx)) return NULL; qctx_lock_for_io(&ctx); if (!ql_listen(ctx.ql)) goto out; /* Wait for an incoming connection if needed. */ new_ch = ossl_quic_port_pop_incoming(ctx.ql->port); if (new_ch == NULL && ossl_quic_port_is_running(ctx.ql->port)) { if (!no_block && qctx_blocking(&ctx)) { ret = block_until_pred(&ctx, quic_accept_connection_wait, ctx.ql->port, 0); if (ret < 1) goto out; } else { qctx_maybe_autotick(&ctx); } if (!ossl_quic_port_is_running(ctx.ql->port)) goto out; new_ch = ossl_quic_port_pop_incoming(ctx.ql->port); } if (new_ch == NULL && ossl_quic_port_is_running(ctx.ql->port)) { /* No connections already queued. */ ossl_quic_reactor_tick(ossl_quic_engine_get0_reactor(ctx.ql->engine), 0); new_ch = ossl_quic_port_pop_incoming(ctx.ql->port); } /* * port_make_channel pre-allocates our user_ssl for us for each newly * created channel, so once we pop the new channel from the port above * we just need to extract it */ if (new_ch == NULL || (conn_ssl = ossl_quic_channel_get0_tls(new_ch)) == NULL || (conn = SSL_CONNECTION_FROM_SSL(conn_ssl)) == NULL || (conn_ssl = SSL_CONNECTION_GET_USER_SSL(conn)) == NULL) goto out; qc = (QUIC_CONNECTION *)conn_ssl; qc->listener = ctx.ql; qc->pending = 0; if (!SSL_up_ref(&ctx.ql->obj.ssl)) { SSL_free(conn_ssl); SSL_free(ossl_quic_channel_get0_tls(new_ch)); conn_ssl = NULL; } out: qctx_unlock(&ctx); return conn_ssl; } static QUIC_CONNECTION *create_qc_from_incoming_conn(QUIC_LISTENER *ql, QUIC_CHANNEL *ch) { QUIC_CONNECTION *qc = NULL; if ((qc = OPENSSL_zalloc(sizeof(*qc))) == NULL) { QUIC_RAISE_NON_NORMAL_ERROR(NULL, ERR_R_CRYPTO_LIB, NULL); goto err; } if (!ossl_quic_obj_init(&qc->obj, ql->obj.ssl.ctx, SSL_TYPE_QUIC_CONNECTION, &ql->obj.ssl, NULL, NULL)) { QUIC_RAISE_NON_NORMAL_ERROR(NULL, ERR_R_INTERNAL_ERROR, NULL); goto err; } ossl_quic_channel_get_peer_addr(ch, &qc->init_peer_addr); /* best effort */ qc->pending = 1; qc->engine = ql->engine; qc->port = ql->port; qc->ch = ch; #if defined(OPENSSL_THREADS) qc->mutex = ql->mutex; #endif qc->tls = ossl_quic_channel_get0_tls(ch); qc->started = 1; qc->as_server = 1; qc->as_server_state = 1; qc->default_stream_mode = SSL_DEFAULT_STREAM_MODE_AUTO_BIDI; qc->default_ssl_options = ql->obj.ssl.ctx->options & OSSL_QUIC_PERMITTED_OPTIONS; qc->incoming_stream_policy = SSL_INCOMING_STREAM_POLICY_AUTO; qc->last_error = SSL_ERROR_NONE; qc_update_reject_policy(qc); return qc; err: OPENSSL_free(qc); return NULL; } DEFINE_LHASH_OF_EX(QUIC_TOKEN); struct ssl_token_store_st { LHASH_OF(QUIC_TOKEN) *cache; CRYPTO_REF_COUNT references; CRYPTO_MUTEX *mutex; }; static unsigned long quic_token_hash(const QUIC_TOKEN *item) { return (unsigned long)ossl_fnv1a_hash(item->hashkey, item->hashkey_len); } static int quic_token_cmp(const QUIC_TOKEN *a, const QUIC_TOKEN *b) { if (a->hashkey_len != b->hashkey_len) return 1; return memcmp(a->hashkey, b->hashkey, a->hashkey_len); } SSL_TOKEN_STORE *ossl_quic_new_token_store(void) { int ok = 0; SSL_TOKEN_STORE *newcache = OPENSSL_zalloc(sizeof(SSL_TOKEN_STORE)); if (newcache == NULL) goto out; newcache->cache = lh_QUIC_TOKEN_new(quic_token_hash, quic_token_cmp); if (newcache->cache == NULL) goto out; #if defined(OPENSSL_THREADS) if ((newcache->mutex = ossl_crypto_mutex_new()) == NULL) goto out; #endif if (!CRYPTO_NEW_REF(&newcache->references, 1)) goto out; ok = 1; out: if (!ok) { ossl_quic_free_token_store(newcache); newcache = NULL; } return newcache; } static void free_this_token(QUIC_TOKEN *tok) { ossl_quic_free_peer_token(tok); } void ossl_quic_free_token_store(SSL_TOKEN_STORE *hdl) { int refs; if (hdl == NULL) return; if (!CRYPTO_DOWN_REF(&hdl->references, &refs)) return; if (refs > 0) return; /* last reference, we can clean up */ ossl_crypto_mutex_free(&hdl->mutex); lh_QUIC_TOKEN_doall(hdl->cache, free_this_token); lh_QUIC_TOKEN_free(hdl->cache); CRYPTO_FREE_REF(&hdl->references); OPENSSL_free(hdl); return; } /** * @brief build a new QUIC_TOKEN * * This function creates a new token storage structure for saving in our * tokencache * * In an effort to make allocation and freeing of these tokens a bit faster * We do them in a single allocation in this format * +---------------+ --\ * | hashkey * |---| | * | hashkey_len | | | QUIC_TOKEN * | token * |---|--| | * | token_len | | | | * +---------------+<--| | --/ * | hashkey buf | | * | | | * |---------------|<-----| * | token buf | * | | * +---------------+ * * @param peer - the peer address that sent the token * @param token - the buffer holding the token * @param token_len - the size of token * * @returns a QUIC_TOKEN pointer or NULL on error */ static QUIC_TOKEN *ossl_quic_build_new_token(BIO_ADDR *peer, uint8_t *token, size_t token_len) { QUIC_TOKEN *new_token; size_t hashkey_len = 0; size_t addr_len = 0; int family; unsigned short port; int *famptr; unsigned short *portptr; uint8_t *addrptr; if ((token != NULL && token_len == 0) || (token == NULL && token_len != 0)) return NULL; if (!BIO_ADDR_rawaddress(peer, NULL, &addr_len)) return NULL; family = BIO_ADDR_family(peer); port = BIO_ADDR_rawport(peer); hashkey_len += sizeof(int); /* hashkey(family) */ hashkey_len += sizeof(unsigned short); /* hashkey(port) */ hashkey_len += addr_len; /* hashkey(address) */ new_token = OPENSSL_zalloc(sizeof(QUIC_TOKEN) + hashkey_len + token_len); if (new_token == NULL) return NULL; if (!CRYPTO_NEW_REF(&new_token->references, 1)) { OPENSSL_free(new_token); return NULL; } new_token->hashkey_len = hashkey_len; /* hashkey is allocated inline, immediately after the QUIC_TOKEN struct */ new_token->hashkey = (uint8_t *)(new_token + 1); /* token buffer follows the hashkey in the inline allocation */ new_token->token = new_token->hashkey + hashkey_len; new_token->token_len = token_len; famptr = (int *)new_token->hashkey; portptr = (unsigned short *)(famptr + 1); addrptr = (uint8_t *)(portptr + 1); *famptr = family; *portptr = port; if (!BIO_ADDR_rawaddress(peer, addrptr, NULL)) { ossl_quic_free_peer_token(new_token); return NULL; } if (token != NULL) memcpy(new_token->token, token, token_len); return new_token; } int ossl_quic_set_peer_token(SSL_CTX *ctx, BIO_ADDR *peer, const uint8_t *token, size_t token_len) { SSL_TOKEN_STORE *c = ctx->tokencache; QUIC_TOKEN *tok, *old = NULL; if (ctx->tokencache == NULL) return 0; tok = ossl_quic_build_new_token(peer, (uint8_t *)token, token_len); if (tok == NULL) return 0; /* we might be sharing this cache, lock it */ ossl_crypto_mutex_lock(c->mutex); old = lh_QUIC_TOKEN_retrieve(c->cache, tok); if (old != NULL) { lh_QUIC_TOKEN_delete(c->cache, old); ossl_quic_free_peer_token(old); } lh_QUIC_TOKEN_insert(c->cache, tok); ossl_crypto_mutex_unlock(c->mutex); return 1; } int ossl_quic_get_peer_token(SSL_CTX *ctx, BIO_ADDR *peer, QUIC_TOKEN **token) { SSL_TOKEN_STORE *c = ctx->tokencache; QUIC_TOKEN *key = NULL; QUIC_TOKEN *tok = NULL; int ret; int rc = 0; if (c == NULL) return 0; key = ossl_quic_build_new_token(peer, NULL, 0); if (key == NULL) return 0; ossl_crypto_mutex_lock(c->mutex); tok = lh_QUIC_TOKEN_retrieve(c->cache, key); if (tok != NULL) { *token = tok; CRYPTO_UP_REF(&tok->references, &ret); rc = 1; } ossl_crypto_mutex_unlock(c->mutex); ossl_quic_free_peer_token(key); return rc; } void ossl_quic_free_peer_token(QUIC_TOKEN *token) { int refs = 0; if (!CRYPTO_DOWN_REF(&token->references, &refs)) return; if (refs > 0) return; CRYPTO_FREE_REF(&token->references); OPENSSL_free(token); } /* * SSL_get_accept_connection_queue_len * ----------------------------------- */ QUIC_TAKES_LOCK size_t ossl_quic_get_accept_connection_queue_len(SSL *ssl) { QCTX ctx; int ret; if (!expect_quic_listener(ssl, &ctx)) return 0; qctx_lock(&ctx); ret = ossl_quic_port_get_num_incoming_channels(ctx.ql->port); qctx_unlock(&ctx); return ret; } /* * QUIC Front-End I/O API: Domains * =============================== */ /* * SSL_new_domain * -------------- */ SSL *ossl_quic_new_domain(SSL_CTX *ctx, uint64_t flags) { QUIC_DOMAIN *qd = NULL; QUIC_ENGINE_ARGS engine_args = {0}; uint64_t domain_flags; domain_flags = ctx->domain_flags; if ((flags & (SSL_DOMAIN_FLAG_SINGLE_THREAD | SSL_DOMAIN_FLAG_MULTI_THREAD | SSL_DOMAIN_FLAG_THREAD_ASSISTED)) != 0) domain_flags = flags; else domain_flags = ctx->domain_flags | flags; if (!ossl_adjust_domain_flags(domain_flags, &domain_flags)) return NULL; if ((qd = OPENSSL_zalloc(sizeof(*qd))) == NULL) { QUIC_RAISE_NON_NORMAL_ERROR(NULL, ERR_R_CRYPTO_LIB, NULL); return NULL; } #if defined(OPENSSL_THREADS) if ((qd->mutex = ossl_crypto_mutex_new()) == NULL) { QUIC_RAISE_NON_NORMAL_ERROR(NULL, ERR_R_CRYPTO_LIB, NULL); goto err; } #endif engine_args.libctx = ctx->libctx; engine_args.propq = ctx->propq; #if defined(OPENSSL_THREADS) engine_args.mutex = qd->mutex; #endif if (need_notifier_for_domain_flags(domain_flags)) engine_args.reactor_flags |= QUIC_REACTOR_FLAG_USE_NOTIFIER; if ((qd->engine = ossl_quic_engine_new(&engine_args)) == NULL) { QUIC_RAISE_NON_NORMAL_ERROR(NULL, ERR_R_INTERNAL_ERROR, NULL); goto err; } /* Initialise the QUIC_DOMAIN's object header. */ if (!ossl_quic_obj_init(&qd->obj, ctx, SSL_TYPE_QUIC_DOMAIN, NULL, qd->engine, NULL)) goto err; ossl_quic_obj_set_domain_flags(&qd->obj, domain_flags); return &qd->obj.ssl; err: ossl_quic_engine_free(qd->engine); #if defined(OPENSSL_THREADS) ossl_crypto_mutex_free(&qd->mutex); #endif OPENSSL_free(qd); return NULL; } /* * QUIC Front-End I/O API: SSL_CTX Management * ========================================== */ long ossl_quic_ctx_ctrl(SSL_CTX *ctx, int cmd, long larg, void *parg) { switch (cmd) { default: return ssl3_ctx_ctrl(ctx, cmd, larg, parg); } } long ossl_quic_callback_ctrl(SSL *s, int cmd, void (*fp) (void)) { QCTX ctx; if (!expect_quic_conn_only(s, &ctx)) return 0; switch (cmd) { case SSL_CTRL_SET_MSG_CALLBACK: ossl_quic_channel_set_msg_callback(ctx.qc->ch, (ossl_msg_cb)fp, &ctx.qc->obj.ssl); /* This callback also needs to be set on the internal SSL object */ return ssl3_callback_ctrl(ctx.qc->tls, cmd, fp);; default: /* Probably a TLS related ctrl. Defer to our internal SSL object */ return ssl3_callback_ctrl(ctx.qc->tls, cmd, fp); } } long ossl_quic_ctx_callback_ctrl(SSL_CTX *ctx, int cmd, void (*fp) (void)) { return ssl3_ctx_callback_ctrl(ctx, cmd, fp); } int ossl_quic_renegotiate_check(SSL *ssl, int initok) { /* We never do renegotiation. */ return 0; } const SSL_CIPHER *ossl_quic_get_cipher_by_char(const unsigned char *p) { const SSL_CIPHER *ciph = ssl3_get_cipher_by_char(p); + if (ciph == NULL) + return NULL; if ((ciph->algorithm2 & SSL_QUIC) == 0) return NULL; return ciph; } /* * These functions define the TLSv1.2 (and below) ciphers that are supported by * the SSL_METHOD. Since QUIC only supports TLSv1.3 we don't support any. */ int ossl_quic_num_ciphers(void) { return 0; } const SSL_CIPHER *ossl_quic_get_cipher(unsigned int u) { return NULL; } /* * SSL_get_shutdown() * ------------------ */ int ossl_quic_get_shutdown(const SSL *s) { QCTX ctx; int shut = 0; if (!expect_quic_conn_only(s, &ctx)) return 0; if (ossl_quic_channel_is_term_any(ctx.qc->ch)) { shut |= SSL_SENT_SHUTDOWN; if (!ossl_quic_channel_is_closing(ctx.qc->ch)) shut |= SSL_RECEIVED_SHUTDOWN; } return shut; } /* * QUIC Polling Support APIs * ========================= */ /* Do we have the R (read) condition? */ QUIC_NEEDS_LOCK static int test_poll_event_r(QUIC_XSO *xso) { int fin = 0; size_t avail = 0; /* * If a stream has had the fin bit set on the last packet * received, then we need to return a 1 here to raise * SSL_POLL_EVENT_R, so that the stream can have its completion * detected and closed gracefully by an application. * However, if the client reads the data via SSL_read[_ex], that api * provides no stream status, and as a result the stream state moves to * QUIC_RSTREAM_STATE_DATA_READ, and the receive buffer is freed, which * stored the fin state, so its not directly know-able here. Instead * check for the stream state being QUIC_RSTREAM_STATE_DATA_READ, which * is only set if the last stream frame received had the fin bit set, and * the client read the data. This catches our poll/read/poll case */ if (xso->stream->recv_state == QUIC_RSTREAM_STATE_DATA_READ) return 1; return ossl_quic_stream_has_recv_buffer(xso->stream) && ossl_quic_rstream_available(xso->stream->rstream, &avail, &fin) && (avail > 0 || (fin && !xso->retired_fin)); } /* Do we have the ER (exception: read) condition? */ QUIC_NEEDS_LOCK static int test_poll_event_er(QUIC_XSO *xso) { return ossl_quic_stream_has_recv(xso->stream) && ossl_quic_stream_recv_is_reset(xso->stream) && !xso->retired_fin; } /* Do we have the W (write) condition? */ QUIC_NEEDS_LOCK static int test_poll_event_w(QUIC_XSO *xso) { return !xso->conn->shutting_down && ossl_quic_stream_has_send_buffer(xso->stream) && ossl_quic_sstream_get_buffer_avail(xso->stream->sstream) && !ossl_quic_sstream_get_final_size(xso->stream->sstream, NULL) && ossl_quic_txfc_get_cwm(&xso->stream->txfc) > ossl_quic_sstream_get_cur_size(xso->stream->sstream) && quic_mutation_allowed(xso->conn, /*req_active=*/1); } /* Do we have the EW (exception: write) condition? */ QUIC_NEEDS_LOCK static int test_poll_event_ew(QUIC_XSO *xso) { return ossl_quic_stream_has_send(xso->stream) && xso->stream->peer_stop_sending && !xso->requested_reset && !xso->conn->shutting_down; } /* Do we have the EC (exception: connection) condition? */ QUIC_NEEDS_LOCK static int test_poll_event_ec(QUIC_CONNECTION *qc) { return ossl_quic_channel_is_term_any(qc->ch); } /* Do we have the ECD (exception: connection drained) condition? */ QUIC_NEEDS_LOCK static int test_poll_event_ecd(QUIC_CONNECTION *qc) { return ossl_quic_channel_is_terminated(qc->ch); } /* Do we have the IS (incoming: stream) condition? */ QUIC_NEEDS_LOCK static int test_poll_event_is(QUIC_CONNECTION *qc, int is_uni) { return ossl_quic_stream_map_get_accept_queue_len(ossl_quic_channel_get_qsm(qc->ch), is_uni); } /* Do we have the OS (outgoing: stream) condition? */ QUIC_NEEDS_LOCK static int test_poll_event_os(QUIC_CONNECTION *qc, int is_uni) { /* Is it currently possible for us to make an outgoing stream? */ return quic_mutation_allowed(qc, /*req_active=*/1) && ossl_quic_channel_get_local_stream_count_avail(qc->ch, is_uni) > 0; } /* Do we have the EL (exception: listener) condition? */ QUIC_NEEDS_LOCK static int test_poll_event_el(QUIC_LISTENER *ql) { return !ossl_quic_port_is_running(ql->port); } /* Do we have the IC (incoming: connection) condition? */ QUIC_NEEDS_LOCK static int test_poll_event_ic(QUIC_LISTENER *ql) { return ossl_quic_port_get_num_incoming_channels(ql->port) > 0; } QUIC_TAKES_LOCK int ossl_quic_conn_poll_events(SSL *ssl, uint64_t events, int do_tick, uint64_t *p_revents) { QCTX ctx; uint64_t revents = 0; if (!expect_quic_csl(ssl, &ctx)) return 0; qctx_lock(&ctx); if (ctx.qc != NULL && !ctx.qc->started) { /* We can only try to write on non-started connection. */ if ((events & SSL_POLL_EVENT_W) != 0) revents |= SSL_POLL_EVENT_W; goto end; } if (do_tick) ossl_quic_reactor_tick(ossl_quic_obj_get0_reactor(ctx.obj), 0); if (ctx.xso != NULL) { /* SSL object has a stream component. */ if ((events & SSL_POLL_EVENT_R) != 0 && test_poll_event_r(ctx.xso)) revents |= SSL_POLL_EVENT_R; if ((events & SSL_POLL_EVENT_ER) != 0 && test_poll_event_er(ctx.xso)) revents |= SSL_POLL_EVENT_ER; if ((events & SSL_POLL_EVENT_W) != 0 && test_poll_event_w(ctx.xso)) revents |= SSL_POLL_EVENT_W; if ((events & SSL_POLL_EVENT_EW) != 0 && test_poll_event_ew(ctx.xso)) revents |= SSL_POLL_EVENT_EW; } if (ctx.qc != NULL && !ctx.is_stream) { if ((events & SSL_POLL_EVENT_EC) != 0 && test_poll_event_ec(ctx.qc)) revents |= SSL_POLL_EVENT_EC; if ((events & SSL_POLL_EVENT_ECD) != 0 && test_poll_event_ecd(ctx.qc)) revents |= SSL_POLL_EVENT_ECD; if ((events & SSL_POLL_EVENT_ISB) != 0 && test_poll_event_is(ctx.qc, /*uni=*/0)) revents |= SSL_POLL_EVENT_ISB; if ((events & SSL_POLL_EVENT_ISU) != 0 && test_poll_event_is(ctx.qc, /*uni=*/1)) revents |= SSL_POLL_EVENT_ISU; if ((events & SSL_POLL_EVENT_OSB) != 0 && test_poll_event_os(ctx.qc, /*uni=*/0)) revents |= SSL_POLL_EVENT_OSB; if ((events & SSL_POLL_EVENT_OSU) != 0 && test_poll_event_os(ctx.qc, /*uni=*/1)) revents |= SSL_POLL_EVENT_OSU; } if (ctx.is_listener) { if ((events & SSL_POLL_EVENT_EL) != 0 && test_poll_event_el(ctx.ql)) revents |= SSL_POLL_EVENT_EL; if ((events & SSL_POLL_EVENT_IC) != 0 && test_poll_event_ic(ctx.ql)) revents |= SSL_POLL_EVENT_IC; } end: qctx_unlock(&ctx); *p_revents = revents; return 1; } QUIC_TAKES_LOCK int ossl_quic_get_notifier_fd(SSL *ssl) { QCTX ctx; QUIC_REACTOR *rtor; RIO_NOTIFIER *nfy; int nfd = -1; if (!expect_quic_any(ssl, &ctx)) return -1; qctx_lock(&ctx); rtor = ossl_quic_obj_get0_reactor(ctx.obj); nfy = ossl_quic_reactor_get0_notifier(rtor); if (nfy == NULL) goto end; nfd = ossl_rio_notifier_as_fd(nfy); end: qctx_unlock(&ctx); return nfd; } QUIC_TAKES_LOCK void ossl_quic_enter_blocking_section(SSL *ssl, QUIC_REACTOR_WAIT_CTX *wctx) { QCTX ctx; QUIC_REACTOR *rtor; if (!expect_quic_any(ssl, &ctx)) return; qctx_lock(&ctx); rtor = ossl_quic_obj_get0_reactor(ctx.obj); ossl_quic_reactor_wait_ctx_enter(wctx, rtor); qctx_unlock(&ctx); } QUIC_TAKES_LOCK void ossl_quic_leave_blocking_section(SSL *ssl, QUIC_REACTOR_WAIT_CTX *wctx) { QCTX ctx; QUIC_REACTOR *rtor; if (!expect_quic_any(ssl, &ctx)) return; qctx_lock(&ctx); rtor = ossl_quic_obj_get0_reactor(ctx.obj); ossl_quic_reactor_wait_ctx_leave(wctx, rtor); qctx_unlock(&ctx); } /* * Internal Testing APIs * ===================== */ QUIC_CHANNEL *ossl_quic_conn_get_channel(SSL *s) { QCTX ctx; if (!expect_quic_conn_only(s, &ctx)) return NULL; return ctx.qc->ch; } int ossl_quic_set_diag_title(SSL_CTX *ctx, const char *title) { #ifndef OPENSSL_NO_QLOG OPENSSL_free(ctx->qlog_title); ctx->qlog_title = NULL; if (title == NULL) return 1; if ((ctx->qlog_title = OPENSSL_strdup(title)) == NULL) return 0; #endif return 1; } diff --git a/crypto/openssl/ssl/statem/statem_lib.c b/crypto/openssl/ssl/statem/statem_lib.c index 1e11d077f9e0..13f1af7e9fd0 100644 --- a/crypto/openssl/ssl/statem/statem_lib.c +++ b/crypto/openssl/ssl/statem/statem_lib.c @@ -1,2932 +1,2938 @@ /* * Copyright 1995-2025 The OpenSSL Project Authors. All Rights Reserved. * Copyright (c) 2002, Oracle and/or its affiliates. All rights reserved * * Licensed under the Apache License 2.0 (the "License"). You may not use * this file except in compliance with the License. You can obtain a copy * in the file LICENSE in the source distribution or at * https://www.openssl.org/source/license.html */ #include #include #include #include "../ssl_local.h" #include "statem_local.h" #include "internal/cryptlib.h" #include "internal/ssl_unwrap.h" #include #include #include #include #include #include #include /* * Map error codes to TLS/SSL alart types. */ typedef struct x509err2alert_st { int x509err; int alert; } X509ERR2ALERT; /* Fixed value used in the ServerHello random field to identify an HRR */ const unsigned char hrrrandom[] = { 0xcf, 0x21, 0xad, 0x74, 0xe5, 0x9a, 0x61, 0x11, 0xbe, 0x1d, 0x8c, 0x02, 0x1e, 0x65, 0xb8, 0x91, 0xc2, 0xa2, 0x11, 0x16, 0x7a, 0xbb, 0x8c, 0x5e, 0x07, 0x9e, 0x09, 0xe2, 0xc8, 0xa8, 0x33, 0x9c }; int ossl_statem_set_mutator(SSL *s, ossl_statem_mutate_handshake_cb mutate_handshake_cb, ossl_statem_finish_mutate_handshake_cb finish_mutate_handshake_cb, void *mutatearg) { SSL_CONNECTION *sc = SSL_CONNECTION_FROM_SSL(s); if (sc == NULL) return 0; sc->statem.mutate_handshake_cb = mutate_handshake_cb; sc->statem.mutatearg = mutatearg; sc->statem.finish_mutate_handshake_cb = finish_mutate_handshake_cb; return 1; } /* * send s->init_buf in records of type 'type' (SSL3_RT_HANDSHAKE or * SSL3_RT_CHANGE_CIPHER_SPEC) */ int ssl3_do_write(SSL_CONNECTION *s, uint8_t type) { int ret; size_t written = 0; SSL *ssl = SSL_CONNECTION_GET_SSL(s); SSL *ussl = SSL_CONNECTION_GET_USER_SSL(s); /* * If we're running the test suite then we may need to mutate the message * we've been asked to write. Does not happen in normal operation. */ if (s->statem.mutate_handshake_cb != NULL && !s->statem.write_in_progress && type == SSL3_RT_HANDSHAKE && s->init_num >= SSL3_HM_HEADER_LENGTH) { unsigned char *msg; size_t msglen; if (!s->statem.mutate_handshake_cb((unsigned char *)s->init_buf->data, s->init_num, &msg, &msglen, s->statem.mutatearg)) return -1; if (msglen < SSL3_HM_HEADER_LENGTH || !BUF_MEM_grow(s->init_buf, msglen)) return -1; memcpy(s->init_buf->data, msg, msglen); s->init_num = msglen; s->init_msg = s->init_buf->data + SSL3_HM_HEADER_LENGTH; s->statem.finish_mutate_handshake_cb(s->statem.mutatearg); s->statem.write_in_progress = 1; } ret = ssl3_write_bytes(ssl, type, &s->init_buf->data[s->init_off], s->init_num, &written); if (ret <= 0) return -1; if (type == SSL3_RT_HANDSHAKE) /* * should not be done for 'Hello Request's, but in that case we'll * ignore the result anyway * TLS1.3 KeyUpdate and NewSessionTicket do not need to be added */ if (!SSL_CONNECTION_IS_TLS13(s) || (s->statem.hand_state != TLS_ST_SW_SESSION_TICKET && s->statem.hand_state != TLS_ST_CW_KEY_UPDATE && s->statem.hand_state != TLS_ST_SW_KEY_UPDATE)) if (!ssl3_finish_mac(s, (unsigned char *)&s->init_buf->data[s->init_off], written)) return -1; if (written == s->init_num) { s->statem.write_in_progress = 0; if (s->msg_callback) s->msg_callback(1, s->version, type, s->init_buf->data, (size_t)(s->init_off + s->init_num), ussl, s->msg_callback_arg); return 1; } s->init_off += written; s->init_num -= written; return 0; } int tls_close_construct_packet(SSL_CONNECTION *s, WPACKET *pkt, int htype) { size_t msglen; if ((htype != SSL3_MT_CHANGE_CIPHER_SPEC && !WPACKET_close(pkt)) || !WPACKET_get_length(pkt, &msglen) || msglen > INT_MAX) return 0; s->init_num = (int)msglen; s->init_off = 0; return 1; } int tls_setup_handshake(SSL_CONNECTION *s) { int ver_min, ver_max, ok; SSL *ssl = SSL_CONNECTION_GET_SSL(s); SSL_CTX *sctx = SSL_CONNECTION_GET_CTX(s); if (!ssl3_init_finished_mac(s)) { /* SSLfatal() already called */ return 0; } /* Reset any extension flags */ memset(s->ext.extflags, 0, sizeof(s->ext.extflags)); if (ssl_get_min_max_version(s, &ver_min, &ver_max, NULL) != 0) { SSLfatal(s, SSL_AD_PROTOCOL_VERSION, SSL_R_NO_PROTOCOLS_AVAILABLE); return 0; } /* Sanity check that we have MD5-SHA1 if we need it */ if (sctx->ssl_digest_methods[SSL_MD_MD5_SHA1_IDX] == NULL) { int negotiated_minversion; int md5sha1_needed_maxversion = SSL_CONNECTION_IS_DTLS(s) ? DTLS1_VERSION : TLS1_1_VERSION; /* We don't have MD5-SHA1 - do we need it? */ if (ssl_version_cmp(s, ver_max, md5sha1_needed_maxversion) <= 0) { SSLfatal_data(s, SSL_AD_HANDSHAKE_FAILURE, SSL_R_NO_SUITABLE_DIGEST_ALGORITHM, "The max supported SSL/TLS version needs the" " MD5-SHA1 digest but it is not available" " in the loaded providers. Use (D)TLSv1.2 or" " above, or load different providers"); return 0; } ok = 1; /* Don't allow TLSv1.1 or below to be negotiated */ negotiated_minversion = SSL_CONNECTION_IS_DTLS(s) ? DTLS1_2_VERSION : TLS1_2_VERSION; if (ssl_version_cmp(s, ver_min, negotiated_minversion) < 0) ok = SSL_set_min_proto_version(ssl, negotiated_minversion); if (!ok) { /* Shouldn't happen */ SSLfatal(s, SSL_AD_HANDSHAKE_FAILURE, ERR_R_INTERNAL_ERROR); return 0; } } ok = 0; if (s->server) { STACK_OF(SSL_CIPHER) *ciphers = SSL_get_ciphers(ssl); int i; /* * Sanity check that the maximum version we accept has ciphers * enabled. For clients we do this check during construction of the * ClientHello. */ for (i = 0; i < sk_SSL_CIPHER_num(ciphers); i++) { const SSL_CIPHER *c = sk_SSL_CIPHER_value(ciphers, i); int cipher_minprotover = SSL_CONNECTION_IS_DTLS(s) ? c->min_dtls : c->min_tls; int cipher_maxprotover = SSL_CONNECTION_IS_DTLS(s) ? c->max_dtls : c->max_tls; if (ssl_version_cmp(s, ver_max, cipher_minprotover) >= 0 && ssl_version_cmp(s, ver_max, cipher_maxprotover) <= 0) { ok = 1; break; } } if (!ok) { SSLfatal_data(s, SSL_AD_HANDSHAKE_FAILURE, SSL_R_NO_CIPHERS_AVAILABLE, "No ciphers enabled for max supported " "SSL/TLS version"); return 0; } if (SSL_IS_FIRST_HANDSHAKE(s)) { /* N.B. s->session_ctx == s->ctx here */ ssl_tsan_counter(s->session_ctx, &s->session_ctx->stats.sess_accept); } else { /* N.B. s->ctx may not equal s->session_ctx */ ssl_tsan_counter(sctx, &sctx->stats.sess_accept_renegotiate); s->s3.tmp.cert_request = 0; } } else { if (SSL_IS_FIRST_HANDSHAKE(s)) ssl_tsan_counter(s->session_ctx, &s->session_ctx->stats.sess_connect); else ssl_tsan_counter(s->session_ctx, &s->session_ctx->stats.sess_connect_renegotiate); /* mark client_random uninitialized */ memset(s->s3.client_random, 0, sizeof(s->s3.client_random)); s->hit = 0; s->s3.tmp.cert_req = 0; if (SSL_CONNECTION_IS_DTLS(s)) s->statem.use_timer = 1; } return 1; } /* * Size of the to-be-signed TLS13 data, without the hash size itself: * 64 bytes of value 32, 33 context bytes, 1 byte separator */ #define TLS13_TBS_START_SIZE 64 #define TLS13_TBS_PREAMBLE_SIZE (TLS13_TBS_START_SIZE + 33 + 1) static int get_cert_verify_tbs_data(SSL_CONNECTION *s, unsigned char *tls13tbs, void **hdata, size_t *hdatalen) { /* ASCII: "TLS 1.3, server CertificateVerify", in hex for EBCDIC compatibility */ static const char servercontext[] = "\x54\x4c\x53\x20\x31\x2e\x33\x2c\x20\x73\x65\x72" "\x76\x65\x72\x20\x43\x65\x72\x74\x69\x66\x69\x63\x61\x74\x65\x56\x65\x72\x69\x66\x79"; /* ASCII: "TLS 1.3, client CertificateVerify", in hex for EBCDIC compatibility */ static const char clientcontext[] = "\x54\x4c\x53\x20\x31\x2e\x33\x2c\x20\x63\x6c\x69" "\x65\x6e\x74\x20\x43\x65\x72\x74\x69\x66\x69\x63\x61\x74\x65\x56\x65\x72\x69\x66\x79"; if (SSL_CONNECTION_IS_TLS13(s)) { size_t hashlen; /* Set the first 64 bytes of to-be-signed data to octet 32 */ memset(tls13tbs, 32, TLS13_TBS_START_SIZE); /* This copies the 33 bytes of context plus the 0 separator byte */ if (s->statem.hand_state == TLS_ST_CR_CERT_VRFY || s->statem.hand_state == TLS_ST_SW_CERT_VRFY) strcpy((char *)tls13tbs + TLS13_TBS_START_SIZE, servercontext); else strcpy((char *)tls13tbs + TLS13_TBS_START_SIZE, clientcontext); /* * If we're currently reading then we need to use the saved handshake * hash value. We can't use the current handshake hash state because * that includes the CertVerify itself. */ if (s->statem.hand_state == TLS_ST_CR_CERT_VRFY || s->statem.hand_state == TLS_ST_SR_CERT_VRFY) { memcpy(tls13tbs + TLS13_TBS_PREAMBLE_SIZE, s->cert_verify_hash, s->cert_verify_hash_len); hashlen = s->cert_verify_hash_len; } else if (!ssl_handshake_hash(s, tls13tbs + TLS13_TBS_PREAMBLE_SIZE, EVP_MAX_MD_SIZE, &hashlen)) { /* SSLfatal() already called */ return 0; } *hdata = tls13tbs; *hdatalen = TLS13_TBS_PREAMBLE_SIZE + hashlen; } else { size_t retlen; long retlen_l; retlen = retlen_l = BIO_get_mem_data(s->s3.handshake_buffer, hdata); if (retlen_l <= 0) { SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR); return 0; } *hdatalen = retlen; } return 1; } CON_FUNC_RETURN tls_construct_cert_verify(SSL_CONNECTION *s, WPACKET *pkt) { EVP_PKEY *pkey = NULL; const EVP_MD *md = NULL; EVP_MD_CTX *mctx = NULL; EVP_PKEY_CTX *pctx = NULL; size_t hdatalen = 0, siglen = 0; void *hdata; unsigned char *sig = NULL; unsigned char tls13tbs[TLS13_TBS_PREAMBLE_SIZE + EVP_MAX_MD_SIZE]; const SIGALG_LOOKUP *lu = s->s3.tmp.sigalg; SSL_CTX *sctx = SSL_CONNECTION_GET_CTX(s); if (lu == NULL || s->s3.tmp.cert == NULL) { SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR); goto err; } pkey = s->s3.tmp.cert->privatekey; if (pkey == NULL || !tls1_lookup_md(sctx, lu, &md)) { SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR); goto err; } mctx = EVP_MD_CTX_new(); if (mctx == NULL) { SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_EVP_LIB); goto err; } /* Get the data to be signed */ if (!get_cert_verify_tbs_data(s, tls13tbs, &hdata, &hdatalen)) { /* SSLfatal() already called */ goto err; } if (SSL_USE_SIGALGS(s) && !WPACKET_put_bytes_u16(pkt, lu->sigalg)) { SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR); goto err; } if (EVP_DigestSignInit_ex(mctx, &pctx, md == NULL ? NULL : EVP_MD_get0_name(md), sctx->libctx, sctx->propq, pkey, NULL) <= 0) { SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_EVP_LIB); goto err; } if (lu->sig == EVP_PKEY_RSA_PSS) { if (EVP_PKEY_CTX_set_rsa_padding(pctx, RSA_PKCS1_PSS_PADDING) <= 0 || EVP_PKEY_CTX_set_rsa_pss_saltlen(pctx, RSA_PSS_SALTLEN_DIGEST) <= 0) { SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_EVP_LIB); goto err; } } if (s->version == SSL3_VERSION) { /* * Here we use EVP_DigestSignUpdate followed by EVP_DigestSignFinal * in order to add the EVP_CTRL_SSL3_MASTER_SECRET call between them. */ if (EVP_DigestSignUpdate(mctx, hdata, hdatalen) <= 0 || EVP_MD_CTX_ctrl(mctx, EVP_CTRL_SSL3_MASTER_SECRET, (int)s->session->master_key_length, s->session->master_key) <= 0 || EVP_DigestSignFinal(mctx, NULL, &siglen) <= 0) { SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_EVP_LIB); goto err; } sig = OPENSSL_malloc(siglen); if (sig == NULL || EVP_DigestSignFinal(mctx, sig, &siglen) <= 0) { SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_EVP_LIB); goto err; } } else { /* * Here we *must* use EVP_DigestSign() because Ed25519/Ed448 does not * support streaming via EVP_DigestSignUpdate/EVP_DigestSignFinal */ if (EVP_DigestSign(mctx, NULL, &siglen, hdata, hdatalen) <= 0) { SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_EVP_LIB); goto err; } sig = OPENSSL_malloc(siglen); if (sig == NULL || EVP_DigestSign(mctx, sig, &siglen, hdata, hdatalen) <= 0) { SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_EVP_LIB); goto err; } } #ifndef OPENSSL_NO_GOST { int pktype = lu->sig; if (pktype == NID_id_GostR3410_2001 || pktype == NID_id_GostR3410_2012_256 || pktype == NID_id_GostR3410_2012_512) BUF_reverse(sig, NULL, siglen); } #endif if (!WPACKET_sub_memcpy_u16(pkt, sig, siglen)) { SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR); goto err; } /* Digest cached records and discard handshake buffer */ if (!ssl3_digest_cached_records(s, 0)) { /* SSLfatal() already called */ goto err; } OPENSSL_free(sig); EVP_MD_CTX_free(mctx); return CON_FUNC_SUCCESS; err: OPENSSL_free(sig); EVP_MD_CTX_free(mctx); return CON_FUNC_ERROR; } MSG_PROCESS_RETURN tls_process_cert_verify(SSL_CONNECTION *s, PACKET *pkt) { EVP_PKEY *pkey = NULL; const unsigned char *data; #ifndef OPENSSL_NO_GOST unsigned char *gost_data = NULL; #endif MSG_PROCESS_RETURN ret = MSG_PROCESS_ERROR; int j; unsigned int len; const EVP_MD *md = NULL; size_t hdatalen = 0; void *hdata; unsigned char tls13tbs[TLS13_TBS_PREAMBLE_SIZE + EVP_MAX_MD_SIZE]; EVP_MD_CTX *mctx = EVP_MD_CTX_new(); EVP_PKEY_CTX *pctx = NULL; SSL_CTX *sctx = SSL_CONNECTION_GET_CTX(s); if (mctx == NULL) { SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_EVP_LIB); goto err; } pkey = tls_get_peer_pkey(s); if (pkey == NULL) { SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR); goto err; } if (ssl_cert_lookup_by_pkey(pkey, NULL, sctx) == NULL) { SSLfatal(s, SSL_AD_ILLEGAL_PARAMETER, SSL_R_SIGNATURE_FOR_NON_SIGNING_CERTIFICATE); goto err; } if (SSL_USE_SIGALGS(s)) { unsigned int sigalg; if (!PACKET_get_net_2(pkt, &sigalg)) { SSLfatal(s, SSL_AD_DECODE_ERROR, SSL_R_BAD_PACKET); goto err; } if (tls12_check_peer_sigalg(s, sigalg, pkey) <= 0) { /* SSLfatal() already called */ goto err; } } else if (!tls1_set_peer_legacy_sigalg(s, pkey)) { SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_R_LEGACY_SIGALG_DISALLOWED_OR_UNSUPPORTED); goto err; } if (!tls1_lookup_md(sctx, s->s3.tmp.peer_sigalg, &md)) { SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR); goto err; } if (SSL_USE_SIGALGS(s)) OSSL_TRACE1(TLS, "USING TLSv1.2 HASH %s\n", md == NULL ? "n/a" : EVP_MD_get0_name(md)); /* Check for broken implementations of GOST ciphersuites */ /* * If key is GOST and len is exactly 64 or 128, it is signature without * length field (CryptoPro implementations at least till TLS 1.2) */ #ifndef OPENSSL_NO_GOST if (!SSL_USE_SIGALGS(s) && ((PACKET_remaining(pkt) == 64 && (EVP_PKEY_get_id(pkey) == NID_id_GostR3410_2001 || EVP_PKEY_get_id(pkey) == NID_id_GostR3410_2012_256)) || (PACKET_remaining(pkt) == 128 && EVP_PKEY_get_id(pkey) == NID_id_GostR3410_2012_512))) { len = PACKET_remaining(pkt); } else #endif if (!PACKET_get_net_2(pkt, &len)) { SSLfatal(s, SSL_AD_DECODE_ERROR, SSL_R_LENGTH_MISMATCH); goto err; } if (!PACKET_get_bytes(pkt, &data, len)) { SSLfatal(s, SSL_AD_DECODE_ERROR, SSL_R_LENGTH_MISMATCH); goto err; } if (PACKET_remaining(pkt) != 0) { SSLfatal(s, SSL_AD_DECODE_ERROR, SSL_R_LENGTH_MISMATCH); goto err; } if (!get_cert_verify_tbs_data(s, tls13tbs, &hdata, &hdatalen)) { /* SSLfatal() already called */ goto err; } OSSL_TRACE1(TLS, "Using client verify alg %s\n", md == NULL ? "n/a" : EVP_MD_get0_name(md)); if (EVP_DigestVerifyInit_ex(mctx, &pctx, md == NULL ? NULL : EVP_MD_get0_name(md), sctx->libctx, sctx->propq, pkey, NULL) <= 0) { SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_EVP_LIB); goto err; } #ifndef OPENSSL_NO_GOST { int pktype = EVP_PKEY_get_id(pkey); if (pktype == NID_id_GostR3410_2001 || pktype == NID_id_GostR3410_2012_256 || pktype == NID_id_GostR3410_2012_512) { if ((gost_data = OPENSSL_malloc(len)) == NULL) goto err; BUF_reverse(gost_data, data, len); data = gost_data; } } #endif if (SSL_USE_PSS(s)) { if (EVP_PKEY_CTX_set_rsa_padding(pctx, RSA_PKCS1_PSS_PADDING) <= 0 || EVP_PKEY_CTX_set_rsa_pss_saltlen(pctx, RSA_PSS_SALTLEN_DIGEST) <= 0) { SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_EVP_LIB); goto err; } } if (s->version == SSL3_VERSION) { if (EVP_DigestVerifyUpdate(mctx, hdata, hdatalen) <= 0 || EVP_MD_CTX_ctrl(mctx, EVP_CTRL_SSL3_MASTER_SECRET, (int)s->session->master_key_length, s->session->master_key) <= 0) { SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_EVP_LIB); goto err; } if (EVP_DigestVerifyFinal(mctx, data, len) <= 0) { SSLfatal(s, SSL_AD_DECRYPT_ERROR, SSL_R_BAD_SIGNATURE); goto err; } } else { j = EVP_DigestVerify(mctx, data, len, hdata, hdatalen); #ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION /* Ignore bad signatures when fuzzing */ if (SSL_IS_QUIC_HANDSHAKE(s)) j = 1; #endif if (j <= 0) { SSLfatal(s, SSL_AD_DECRYPT_ERROR, SSL_R_BAD_SIGNATURE); goto err; } } /* * In TLSv1.3 on the client side we make sure we prepare the client * certificate after the CertVerify instead of when we get the * CertificateRequest. This is because in TLSv1.3 the CertificateRequest * comes *before* the Certificate message. In TLSv1.2 it comes after. We * want to make sure that SSL_get1_peer_certificate() will return the actual * server certificate from the client_cert_cb callback. */ if (!s->server && SSL_CONNECTION_IS_TLS13(s) && s->s3.tmp.cert_req == 1) ret = MSG_PROCESS_CONTINUE_PROCESSING; else ret = MSG_PROCESS_CONTINUE_READING; err: BIO_free(s->s3.handshake_buffer); s->s3.handshake_buffer = NULL; EVP_MD_CTX_free(mctx); #ifndef OPENSSL_NO_GOST OPENSSL_free(gost_data); #endif return ret; } CON_FUNC_RETURN tls_construct_finished(SSL_CONNECTION *s, WPACKET *pkt) { size_t finish_md_len; const char *sender; size_t slen; SSL *ssl = SSL_CONNECTION_GET_SSL(s); /* This is a real handshake so make sure we clean it up at the end */ if (!s->server && s->post_handshake_auth != SSL_PHA_REQUESTED) s->statem.cleanuphand = 1; /* * If we attempted to write early data or we're in middlebox compat mode * then we deferred changing the handshake write keys to the last possible * moment. If we didn't already do this when we sent the client certificate * then we need to do it now. */ if (SSL_CONNECTION_IS_TLS13(s) && !s->server && !SSL_IS_QUIC_HANDSHAKE(s) && (s->early_data_state != SSL_EARLY_DATA_NONE || (s->options & SSL_OP_ENABLE_MIDDLEBOX_COMPAT) != 0) && s->s3.tmp.cert_req == 0 && (!ssl->method->ssl3_enc->change_cipher_state(s, SSL3_CC_HANDSHAKE | SSL3_CHANGE_CIPHER_CLIENT_WRITE))) {; /* SSLfatal() already called */ return CON_FUNC_ERROR; } if (s->server) { sender = ssl->method->ssl3_enc->server_finished_label; slen = ssl->method->ssl3_enc->server_finished_label_len; } else { sender = ssl->method->ssl3_enc->client_finished_label; slen = ssl->method->ssl3_enc->client_finished_label_len; } finish_md_len = ssl->method->ssl3_enc->final_finish_mac(s, sender, slen, s->s3.tmp.finish_md); if (finish_md_len == 0) { /* SSLfatal() already called */ return CON_FUNC_ERROR; } s->s3.tmp.finish_md_len = finish_md_len; if (!WPACKET_memcpy(pkt, s->s3.tmp.finish_md, finish_md_len)) { SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR); return CON_FUNC_ERROR; } /* * Log the master secret, if logging is enabled. We don't log it for * TLSv1.3: there's a different key schedule for that. */ if (!SSL_CONNECTION_IS_TLS13(s) && !ssl_log_secret(s, MASTER_SECRET_LABEL, s->session->master_key, s->session->master_key_length)) { /* SSLfatal() already called */ return CON_FUNC_ERROR; } /* * Copy the finished so we can use it for renegotiation checks */ if (!ossl_assert(finish_md_len <= EVP_MAX_MD_SIZE)) { SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR); return CON_FUNC_ERROR; } if (!s->server) { memcpy(s->s3.previous_client_finished, s->s3.tmp.finish_md, finish_md_len); s->s3.previous_client_finished_len = finish_md_len; } else { memcpy(s->s3.previous_server_finished, s->s3.tmp.finish_md, finish_md_len); s->s3.previous_server_finished_len = finish_md_len; } return CON_FUNC_SUCCESS; } CON_FUNC_RETURN tls_construct_key_update(SSL_CONNECTION *s, WPACKET *pkt) { if (!WPACKET_put_bytes_u8(pkt, s->key_update)) { SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR); return CON_FUNC_ERROR; } s->key_update = SSL_KEY_UPDATE_NONE; return CON_FUNC_SUCCESS; } MSG_PROCESS_RETURN tls_process_key_update(SSL_CONNECTION *s, PACKET *pkt) { unsigned int updatetype; /* * A KeyUpdate message signals a key change so the end of the message must * be on a record boundary. */ if (RECORD_LAYER_processed_read_pending(&s->rlayer)) { SSLfatal(s, SSL_AD_UNEXPECTED_MESSAGE, SSL_R_NOT_ON_RECORD_BOUNDARY); return MSG_PROCESS_ERROR; } if (!PACKET_get_1(pkt, &updatetype) || PACKET_remaining(pkt) != 0) { SSLfatal(s, SSL_AD_DECODE_ERROR, SSL_R_BAD_KEY_UPDATE); return MSG_PROCESS_ERROR; } /* * There are only two defined key update types. Fail if we get a value we * didn't recognise. */ if (updatetype != SSL_KEY_UPDATE_NOT_REQUESTED && updatetype != SSL_KEY_UPDATE_REQUESTED) { SSLfatal(s, SSL_AD_ILLEGAL_PARAMETER, SSL_R_BAD_KEY_UPDATE); return MSG_PROCESS_ERROR; } /* * If we get a request for us to update our sending keys too then, we need * to additionally send a KeyUpdate message. However that message should * not also request an update (otherwise we get into an infinite loop). */ if (updatetype == SSL_KEY_UPDATE_REQUESTED) s->key_update = SSL_KEY_UPDATE_NOT_REQUESTED; if (!tls13_update_key(s, 0)) { /* SSLfatal() already called */ return MSG_PROCESS_ERROR; } return MSG_PROCESS_FINISHED_READING; } /* * ssl3_take_mac calculates the Finished MAC for the handshakes messages seen * to far. */ int ssl3_take_mac(SSL_CONNECTION *s) { const char *sender; size_t slen; SSL *ssl = SSL_CONNECTION_GET_SSL(s); if (!s->server) { sender = ssl->method->ssl3_enc->server_finished_label; slen = ssl->method->ssl3_enc->server_finished_label_len; } else { sender = ssl->method->ssl3_enc->client_finished_label; slen = ssl->method->ssl3_enc->client_finished_label_len; } s->s3.tmp.peer_finish_md_len = ssl->method->ssl3_enc->final_finish_mac(s, sender, slen, s->s3.tmp.peer_finish_md); if (s->s3.tmp.peer_finish_md_len == 0) { /* SSLfatal() already called */ return 0; } return 1; } MSG_PROCESS_RETURN tls_process_change_cipher_spec(SSL_CONNECTION *s, PACKET *pkt) { size_t remain; remain = PACKET_remaining(pkt); /* * 'Change Cipher Spec' is just a single byte, which should already have * been consumed by ssl_get_message() so there should be no bytes left, * unless we're using DTLS1_BAD_VER, which has an extra 2 bytes */ if (SSL_CONNECTION_IS_DTLS(s)) { if ((s->version == DTLS1_BAD_VER && remain != DTLS1_CCS_HEADER_LENGTH + 1) || (s->version != DTLS1_BAD_VER && remain != DTLS1_CCS_HEADER_LENGTH - 1)) { SSLfatal(s, SSL_AD_DECODE_ERROR, SSL_R_BAD_CHANGE_CIPHER_SPEC); return MSG_PROCESS_ERROR; } } else { if (remain != 0) { SSLfatal(s, SSL_AD_DECODE_ERROR, SSL_R_BAD_CHANGE_CIPHER_SPEC); return MSG_PROCESS_ERROR; } } /* Check we have a cipher to change to */ if (s->s3.tmp.new_cipher == NULL) { SSLfatal(s, SSL_AD_UNEXPECTED_MESSAGE, SSL_R_CCS_RECEIVED_EARLY); return MSG_PROCESS_ERROR; } s->s3.change_cipher_spec = 1; if (!ssl3_do_change_cipher_spec(s)) { SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR); return MSG_PROCESS_ERROR; } if (SSL_CONNECTION_IS_DTLS(s)) { if (s->version == DTLS1_BAD_VER) s->d1->handshake_read_seq++; #ifndef OPENSSL_NO_SCTP /* * Remember that a CCS has been received, so that an old key of * SCTP-Auth can be deleted when a CCS is sent. Will be ignored if no * SCTP is used */ BIO_ctrl(SSL_get_wbio(SSL_CONNECTION_GET_SSL(s)), BIO_CTRL_DGRAM_SCTP_AUTH_CCS_RCVD, 1, NULL); #endif } return MSG_PROCESS_CONTINUE_READING; } MSG_PROCESS_RETURN tls_process_finished(SSL_CONNECTION *s, PACKET *pkt) { size_t md_len; SSL *ssl = SSL_CONNECTION_GET_SSL(s); int was_first = SSL_IS_FIRST_HANDSHAKE(s); int ok; /* This is a real handshake so make sure we clean it up at the end */ if (s->server) { /* * To get this far we must have read encrypted data from the client. We * no longer tolerate unencrypted alerts. This is ignored if less than * TLSv1.3 */ if (s->rlayer.rrlmethod->set_plain_alerts != NULL) s->rlayer.rrlmethod->set_plain_alerts(s->rlayer.rrl, 0); if (s->post_handshake_auth != SSL_PHA_REQUESTED) s->statem.cleanuphand = 1; if (SSL_CONNECTION_IS_TLS13(s) && !tls13_save_handshake_digest_for_pha(s)) { /* SSLfatal() already called */ return MSG_PROCESS_ERROR; } } /* * In TLSv1.3 a Finished message signals a key change so the end of the * message must be on a record boundary. */ if (SSL_CONNECTION_IS_TLS13(s) && RECORD_LAYER_processed_read_pending(&s->rlayer)) { SSLfatal(s, SSL_AD_UNEXPECTED_MESSAGE, SSL_R_NOT_ON_RECORD_BOUNDARY); return MSG_PROCESS_ERROR; } /* If this occurs, we have missed a message */ if (!SSL_CONNECTION_IS_TLS13(s) && !s->s3.change_cipher_spec) { SSLfatal(s, SSL_AD_UNEXPECTED_MESSAGE, SSL_R_GOT_A_FIN_BEFORE_A_CCS); return MSG_PROCESS_ERROR; } s->s3.change_cipher_spec = 0; md_len = s->s3.tmp.peer_finish_md_len; if (md_len != PACKET_remaining(pkt)) { SSLfatal(s, SSL_AD_DECODE_ERROR, SSL_R_BAD_DIGEST_LENGTH); return MSG_PROCESS_ERROR; } ok = CRYPTO_memcmp(PACKET_data(pkt), s->s3.tmp.peer_finish_md, md_len); #ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION if (ok != 0) { if ((PACKET_data(pkt)[0] ^ s->s3.tmp.peer_finish_md[0]) != 0xFF) { ok = 0; } } #endif if (ok != 0) { SSLfatal(s, SSL_AD_DECRYPT_ERROR, SSL_R_DIGEST_CHECK_FAILED); return MSG_PROCESS_ERROR; } /* * Copy the finished so we can use it for renegotiation checks */ if (!ossl_assert(md_len <= EVP_MAX_MD_SIZE)) { SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR); return MSG_PROCESS_ERROR; } if (s->server) { memcpy(s->s3.previous_client_finished, s->s3.tmp.peer_finish_md, md_len); s->s3.previous_client_finished_len = md_len; } else { memcpy(s->s3.previous_server_finished, s->s3.tmp.peer_finish_md, md_len); s->s3.previous_server_finished_len = md_len; } /* * In TLS1.3 we also have to change cipher state and do any final processing * of the initial server flight (if we are a client) */ if (SSL_CONNECTION_IS_TLS13(s)) { if (s->server) { if (s->post_handshake_auth != SSL_PHA_REQUESTED && !ssl->method->ssl3_enc->change_cipher_state(s, SSL3_CC_APPLICATION | SSL3_CHANGE_CIPHER_SERVER_READ)) { /* SSLfatal() already called */ return MSG_PROCESS_ERROR; } } else { /* TLS 1.3 gets the secret size from the handshake md */ size_t dummy; if (!ssl->method->ssl3_enc->generate_master_secret(s, s->master_secret, s->handshake_secret, 0, &dummy)) { /* SSLfatal() already called */ return MSG_PROCESS_ERROR; } if (!tls13_store_server_finished_hash(s)) { /* SSLfatal() already called */ return MSG_PROCESS_ERROR; } /* * For non-QUIC we set up the client's app data read keys now, so * that we can go straight into reading 0.5RTT data from the server. * For QUIC we don't do that, and instead defer setting up the keys * until after we have set up the write keys in order to ensure that * write keys are always set up before read keys (so that if we read * a message we have the correct keys in place to ack it) */ if (!SSL_IS_QUIC_HANDSHAKE(s) && !ssl->method->ssl3_enc->change_cipher_state(s, SSL3_CC_APPLICATION | SSL3_CHANGE_CIPHER_CLIENT_READ)) { /* SSLfatal() already called */ return MSG_PROCESS_ERROR; } if (!tls_process_initial_server_flight(s)) { /* SSLfatal() already called */ return MSG_PROCESS_ERROR; } } } if (was_first && !SSL_IS_FIRST_HANDSHAKE(s) && s->rlayer.rrlmethod->set_first_handshake != NULL) s->rlayer.rrlmethod->set_first_handshake(s->rlayer.rrl, 0); return MSG_PROCESS_FINISHED_READING; } CON_FUNC_RETURN tls_construct_change_cipher_spec(SSL_CONNECTION *s, WPACKET *pkt) { if (!WPACKET_put_bytes_u8(pkt, SSL3_MT_CCS)) { SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR); return CON_FUNC_ERROR; } return CON_FUNC_SUCCESS; } /* Add a certificate to the WPACKET */ static int ssl_add_cert_to_wpacket(SSL_CONNECTION *s, WPACKET *pkt, X509 *x, int chain, int for_comp) { int len; unsigned char *outbytes; int context = SSL_EXT_TLS1_3_CERTIFICATE; if (for_comp) context |= SSL_EXT_TLS1_3_CERTIFICATE_COMPRESSION; len = i2d_X509(x, NULL); if (len < 0) { if (!for_comp) SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_BUF_LIB); return 0; } if (!WPACKET_sub_allocate_bytes_u24(pkt, len, &outbytes) || i2d_X509(x, &outbytes) != len) { if (!for_comp) SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR); return 0; } if ((SSL_CONNECTION_IS_TLS13(s) || for_comp) && !tls_construct_extensions(s, pkt, context, x, chain)) { /* SSLfatal() already called */ return 0; } return 1; } /* Add certificate chain to provided WPACKET */ static int ssl_add_cert_chain(SSL_CONNECTION *s, WPACKET *pkt, CERT_PKEY *cpk, int for_comp) { int i, chain_count; X509 *x; STACK_OF(X509) *extra_certs; STACK_OF(X509) *chain = NULL; X509_STORE *chain_store; SSL_CTX *sctx = SSL_CONNECTION_GET_CTX(s); if (cpk == NULL || cpk->x509 == NULL) return 1; x = cpk->x509; /* * If we have a certificate specific chain use it, else use parent ctx. */ if (cpk->chain != NULL) extra_certs = cpk->chain; else extra_certs = sctx->extra_certs; if ((s->mode & SSL_MODE_NO_AUTO_CHAIN) || extra_certs) chain_store = NULL; else if (s->cert->chain_store) chain_store = s->cert->chain_store; else chain_store = sctx->cert_store; if (chain_store != NULL) { X509_STORE_CTX *xs_ctx = X509_STORE_CTX_new_ex(sctx->libctx, sctx->propq); if (xs_ctx == NULL) { if (!for_comp) SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_X509_LIB); return 0; } if (!X509_STORE_CTX_init(xs_ctx, chain_store, x, NULL)) { X509_STORE_CTX_free(xs_ctx); if (!for_comp) SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_X509_LIB); return 0; } /* * It is valid for the chain not to be complete (because normally we * don't include the root cert in the chain). Therefore we deliberately * ignore the error return from this call. We're not actually verifying * the cert - we're just building as much of the chain as we can */ (void)X509_verify_cert(xs_ctx); /* Don't leave errors in the queue */ ERR_clear_error(); chain = X509_STORE_CTX_get0_chain(xs_ctx); i = ssl_security_cert_chain(s, chain, NULL, 0); if (i != 1) { #if 0 /* Dummy error calls so mkerr generates them */ ERR_raise(ERR_LIB_SSL, SSL_R_EE_KEY_TOO_SMALL); ERR_raise(ERR_LIB_SSL, SSL_R_CA_KEY_TOO_SMALL); ERR_raise(ERR_LIB_SSL, SSL_R_CA_MD_TOO_WEAK); #endif X509_STORE_CTX_free(xs_ctx); if (!for_comp) SSLfatal(s, SSL_AD_INTERNAL_ERROR, i); return 0; } chain_count = sk_X509_num(chain); for (i = 0; i < chain_count; i++) { x = sk_X509_value(chain, i); if (!ssl_add_cert_to_wpacket(s, pkt, x, i, for_comp)) { /* SSLfatal() already called */ X509_STORE_CTX_free(xs_ctx); return 0; } } X509_STORE_CTX_free(xs_ctx); } else { i = ssl_security_cert_chain(s, extra_certs, x, 0); if (i != 1) { if (!for_comp) SSLfatal(s, SSL_AD_INTERNAL_ERROR, i); return 0; } if (!ssl_add_cert_to_wpacket(s, pkt, x, 0, for_comp)) { /* SSLfatal() already called */ return 0; } for (i = 0; i < sk_X509_num(extra_certs); i++) { x = sk_X509_value(extra_certs, i); if (!ssl_add_cert_to_wpacket(s, pkt, x, i + 1, for_comp)) { /* SSLfatal() already called */ return 0; } } } return 1; } EVP_PKEY* tls_get_peer_pkey(const SSL_CONNECTION *sc) { if (sc->session->peer_rpk != NULL) return sc->session->peer_rpk; if (sc->session->peer != NULL) return X509_get0_pubkey(sc->session->peer); return NULL; } int tls_process_rpk(SSL_CONNECTION *sc, PACKET *pkt, EVP_PKEY **peer_rpk) { EVP_PKEY *pkey = NULL; int ret = 0; RAW_EXTENSION *rawexts = NULL; PACKET extensions; PACKET context; unsigned long cert_len = 0, spki_len = 0; const unsigned char *spki, *spkistart; SSL_CTX *sctx = SSL_CONNECTION_GET_CTX(sc); /*- * ---------------------------- * TLS 1.3 Certificate message: * ---------------------------- * https://datatracker.ietf.org/doc/html/rfc8446#section-4.4.2 * * enum { * X509(0), * RawPublicKey(2), * (255) * } CertificateType; * * struct { * select (certificate_type) { * case RawPublicKey: * // From RFC 7250 ASN.1_subjectPublicKeyInfo * opaque ASN1_subjectPublicKeyInfo<1..2^24-1>; * * case X509: * opaque cert_data<1..2^24-1>; * }; * Extension extensions<0..2^16-1>; * } CertificateEntry; * * struct { * opaque certificate_request_context<0..2^8-1>; * CertificateEntry certificate_list<0..2^24-1>; * } Certificate; * * The client MUST send a Certificate message if and only if the server * has requested client authentication via a CertificateRequest message * (Section 4.3.2). If the server requests client authentication but no * suitable certificate is available, the client MUST send a Certificate * message containing no certificates (i.e., with the "certificate_list" * field having length 0). * * ---------------------------- * TLS 1.2 Certificate message: * ---------------------------- * https://datatracker.ietf.org/doc/html/rfc7250#section-3 * * opaque ASN.1Cert<1..2^24-1>; * * struct { * select(certificate_type){ * * // certificate type defined in this document. * case RawPublicKey: * opaque ASN.1_subjectPublicKeyInfo<1..2^24-1>; * * // X.509 certificate defined in RFC 5246 * case X.509: * ASN.1Cert certificate_list<0..2^24-1>; * * // Additional certificate type based on * // "TLS Certificate Types" subregistry * }; * } Certificate; * * ------------- * Consequently: * ------------- * After the (TLS 1.3 only) context octet string (1 byte length + data) the * Certificate message has a 3-byte length that is zero in the client to * server message when the client has no RPK to send. In that case, there * are no (TLS 1.3 only) per-certificate extensions either, because the * [CertificateEntry] list is empty. * * In the server to client direction, or when the client had an RPK to send, * the TLS 1.3 message just prepends the length of the RPK+extensions, * while TLS <= 1.2 sends just the RPK (octet-string). * * The context must be zero-length in the server to client direction, and * must match the value recorded in the certificate request in the client * to server direction. */ if (SSL_CONNECTION_IS_TLS13(sc)) { if (!PACKET_get_length_prefixed_1(pkt, &context)) { SSLfatal(sc, SSL_AD_DECODE_ERROR, SSL_R_INVALID_CONTEXT); goto err; } if (sc->server) { if (sc->pha_context == NULL) { if (PACKET_remaining(&context) != 0) { SSLfatal(sc, SSL_AD_DECODE_ERROR, SSL_R_INVALID_CONTEXT); goto err; } } else { if (!PACKET_equal(&context, sc->pha_context, sc->pha_context_len)) { SSLfatal(sc, SSL_AD_DECODE_ERROR, SSL_R_INVALID_CONTEXT); goto err; } } } else { if (PACKET_remaining(&context) != 0) { SSLfatal(sc, SSL_AD_DECODE_ERROR, SSL_R_INVALID_CONTEXT); goto err; } } } if (!PACKET_get_net_3(pkt, &cert_len) || PACKET_remaining(pkt) != cert_len) { SSLfatal(sc, SSL_AD_DECODE_ERROR, SSL_R_LENGTH_MISMATCH); goto err; } /* * The list length may be zero when there is no RPK. In the case of TLS * 1.2 this is actually the RPK length, which cannot be zero as specified, * but that breaks the ability of the client to decline client auth. We * overload the 0 RPK length to mean "no RPK". This interpretation is * also used some other (reference?) implementations, but is not supported * by the verbatim RFC7250 text. */ if (cert_len == 0) return 1; if (SSL_CONNECTION_IS_TLS13(sc)) { /* * With TLS 1.3, a non-empty explicit-length RPK octet-string followed * by a possibly empty extension block. */ if (!PACKET_get_net_3(pkt, &spki_len)) { SSLfatal(sc, SSL_AD_DECODE_ERROR, SSL_R_LENGTH_MISMATCH); goto err; } if (spki_len == 0) { /* empty RPK */ SSLfatal(sc, SSL_AD_DECODE_ERROR, SSL_R_EMPTY_RAW_PUBLIC_KEY); goto err; } } else { spki_len = cert_len; } if (!PACKET_get_bytes(pkt, &spki, spki_len)) { SSLfatal(sc, SSL_AD_DECODE_ERROR, SSL_R_LENGTH_MISMATCH); goto err; } spkistart = spki; if ((pkey = d2i_PUBKEY_ex(NULL, &spki, spki_len, sctx->libctx, sctx->propq)) == NULL || spki != (spkistart + spki_len)) { SSLfatal(sc, SSL_AD_DECODE_ERROR, SSL_R_LENGTH_MISMATCH); goto err; } if (EVP_PKEY_missing_parameters(pkey)) { SSLfatal(sc, SSL_AD_INTERNAL_ERROR, SSL_R_UNABLE_TO_FIND_PUBLIC_KEY_PARAMETERS); goto err; } /* Process the Extensions block */ if (SSL_CONNECTION_IS_TLS13(sc)) { if (PACKET_remaining(pkt) != (cert_len - 3 - spki_len)) { SSLfatal(sc, SSL_AD_DECODE_ERROR, SSL_R_BAD_LENGTH); goto err; } if (!PACKET_as_length_prefixed_2(pkt, &extensions) || PACKET_remaining(pkt) != 0) { SSLfatal(sc, SSL_AD_DECODE_ERROR, SSL_R_LENGTH_MISMATCH); goto err; } if (!tls_collect_extensions(sc, &extensions, SSL_EXT_TLS1_3_RAW_PUBLIC_KEY, &rawexts, NULL, 1)) { /* SSLfatal already called */ goto err; } /* chain index is always zero and fin always 1 for RPK */ if (!tls_parse_all_extensions(sc, SSL_EXT_TLS1_3_RAW_PUBLIC_KEY, rawexts, NULL, 0, 1)) { /* SSLfatal already called */ goto err; } } ret = 1; if (peer_rpk != NULL) { *peer_rpk = pkey; pkey = NULL; } err: OPENSSL_free(rawexts); EVP_PKEY_free(pkey); return ret; } unsigned long tls_output_rpk(SSL_CONNECTION *sc, WPACKET *pkt, CERT_PKEY *cpk) { int pdata_len = 0; unsigned char *pdata = NULL; X509_PUBKEY *xpk = NULL; unsigned long ret = 0; X509 *x509 = NULL; if (cpk != NULL && cpk->x509 != NULL) { x509 = cpk->x509; /* Get the RPK from the certificate */ xpk = X509_get_X509_PUBKEY(cpk->x509); if (xpk == NULL) { SSLfatal(sc, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR); goto err; } pdata_len = i2d_X509_PUBKEY(xpk, &pdata); } else if (cpk != NULL && cpk->privatekey != NULL) { /* Get the RPK from the private key */ pdata_len = i2d_PUBKEY(cpk->privatekey, &pdata); } else { /* The server RPK is not optional */ if (sc->server) { SSLfatal(sc, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR); goto err; } /* The client can send a zero length certificate list */ if (!WPACKET_sub_memcpy_u24(pkt, pdata, pdata_len)) { SSLfatal(sc, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR); goto err; } return 1; } if (pdata_len <= 0) { SSLfatal(sc, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR); goto err; } /* * TLSv1.2 is _just_ the raw public key * TLSv1.3 includes extensions, so there's a length wrapper */ if (SSL_CONNECTION_IS_TLS13(sc)) { if (!WPACKET_start_sub_packet_u24(pkt)) { SSLfatal(sc, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR); goto err; } } if (!WPACKET_sub_memcpy_u24(pkt, pdata, pdata_len)) { SSLfatal(sc, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR); goto err; } if (SSL_CONNECTION_IS_TLS13(sc)) { /* * Only send extensions relevant to raw public keys. Until such * extensions are defined, this will be an empty set of extensions. * |x509| may be NULL, which raw public-key extensions need to handle. */ if (!tls_construct_extensions(sc, pkt, SSL_EXT_TLS1_3_RAW_PUBLIC_KEY, x509, 0)) { /* SSLfatal() already called */ goto err; } if (!WPACKET_close(pkt)) { SSLfatal(sc, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR); goto err; } } ret = 1; err: OPENSSL_free(pdata); return ret; } unsigned long ssl3_output_cert_chain(SSL_CONNECTION *s, WPACKET *pkt, CERT_PKEY *cpk, int for_comp) { if (!WPACKET_start_sub_packet_u24(pkt)) { if (!for_comp) SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR); return 0; } if (!ssl_add_cert_chain(s, pkt, cpk, for_comp)) return 0; if (!WPACKET_close(pkt)) { if (!for_comp) SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR); return 0; } return 1; } /* * Tidy up after the end of a handshake. In the case of SCTP this may result * in NBIO events. If |clearbufs| is set then init_buf and the wbio buffer is * freed up as well. */ WORK_STATE tls_finish_handshake(SSL_CONNECTION *s, ossl_unused WORK_STATE wst, int clearbufs, int stop) { void (*cb) (const SSL *ssl, int type, int val) = NULL; int cleanuphand = s->statem.cleanuphand; SSL *ssl = SSL_CONNECTION_GET_USER_SSL(s); SSL_CTX *sctx = SSL_CONNECTION_GET_CTX(s); if (clearbufs) { if (!SSL_CONNECTION_IS_DTLS(s) #ifndef OPENSSL_NO_SCTP /* * RFC6083: SCTP provides a reliable and in-sequence transport service for DTLS * messages that require it. Therefore, DTLS procedures for retransmissions * MUST NOT be used. * Hence the init_buf can be cleared when DTLS over SCTP as transport is used. */ || BIO_dgram_is_sctp(SSL_get_wbio(SSL_CONNECTION_GET_SSL(s))) #endif ) { /* * We don't do this in DTLS over UDP because we may still need the init_buf * in case there are any unexpected retransmits */ BUF_MEM_free(s->init_buf); s->init_buf = NULL; } if (!ssl_free_wbio_buffer(s)) { SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR); return WORK_ERROR; } s->init_num = 0; } if (SSL_CONNECTION_IS_TLS13(s) && !s->server && s->post_handshake_auth == SSL_PHA_REQUESTED) s->post_handshake_auth = SSL_PHA_EXT_SENT; /* * Only set if there was a Finished message and this isn't after a TLSv1.3 * post handshake exchange */ if (cleanuphand) { /* skipped if we just sent a HelloRequest */ s->renegotiate = 0; s->new_session = 0; s->statem.cleanuphand = 0; s->ext.ticket_expected = 0; ssl3_cleanup_key_block(s); if (s->server) { /* * In TLSv1.3 we update the cache as part of constructing the * NewSessionTicket */ if (!SSL_CONNECTION_IS_TLS13(s)) ssl_update_cache(s, SSL_SESS_CACHE_SERVER); /* N.B. s->ctx may not equal s->session_ctx */ ssl_tsan_counter(sctx, &sctx->stats.sess_accept_good); s->handshake_func = ossl_statem_accept; } else { if (SSL_CONNECTION_IS_TLS13(s)) { /* * We encourage applications to only use TLSv1.3 tickets once, * so we remove this one from the cache. */ if ((s->session_ctx->session_cache_mode & SSL_SESS_CACHE_CLIENT) != 0) SSL_CTX_remove_session(s->session_ctx, s->session); } else { /* * In TLSv1.3 we update the cache as part of processing the * NewSessionTicket */ ssl_update_cache(s, SSL_SESS_CACHE_CLIENT); } if (s->hit) ssl_tsan_counter(s->session_ctx, &s->session_ctx->stats.sess_hit); s->handshake_func = ossl_statem_connect; ssl_tsan_counter(s->session_ctx, &s->session_ctx->stats.sess_connect_good); } if (SSL_CONNECTION_IS_DTLS(s)) { /* done with handshaking */ s->d1->handshake_read_seq = 0; s->d1->handshake_write_seq = 0; s->d1->next_handshake_write_seq = 0; dtls1_clear_received_buffer(s); } } if (s->info_callback != NULL) cb = s->info_callback; else if (sctx->info_callback != NULL) cb = sctx->info_callback; /* The callback may expect us to not be in init at handshake done */ ossl_statem_set_in_init(s, 0); if (cb != NULL) { if (cleanuphand || !SSL_CONNECTION_IS_TLS13(s) || SSL_IS_FIRST_HANDSHAKE(s)) cb(ssl, SSL_CB_HANDSHAKE_DONE, 1); } if (!stop) { /* If we've got more work to do we go back into init */ ossl_statem_set_in_init(s, 1); return WORK_FINISHED_CONTINUE; } return WORK_FINISHED_STOP; } int tls_get_message_header(SSL_CONNECTION *s, int *mt) { /* s->init_num < SSL3_HM_HEADER_LENGTH */ int skip_message, i; uint8_t recvd_type; unsigned char *p; size_t l, readbytes; SSL *ssl = SSL_CONNECTION_GET_SSL(s); SSL *ussl = SSL_CONNECTION_GET_USER_SSL(s); p = (unsigned char *)s->init_buf->data; do { while (s->init_num < SSL3_HM_HEADER_LENGTH) { i = ssl->method->ssl_read_bytes(ssl, SSL3_RT_HANDSHAKE, &recvd_type, &p[s->init_num], SSL3_HM_HEADER_LENGTH - s->init_num, 0, &readbytes); if (i <= 0) { s->rwstate = SSL_READING; return 0; } if (recvd_type == SSL3_RT_CHANGE_CIPHER_SPEC) { /* * A ChangeCipherSpec must be a single byte and may not occur * in the middle of a handshake message. */ if (s->init_num != 0 || readbytes != 1 || p[0] != SSL3_MT_CCS) { SSLfatal(s, SSL_AD_UNEXPECTED_MESSAGE, SSL_R_BAD_CHANGE_CIPHER_SPEC); return 0; } if (s->statem.hand_state == TLS_ST_BEFORE && (s->s3.flags & TLS1_FLAGS_STATELESS) != 0) { /* * We are stateless and we received a CCS. Probably this is * from a client between the first and second ClientHellos. * We should ignore this, but return an error because we do * not return success until we see the second ClientHello * with a valid cookie. */ return 0; } s->s3.tmp.message_type = *mt = SSL3_MT_CHANGE_CIPHER_SPEC; s->init_num = readbytes - 1; s->init_msg = s->init_buf->data; s->s3.tmp.message_size = readbytes; return 1; } else if (recvd_type != SSL3_RT_HANDSHAKE) { SSLfatal(s, SSL_AD_UNEXPECTED_MESSAGE, SSL_R_CCS_RECEIVED_EARLY); return 0; } s->init_num += readbytes; } skip_message = 0; if (!s->server) if (s->statem.hand_state != TLS_ST_OK && p[0] == SSL3_MT_HELLO_REQUEST) /* * The server may always send 'Hello Request' messages -- * we are doing a handshake anyway now, so ignore them if * their format is correct. Does not count for 'Finished' * MAC. */ if (p[1] == 0 && p[2] == 0 && p[3] == 0) { s->init_num = 0; skip_message = 1; if (s->msg_callback) s->msg_callback(0, s->version, SSL3_RT_HANDSHAKE, p, SSL3_HM_HEADER_LENGTH, ussl, s->msg_callback_arg); } } while (skip_message); /* s->init_num == SSL3_HM_HEADER_LENGTH */ *mt = *p; s->s3.tmp.message_type = *(p++); if (RECORD_LAYER_is_sslv2_record(&s->rlayer)) { /* * Only happens with SSLv3+ in an SSLv2 backward compatible * ClientHello * * Total message size is the remaining record bytes to read * plus the SSL3_HM_HEADER_LENGTH bytes that we already read */ l = s->rlayer.tlsrecs[0].length + SSL3_HM_HEADER_LENGTH; s->s3.tmp.message_size = l; s->init_msg = s->init_buf->data; s->init_num = SSL3_HM_HEADER_LENGTH; } else { n2l3(p, l); /* BUF_MEM_grow takes an 'int' parameter */ if (l > (INT_MAX - SSL3_HM_HEADER_LENGTH)) { SSLfatal(s, SSL_AD_ILLEGAL_PARAMETER, SSL_R_EXCESSIVE_MESSAGE_SIZE); return 0; } s->s3.tmp.message_size = l; s->init_msg = s->init_buf->data + SSL3_HM_HEADER_LENGTH; s->init_num = 0; } return 1; } int tls_get_message_body(SSL_CONNECTION *s, size_t *len) { size_t n, readbytes; unsigned char *p; int i; SSL *ssl = SSL_CONNECTION_GET_SSL(s); SSL *ussl = SSL_CONNECTION_GET_USER_SSL(s); if (s->s3.tmp.message_type == SSL3_MT_CHANGE_CIPHER_SPEC) { /* We've already read everything in */ *len = (unsigned long)s->init_num; return 1; } p = s->init_msg; n = s->s3.tmp.message_size - s->init_num; while (n > 0) { i = ssl->method->ssl_read_bytes(ssl, SSL3_RT_HANDSHAKE, NULL, &p[s->init_num], n, 0, &readbytes); if (i <= 0) { s->rwstate = SSL_READING; *len = 0; return 0; } s->init_num += readbytes; n -= readbytes; } /* * If receiving Finished, record MAC of prior handshake messages for * Finished verification. */ if (*(s->init_buf->data) == SSL3_MT_FINISHED && !ssl3_take_mac(s)) { /* SSLfatal() already called */ *len = 0; return 0; } /* Feed this message into MAC computation. */ if (RECORD_LAYER_is_sslv2_record(&s->rlayer)) { if (!ssl3_finish_mac(s, (unsigned char *)s->init_buf->data, s->init_num)) { /* SSLfatal() already called */ *len = 0; return 0; } if (s->msg_callback) s->msg_callback(0, SSL2_VERSION, 0, s->init_buf->data, (size_t)s->init_num, ussl, s->msg_callback_arg); } else { /* * We defer feeding in the HRR until later. We'll do it as part of * processing the message * The TLsv1.3 handshake transcript stops at the ClientFinished * message. */ #define SERVER_HELLO_RANDOM_OFFSET (SSL3_HM_HEADER_LENGTH + 2) /* KeyUpdate and NewSessionTicket do not need to be added */ if (!SSL_CONNECTION_IS_TLS13(s) || (s->s3.tmp.message_type != SSL3_MT_NEWSESSION_TICKET && s->s3.tmp.message_type != SSL3_MT_KEY_UPDATE)) { if (s->s3.tmp.message_type != SSL3_MT_SERVER_HELLO || s->init_num < SERVER_HELLO_RANDOM_OFFSET + SSL3_RANDOM_SIZE || memcmp(hrrrandom, s->init_buf->data + SERVER_HELLO_RANDOM_OFFSET, SSL3_RANDOM_SIZE) != 0) { if (!ssl3_finish_mac(s, (unsigned char *)s->init_buf->data, s->init_num + SSL3_HM_HEADER_LENGTH)) { /* SSLfatal() already called */ *len = 0; return 0; } } } if (s->msg_callback) s->msg_callback(0, s->version, SSL3_RT_HANDSHAKE, s->init_buf->data, (size_t)s->init_num + SSL3_HM_HEADER_LENGTH, ussl, s->msg_callback_arg); } *len = s->init_num; return 1; } static const X509ERR2ALERT x509table[] = { {X509_V_ERR_APPLICATION_VERIFICATION, SSL_AD_HANDSHAKE_FAILURE}, {X509_V_ERR_CA_KEY_TOO_SMALL, SSL_AD_BAD_CERTIFICATE}, {X509_V_ERR_EC_KEY_EXPLICIT_PARAMS, SSL_AD_BAD_CERTIFICATE}, {X509_V_ERR_CA_MD_TOO_WEAK, SSL_AD_BAD_CERTIFICATE}, {X509_V_ERR_CERT_CHAIN_TOO_LONG, SSL_AD_UNKNOWN_CA}, {X509_V_ERR_CERT_HAS_EXPIRED, SSL_AD_CERTIFICATE_EXPIRED}, {X509_V_ERR_CERT_NOT_YET_VALID, SSL_AD_BAD_CERTIFICATE}, {X509_V_ERR_CERT_REJECTED, SSL_AD_BAD_CERTIFICATE}, {X509_V_ERR_CERT_REVOKED, SSL_AD_CERTIFICATE_REVOKED}, {X509_V_ERR_CERT_SIGNATURE_FAILURE, SSL_AD_DECRYPT_ERROR}, {X509_V_ERR_CERT_UNTRUSTED, SSL_AD_BAD_CERTIFICATE}, {X509_V_ERR_CRL_HAS_EXPIRED, SSL_AD_CERTIFICATE_EXPIRED}, {X509_V_ERR_CRL_NOT_YET_VALID, SSL_AD_BAD_CERTIFICATE}, {X509_V_ERR_CRL_SIGNATURE_FAILURE, SSL_AD_DECRYPT_ERROR}, {X509_V_ERR_DANE_NO_MATCH, SSL_AD_BAD_CERTIFICATE}, {X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT, SSL_AD_UNKNOWN_CA}, {X509_V_ERR_EE_KEY_TOO_SMALL, SSL_AD_BAD_CERTIFICATE}, {X509_V_ERR_EMAIL_MISMATCH, SSL_AD_BAD_CERTIFICATE}, {X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD, SSL_AD_BAD_CERTIFICATE}, {X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD, SSL_AD_BAD_CERTIFICATE}, {X509_V_ERR_ERROR_IN_CRL_LAST_UPDATE_FIELD, SSL_AD_BAD_CERTIFICATE}, {X509_V_ERR_ERROR_IN_CRL_NEXT_UPDATE_FIELD, SSL_AD_BAD_CERTIFICATE}, {X509_V_ERR_HOSTNAME_MISMATCH, SSL_AD_BAD_CERTIFICATE}, {X509_V_ERR_INVALID_CA, SSL_AD_UNKNOWN_CA}, {X509_V_ERR_INVALID_CALL, SSL_AD_INTERNAL_ERROR}, {X509_V_ERR_INVALID_PURPOSE, SSL_AD_UNSUPPORTED_CERTIFICATE}, {X509_V_ERR_IP_ADDRESS_MISMATCH, SSL_AD_BAD_CERTIFICATE}, {X509_V_ERR_OUT_OF_MEM, SSL_AD_INTERNAL_ERROR}, {X509_V_ERR_PATH_LENGTH_EXCEEDED, SSL_AD_UNKNOWN_CA}, {X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN, SSL_AD_UNKNOWN_CA}, {X509_V_ERR_STORE_LOOKUP, SSL_AD_INTERNAL_ERROR}, {X509_V_ERR_UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY, SSL_AD_BAD_CERTIFICATE}, {X509_V_ERR_UNABLE_TO_DECRYPT_CERT_SIGNATURE, SSL_AD_BAD_CERTIFICATE}, {X509_V_ERR_UNABLE_TO_DECRYPT_CRL_SIGNATURE, SSL_AD_BAD_CERTIFICATE}, {X509_V_ERR_UNABLE_TO_GET_CRL, SSL_AD_UNKNOWN_CA}, {X509_V_ERR_UNABLE_TO_GET_CRL_ISSUER, SSL_AD_UNKNOWN_CA}, {X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT, SSL_AD_UNKNOWN_CA}, {X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY, SSL_AD_UNKNOWN_CA}, {X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE, SSL_AD_UNKNOWN_CA}, {X509_V_ERR_UNSPECIFIED, SSL_AD_INTERNAL_ERROR}, /* Last entry; return this if we don't find the value above. */ {X509_V_OK, SSL_AD_CERTIFICATE_UNKNOWN} }; int ssl_x509err2alert(int x509err) { const X509ERR2ALERT *tp; for (tp = x509table; tp->x509err != X509_V_OK; ++tp) if (tp->x509err == x509err) break; return tp->alert; } int ssl_allow_compression(SSL_CONNECTION *s) { if (s->options & SSL_OP_NO_COMPRESSION) return 0; return ssl_security(s, SSL_SECOP_COMPRESSION, 0, 0, NULL); } /* * SSL/TLS/DTLS version comparison * * Returns * 0 if versiona is equal to versionb * 1 if versiona is greater than versionb * -1 if versiona is less than versionb */ int ssl_version_cmp(const SSL_CONNECTION *s, int versiona, int versionb) { int dtls = SSL_CONNECTION_IS_DTLS(s); if (versiona == versionb) return 0; if (!dtls) return versiona < versionb ? -1 : 1; return DTLS_VERSION_LT(versiona, versionb) ? -1 : 1; } typedef struct { int version; const SSL_METHOD *(*cmeth) (void); const SSL_METHOD *(*smeth) (void); } version_info; #if TLS_MAX_VERSION_INTERNAL != TLS1_3_VERSION # error Code needs update for TLS_method() support beyond TLS1_3_VERSION. #endif /* Must be in order high to low */ static const version_info tls_version_table[] = { #ifndef OPENSSL_NO_TLS1_3 {TLS1_3_VERSION, tlsv1_3_client_method, tlsv1_3_server_method}, #else {TLS1_3_VERSION, NULL, NULL}, #endif #ifndef OPENSSL_NO_TLS1_2 {TLS1_2_VERSION, tlsv1_2_client_method, tlsv1_2_server_method}, #else {TLS1_2_VERSION, NULL, NULL}, #endif #ifndef OPENSSL_NO_TLS1_1 {TLS1_1_VERSION, tlsv1_1_client_method, tlsv1_1_server_method}, #else {TLS1_1_VERSION, NULL, NULL}, #endif #ifndef OPENSSL_NO_TLS1 {TLS1_VERSION, tlsv1_client_method, tlsv1_server_method}, #else {TLS1_VERSION, NULL, NULL}, #endif #ifndef OPENSSL_NO_SSL3 {SSL3_VERSION, sslv3_client_method, sslv3_server_method}, #else {SSL3_VERSION, NULL, NULL}, #endif {0, NULL, NULL}, }; #if DTLS_MAX_VERSION_INTERNAL != DTLS1_2_VERSION # error Code needs update for DTLS_method() support beyond DTLS1_2_VERSION. #endif /* Must be in order high to low */ static const version_info dtls_version_table[] = { #ifndef OPENSSL_NO_DTLS1_2 {DTLS1_2_VERSION, dtlsv1_2_client_method, dtlsv1_2_server_method}, #else {DTLS1_2_VERSION, NULL, NULL}, #endif #ifndef OPENSSL_NO_DTLS1 {DTLS1_VERSION, dtlsv1_client_method, dtlsv1_server_method}, {DTLS1_BAD_VER, dtls_bad_ver_client_method, NULL}, #else {DTLS1_VERSION, NULL, NULL}, {DTLS1_BAD_VER, NULL, NULL}, #endif {0, NULL, NULL}, }; /* * ssl_method_error - Check whether an SSL_METHOD is enabled. * * @s: The SSL handle for the candidate method * @method: the intended method. * * Returns 0 on success, or an SSL error reason on failure. */ static int ssl_method_error(const SSL_CONNECTION *s, const SSL_METHOD *method) { int version = method->version; if ((s->min_proto_version != 0 && ssl_version_cmp(s, version, s->min_proto_version) < 0) || ssl_security(s, SSL_SECOP_VERSION, 0, version, NULL) == 0) return SSL_R_VERSION_TOO_LOW; if (s->max_proto_version != 0 && ssl_version_cmp(s, version, s->max_proto_version) > 0) return SSL_R_VERSION_TOO_HIGH; if ((s->options & method->mask) != 0) return SSL_R_UNSUPPORTED_PROTOCOL; if ((method->flags & SSL_METHOD_NO_SUITEB) != 0 && tls1_suiteb(s)) return SSL_R_AT_LEAST_TLS_1_2_NEEDED_IN_SUITEB_MODE; return 0; } /* * Only called by servers. Returns 1 if the server has a TLSv1.3 capable * certificate type, or has PSK or a certificate callback configured, or has * a servername callback configure. Otherwise returns 0. */ static int is_tls13_capable(const SSL_CONNECTION *s) { size_t i; int curve; SSL_CTX *sctx = SSL_CONNECTION_GET_CTX(s); if (!ossl_assert(sctx != NULL) || !ossl_assert(s->session_ctx != NULL)) return 0; /* * A servername callback can change the available certs, so if a servername * cb is set then we just assume TLSv1.3 will be ok */ if (sctx->ext.servername_cb != NULL || s->session_ctx->ext.servername_cb != NULL) return 1; #ifndef OPENSSL_NO_PSK if (s->psk_server_callback != NULL) return 1; #endif if (s->psk_find_session_cb != NULL || s->cert->cert_cb != NULL) return 1; /* All provider-based sig algs are required to support at least TLS1.3 */ for (i = 0; i < s->ssl_pkey_num; i++) { /* Skip over certs disallowed for TLSv1.3 */ switch (i) { case SSL_PKEY_DSA_SIGN: case SSL_PKEY_GOST01: case SSL_PKEY_GOST12_256: case SSL_PKEY_GOST12_512: continue; default: break; } if (!ssl_has_cert(s, i)) continue; if (i != SSL_PKEY_ECC) return 1; /* * Prior to TLSv1.3 sig algs allowed any curve to be used. TLSv1.3 is * more restrictive so check that our sig algs are consistent with this * EC cert. See section 4.2.3 of RFC8446. */ curve = ssl_get_EC_curve_nid(s->cert->pkeys[SSL_PKEY_ECC].privatekey); if (tls_check_sigalg_curve(s, curve)) return 1; } return 0; } /* * ssl_version_supported - Check that the specified `version` is supported by * `SSL *` instance * * @s: The SSL handle for the candidate method * @version: Protocol version to test against * * Returns 1 when supported, otherwise 0 */ int ssl_version_supported(const SSL_CONNECTION *s, int version, const SSL_METHOD **meth) { const version_info *vent; const version_info *table; switch (SSL_CONNECTION_GET_SSL(s)->method->version) { default: /* Version should match method version for non-ANY method */ return ssl_version_cmp(s, version, s->version) == 0; case TLS_ANY_VERSION: table = tls_version_table; break; case DTLS_ANY_VERSION: table = dtls_version_table; break; } for (vent = table; vent->version != 0 && ssl_version_cmp(s, version, vent->version) <= 0; ++vent) { const SSL_METHOD *(*thismeth)(void) = s->server ? vent->smeth : vent->cmeth; if (thismeth != NULL && ssl_version_cmp(s, version, vent->version) == 0 && ssl_method_error(s, thismeth()) == 0 && (!s->server || version != TLS1_3_VERSION || is_tls13_capable(s))) { if (meth != NULL) *meth = thismeth(); return 1; } } return 0; } /* * ssl_check_version_downgrade - In response to RFC7507 SCSV version * fallback indication from a client check whether we're using the highest * supported protocol version. * * @s server SSL handle. * * Returns 1 when using the highest enabled version, 0 otherwise. */ int ssl_check_version_downgrade(SSL_CONNECTION *s) { const version_info *vent; const version_info *table; SSL *ssl = SSL_CONNECTION_GET_SSL(s); /* * Check that the current protocol is the highest enabled version * (according to ssl->defltmethod, as version negotiation may have changed * s->method). */ if (s->version == ssl->defltmeth->version) return 1; /* * Apparently we're using a version-flexible SSL_METHOD (not at its * highest protocol version). */ if (ssl->defltmeth->version == TLS_method()->version) table = tls_version_table; else if (ssl->defltmeth->version == DTLS_method()->version) table = dtls_version_table; else { /* Unexpected state; fail closed. */ return 0; } for (vent = table; vent->version != 0; ++vent) { if (vent->smeth != NULL && ssl_method_error(s, vent->smeth()) == 0) return s->version == vent->version; } return 0; } /* * ssl_set_version_bound - set an upper or lower bound on the supported (D)TLS * protocols, provided the initial (D)TLS method is version-flexible. This * function sanity-checks the proposed value and makes sure the method is * version-flexible, then sets the limit if all is well. * * @method_version: The version of the current SSL_METHOD. * @version: the intended limit. * @bound: pointer to limit to be updated. * * Returns 1 on success, 0 on failure. */ int ssl_set_version_bound(int method_version, int version, int *bound) { int valid_tls; int valid_dtls; if (version == 0) { *bound = version; return 1; } valid_tls = version >= SSL3_VERSION && version <= TLS_MAX_VERSION_INTERNAL; valid_dtls = /* We support client side pre-standardisation version of DTLS */ (version == DTLS1_BAD_VER) || (DTLS_VERSION_LE(version, DTLS_MAX_VERSION_INTERNAL) && DTLS_VERSION_GE(version, DTLS1_VERSION)); if (!valid_tls && !valid_dtls) return 0; /*- * Restrict TLS methods to TLS protocol versions. * Restrict DTLS methods to DTLS protocol versions. * Note, DTLS version numbers are decreasing, use comparison macros. * * Note that for both lower-bounds we use explicit versions, not * (D)TLS_MIN_VERSION. This is because we don't want to break user * configurations. If the MIN (supported) version ever rises, the user's * "floor" remains valid even if no longer available. We don't expect the * MAX ceiling to ever get lower, so making that variable makes sense. * * We ignore attempts to set bounds on version-inflexible methods, * returning success. */ switch (method_version) { default: break; case TLS_ANY_VERSION: if (valid_tls) *bound = version; break; case DTLS_ANY_VERSION: if (valid_dtls) *bound = version; break; } return 1; } static void check_for_downgrade(SSL_CONNECTION *s, int vers, DOWNGRADE *dgrd) { if (vers == TLS1_2_VERSION && ssl_version_supported(s, TLS1_3_VERSION, NULL)) { *dgrd = DOWNGRADE_TO_1_2; } else if (!SSL_CONNECTION_IS_DTLS(s) && vers < TLS1_2_VERSION /* * We need to ensure that a server that disables TLSv1.2 * (creating a hole between TLSv1.3 and TLSv1.1) can still * complete handshakes with clients that support TLSv1.2 and * below. Therefore we do not enable the sentinel if TLSv1.3 is * enabled and TLSv1.2 is not. */ && ssl_version_supported(s, TLS1_2_VERSION, NULL)) { *dgrd = DOWNGRADE_TO_1_1; } else { *dgrd = DOWNGRADE_NONE; } } /* * ssl_choose_server_version - Choose server (D)TLS version. Called when the * client HELLO is received to select the final server protocol version and * the version specific method. * * @s: server SSL handle. * * Returns 0 on success or an SSL error reason number on failure. */ int ssl_choose_server_version(SSL_CONNECTION *s, CLIENTHELLO_MSG *hello, DOWNGRADE *dgrd) { /*- * With version-flexible methods we have an initial state with: * * s->method->version == (D)TLS_ANY_VERSION, * s->version == (D)TLS_MAX_VERSION_INTERNAL. * * So we detect version-flexible methods via the method version, not the * handle version. */ SSL *ssl = SSL_CONNECTION_GET_SSL(s); int server_version = ssl->method->version; int client_version = hello->legacy_version; const version_info *vent; const version_info *table; int disabled = 0; RAW_EXTENSION *suppversions; s->client_version = client_version; switch (server_version) { default: if (!SSL_CONNECTION_IS_TLS13(s)) { if (ssl_version_cmp(s, client_version, s->version) < 0) return SSL_R_WRONG_SSL_VERSION; *dgrd = DOWNGRADE_NONE; /* * If this SSL handle is not from a version flexible method we don't * (and never did) check min/max FIPS or Suite B constraints. Hope * that's OK. It is up to the caller to not choose fixed protocol * versions they don't want. If not, then easy to fix, just return * ssl_method_error(s, s->method) */ return 0; } /* * Fall through if we are TLSv1.3 already (this means we must be after * a HelloRetryRequest */ /* fall thru */ case TLS_ANY_VERSION: table = tls_version_table; break; case DTLS_ANY_VERSION: table = dtls_version_table; break; } suppversions = &hello->pre_proc_exts[TLSEXT_IDX_supported_versions]; /* If we did an HRR then supported versions is mandatory */ if (!suppversions->present && s->hello_retry_request != SSL_HRR_NONE) return SSL_R_UNSUPPORTED_PROTOCOL; if (suppversions->present && !SSL_CONNECTION_IS_DTLS(s)) { unsigned int candidate_vers = 0; unsigned int best_vers = 0; const SSL_METHOD *best_method = NULL; PACKET versionslist; suppversions->parsed = 1; if (!PACKET_as_length_prefixed_1(&suppversions->data, &versionslist)) { /* Trailing or invalid data? */ return SSL_R_LENGTH_MISMATCH; } /* * The TLSv1.3 spec says the client MUST set this to TLS1_2_VERSION. * The spec only requires servers to check that it isn't SSLv3: * "Any endpoint receiving a Hello message with * ClientHello.legacy_version or ServerHello.legacy_version set to * 0x0300 MUST abort the handshake with a "protocol_version" alert." * We are slightly stricter and require that it isn't SSLv3 or lower. * We tolerate TLSv1 and TLSv1.1. */ if (client_version <= SSL3_VERSION) return SSL_R_BAD_LEGACY_VERSION; while (PACKET_get_net_2(&versionslist, &candidate_vers)) { if (ssl_version_cmp(s, candidate_vers, best_vers) <= 0) continue; if (ssl_version_supported(s, candidate_vers, &best_method)) best_vers = candidate_vers; } if (PACKET_remaining(&versionslist) != 0) { /* Trailing data? */ return SSL_R_LENGTH_MISMATCH; } if (best_vers > 0) { if (s->hello_retry_request != SSL_HRR_NONE) { /* * This is after a HelloRetryRequest so we better check that we * negotiated TLSv1.3 */ if (best_vers != TLS1_3_VERSION) return SSL_R_UNSUPPORTED_PROTOCOL; return 0; } check_for_downgrade(s, best_vers, dgrd); s->version = best_vers; ssl->method = best_method; if (!ssl_set_record_protocol_version(s, best_vers)) return ERR_R_INTERNAL_ERROR; return 0; } return SSL_R_UNSUPPORTED_PROTOCOL; } /* * If the supported versions extension isn't present, then the highest * version we can negotiate is TLSv1.2 */ if (ssl_version_cmp(s, client_version, TLS1_3_VERSION) >= 0) client_version = TLS1_2_VERSION; /* * No supported versions extension, so we just use the version supplied in * the ClientHello. */ for (vent = table; vent->version != 0; ++vent) { const SSL_METHOD *method; if (vent->smeth == NULL || ssl_version_cmp(s, client_version, vent->version) < 0) continue; method = vent->smeth(); if (ssl_method_error(s, method) == 0) { check_for_downgrade(s, vent->version, dgrd); s->version = vent->version; ssl->method = method; if (!ssl_set_record_protocol_version(s, s->version)) return ERR_R_INTERNAL_ERROR; return 0; } disabled = 1; } return disabled ? SSL_R_UNSUPPORTED_PROTOCOL : SSL_R_VERSION_TOO_LOW; } /* * ssl_choose_client_version - Choose client (D)TLS version. Called when the * server HELLO is received to select the final client protocol version and * the version specific method. * * @s: client SSL handle. * @version: The proposed version from the server's HELLO. * @extensions: The extensions received * * Returns 1 on success or 0 on error. */ int ssl_choose_client_version(SSL_CONNECTION *s, int version, RAW_EXTENSION *extensions) { const version_info *vent; const version_info *table; int ret, ver_min, ver_max, real_max, origv; SSL *ssl = SSL_CONNECTION_GET_SSL(s); origv = s->version; s->version = version; /* This will overwrite s->version if the extension is present */ if (!tls_parse_extension(s, TLSEXT_IDX_supported_versions, SSL_EXT_TLS1_2_SERVER_HELLO | SSL_EXT_TLS1_3_SERVER_HELLO, extensions, NULL, 0)) { s->version = origv; return 0; } if (s->hello_retry_request != SSL_HRR_NONE && s->version != TLS1_3_VERSION) { s->version = origv; SSLfatal(s, SSL_AD_PROTOCOL_VERSION, SSL_R_WRONG_SSL_VERSION); return 0; } switch (ssl->method->version) { default: if (s->version != ssl->method->version) { s->version = origv; SSLfatal(s, SSL_AD_PROTOCOL_VERSION, SSL_R_WRONG_SSL_VERSION); return 0; } /* * If this SSL handle is not from a version flexible method we don't * (and never did) check min/max, FIPS or Suite B constraints. Hope * that's OK. It is up to the caller to not choose fixed protocol * versions they don't want. If not, then easy to fix, just return * ssl_method_error(s, s->method) */ if (!ssl_set_record_protocol_version(s, s->version)) { SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR); return 0; } return 1; case TLS_ANY_VERSION: table = tls_version_table; break; case DTLS_ANY_VERSION: table = dtls_version_table; break; } ret = ssl_get_min_max_version(s, &ver_min, &ver_max, &real_max); if (ret != 0) { s->version = origv; SSLfatal(s, SSL_AD_PROTOCOL_VERSION, ret); return 0; } if (ssl_version_cmp(s, s->version, ver_min) < 0 || ssl_version_cmp(s, s->version, ver_max) > 0) { s->version = origv; SSLfatal(s, SSL_AD_PROTOCOL_VERSION, SSL_R_UNSUPPORTED_PROTOCOL); return 0; } if ((s->mode & SSL_MODE_SEND_FALLBACK_SCSV) == 0) real_max = ver_max; /* Check for downgrades */ /* TODO(DTLSv1.3): Update this code for DTLSv1.3 */ if (!SSL_CONNECTION_IS_DTLS(s) && real_max > s->version) { /* Signal applies to all versions */ if (memcmp(tls11downgrade, s->s3.server_random + SSL3_RANDOM_SIZE - sizeof(tls11downgrade), sizeof(tls11downgrade)) == 0) { s->version = origv; SSLfatal(s, SSL_AD_ILLEGAL_PARAMETER, SSL_R_INAPPROPRIATE_FALLBACK); return 0; } /* Only when accepting TLS1.3 */ if (real_max == TLS1_3_VERSION && memcmp(tls12downgrade, s->s3.server_random + SSL3_RANDOM_SIZE - sizeof(tls12downgrade), sizeof(tls12downgrade)) == 0) { s->version = origv; SSLfatal(s, SSL_AD_ILLEGAL_PARAMETER, SSL_R_INAPPROPRIATE_FALLBACK); return 0; } } for (vent = table; vent->version != 0; ++vent) { if (vent->cmeth == NULL || s->version != vent->version) continue; ssl->method = vent->cmeth(); if (!ssl_set_record_protocol_version(s, s->version)) { SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR); return 0; } return 1; } s->version = origv; SSLfatal(s, SSL_AD_PROTOCOL_VERSION, SSL_R_UNSUPPORTED_PROTOCOL); return 0; } /* * ssl_get_min_max_version - get minimum and maximum protocol version * @s: The SSL connection * @min_version: The minimum supported version * @max_version: The maximum supported version * @real_max: The highest version below the lowest compile time version hole * where that hole lies above at least one run-time enabled * protocol. * * Work out what version we should be using for the initial ClientHello if the * version is initially (D)TLS_ANY_VERSION. We apply any explicit SSL_OP_NO_xxx * options, the MinProtocol and MaxProtocol configuration commands, any Suite B * constraints and any floor imposed by the security level here, * so we don't advertise the wrong protocol version to only reject the outcome later. * * Computing the right floor matters. If, e.g., TLS 1.0 and 1.2 are enabled, * TLS 1.1 is disabled, but the security level, Suite-B and/or MinProtocol * only allow TLS 1.2, we want to advertise TLS1.2, *not* TLS1. * * Returns 0 on success or an SSL error reason number on failure. On failure * min_version and max_version will also be set to 0. */ int ssl_get_min_max_version(const SSL_CONNECTION *s, int *min_version, int *max_version, int *real_max) { int version, tmp_real_max; int hole; const SSL_METHOD *method; const version_info *table; const version_info *vent; const SSL *ssl = SSL_CONNECTION_GET_SSL(s); switch (ssl->method->version) { default: /* * If this SSL handle is not from a version flexible method we don't * (and never did) check min/max FIPS or Suite B constraints. Hope * that's OK. It is up to the caller to not choose fixed protocol * versions they don't want. If not, then easy to fix, just return * ssl_method_error(s, s->method) */ *min_version = *max_version = s->version; /* * Providing a real_max only makes sense where we're using a version * flexible method. */ if (!ossl_assert(real_max == NULL)) return ERR_R_INTERNAL_ERROR; return 0; case TLS_ANY_VERSION: table = tls_version_table; break; case DTLS_ANY_VERSION: table = dtls_version_table; break; } /* * SSL_OP_NO_X disables all protocols above X *if* there are some protocols * below X enabled. This is required in order to maintain the "version * capability" vector contiguous. Any versions with a NULL client method * (protocol version client is disabled at compile-time) is also a "hole". * * Our initial state is hole == 1, version == 0. That is, versions above * the first version in the method table are disabled (a "hole" above * the valid protocol entries) and we don't have a selected version yet. * * Whenever "hole == 1", and we hit an enabled method, its version becomes * the selected version. We're no longer in a hole, so "hole" becomes 0. * * If "hole == 0" and we hit an enabled method, we support a contiguous * range of at least two methods. If we hit a disabled method, * then hole becomes true again, but nothing else changes yet, * because all the remaining methods may be disabled too. * If we again hit an enabled method after the new hole, it becomes * selected, as we start from scratch. */ *min_version = version = 0; hole = 1; if (real_max != NULL) *real_max = 0; tmp_real_max = 0; for (vent = table; vent->version != 0; ++vent) { /* * A table entry with a NULL client method is still a hole in the * "version capability" vector. */ if (vent->cmeth == NULL) { hole = 1; tmp_real_max = 0; continue; } method = vent->cmeth(); if (hole == 1 && tmp_real_max == 0) tmp_real_max = vent->version; if (ssl_method_error(s, method) != 0) { hole = 1; } else if (!hole) { *min_version = method->version; } else { if (real_max != NULL && tmp_real_max != 0) *real_max = tmp_real_max; version = method->version; *min_version = version; hole = 0; } } *max_version = version; /* Fail if everything is disabled */ if (version == 0) return SSL_R_NO_PROTOCOLS_AVAILABLE; return 0; } /* * ssl_set_client_hello_version - Work out what version we should be using for * the initial ClientHello.legacy_version field. * * @s: client SSL handle. * * Returns 0 on success or an SSL error reason number on failure. */ int ssl_set_client_hello_version(SSL_CONNECTION *s) { int ver_min, ver_max, ret; /* * In a renegotiation we always send the same client_version that we sent * last time, regardless of which version we eventually negotiated. */ if (!SSL_IS_FIRST_HANDSHAKE(s)) return 0; ret = ssl_get_min_max_version(s, &ver_min, &ver_max, NULL); if (ret != 0) return ret; s->version = ver_max; if (SSL_CONNECTION_IS_DTLS(s)) { if (ver_max == DTLS1_BAD_VER) { /* * Even though this is technically before version negotiation, * because we have asked for DTLS1_BAD_VER we will never negotiate * anything else, and this has impacts on the record layer for when * we read the ServerHello. So we need to tell the record layer * about this immediately. */ if (!ssl_set_record_protocol_version(s, ver_max)) return 0; } } else if (ver_max > TLS1_2_VERSION) { /* TLS1.3 always uses TLS1.2 in the legacy_version field */ ver_max = TLS1_2_VERSION; } s->client_version = ver_max; return 0; } /* * Checks a list of |groups| to determine if the |group_id| is in it. If it is * and |checkallow| is 1 then additionally check if the group is allowed to be * used. Returns 1 if the group is in the list (and allowed if |checkallow| is * 1) or 0 otherwise. If provided a pointer it will also return the position * where the group was found. */ int check_in_list(SSL_CONNECTION *s, uint16_t group_id, const uint16_t *groups, size_t num_groups, int checkallow, size_t *pos) { size_t i; if (groups == NULL || num_groups == 0) return 0; for (i = 0; i < num_groups; i++) { uint16_t group = groups[i]; if (group_id == group && (!checkallow || tls_group_allowed(s, group, SSL_SECOP_CURVE_CHECK))) { if (pos != NULL) *pos = i; return 1; } } return 0; } /* Replace ClientHello1 in the transcript hash with a synthetic message */ int create_synthetic_message_hash(SSL_CONNECTION *s, const unsigned char *hashval, size_t hashlen, const unsigned char *hrr, size_t hrrlen) { unsigned char hashvaltmp[EVP_MAX_MD_SIZE]; unsigned char msghdr[SSL3_HM_HEADER_LENGTH]; memset(msghdr, 0, sizeof(msghdr)); if (hashval == NULL) { hashval = hashvaltmp; hashlen = 0; /* Get the hash of the initial ClientHello */ if (!ssl3_digest_cached_records(s, 0) || !ssl_handshake_hash(s, hashvaltmp, sizeof(hashvaltmp), &hashlen)) { /* SSLfatal() already called */ return 0; } } /* Reinitialise the transcript hash */ if (!ssl3_init_finished_mac(s)) { /* SSLfatal() already called */ return 0; } /* Inject the synthetic message_hash message */ msghdr[0] = SSL3_MT_MESSAGE_HASH; msghdr[SSL3_HM_HEADER_LENGTH - 1] = (unsigned char)hashlen; if (!ssl3_finish_mac(s, msghdr, SSL3_HM_HEADER_LENGTH) || !ssl3_finish_mac(s, hashval, hashlen)) { /* SSLfatal() already called */ return 0; } /* * Now re-inject the HRR and current message if appropriate (we just deleted * it when we reinitialised the transcript hash above). Only necessary after * receiving a ClientHello2 with a cookie. */ if (hrr != NULL && (!ssl3_finish_mac(s, hrr, hrrlen) || !ssl3_finish_mac(s, (unsigned char *)s->init_buf->data, s->s3.tmp.message_size + SSL3_HM_HEADER_LENGTH))) { /* SSLfatal() already called */ return 0; } return 1; } static int ca_dn_cmp(const X509_NAME *const *a, const X509_NAME *const *b) { return X509_NAME_cmp(*a, *b); } int parse_ca_names(SSL_CONNECTION *s, PACKET *pkt) { STACK_OF(X509_NAME) *ca_sk = sk_X509_NAME_new(ca_dn_cmp); X509_NAME *xn = NULL; PACKET cadns; if (ca_sk == NULL) { SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_CRYPTO_LIB); goto err; } /* get the CA RDNs */ if (!PACKET_get_length_prefixed_2(pkt, &cadns)) { SSLfatal(s, SSL_AD_DECODE_ERROR, SSL_R_LENGTH_MISMATCH); goto err; } while (PACKET_remaining(&cadns)) { const unsigned char *namestart, *namebytes; unsigned int name_len; if (!PACKET_get_net_2(&cadns, &name_len) || !PACKET_get_bytes(&cadns, &namebytes, name_len)) { SSLfatal(s, SSL_AD_DECODE_ERROR, SSL_R_LENGTH_MISMATCH); goto err; } namestart = namebytes; if ((xn = d2i_X509_NAME(NULL, &namebytes, name_len)) == NULL) { SSLfatal(s, SSL_AD_DECODE_ERROR, ERR_R_ASN1_LIB); goto err; } if (namebytes != (namestart + name_len)) { SSLfatal(s, SSL_AD_DECODE_ERROR, SSL_R_CA_DN_LENGTH_MISMATCH); goto err; } if (!sk_X509_NAME_push(ca_sk, xn)) { SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_CRYPTO_LIB); goto err; } xn = NULL; } sk_X509_NAME_pop_free(s->s3.tmp.peer_ca_names, X509_NAME_free); s->s3.tmp.peer_ca_names = ca_sk; return 1; err: sk_X509_NAME_pop_free(ca_sk, X509_NAME_free); X509_NAME_free(xn); return 0; } const STACK_OF(X509_NAME) *get_ca_names(SSL_CONNECTION *s) { const STACK_OF(X509_NAME) *ca_sk = NULL; SSL *ssl = SSL_CONNECTION_GET_SSL(s); if (s->server) { ca_sk = SSL_get_client_CA_list(ssl); if (ca_sk != NULL && sk_X509_NAME_num(ca_sk) == 0) ca_sk = NULL; } if (ca_sk == NULL) ca_sk = SSL_get0_CA_list(ssl); return ca_sk; } int construct_ca_names(SSL_CONNECTION *s, const STACK_OF(X509_NAME) *ca_sk, WPACKET *pkt) { /* Start sub-packet for client CA list */ if (!WPACKET_start_sub_packet_u16(pkt)) { SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR); return 0; } if ((ca_sk != NULL) && !(s->options & SSL_OP_DISABLE_TLSEXT_CA_NAMES)) { int i; for (i = 0; i < sk_X509_NAME_num(ca_sk); i++) { unsigned char *namebytes; X509_NAME *name = sk_X509_NAME_value(ca_sk, i); int namelen; if (name == NULL || (namelen = i2d_X509_NAME(name, NULL)) < 0 || !WPACKET_sub_allocate_bytes_u16(pkt, namelen, &namebytes) || i2d_X509_NAME(name, &namebytes) != namelen) { SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR); return 0; } } } if (!WPACKET_close(pkt)) { SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR); return 0; } return 1; } /* Create a buffer containing data to be signed for server key exchange */ size_t construct_key_exchange_tbs(SSL_CONNECTION *s, unsigned char **ptbs, const void *param, size_t paramlen) { size_t tbslen = 2 * SSL3_RANDOM_SIZE + paramlen; unsigned char *tbs = OPENSSL_malloc(tbslen); if (tbs == NULL) { SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_CRYPTO_LIB); return 0; } memcpy(tbs, s->s3.client_random, SSL3_RANDOM_SIZE); memcpy(tbs + SSL3_RANDOM_SIZE, s->s3.server_random, SSL3_RANDOM_SIZE); memcpy(tbs + SSL3_RANDOM_SIZE * 2, param, paramlen); *ptbs = tbs; return tbslen; } /* * Saves the current handshake digest for Post-Handshake Auth, * Done after ClientFinished is processed, done exactly once */ int tls13_save_handshake_digest_for_pha(SSL_CONNECTION *s) { if (s->pha_dgst == NULL) { if (!ssl3_digest_cached_records(s, 1)) /* SSLfatal() already called */ return 0; s->pha_dgst = EVP_MD_CTX_new(); if (s->pha_dgst == NULL) { SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR); return 0; } if (!EVP_MD_CTX_copy_ex(s->pha_dgst, s->s3.handshake_dgst)) { SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR); EVP_MD_CTX_free(s->pha_dgst); s->pha_dgst = NULL; return 0; } } return 1; } /* * Restores the Post-Handshake Auth handshake digest * Done just before sending/processing the Cert Request */ int tls13_restore_handshake_digest_for_pha(SSL_CONNECTION *s) { if (s->pha_dgst == NULL) { SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR); return 0; } if (!EVP_MD_CTX_copy_ex(s->s3.handshake_dgst, s->pha_dgst)) { SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR); return 0; } return 1; } #ifndef OPENSSL_NO_COMP_ALG MSG_PROCESS_RETURN tls13_process_compressed_certificate(SSL_CONNECTION *sc, PACKET *pkt, PACKET *tmppkt, BUF_MEM *buf) { MSG_PROCESS_RETURN ret = MSG_PROCESS_ERROR; int comp_alg; COMP_METHOD *method = NULL; COMP_CTX *comp = NULL; size_t expected_length; size_t comp_length; int i; int found = 0; if (buf == NULL) { SSLfatal(sc, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR); goto err; } if (!PACKET_get_net_2(pkt, (unsigned int*)&comp_alg)) { SSLfatal(sc, SSL_AD_BAD_CERTIFICATE, ERR_R_INTERNAL_ERROR); goto err; } /* If we have a prefs list, make sure the algorithm is in it */ if (sc->cert_comp_prefs[0] != TLSEXT_comp_cert_none) { for (i = 0; sc->cert_comp_prefs[i] != TLSEXT_comp_cert_none; i++) { if (sc->cert_comp_prefs[i] == comp_alg) { found = 1; break; } } if (!found) { SSLfatal(sc, SSL_AD_ILLEGAL_PARAMETER, SSL_R_BAD_COMPRESSION_ALGORITHM); goto err; } } if (!ossl_comp_has_alg(comp_alg)) { SSLfatal(sc, SSL_AD_BAD_CERTIFICATE, SSL_R_BAD_COMPRESSION_ALGORITHM); goto err; } switch (comp_alg) { case TLSEXT_comp_cert_zlib: method = COMP_zlib_oneshot(); break; case TLSEXT_comp_cert_brotli: method = COMP_brotli_oneshot(); break; case TLSEXT_comp_cert_zstd: method = COMP_zstd_oneshot(); break; default: SSLfatal(sc, SSL_AD_BAD_CERTIFICATE, SSL_R_BAD_COMPRESSION_ALGORITHM); goto err; } if ((comp = COMP_CTX_new(method)) == NULL || !PACKET_get_net_3_len(pkt, &expected_length) || !PACKET_get_net_3_len(pkt, &comp_length)) { SSLfatal(sc, SSL_AD_BAD_CERTIFICATE, SSL_R_BAD_DECOMPRESSION); goto err; } + /* Prevent excessive pre-decompression allocation */ + if (expected_length > sc->max_cert_list) { + SSLfatal(sc, SSL_AD_ILLEGAL_PARAMETER, SSL_R_EXCESSIVE_MESSAGE_SIZE); + goto err; + } + if (PACKET_remaining(pkt) != comp_length || comp_length == 0) { SSLfatal(sc, SSL_AD_DECODE_ERROR, SSL_R_BAD_DECOMPRESSION); goto err; } if (!BUF_MEM_grow(buf, expected_length) || !PACKET_buf_init(tmppkt, (unsigned char *)buf->data, expected_length) || COMP_expand_block(comp, (unsigned char *)buf->data, expected_length, (unsigned char*)PACKET_data(pkt), comp_length) != (int)expected_length) { SSLfatal(sc, SSL_AD_BAD_CERTIFICATE, SSL_R_BAD_DECOMPRESSION); goto err; } ret = MSG_PROCESS_CONTINUE_PROCESSING; err: COMP_CTX_free(comp); return ret; } #endif