<commit_before>/*
 * Open AnyConnect (SSL + DTLS) client
 *
 * © 2008 <NAME> <<EMAIL>>
 *
 * Permission to use, copy, modify, and/or 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 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 <sys/socket.h>
#include <netdb.h>
#include <unistd.h>
#include <fcntl.h>
#include <time.h>

#include <openssl/ssl.h>
#include <openssl/err.h>
#include <openssl/engine.h>

#include "anyconnect.h"

/* Helper functions for reading/writing lines over SSL.
   We could use cURL for the HTTP stuff, but it's overkill */

static int  __attribute__ ((format (printf, 2, 3)))
	my_SSL_printf(SSL *ssl, const char *fmt, ...) 
{
	char buf[1024];
	va_list args;

	
	buf[1023] = 0;

	va_start(args, fmt);
	vsnprintf(buf, 1023, fmt, args);
	va_end(args);
	if (verbose)
		printf("%s", buf);
	return SSL_write(ssl, buf, strlen(buf));

}

static int my_SSL_gets(SSL *ssl, char *buf, size_t len)
{
	int i = 0;
	int ret;

	if (len < 2)
		return -EINVAL;

	while ( (ret = SSL_read(ssl, buf + i, 1)) == 1) {
		if (buf[i] == '\n') {
			buf[i] = 0;
			if (i && buf[i-1] == '\r') {
				buf[i-1] = 0;
				i--;
			}
			return i;
		}
		i++;

		if (i >= len - 1) {
			buf[i] = 0;
			return i;
		}
	}

	buf[i] = 0;
	return i?:ret;
}


static int ui_open(UI *ui)
{
	return UI_method_get_opener(UI_OpenSSL())(ui);
}

static int ui_read(UI *ui, UI_STRING *uis)
{
	struct anyconnect_info *vpninfo = UI_get0_user_data(ui);
	if (vpninfo->tpmpass)
		return 1;

	return UI_method_get_reader(UI_OpenSSL())(ui, uis);

}
static int ui_write(UI *ui, UI_STRING *uis)
{
	struct anyconnect_info *vpninfo = UI_get0_user_data(ui);
	if (vpninfo->tpmpass) {
		UI_set_result(ui, uis, vpninfo->tpmpass);
		return 1;
	}
		

	return UI_method_get_writer(UI_OpenSSL())(ui, uis);

}
static int ui_close(UI *ui)
{
	return 1;
	return UI_method_get_closer(UI_OpenSSL())(ui);
}

static int load_certificate(struct anyconnect_info *vpninfo, 
			    SSL_CTX *https_ctx)
{
	if (verbose)
		printf("Using Certificate file %s\n", vpninfo->cert);
	if (!SSL_CTX_use_certificate_file(https_ctx, vpninfo->cert,
					  SSL_FILETYPE_PEM)) {
		fprintf(stderr, "Certificate failed\n");
		ERR_print_errors_fp(stderr);
		return -EINVAL;
	}
	
	if (vpninfo->tpmkey) {
		ENGINE *e;
		EVP_PKEY *key;
		UI_METHOD *ui_method = UI_create_method("AnyConnect VPN UI");

		UI_method_set_opener(ui_method, ui_open);
		UI_method_set_reader(ui_method, ui_read);
		UI_method_set_writer(ui_method, ui_write);
		UI_method_set_closer(ui_method, ui_close);

		ENGINE_load_builtin_engines();

		e = ENGINE_by_id("tpm");
		if (!e) {
			fprintf(stderr, "Can't load TPM engine.\n");
			ERR_print_errors_fp(stderr);
			return -EINVAL;
		}
		if (!ENGINE_init(e) || !ENGINE_set_default_RSA(e) ||
		    !ENGINE_set_default_RAND(e)) {
			fprintf(stderr, "Failed to init TPM engine\n");
			ERR_print_errors_fp(stderr);
			ENGINE_free(e);
			ENGINE_finish(e);
			return -EINVAL;
		}     

		if (0 && vpninfo->tpmpass) {
			if (!ENGINE_ctrl_cmd(e, "PIN", strlen(vpninfo->tpmpass),
					     vpninfo->tpmpass, NULL, 0)) {
				fprintf(stderr, "Failed to set TPM SRK password\n");
				ERR_print_errors_fp(stderr);
			}
			/* Try it manually */
		}
		key = ENGINE_load_private_key(e, vpninfo->tpmkey,
					      ui_method, vpninfo);
		if (!key) {
			fprintf(stderr, 
				"Failed to load TPM private key\n");
			ERR_print_errors_fp(stderr);
			ENGINE_free(e);
			ENGINE_finish(e);
			return -EINVAL;
		}
		if (!SSL_CTX_use_PrivateKey(https_ctx, key)) {
			fprintf(stderr, "Add key from TPM failed\n");
			ERR_print_errors_fp(stderr);
			ENGINE_free(e);
			ENGINE_finish(e);
			return -EINVAL;
		}
	} else {
		/* Key should be in cert file too*/
		if (!SSL_CTX_use_RSAPrivateKey_file(https_ctx, vpninfo->cert,
						    SSL_FILETYPE_PEM)) {
			fprintf(stderr, "Private key failed\n");
			ERR_print_errors_fp(stderr);
			return -EINVAL;
		}
	}
	return 0;
}
 
