$OpenBSD: patch-ssl_c,v 1.1 2021/01/08 13:32:57 sthen Exp $

Add SSL/auth code
https://github.com/Openwsman/wsmancli/issues/10#issuecomment-751253133

Index: ssl.c
--- ssl.c.orig
+++ ssl.c
@@ -0,0 +1,452 @@
+/*
+ *  SSL helper functions.
+ *
+ *  Copyright (C) 2014 Andreas Steinmetz <ast@domdv.de>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License along
+ *  with this program; if not, write to the Free Software Foundation, Inc.,
+ *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+
+#if defined(USE_OPENSSL)
+#include <openssl/ssl.h>
+#include <openssl/err.h>
+#elif defined(USE_GNUTLS)
+#include <gnutls/gnutls.h>
+#endif
+
+#include "ssl.h"
+
+struct ctx
+{
+	int fd;
+#if defined(USE_OPENSSL)
+	SSL *ssl;
+	SSL_CTX *ctx;
+#elif defined(USE_GNUTLS)
+	gnutls_session_t ssl;
+	gnutls_certificate_credentials_t cred;
+#endif
+};
+
+static struct ctx *newctx(int fd)
+{
+	struct ctx *ctx;
+
+	if(!(ctx=malloc(sizeof(struct ctx))))
+	{
+		perror("malloc");
+		return NULL;
+	}
+
+	memset(ctx,0,sizeof(struct ctx));
+
+	ctx->fd=fd;
+
+	return ctx;
+}
+
+#if defined(USE_OPENSSL)
+
+struct ctx *sslinit(int fd,char *cacert)
+{
+	int r;
+	int c=0;
+	struct ctx *ctx;
+
+	if(!(ctx=newctx(fd)))return NULL;
+
+	if(!cacert)return ctx;
+
+	SSL_load_error_strings();
+	SSL_library_init();
+
+	if(!(ctx->ctx=SSL_CTX_new(SSLv23_client_method())))
+	{
+		ERR_print_errors_fp(stderr);
+		goto err1;
+	}
+
+#if OPENSSL_VERSION_NUMBER >= 0x1000100FL
+	SSL_CTX_set_options(ctx->ctx,
+		SSL_OP_NO_TLSv1_1|SSL_OP_NO_TLSv1_2);
+#endif
+
+	if(!SSL_CTX_load_verify_locations(ctx->ctx,cacert,NULL))
+	{
+		ERR_print_errors_fp(stderr);
+		goto err2;
+	}
+
+	SSL_CTX_set_verify_depth(ctx->ctx,5);
+	SSL_CTX_set_verify(ctx->ctx,SSL_VERIFY_PEER,NULL);
+
+	if(!(ctx->ssl=SSL_new(ctx->ctx)))
+	{
+		ERR_print_errors_fp(stderr);
+		goto err2;
+	}
+
+	if(!SSL_set_fd(ctx->ssl,ctx->fd))
+	{
+		ERR_print_errors_fp(stderr);
+		goto err3;
+	}
+
+repeat:	if((r=SSL_connect(ctx->ssl))!=1)
+	{
+		switch(SSL_get_error(ctx->ssl,r))
+		{
+		case SSL_ERROR_WANT_READ:
+		case SSL_ERROR_WANT_WRITE:
+			if(++c<100)
+			{
+				usleep(10000);
+				goto repeat;
+			}
+		}
+		ERR_print_errors_fp(stderr);
+		goto err3;
+	}
+
+	return ctx;
+
+err3:	SSL_free(ctx->ssl);
+err2:	SSL_CTX_free(ctx->ctx);
+err1:	free(ctx);
+	return NULL;
+}
+
+void sslexit(struct ctx *ctx)
+{
+	if(ctx->ssl)
+	{
+		SSL_shutdown(ctx->ssl);
+		SSL_free(ctx->ssl);
+		SSL_CTX_free(ctx->ctx);
+	}
+	free(ctx);
+}
+
+int sslready(struct ctx *ctx)
+{
+	if(ctx->ssl)return SSL_pending(ctx->ssl);
+	else return 0;
+}
+
+ssize_t sslread(struct ctx *ctx,void *buf,size_t count)
+{
+	int l;
+	int c=0;
+
+	if(!ctx->ssl)return read(ctx->fd,buf,count);
+	if(!count)return 0;
+
+repeat:	if((l=SSL_read(ctx->ssl,buf,count))>0)return l;
+
+	switch(SSL_get_error(ctx->ssl,l))
+	{
+	case SSL_ERROR_WANT_READ:
+	case SSL_ERROR_WANT_WRITE:
+		if(++c<100)
+		{
+			usleep(10000);
+			goto repeat;
+		}
+		break;
+	case SSL_ERROR_WANT_X509_LOOKUP:
+		return -1;
+	case SSL_ERROR_ZERO_RETURN:
+		return 0;
+	case SSL_ERROR_SSL:
+		ERR_print_errors_fp(stderr);
+	}
+
+	return -1;
+}
+
+ssize_t sslwrite(struct ctx *ctx,const void *buf,size_t count)
+{
+	int l;
+	int c=0;
+
+	if(!ctx->ssl)return write(ctx->fd,buf,count);
+	if(!count)return 0;
+
+repeat:	if((l=SSL_write(ctx->ssl,buf,count))>0)return l;
+
+	switch(SSL_get_error(ctx->ssl,l))
+	{
+	case SSL_ERROR_WANT_READ:
+	case SSL_ERROR_WANT_WRITE:
+		if(++c<100)
+		{
+			usleep(10000);
+			goto repeat;
+		}
+		break;
+	case SSL_ERROR_WANT_X509_LOOKUP:
+		return -1;
+	case SSL_ERROR_ZERO_RETURN:
+		return 0;
+	case SSL_ERROR_SSL:
+		ERR_print_errors_fp(stderr);
+	}
+
+	return -1;
+}
+
+#elif defined(USE_GNUTLS)
+
+static int vrycb(gnutls_session_t ssl)
+{
+	int r;
+	int type;
+	unsigned int status;
+	gnutls_datum_t msg;
+
+	if((r=gnutls_certificate_verify_peers3(ssl,NULL,&status))<0)
+	{
+		fprintf(stderr,"gnutls_certificate_verify_peers3: %s\n",
+			gnutls_strerror(r));
+		return GNUTLS_E_CERTIFICATE_ERROR;
+	}
+
+	if(status)
+	{
+		type=gnutls_certificate_type_get(ssl);
+		if((r=gnutls_certificate_verification_status_print(status,type,
+			&msg,0))<0)
+		{
+			fprintf(stderr,"gnutls_certificate_verification_"
+				"status_print %s\n",gnutls_strerror(r));
+		}
+		else
+		{
+			fprintf(stderr,"certificate status: %s\n",msg.data);
+			gnutls_free(msg.data);
+		}
+		return GNUTLS_E_CERTIFICATE_ERROR;
+	}
+
+	return 0;
+}
+
+struct ctx *sslinit(int fd,char *cacert)
+{
+	int r;
+	const char *e;
+	struct ctx *ctx;
+
+	if(!(ctx=newctx(fd)))return NULL;
+
+	if(!cacert)return ctx;
+
+	if((r=gnutls_global_init()))
+	{
+		fprintf(stderr,"gnutls_global_init: %s\n",gnutls_strerror(r));
+		goto err1;
+	}
+
+	if((r=gnutls_certificate_allocate_credentials(&ctx->cred)))
+	{
+		fprintf(stderr,"gnutls_certificate_allocate_credentials: "
+			"%s\n",gnutls_strerror(r));
+		goto err2;
+	}
+
+	if((r=gnutls_certificate_set_x509_trust_file(ctx->cred,cacert,
+		GNUTLS_X509_FMT_PEM))<0)
+	{
+		fprintf(stderr,"gnutls_certificate_set_x509_trust_file: "
+			"%s\n",gnutls_strerror(r));
+		goto err3;
+	}
+
+	gnutls_certificate_set_verify_function(ctx->cred,vrycb);
+
+	if((r=gnutls_init(&ctx->ssl,GNUTLS_CLIENT)))
+	{
+		fprintf(stderr,"gnutls_init: %s\n",gnutls_strerror(r));
+		goto err3;
+	}
+
+	/* oh well, isn't _that_ easy ?!? :-(  ... compare to openssl ... */
+	if((r=gnutls_priority_set_direct(ctx->ssl,"NONE:+AES-256-CBC:"
+		"+AES-128-CBC:+3DES-CBC:+COMP-NULL:+CTYPE-X509:+VERS-SSL3.0:"
+		"+SHA256:+SHA1:+RSA:%UNSAFE_RENEGOTIATION",&e)))
+	{
+		fprintf(stderr,"gnutls_priority_set_direct: %s\n",
+			gnutls_strerror(r));
+		if(r==GNUTLS_E_INVALID_REQUEST)
+			fprintf(stderr,"additional info: %s\n",e);
+		goto err4;
+	}
+
+	if((r=gnutls_credentials_set(ctx->ssl,GNUTLS_CRD_CERTIFICATE,
+		ctx->cred)))
+	{
+		fprintf(stderr,"gnutls_credentials_set: %s\n",
+			gnutls_strerror(r));
+		goto err4;
+	}
+
+	gnutls_transport_set_int(ctx->ssl,ctx->fd);
+
+	gnutls_handshake_set_timeout(ctx->ssl,GNUTLS_DEFAULT_HANDSHAKE_TIMEOUT);
+
+	do
+	{
+		r=gnutls_handshake(ctx->ssl);
+	} while(r<0&&!gnutls_error_is_fatal(r));
+	if(r<0)
+	{
+		fprintf(stderr,"gnutls_handshake: %s\n",gnutls_strerror(r));
+		goto err4;
+	}
+
+	return ctx;
+
+err4:	gnutls_deinit(ctx->ssl);
+err3:	gnutls_certificate_free_credentials(ctx->cred);
+err2:	gnutls_global_deinit();
+err1:	free(ctx);
+	return NULL;
+}
+
+void sslexit(struct ctx *ctx)
+{
+	if(ctx->ssl)
+	{
+		gnutls_deinit(ctx->ssl);
+		gnutls_certificate_free_credentials(ctx->cred);
+		gnutls_global_deinit();
+	}
+	free(ctx);
+}
+
+int sslready(struct ctx *ctx)
+{
+	if(ctx->ssl)return gnutls_record_check_pending(ctx->ssl);
+	else return 0;
+}
+
+ssize_t sslread(struct ctx *ctx,void *buf,size_t count)
+{
+	ssize_t l;
+	int c=0;
+	int r;
+
+	if(!ctx->ssl)return read(ctx->fd,buf,count);
+	if(!count)return 0;
+
+repeat:	if((l=gnutls_record_recv(ctx->ssl,buf,count))>0)return l;
+
+	switch(l)
+	{
+	case GNUTLS_E_REHANDSHAKE:
+		do
+		{
+			r=gnutls_handshake(ctx->ssl);
+		} while(r<0&&!gnutls_error_is_fatal(r));
+		if(r<0)
+		{
+			fprintf(stderr,"gnutls_handshake: %s\n",
+			gnutls_strerror(r));
+			return -1;
+		}
+	case GNUTLS_E_INTERRUPTED:
+	case GNUTLS_E_AGAIN:
+		if(++c<100)
+		{
+			usleep(10000);
+			goto repeat;
+		}
+	default:fprintf(stderr,"gnutls_record_recv: %s\n",gnutls_strerror(l));
+	case GNUTLS_E_PUSH_ERROR:
+	case GNUTLS_E_PULL_ERROR:
+		return -1;
+	}
+}
+
+ssize_t sslwrite(struct ctx *ctx,const void *buf,size_t count)
+{
+	ssize_t l;
+	int c=0;
+	int r;
+
+	if(!ctx->ssl)return write(ctx->fd,buf,count);
+	if(!count)return 0;
+
+repeat:	if((l=gnutls_record_send(ctx->ssl,buf,count))>0)return l;
+
+	switch(l)
+	{
+	case GNUTLS_E_REHANDSHAKE:
+		do
+		{
+			r=gnutls_handshake(ctx->ssl);
+		} while(r<0&&!gnutls_error_is_fatal(r));
+		if(r<0)
+		{
+			fprintf(stderr,"gnutls_handshake: %s\n",
+			gnutls_strerror(r));
+			return -1;
+		}
+	case GNUTLS_E_INTERRUPTED:
+	case GNUTLS_E_AGAIN:
+		if(++c<100)
+		{
+			usleep(10000);
+			goto repeat;
+		}
+	default:fprintf(stderr,"gnutls_record_send: %s\n",gnutls_strerror(l));
+	case GNUTLS_E_PUSH_ERROR:
+	case GNUTLS_E_PULL_ERROR:
+		return -1;
+	}
+}
+
+#else
+
+struct ctx *sslinit(int fd,char *cacert)
+{
+	return newctx(fd);
+}
+
+void sslexit(struct ctx *ctx)
+{
+	free(ctx);
+}
+
+int sslready(struct ctx *ctx)
+{
+	return 0;
+}
+
+ssize_t sslread(struct ctx *ctx,void *buf,size_t count)
+{
+	return read(ctx->fd,buf,count);
+}
+
+ssize_t sslwrite(struct ctx *ctx,const void *buf,size_t count)
+{
+	return write(ctx->fd,buf,count);
+}
+
+#endif
