Path: blob/main/crypto/openssl/providers/implementations/rands/seeding/rand_vms.c
108728 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 += prepare_item_list(JPI_item_data_64bit, OSSL_NELEM(JPI_item_data_64bit),370JPI_items_64bit, &data.buffer[total_elems]);371total_elems += prepare_item_list(RMI_item_data_64bit, OSSL_NELEM(RMI_item_data_64bit),372RMI_items_64bit, &data.buffer[total_elems]);373/* Now the 32-bit items */374total_elems += prepare_item_list(DVI_item_data, OSSL_NELEM(DVI_item_data),375DVI_items, &data.buffer[total_elems]);376total_elems += prepare_item_list(JPI_item_data, OSSL_NELEM(JPI_item_data),377JPI_items, &data.buffer[total_elems]);378total_elems += prepare_item_list(RMI_item_data, OSSL_NELEM(RMI_item_data),379RMI_items, &data.buffer[total_elems]);380total_elems += prepare_item_list(SYI_item_data, OSSL_NELEM(SYI_item_data),381SYI_items, &data.buffer[total_elems]);382total_length = total_elems * sizeof(data.buffer[0]);383384/* Fill data.buffer with various info bits from this process */385{386uint32_t status;387uint32_t efn;388IOSB iosb;389$DESCRIPTOR(SYSDEVICE, "SYS$SYSDEVICE:");390391if ((status = sys$getdviw(EFN$C_ENF, 0, &SYSDEVICE, DVI_items,3920, 0, 0, 0, 0))393!= SS$_NORMAL) {394lib$signal(status);395return 0;396}397if ((status = sys$getjpiw(EFN$C_ENF, 0, 0, JPI_items_64bit, 0, 0, 0))398!= SS$_NORMAL) {399lib$signal(status);400return 0;401}402if ((status = sys$getjpiw(EFN$C_ENF, 0, 0, JPI_items, 0, 0, 0))403!= SS$_NORMAL) {404lib$signal(status);405return 0;406}407if ((status = sys$getsyiw(EFN$C_ENF, 0, 0, SYI_items, 0, 0, 0))408!= SS$_NORMAL) {409lib$signal(status);410return 0;411}412/*413* The RMI service is a bit special, as there is no synchronous414* variant, so we MUST create an event flag to synchronise on.415*/416if ((status = lib$get_ef(&efn)) != SS$_NORMAL) {417lib$signal(status);418return 0;419}420if ((status = sys$getrmi(efn, 0, 0, RMI_items_64bit, &iosb, 0, 0))421!= SS$_NORMAL) {422lib$signal(status);423return 0;424}425if ((status = sys$synch(efn, &iosb)) != SS$_NORMAL) {426lib$signal(status);427return 0;428}429if (iosb.iosb$l_getxxi_status != SS$_NORMAL) {430lib$signal(iosb.iosb$l_getxxi_status);431return 0;432}433if ((status = sys$getrmi(efn, 0, 0, RMI_items, &iosb, 0, 0))434!= SS$_NORMAL) {435lib$signal(status);436return 0;437}438if ((status = sys$synch(efn, &iosb)) != SS$_NORMAL) {439lib$signal(status);440return 0;441}442if (iosb.iosb$l_getxxi_status != SS$_NORMAL) {443lib$signal(iosb.iosb$l_getxxi_status);444return 0;445}446if ((status = lib$free_ef(&efn)) != SS$_NORMAL) {447lib$signal(status);448return 0;449}450}451452massage_JPI(JPI_items);453454/*455* If we can't feed the requirements from the caller, we're in deep trouble.456*/457if (!ossl_assert(total_length >= bytes_needed)) {458ERR_raise_data(ERR_LIB_RAND, RAND_R_RANDOM_POOL_UNDERFLOW,459"Needed: %zu, Available: %zu",460bytes_needed, total_length);461return 0;462}463464/*465* Try not to overfeed the pool466*/467if (total_length > bytes_remaining)468total_length = bytes_remaining;469470/* We give the pessimistic value for the amount of entropy */471ossl_rand_pool_add(pool, (unsigned char *)data.buffer, total_length,4728 * total_length / ENTROPY_FACTOR);473return ossl_rand_pool_entropy_available(pool);474}475476/*477* SYS$GET_ENTROPY METHOD478* ======================479*480* This is a high entropy method based on a new system service that is481* based on getentropy() from FreeBSD 12. It's only used if available,482* and its availability is detected at run-time.483*484* We assume that this function provides full entropy random output.485*/486#define PUBLIC_VECTORS "SYS$LIBRARY:SYS$PUBLIC_VECTORS.EXE"487#define GET_ENTROPY "SYS$GET_ENTROPY"488489static int get_entropy_address_flag = 0;490static int (*get_entropy_address)(void *buffer, size_t buffer_size) = NULL;491static int init_get_entropy_address(void)492{493if (get_entropy_address_flag == 0)494get_entropy_address = dlsym(dlopen(PUBLIC_VECTORS, 0), GET_ENTROPY);495get_entropy_address_flag = 1;496return get_entropy_address != NULL;497}498499size_t get_entropy_method(RAND_POOL *pool)500{501/*502* The documentation says that SYS$GET_ENTROPY will give a maximum of503* 256 bytes of data.504*/505unsigned char buffer[256];506size_t bytes_needed;507size_t bytes_to_get = 0;508uint32_t status;509510for (bytes_needed = ossl_rand_pool_bytes_needed(pool, 1);511bytes_needed > 0;512bytes_needed -= bytes_to_get) {513bytes_to_get = bytes_needed > sizeof(buffer) ? sizeof(buffer) : bytes_needed;514515status = get_entropy_address(buffer, bytes_to_get);516if (status == SS$_RETRY) {517/* Set to zero so the loop doesn't diminish |bytes_needed| */518bytes_to_get = 0;519/* Should sleep some amount of time */520continue;521}522523if (status != SS$_NORMAL) {524lib$signal(status);525return 0;526}527528ossl_rand_pool_add(pool, buffer, bytes_to_get, 8 * bytes_to_get);529}530531return ossl_rand_pool_entropy_available(pool);532}533534/*535* MAIN ENTROPY ACQUISITION FUNCTIONS536* ==================================537*538* These functions are called by the RAND / DRBG functions539*/540541size_t ossl_pool_acquire_entropy(RAND_POOL *pool)542{543if (init_get_entropy_address())544return get_entropy_method(pool);545return data_collect_method(pool);546}547548int ossl_pool_add_nonce_data(RAND_POOL *pool)549{550/*551* Two variables to ensure that two nonces won't ever be the same552*/553static unsigned __int64 last_time = 0;554static unsigned __int32 last_seq = 0;555556struct {557pid_t pid;558CRYPTO_THREAD_ID tid;559unsigned __int64 time;560unsigned __int32 seq;561} data;562563/* Erase the entire structure including any padding */564memset(&data, 0, sizeof(data));565566/*567* Add process id, thread id, a timestamp, and a sequence number in case568* the same time stamp is repeated, to ensure that the nonce is unique569* with high probability for different process instances.570*571* The normal OpenVMS time is specified to be high granularity (100ns),572* but the time update granularity given by sys$gettim() may be lower.573*574* OpenVMS version 8.4 (which is the latest for Alpha and Itanium) and575* on have sys$gettim_prec() as well, which is supposedly having a better576* time update granularity, but tests on Itanium (and even Alpha) have577* shown that compared with sys$gettim(), the difference is marginal,578* so of very little significance in terms of entropy.579* Given that, and that it's a high ask to expect everyone to have580* upgraded to OpenVMS version 8.4, only sys$gettim() is used, and a581* sequence number is added as well, in case sys$gettim() returns the582* same time value more than once.583*584* This function is assumed to be called under thread lock, and does585* therefore not take concurrency into account.586*/587data.pid = getpid();588data.tid = CRYPTO_THREAD_get_current_id();589data.seq = 0;590sys$gettim((void *)&data.time);591592if (data.time == last_time) {593data.seq = ++last_seq;594} else {595last_time = data.time;596last_seq = 0;597}598599return ossl_rand_pool_add(pool, (unsigned char *)&data, sizeof(data), 0);600}601602int ossl_rand_pool_init(void)603{604return 1;605}606607void ossl_rand_pool_cleanup(void)608{609}610611void ossl_rand_pool_keep_random_devices_open(int keep)612{613}614615616