Raise an error on syscall failure in tls_retry_write_records
[openssl.git] / engines / e_afalg.c
index 982a53d280d0f0025a15614748d18d2142f3b9c1..3ca5b0211e7b6c703bcd733aee08618a9dd808a3 100644 (file)
@@ -1,12 +1,15 @@
 /*
- * Copyright 2016 The OpenSSL Project Authors. All Rights Reserved.
+ * Copyright 2016-2024 The OpenSSL Project Authors. All Rights Reserved.
  *
- * Licensed under the OpenSSL license (the "License").  You may not use
+ * Licensed under the Apache License 2.0 (the "License").  You may not use
  * this file except in compliance with the License.  You can obtain a copy
  * in the file LICENSE in the source distribution or at
  * https://www.openssl.org/source/license.html
  */
 
+/* We need to use some deprecated APIs */
+#define OPENSSL_SUPPRESS_DEPRECATED
+
 /* Required for vmsplice */
 #ifndef _GNU_SOURCE
 # define _GNU_SOURCE
@@ -18,6 +21,7 @@
 #include <openssl/engine.h>
 #include <openssl/async.h>
 #include <openssl/err.h>
+#include "internal/nelem.h"
 
 #include <sys/socket.h>
 #include <linux/version.h>
@@ -62,9 +66,6 @@ void engine_load_afalg_int(void)
 # define ALG_OP_TYPE     unsigned int
 # define ALG_OP_LEN      (sizeof(ALG_OP_TYPE))
 
-#define ALG_MAX_SALG_NAME       64
-#define ALG_MAX_SALG_TYPE       14
-
 # ifdef OPENSSL_NO_DYNAMIC_ENGINE
 void engine_load_afalg_int(void);
 # endif
