Raise an error on syscall failure in tls_retry_write_records
[openssl.git] / crypto / param_build.c
index 21bed31393fd8be89acc27e2a6acf74aadb69882..3294045156fa9ac7b304cb643ab801f2b3a46756 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright 2019 The OpenSSL Project Authors. All Rights Reserved.
+ * Copyright 2019-2023 The OpenSSL Project Authors. All Rights Reserved.
  * Copyright (c) 2019, Oracle and/or its affiliates.  All rights reserved.
  *
  * Licensed under the Apache License 2.0 (the "License").  You may not use
 #include <openssl/err.h>
 #include <openssl/cryptoerr.h>
 #include <openssl/params.h>
-#include "internal/cryptlib.h"
-#include "internal/param_build.h"
+#include <openssl/types.h>
+#include <openssl/safestack.h>
+#include "internal/param_build_set.h"
 
-#define OSSL_PARAM_ALLOCATED_END    127
-
-typedef union {
-    OSSL_UNION_ALIGN;
-} OSSL_PARAM_BLD_BLOCK;
+/*
+ * Special internal param type to indicate the end of an allocate OSSL_PARAM
+ * array.
+ */
 
-#define ALIGN_SIZE  sizeof(OSSL_PARAM_BLD_BLOCK)
-
-static size_t bytes_to_blocks(size_t bytes)
-{
-    return (bytes + ALIGN_SIZE - 1) / ALIGN_SIZE;
-}
+typedef struct {
+    const char *key;
+    int type;
+    int secure;
+    size_t size;
+    size_t alloc_blocks;
+    const BIGNUM *bn;
+    const void *string;
+    union {
+        /*
+         * These fields are never directly addressed, but their sizes are
+         * important so that all native types can be copied here without overrun.
+         */
+        ossl_intmax_t i;
+        ossl_uintmax_t u;
+        double d;
+    } num;
+} OSSL_PARAM_BLD_DEF;
+
+DEFINE_STACK_OF(OSSL_PARAM_BLD_DEF)
+
+struct ossl_param_bld_st {
+    size_t total_blocks;
+    size_t secure_blocks;
+    STACK_OF(OSSL_PARAM_BLD_DEF) *params;
+};
 
 static OSSL_PARAM_BLD_DEF *param_push(OSSL_PARAM_BLD *bld, const char *key,
-                                      int size, size_t alloc, int type,
+                                      size_t size, size_t alloc, int type,
                                       int secure)
 {
-    OSSL_PARAM_BLD_DEF *pd;
+    OSSL_PARAM_BLD_DEF *pd = OPENSSL_zalloc(sizeof(*pd));
 
-    if (bld->curr >= OSSL_PARAM_BLD_MAX) {
-        CRYPTOerr(CRYPTO_F_PARAM_PUSH, CRYPTO_R_TOO_MANY_RECORDS);
+    if (pd == NULL)
         return NULL;
-    }
-    pd = bld->params + bld->curr++;
-    memset(pd, 0, sizeof(*pd));
     pd->key = key;
     pd->type = type;
     pd->size = size;
-    pd->alloc_blocks = bytes_to_blocks(size);
+    pd->alloc_blocks = ossl_param_bytes_to_blocks(alloc);
     if ((pd->secure = secure) != 0)
         bld->secure_blocks += pd->alloc_blocks;
     else
         bld->total_blocks += pd->alloc_blocks;
+    if (sk_OSSL_PARAM_BLD_DEF_push(bld->params, pd) <= 0) {
+        OPENSSL_free(pd);
+        pd = NULL;
+    }
     return pd;
 }
 
