| /* |
| * Copyright 2001-2020 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 "rand_local.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. TODO: To be figured out. */ |
| #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 = rand_pool_bytes_needed(pool, ENTROPY_FACTOR); |
| size_t bytes_remaining = 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 */ |
| rand_pool_add(pool, (unsigned char *)data.buffer, total_length, |
| 8 * total_length / ENTROPY_FACTOR); |
| return rand_pool_entropy_available(pool); |
| } |
| |
| int rand_pool_add_nonce_data(RAND_POOL *pool) |
| { |
| struct { |
| pid_t pid; |
| CRYPTO_THREAD_ID tid; |
| uint64_t time; |
| } data; |
| |
| /* Erase the entire structure including any padding */ |
| memset(&data, 0, sizeof(data)); |
| |
| /* |
| * Add process id, thread id, and a high resolution timestamp |
| * (where available, which is OpenVMS v8.4 and up) to ensure that |
| * the nonce is unique with high probability for different process |
| * instances. |
| */ |
| data.pid = getpid(); |
| data.tid = CRYPTO_THREAD_get_current_id(); |
| #if __CRTL_VER >= 80400000 |
| sys$gettim_prec(&data.time); |
| #else |
| sys$gettim((void*)&data.time); |
| #endif |
| |
| return rand_pool_add(pool, (unsigned char *)&data, sizeof(data), 0); |
| } |
| |
| /* |
| * 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 = 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; |
| } |
| |
| rand_pool_add(pool, buffer, bytes_to_get, 8 * bytes_to_get); |
| } |
| |
| return rand_pool_entropy_available(pool); |
| } |
| |
| /* |
| * MAIN ENTROPY ACQUISITION FUNCTIONS |
| * ================================== |
| * |
| * These functions are called by the RAND / DRBG functions |
| */ |
| |
| size_t rand_pool_acquire_entropy(RAND_POOL *pool) |
| { |
| if (init_get_entropy_address()) |
| return get_entropy_method(pool); |
| return data_collect_method(pool); |
| } |
| |
| |
| int rand_pool_add_additional_data(RAND_POOL *pool) |
| { |
| struct { |
| CRYPTO_THREAD_ID tid; |
| uint64_t time; |
| } data; |
| |
| /* Erase the entire structure including any padding */ |
| memset(&data, 0, sizeof(data)); |
| |
| /* |
| * Add some noise from the thread id and a high resolution timer. |
| * The thread id adds a little randomness if the drbg is accessed |
| * concurrently (which is the case for the <master> drbg). |
| */ |
| data.tid = CRYPTO_THREAD_get_current_id(); |
| #if __CRTL_VER >= 80400000 |
| sys$gettim_prec(&data.time); |
| #else |
| sys$gettim((void*)&data.time); |
| #endif |
| |
| return rand_pool_add(pool, (unsigned char *)&data, sizeof(data), 0); |
| } |
| |
| int rand_pool_init(void) |
| { |
| return 1; |
| } |
| |
| void rand_pool_cleanup(void) |
| { |
| } |
| |
| void rand_pool_keep_random_devices_open(int keep) |
| { |
| } |