Path: blob/main/crypto/openssl/providers/implementations/rands/seeding/rand_vms.c
48531 views
/*1* Copyright 2001-2022 The OpenSSL Project Authors. All Rights Reserved.2*3* Licensed under the Apache License 2.0 (the "License"). You may not use4* this file except in compliance with the License. You can obtain a copy5* in the file LICENSE in the source distribution or at6* https://www.openssl.org/source/license.html7*/89#include "internal/e_os.h"1011#define __NEW_STARLET 1 /* New starlet definitions since VMS 7.0 */12#include <unistd.h>13#include "internal/cryptlib.h"14#include "internal/nelem.h"15#include <openssl/rand.h>16#include "crypto/rand.h"17#include "crypto/rand_pool.h"18#include "prov/seeding.h"19#include <descrip.h>20#include <dvidef.h>21#include <jpidef.h>22#include <rmidef.h>23#include <syidef.h>24#include <ssdef.h>25#include <starlet.h>26#include <efndef.h>27#include <gen64def.h>28#include <iosbdef.h>29#include <iledef.h>30#include <lib$routines.h>31#ifdef __DECC32# pragma message disable DOLLARID33#endif3435#include <dlfcn.h> /* SYS$GET_ENTROPY presence */3637#ifndef OPENSSL_RAND_SEED_OS38# error "Unsupported seeding method configured; must be os"39#endif4041/*42* DATA COLLECTION METHOD43* ======================44*45* This is a method to get low quality entropy.46* It works by collecting all kinds of statistical data that47* VMS offers and using them as random seed.48*/4950/* We need to make sure we have the right size pointer in some cases */51#if __INITIAL_POINTER_SIZE == 6452# pragma pointer_size save53# pragma pointer_size 3254#endif55typedef uint32_t *uint32_t__ptr32;56#if __INITIAL_POINTER_SIZE == 6457# pragma pointer_size restore58#endif5960struct item_st {61short length, code; /* length is number of bytes */62};6364static const struct item_st DVI_item_data[] = {65{4, DVI$_ERRCNT},66{4, DVI$_REFCNT},67};6869static const struct item_st JPI_item_data[] = {70{4, JPI$_BUFIO},71{4, JPI$_CPUTIM},72{4, JPI$_DIRIO},73{4, JPI$_IMAGECOUNT},74{4, JPI$_PAGEFLTS},75{4, JPI$_PID},76{4, JPI$_PPGCNT},77{4, JPI$_WSPEAK},78/*79* Note: the direct result is just a 32-bit address. However, it points80* to a list of 4 32-bit words, so we make extra space for them so we can81* do in-place replacement of values82*/83{16, JPI$_FINALEXC},84};8586static const struct item_st JPI_item_data_64bit[] = {87{8, JPI$_LAST_LOGIN_I},88{8, JPI$_LOGINTIM},89};9091static const struct item_st RMI_item_data[] = {92{4, RMI$_COLPG},93{4, RMI$_MWAIT},94{4, RMI$_CEF},95{4, RMI$_PFW},96{4, RMI$_LEF},97{4, RMI$_LEFO},98{4, RMI$_HIB},99{4, RMI$_HIBO},100{4, RMI$_SUSP},101{4, RMI$_SUSPO},102{4, RMI$_FPG},103{4, RMI$_COM},104{4, RMI$_COMO},105{4, RMI$_CUR},106#if defined __alpha107{4, RMI$_FRLIST},108{4, RMI$_MODLIST},109#endif110{4, RMI$_FAULTS},111{4, RMI$_PREADS},112{4, RMI$_PWRITES},113{4, RMI$_PWRITIO},114{4, RMI$_PREADIO},115{4, RMI$_GVALFLTS},116{4, RMI$_WRTINPROG},117{4, RMI$_FREFLTS},118{4, RMI$_DZROFLTS},119{4, RMI$_SYSFAULTS},120{4, RMI$_ISWPCNT},121{4, RMI$_DIRIO},122{4, RMI$_BUFIO},123{4, RMI$_MBREADS},124{4, RMI$_MBWRITES},125{4, RMI$_LOGNAM},126{4, RMI$_FCPCALLS},127{4, RMI$_FCPREAD},128{4, RMI$_FCPWRITE},129{4, RMI$_FCPCACHE},130{4, RMI$_FCPCPU},131{4, RMI$_FCPHIT},132{4, RMI$_FCPSPLIT},133{4, RMI$_FCPFAULT},134{4, RMI$_ENQNEW},135{4, RMI$_ENQCVT},136{4, RMI$_DEQ},137{4, RMI$_BLKAST},138{4, RMI$_ENQWAIT},139{4, RMI$_ENQNOTQD},140{4, RMI$_DLCKSRCH},141{4, RMI$_DLCKFND},142{4, RMI$_NUMLOCKS},143{4, RMI$_NUMRES},144{4, RMI$_ARRLOCPK},145{4, RMI$_DEPLOCPK},146{4, RMI$_ARRTRAPK},147{4, RMI$_TRCNGLOS},148{4, RMI$_RCVBUFFL},149{4, RMI$_ENQNEWLOC},150{4, RMI$_ENQNEWIN},151{4, RMI$_ENQNEWOUT},152{4, RMI$_ENQCVTLOC},153{4, RMI$_ENQCVTIN},154{4, RMI$_ENQCVTOUT},155{4, RMI$_DEQLOC},156{4, RMI$_DEQIN},157{4, RMI$_DEQOUT},158{4, RMI$_BLKLOC},159{4, RMI$_BLKIN},160{4, RMI$_BLKOUT},161{4, RMI$_DIRIN},162{4, RMI$_DIROUT},163/* We currently get a fault when trying these */164#if 0165{140, RMI$_MSCP_EVERYTHING}, /* 35 32-bit words */166{152, RMI$_DDTM_ALL}, /* 38 32-bit words */167{80, RMI$_TMSCP_EVERYTHING} /* 20 32-bit words */168#endif169{4, RMI$_LPZ_PAGCNT},170{4, RMI$_LPZ_HITS},171{4, RMI$_LPZ_MISSES},172{4, RMI$_LPZ_EXPCNT},173{4, RMI$_LPZ_ALLOCF},174{4, RMI$_LPZ_ALLOC2},175{4, RMI$_ACCESS},176{4, RMI$_ALLOC},177{4, RMI$_FCPCREATE},178{4, RMI$_VOLWAIT},179{4, RMI$_FCPTURN},180{4, RMI$_FCPERASE},181{4, RMI$_OPENS},182{4, RMI$_FIDHIT},183{4, RMI$_FIDMISS},184{4, RMI$_FILHDR_HIT},185{4, RMI$_DIRFCB_HIT},186{4, RMI$_DIRFCB_MISS},187{4, RMI$_DIRDATA_HIT},188{4, RMI$_EXTHIT},189{4, RMI$_EXTMISS},190{4, RMI$_QUOHIT},191{4, RMI$_QUOMISS},192{4, RMI$_STORAGMAP_HIT},193{4, RMI$_VOLLCK},194{4, RMI$_SYNCHLCK},195{4, RMI$_SYNCHWAIT},196{4, RMI$_ACCLCK},197{4, RMI$_XQPCACHEWAIT},198{4, RMI$_DIRDATA_MISS},199{4, RMI$_FILHDR_MISS},200{4, RMI$_STORAGMAP_MISS},201{4, RMI$_PROCCNTMAX},202{4, RMI$_PROCBATCNT},203{4, RMI$_PROCINTCNT},204{4, RMI$_PROCNETCNT},205{4, RMI$_PROCSWITCHCNT},206{4, RMI$_PROCBALSETCNT},207{4, RMI$_PROCLOADCNT},208{4, RMI$_BADFLTS},209{4, RMI$_EXEFAULTS},210{4, RMI$_HDRINSWAPS},211{4, RMI$_HDROUTSWAPS},212{4, RMI$_IOPAGCNT},213{4, RMI$_ISWPCNTPG},214{4, RMI$_OSWPCNT},215{4, RMI$_OSWPCNTPG},216{4, RMI$_RDFAULTS},217{4, RMI$_TRANSFLTS},218{4, RMI$_WRTFAULTS},219#if defined __alpha220{4, RMI$_USERPAGES},221#endif222{4, RMI$_VMSPAGES},223{4, RMI$_TTWRITES},224{4, RMI$_BUFOBJPAG},225{4, RMI$_BUFOBJPAGPEAK},226{4, RMI$_BUFOBJPAGS01},227{4, RMI$_BUFOBJPAGS2},228{4, RMI$_BUFOBJPAGMAXS01},229{4, RMI$_BUFOBJPAGMAXS2},230{4, RMI$_BUFOBJPAGPEAKS01},231{4, RMI$_BUFOBJPAGPEAKS2},232{4, RMI$_BUFOBJPGLTMAXS01},233{4, RMI$_BUFOBJPGLTMAXS2},234{4, RMI$_DLCK_INCMPLT},235{4, RMI$_DLCKMSGS_IN},236{4, RMI$_DLCKMSGS_OUT},237{4, RMI$_MCHKERRS},238{4, RMI$_MEMERRS},239};240241static const struct item_st RMI_item_data_64bit[] = {242#if defined __ia64243{8, RMI$_FRLIST},244{8, RMI$_MODLIST},245#endif246{8, RMI$_LCKMGR_REQCNT},247{8, RMI$_LCKMGR_REQTIME},248{8, RMI$_LCKMGR_SPINCNT},249{8, RMI$_LCKMGR_SPINTIME},250{8, RMI$_CPUINTSTK},251{8, RMI$_CPUMPSYNCH},252{8, RMI$_CPUKERNEL},253{8, RMI$_CPUEXEC},254{8, RMI$_CPUSUPER},255{8, RMI$_CPUUSER},256#if defined __ia64257{8, RMI$_USERPAGES},258#endif259{8, RMI$_TQETOTAL},260{8, RMI$_TQESYSUB},261{8, RMI$_TQEUSRTIMR},262{8, RMI$_TQEUSRWAKE},263};264265static const struct item_st SYI_item_data[] = {266{4, SYI$_PAGEFILE_FREE},267};268269/*270* Input:271* items_data - an array of lengths and codes272* items_data_num - number of elements in that array273*274* Output:275* items - pre-allocated ILE3 array to be filled.276* It's assumed to have items_data_num elements plus277* one extra for the terminating NULL element278* databuffer - pre-allocated 32-bit word array.279*280* Returns the number of elements used in databuffer281*/282static size_t prepare_item_list(const struct item_st *items_input,283size_t items_input_num,284ILE3 *items,285uint32_t__ptr32 databuffer)286{287size_t data_sz = 0;288289for (; items_input_num-- > 0; items_input++, items++) {290291items->ile3$w_code = items_input->code;292/* Special treatment of JPI$_FINALEXC */293if (items->ile3$w_code == JPI$_FINALEXC)294items->ile3$w_length = 4;295else296items->ile3$w_length = items_input->length;297298items->ile3$ps_bufaddr = databuffer;299items->ile3$ps_retlen_addr = 0;300301databuffer += items_input->length / sizeof(databuffer[0]);302data_sz += items_input->length;303}304/* Terminating NULL entry */305items->ile3$w_length = items->ile3$w_code = 0;306items->ile3$ps_bufaddr = items->ile3$ps_retlen_addr = NULL;307308return data_sz / sizeof(databuffer[0]);309}310311static void massage_JPI(ILE3 *items)312{313/*314* Special treatment of JPI$_FINALEXC315* The result of that item's data buffer is a 32-bit address to a list of316* 4 32-bit words.317*/318for (; items->ile3$w_length != 0; items++) {319if (items->ile3$w_code == JPI$_FINALEXC) {320uint32_t *data = items->ile3$ps_bufaddr;321uint32_t *ptr = (uint32_t *)*data;322size_t j;323324/*325* We know we made space for 4 32-bit words, so we can do in-place326* replacement.327*/328for (j = 0; j < 4; j++)329data[j] = ptr[j];330331break;332}333}334}335336/*337* This number expresses how many bits of data contain 1 bit of entropy.338*339* For the moment, we assume about 0.05 entropy bits per data bit, or 1340* bit of entropy per 20 data bits.341*/342#define ENTROPY_FACTOR 20343344size_t data_collect_method(RAND_POOL *pool)345{346ILE3 JPI_items_64bit[OSSL_NELEM(JPI_item_data_64bit) + 1];347ILE3 RMI_items_64bit[OSSL_NELEM(RMI_item_data_64bit) + 1];348ILE3 DVI_items[OSSL_NELEM(DVI_item_data) + 1];349ILE3 JPI_items[OSSL_NELEM(JPI_item_data) + 1];350ILE3 RMI_items[OSSL_NELEM(RMI_item_data) + 1];351ILE3 SYI_items[OSSL_NELEM(SYI_item_data) + 1];352union {353/* This ensures buffer starts at 64 bit boundary */354uint64_t dummy;355uint32_t buffer[OSSL_NELEM(JPI_item_data_64bit) * 2356+ OSSL_NELEM(RMI_item_data_64bit) * 2357+ OSSL_NELEM(DVI_item_data)358+ OSSL_NELEM(JPI_item_data)359+ OSSL_NELEM(RMI_item_data)360+ OSSL_NELEM(SYI_item_data)361+ 4 /* For JPI$_FINALEXC */];362} data;363size_t total_elems = 0;364size_t total_length = 0;365size_t bytes_needed = ossl_rand_pool_bytes_needed(pool, ENTROPY_FACTOR);366size_t bytes_remaining = ossl_rand_pool_bytes_remaining(pool);367368/* Take all the 64-bit items first, to ensure proper alignment of data */369total_elems +=370prepare_item_list(JPI_item_data_64bit, OSSL_NELEM(JPI_item_data_64bit),371JPI_items_64bit, &data.buffer[total_elems]);372total_elems +=373prepare_item_list(RMI_item_data_64bit, OSSL_NELEM(RMI_item_data_64bit),374RMI_items_64bit, &data.buffer[total_elems]);375/* Now the 32-bit items */376total_elems += prepare_item_list(DVI_item_data, OSSL_NELEM(DVI_item_data),377DVI_items, &data.buffer[total_elems]);378total_elems += prepare_item_list(JPI_item_data, OSSL_NELEM(JPI_item_data),379JPI_items, &data.buffer[total_elems]);380total_elems += prepare_item_list(RMI_item_data, OSSL_NELEM(RMI_item_data),381RMI_items, &data.buffer[total_elems]);382total_elems += prepare_item_list(SYI_item_data, OSSL_NELEM(SYI_item_data),383SYI_items, &data.buffer[total_elems]);384total_length = total_elems * sizeof(data.buffer[0]);385386/* Fill data.buffer with various info bits from this process */387{388uint32_t status;389uint32_t efn;390IOSB iosb;391$DESCRIPTOR(SYSDEVICE, "SYS$SYSDEVICE:");392393if ((status = sys$getdviw(EFN$C_ENF, 0, &SYSDEVICE, DVI_items,3940, 0, 0, 0, 0)) != SS$_NORMAL) {395lib$signal(status);396return 0;397}398if ((status = sys$getjpiw(EFN$C_ENF, 0, 0, JPI_items_64bit, 0, 0, 0))399!= SS$_NORMAL) {400lib$signal(status);401return 0;402}403if ((status = sys$getjpiw(EFN$C_ENF, 0, 0, JPI_items, 0, 0, 0))404!= SS$_NORMAL) {405lib$signal(status);406return 0;407}408if ((status = sys$getsyiw(EFN$C_ENF, 0, 0, SYI_items, 0, 0, 0))409!= SS$_NORMAL) {410lib$signal(status);411return 0;412}413/*414* The RMI service is a bit special, as there is no synchronous415* variant, so we MUST create an event flag to synchronise on.416*/417if ((status = lib$get_ef(&efn)) != SS$_NORMAL) {418lib$signal(status);419return 0;420}421if ((status = sys$getrmi(efn, 0, 0, RMI_items_64bit, &iosb, 0, 0))422!= SS$_NORMAL) {423lib$signal(status);424return 0;425}426if ((status = sys$synch(efn, &iosb)) != SS$_NORMAL) {427lib$signal(status);428return 0;429}430if (iosb.iosb$l_getxxi_status != SS$_NORMAL) {431lib$signal(iosb.iosb$l_getxxi_status);432return 0;433}434if ((status = sys$getrmi(efn, 0, 0, RMI_items, &iosb, 0, 0))435!= SS$_NORMAL) {436lib$signal(status);437return 0;438}439if ((status = sys$synch(efn, &iosb)) != SS$_NORMAL) {440lib$signal(status);441return 0;442}443if (iosb.iosb$l_getxxi_status != SS$_NORMAL) {444lib$signal(iosb.iosb$l_getxxi_status);445return 0;446}447if ((status = lib$free_ef(&efn)) != SS$_NORMAL) {448lib$signal(status);449return 0;450}451}452453massage_JPI(JPI_items);454455/*456* If we can't feed the requirements from the caller, we're in deep trouble.457*/458if (!ossl_assert(total_length >= bytes_needed)) {459ERR_raise_data(ERR_LIB_RAND, RAND_R_RANDOM_POOL_UNDERFLOW,460"Needed: %zu, Available: %zu",461bytes_needed, total_length);462return 0;463}464465/*466* Try not to overfeed the pool467*/468if (total_length > bytes_remaining)469total_length = bytes_remaining;470471/* We give the pessimistic value for the amount of entropy */472ossl_rand_pool_add(pool, (unsigned char *)data.buffer, total_length,4738 * total_length / ENTROPY_FACTOR);474return ossl_rand_pool_entropy_available(pool);475}476477/*478* SYS$GET_ENTROPY METHOD479* ======================480*481* This is a high entropy method based on a new system service that is482* based on getentropy() from FreeBSD 12. It's only used if available,483* and its availability is detected at run-time.484*485* We assume that this function provides full entropy random output.486*/487#define PUBLIC_VECTORS "SYS$LIBRARY:SYS$PUBLIC_VECTORS.EXE"488#define GET_ENTROPY "SYS$GET_ENTROPY"489490static int get_entropy_address_flag = 0;491static int (*get_entropy_address)(void *buffer, size_t buffer_size) = NULL;492static int init_get_entropy_address(void)493{494if (get_entropy_address_flag == 0)495get_entropy_address = dlsym(dlopen(PUBLIC_VECTORS, 0), GET_ENTROPY);496get_entropy_address_flag = 1;497return get_entropy_address != NULL;498}499500size_t get_entropy_method(RAND_POOL *pool)501{502/*503* The documentation says that SYS$GET_ENTROPY will give a maximum of504* 256 bytes of data.505*/506unsigned char buffer[256];507size_t bytes_needed;508size_t bytes_to_get = 0;509uint32_t status;510511for (bytes_needed = ossl_rand_pool_bytes_needed(pool, 1);512bytes_needed > 0;513bytes_needed -= bytes_to_get) {514bytes_to_get =515bytes_needed > sizeof(buffer) ? sizeof(buffer) : bytes_needed;516517status = get_entropy_address(buffer, bytes_to_get);518if (status == SS$_RETRY) {519/* Set to zero so the loop doesn't diminish |bytes_needed| */520bytes_to_get = 0;521/* Should sleep some amount of time */522continue;523}524525if (status != SS$_NORMAL) {526lib$signal(status);527return 0;528}529530ossl_rand_pool_add(pool, buffer, bytes_to_get, 8 * bytes_to_get);531}532533return ossl_rand_pool_entropy_available(pool);534}535536/*537* MAIN ENTROPY ACQUISITION FUNCTIONS538* ==================================539*540* These functions are called by the RAND / DRBG functions541*/542543size_t ossl_pool_acquire_entropy(RAND_POOL *pool)544{545if (init_get_entropy_address())546return get_entropy_method(pool);547return data_collect_method(pool);548}549550int ossl_pool_add_nonce_data(RAND_POOL *pool)551{552/*553* Two variables to ensure that two nonces won't ever be the same554*/555static unsigned __int64 last_time = 0;556static unsigned __int32 last_seq = 0;557558struct {559pid_t pid;560CRYPTO_THREAD_ID tid;561unsigned __int64 time;562unsigned __int32 seq;563} data;564565/* Erase the entire structure including any padding */566memset(&data, 0, sizeof(data));567568/*569* Add process id, thread id, a timestamp, and a sequence number in case570* the same time stamp is repeated, to ensure that the nonce is unique571* with high probability for different process instances.572*573* The normal OpenVMS time is specified to be high granularity (100ns),574* but the time update granularity given by sys$gettim() may be lower.575*576* OpenVMS version 8.4 (which is the latest for Alpha and Itanium) and577* on have sys$gettim_prec() as well, which is supposedly having a better578* time update granularity, but tests on Itanium (and even Alpha) have579* shown that compared with sys$gettim(), the difference is marginal,580* so of very little significance in terms of entropy.581* Given that, and that it's a high ask to expect everyone to have582* upgraded to OpenVMS version 8.4, only sys$gettim() is used, and a583* sequence number is added as well, in case sys$gettim() returns the584* same time value more than once.585*586* This function is assumed to be called under thread lock, and does587* therefore not take concurrency into account.588*/589data.pid = getpid();590data.tid = CRYPTO_THREAD_get_current_id();591data.seq = 0;592sys$gettim((void*)&data.time);593594if (data.time == last_time) {595data.seq = ++last_seq;596} else {597last_time = data.time;598last_seq = 0;599}600601return ossl_rand_pool_add(pool, (unsigned char *)&data, sizeof(data), 0);602}603604int ossl_rand_pool_init(void)605{606return 1;607}608609void ossl_rand_pool_cleanup(void)610{611}612613void ossl_rand_pool_keep_random_devices_open(int keep)614{615}616617618