/*
 * Copyright (c) 2005 Darren Tucker <dtucker@zip.com.au>
 * Copyright (c) 2011-2026 Roumen Petrov.  All rights reserved.
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
 * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
 * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

#include "includes.h"

#ifdef WITH_OPENSSL

#include <stdarg.h>
#include <string.h>

#include "evp-compat.h"

#if !defined(HAVE_ERR_ADD_ERROR_DATA) && defined(HAVE_ERR_ASPRINTF_ERROR_DATA)
#include <openssl/err.h>	/* for ERR_asprintf_error_data() */

void
ERR_add_error_data(int num, ...) {
	va_list args;
	va_start(args, num);
	ERR_add_error_vdata(num, args);
	va_end(args);
}

void
ERR_add_error_vdata(int num, va_list args) {
	char *arg;
	if (num != 1) return; /* PKIX-SSH call only with one argument */
	arg = va_arg(args, char *);
	ERR_asprintf_error_data("%s", arg);
}
#endif

#ifndef HAVE_EVP_PKEY_PRINT_PARAMS
int
EVP_PKEY_print_params(BIO *out, const EVP_PKEY *pkey,
    int indent, /*ASN1_PCTX*/void *pctx)
{
	int ret;
	int evp_id = -1;

	UNUSED(indent);
	UNUSED(pctx);
	if (pkey == NULL) goto err;

	evp_id = EVP_PKEY_base_id(pkey);
	switch (evp_id) {
	case EVP_PKEY_DH: {
		DH *dh = EVP_PKEY_get1_DH((EVP_PKEY*/*safe cast*/)pkey);
		if (dh == NULL) goto err;
		ret = DHparams_print(out, dh);
		DH_free(dh);
		} break;
	/*TODO*/
	default:
		goto err;
	}

	return ret;

err:
	BIO_printf(out, "cannot print parameters for pkey type %d", evp_id);
	return -1;
}
#endif

/* Notes about new OpenSSL 3+ version scheme:
 * Test for compatibility version is not applicable as new base version
 * is assigned to all library symbols. With other words for any new major
 * release, the version number for all symbols is automatically bumped
 * to the new release's version number. Also minor releases should keep
 * binary compatibility.
 * Usable only if OS loader does not support symbol versioning.
 */
/*
 * OpenSSL version numbers: MNNFFPPS: major minor fix patch status.
 * Versions >= 3.0 require only major versions to match.
 * For versions < 3.0, accept compatible fix versions, i.e. allow 1.0.1
 * to work with 1.0.0.
 * Going backwards is only allowed within a patch series.
 * See https://www.openssl.org/policies/releasestrat.html
 */
int ssh_compatible_openssl(long headerver, long libver);

int
ssh_compatible_openssl(long headerver, long libver)
{
	long mask, hfix, lfix;

	/* exact match is always OK */
	if (headerver == libver)
		return 1;

	/* for versions >= 3.0, only the major must match */
	if (headerver >= 0x30000000) {
		mask = 0xf0000000L; /* major */
		return (headerver & mask) == (libver & mask);
	}

	/* for versions < 1.0.0, major,minor,fix,status must match */
	if (headerver < 0x1000000f) {
		mask = 0xfffff00fL; /* major,minor,fix,status */
		return (headerver & mask) == (libver & mask);
	}

	/*
	 * For versions >= 1.0.0, but < 3, major,minor,status must match and
	 * library fix version must be equal to or newer than the header.
	 */
	mask = 0xfff0000fL; /* major,minor,status */
	hfix = (headerver & 0x000ff000) >> 12;
	lfix = (libver & 0x000ff000) >> 12;
	if ( (headerver & mask) == (libver & mask) && lfix >= hfix)
		return 1;
	return 0;
}
#else

typedef int openssl_compat_empty_translation_unit;

#endif /* WITH_OPENSSL */