@@ -56,149 +76,202 @@ static int param_push_num(OSSL_PARAM_BLD *bld, const char *key,
 {
     OSSL_PARAM_BLD_DEF *pd = param_push(bld, key, size, size, type, 0);
 
-    if (pd == NULL)
+    if (pd == NULL) {
+        ERR_raise(ERR_LIB_CRYPTO, ERR_R_PASSED_NULL_PARAMETER);
         return 0;
+    }
     if (size > sizeof(pd->num)) {
-        CRYPTOerr(CRYPTO_F_PARAM_PUSH_NUM, CRYPTO_R_TOO_MANY_BYTES);
+        ERR_raise(ERR_LIB_CRYPTO, CRYPTO_R_TOO_MANY_BYTES);
         return 0;
     }
     memcpy(&pd->num, num, size);
     return 1;
 }
 
-void ossl_param_bld_init(OSSL_PARAM_BLD *bld)
+OSSL_PARAM_BLD *OSSL_PARAM_BLD_new(void)
+{
+    OSSL_PARAM_BLD *r = OPENSSL_zalloc(sizeof(OSSL_PARAM_BLD));
+
+    if (r != NULL) {
+        r->params = sk_OSSL_PARAM_BLD_DEF_new_null();
+        if (r->params == NULL) {
+            OPENSSL_free(r);
+            r = NULL;
+        }
+    }
+    return r;
+}
+
+static void free_all_params(OSSL_PARAM_BLD *bld)
+{
+    int i, n = sk_OSSL_PARAM_BLD_DEF_num(bld->params);
+
+    for (i = 0; i < n; i++)
+        OPENSSL_free(sk_OSSL_PARAM_BLD_DEF_pop(bld->params));
+}
+
+void OSSL_PARAM_BLD_free(OSSL_PARAM_BLD *bld)
 {
-    memset(bld, 0, sizeof(*bld));
+    if (bld == NULL)
+        return;
+    free_all_params(bld);
+    sk_OSSL_PARAM_BLD_DEF_free(bld->params);
+    OPENSSL_free(bld);
 }
 
-int ossl_param_bld_push_int(OSSL_PARAM_BLD *bld, const char *key, int num)
+int OSSL_PARAM_BLD_push_int(OSSL_PARAM_BLD *bld, const char *key, int num)
 {
     return param_push_num(bld, key, &num, sizeof(num), OSSL_PARAM_INTEGER);
 }
 
-int ossl_param_bld_push_uint(OSSL_PARAM_BLD *bld, const char *key,
+int OSSL_PARAM_BLD_push_uint(OSSL_PARAM_BLD *bld, const char *key,
                              unsigned int num)
 {
     return param_push_num(bld, key, &num, sizeof(num),
                           OSSL_PARAM_UNSIGNED_INTEGER);
 }
 
-int ossl_param_bld_push_long(OSSL_PARAM_BLD *bld, const char *key,
+int OSSL_PARAM_BLD_push_long(OSSL_PARAM_BLD *bld, const char *key,
                              long int num)
 {
     return param_push_num(bld, key, &num, sizeof(num), OSSL_PARAM_INTEGER);
 }
 
-int ossl_param_bld_push_ulong(OSSL_PARAM_BLD *bld, const char *key,
+int OSSL_PARAM_BLD_push_ulong(OSSL_PARAM_BLD *bld, const char *key,
                               unsigned long int num)
 {
     return param_push_num(bld, key, &num, sizeof(num),
                           OSSL_PARAM_UNSIGNED_INTEGER);
 }
 
-int ossl_param_bld_push_int32(OSSL_PARAM_BLD *bld, const char *key,
+int OSSL_PARAM_BLD_push_int32(OSSL_PARAM_BLD *bld, const char *key,
                               int32_t num)
 {
     return param_push_num(bld, key, &num, sizeof(num), OSSL_PARAM_INTEGER);
 }
 
-int ossl_param_bld_push_uint32(OSSL_PARAM_BLD *bld, const char *key,
+int OSSL_PARAM_BLD_push_uint32(OSSL_PARAM_BLD *bld, const char *key,
                                uint32_t num)
 {
     return param_push_num(bld, key, &num, sizeof(num),
                           OSSL_PARAM_UNSIGNED_INTEGER);
 }
 
-int ossl_param_bld_push_int64(OSSL_PARAM_BLD *bld, const char *key,
+int OSSL_PARAM_BLD_push_int64(OSSL_PARAM_BLD *bld, const char *key,
                               int64_t num)
 {
     return param_push_num(bld, key, &num, sizeof(num), OSSL_PARAM_INTEGER);
 }
 
-int ossl_param_bld_push_uint64(OSSL_PARAM_BLD *bld, const char *key,
+int OSSL_PARAM_BLD_push_uint64(OSSL_PARAM_BLD *bld, const char *key,
                                uint64_t num)
 {
     return param_push_num(bld, key, &num, sizeof(num),
                           OSSL_PARAM_UNSIGNED_INTEGER);
 }
 
-int ossl_param_bld_push_size_t(OSSL_PARAM_BLD *bld, const char *key,
+int OSSL_PARAM_BLD_push_size_t(OSSL_PARAM_BLD *bld, const char *key,
                                size_t num)
 {
     return param_push_num(bld, key, &num, sizeof(num),
                           OSSL_PARAM_UNSIGNED_INTEGER);
 }
 
-int ossl_param_bld_push_double(OSSL_PARAM_BLD *bld, const char *key,
-                               double num)
+int OSSL_PARAM_BLD_push_time_t(OSSL_PARAM_BLD *bld, const char *key,
+                               time_t num)
 {
-    return param_push_num(bld, key, &num, sizeof(num), OSSL_PARAM_REAL);
+    return param_push_num(bld, key, &num, sizeof(num),
+                          OSSL_PARAM_INTEGER);
 }
 
-int ossl_param_bld_push_BN(OSSL_PARAM_BLD *bld, const char *key,
-                           const BIGNUM *bn)
+int OSSL_PARAM_BLD_push_double(OSSL_PARAM_BLD *bld, const char *key,
+                               double num)
 {
-    return ossl_param_bld_push_BN_pad(bld, key, bn,
-                                      bn == NULL ? 0 : BN_num_bytes(bn));
+    return param_push_num(bld, key, &num, sizeof(num), OSSL_PARAM_REAL);
 }
 
-int ossl_param_bld_push_BN_pad(OSSL_PARAM_BLD *bld, const char *key,
-                               const BIGNUM *bn, size_t sz)
+static int push_BN(OSSL_PARAM_BLD *bld, const char *key,
+                   const BIGNUM *bn, size_t sz, int type)
 {
     int n, secure = 0;
     OSSL_PARAM_BLD_DEF *pd;
 
+    if (!ossl_assert(type == OSSL_PARAM_UNSIGNED_INTEGER
+                     || type == OSSL_PARAM_INTEGER))
+        return 0;
+
     if (bn != NULL) {
+        if (type == OSSL_PARAM_UNSIGNED_INTEGER && BN_is_negative(bn)) {
+            ERR_raise_data(ERR_LIB_CRYPTO, ERR_R_UNSUPPORTED,
+                           "Negative big numbers are unsupported for OSSL_PARAM_UNSIGNED_INTEGER");
+            return 0;
+        }
+
         n = BN_num_bytes(bn);
         if (n < 0) {
-            CRYPTOerr(0, CRYPTO_R_ZERO_LENGTH_NUMBER);
+            ERR_raise(ERR_LIB_CRYPTO, CRYPTO_R_ZERO_LENGTH_NUMBER);
             return 0;
         }
         if (sz < (size_t)n) {
-            CRYPTOerr(0, CRYPTO_R_TOO_SMALL_BUFFER);
+            ERR_raise(ERR_LIB_CRYPTO, CRYPTO_R_TOO_SMALL_BUFFER);
             return 0;
         }
         if (BN_get_flags(bn, BN_FLG_SECURE) == BN_FLG_SECURE)
             secure = 1;
+
+        /* The BIGNUM is zero, we must transfer at least one byte */
+        if (sz == 0)
+            sz++;
     }
-    pd = param_push(bld, key, sz, sz, OSSL_PARAM_UNSIGNED_INTEGER, secure);
+    pd = param_push(bld, key, sz, sz, type, secure);
     if (pd == NULL)
         return 0;
     pd->bn = bn;
     return 1;
 }
 
-int ossl_param_bld_push_utf8_string(OSSL_PARAM_BLD *bld, const char *key,
+int OSSL_PARAM_BLD_push_BN(OSSL_PARAM_BLD *bld, const char *key,
+                           const BIGNUM *bn)
+{
+    if (bn != NULL && BN_is_negative(bn))
+        return push_BN(bld, key, bn, BN_num_bytes(bn) + 1,
+                       OSSL_PARAM_INTEGER);
+    return push_BN(bld, key, bn, bn == NULL ? 0 : BN_num_bytes(bn),
+                   OSSL_PARAM_UNSIGNED_INTEGER);
+}
+
+int OSSL_PARAM_BLD_push_BN_pad(OSSL_PARAM_BLD *bld, const char *key,
+                               const BIGNUM *bn, size_t sz)
+{
+    if (bn != NULL && BN_is_negative(bn))
+        return push_BN(bld, key, bn, BN_num_bytes(bn),
+                       OSSL_PARAM_INTEGER);
+    return push_BN(bld, key, bn, sz, OSSL_PARAM_UNSIGNED_INTEGER);
+}
+
+int OSSL_PARAM_BLD_push_utf8_string(OSSL_PARAM_BLD *bld, const char *key,
                                     const char *buf, size_t bsize)
 {
     OSSL_PARAM_BLD_DEF *pd;
+    int secure;
 
-    if (bsize == 0) {
-        bsize = strlen(buf) + 1;
-    } else if (bsize > INT_MAX) {
-        CRYPTOerr(CRYPTO_F_OSSL_PARAM_BLD_PUSH_UTF8_STRING,
-                  CRYPTO_R_STRING_TOO_LONG);
-        return 0;
-    }
-    pd = param_push(bld, key, bsize, bsize, OSSL_PARAM_UTF8_STRING, 0);
+    if (bsize == 0)
+        bsize = strlen(buf);
+    secure = CRYPTO_secure_allocated(buf);
+    pd = param_push(bld, key, bsize, bsize + 1, OSSL_PARAM_UTF8_STRING, secure);
     if (pd == NULL)
         return 0;
     pd->string = buf;
     return 1;
 }
 
-int ossl_param_bld_push_utf8_ptr(OSSL_PARAM_BLD *bld, const char *key,
+int OSSL_PARAM_BLD_push_utf8_ptr(OSSL_PARAM_BLD *bld, const char *key,
                                  char *buf, size_t bsize)
 {
     OSSL_PARAM_BLD_DEF *pd;
 
-    if (bsize == 0) {
-        bsize = strlen(buf) + 1;
-    } else if (bsize > INT_MAX) {
-        CRYPTOerr(CRYPTO_F_OSSL_PARAM_BLD_PUSH_UTF8_PTR,
-                  CRYPTO_R_STRING_TOO_LONG);
-        return 0;
-    }
+    if (bsize == 0)
+        bsize = strlen(buf);
     pd = param_push(bld, key, bsize, sizeof(buf), OSSL_PARAM_UTF8_PTR, 0);
     if (pd == NULL)
         return 0;
@@ -206,33 +279,25 @@ int ossl_param_bld_push_utf8_ptr(OSSL_PARAM_BLD *bld, const char *key,
     return 1;
 }
 
-int ossl_param_bld_push_octet_string(OSSL_PARAM_BLD *bld, const char *key,
+int OSSL_PARAM_BLD_push_octet_string(OSSL_PARAM_BLD *bld, const char *key,
                                      const void *buf, size_t bsize)
 {
     OSSL_PARAM_BLD_DEF *pd;
+    int secure;
 
-    if (bsize > INT_MAX) {
-        CRYPTOerr(CRYPTO_F_OSSL_PARAM_BLD_PUSH_OCTET_STRING,
-                  CRYPTO_R_STRING_TOO_LONG);
-        return 0;
-    }
-    pd = param_push(bld, key, bsize, bsize, OSSL_PARAM_OCTET_STRING, 0);
+    secure = CRYPTO_secure_allocated(buf);
+    pd = param_push(bld, key, bsize, bsize, OSSL_PARAM_OCTET_STRING, secure);
     if (pd == NULL)
         return 0;
     pd->string = buf;
     return 1;
 }
 
-int ossl_param_bld_push_octet_ptr(OSSL_PARAM_BLD *bld, const char *key,
+int OSSL_PARAM_BLD_push_octet_ptr(OSSL_PARAM_BLD *bld, const char *key,
                                   void *buf, size_t bsize)
 {
     OSSL_PARAM_BLD_DEF *pd;
 
-    if (bsize > INT_MAX) {
-        CRYPTOerr(CRYPTO_F_OSSL_PARAM_BLD_PUSH_OCTET_PTR,
-                  CRYPTO_R_STRING_TOO_LONG);
-        return 0;
-    }
     pd = param_push(bld, key, bsize, sizeof(buf), OSSL_PARAM_OCTET_PTR, 0);
     if (pd == NULL)
         return 0;
@@ -241,19 +306,19 @@ int ossl_param_bld_push_octet_ptr(OSSL_PARAM_BLD *bld, const char *key,
 }
 
 static OSSL_PARAM *param_bld_convert(OSSL_PARAM_BLD *bld, OSSL_PARAM *param,
-                                     OSSL_PARAM_BLD_BLOCK *blk,
-                                     OSSL_PARAM_BLD_BLOCK *secure)
+                                     OSSL_PARAM_ALIGNED_BLOCK *blk,
+                                     OSSL_PARAM_ALIGNED_BLOCK *secure)
 {
-    size_t i;
+    int i, num = sk_OSSL_PARAM_BLD_DEF_num(bld->params);
     OSSL_PARAM_BLD_DEF *pd;
     void *p;
 
-    for (i = 0; i < bld->curr; i++) {
-        pd = bld->params + i;
+    for (i = 0; i < num; i++) {
+        pd = sk_OSSL_PARAM_BLD_DEF_value(bld->params, i);
         param[i].key = pd->key;
         param[i].data_type = pd->type;
         param[i].data_size = pd->size;
-        param[i].return_size = 0;
+        param[i].return_size = OSSL_PARAM_UNMODIFIED;
 
         if (pd->secure) {
             p = secure;
@@ -265,7 +330,10 @@ static OSSL_PARAM *param_bld_convert(OSSL_PARAM_BLD *bld, OSSL_PARAM *param,
         param[i].data = p;
         if (pd->bn != NULL) {
             /* BIGNUM */
-            BN_bn2nativepad(pd->bn, (unsigned char *)p, pd->size);
+            if (pd->type == OSSL_PARAM_UNSIGNED_INTEGER)
+                BN_bn2nativepad(pd->bn, (unsigned char *)p, pd->size);
+            else
+                BN_signed_bn2native(pd->bn, (unsigned char *)p, pd->size);
         } else if (pd->type == OSSL_PARAM_OCTET_PTR
                    || pd->type == OSSL_PARAM_UTF8_PTR) {
             /* PTR */
@@ -276,6 +344,8 @@ static OSSL_PARAM *param_bld_convert(OSSL_PARAM_BLD *bld, OSSL_PARAM *param,
                 memcpy(p, pd->string, pd->size);
             else
                 memset(p, 0, pd->size);
+            if (pd->type == OSSL_PARAM_UTF8_STRING)
+                ((char *)p)[pd->size] = '\0';
         } else {
             /* Number, but could also be a NULL BIGNUM */
             if (pd->size > sizeof(pd->num))
@@ -288,75 +358,34 @@ static OSSL_PARAM *param_bld_convert(OSSL_PARAM_BLD *bld, OSSL_PARAM *param,
     return param + i;
 }
 
-OSSL_PARAM *ossl_param_bld_to_param(OSSL_PARAM_BLD *bld)
+OSSL_PARAM *OSSL_PARAM_BLD_to_param(OSSL_PARAM_BLD *bld)
 {
-    OSSL_PARAM_BLD_BLOCK *blk, *s = NULL;
+    OSSL_PARAM_ALIGNED_BLOCK *blk, *s = NULL;
     OSSL_PARAM *params, *last;
-    const size_t p_blks = bytes_to_blocks((1 + bld->curr) * sizeof(*params));
-    const size_t total = ALIGN_SIZE * (p_blks + bld->total_blocks);
-    const size_t ss = ALIGN_SIZE * bld->secure_blocks;
+    const int num = sk_OSSL_PARAM_BLD_DEF_num(bld->params);
+    const size_t p_blks = ossl_param_bytes_to_blocks((1 + num) * sizeof(*params));
+    const size_t total = OSSL_PARAM_ALIGN_SIZE * (p_blks + bld->total_blocks);
+    const size_t ss = OSSL_PARAM_ALIGN_SIZE * bld->secure_blocks;
 
     if (ss > 0) {
         s = OPENSSL_secure_malloc(ss);
         if (s == NULL) {
-            CRYPTOerr(CRYPTO_F_OSSL_PARAM_BLD_TO_PARAM,
-                      CRYPTO_R_SECURE_MALLOC_FAILURE);
+            ERR_raise(ERR_LIB_CRYPTO, CRYPTO_R_SECURE_MALLOC_FAILURE);
             return NULL;
         }
     }
     params = OPENSSL_malloc(total);
     if (params == NULL) {
-        CRYPTOerr(CRYPTO_F_OSSL_PARAM_BLD_TO_PARAM, ERR_R_MALLOC_FAILURE);
         OPENSSL_secure_free(s);
         return NULL;
     }
-    blk = p_blks + (OSSL_PARAM_BLD_BLOCK *)(params);
+    blk = p_blks + (OSSL_PARAM_ALIGNED_BLOCK *)(params);
     last = param_bld_convert(bld, params, blk, s);
-    last->data_size = ss;
-    last->data = s;
-    last->data_type = OSSL_PARAM_ALLOCATED_END;
-    return params;
-}
-
-void ossl_param_bld_free(OSSL_PARAM *params)
-{
-    if (params != NULL) {
-        OSSL_PARAM *p;
-
-        for (p = params; p->key != NULL; p++)
-            ;
-        if (p->data_type == OSSL_PARAM_ALLOCATED_END)
-            OPENSSL_secure_clear_free(p->data, p->data_size);
-        OPENSSL_free(params);
-    }
-}
+    ossl_param_set_secure_block(last, s, ss);
 
-OSSL_PARAM *ossl_param_bld_to_param_ex(OSSL_PARAM_BLD *bld, OSSL_PARAM *params,
-                                       size_t param_n, void *data,
-                                       size_t data_n, void *secure,
-                                       size_t secure_n)
-{
-    if (params == NULL || data == NULL) {
-        CRYPTOerr(CRYPTO_F_OSSL_PARAM_BLD_TO_PARAM_EX,
-                  CRYPTO_R_INVALID_NULL_ARGUMENT);
-        return NULL;
-    }
-    if (param_n < bld->curr + 1) {
-        CRYPTOerr(CRYPTO_F_OSSL_PARAM_BLD_TO_PARAM_EX,
-                  CRYPTO_R_INSUFFICIENT_PARAM_SIZE);
-        return NULL;
-    }
-    if (data_n < ALIGN_SIZE * bld->total_blocks) {
-        CRYPTOerr(CRYPTO_F_OSSL_PARAM_BLD_TO_PARAM_EX,
-                  CRYPTO_R_INSUFFICIENT_DATA_SPACE);
-        return NULL;
-    }
-    if (bld->secure_blocks > 0 && secure_n < ALIGN_SIZE * bld->secure_blocks) {
-        CRYPTOerr(CRYPTO_F_OSSL_PARAM_BLD_TO_PARAM_EX,
-                  CRYPTO_R_INSUFFICIENT_SECURE_DATA_SPACE);
-        return NULL;
-    }
-    param_bld_convert(bld, params, (OSSL_PARAM_BLD_BLOCK *)data,
-                      (OSSL_PARAM_BLD_BLOCK *)secure);
+    /* Reset builder for reuse */
+    bld->total_blocks = 0;
+    bld->secure_blocks = 0;
+    free_all_params(bld);
     return params;
 }