Skip to main content

  | मौसम…

411 समाचार
Public archive edition · Attributed source reporting with original dates · AI drafts are excluded

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".