diff options
Diffstat (limited to 'providers/implementations/rands/seeding/rand_vms.c')
-rw-r--r-- | providers/implementations/rands/seeding/rand_vms.c | 615 |
1 files changed, 615 insertions, 0 deletions
diff --git a/providers/implementations/rands/seeding/rand_vms.c b/providers/implementations/rands/seeding/rand_vms.c new file mode 100644 index 000000000000..f12ecb3b075e --- /dev/null +++ b/providers/implementations/rands/seeding/rand_vms.c @@ -0,0 +1,615 @@ +/* + * Copyright 2001-2022 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 + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +#include "e_os.h" + +#define __NEW_STARLET 1 /* New starlet definitions since VMS 7.0 */ +#include <unistd.h> +#include "internal/cryptlib.h" +#include <openssl/rand.h> +#include "crypto/rand.h" +#include "crypto/rand_pool.h" +#include "prov/seeding.h" +#include <descrip.h> +#include <dvidef.h> +#include <jpidef.h> +#include <rmidef.h> +#include <syidef.h> +#include <ssdef.h> +#include <starlet.h> +#include <efndef.h> +#include <gen64def.h> +#include <iosbdef.h> +#include <iledef.h> +#include <lib$routines.h> +#ifdef __DECC +# pragma message disable DOLLARID +#endif + +#include <dlfcn.h> /* SYS$GET_ENTROPY presence */ + +#ifndef OPENSSL_RAND_SEED_OS +# error "Unsupported seeding method configured; must be os" +#endif + +/* + * DATA COLLECTION METHOD + * ====================== + * + * This is a method to get low quality entropy. + * It works by collecting all kinds of statistical data that + * VMS offers and using them as random seed. + */ + +/* We need to make sure we have the right size pointer in some cases */ +#if __INITIAL_POINTER_SIZE == 64 +# pragma pointer_size save +# pragma pointer_size 32 +#endif +typedef uint32_t *uint32_t__ptr32; +#if __INITIAL_POINTER_SIZE == 64 +# pragma pointer_size restore +#endif + +struct item_st { + short length, code; /* length is number of bytes */ +}; + +static const struct item_st DVI_item_data[] = { + {4, DVI$_ERRCNT}, + {4, DVI$_REFCNT}, +}; + +static const struct item_st JPI_item_data[] = { + {4, JPI$_BUFIO}, + {4, JPI$_CPUTIM}, + {4, JPI$_DIRIO}, + {4, JPI$_IMAGECOUNT}, + {4, JPI$_PAGEFLTS}, + {4, JPI$_PID}, + {4, JPI$_PPGCNT}, + {4, JPI$_WSPEAK}, + /* + * Note: the direct result is just a 32-bit address. However, it points + * to a list of 4 32-bit words, so we make extra space for them so we can + * do in-place replacement of values + */ + {16, JPI$_FINALEXC}, +}; + +static const struct item_st JPI_item_data_64bit[] = { + {8, JPI$_LAST_LOGIN_I}, + {8, JPI$_LOGINTIM}, +}; + +static const struct item_st RMI_item_data[] = { + {4, RMI$_COLPG}, + {4, RMI$_MWAIT}, + {4, RMI$_CEF}, + {4, RMI$_PFW}, + {4, RMI$_LEF}, + {4, RMI$_LEFO}, + {4, RMI$_HIB}, + {4, RMI$_HIBO}, + {4, RMI$_SUSP}, + {4, RMI$_SUSPO}, + {4, RMI$_FPG}, + {4, RMI$_COM}, + {4, RMI$_COMO}, + {4, RMI$_CUR}, +#if defined __alpha + {4, RMI$_FRLIST}, + {4, RMI$_MODLIST}, +#endif + {4, RMI$_FAULTS}, + {4, RMI$_PREADS}, + {4, RMI$_PWRITES}, + {4, RMI$_PWRITIO}, + {4, RMI$_PREADIO}, + {4, RMI$_GVALFLTS}, + {4, RMI$_WRTINPROG}, + {4, RMI$_FREFLTS}, + {4, RMI$_DZROFLTS}, + {4, RMI$_SYSFAULTS}, + {4, RMI$_ISWPCNT}, + {4, RMI$_DIRIO}, + {4, RMI$_BUFIO}, + {4, RMI$_MBREADS}, + {4, RMI$_MBWRITES}, + {4, RMI$_LOGNAM}, + {4, RMI$_FCPCALLS}, + {4, RMI$_FCPREAD}, + {4, RMI$_FCPWRITE}, + {4, RMI$_FCPCACHE}, + {4, RMI$_FCPCPU}, + {4, RMI$_FCPHIT}, + {4, RMI$_FCPSPLIT}, + {4, RMI$_FCPFAULT}, + {4, RMI$_ENQNEW}, + {4, RMI$_ENQCVT}, + {4, RMI$_DEQ}, + {4, RMI$_BLKAST}, + {4, RMI$_ENQWAIT}, + {4, RMI$_ENQNOTQD}, + {4, RMI$_DLCKSRCH}, + {4, RMI$_DLCKFND}, + {4, RMI$_NUMLOCKS}, + {4, RMI$_NUMRES}, + {4, RMI$_ARRLOCPK}, + {4, RMI$_DEPLOCPK}, + {4, RMI$_ARRTRAPK}, + {4, RMI$_TRCNGLOS}, + {4, RMI$_RCVBUFFL}, + {4, RMI$_ENQNEWLOC}, + {4, RMI$_ENQNEWIN}, + {4, RMI$_ENQNEWOUT}, + {4, RMI$_ENQCVTLOC}, + {4, RMI$_ENQCVTIN}, + {4, RMI$_ENQCVTOUT}, + {4, RMI$_DEQLOC}, + {4, RMI$_DEQIN}, + {4, RMI$_DEQOUT}, + {4, RMI$_BLKLOC}, + {4, RMI$_BLKIN}, + {4, RMI$_BLKOUT}, + {4, RMI$_DIRIN}, + {4, RMI$_DIROUT}, + /* We currently get a fault when trying these */ +#if 0 + {140, RMI$_MSCP_EVERYTHING}, /* 35 32-bit words */ + {152, RMI$_DDTM_ALL}, /* 38 32-bit words */ + {80, RMI$_TMSCP_EVERYTHING} /* 20 32-bit words */ +#endif + {4, RMI$_LPZ_PAGCNT}, + {4, RMI$_LPZ_HITS}, + {4, RMI$_LPZ_MISSES}, + {4, RMI$_LPZ_EXPCNT}, + {4, RMI$_LPZ_ALLOCF}, + {4, RMI$_LPZ_ALLOC2}, + {4, RMI$_ACCESS}, + {4, RMI$_ALLOC}, + {4, RMI$_FCPCREATE}, + {4, RMI$_VOLWAIT}, + {4, RMI$_FCPTURN}, + {4, RMI$_FCPERASE}, + {4, RMI$_OPENS}, + {4, RMI$_FIDHIT}, + {4, RMI$_FIDMISS}, + {4, RMI$_FILHDR_HIT}, + {4, RMI$_DIRFCB_HIT}, + {4, RMI$_DIRFCB_MISS}, + {4, RMI$_DIRDATA_HIT}, + {4, RMI$_EXTHIT}, + {4, RMI$_EXTMISS}, + {4, RMI$_QUOHIT}, + {4, RMI$_QUOMISS}, + {4, RMI$_STORAGMAP_HIT}, + {4, RMI$_VOLLCK}, + {4, RMI$_SYNCHLCK}, + {4, RMI$_SYNCHWAIT}, + {4, RMI$_ACCLCK}, + {4, RMI$_XQPCACHEWAIT}, + {4, RMI$_DIRDATA_MISS}, + {4, RMI$_FILHDR_MISS}, + {4, RMI$_STORAGMAP_MISS}, + {4, RMI$_PROCCNTMAX}, + {4, RMI$_PROCBATCNT}, + {4, RMI$_PROCINTCNT}, + {4, RMI$_PROCNETCNT}, + {4, RMI$_PROCSWITCHCNT}, + {4, RMI$_PROCBALSETCNT}, + {4, RMI$_PROCLOADCNT}, + {4, RMI$_BADFLTS}, + {4, RMI$_EXEFAULTS}, + {4, RMI$_HDRINSWAPS}, + {4, RMI$_HDROUTSWAPS}, + {4, RMI$_IOPAGCNT}, + {4, RMI$_ISWPCNTPG}, + {4, RMI$_OSWPCNT}, + {4, RMI$_OSWPCNTPG}, + {4, RMI$_RDFAULTS}, + {4, RMI$_TRANSFLTS}, + {4, RMI$_WRTFAULTS}, +#if defined __alpha + {4, RMI$_USERPAGES}, +#endif + {4, RMI$_VMSPAGES}, + {4, RMI$_TTWRITES}, + {4, RMI$_BUFOBJPAG}, + {4, RMI$_BUFOBJPAGPEAK}, + {4, RMI$_BUFOBJPAGS01}, + {4, RMI$_BUFOBJPAGS2}, + {4, RMI$_BUFOBJPAGMAXS01}, + {4, RMI$_BUFOBJPAGMAXS2}, + {4, RMI$_BUFOBJPAGPEAKS01}, + {4, RMI$_BUFOBJPAGPEAKS2}, + {4, RMI$_BUFOBJPGLTMAXS01}, + {4, RMI$_BUFOBJPGLTMAXS2}, + {4, RMI$_DLCK_INCMPLT}, + {4, RMI$_DLCKMSGS_IN}, + {4, RMI$_DLCKMSGS_OUT}, + {4, RMI$_MCHKERRS}, + {4, RMI$_MEMERRS}, +}; + +static const struct item_st RMI_item_data_64bit[] = { +#if defined __ia64 + {8, RMI$_FRLIST}, + {8, RMI$_MODLIST}, +#endif + {8, RMI$_LCKMGR_REQCNT}, + {8, RMI$_LCKMGR_REQTIME}, + {8, RMI$_LCKMGR_SPINCNT}, + {8, RMI$_LCKMGR_SPINTIME}, + {8, RMI$_CPUINTSTK}, + {8, RMI$_CPUMPSYNCH}, + {8, RMI$_CPUKERNEL}, + {8, RMI$_CPUEXEC}, + {8, RMI$_CPUSUPER}, + {8, RMI$_CPUUSER}, +#if defined __ia64 + {8, RMI$_USERPAGES}, +#endif + {8, RMI$_TQETOTAL}, + {8, RMI$_TQESYSUB}, + {8, RMI$_TQEUSRTIMR}, + {8, RMI$_TQEUSRWAKE}, +}; + +static const struct item_st SYI_item_data[] = { + {4, SYI$_PAGEFILE_FREE}, +}; + +/* + * Input: + * items_data - an array of lengths and codes + * items_data_num - number of elements in that array + * + * Output: + * items - pre-allocated ILE3 array to be filled. + * It's assumed to have items_data_num elements plus + * one extra for the terminating NULL element + * databuffer - pre-allocated 32-bit word array. + * + * Returns the number of elements used in databuffer + */ +static size_t prepare_item_list(const struct item_st *items_input, + size_t items_input_num, + ILE3 *items, + uint32_t__ptr32 databuffer) +{ + size_t data_sz = 0; + + for (; items_input_num-- > 0; items_input++, items++) { + + items->ile3$w_code = items_input->code; + /* Special treatment of JPI$_FINALEXC */ + if (items->ile3$w_code == JPI$_FINALEXC) + items->ile3$w_length = 4; + else + items->ile3$w_length = items_input->length; + + items->ile3$ps_bufaddr = databuffer; + items->ile3$ps_retlen_addr = 0; + + databuffer += items_input->length / sizeof(databuffer[0]); + data_sz += items_input->length; + } + /* Terminating NULL entry */ + items->ile3$w_length = items->ile3$w_code = 0; + items->ile3$ps_bufaddr = items->ile3$ps_retlen_addr = NULL; + + return data_sz / sizeof(databuffer[0]); +} + +static void massage_JPI(ILE3 *items) +{ + /* + * Special treatment of JPI$_FINALEXC + * The result of that item's data buffer is a 32-bit address to a list of + * 4 32-bit words. + */ + for (; items->ile3$w_length != 0; items++) { + if (items->ile3$w_code == JPI$_FINALEXC) { + uint32_t *data = items->ile3$ps_bufaddr; + uint32_t *ptr = (uint32_t *)*data; + size_t j; + + /* + * We know we made space for 4 32-bit words, so we can do in-place + * replacement. + */ + for (j = 0; j < 4; j++) + data[j] = ptr[j]; + + break; + } + } +} + +/* + * This number expresses how many bits of data contain 1 bit of entropy. + * + * For the moment, we assume about 0.05 entropy bits per data bit, or 1 + * bit of entropy per 20 data bits. + */ +#define ENTROPY_FACTOR 20 + +size_t data_collect_method(RAND_POOL *pool) +{ + ILE3 JPI_items_64bit[OSSL_NELEM(JPI_item_data_64bit) + 1]; + ILE3 RMI_items_64bit[OSSL_NELEM(RMI_item_data_64bit) + 1]; + ILE3 DVI_items[OSSL_NELEM(DVI_item_data) + 1]; + ILE3 JPI_items[OSSL_NELEM(JPI_item_data) + 1]; + ILE3 RMI_items[OSSL_NELEM(RMI_item_data) + 1]; + ILE3 SYI_items[OSSL_NELEM(SYI_item_data) + 1]; + union { + /* This ensures buffer starts at 64 bit boundary */ + uint64_t dummy; + uint32_t buffer[OSSL_NELEM(JPI_item_data_64bit) * 2 + + OSSL_NELEM(RMI_item_data_64bit) * 2 + + OSSL_NELEM(DVI_item_data) + + OSSL_NELEM(JPI_item_data) + + OSSL_NELEM(RMI_item_data) + + OSSL_NELEM(SYI_item_data) + + 4 /* For JPI$_FINALEXC */]; + } data; + size_t total_elems = 0; + size_t total_length = 0; + size_t bytes_needed = ossl_rand_pool_bytes_needed(pool, ENTROPY_FACTOR); + size_t bytes_remaining = ossl_rand_pool_bytes_remaining(pool); + + /* Take all the 64-bit items first, to ensure proper alignment of data */ + total_elems += + prepare_item_list(JPI_item_data_64bit, OSSL_NELEM(JPI_item_data_64bit), + JPI_items_64bit, &data.buffer[total_elems]); + total_elems += + prepare_item_list(RMI_item_data_64bit, OSSL_NELEM(RMI_item_data_64bit), + RMI_items_64bit, &data.buffer[total_elems]); + /* Now the 32-bit items */ + total_elems += prepare_item_list(DVI_item_data, OSSL_NELEM(DVI_item_data), + DVI_items, &data.buffer[total_elems]); + total_elems += prepare_item_list(JPI_item_data, OSSL_NELEM(JPI_item_data), + JPI_items, &data.buffer[total_elems]); + total_elems += prepare_item_list(RMI_item_data, OSSL_NELEM(RMI_item_data), + RMI_items, &data.buffer[total_elems]); + total_elems += prepare_item_list(SYI_item_data, OSSL_NELEM(SYI_item_data), + SYI_items, &data.buffer[total_elems]); + total_length = total_elems * sizeof(data.buffer[0]); + + /* Fill data.buffer with various info bits from this process */ + { + uint32_t status; + uint32_t efn; + IOSB iosb; + $DESCRIPTOR(SYSDEVICE,"SYS$SYSDEVICE:"); + + if ((status = sys$getdviw(EFN$C_ENF, 0, &SYSDEVICE, DVI_items, + 0, 0, 0, 0, 0)) != SS$_NORMAL) { + lib$signal(status); + return 0; + } + if ((status = sys$getjpiw(EFN$C_ENF, 0, 0, JPI_items_64bit, 0, 0, 0)) + != SS$_NORMAL) { + lib$signal(status); + return 0; + } + if ((status = sys$getjpiw(EFN$C_ENF, 0, 0, JPI_items, 0, 0, 0)) + != SS$_NORMAL) { + lib$signal(status); + return 0; + } + if ((status = sys$getsyiw(EFN$C_ENF, 0, 0, SYI_items, 0, 0, 0)) + != SS$_NORMAL) { + lib$signal(status); + return 0; + } + /* + * The RMI service is a bit special, as there is no synchronous + * variant, so we MUST create an event flag to synchronise on. + */ + if ((status = lib$get_ef(&efn)) != SS$_NORMAL) { + lib$signal(status); + return 0; + } + if ((status = sys$getrmi(efn, 0, 0, RMI_items_64bit, &iosb, 0, 0)) + != SS$_NORMAL) { + lib$signal(status); + return 0; + } + if ((status = sys$synch(efn, &iosb)) != SS$_NORMAL) { + lib$signal(status); + return 0; + } + if (iosb.iosb$l_getxxi_status != SS$_NORMAL) { + lib$signal(iosb.iosb$l_getxxi_status); + return 0; + } + if ((status = sys$getrmi(efn, 0, 0, RMI_items, &iosb, 0, 0)) + != SS$_NORMAL) { + lib$signal(status); + return 0; + } + if ((status = sys$synch(efn, &iosb)) != SS$_NORMAL) { + lib$signal(status); + return 0; + } + if (iosb.iosb$l_getxxi_status != SS$_NORMAL) { + lib$signal(iosb.iosb$l_getxxi_status); + return 0; + } + if ((status = lib$free_ef(&efn)) != SS$_NORMAL) { + lib$signal(status); + return 0; + } + } + + massage_JPI(JPI_items); + + /* + * If we can't feed the requirements from the caller, we're in deep trouble. + */ + if (!ossl_assert(total_length >= bytes_needed)) { + ERR_raise_data(ERR_LIB_RAND, RAND_R_RANDOM_POOL_UNDERFLOW, + "Needed: %zu, Available: %zu", + bytes_needed, total_length); + return 0; + } + + /* + * Try not to overfeed the pool + */ + if (total_length > bytes_remaining) + total_length = bytes_remaining; + + /* We give the pessimistic value for the amount of entropy */ + ossl_rand_pool_add(pool, (unsigned char *)data.buffer, total_length, + 8 * total_length / ENTROPY_FACTOR); + return ossl_rand_pool_entropy_available(pool); +} + +/* + * SYS$GET_ENTROPY METHOD + * ====================== + * + * This is a high entropy method based on a new system service that is + * based on getentropy() from FreeBSD 12. It's only used if available, + * and its availability is detected at run-time. + * + * We assume that this function provides full entropy random output. + */ +#define PUBLIC_VECTORS "SYS$LIBRARY:SYS$PUBLIC_VECTORS.EXE" +#define GET_ENTROPY "SYS$GET_ENTROPY" + +static int get_entropy_address_flag = 0; +static int (*get_entropy_address)(void *buffer, size_t buffer_size) = NULL; +static int init_get_entropy_address(void) +{ + if (get_entropy_address_flag == 0) + get_entropy_address = dlsym(dlopen(PUBLIC_VECTORS, 0), GET_ENTROPY); + get_entropy_address_flag = 1; + return get_entropy_address != NULL; +} + +size_t get_entropy_method(RAND_POOL *pool) +{ + /* + * The documentation says that SYS$GET_ENTROPY will give a maximum of + * 256 bytes of data. + */ + unsigned char buffer[256]; + size_t bytes_needed; + size_t bytes_to_get = 0; + uint32_t status; + + for (bytes_needed = ossl_rand_pool_bytes_needed(pool, 1); + bytes_needed > 0; + bytes_needed -= bytes_to_get) { + bytes_to_get = + bytes_needed > sizeof(buffer) ? sizeof(buffer) : bytes_needed; + + status = get_entropy_address(buffer, bytes_to_get); + if (status == SS$_RETRY) { + /* Set to zero so the loop doesn't diminish |bytes_needed| */ + bytes_to_get = 0; + /* Should sleep some amount of time */ + continue; + } + + if (status != SS$_NORMAL) { + lib$signal(status); + return 0; + } + + ossl_rand_pool_add(pool, buffer, bytes_to_get, 8 * bytes_to_get); + } + + return ossl_rand_pool_entropy_available(pool); +} + +/* + * MAIN ENTROPY ACQUISITION FUNCTIONS + * ================================== + * + * These functions are called by the RAND / DRBG functions + */ + +size_t ossl_pool_acquire_entropy(RAND_POOL *pool) +{ + if (init_get_entropy_address()) + return get_entropy_method(pool); + return data_collect_method(pool); +} + +int ossl_pool_add_nonce_data(RAND_POOL *pool) +{ + /* + * Two variables to ensure that two nonces won't ever be the same + */ + static unsigned __int64 last_time = 0; + static unsigned __int32 last_seq = 0; + + struct { + pid_t pid; + CRYPTO_THREAD_ID tid; + unsigned __int64 time; + unsigned __int32 seq; + } data; + + /* Erase the entire structure including any padding */ + memset(&data, 0, sizeof(data)); + + /* + * Add process id, thread id, a timestamp, and a sequence number in case + * the same time stamp is repeated, to ensure that the nonce is unique + * with high probability for different process instances. + * + * The normal OpenVMS time is specified to be high granularity (100ns), + * but the time update granularity given by sys$gettim() may be lower. + * + * OpenVMS version 8.4 (which is the latest for Alpha and Itanium) and + * on have sys$gettim_prec() as well, which is supposedly having a better + * time update granularity, but tests on Itanium (and even Alpha) have + * shown that compared with sys$gettim(), the difference is marginal, + * so of very little significance in terms of entropy. + * Given that, and that it's a high ask to expect everyone to have + * upgraded to OpenVMS version 8.4, only sys$gettim() is used, and a + * sequence number is added as well, in case sys$gettim() returns the + * same time value more than once. + * + * This function is assumed to be called under thread lock, and does + * therefore not take concurrency into account. + */ + data.pid = getpid(); + data.tid = CRYPTO_THREAD_get_current_id(); + data.seq = 0; + sys$gettim((void*)&data.time); + + if (data.time == last_time) { + data.seq = ++last_seq; + } else { + last_time = data.time; + last_seq = 0; + } + + return ossl_rand_pool_add(pool, (unsigned char *)&data, sizeof(data), 0); +} + +int ossl_rand_pool_init(void) +{ + return 1; +} + +void ossl_rand_pool_cleanup(void) +{ +} + +void ossl_rand_pool_keep_random_devices_open(int keep) +{ +} |