411 REFERENCE · Wikibooks
Aros/Developer/AmiSSL
Reference material by Wikibooks contributors. This retains its source identity and is not 411 news reporting.
Introduction
Work started on AmiSSL v5 aka port of OpenSSL 3.6 which contains developer files and example programs which should make it possible to add AmiSSL support to client and server applications interested in providing SSL/TLS functionality to end users. This SDK release provides include files for GCC and vbcc for OS3/m68k and OS4/PPC. Also, SAS/C is supported, but please see Issues for important information.
The information given here is mostly AmiSSL specific and does not cover OpenSSL
For more information on OpenSSL programming, check the OpenSSL documentation and examples
The AmiSSL v5 API is slightly different from previous versions and has been simplified like the below pseudo-code
#include <proto/amisslmaster.h>
#include <proto/amissl.h>
#include <libraries/amisslmaster.h>
#include <libraries/amissl.h>
#include <amissl/amissl.h>
if((AmiSSLMasterBase = OpenLibrary("amisslmaster.library",
AMISSLMASTER_MIN_VERSION))
{
#if defined(__amigaos4__)
if(IAmiSSLMaster = (struct AmiSSLMasterIFace *)
GetInterface((struct Library *)AmiSSLMasterBase,
"main", 1, NULL))
{
if(OpenAmiSSLTags(AMISSL_CURRENT_VERSION,
AmiSSL_UsesOpenSSLStructs, TRUE or FALSE,
AmiSSL_GetIAmiSSL, &IAmiSSL,
AmiSSL_ISocket, ISocket,
AmiSSL_ErrNoPtr, &errno,
..., TAG_DONE) == 0)
{
CloseAmiSSL();
}
else printf("ERROR: Couldn't open AmiSSL!");
DropInterface((struct Interface *)IAmiSSLMaster);
}
else printf("ERROR: Couldn't obtain AmiSSLMaster interface!");
#else
if(OpenAmiSSLTags(AMISSL_CURRENT_VERSION,
AmiSSL_UsesOpenSSLStructs, TRUE or FALSE,
AmiSSL_GetAmiSSLBase, &AmiSSLBase,
AmiSSL_GetAmiSSLExtBase, &AmiSSLExtBase,
AmiSSL_SocketBase, SocketBase,
AmiSSL_ErrNoPtr, &errno,
..., TAG_DONE) == 0)
{
CloseAmiSSL();
}
else printf("ERROR: Couldn't open AmiSSL!");
#endif
CloseLibrary(AmiSSLMasterBase);
}
else printf("ERROR: Couldn't open amisslmaster.library!");
The first step is to open amisslmaster.library requesting at least AMISSLMASTER_MIN_VERSION version (and, for OS4, call GetInterface() to get the
"main" interface and later DropInterface() in the cleanup phase).
Then amisslmaster.library/OpenAmiSSLTags() has to be called specifying which API version the program was compiled for and whether it uses OpenSSL structures
in some way (ie. if it accesses some of the fields directly and not through AmiSSL API or if it includes OpenSSL structures in its own structures - if
unsure, specify TRUE). It will return zero on success or a non-zero value on failure. See the autodocs for amisslmaster.library/OpenAmiSSLTags() and
amissl.library/InitAmiSSL() for currently available tags.
If you need to compile a program specifically for f. ex. OpenSSL 0.9.7g API, you need to use the SDK in which AMISSL_CURRENT_VERSION is AMISSL_V097g. If
you are, however, always using the latest AmiSSL SDK version you can keep using AMISSL_CURRENT_VERSION.
The use of amisslmaster.library makes it possible to have multiple versions of amissl.library with incompatible APIs installed keeping the programs compiled
with an older amissl.library working. This is necessary since OpenSSL API changes from time to time. Also, when a new version of AmiSSL is released with
amissl.library API that is backwards compatible to some previous version, a program requesting the old version of amissl.library will automatically get
the new version and the old version can be deleted.
3. Subprocesses
Note, that unlike in AmiSSL v1, AmiSSLBase can be shared between different subprocesses. Furthermore, this is encouraged since it allows the cache of
server certificates to be used by all subprocesses that share the same AmiSSL base resulting in speed increase. Each subprocess must call InitAmiSSL() with
appropriate arguments before using any other amissl.library calls and CleanupAmiSSL() before it exits. AmiSSL then ensures due to its baserel-based
environment that any opener receives an own baserel based library base.
Examples
The provided example, httpget.c, is an example of how to use the new OpenSSL 3.x built-in HTTP client functions. It shows how to retrieve a
document from a "http://" or "https://" server, with or without a password and how you can also use the URL parsing functions from OpenSSL 3.x.
/***************************************************************************
AmiSSL - OpenSSL wrapper for AmigaOS-based systems
Copyright (c) 1999-2006 Andrija Antonijevic, Stefan Burstroem.
Copyright (c) 2006-2022 AmiSSL Open Source Team.
All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License in the file LICENSE in the
source distribution or at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
AmiSSL Official Support Site: https://github.com/jens-maus/amissl
***************************************************************************/
/*
* This example program demonstrates how to use the HTTP client function
* OSSL_HTTP_get() and URL parsing function OSSL_HTTP_parse_url(), which
* were newly introduced in OpenSSL 3.0 and AmiSSL v5.
*
* These allow any application to load http or https URLs completely
* within AmiSSL v5, without the need for any other external libraries,
* such as libcurl, or custom code. OSSL_HTTP_get() is the highest level
* function provided by the HTTP client, but you can have more control by
* using the lower level HTTP functions described in the OpenSSL manual at
* https://www.openssl.org/docs/man3.0/man3/OSSL_HTTP_get.html
*/
#if defined(__amigaos4__) && !defined(__USE_INLINE__)
# define __USE_INLINE__
#endif
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <errno.h>
#include <proto/exec.h>
#include <proto/dos.h>
#include <proto/amissl.h>
#include <proto/amisslmaster.h>
#include <amissl/amissl.h>
#include <libraries/amisslmaster.h>
#include <libraries/amissl.h>
#if !defined(__amigaos4__)
# include <SDI_compiler.h>
#endif
const char stack_size[] = "$STACK:8192";
static SSL_CTX *Init(void);
static void Cleanup(SSL_CTX *ctx);
/* Check if URL is valid and extract any username/password
*/
BOOL IsSupportedURL(char *url, char **userinfo)
{
int ssl, portnum;
char *host, *port, *path, *query, *fragment;
/* You may wish to make use of OSSL_parse_url() which can be
* used to parse non-http(s) URLs too
*/
return OSSL_HTTP_parse_url(url, &ssl, userinfo, &host, &port,
&portnum, &path, &query, &fragment);
}
/* Required callback to enable HTTPS connection, when necessary
*/
SAVEDS STDARGS BIO *HTTP_TLS_cb(BIO *bio, void *arg, int connect, int detail)
{
if (connect && detail)
{
/* Connect with TLS */
BIO *sbio = BIO_new_ssl((SSL_CTX *)arg, 1);
bio = (sbio != NULL) ? BIO_push(sbio, bio) : NULL;
}
return bio;
}
/* Stub function used when freeing our stack of headers
*/
SAVEDS STDARGS void stub_X509V3_conf_free(CONF_VALUE *val)
{
X509V3_conf_free(val);
}
/* Read content from URL and dump to Output()
*/
BOOL GetURL(char *url, char *userinfo, SSL_CTX *sslctx)
{
STACK_OF(CONF_VALUE) *headers = NULL;
BIO *bio, *bio_err;
/* Add our own HTTP headers */
X509V3_add_value("User-Agent", "AmiSSL/5.1", &headers);
X509V3_add_value("Referer", "GetURL()", &headers);
if (userinfo)
{
/* Optionally specify username:password */
char auth[256] = "Basic ";
/* Base64 encode */
EVP_EncodeBlock((unsigned char *)&auth[6], (unsigned char *)userinfo, strlen(userinfo));
X509V3_add_value("Authorization", auth, &headers);
}
/* Initiate the request (supports redirection) */
if ((bio = OSSL_HTTP_get(url, NULL /* proxy */, NULL /* no_proxy */,
NULL /* bio */, NULL /* rbio */,
(BIO *(*)(BIO *, void *, int, int))HTTP_TLS_cb, sslctx,
0 /* buf_size */, headers,
NULL /* expected_content_type */, 0 /* expect_asn1 */,
0 /* max_resp_len */, 0 /* timeout */)) != NULL)
{
/* HTTP request succeeded */
char buffer[2048]; /* should really move this off the stack */
int length;
while ((length = BIO_read(bio, buffer, sizeof(buffer))) > 0)
{
FWrite(Output(), buffer, 1, length);
}
BIO_free(bio);
}
else if((bio_err = BIO_new(BIO_s_file())) != NULL)
{
/* HTTP request failed - try to show why */
BIO_set_fp_amiga(bio_err, Output(), BIO_NOCLOSE | BIO_FP_TEXT);
ERR_print_errors(bio_err);
BIO_free(bio_err);
}
/* Free our custom headers */
sk_CONF_VALUE_pop_free(headers,(void (*)(CONF_VALUE *))stub_X509V3_conf_free);
return (bio != NULL);
}
int main(int argc, char *argv[])
{
BOOL is_ok = FALSE;
SSL_CTX *sslctx = NULL;
char *userinfo = NULL;
if (argc > 1)
{
/* Open required OS libraries and initialize AmiSSL */
if ((sslctx = Init()) != NULL)
{
/* First check if URL is valid (optional) */
if (IsSupportedURL(argv[1], &userinfo))
{
/* Open the URL and dump the response to Output() */
is_ok = GetURL(argv[1], userinfo, sslctx);
}
else
Printf("Not a valid http or https URL\n");
}
/* Free resources */
Cleanup(sslctx);
}
else
Printf("Usage: %s [url] (http:// and https:// supported)\n", argv[0]);
return(is_ok ? RETURN_OK : RETURN_ERROR);
}
#if defined(USE_AUTOINIT)
/* Link with -lamisslauto
*/
LONG UsesOpenSSLStructs = FALSE;
static SSL_CTX *Init(void)
{
SSL_CTX *ctx;
if (!(ctx = SSL_CTX_new(TLS_client_method())))
Printf("Couldn't create SSL context!\n");
return ctx;
}
static void Cleanup(SSL_CTX *ctx)
{
SSL_CTX_free(ctx);
}
#else
struct Library *AmiSSLMasterBase, *SocketBase;
# if defined(__amigaos4__)
struct AmiSSLMasterIFace *IAmiSSLMaster;
struct AmiSSLIFace *IAmiSSL;
struct SocketIFace *ISocket;
# define GETINTERFACE(iface, base) (iface = (APTR)GetInterface((struct Library *)(base), "main", 1L, NULL))
# define DROPINTERFACE(iface) (DropInterface((struct Interface *)iface), iface = NULL)
# else
struct Library *AmiSSLBase, *AmiSSLExtBase;
# define GETINTERFACE(iface, base) TRUE
# define DROPINTERFACE(iface)
# endif
# define XMKSTR(x) #x
# define MKSTR(x) XMKSTR(x)
/* Open and initialize AmiSSL
*/
static SSL_CTX *Init(void)
{
SSL_CTX *ctx = NULL;
if (!(SocketBase = OpenLibrary("bsdsocket.library", 4)))
Printf("Couldn't open bsdsocket.library v4!\n");
else if (!GETINTERFACE(ISocket, SocketBase))
Printf("Couldn't get Socket interface!\n");
else if (!(AmiSSLMasterBase = OpenLibrary("amisslmaster.library",
AMISSLMASTER_MIN_VERSION)))
Printf("Couldn't open amisslmaster.library v"
MKSTR(AMISSLMASTER_MIN_VERSION) "!\n");
else if (!GETINTERFACE(IAmiSSLMaster, AmiSSLMasterBase))
Printf("Couldn't get AmiSSLMaster interface!\n");
# if defined(__amigaos4__)
else if (OpenAmiSSLTags(AMISSL_CURRENT_VERSION,
AmiSSL_UsesOpenSSLStructs, FALSE,
AmiSSL_GetIAmiSSL, &IAmiSSL,
AmiSSL_ISocket, ISocket,
AmiSSL_ErrNoPtr, &errno,
TAG_DONE) != 0)
# else
else if (OpenAmiSSLTags(AMISSL_CURRENT_VERSION,
AmiSSL_UsesOpenSSLStructs, FALSE,
AmiSSL_GetAmiSSLBase, &AmiSSLBase,
AmiSSL_GetAmiSSLExtBase, &AmiSSLExtBase,
AmiSSL_SocketBase, SocketBase,
AmiSSL_ErrNoPtr, &errno,
TAG_DONE) != 0)
# endif
Printf("Couldn't open and initialize AmiSSL!\n");
else if (!(ctx = SSL_CTX_new(TLS_client_method())))
Printf("Couldn't create SSL context!\n");
return ctx;
}
/* Close AmiSSL
*/
static void Cleanup(SSL_CTX *ctx)
{
# if defined(__amigaos4__)
if (IAmiSSL)
# else
if (AmiSSLBase)
# endif
{
SSL_CTX_free(ctx);
CloseAmiSSL();
}
DROPINTERFACE(IAmiSSLMaster);
CloseLibrary(AmiSSLMasterBase);
DROPINTERFACE(ISocket);
CloseLibrary(SocketBase);
}
#endif /* !USE_AUTOINIT */
Another example, https.c, is a very simple client. It shows how to retrieve a document from a "https://" server, at a lower level, by connecting directly
to the site, and also by connecting through a proxy server (SSL tunneling).
/***************************************************************************
AmiSSL - OpenSSL wrapper for AmigaOS-based systems
Copyright (c) 1999-2006 Andrija Antonijevic, Stefan Burstroem.
Copyright (c) 2006-2022 AmiSSL Open Source Team.
All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License in the file LICENSE in the
source distribution or at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
AmiSSL Official Support Site: https://github.com/jens-maus/amissl
***************************************************************************/
#if defined(__amigaos4__)
# ifndef __USE_INLINE__
# define __USE_INLINE__
# endif
# if defined(__VBCC__) && !defined(__USE_OLD_TIMEVAL__)
# define __USE_OLD_TIMEVAL__
# endif
#endif
#include <sys/socket.h>
#include <netdb.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <errno.h>
#include <proto/exec.h>
#include <proto/dos.h>
#define __NOLIBBASE__
#include <proto/utility.h>
#undef __NOLIBBASE__
#include <proto/amissl.h>
#include <proto/amisslmaster.h>
#include <proto/socket.h>
#include <clib/alib_protos.h>
#include <utility/utility.h>
#include <amissl/amissl.h>
#include <libraries/amisslmaster.h>
#include <libraries/amissl.h>
const char stack_size[] = "$STACK:8192";
static BOOL Init(void);
static void Cleanup(void);
static void GenerateRandomSeed(char *buffer, int size);
static int ConnectToServer(char *, short, char *, short);
static int verify_cb(int preverify_ok, X509_STORE_CTX *ctx);
struct Library *AmiSSLMasterBase, *AmiSSLBase, *SocketBase, *UtilityBase;
BOOL AmiSSLInitialized;
#if defined(__amigaos4__)
struct AmiSSLMasterIFace *IAmiSSLMaster;
struct AmiSSLIFace *IAmiSSL;
struct SocketIFace *ISocket;
struct UtilityIFace *IUtility;
#else
static BPTR ErrorOutput(void)
{
return(((struct Process *)FindTask(NULL))->pr_CES);
}
#define FFlush(x) Flush(x)
#endif /* !__amigaos4__ */
static BPTR GetStdErr(void)
{
BPTR err = ErrorOutput();
return(err ? err : Output());
}
/* Usage: https <host> <port> [proxyhost] [proxyport]
*
* host: name of host (default: "localhost")
* port: port to connect to (default: 443)
* proxyhost: name of proxy (optional)
* proxyport: name of proxy (optional)
*
* If any proxy parameter is omitted, the program will
* connect directly to the host.
*/
int main(int argc, char *argv[])
{
char buffer[4096]; /* This should be dynamically allocated */
BOOL is_ok = FALSE;
X509 *server_cert;
SSL_CTX *ctx;
BIO *bio_err;
SSL *ssl;
if (Init()) /* Open required OS libraries and initialize AmiSSL */
{
/* Basic intialization. Next few steps (up to SSL_new()) need
* to be done only once per AmiSSL opener.
*/
OPENSSL_init_ssl(OPENSSL_INIT_SSL_DEFAULT | OPENSSL_INIT_ADD_ALL_CIPHERS | OPENSSL_INIT_ADD_ALL_DIGESTS, NULL);
/* Seed the entropy engine */
GenerateRandomSeed(buffer, 128);
RAND_seed(buffer, 128);
/* Note: BIO writing routines are prepared for NULL BIO handle */
if((bio_err = BIO_new(BIO_s_file())) != NULL)
BIO_set_fp_amiga(bio_err, GetStdErr(), BIO_NOCLOSE | BIO_FP_TEXT);
/* Get a new SSL context */
if((ctx = SSL_CTX_new(TLS_client_method())) != NULL)
{
/* Basic certificate handling. OpenSSL documentation has more
* information on this.
*/
SSL_CTX_set_default_verify_paths(ctx);
SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT,
verify_cb);
/* The following needs to be done once per socket */
if((ssl = SSL_new(ctx)) != NULL)
{
int sock;
int port, pport;
char *host, *proxy;
/* Connect to the HTTPS server, directly or through a proxy */
host = (argc > 1) ? argv[1] : (char *)"localhost";
port = (argc > 2) ? atol(argv[2]) : 443;
proxy = (argc > 3) ? argv[3] : NULL;
pport = (argc > 4) ? atol(argv[4]) : 0;
sock = ConnectToServer(host, port, proxy, pport);
/* Check if connection was established */
if (sock >= 0)
{
int ssl_err = 0;
/* Associate the socket with the ssl structure */
SSL_set_fd(ssl, sock);
/* Set up SNI (Server Name Indication) */
SSL_set_tlsext_host_name(ssl, host);
/* Perform SSL handshake */
if((ssl_err = SSL_connect(ssl)) >= 0)
{
Printf("SSL connection using %s\n", SSL_get_cipher(ssl));
/* Certificate checking. This example is *very* basic */
if((server_cert = SSL_get_peer_certificate(ssl)))
{
char *str;
Printf("Server certificate:\n");
if((str = X509_NAME_oneline(X509_get_subject_name(server_cert), 0, 0)))
{
Printf("\tSubject: %s\n", str);
OPENSSL_free(str);
}
else
FPrintf(GetStdErr(), "Warning: couldn't read subject name in certificate!\n");
if((str = X509_NAME_oneline(X509_get_issuer_name(server_cert),
0, 0)) != NULL)
{
Printf("\tIssuer: %s\n", str);
OPENSSL_free(str);
}
else
FPrintf(GetStdErr(), "Warning: couldn't read issuer name in certificate!\n");
X509_free(server_cert);
/* Send a HTTP request. Again, this is just
* a very basic example.
*/
sprintf(buffer,"GET / HTTP/1.0\r\nHost: %s\r\n\r\n",host);
if ((ssl_err = SSL_write(ssl, buffer, strlen(buffer)))
> 0)
{
/* Dump everything to output */
while ((ssl_err = SSL_read(ssl, buffer,
sizeof(buffer) - 1))
> 0)
FWrite(Output(), buffer, ssl_err, 1);
FFlush(Output());
/* This is not entirely true, check
* the SSL_read documentation
*/
is_ok = ssl_err == 0;
}
else
FPrintf(GetStdErr(), "Couldn't write request!\n");
}
else
FPrintf(GetStdErr(), "Couldn't get server certificate!\n");
/* Send SSL close notification */
SSL_shutdown(ssl);
}
else
FPrintf(GetStdErr(), "Couldn't establish SSL connection!\n");
/* If there were errors, print them */
if (ssl_err < 0)
ERR_print_errors(bio_err);
/* Close the socket */
CloseSocket(sock);
}
else
FPrintf(GetStdErr(), "Couldn't connect to host!\n");
FPrintf(GetStdErr(), "before SSL_free()\n");
SSL_free(ssl);
}
else
FPrintf(GetStdErr(), "Couldn't create new SSL handle!\n");
FPrintf(GetStdErr(), "before SSL_CTX_free()\n");
SSL_CTX_free(ctx);
}
else
FPrintf(GetStdErr(), "Couldn't create new context!\n");
BIO_free(bio_err);
FPrintf(GetStdErr(), "before Cleanup()\n");
Cleanup();
}
FPrintf(GetStdErr(), "before end of main()\n");
return(is_ok ? RETURN_OK : RETURN_ERROR);
}
#define XMKSTR(x) #x
#define MKSTR(x) XMKSTR(x)
#if defined(__amigaos4__)
#define GETINTERFACE(iface, base) (iface = (APTR)GetInterface((struct Library *)(base), "main", 1L, NULL))
#define DROPINTERFACE(iface) (DropInterface((struct Interface *)iface), iface = NULL)
#else
#define GETINTERFACE(iface, base) TRUE
#define DROPINTERFACE(iface)
#endif
/* Open and initialize AmiSSL */
static BOOL Init(void)
{
AmiSSLInitialized = FALSE;
if (!(UtilityBase = OpenLibrary("utility.library", 0)))
FPrintf(GetStdErr(), "Couldn't open utility.library!\n");
else if (!GETINTERFACE(IUtility, UtilityBase))
FPrintf(GetStdErr(), "Couldn't get Socket interface!\n");
else if (!(SocketBase = OpenLibrary("bsdsocket.library", 4)))
FPrintf(GetStdErr(), "Couldn't open bsdsocket.library v4!\n");
else if (!GETINTERFACE(ISocket, SocketBase))
FPrintf(GetStdErr(), "Couldn't get Socket interface!\n");
else if (!(AmiSSLMasterBase = OpenLibrary("amisslmaster.library",
AMISSLMASTER_MIN_VERSION)))
FPrintf(GetStdErr(), "Couldn't open amisslmaster.library v"
MKSTR(AMISSLMASTER_MIN_VERSION) "!\n");
else if (!GETINTERFACE(IAmiSSLMaster, AmiSSLMasterBase))
FPrintf(GetStdErr(), "Couldn't get AmiSSLMaster interface!\n");
else if (!InitAmiSSLMaster(AMISSL_CURRENT_VERSION, TRUE))
FPrintf(GetStdErr(), "AmiSSL version is too old!\n");
else if (!(AmiSSLBase = OpenAmiSSL()))
FPrintf(GetStdErr(), "Couldn't open AmiSSL!\n");
else if (!GETINTERFACE(IAmiSSL, AmiSSLBase))
FPrintf(GetStdErr(), "Couldn't get AmiSSL interface!\n");
#if defined(__amigaos4__)
else if (InitAmiSSL(AmiSSL_ErrNoPtr, &errno,
AmiSSL_ISocket, ISocket,
TAG_DONE) != 0)
#else
else if (InitAmiSSL(AmiSSL_ErrNoPtr, &errno,
AmiSSL_SocketBase, SocketBase,
TAG_DONE) != 0)
#endif
FPrintf(GetStdErr(), "Couldn't initialize AmiSSL!\n");
else
AmiSSLInitialized = TRUE;
if (!AmiSSLInitialized)
Cleanup(); /* This is safe to call even if something failed above */
return(AmiSSLInitialized);
}
static void Cleanup(void)
{
if (AmiSSLInitialized)
{ /* Must always call after successful InitAmiSSL() */
CleanupAmiSSLA(NULL);
}
if (AmiSSLBase)
{
DROPINTERFACE(IAmiSSL);
CloseAmiSSL();
AmiSSLBase = NULL;
}
DROPINTERFACE(IAmiSSLMaster);
CloseLibrary(AmiSSLMasterBase);
AmiSSLMasterBase = NULL;
DROPINTERFACE(ISocket);
CloseLibrary(SocketBase);
SocketBase = NULL;
DROPINTERFACE(IUtility);
CloseLibrary(UtilityBase);
UtilityBase = NULL;
}
/* Get some suitable random seed data
*/
static void GenerateRandomSeed(char *buffer, int size)
{
int i;
#ifdef __amigaos4__
struct RandomState rs;
rs.rs_Low = clock();
rs.rs_High = time(NULL);
for(i = 0; i < size/4; i++)
{
((LONG *)buffer)[i] = Random(&rs);
}
#else
for(i = 0; i < size/2; i++)
{
((UWORD *)buffer)[i] = RangeRand(65535);
}
#endif
}
/* This callback is called everytime OpenSSL verifies a certificate
* in the chain during a connection, indicating success or failure.
*/
static int verify_cb(int preverify_ok, X509_STORE_CTX *ctx)
{
if (!preverify_ok)
{
/* Here, you could ask the user whether to ignore the failure,
* displaying information from the certificate, for example.
*/
FPrintf(GetStdErr(),"Certificate verification failed (%s)\n",
X509_verify_cert_error_string(X509_STORE_CTX_get_error(ctx)));
}
else
{
FPrintf(GetStdErr(),"Certificate verification successful (hash %08lx)\n",
X509_issuer_and_serial_hash(X509_STORE_CTX_get_current_cert(ctx)));
}
return preverify_ok;
}
/* Connect to the specified server, either directly or through the specified
* proxy using HTTP CONNECT method.
*/
static int ConnectToServer(char *host, short port, char *proxy, short pport)
{
struct sockaddr_in addr;
struct hostent *hostent;
char buffer[1024]; /* This should be dynamically alocated */
BOOL is_ok = FALSE;
char *s1, *s2;
int sock = -1;
/* Lookup hostname */
if ((hostent = gethostbyname((proxy && pport) ? proxy : host)) != NULL)
{
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_port = htons((proxy && pport) ? pport : port);
addr.sin_len = hostent->h_length;;
memcpy(&addr.sin_addr,hostent->h_addr,hostent->h_length);
}
else
FPrintf(GetStdErr(), "Host lookup failed\n");
/* Create a socket and connect to the server */
if (hostent && ((sock = socket(AF_INET, SOCK_STREAM, 0)) >= 0))
{
if (connect(sock, (struct sockaddr *)&addr, sizeof(addr)) >= 0)
{
/* For proxy connection, use SSL tunneling. First issue a HTTP CONNECT
* request and then proceed as with direct HTTPS connection.
*/
if (proxy && pport)
{
/* This should be done with snprintf to prevent buffer
* overflows, but some compilers don't have it and
* handling that would be an overkill for this example
*/
sprintf(buffer, "CONNECT %s:%ld HTTP/1.0\r\n\r\n",
host, (long)port);
/* In a real application, it would be necessary to loop
* until everything is sent or an error occurs, but here we
* hope that everything gets sent at once.
*/
if (send(sock, buffer, strlen(buffer), 0) >= 0)
{
int len;
/* Again, some optimistic behaviour: HTTP response might not be
* received with only one recv
*/
if ((len = recv(sock, buffer, sizeof(buffer) - 1, 0)) >= 0)
{
/* Assuming it was received, find the end of
* the line and cut it off
*/
if ((s1 = strchr(buffer, '\r'))
|| (s1 = strchr(buffer, '\n')))
*s1 = '\0';
else
buffer[len] = '\0';
Printf("Proxy returned: %s\n", buffer);
/* Check if HTTP response makes sense */
if (strncmp(buffer, "HTTP/", 4) == 0
&& (s1 = strchr(buffer, ' '))
&& (s2 = strchr(++s1, ' '))
&& (s2 - s1 == 3))
{
/* Only accept HTTP 200 OK response */
if (atol(s1) == 200)
is_ok = TRUE;
else
FPrintf(GetStdErr(), "Proxy responce indicates error!\n");
}
else
FPrintf(GetStdErr(), "Amibigous proxy responce!\n");
}
else
FPrintf(GetStdErr(), "Couldn't get proxy response!\n");
}
else
FPrintf(GetStdErr(), "Couldn't send request to proxy!\n");
}
else
is_ok = TRUE;
}
else
FPrintf(GetStdErr(), "Couldn't connect to server\n");
if (!is_ok)
{
CloseSocket(sock);
sock = -1;
}
}
return(sock);
}
Reference
5. Link libraries
The following optional link libraries are supplied, for GCC on OS3/OS4 and vbcc on OS4:
- libamisslauto.a (link with -lamisslauto)
As per the standard libauto, automatically open and set up AmiSSL, without
having to worry about the initialisation code yourself. You may also compile
your own link library for a different compiler from lib/autoinit_amissl_main.c
which can be compiled with VBCC, SAS/C and GCC.
- libamisslstubs.a (link with -lamisslstubs)
Some OpenSSL functions take a pointer to another OpenSSL function as a
parameter. This becomes a problem when using standard AmigaOS library inlines,
usually causing undefined reference errors (e.g. X509_free) when linking. You
can either create your own stub functions to resolve this, which simply call
the AmiSSL functions, but for convenience you can instead link with this
library which contains stubs for all OpenSSL functions. If you wish to create
this library for a different compiler, the stubs source code can be created
on OS4 using "idltool -L amissl.xml" or on OS3 from the sfd file using tools
such as sfdc, sfd or fd2pragma.
6. Migration from AmiSSL v4
AmiSSL v4 is OpenSSL 1.1.x based, whilst AmiSSL v5 uses OpenSSL 3.x, but
fortunately you will not usually need to make any changes to your code, as most
things are actually backwards compatible (most functions that have been removed
or changed will be replaced by macros in the OpenSSL header files). However, we
encourage developers to use the new OpenAmiSSLTags() function, in place of
InitAmiSSLMaster(), OpenAmiSSL() and InitAmiSSL() (these functions will still
continue to operate as before). Details of OpenSSL changes are available at
https://www.openssl.org/docs/manmaster/man7/migration_guide.html and with
GCC you will receive warnings if you use any deprecated functions.
Due to the shear amount of OpenSSL 3.x API functions available, we had to split
them across two library bases on OS3 which meant the addition of AmiSSLExtBase.
AmiSSLExtBase can be obtained using the AmiSSL_GetAmiSSLExtBase tag in
OpenAmiSSLTags() (or alternatively, in InitAmiSSL() if you wish to keep using
the old functions). This does not apply to OS4, where there is one single
interface as before.
The procedure for subprocesses remains unchanged - they must continue to call
InitAmiSSL() and CleanupAmiSSL() if sharing the AmiSSL instance from the parent
task where OpenAmiSSLTags() was called.
7. Migration from AmiSSL v3
AmiSSL v3 is OpenSSL 0.9.x based and there were many fundamental changes in
AmiSSL v4 due to the switch to OpenSSL 1.1.x. Although there were no changes in
the AmiSSL API itself, there were lots of important changes in the OpenSSL API.
Rather than simply recompiling, depending on how heavy your OpenSSL usage is,
you may well need to make changes to your code. For example, many public
structures that were defined in the header files have now been made private.
Rather than allocating them yourself, or putting them on the stack, you will
need to use the provided new functions to obtain, access and modify these
structures. We also dropped SSLv2/v3 methods completely, so you will need to
switch to TLS.
8. Differences to OpenSSL
AmiSSL provides essentially the same functionality as OpenSSL, but there are a
few differences as a result of the Amiga shared library model and necessity to
support different compilers. Unlike AmiSSL v1, AmiSSL v3/v4/v5 uses
"#include <openssl/xxx.h>" scheme instead of "#include <amissl/xxx.h>" scheme.
This and some other changes should make it possible to compile most OpenSSL
programs out of the box.
The functions, e.g., BIO_set_fp(), BIO_get_fp() and BIO_new_fp() are not
available since they require a FILE * argument which is C runtime library
specific. However, there are replacement functions which have _amiga added to
their name and take a BPTR instead of FILE * argument. For example,
BIO_set_fp(bio_out, stdout, BIO_NOCLOSE) can be replaced by
BIO_set_fp_amiga(bio_out, Output(), BIO_NOCLOSE);
Also, all other stdio functions that have a FILE * argument are not available.
However, all these functions have an equivalent that accepts BIO * instead of
FILE * so these functions can be used instead. The simple sequence
fp = fopen(file, mode);
openssl_func_fp(fp, ...);
fclose(fp)
sequence can be replaced with the following:
bio = BIO_new_file(file, mode);
openssl_func(bio, ...);
BIO_free(bio);
In addition, AmiSSL adds several functions which deal with OpenSSL ciphers:
const char *SSL_CIPHER_get_mac(SSL_CIPHER *cipher);
const char *SSL_CIPHER_get_encryption(SSL_CIPHER *cipher);
const char *SSL_CIPHER_get_authentication(SSL_CIPHER *cipher);
const char *SSL_CIPHER_get_key_exchange(SSL_CIPHER *cipher);
const char *SSL_CIPHER_get_export(SSL_CIPHER *cipher);
These will return textual representation of requested cipher data. For example,
SSL_CIPHER_get_encryption() will return "AES(256)" when 256-bit AES cipher is
used.
9. Callback functions
Some OpenSSL functions require you to pass your own callback function in as a
parameter. These functions will be called using default calling conventions
on OS3 and OS4. On OS3 this means AmiSSL will call your function with the
function arguments placed on the stack, whilst on OS4 they will be placed in
registers, as usual. If you have set your compiler to use a different
convention, be sure to define your callback function correctly. For example:
STDARGS SAVEDS int verify_cb(int preverify_ok, X509_STORE_CTX *ctx)
{
return preverify_ok;
}
SSL_CTX_set_verify(sslctx,SSL_VERIFY_PEER,verify_cb);
10. Stub functions
Sometimes it is necessary to pass an AmiSSL function as a function pointer
argument. Since AmiSSL functions are inside the library, it is not possible
directly. See the section about the libamisslstubs.a link library which aims to
solve this issue. However, if not using the link library, it is possible to
write your own stub function:
void NOSTACKCHECK /* STDARGS */ SAVEDS (ASN1_OBJECT_free)(ASN1_OBJECT *a)
{
ASN1_OBJECT_free(a);
}
In newly written code it is better to use a different name for the function,
but when working with already existing code it might be easier to use the same
name than to change many occurrences of f. ex. ASN1_OBJECT_free to f. ex.
ASN1_OBJECT_free_stub whenever ASN1_OBJECT_free is passed as a function
pointer.
Issues
11. VBCC issues
The OpenSSL header files rely on using and declaring static inline helper
functions. This means that lots of functions will be declared in every
module that you include the OpenSSL headers and probably most of them you
will not use or need. Most compilers remove these unused functions by
default and sometimes even with the optimiser completely disabled.
Unfortunately, VBCC is different and will only remove these functions if
you use -O3 when compiling. -O1 and -O2 will leave all the unused functions
in your code. If for some reason you do not wish to use -O3, you could try
-O=17375 or -O=17407. These are the equivalent of -O1 and -O2 respectively,
but with the required optimiser bit added to enable removal of the unused
functions. Alternatively, you may compile with -DAMISSL_NO_STATIC_FUNCTIONS,
which will stop all the affected functions from being declared in the first
place, but obviously you can only use this if your code is not using any of
these functions otherwise you will get errors when compiling.
12. SAS/C issues
You will need to add "PreprocessorBuffer=32768 IdentifierLength=80" to your
SC command line and have to take special care where 64-bit integers are
concerned. In particular, OPENSSL_init_ssl(), which you mignt not even be
calling directly, but is ultimately the end result of many of the macros for
older legacy SSL initialisation functions. Remember that for 64-bit
parameters, the high 32-bits are placed in D0 and the low 32-bits in D1. So,
you might want to use a workaround similar to this:
#define OPENSSL_init_ssl_32(opts, settings) \
(putreg(REG_D1,opts), OPENSSL_init_ssl(0, settings))
#ifndef LIBRARIES_AMISSL_H
#define LIBRARIES_AMISSL_H
/***************************************************************************
AmiSSL - OpenSSL wrapper for AmigaOS-based systems
Copyright (c) 1999-2006 Andrija Antonijevic, Stefan Burstroem.
Copyright (c) 2006-2022 AmiSSL Open Source Team.
All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License in the file LICENSE in the
source distribution or at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
AmiSSL Official Support Site: https://github.com/jens-maus/amissl
***************************************************************************/
#define CIPHER_BLOWFISH 0x01
#define CIPHER_CAST 0x02
#define CIPHER_DES 0x03
#define CIPHER_DH 0x04
#define CIPHER_DSA 0x05
#define CIPHER_IDEA 0x06
#define CIPHER_MD2 0x07
#define CIPHER_MD4 0x08
#define CIPHER_MD5 0x09
#define CIPHER_MDC2 0x0a
#define CIPHER_RC2 0x0b
#define CIPHER_RC4 0x0c
#define CIPHER_RC5 0x0d
#define CIPHER_RIPEMD 0x0e
#define CIPHER_RSA 0x0f
#define CIPHER_SHA 0x10
#define CIPHER_AES 0x11
#define CIPHER_BlowFish CIPHER_BLOWFISH
#endif /* !LIBRARIES_AMISSL_H */
TABLE OF CONTENTS
amissl.library/--background--
amissl.library/CleanupAmiSSLA
amissl.library/InitAmiSSLA
amissl.library/SSL_CIPHER_get_authentication
amissl.library/SSL_CIPHER_get_encryption
amissl.library/SSL_CIPHER_get_key_exchange
amissl.library/SSL_CIPHER_get_mac
amissl.library/--background-- amissl.library/--background--
PURPOSE
The amissl.library contains the public OpenSSL API functions. There are
too many to list and document here, but you can find documentation on
the OpenSSL API at https://www.openssl.org/docs/manmaster/man3.
Instead, only the AmiSSL specific functions are documented here.
Since AmiSSL v2, you must not open this library directly. In AmiSSL v5
you should use use amisslmaster.library/OpenAmiSSLTags() instead.
In AmiSSL v2/v3/v4, use amisslmaster.library/OpenAmiSSL() and then call
InitAmiSSL() before you can start using OpenSSL functions. This is
because multiple versions of AmiSSL can be installed at the same time
with the libraries named of the form amissl_v<version>.library.
Since AmiSSL v2, each opener gets their own unique baserel based
AmiSSLBase. This can be shared between different subprocesses.
Furthermore, this is encouraged since it allows the cache of server
certificates to be used by all subprocesses that share the same
AmiSSLBase resulting in speed increase. Each subprocess MUST call
InitAmiSSL() with appropriate arguments before using any other
amissl.library calls and CleanupAmiSSL() before it exits.
amissl.library/CleanupAmiSSLA amissl.library/CleanupAmiSSLA
NAME
CleanupAmiSSLA -- cleanup the AmiSSL library (V1)
CleanupAmiSSL -- varargs stub for CleanupAmiSSLA() (V1)
SYNOPSIS
error = CleanupAmiSSLA( tagList )
LONG CleanupAmiSSLA( struct TagItem * )
error = CleanupAmiSSL( tag, ... )
LONG CleanupAmiSSL( Tag, ... );
FUNCTION
If using amisslmaster.library/OpenSSLTags() and the AmiSSL_InitAmiSSL
tag to initialise AmiSSL, you must not use this function,
as amisslmaster.library/CloseAmiSSL() will call it for you.
However, for each process using AmiSSL, where you made a successful
InitAmiSSLA() call, you must pair it with a call to this function
to cleanup and process related resources. Failure to do so can cause
AmiSSL to crash. Once this function has been called, you must not use
any OpenSSL functions.
INPUTS
tagList - pointer to an array of tags providing optional extra
parameters, or NULL.
TAGS
RESULTS
error - returns 0 on success or non-zero on failure.
SEE ALSO
InitAmiSSLA()
amissl.library/InitAmiSSLA amissl.library/InitAmiSSLA
NAME
InitAmiSSLA -- initialise the AmiSSL library (V1)
InitAmiSSL -- varargs stub for InitAmiSSLA() (V1)
SYNOPSIS
error = InitAmiSSLA( tagList )
LONG InitAmiSSLA( struct TagItem * )
error = InitAmiSSL( tag, ... )
LONG InitAmiSSL( Tag, ... );
FUNCTION
Usually, you will be using amisslmaster.library/OpenSSLTags() to
initialise AmiSSL, so you should not need to use this function
in simple applications. If you have decided delay the initialisation,
this function must be called to initialise AmiSSL, once opened,
before you can use any of the available OpenSSL functions.
Furthermore, if you would like to use AmiSSL in your application's
subprocesses, it is recommended that you share the AmiSSL instance
opened from your main process and have each process simply call
InitAmiSSLA() instead of wasting resources and opening AmiSSL for
every process.
INPUTS
tagList - pointer to an array of tags providing optional extra
parameters, or NULL.
TAGS
AmiSSL_GetAmiSSLBase - (struct Library **) Pointer to your
AmiSSLExtBase variable for AmigaOS 3.x. Because of the sheer
number of available public functions in OpenSSL, AmiSSL v5 spans
two library bases. If successful, this function will set this to
the opened library. DO NOT call CloseLibrary() on this as
CloseAmiSSL() takes care of it. Not needed in AmigaOS 4.x
applications. (V5)
AmiSSL_SocketBase - (struct Library *) Pointer to the socket base. If
the caller doesn't need any networking functionality, or if using
AmiSSL_ISocket / AmiSSL_ISocketPtr for AmigaOS 4.x, this can be
omitted. (defaults to NULL). (V1)
AmiSSL_ISocket - (struct SocketIFace *) Pointer to the socket library
interface for AmigaOS 4.x. If the caller doesn't need any
networking functionality, this can be omitted, likewise if
AmiSSL_ISocketPtr or AmiSSL_SocketBase has been specified
instead. (V3)
AmiSSL_ISocketPtr - (struct SocketIFace **) Pointer to your ISocket
variable for AmigaOS 4.x. If the caller doesn't need networking
functionality, this can be omitted, likewise if AmiSSL_ISocket or
AmiSSL_SocketBase has been specified instead. This generally
doesn't need to be used, but may be useful if you need to open
ISocket after initiliasing AmiSSL. (V3)
AmiSSL_ErrNoPtr - (int *)Pointer to the errno variable to set when
some of the C or socket library functions called by AmiSSL return
an error. You should always specify this tag or errno error
detection in your program will not work reliably. AmiSSL will
always instruct the network library to use a certain errno
pointer (ie. it will specify SBTM_SETVAL(SBTC_ERRNOLONGPTR) in
SocketBaseTags() for bsdsocket.library and similarly for other
networking packages) possibly overriding the errno pointer you
specified to the networking library yourself. If AmiSSL_ErrNoPtr
is specified, AmiSSL will use it for SBTM_SETVAL(SBTC_ERRNOLONGPTR)
or equivalent calls, otherwise it will use an internal errno
pointer and thus errno changes will not be propagated to your
application. (V2)
AmiSSL_TimerPort - (struct MsgPort *) Provide an optional dedicated
message port to be used for all timer related functions, instead of
the port that AmiSSL usually allocates itself. It must belong to
the calling process and remain valid until after CleanupAmiSSL()
has been executed, after which the application is responsible for
freeing the port. (V5.27)
AmiSSL_TimerSignal - (LONG) The signal bit to be used by the message
port which AmiSSL allocates for all timer related functions. It
must remain valid until CleanupAmiSSL() has been executed, after
which the application is responsible for freeing the signal, when
necessary. This tag is only implemented for AmigaOS 4.x, and will
be ignored if AmiSSL_TimerPort has also been specified.
(defaults to -1, allocate a new signal). (V5.27)
AmiSSL_SocketBaseBrand - (LONG) useful only if you are using
MultiTCP linker library by Stefan Burstroem, you can ignore it
otherwise. (defaults to TCPIP_AmiTCP). (V2)
AmiSSL_MLinkLock - useful only if you are using MultiTCP linker
library by Stefan Burstroem, you can ignore it otherwise. (V2)
AmiSSL_Version - OBSOLETE - Used for AmiSSL v1 only
AmiSSL_Revision - OBSOLETE - Used for AmiSSL v1 only
AmiSSL_Version_Override - OBSOLETE - Used for AmiSSL v1 only
AmiSSL_TCPStack - OBSOLETE - Used for AmiSSL v2 only
AmiSSL_SSLVersionApp - OBSOLETE - Used for AmiSSL v2/v3 only
RESULTS
error - returns 0 on success or non-zero on failure.
SEE ALSO
CleanupAmiSSLA()
amissl.library/SSL_CIPHER_get_authentication amissl.library/SSL_CIPHER_get_authentication
NAME
SSL_CIPHER_get_authentication -- get the name of a cipher's
authentication algorithm (V3)
SYNOPSIS
name = SSL_CIPHER_get_authentication( cipher )
const char *SSL_CIPHER_get_authentication( SSL_CIPHER * );
FUNCTION
Return the textual representation of a cipher's authentication algorithm.
INPUTS
cipher - pointer to a SSL cipher.
RESULTS
name - returns the cipher authentication name or "unknown".
amissl.library/SSL_CIPHER_get_encryption amissl.library/SSL_CIPHER_get_encryption
NAME
SSL_CIPHER_get_encryption -- get the name of a cipher's
encryption algorithm (V3)
SYNOPSIS
name = SSL_CIPHER_get_encryption( cipher )
const char *SSL_CIPHER_get_encryption( SSL_CIPHER * );
FUNCTION
Return the textual representation of a cipher's encryption algorithm.
INPUTS
cipher - pointer to a SSL cipher.
RESULTS
name - returns the cipher encryption name or "unknown".
amissl.library/SSL_CIPHER_get_export amissl.library/SSL_CIPHER_get_export
NAME
SSL_CIPHER_get_key_exchange -- get the name of a cipher's
key exchange algorithm (V3)
SYNOPSIS
name = SSL_CIPHER_get_key_exchange( cipher )
const char *SSL_CIPHER_get_key_exchange( SSL_CIPHER * );
FUNCTION
Return the textual representation of a cipher's key exchange algorithm.
INPUTS
cipher - pointer to a SSL cipher.
RESULTS
name - returns the cipher key exchange name or "unknown".
amissl.library/SSL_CIPHER_get_mac amissl.library/SSL_CIPHER_get_mac
NAME
SSL_CIPHER_get_mac -- get the name of a cipher's mac algorithm (V3)
SYNOPSIS
name = SSL_CIPHER_get_mac( cipher )
const char *SSL_CIPHER_get_mac( SSL_CIPHER * );
FUNCTION
Return the textual representation of a cipher's mac algorithm.
INPUTS
cipher - pointer to a SSL cipher.
RESULTS
name - returns the cipher mac name or "unknown".