@@ -78,7 +79,8 @@ static int afalg_create_sk(afalg_ctx *actx, const char *ciphertype,
 static int afalg_destroy(ENGINE *e);
 static int afalg_init(ENGINE *e);
 static int afalg_finish(ENGINE *e);
-const EVP_CIPHER *afalg_aes_128_cbc(void);
+static const EVP_CIPHER *afalg_aes_cbc(int nid);
+static cbc_handles *get_cipher_handle(int nid);
 static int afalg_ciphers(ENGINE *e, const EVP_CIPHER **cipher,
                          const int **nids, int nid);
 static int afalg_cipher_init(EVP_CIPHER_CTX *ctx, const unsigned char *key,
@@ -93,10 +95,14 @@ static const char *engine_afalg_id = "afalg";
 static const char *engine_afalg_name = "AFALG engine support";
 
 static int afalg_cipher_nids[] = {
-    NID_aes_128_cbc
+    NID_aes_128_cbc,
+    NID_aes_192_cbc,
+    NID_aes_256_cbc,
 };
 
-static EVP_CIPHER *_hidden_aes_128_cbc = NULL;
+static cbc_handles cbc_handle[] = {{AES_KEY_SIZE_128, NULL},
+                                    {AES_KEY_SIZE_192, NULL},
+                                    {AES_KEY_SIZE_256, NULL}};
 
 static ossl_inline int io_setup(unsigned n, aio_context_t *ctx)
 {
@@ -118,11 +124,56 @@ static ossl_inline int io_read(aio_context_t ctx, long n, struct iocb **iocb)
     return syscall(__NR_io_submit, ctx, n, iocb);
 }
 
+/* A version of 'struct timespec' with 32-bit time_t and nanoseconds.  */
+struct __timespec32
+{
+  __kernel_long_t tv_sec;
+  __kernel_long_t tv_nsec;
+};
+
 static ossl_inline int io_getevents(aio_context_t ctx, long min, long max,
                                struct io_event *events,
                                struct timespec *timeout)
 {
-    return syscall(__NR_io_getevents, ctx, min, max, events, timeout);
+#if defined(__NR_io_pgetevents_time64)
+    /* Check if we are a 32-bit architecture with a 64-bit time_t */
+    if (sizeof(*timeout) != sizeof(struct __timespec32)) {
+        int ret = syscall(__NR_io_pgetevents_time64, ctx, min, max, events,
+                          timeout, NULL);
+        if (ret == 0 || errno != ENOSYS)
+            return ret;
+    }
+#endif
+
+#if defined(__NR_io_getevents)
+    if (sizeof(*timeout) == sizeof(struct __timespec32))
+        /*
+         * time_t matches our architecture length, we can just use
+         * __NR_io_getevents
+         */
+        return syscall(__NR_io_getevents, ctx, min, max, events, timeout);
+    else {
+        /*
+         * We don't have __NR_io_pgetevents_time64, but we are using a
+         * 64-bit time_t on a 32-bit architecture. If we can fit the
+         * timeout value in a 32-bit time_t, then let's do that
+         * and then use the __NR_io_getevents syscall.
+         */
+        if (timeout && timeout->tv_sec == (long)timeout->tv_sec) {
+            struct __timespec32 ts32;
+
+            ts32.tv_sec = (__kernel_long_t) timeout->tv_sec;
+            ts32.tv_nsec = (__kernel_long_t) timeout->tv_nsec;
+
+            return syscall(__NR_io_getevents, ctx, min, max, events, ts32);
+        } else {
+            return syscall(__NR_io_getevents, ctx, min, max, events, NULL);
+        }
+    }
+#endif
+
+    errno = ENOSYS;
+    return -1;
 }
 
 static void afalg_waitfd_cleanup(ASYNC_WAIT_CTX *ctx, const void *key,
@@ -142,7 +193,7 @@ static int afalg_setup_async_event_notification(afalg_aio *aio)
         /* Async mode */
         waitctx = ASYNC_get_wait_ctx(job);
         if (waitctx == NULL) {
-            ALG_WARN("%s: ASYNC_get_wait_ctx error", __func__);
+            ALG_WARN("%s(%d): ASYNC_get_wait_ctx error", __FILE__, __LINE__);
             return 0;
         }
         /* Get waitfd from ASYNC_WAIT_CTX if it is already set */
@@ -155,7 +206,8 @@ static int afalg_setup_async_event_notification(afalg_aio *aio)
              */
             aio->efd = eventfd(0);
             if (aio->efd == -1) {
-                ALG_PERR("%s: Failed to get eventfd : ", __func__);
+                ALG_PERR("%s(%d): Failed to get eventfd : ", __FILE__,
+                         __LINE__);
                 AFALGerr(AFALG_F_AFALG_SETUP_ASYNC_EVENT_NOTIFICATION,
                          AFALG_R_EVENTFD_FAILED);
                 return 0;
@@ -164,14 +216,14 @@ static int afalg_setup_async_event_notification(afalg_aio *aio)
                                              aio->efd, custom,
                                              afalg_waitfd_cleanup);
             if (ret == 0) {
-                ALG_WARN("%s: Failed to set wait fd", __func__);
+                ALG_WARN("%s(%d): Failed to set wait fd", __FILE__, __LINE__);
                 close(aio->efd);
                 return 0;
             }
             /* make fd non-blocking in async mode */
             if (fcntl(aio->efd, F_SETFL, O_NONBLOCK) != 0) {
-                ALG_WARN("%s: Failed to set event fd as NONBLOCKING",
-                         __func__);
+                ALG_WARN("%s(%d): Failed to set event fd as NONBLOCKING",
+                         __FILE__, __LINE__);
             }
         }
         aio->mode = MODE_ASYNC;
@@ -179,7 +231,7 @@ static int afalg_setup_async_event_notification(afalg_aio *aio)
         /* Sync mode */
         aio->efd = eventfd(0);
         if (aio->efd == -1) {
-            ALG_PERR("%s: Failed to get eventfd : ", __func__);
+            ALG_PERR("%s(%d): Failed to get eventfd : ", __FILE__, __LINE__);
             AFALGerr(AFALG_F_AFALG_SETUP_ASYNC_EVENT_NOTIFICATION,
                      AFALG_R_EVENTFD_FAILED);
             return 0;
@@ -189,7 +241,7 @@ static int afalg_setup_async_event_notification(afalg_aio *aio)
     return 1;
 }
 
-int afalg_init_aio(afalg_aio *aio)
+static int afalg_init_aio(afalg_aio *aio)
 {
     int r = -1;
 
@@ -197,7 +249,7 @@ int afalg_init_aio(afalg_aio *aio)
     aio->aio_ctx = 0;
     r = io_setup(MAX_INFLIGHTS, &aio->aio_ctx);
     if (r < 0) {
-        ALG_PERR("%s: io_setup error : ", __func__);
+        ALG_PERR("%s(%d): io_setup error : ", __FILE__, __LINE__);
         AFALGerr(AFALG_F_AFALG_INIT_AIO, AFALG_R_IO_SETUP_FAILED);
         return 0;
     }
@@ -209,8 +261,8 @@ int afalg_init_aio(afalg_aio *aio)
     return 1;
 }
 
-int afalg_fin_cipher_aio(afalg_aio *aio, int sfd, unsigned char *buf,
-                         size_t len)
+static int afalg_fin_cipher_aio(afalg_aio *aio, int sfd, unsigned char *buf,
+                                size_t len)
 {
     int r;
     int retry = 0;
@@ -251,7 +303,7 @@ int afalg_fin_cipher_aio(afalg_aio *aio, int sfd, unsigned char *buf,
      */
     r = io_read(aio->aio_ctx, 1, &cb);
     if (r < 0) {
-        ALG_PWARN("%s: io_read failed : ", __func__);
+        ALG_PWARN("%s(%d): io_read failed : ", __FILE__, __LINE__);
         return 0;
     }
 
@@ -264,14 +316,23 @@ int afalg_fin_cipher_aio(afalg_aio *aio, int sfd, unsigned char *buf,
         if (r < 0) {
             if (errno == EAGAIN || errno == EWOULDBLOCK)
                 continue;
-            ALG_PERR("%s: read failed for event fd : ", __func__);
+            ALG_PERR("%s(%d): read failed for event fd : ", __FILE__, __LINE__);
             return 0;
         } else if (r == 0 || eval <= 0) {
-            ALG_WARN("%s: eventfd read %d bytes, eval = %lu\n", __func__, r,
-                     eval);
+            ALG_WARN("%s(%d): eventfd read %d bytes, eval = %lu\n", __FILE__,
+                     __LINE__, r, eval);
         }
         if (eval > 0) {
 
+#ifdef OSSL_SANITIZE_MEMORY
+            /*
+             * In a memory sanitiser build, the changes to memory made by the
+             * system call aren't reliably detected.  By initialising the
+             * memory here, the sanitiser is told that they are okay.
+             */
+            memset(events, 0, sizeof(events));
+#endif
+
             /* Get results of AIO read */
             r = io_getevents(aio->aio_ctx, 1, MAX_INFLIGHTS,
                              events, &timeout);
@@ -288,29 +349,53 @@ int afalg_fin_cipher_aio(afalg_aio *aio, int sfd, unsigned char *buf,
                     if (events[0].res == -EBUSY && retry++ < 3) {
                         r = io_read(aio->aio_ctx, 1, &cb);
                         if (r < 0) {
-                            ALG_PERR("%s: retry %d for io_read failed : ",
-                                     __func__, retry);
+                            ALG_PERR("%s(%d): retry %d for io_read failed : ",
+                                     __FILE__, __LINE__, retry);
                             return 0;
                         }
                         continue;
                     } else {
+                        char strbuf[32];
+                        /*
+                         * sometimes __s64 is defined as long long int
+                         * but on some archs ( like mips64 or powerpc64 ) it's just long int
+                         *
+                         * to be able to use BIO_snprintf() with %lld without warnings
+                         * copy events[0].res to an long long int variable
+                         *
+                         * because long long int should always be at least 64 bit this should work
+                         */
+                        long long int op_ret = events[0].res;
+
                         /*
                          * Retries exceed for -EBUSY or unrecoverable error
                          * condition for this instance of operation.
                          */
                         ALG_WARN
-                            ("%s: Crypto Operation failed with code %lld\n",
-                             __func__, events[0].res);
+                            ("%s(%d): Crypto Operation failed with code %lld\n",
+                             __FILE__, __LINE__, events[0].res);
+                        BIO_snprintf(strbuf, sizeof(strbuf), "%lld", op_ret);
+                        switch (events[0].res) {
+                        case -ENOMEM:
+                            AFALGerr(0, AFALG_R_KERNEL_OP_FAILED);
+                            ERR_add_error_data(3, "-ENOMEM ( code ", strbuf, " )");
+                            break;
+                        default:
+                            AFALGerr(0, AFALG_R_KERNEL_OP_FAILED);
+                            ERR_add_error_data(2, "code ", strbuf);
+                            break;
+                        }
                         return 0;
                     }
                 }
                 /* Operation successful. */
                 done = 1;
             } else if (r < 0) {
-                ALG_PERR("%s: io_getevents failed : ", __func__);
+                ALG_PERR("%s(%d): io_getevents failed : ", __FILE__, __LINE__);
                 return 0;
             } else {
-                ALG_WARN("%s: io_geteventd read 0 bytes\n", __func__);
+                ALG_WARN("%s(%d): io_geteventd read 0 bytes\n", __FILE__,
+                         __LINE__);
             }
         }
     } while (!done);
@@ -346,11 +431,10 @@ static ossl_inline int afalg_set_key(afalg_ctx *actx, const unsigned char *key,
     int ret;
     ret = setsockopt(actx->bfd, SOL_ALG, ALG_SET_KEY, key, klen);
     if (ret < 0) {
-        ALG_PERR("%s: Failed to set socket option : ", __func__);
+        ALG_PERR("%s(%d): Failed to set socket option : ", __FILE__, __LINE__);
         AFALGerr(AFALG_F_AFALG_SET_KEY, AFALG_R_SOCKET_SET_KEY_FAILED);
         return 0;
     }
-
     return 1;
 }
 
@@ -364,28 +448,26 @@ static int afalg_create_sk(afalg_ctx *actx, const char *ciphertype,
 
     memset(&sa, 0, sizeof(sa));
     sa.salg_family = AF_ALG;
-    strncpy((char *) sa.salg_type, ciphertype, ALG_MAX_SALG_TYPE);
-    sa.salg_type[ALG_MAX_SALG_TYPE-1] = '\0';
-    strncpy((char *) sa.salg_name, ciphername, ALG_MAX_SALG_NAME);
-    sa.salg_name[ALG_MAX_SALG_NAME-1] = '\0';
+    OPENSSL_strlcpy((char *) sa.salg_type, ciphertype, sizeof(sa.salg_type));
+    OPENSSL_strlcpy((char *) sa.salg_name, ciphername, sizeof(sa.salg_name));
 
     actx->bfd = socket(AF_ALG, SOCK_SEQPACKET, 0);
     if (actx->bfd == -1) {
-        ALG_PERR("%s: Failed to open socket : ", __func__);
+        ALG_PERR("%s(%d): Failed to open socket : ", __FILE__, __LINE__);
         AFALGerr(AFALG_F_AFALG_CREATE_SK, AFALG_R_SOCKET_CREATE_FAILED);
         goto err;
     }
 
     r = bind(actx->bfd, (struct sockaddr *)&sa, sizeof(sa));
     if (r < 0) {
-        ALG_PERR("%s: Failed to bind socket : ", __func__);
+        ALG_PERR("%s(%d): Failed to bind socket : ", __FILE__, __LINE__);
         AFALGerr(AFALG_F_AFALG_CREATE_SK, AFALG_R_SOCKET_BIND_FAILED);
         goto err;
     }
 
     actx->sfd = accept(actx->bfd, NULL, 0);
     if (actx->sfd < 0) {
-        ALG_PERR("%s: Socket Accept Failed : ", __func__);
+        ALG_PERR("%s(%d): Socket Accept Failed : ", __FILE__, __LINE__);
         AFALGerr(AFALG_F_AFALG_CREATE_SK, AFALG_R_SOCKET_ACCEPT_FAILED);
         goto err;
     }
@@ -405,7 +487,7 @@ static int afalg_start_cipher_sk(afalg_ctx *actx, const unsigned char *in,
                                  size_t inl, const unsigned char *iv,
                                  unsigned int enc)
 {
-    struct msghdr msg = { 0 };
+    struct msghdr msg;
     struct cmsghdr *cmsg;
     struct iovec iov;
     ssize_t sbytes;
@@ -414,6 +496,7 @@ static int afalg_start_cipher_sk(afalg_ctx *actx, const unsigned char *in,
 # endif
     char cbuf[CMSG_SPACE(ALG_IV_LEN(ALG_AES_IV_LEN)) + CMSG_SPACE(ALG_OP_LEN)];
 
+    memset(&msg, 0, sizeof(msg));
     memset(cbuf, 0, sizeof(cbuf));
     msg.msg_control = cbuf;
     msg.msg_controllen = sizeof(cbuf);
@@ -447,24 +530,24 @@ static int afalg_start_cipher_sk(afalg_ctx *actx, const unsigned char *in,
     /* Sendmsg() sends iv and cipher direction to the kernel */
     sbytes = sendmsg(actx->sfd, &msg, 0);
     if (sbytes < 0) {
-        ALG_PERR("%s: sendmsg failed for zero copy cipher operation : ",
-                 __func__);
+        ALG_PERR("%s(%d): sendmsg failed for zero copy cipher operation : ",
+                 __FILE__, __LINE__);
         return 0;
     }
 
     /*
      * vmsplice and splice are used to pin the user space input buffer for
-     * kernel space processing avoiding copys from user to kernel space
+     * kernel space processing avoiding copies from user to kernel space
      */
     ret = vmsplice(actx->zc_pipe[1], &iov, 1, SPLICE_F_GIFT);
     if (ret < 0) {
-        ALG_PERR("%s: vmsplice failed : ", __func__);
+        ALG_PERR("%s(%d): vmsplice failed : ", __FILE__, __LINE__);
         return 0;
     }
 
     ret = splice(actx->zc_pipe[0], NULL, actx->sfd, NULL, inl, 0);
     if (ret < 0) {
-        ALG_PERR("%s: splice failed : ", __func__);
+        ALG_PERR("%s(%d): splice failed : ", __FILE__, __LINE__);
         return 0;
     }
 # else
@@ -474,7 +557,8 @@ static int afalg_start_cipher_sk(afalg_ctx *actx, const unsigned char *in,
     /* Sendmsg() sends iv, cipher direction and input data to the kernel */
     sbytes = sendmsg(actx->sfd, &msg, 0);
     if (sbytes < 0) {
-        ALG_PERR("%s: sendmsg failed for cipher operation : ", __func__);
+        ALG_PERR("%s(%d): sendmsg failed for cipher operation : ", __FILE__,
+                 __LINE__);
         return 0;
     }
 
@@ -492,40 +576,42 @@ static int afalg_cipher_init(EVP_CIPHER_CTX *ctx, const unsigned char *key,
                              const unsigned char *iv, int enc)
 {
     int ciphertype;
-    int ret;
+    int ret, len;
     afalg_ctx *actx;
-    char ciphername[ALG_MAX_SALG_NAME];
+    const char *ciphername;
 
     if (ctx == NULL || key == NULL) {
-        ALG_WARN("%s: Null Parameter\n", __func__);
+        ALG_WARN("%s(%d): Null Parameter\n", __FILE__, __LINE__);
         return 0;
     }
 
-    if (EVP_CIPHER_CTX_cipher(ctx) == NULL) {
-        ALG_WARN("%s: Cipher object NULL\n", __func__);
+    if (EVP_CIPHER_CTX_get0_cipher(ctx) == NULL) {
+        ALG_WARN("%s(%d): Cipher object NULL\n", __FILE__, __LINE__);
         return 0;
     }
 
     actx = EVP_CIPHER_CTX_get_cipher_data(ctx);
     if (actx == NULL) {
-        ALG_WARN("%s: Cipher data NULL\n", __func__);
+        ALG_WARN("%s(%d): Cipher data NULL\n", __FILE__, __LINE__);
         return 0;
     }
 
-    ciphertype = EVP_CIPHER_CTX_nid(ctx);
+    ciphertype = EVP_CIPHER_CTX_get_nid(ctx);
     switch (ciphertype) {
     case NID_aes_128_cbc:
-        strncpy(ciphername, "cbc(aes)", ALG_MAX_SALG_NAME);
+    case NID_aes_192_cbc:
+    case NID_aes_256_cbc:
+        ciphername = "cbc(aes)";
         break;
     default:
-        ALG_WARN("%s: Unsupported Cipher type %d\n", __func__, ciphertype);
+        ALG_WARN("%s(%d): Unsupported Cipher type %d\n", __FILE__, __LINE__,
+                 ciphertype);
         return 0;
     }
-    ciphername[ALG_MAX_SALG_NAME-1]='\0';
 
-    if (ALG_AES_IV_LEN != EVP_CIPHER_CTX_iv_length(ctx)) {
-        ALG_WARN("%s: Unsupported IV length :%d\n", __func__,
-                EVP_CIPHER_CTX_iv_length(ctx));
+    if (ALG_AES_IV_LEN != EVP_CIPHER_CTX_get_iv_length(ctx)) {
+        ALG_WARN("%s(%d): Unsupported IV length :%d\n", __FILE__, __LINE__,
+                 EVP_CIPHER_CTX_get_iv_length(ctx));
         return 0;
     }
 
@@ -534,8 +620,9 @@ static int afalg_cipher_init(EVP_CIPHER_CTX *ctx, const unsigned char *key,
     if (ret < 1)
         return 0;
 
-
-    ret = afalg_set_key(actx, key, EVP_CIPHER_CTX_key_length(ctx));
+    if ((len = EVP_CIPHER_CTX_get_key_length(ctx)) <= 0)
+        goto err;
+    ret = afalg_set_key(actx, key, len);
     if (ret < 1)
         goto err;
 
@@ -565,7 +652,8 @@ static int afalg_do_cipher(EVP_CIPHER_CTX *ctx, unsigned char *out,
     char nxtiv[ALG_AES_IV_LEN] = { 0 };
 
     if (ctx == NULL || out == NULL || in == NULL) {
-        ALG_WARN("NULL parameter passed to function %s\n", __func__);
+        ALG_WARN("NULL parameter passed to function %s(%d)\n", __FILE__,
+                 __LINE__);
         return 0;
     }
 
@@ -580,14 +668,14 @@ static int afalg_do_cipher(EVP_CIPHER_CTX *ctx, unsigned char *out,
      * set iv now for decrypt operation as the input buffer can be
      * overwritten for inplace operation where in = out.
      */
-    if (EVP_CIPHER_CTX_encrypting(ctx) == 0) {
+    if (EVP_CIPHER_CTX_is_encrypting(ctx) == 0) {
         memcpy(nxtiv, in + (inl - ALG_AES_IV_LEN), ALG_AES_IV_LEN);
     }
 
     /* Send input data to kernel space */
     ret = afalg_start_cipher_sk(actx, (unsigned char *)in, inl,
                                 EVP_CIPHER_CTX_iv(ctx),
-                                EVP_CIPHER_CTX_encrypting(ctx));
+                                EVP_CIPHER_CTX_is_encrypting(ctx));
     if (ret < 1) {
         return 0;
     }
@@ -597,7 +685,7 @@ static int afalg_do_cipher(EVP_CIPHER_CTX *ctx, unsigned char *out,
     if (ret < 1)
         return 0;
 
-    if (EVP_CIPHER_CTX_encrypting(ctx)) {
+    if (EVP_CIPHER_CTX_is_encrypting(ctx)) {
         memcpy(EVP_CIPHER_CTX_iv_noconst(ctx), out + (inl - ALG_AES_IV_LEN),
                ALG_AES_IV_LEN);
     } else {
@@ -612,16 +700,14 @@ static int afalg_cipher_cleanup(EVP_CIPHER_CTX *ctx)
     afalg_ctx *actx;
 
     if (ctx == NULL) {
-        ALG_WARN("NULL parameter passed to function %s\n", __func__);
+        ALG_WARN("NULL parameter passed to function %s(%d)\n", __FILE__,
+                 __LINE__);
         return 0;
     }
 
     actx = (afalg_ctx *) EVP_CIPHER_CTX_get_cipher_data(ctx);
-    if (actx == NULL || actx->init_done != MAGIC_INIT_NUM) {
-        ALG_WARN("%s afalg ctx passed\n",
-                 ctx == NULL ? "NULL" : "Uninitialised");
-        return 0;
-    }
+    if (actx == NULL || actx->init_done != MAGIC_INIT_NUM)
+        return 1;
 
     close(actx->sfd);
     close(actx->bfd);
@@ -637,29 +723,48 @@ static int afalg_cipher_cleanup(EVP_CIPHER_CTX *ctx)
     return 1;
 }
 
-const EVP_CIPHER *afalg_aes_128_cbc(void)
+static cbc_handles *get_cipher_handle(int nid)
 {
-    if (_hidden_aes_128_cbc == NULL
-        && ((_hidden_aes_128_cbc =
-             EVP_CIPHER_meth_new(NID_aes_128_cbc,
-                                 AES_BLOCK_SIZE,
-                                 AES_KEY_SIZE_128)) == NULL
-            || !EVP_CIPHER_meth_set_iv_length(_hidden_aes_128_cbc, AES_IV_LEN)
-            || !EVP_CIPHER_meth_set_flags(_hidden_aes_128_cbc,
-                                          EVP_CIPH_CBC_MODE |
-                                          EVP_CIPH_FLAG_DEFAULT_ASN1)
-            || !EVP_CIPHER_meth_set_init(_hidden_aes_128_cbc,
-                                         afalg_cipher_init)
-            || !EVP_CIPHER_meth_set_do_cipher(_hidden_aes_128_cbc,
-                                              afalg_do_cipher)
-            || !EVP_CIPHER_meth_set_cleanup(_hidden_aes_128_cbc,
-                                            afalg_cipher_cleanup)
-            || !EVP_CIPHER_meth_set_impl_ctx_size(_hidden_aes_128_cbc,
-                                                  sizeof(afalg_ctx)))) {
-        EVP_CIPHER_meth_free(_hidden_aes_128_cbc);
-        _hidden_aes_128_cbc = NULL;
-    }
-    return _hidden_aes_128_cbc;
+    switch (nid) {
+    case NID_aes_128_cbc:
+        return &cbc_handle[AES_CBC_128];
+    case NID_aes_192_cbc:
+        return &cbc_handle[AES_CBC_192];
+    case NID_aes_256_cbc:
+        return &cbc_handle[AES_CBC_256];
+    default:
+        return NULL;
+    }
+}
+
+static const EVP_CIPHER *afalg_aes_cbc(int nid)
+{
+    cbc_handles *cipher_handle = get_cipher_handle(nid);
+
+    if (cipher_handle == NULL)
+            return NULL;
+    if (cipher_handle->_hidden == NULL
+        && ((cipher_handle->_hidden =
+         EVP_CIPHER_meth_new(nid,
+                             AES_BLOCK_SIZE,
+                             cipher_handle->key_size)) == NULL
+        || !EVP_CIPHER_meth_set_iv_length(cipher_handle->_hidden,
+                                          AES_IV_LEN)
+        || !EVP_CIPHER_meth_set_flags(cipher_handle->_hidden,
+                                      EVP_CIPH_CBC_MODE |
+                                      EVP_CIPH_FLAG_DEFAULT_ASN1)
+        || !EVP_CIPHER_meth_set_init(cipher_handle->_hidden,
+                                     afalg_cipher_init)
+        || !EVP_CIPHER_meth_set_do_cipher(cipher_handle->_hidden,
+                                          afalg_do_cipher)
+        || !EVP_CIPHER_meth_set_cleanup(cipher_handle->_hidden,
+                                        afalg_cipher_cleanup)
+        || !EVP_CIPHER_meth_set_impl_ctx_size(cipher_handle->_hidden,
+                                              sizeof(afalg_ctx)))) {
+        EVP_CIPHER_meth_free(cipher_handle->_hidden);
+        cipher_handle->_hidden= NULL;
+    }
+    return cipher_handle->_hidden;
 }
 
 static int afalg_ciphers(ENGINE *e, const EVP_CIPHER **cipher,
@@ -674,19 +779,21 @@ static int afalg_ciphers(ENGINE *e, const EVP_CIPHER **cipher,
 
     switch (nid) {
     case NID_aes_128_cbc:
-        *cipher = afalg_aes_128_cbc();
+    case NID_aes_192_cbc:
+    case NID_aes_256_cbc:
+        *cipher = afalg_aes_cbc(nid);
         break;
     default:
         *cipher = NULL;
         r = 0;
     }
-
     return r;
 }
 
 static int bind_afalg(ENGINE *e)
 {
     /* Ensure the afalg error handling is set up */
+    unsigned short i;
     ERR_load_AFALG_strings();
 
     if (!ENGINE_set_id(e, engine_afalg_id)
@@ -699,13 +806,15 @@ static int bind_afalg(ENGINE *e)
     }
 
     /*
-     * Create _hidden_aes_128_cbc by calling afalg_aes_128_cbc
+     * Create _hidden_aes_xxx_cbc by calling afalg_aes_xxx_cbc
      * now, as bind_aflag can only be called by one thread at a
      * time.
      */
-    if (afalg_aes_128_cbc() == NULL) {
-        AFALGerr(AFALG_F_BIND_AFALG, AFALG_R_INIT_FAILED);
-        return 0;
+    for (i = 0; i < OSSL_NELEM(afalg_cipher_nids); i++) {
+        if (afalg_aes_cbc(afalg_cipher_nids[i]) == NULL) {
+            AFALGerr(AFALG_F_BIND_AFALG, AFALG_R_INIT_FAILED);
+            return 0;
+        }
     }
 
     if (!ENGINE_set_ciphers(e, afalg_ciphers)) {
@@ -725,8 +834,10 @@ static int bind_helper(ENGINE *e, const char *id)
     if (!afalg_chk_platform())
         return 0;
 
-    if (!bind_afalg(e))
+    if (!bind_afalg(e)) {
+        afalg_destroy(e);
         return 0;
+    }
     return 1;
 }
 
@@ -801,9 +912,19 @@ void engine_load_afalg_int(void)
     toadd = engine_afalg();
     if (toadd == NULL)
         return;
+    ERR_set_mark();
     ENGINE_add(toadd);
+    /*
+     * If the "add" worked, it gets a structural reference. So either way, we
+     * release our just-created reference.
+     */
     ENGINE_free(toadd);
-    ERR_clear_error();
+    /*
+     * If the "add" didn't work, it was probably a conflict because it was
+     * already added (eg. someone calling ENGINE_load_blah then calling
+     * ENGINE_load_builtin_engines() perhaps).
+     */
+    ERR_pop_to_mark();
 }
 # endif
 
@@ -817,11 +938,20 @@ static int afalg_finish(ENGINE *e)
     return 1;
 }
 
+static int free_cbc(void)
+{
+    short unsigned int i;
+    for (i = 0; i < OSSL_NELEM(afalg_cipher_nids); i++) {
+        EVP_CIPHER_meth_free(cbc_handle[i]._hidden);
+        cbc_handle[i]._hidden = NULL;
+    }
+    return 1;
+}
+
 static int afalg_destroy(ENGINE *e)
 {
     ERR_unload_AFALG_strings();
-    EVP_CIPHER_meth_free(_hidden_aes_128_cbc);
-    _hidden_aes_128_cbc = NULL;
+    free_cbc();
     return 1;
 }