daae926ef5be41ad6d3380079e993186ff7b8031
[openssl.git] / crypto / rand / rand_vms.c
1 /*
2  * Copyright 2001-2018 The OpenSSL Project Authors. All Rights Reserved.
3  *
4  * Licensed under the OpenSSL license (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 #if defined(OPENSSL_SYS_VMS)
13 # define __NEW_STARLET 1         /* New starlet definitions since VMS 7.0 */
14 # include <unistd.h>
15 # include "internal/cryptlib.h"
16 # include <openssl/rand.h>
17 # include "internal/rand_int.h"
18 # include "rand_lcl.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 # ifndef OPENSSL_RAND_SEED_OS
36 #  error "Unsupported seeding method configured; must be os"
37 # endif
38
39 /* We need to make sure we have the right size pointer in some cases */
40 # if __INITIAL_POINTER_SIZE == 64
41 #  pragma pointer_size save
42 #  pragma pointer_size 32
43 # endif
44 typedef uint32_t *uint32_t__ptr32;
45 # if __INITIAL_POINTER_SIZE == 64
46 #  pragma pointer_size restore
47 # endif
48
49 static const struct item_st {
50     short length, code;         /* length is number of bytes */
51 } item_data[] = {
52     {4,  JPI$_BUFIO},
53     {4,  JPI$_CPUTIM},
54     {4,  JPI$_DIRIO},
55     {4,  JPI$_IMAGECOUNT},
56     {8,  JPI$_LAST_LOGIN_I},
57     {8,  JPI$_LOGINTIM},
58     {4,  JPI$_PAGEFLTS},
59     {4,  JPI$_PID},
60     {4,  JPI$_PPGCNT},
61     {4,  JPI$_WSPEAK},
62     /*
63      * Note: the direct result is just a 32-bit address.  However, it points
64      * to a list of 4 32-bit words, so we make extra space for them so we can
65      * do in-place replacement of values
66      */
67     {16, JPI$_FINALEXC},
68 };
69
70 /*
71  * Input:
72  * items_data           - an array of lengths and codes
73  * items_data_num       - number of elements in that array, minus one
74  *                        (caller MUST have space for one extra NULL element)
75  *
76  * Output:
77  * items                - pre-allocated ILE3 array to be filled.
78  *                        It's assume to have items_data_num elements.
79  * databuffer           - pre-allocated 32-bit word array.
80  *
81  * Returns the number of bytes used in databuffer
82  */
83 static size_t prepare_item_list(const struct item_st *items_input,
84                                 size_t items_input_num,
85                                 ILE3 *items,
86                                 uint32_t__ptr32 databuffer)
87 {
88     const struct item_st *pitems_input;
89     ILE3 *pitems;
90     size_t data_sz = 0;
91
92     for (pitems_input = items_input, pitems = items;
93          items_input_num-- > 0;
94          pitems_input++, pitems++) {
95
96         /* Special treatment of JPI$_FINALEXC */
97         if (pitems->ile3$w_code == JPI$_FINALEXC)
98             pitems->ile3$w_length = 4;
99         else
100             pitems->ile3$w_length = pitems_input->length;
101
102         pitems->ile3$w_code   = pitems_input->code;
103         pitems->ile3$ps_bufaddr = databuffer;
104         pitems->ile3$ps_retlen_addr = 0;
105
106         databuffer += pitems_input->length / sizeof(*databuffer);
107         data_sz += pitems_input->length;
108     }
109     /* Terminating NULL entry */
110     pitems->ile3$w_length = pitems->ile3$w_code = 0;
111
112     return data_sz;
113 }
114
115 static void massage_JPI(ILE3 *items)
116 {
117     /*
118      * Special treatment of JPI$_FINALEXC
119      * The result of that item's data buffer is a 32-bit address to a list of
120      * 4 32-bit words.
121      */
122     for (; items->ile3$w_length != 0; items++) {
123         if (items->ile3$w_code == JPI$_FINALEXC) {
124             uint32_t *data = items->ile3$ps_bufaddr;
125             uint32_t *ptr = (uint32_t *)*data;
126             size_t j;
127
128             /*
129              * We know we made space for 4 32-bit words, so we can do in-place
130              * replacement.
131              */
132             for (j = 0; j < 4; j++)
133                 data[j] = ptr[j];
134
135             break;
136         }
137     }
138 }
139
140 /*
141  * This number expresses how many bits of data contain 1 bit of entropy.
142  *
143  * For the moment, we assume about 0.5 entropy bits per data bit, or 1
144  * bit of entropy per 2 data bits.
145  */
146 #define ENTROPY_FACTOR  2
147
148 size_t rand_pool_acquire_entropy(RAND_POOL *pool)
149 {
150     ILE3 items[OSSL_NELEM(item_data) + 1];
151     /*
152      * All items get 1 or 2 32-bit words of data, except JPI$_FINALEXC
153      * We make sure that we have ample space
154      */
155     uint32_t data_buffer[(OSSL_NELEM(item_data)) * 2 + 4];
156     size_t total_length = 0;
157     size_t bytes_needed = rand_pool_bytes_needed(pool, ENTROPY_FACTOR);
158     size_t bytes_remaining = rand_pool_bytes_remaining(pool);
159
160     total_length += prepare_item_list(item_data, OSSL_NELEM(item_data),
161                                       items, &data_buffer[total_length]);
162
163     /* Fill data_buffer with various info bits from this process */
164     {
165         uint32_t status;
166
167         if ((status = sys$getjpiw(EFN$C_ENF, 0, 0, items, 0, 0, 0))
168             != SS$_NORMAL) {
169             lib$signal(status);
170             return 0;
171         }
172     }
173
174     massage_JPI(items);
175
176     /*
177      * If we can't feed the requirements from the caller, we're in deep trouble.
178      */
179     if (!ossl_assert(total_length >= bytes_needed)) {
180         char neededstr[20];
181         char availablestr[20];
182
183         BIO_snprintf(neededstr, sizeof(neededstr), "%zu", bytes_needed);
184         BIO_snprintf(availablestr, sizeof(availablestr), "%zu", total_length);
185         RANDerr(RAND_F_RAND_POOL_ACQUIRE_ENTROPY,
186                 RAND_R_RANDOM_POOL_UNDERFLOW);
187         ERR_add_error_data(4, "Needed: ", neededstr, ", Available: ",
188                            availablestr);
189         return 0;
190     }
191
192     /*
193      * Try not to overfeed the pool
194      */
195     if (total_length > bytes_remaining)
196         total_length = bytes_remaining;
197
198     /* We give the pessimistic value for the amount of entropy */
199     rand_pool_add(pool, (unsigned char *)data_buffer, total_length,
200                   total_length / ENTROPY_FACTOR);
201     return rand_pool_entropy_available(pool);
202 }
203
204 int rand_pool_add_nonce_data(RAND_POOL *pool)
205 {
206     struct {
207         pid_t pid;
208         CRYPTO_THREAD_ID tid;
209         uint64_t time;
210     } data = { 0 };
211
212     /*
213      * Add process id, thread id, and a high resolution timestamp to
214      * ensure that the nonce is unique whith high probability for
215      * different process instances.
216      */
217     data.pid = getpid();
218     data.tid = CRYPTO_THREAD_get_current_id();
219     sys$gettim_prec(&data.time);
220
221     return rand_pool_add(pool, (unsigned char *)&data, sizeof(data), 0);
222 }
223
224 int rand_pool_add_additional_data(RAND_POOL *pool)
225 {
226     struct {
227         CRYPTO_THREAD_ID tid;
228         uint64_t time;
229     } data = { 0 };
230
231     /*
232      * Add some noise from the thread id and a high resolution timer.
233      * The thread id adds a little randomness if the drbg is accessed
234      * concurrently (which is the case for the <master> drbg).
235      */
236     data.tid = CRYPTO_THREAD_get_current_id();
237     sys$gettim_prec(&data.time);
238
239     return rand_pool_add(pool, (unsigned char *)&data, sizeof(data), 0);
240 }
241
242 #endif