1 /*
2 * Copyright 2001-2022 The OpenSSL Project Authors. All Rights Reserved.
3 *
4 * Licensed under the Apache License 2.0 (the "License"). You may not use
5 * this file except in compliance with the License. You can obtain a copy
6 * in the file LICENSE in the source distribution or at
7 * https://www.openssl.org/source/license.html
8 */
9
10 #include "e_os.h"
11
12 #define __NEW_STARLET 1 /* New starlet definitions since VMS 7.0 */
13 #include <unistd.h>
14 #include "internal/cryptlib.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 __DECC
32 # pragma message disable DOLLARID
33 #endif
34
35 #include <dlfcn.h> /* SYS$GET_ENTROPY presence */
36
37 #ifndef OPENSSL_RAND_SEED_OS
38 # error "Unsupported seeding method configured; must be os"
39 #endif
40
41 /*
42 * DATA COLLECTION METHOD
43 * ======================
44 *
45 * This is a method to get low quality entropy.
46 * It works by collecting all kinds of statistical data that
47 * VMS offers and using them as random seed.
48 */
49
50 /* We need to make sure we have the right size pointer in some cases */
51 #if __INITIAL_POINTER_SIZE == 64
52 # pragma pointer_size save
53 # pragma pointer_size 32
54 #endif
55 typedef uint32_t *uint32_t__ptr32;
56 #if __INITIAL_POINTER_SIZE == 64
57 # pragma pointer_size restore
58 #endif
59
60 struct item_st {
61 short length, code; /* length is number of bytes */
62 };
63
64 static const struct item_st DVI_item_data[] = {
65 {4, DVI$_ERRCNT},
66 {4, DVI$_REFCNT},
67 };
68
69 static 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 points
80 * to a list of 4 32-bit words, so we make extra space for them so we can
81 * do in-place replacement of values
82 */
83 {16, JPI$_FINALEXC},
84 };
85
86 static const struct item_st JPI_item_data_64bit[] = {
87 {8, JPI$_LAST_LOGIN_I},
88 {8, JPI$_LOGINTIM},
89 };
90
91 static 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 __alpha
107 {4, RMI$_FRLIST},
108 {4, RMI$_MODLIST},
109 #endif
110 {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 0
165 {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 #endif
169 {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 __alpha
220 {4, RMI$_USERPAGES},
221 #endif
222 {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 };
240
241 static const struct item_st RMI_item_data_64bit[] = {
242 #if defined __ia64
243 {8, RMI$_FRLIST},
244 {8, RMI$_MODLIST},
245 #endif
246 {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 __ia64
257 {8, RMI$_USERPAGES},
258 #endif
259 {8, RMI$_TQETOTAL},
260 {8, RMI$_TQESYSUB},
261 {8, RMI$_TQEUSRTIMR},
262 {8, RMI$_TQEUSRWAKE},
263 };
264
265 static const struct item_st SYI_item_data[] = {
266 {4, SYI$_PAGEFILE_FREE},
267 };
268
269 /*
270 * Input:
271 * items_data - an array of lengths and codes
272 * items_data_num - number of elements in that array
273 *
274 * Output:
275 * items - pre-allocated ILE3 array to be filled.
276 * It's assumed to have items_data_num elements plus
277 * one extra for the terminating NULL element
278 * databuffer - pre-allocated 32-bit word array.
279 *
280 * Returns the number of elements used in databuffer
281 */
prepare_item_list(const struct item_st * items_input,size_t items_input_num,ILE3 * items,uint32_t__ptr32 databuffer)282 static size_t prepare_item_list(const struct item_st *items_input,
283 size_t items_input_num,
284 ILE3 *items,
285 uint32_t__ptr32 databuffer)
286 {
287 size_t data_sz = 0;
288
289 for (; items_input_num-- > 0; items_input++, items++) {
290
291 items->ile3$w_code = items_input->code;
292 /* Special treatment of JPI$_FINALEXC */
293 if (items->ile3$w_code == JPI$_FINALEXC)
294 items->ile3$w_length = 4;
295 else
296 items->ile3$w_length = items_input->length;
297
298 items->ile3$ps_bufaddr = databuffer;
299 items->ile3$ps_retlen_addr = 0;
300
301 databuffer += items_input->length / sizeof(databuffer[0]);
302 data_sz += items_input->length;
303 }
304 /* Terminating NULL entry */
305 items->ile3$w_length = items->ile3$w_code = 0;
306 items->ile3$ps_bufaddr = items->ile3$ps_retlen_addr = NULL;
307
308 return data_sz / sizeof(databuffer[0]);
309 }
310
massage_JPI(ILE3 * items)311 static void massage_JPI(ILE3 *items)
312 {
313 /*
314 * Special treatment of JPI$_FINALEXC
315 * The result of that item's data buffer is a 32-bit address to a list of
316 * 4 32-bit words.
317 */
318 for (; items->ile3$w_length != 0; items++) {
319 if (items->ile3$w_code == JPI$_FINALEXC) {
320 uint32_t *data = items->ile3$ps_bufaddr;
321 uint32_t *ptr = (uint32_t *)*data;
322 size_t j;
323
324 /*
325 * We know we made space for 4 32-bit words, so we can do in-place
326 * replacement.
327 */
328 for (j = 0; j < 4; j++)
329 data[j] = ptr[j];
330
331 break;
332 }
333 }
334 }
335
336 /*
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 1
340 * bit of entropy per 20 data bits.
341 */
342 #define ENTROPY_FACTOR 20
343
data_collect_method(RAND_POOL * pool)344 size_t data_collect_method(RAND_POOL *pool)
345 {
346 ILE3 JPI_items_64bit[OSSL_NELEM(JPI_item_data_64bit) + 1];
347 ILE3 RMI_items_64bit[OSSL_NELEM(RMI_item_data_64bit) + 1];
348 ILE3 DVI_items[OSSL_NELEM(DVI_item_data) + 1];
349 ILE3 JPI_items[OSSL_NELEM(JPI_item_data) + 1];
350 ILE3 RMI_items[OSSL_NELEM(RMI_item_data) + 1];
351 ILE3 SYI_items[OSSL_NELEM(SYI_item_data) + 1];
352 union {
353 /* This ensures buffer starts at 64 bit boundary */
354 uint64_t dummy;
355 uint32_t buffer[OSSL_NELEM(JPI_item_data_64bit) * 2
356 + OSSL_NELEM(RMI_item_data_64bit) * 2
357 + 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;
363 size_t total_elems = 0;
364 size_t total_length = 0;
365 size_t bytes_needed = ossl_rand_pool_bytes_needed(pool, ENTROPY_FACTOR);
366 size_t bytes_remaining = ossl_rand_pool_bytes_remaining(pool);
367
368 /* Take all the 64-bit items first, to ensure proper alignment of data */
369 total_elems +=
370 prepare_item_list(JPI_item_data_64bit, OSSL_NELEM(JPI_item_data_64bit),
371 JPI_items_64bit, &data.buffer[total_elems]);
372 total_elems +=
373 prepare_item_list(RMI_item_data_64bit, OSSL_NELEM(RMI_item_data_64bit),
374 RMI_items_64bit, &data.buffer[total_elems]);
375 /* Now the 32-bit items */
376 total_elems += prepare_item_list(DVI_item_data, OSSL_NELEM(DVI_item_data),
377 DVI_items, &data.buffer[total_elems]);
378 total_elems += prepare_item_list(JPI_item_data, OSSL_NELEM(JPI_item_data),
379 JPI_items, &data.buffer[total_elems]);
380 total_elems += prepare_item_list(RMI_item_data, OSSL_NELEM(RMI_item_data),
381 RMI_items, &data.buffer[total_elems]);
382 total_elems += prepare_item_list(SYI_item_data, OSSL_NELEM(SYI_item_data),
383 SYI_items, &data.buffer[total_elems]);
384 total_length = total_elems * sizeof(data.buffer[0]);
385
386 /* Fill data.buffer with various info bits from this process */
387 {
388 uint32_t status;
389 uint32_t efn;
390 IOSB iosb;
391 $DESCRIPTOR(SYSDEVICE,"SYS$SYSDEVICE:");
392
393 if ((status = sys$getdviw(EFN$C_ENF, 0, &SYSDEVICE, DVI_items,
394 0, 0, 0, 0, 0)) != SS$_NORMAL) {
395 lib$signal(status);
396 return 0;
397 }
398 if ((status = sys$getjpiw(EFN$C_ENF, 0, 0, JPI_items_64bit, 0, 0, 0))
399 != SS$_NORMAL) {
400 lib$signal(status);
401 return 0;
402 }
403 if ((status = sys$getjpiw(EFN$C_ENF, 0, 0, JPI_items, 0, 0, 0))
404 != SS$_NORMAL) {
405 lib$signal(status);
406 return 0;
407 }
408 if ((status = sys$getsyiw(EFN$C_ENF, 0, 0, SYI_items, 0, 0, 0))
409 != SS$_NORMAL) {
410 lib$signal(status);
411 return 0;
412 }
413 /*
414 * The RMI service is a bit special, as there is no synchronous
415 * variant, so we MUST create an event flag to synchronise on.
416 */
417 if ((status = lib$get_ef(&efn)) != SS$_NORMAL) {
418 lib$signal(status);
419 return 0;
420 }
421 if ((status = sys$getrmi(efn, 0, 0, RMI_items_64bit, &iosb, 0, 0))
422 != SS$_NORMAL) {
423 lib$signal(status);
424 return 0;
425 }
426 if ((status = sys$synch(efn, &iosb)) != SS$_NORMAL) {
427 lib$signal(status);
428 return 0;
429 }
430 if (iosb.iosb$l_getxxi_status != SS$_NORMAL) {
431 lib$signal(iosb.iosb$l_getxxi_status);
432 return 0;
433 }
434 if ((status = sys$getrmi(efn, 0, 0, RMI_items, &iosb, 0, 0))
435 != SS$_NORMAL) {
436 lib$signal(status);
437 return 0;
438 }
439 if ((status = sys$synch(efn, &iosb)) != SS$_NORMAL) {
440 lib$signal(status);
441 return 0;
442 }
443 if (iosb.iosb$l_getxxi_status != SS$_NORMAL) {
444 lib$signal(iosb.iosb$l_getxxi_status);
445 return 0;
446 }
447 if ((status = lib$free_ef(&efn)) != SS$_NORMAL) {
448 lib$signal(status);
449 return 0;
450 }
451 }
452
453 massage_JPI(JPI_items);
454
455 /*
456 * If we can't feed the requirements from the caller, we're in deep trouble.
457 */
458 if (!ossl_assert(total_length >= bytes_needed)) {
459 ERR_raise_data(ERR_LIB_RAND, RAND_R_RANDOM_POOL_UNDERFLOW,
460 "Needed: %zu, Available: %zu",
461 bytes_needed, total_length);
462 return 0;
463 }
464
465 /*
466 * Try not to overfeed the pool
467 */
468 if (total_length > bytes_remaining)
469 total_length = bytes_remaining;
470
471 /* We give the pessimistic value for the amount of entropy */
472 ossl_rand_pool_add(pool, (unsigned char *)data.buffer, total_length,
473 8 * total_length / ENTROPY_FACTOR);
474 return ossl_rand_pool_entropy_available(pool);
475 }
476
477 /*
478 * SYS$GET_ENTROPY METHOD
479 * ======================
480 *
481 * This is a high entropy method based on a new system service that is
482 * 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"
489
490 static int get_entropy_address_flag = 0;
491 static int (*get_entropy_address)(void *buffer, size_t buffer_size) = NULL;
init_get_entropy_address(void)492 static int init_get_entropy_address(void)
493 {
494 if (get_entropy_address_flag == 0)
495 get_entropy_address = dlsym(dlopen(PUBLIC_VECTORS, 0), GET_ENTROPY);
496 get_entropy_address_flag = 1;
497 return get_entropy_address != NULL;
498 }
499
get_entropy_method(RAND_POOL * pool)500 size_t get_entropy_method(RAND_POOL *pool)
501 {
502 /*
503 * The documentation says that SYS$GET_ENTROPY will give a maximum of
504 * 256 bytes of data.
505 */
506 unsigned char buffer[256];
507 size_t bytes_needed;
508 size_t bytes_to_get = 0;
509 uint32_t status;
510
511 for (bytes_needed = ossl_rand_pool_bytes_needed(pool, 1);
512 bytes_needed > 0;
513 bytes_needed -= bytes_to_get) {
514 bytes_to_get =
515 bytes_needed > sizeof(buffer) ? sizeof(buffer) : bytes_needed;
516
517 status = get_entropy_address(buffer, bytes_to_get);
518 if (status == SS$_RETRY) {
519 /* Set to zero so the loop doesn't diminish |bytes_needed| */
520 bytes_to_get = 0;
521 /* Should sleep some amount of time */
522 continue;
523 }
524
525 if (status != SS$_NORMAL) {
526 lib$signal(status);
527 return 0;
528 }
529
530 ossl_rand_pool_add(pool, buffer, bytes_to_get, 8 * bytes_to_get);
531 }
532
533 return ossl_rand_pool_entropy_available(pool);
534 }
535
536 /*
537 * MAIN ENTROPY ACQUISITION FUNCTIONS
538 * ==================================
539 *
540 * These functions are called by the RAND / DRBG functions
541 */
542
ossl_pool_acquire_entropy(RAND_POOL * pool)543 size_t ossl_pool_acquire_entropy(RAND_POOL *pool)
544 {
545 if (init_get_entropy_address())
546 return get_entropy_method(pool);
547 return data_collect_method(pool);
548 }
549
ossl_pool_add_nonce_data(RAND_POOL * pool)550 int ossl_pool_add_nonce_data(RAND_POOL *pool)
551 {
552 /*
553 * Two variables to ensure that two nonces won't ever be the same
554 */
555 static unsigned __int64 last_time = 0;
556 static unsigned __int32 last_seq = 0;
557
558 struct {
559 pid_t pid;
560 CRYPTO_THREAD_ID tid;
561 unsigned __int64 time;
562 unsigned __int32 seq;
563 } data;
564
565 /* Erase the entire structure including any padding */
566 memset(&data, 0, sizeof(data));
567
568 /*
569 * Add process id, thread id, a timestamp, and a sequence number in case
570 * the same time stamp is repeated, to ensure that the nonce is unique
571 * 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) and
577 * on have sys$gettim_prec() as well, which is supposedly having a better
578 * time update granularity, but tests on Itanium (and even Alpha) have
579 * 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 have
582 * upgraded to OpenVMS version 8.4, only sys$gettim() is used, and a
583 * sequence number is added as well, in case sys$gettim() returns the
584 * same time value more than once.
585 *
586 * This function is assumed to be called under thread lock, and does
587 * therefore not take concurrency into account.
588 */
589 data.pid = getpid();
590 data.tid = CRYPTO_THREAD_get_current_id();
591 data.seq = 0;
592 sys$gettim((void*)&data.time);
593
594 if (data.time == last_time) {
595 data.seq = ++last_seq;
596 } else {
597 last_time = data.time;
598 last_seq = 0;
599 }
600
601 return ossl_rand_pool_add(pool, (unsigned char *)&data, sizeof(data), 0);
602 }
603
ossl_rand_pool_init(void)604 int ossl_rand_pool_init(void)
605 {
606 return 1;
607 }
608
ossl_rand_pool_cleanup(void)609 void ossl_rand_pool_cleanup(void)
610 {
611 }
612
ossl_rand_pool_keep_random_devices_open(int keep)613 void ossl_rand_pool_keep_random_devices_open(int keep)
614 {
615 }
616