static int open_https(struct anyconnect_info *vpninfo)
{
	SSL_METHOD *ssl3_method;<commit_msg>fixme for cert passphrase<commit_after>/*
 * Open AnyConnect (SSL + DTLS) client
 *
 * © 2008 <NAME> <<EMAIL>>
 *
 * Permission to use, copy, modify, and/or 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 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 <sys/socket.h>
#include <netdb.h>
#include <unistd.h>
#include <fcntl.h>
#include <time.h>

#include <openssl/ssl.h>
#include <openssl/err.h>
#include <openssl/engine.h>

#include "anyconnect.h"

/* Helper functions for reading/writing lines over SSL.
   We could use cURL for the HTTP stuff, but it's overkill */

static int  __attribute__ ((format (printf, 2, 3)))
	my_SSL_printf(SSL *ssl, const char *fmt, ...) 
{
	char buf[1024];
	va_list args;

	
	buf[1023] = 0;

	va_start(args, fmt);
	vsnprintf(buf, 1023, fmt, args);
	va_end(args);
	if (verbose)
		printf("%s", buf);
	return SSL_write(ssl, buf, strlen(buf));

}

static int my_SSL_gets(SSL *ssl, char *buf, size_t len)
{
	int i = 0;
	int ret;

	if (len < 2)
		return -EINVAL;

	while ( (ret = SSL_read(ssl, buf + i, 1)) == 1) {
		if (buf[i] == '\n') {
			buf[i] = 0;
			if (i && buf[i-1] == '\r') {
				buf[i-1] = 0;
				i--;
			}
			return i;
		}
		i++;

		if (i >= len - 1) {
			buf[i] = 0;
			return i;
		}
	}

	buf[i] = 0;
	return i?:ret;
}


static int ui_open(UI *ui)
{
	return UI_method_get_opener(UI_OpenSSL())(ui);
}

static int ui_read(UI *ui, UI_STRING *uis)
{
	struct anyconnect_info *vpninfo = UI_get0_user_data(ui);
	if (vpninfo->tpmpass)
		return 1;

	return UI_method_get_reader(UI_OpenSSL())(ui, uis);

}
static int ui_write(UI *ui, UI_STRING *uis)
{
	struct anyconnect_info *vpninfo = UI_get0_user_data(ui);
	if (vpninfo->tpmpass) {
		UI_set_result(ui, uis, vpninfo->tpmpass);
		return 1;
	}
		

	return UI_method_get_writer(UI_OpenSSL())(ui, uis);

}
static int ui_close(UI *ui)
{
	return 1;
	return UI_method_get_closer(UI_OpenSSL())(ui);
}

static int load_certificate(struct anyconnect_info *vpninfo, 
			    SSL_CTX *https_ctx)
{
	if (verbose)
		printf("Using Certificate file %s\n", vpninfo->cert);
	if (!SSL_CTX_use_certificate_file(https_ctx, vpninfo->cert,
					  SSL_FILETYPE_PEM)) {
		fprintf(stderr, "Certificate failed\n");
		ERR_print_errors_fp(stderr);
		return -EINVAL;
	}
	
	if (vpninfo->tpmkey) {
		ENGINE *e;
		EVP_PKEY *key;
		UI_METHOD *ui_method = UI_create_method("AnyConnect VPN UI");

		UI_method_set_opener(ui_method, ui_open);
		UI_method_set_reader(ui_method, ui_read);
		UI_method_set_writer(ui_method, ui_write);
		UI_method_set_closer(ui_method, ui_close);

		ENGINE_load_builtin_engines();

		e = ENGINE_by_id("tpm");
		if (!e) {
			fprintf(stderr, "Can't load TPM engine.\n");
			ERR_print_errors_fp(stderr);
			return -EINVAL;
		}
		if (!ENGINE_init(e) || !ENGINE_set_default_RSA(e) ||
		    !ENGINE_set_default_RAND(e)) {
			fprintf(stderr, "Failed to init TPM engine\n");
			ERR_print_errors_fp(stderr);
			ENGINE_free(e);
			ENGINE_finish(e);
			return -EINVAL;
		}     

		if (0 && vpninfo->tpmpass) {
			if (!ENGINE_ctrl_cmd(e, "PIN", strlen(vpninfo->tpmpass),
					     vpninfo->tpmpass, NULL, 0)) {
				fprintf(stderr, "Failed to set TPM SRK password\n");
				ERR_print_errors_fp(stderr);
			}
			/* Try it manually */
		}
		key = ENGINE_load_private_key(e, vpninfo->tpmkey,
					      ui_method, vpninfo);
		if (!key) {
			fprintf(stderr, 
				"Failed to load TPM private key\n");
			ERR_print_errors_fp(stderr);
			ENGINE_free(e);
			ENGINE_finish(e);
			return -EINVAL;
		}
		if (!SSL_CTX_use_PrivateKey(https_ctx, key)) {
			fprintf(stderr, "Add key from TPM failed\n");
			ERR_print_errors_fp(stderr);
			ENGINE_free(e);
			ENGINE_finish(e);
			return -EINVAL;
		}
	} else {
		/* Key should be in cert file too*/
		/* FIXME: Can we do our own UI for PEM passphrase too? */
		if (!SSL_CTX_use_RSAPrivateKey_file(https_ctx, vpninfo->cert,
						    SSL_FILETYPE_PEM)) {
			fprintf(stderr, "Private key failed\n");
			ERR_print_errors_fp(stderr);
			return -EINVAL;
		}
	}
	return 0;
}
 
static int open_https(struct anyconnect_info *vpninfo)
{
	SSL_METHOD *ssl3_method;
{'fraction_non_alphanumeric': 0.13131094257854822, 'fraction_numerical': 0.008450704225352112, 'mean_word_length': 3.881544156530936, 'pattern_counts': {'":': 0, '<': 27, '<?xml version=': 0, '>': 49, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '7066084', 'n_tokens_mistral': 3956, 'n_tokens_neox': 3510, 'n_words': 905}