Security hardening: Expose Build flags for Position Independed Execution (PIE)
[openssl.git] / engines / e_devcrypto.c
index 0054319d83c8d7dff812ede696044d763a32399b..1e1d9d12b83c719cd2cfbb98f2ebb3c643b16f36 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright 2017-2018 The OpenSSL Project Authors. All Rights Reserved.
+ * Copyright 2017-2023 The OpenSSL Project Authors. All Rights Reserved.
  *
  * 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
@@ -7,7 +7,10 @@
  * https://www.openssl.org/source/license.html
  */
 
-#include "../e_os.h"
+/* We need to use some deprecated APIs */
+#define OPENSSL_SUPPRESS_DEPRECATED
+
+#include "internal/e_os.h"
 #include <string.h>
 #include <sys/types.h>
 #include <sys/stat.h>
@@ -21,7 +24,8 @@
 #include <openssl/err.h>
 #include <openssl/engine.h>
 #include <openssl/objects.h>
-#include <crypto/cryptodev.h>
+#include "crypto/cryptodev.h"
+#include "internal/nelem.h"
 
 /* #define ENGINE_DEVCRYPTO_DEBUG */
 
 
 #define engine_devcrypto_id "devcrypto"
 
+/*
+ * Use session2_op on FreeBSD which permits requesting specific
+ * drivers or classes of drivers at session creation time.
+ */
+#ifdef CIOCGSESSION2
+typedef struct session2_op session_op_t;
+#else
+typedef struct session_op session_op_t;
+#endif
+
 /*
  * ONE global file descriptor for all sessions.  This allows operations
  * such as digest session data copying (see digest_copy()), but is also
@@ -70,12 +84,12 @@ struct driver_info_st {
 void engine_load_devcrypto_int(void);
 #endif
 
-static int clean_devcrypto_session(struct session_op *sess) {
+static int clean_devcrypto_session(session_op_t *sess) {
     if (ioctl(cfd, CIOCFSESSION, &sess->ses) < 0) {
-        SYSerr("ioctl", errno);
+        ERR_raise_data(ERR_LIB_SYS, errno, "calling ioctl()");
         return 0;
     }
-    memset(sess, 0, sizeof(struct session_op));
+    memset(sess, 0, sizeof(*sess));
     return 1;
 }
 
@@ -90,7 +104,7 @@ static int clean_devcrypto_session(struct session_op *sess) {
  *****/
 
 struct cipher_ctx {
-    struct session_op sess;
+    session_op_t sess;
     int op;                      /* COP_ENCRYPT or COP_DECRYPT */
     unsigned long mode;          /* EVP_CIPH_*_MODE */
 
@@ -194,7 +208,8 @@ static int cipher_init(EVP_CIPHER_CTX *ctx, const unsigned char *key,
     struct cipher_ctx *cipher_ctx =
         (struct cipher_ctx *)EVP_CIPHER_CTX_get_cipher_data(ctx);
     const struct cipher_data_st *cipher_d =
-        get_cipher_data(EVP_CIPHER_CTX_nid(ctx));
+        get_cipher_data(EVP_CIPHER_CTX_get_nid(ctx));
+    int ret;
 
     /* cleanup a previous session */
     if (cipher_ctx->sess.ses != 0 &&
@@ -207,8 +222,16 @@ static int cipher_init(EVP_CIPHER_CTX *ctx, const unsigned char *key,
     cipher_ctx->op = enc ? COP_ENCRYPT : COP_DECRYPT;
     cipher_ctx->mode = cipher_d->flags & EVP_CIPH_MODE;
     cipher_ctx->blocksize = cipher_d->blocksize;
-    if (ioctl(cfd, CIOCGSESSION, &cipher_ctx->sess) < 0) {
-        SYSerr("ioctl", errno);
+#ifdef CIOCGSESSION2
+    cipher_ctx->sess.crid = (use_softdrivers == DEVCRYPTO_USE_SOFTWARE) ?
+        CRYPTO_FLAG_SOFTWARE | CRYPTO_FLAG_HARDWARE :
+        CRYPTO_FLAG_HARDWARE;
+    ret = ioctl(cfd, CIOCGSESSION2, &cipher_ctx->sess);
+#else
+    ret = ioctl(cfd, CIOCGSESSION, &cipher_ctx->sess);
+#endif
+    if (ret < 0) {
+        ERR_raise_data(ERR_LIB_SYS, errno, "calling ioctl()");
         return 0;
     }
 
@@ -238,12 +261,12 @@ static int cipher_do_cipher(EVP_CIPHER_CTX *ctx, unsigned char *out,
 #if !defined(COP_FLAG_WRITE_IV)
     cryp.flags = 0;
 
-    ivlen = EVP_CIPHER_CTX_iv_length(ctx);
+    ivlen = EVP_CIPHER_CTX_get_iv_length(ctx);
     if (ivlen > 0)
         switch (cipher_ctx->mode) {
         case EVP_CIPH_CBC_MODE:
             assert(inl >= ivlen);
-            if (!EVP_CIPHER_CTX_encrypting(ctx)) {
+            if (!EVP_CIPHER_CTX_is_encrypting(ctx)) {
                 ivptr = in + inl - ivlen;
                 memcpy(saved_iv, ivptr, ivlen);
             }
@@ -260,7 +283,7 @@ static int cipher_do_cipher(EVP_CIPHER_CTX *ctx, unsigned char *out,
 #endif
 
     if (ioctl(cfd, CIOCCRYPT, &cryp) < 0) {
-        SYSerr("ioctl", errno);
+        ERR_raise_data(ERR_LIB_SYS, errno, "calling ioctl()");
         return 0;
     }
 
@@ -269,7 +292,7 @@ static int cipher_do_cipher(EVP_CIPHER_CTX *ctx, unsigned char *out,
         switch (cipher_ctx->mode) {
         case EVP_CIPH_CBC_MODE:
             assert(inl >= ivlen);
-            if (EVP_CIPHER_CTX_encrypting(ctx))
+            if (EVP_CIPHER_CTX_is_encrypting(ctx))
                 ivptr = out + inl - ivlen;
             else
                 ivptr = saved_iv;
@@ -311,7 +334,7 @@ static int ctr_do_cipher(EVP_CIPHER_CTX *ctx, unsigned char *out,
     }
 
     /* full blocks */
-    if (inl > (unsigned int) cipher_ctx->blocksize) {
+    if (inl > cipher_ctx->blocksize) {
         nblocks = inl/cipher_ctx->blocksize;
         len = nblocks * cipher_ctx->blocksize;
         if (cipher_do_cipher(ctx, out, in, len) < 1)
@@ -403,9 +426,12 @@ static int devcrypto_test_cipher(size_t cipher_data_index)
 static void prepare_cipher_methods(void)
 {
     size_t i;
-    struct session_op sess;
+    session_op_t sess;
     unsigned long cipher_mode;
-#ifdef CIOCGSESSINFO
+#ifdef CIOCGSESSION2
+    struct crypt_find_op fop;
+    enum devcrypto_accelerated_t accelerated;
+#elif defined(CIOCGSESSINFO)
     struct session_info_op siop;
 #endif
 
@@ -423,10 +449,29 @@ static void prepare_cipher_methods(void)
          */
         sess.cipher = cipher_data[i].devcryptoid;
         sess.keylen = cipher_data[i].keylen;
+#ifdef CIOCGSESSION2
+        /*
+         * When using CIOCGSESSION2, first try to allocate a hardware
+         * ("accelerated") session.  If that fails, fall back to
+         * allocating a software session.
+         */
+        sess.crid = CRYPTO_FLAG_HARDWARE;
+        if (ioctl(cfd, CIOCGSESSION2, &sess) == 0) {
+            accelerated = DEVCRYPTO_ACCELERATED;
+        } else {
+            sess.crid = CRYPTO_FLAG_SOFTWARE;
+            if (ioctl(cfd, CIOCGSESSION2, &sess) < 0) {
+                cipher_driver_info[i].status = DEVCRYPTO_STATUS_NO_CIOCGSESSION;
+                continue;
+            }
+            accelerated = DEVCRYPTO_NOT_ACCELERATED;
+        }
+#else
         if (ioctl(cfd, CIOCGSESSION, &sess) < 0) {
             cipher_driver_info[i].status = DEVCRYPTO_STATUS_NO_CIOCGSESSION;
             continue;
         }
+#endif
 
         cipher_mode = cipher_data[i].flags & EVP_CIPH_MODE;
 
@@ -457,7 +502,14 @@ static void prepare_cipher_methods(void)
             known_cipher_methods[i] = NULL;
         } else {
             cipher_driver_info[i].status = DEVCRYPTO_STATUS_USABLE;
-#ifdef CIOCGSESSINFO
+#ifdef CIOCGSESSION2
+            cipher_driver_info[i].accelerated = accelerated;
+            fop.crid = sess.crid;
+            if (ioctl(cfd, CIOCFINDDEV, &fop) == 0) {
+                cipher_driver_info[i].driver_name =
+                    OPENSSL_strndup(fop.name, sizeof(fop.name));
+            }
+#elif defined(CIOCGSESSINFO)
             siop.ses = sess.ses;
             if (ioctl(cfd, CIOCGSESSINFO, &siop) < 0) {
                 cipher_driver_info[i].accelerated = DEVCRYPTO_ACCELERATION_UNKNOWN;
@@ -559,7 +611,7 @@ static int cryptodev_select_cipher_cb(const char *str, int len, void *usr)
     EVP = EVP_get_cipherbyname(name);
     if (EVP == NULL)
         fprintf(stderr, "devcrypto: unknown cipher %s\n", name);
-    else if ((i = find_cipher_data_index(EVP_CIPHER_nid(EVP))) != (size_t)-1)
+    else if ((i = find_cipher_data_index(EVP_CIPHER_get_nid(EVP))) != (size_t)-1)
         cipher_list[i] = 1;
     else
         fprintf(stderr, "devcrypto: cipher %s not available\n", name);
@@ -582,7 +634,7 @@ static void dump_cipher_info(void)
         fprintf (stderr, "Cipher %s, NID=%d, /dev/crypto info: id=%d, ",
                  name ? name : "unknown", cipher_data[i].nid,
                  cipher_data[i].devcryptoid);
-        if (cipher_driver_info[i].status == DEVCRYPTO_STATUS_NO_CIOCGSESSION ) {
+        if (cipher_driver_info[i].status == DEVCRYPTO_STATUS_NO_CIOCGSESSION) {
             fprintf (stderr, "CIOCGSESSION (session open call) failed\n");
             continue;
         }
@@ -621,7 +673,7 @@ static void dump_cipher_info(void)
  *****/
 
 struct digest_ctx {
-    struct session_op sess;
+    session_op_t sess;
     /* This signals that the init function was called, not that it succeeded. */
     int init_called;
     unsigned char digest_res[HASH_MAX_LEN];
@@ -695,16 +747,16 @@ static const struct digest_data_st *get_digest_data(int nid)
 static int digest_init(EVP_MD_CTX *ctx)
 {
     struct digest_ctx *digest_ctx =
-        (struct digest_ctx *)EVP_MD_CTX_md_data(ctx);
+        (struct digest_ctx *)EVP_MD_CTX_get0_md_data(ctx);
     const struct digest_data_st *digest_d =
-        get_digest_data(EVP_MD_CTX_type(ctx));
+        get_digest_data(EVP_MD_CTX_get_type(ctx));
 
     digest_ctx->init_called = 1;
 
     memset(&digest_ctx->sess, 0, sizeof(digest_ctx->sess));
     digest_ctx->sess.mac = digest_d->devcryptoid;
     if (ioctl(cfd, CIOCGSESSION, &digest_ctx->sess) < 0) {
-        SYSerr("ioctl", errno);
+        ERR_raise_data(ERR_LIB_SYS, errno, "calling ioctl()");
         return 0;
     }
     return 1;
@@ -728,7 +780,7 @@ static int digest_op(struct digest_ctx *ctx, const void *src, size_t srclen,
 static int digest_update(EVP_MD_CTX *ctx, const void *data, size_t count)
 {
     struct digest_ctx *digest_ctx =
-        (struct digest_ctx *)EVP_MD_CTX_md_data(ctx);
+        (struct digest_ctx *)EVP_MD_CTX_get0_md_data(ctx);
 
     if (count == 0)
         return 1;
@@ -743,22 +795,22 @@ static int digest_update(EVP_MD_CTX *ctx, const void *data, size_t count)
         return 1;
     }
 
-    SYSerr("ioctl", errno);
+    ERR_raise_data(ERR_LIB_SYS, errno, "calling ioctl()");
     return 0;
 }
 
 static int digest_final(EVP_MD_CTX *ctx, unsigned char *md)
 {
     struct digest_ctx *digest_ctx =
-        (struct digest_ctx *)EVP_MD_CTX_md_data(ctx);
+        (struct digest_ctx *)EVP_MD_CTX_get0_md_data(ctx);
 
     if (md == NULL || digest_ctx == NULL)
         return 0;
 
     if (EVP_MD_CTX_test_flags(ctx, EVP_MD_CTX_FLAG_ONESHOT)) {
-        memcpy(md, digest_ctx->digest_res, EVP_MD_CTX_size(ctx));
+        memcpy(md, digest_ctx->digest_res, EVP_MD_CTX_get_size(ctx));
     } else if (digest_op(digest_ctx, NULL, 0, md, COP_FLAG_FINAL) < 0) {
-        SYSerr("ioctl", errno);
+        ERR_raise_data(ERR_LIB_SYS, errno, "calling ioctl()");
         return 0;
     }
 
@@ -768,23 +820,23 @@ static int digest_final(EVP_MD_CTX *ctx, unsigned char *md)
 static int digest_copy(EVP_MD_CTX *to, const EVP_MD_CTX *from)
 {
     struct digest_ctx *digest_from =
-        (struct digest_ctx *)EVP_MD_CTX_md_data(from);
+        (struct digest_ctx *)EVP_MD_CTX_get0_md_data(from);
     struct digest_ctx *digest_to =
-        (struct digest_ctx *)EVP_MD_CTX_md_data(to);
+        (struct digest_ctx *)EVP_MD_CTX_get0_md_data(to);
     struct cphash_op cphash;
 
     if (digest_from == NULL || digest_from->init_called != 1)
         return 1;
 
     if (!digest_init(to)) {
-        SYSerr("ioctl", errno);
+        ERR_raise_data(ERR_LIB_SYS, errno, "calling ioctl()");
         return 0;
     }
 
     cphash.src_ses = digest_from->sess.ses;
     cphash.dst_ses = digest_to->sess.ses;
     if (ioctl(cfd, CIOCCPHASH, &cphash) < 0) {
-        SYSerr("ioctl", errno);
+        ERR_raise_data(ERR_LIB_SYS, errno, "calling ioctl()");
         return 0;
     }
     return 1;
@@ -793,7 +845,7 @@ static int digest_copy(EVP_MD_CTX *to, const EVP_MD_CTX *from)
 static int digest_cleanup(EVP_MD_CTX *ctx)
 {
     struct digest_ctx *digest_ctx =
-        (struct digest_ctx *)EVP_MD_CTX_md_data(ctx);
+        (struct digest_ctx *)EVP_MD_CTX_get0_md_data(ctx);
 
     if (digest_ctx == NULL)
         return 1;
@@ -840,7 +892,7 @@ static void rebuild_known_digest_nids(ENGINE *e)
 static void prepare_digest_methods(void)
 {
     size_t i;
-    struct session_op sess1, sess2;
+    session_op_t sess1, sess2;
 #ifdef CIOCGSESSINFO
     struct session_info_op siop;
 #endif
@@ -989,7 +1041,7 @@ static int cryptodev_select_digest_cb(const char *str, int len, void *usr)
     EVP = EVP_get_digestbyname(name);
     if (EVP == NULL)
         fprintf(stderr, "devcrypto: unknown digest %s\n", name);
-    else if ((i = find_digest_data_index(EVP_MD_type(EVP))) != (size_t)-1)
+    else if ((i = find_digest_data_index(EVP_MD_get_type(EVP))) != (size_t)-1)
         digest_list[i] = 1;
     else
         fprintf(stderr, "devcrypto: digest %s not available\n", name);
@@ -1048,7 +1100,7 @@ static void dump_digest_info(void)
 #define DEVCRYPTO_CMD_DUMP_INFO (ENGINE_CMD_BASE + 3)
 
 static const ENGINE_CMD_DEFN devcrypto_cmds[] = {
-#ifdef CIOCGSESSINFO
+#if defined(CIOCGSESSINFO) || defined(CIOCGSESSION2)
    {DEVCRYPTO_CMD_USE_SOFTDRIVERS,
     "USE_SOFTDRIVERS",
     "specifies whether to use software (not accelerated) drivers ("
@@ -1084,7 +1136,7 @@ static int devcrypto_ctrl(ENGINE *e, int cmd, long i, void *p, void (*f) (void))
 {
     int *new_list;
     switch (cmd) {
-#ifdef CIOCGSESSINFO
+#if defined(CIOCGSESSINFO) || defined(CIOCGSESSION2)
     case DEVCRYPTO_CMD_USE_SOFTDRIVERS:
         switch (i) {
         case DEVCRYPTO_REQUIRE_ACCELERATED:
@@ -1103,14 +1155,14 @@ static int devcrypto_ctrl(ENGINE *e, int cmd, long i, void *p, void (*f) (void))
 #endif
         rebuild_known_cipher_nids(e);
         return 1;
-#endif /* CIOCGSESSINFO */
+#endif /* CIOCGSESSINFO || CIOCGSESSION2 */
 
     case DEVCRYPTO_CMD_CIPHERS:
         if (p == NULL)
             return 1;
-        if (strcasecmp((const char *)p, "ALL") == 0) {
+        if (OPENSSL_strcasecmp((const char *)p, "ALL") == 0) {
             devcrypto_select_all_ciphers(selected_ciphers);
-        } else if (strcasecmp((const char*)p, "NONE") == 0) {
+        } else if (OPENSSL_strcasecmp((const char*)p, "NONE") == 0) {
             memset(selected_ciphers, 0, sizeof(selected_ciphers));
         } else {
             new_list=OPENSSL_zalloc(sizeof(selected_ciphers));
@@ -1128,9 +1180,9 @@ static int devcrypto_ctrl(ENGINE *e, int cmd, long i, void *p, void (*f) (void))
     case DEVCRYPTO_CMD_DIGESTS:
         if (p == NULL)
             return 1;
-        if (strcasecmp((const char *)p, "ALL") == 0) {
+        if (OPENSSL_strcasecmp((const char *)p, "ALL") == 0) {
             devcrypto_select_all_digests(selected_digests);
-        } else if (strcasecmp((const char*)p, "NONE") == 0) {
+        } else if (OPENSSL_strcasecmp((const char*)p, "NONE") == 0) {
             memset(selected_digests, 0, sizeof(selected_digests));
         } else {
             new_list=OPENSSL_zalloc(sizeof(selected_digests));
@@ -1169,17 +1221,31 @@ static int devcrypto_ctrl(ENGINE *e, int cmd, long i, void *p, void (*f) (void))
  */
 static int open_devcrypto(void)
 {
+    int fd;
+
     if (cfd >= 0)
         return 1;
 
-    if ((cfd = open("/dev/crypto", O_RDWR, 0)) < 0) {
+    if ((fd = open("/dev/crypto", O_RDWR, 0)) < 0) {
 #ifndef ENGINE_DEVCRYPTO_DEBUG
-        if (errno != ENOENT)
+        if (errno != ENOENT && errno != ENXIO)
 #endif
             fprintf(stderr, "Could not open /dev/crypto: %s\n", strerror(errno));
         return 0;
     }
 
+#ifdef CRIOGET
+    if (ioctl(fd, CRIOGET, &cfd) < 0) {
+        fprintf(stderr, "Could not create crypto fd: %s\n", strerror(errno));
+        close(fd);
+        cfd = -1;
+        return 0;
+    }
+    close(fd);
+#else
+    cfd = fd;
+#endif
+
     return 1;
 }
 
@@ -1249,9 +1315,7 @@ static int bind_devcrypto(ENGINE *e) {
  * /Richard Levitte, 2017-05-11
  */
 #if 0
-# ifndef OPENSSL_NO_RSA
         && ENGINE_set_RSA(e, devcrypto_rsa)
-# endif
 # ifndef OPENSSL_NO_DSA
         && ENGINE_set_DSA(e, devcrypto_dsa)
 # endif
@@ -1284,9 +1348,19 @@ void engine_load_devcrypto_int(void)
         return;
     }
 
+    ERR_set_mark();
     ENGINE_add(e);
+    /*
+     * If the "add" worked, it gets a structural reference. So either way, we
+     * release our just-created reference.
+     */
     ENGINE_free(e);          /* Loose our local reference */
-    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();
 }
 
 #else