123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616 |
- /*
- * 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 "internal/e_os.h"
- #define __NEW_STARLET 1 /* New starlet definitions since VMS 7.0 */
- #include <unistd.h>
- #include "internal/cryptlib.h"
- #include "internal/nelem.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)
- {
- }
|