~ubuntu-branches/debian/stretch/dropbear/stretch

« back to all changes in this revision

Viewing changes to random.c

  • Committer: Package Import Robot
  • Author(s): Gerrit Pape, Matt Johnston, Gerrit Pape
  • Date: 2013-10-25 15:00:48 UTC
  • mfrom: (1.4.6)
  • Revision ID: package-import@ubuntu.com-20131025150048-3jq765x96xayk392
Tags: 2013.60-1
[ Matt Johnston ]
* New upstream release.

[ Gerrit Pape ]
* debian/diff/0004-cve-2013-4421.diff, 0005-user-disclosure.diff:
  remove; fixed upstream.
* debian/dropbear.postinst: don't fail if initramfs-tools it not
  installed (closes: #692653).

Show diffs side-by-side

added added

removed removed

Lines of Context:
26
26
#include "buffer.h"
27
27
#include "dbutil.h"
28
28
#include "bignum.h"
29
 
 
30
 
static int donerandinit = 0;
 
29
#include "random.h"
31
30
 
32
31
/* this is used to generate unique output from the same hashpool */
33
32
static uint32_t counter = 0;
34
33
/* the max value for the counter, so it won't integer overflow */
35
34
#define MAX_COUNTER 1<<30 
36
35
 
37
 
static unsigned char hashpool[SHA1_HASH_SIZE];
 
36
static unsigned char hashpool[SHA1_HASH_SIZE] = {0};
 
37
static int donerandinit = 0;
38
38
 
39
39
#define INIT_SEED_SIZE 32 /* 256 bits */
40
40
 
41
 
static void readrand(unsigned char* buf, unsigned int buflen);
42
 
 
43
41
/* The basic setup is we read some data from /dev/(u)random or prngd and hash it
44
42
 * into hashpool. To read data, we hash together current hashpool contents,
45
43
 * and a counter. We feed more data in by hashing the current pool and new
50
48
 *
51
49
 */
52
50
 
53
 
static void readrand(unsigned char* buf, unsigned int buflen) {
54
 
 
 
51
/* Pass len=0 to hash an entire file */
 
52
static int
 
53
process_file(hash_state *hs, const char *filename,
 
54
                unsigned int len, int prngd)
 
55
{
55
56
        static int already_blocked = 0;
56
57
        int readfd;
57
 
        unsigned int readpos;
58
 
        int readlen;
59
 
#ifdef DROPBEAR_PRNGD_SOCKET
60
 
        struct sockaddr_un egdsock;
61
 
        char egdcmd[2];
62
 
#endif
63
 
 
64
 
#ifdef DROPBEAR_RANDOM_DEV
65
 
        readfd = open(DROPBEAR_RANDOM_DEV, O_RDONLY);
66
 
        if (readfd < 0) {
67
 
                dropbear_exit("Couldn't open random device");
68
 
        }
69
 
#endif
70
 
 
71
 
#ifdef DROPBEAR_PRNGD_SOCKET
72
 
        readfd = connect_unix(DROPBEAR_PRNGD_SOCKET);
73
 
 
74
 
        if (readfd < 0) {
75
 
                dropbear_exit("Couldn't open random device");
76
 
        }
77
 
 
78
 
        if (buflen > 255)
79
 
                dropbear_exit("Can't request more than 255 bytes from egd");
80
 
        egdcmd[0] = 0x02;       /* blocking read */
81
 
        egdcmd[1] = (unsigned char)buflen;
82
 
        if (write(readfd, egdcmd, 2) < 0)
83
 
                dropbear_exit("Can't send command to egd");
84
 
#endif
85
 
 
86
 
        /* read the actual random data */
87
 
        readpos = 0;
88
 
        do {
 
58
        unsigned int readcount;
 
59
        int ret = DROPBEAR_FAILURE;
 
60
 
 
61
#ifdef DROPBEAR_PRNGD_SOCKET
 
62
        if (prngd)
 
63
        {
 
64
                readfd = connect_unix(filename);
 
65
        }
 
66
        else
 
67
#endif
 
68
        {
 
69
                readfd = open(filename, O_RDONLY);
 
70
        }
 
71
 
 
72
        if (readfd < 0) {
 
73
                goto out;
 
74
        }
 
75
 
 
76
        readcount = 0;
 
77
        while (len == 0 || readcount < len)
 
78
        {
 
79
                int readlen, wantread;
 
80
                unsigned char readbuf[4096];
89
81
                if (!already_blocked)
90
82
                {
91
83
                        int ret;
92
 
                        struct timeval timeout;
 
84
                        struct timeval timeout = { .tv_sec = 2, .tv_usec = 0};
93
85
                        fd_set read_fds;
94
86
 
95
 
                        timeout.tv_sec = 2; /* two seconds should be enough */
96
 
                        timeout.tv_usec = 0;
97
 
 
98
87
                        FD_ZERO(&read_fds);
99
88
                        FD_SET(readfd, &read_fds);
100
89
                        ret = select(readfd + 1, &read_fds, NULL, NULL, &timeout);
101
90
                        if (ret == 0)
102
91
                        {
103
 
                                dropbear_log(LOG_INFO, "Warning: Reading the random source seems to have blocked.\nIf you experience problems, you probably need to find a better entropy source.");
 
92
                                dropbear_log(LOG_WARNING, "Warning: Reading the randomness source '%s' seems to have blocked.\nYou may need to find a better entropy source.", filename);
104
93
                                already_blocked = 1;
105
94
                        }
106
95
                }
107
 
                readlen = read(readfd, &buf[readpos], buflen - readpos);
 
96
 
 
97
                if (len == 0)
 
98
                {
 
99
                        wantread = sizeof(readbuf);
 
100
                } 
 
101
                else
 
102
                {
 
103
                        wantread = MIN(sizeof(readbuf), len-readcount);
 
104
                }
 
105
 
 
106
#ifdef DROPBEAR_PRNGD_SOCKET
 
107
                if (prngd)
 
108
                {
 
109
                        char egdcmd[2];
 
110
                        egdcmd[0] = 0x02;       /* blocking read */
 
111
                        egdcmd[1] = (unsigned char)wantread;
 
112
                        if (write(readfd, egdcmd, 2) < 0)
 
113
                        {
 
114
                                dropbear_exit("Can't send command to egd");
 
115
                        }
 
116
                }
 
117
#endif
 
118
 
 
119
                readlen = read(readfd, readbuf, wantread);
108
120
                if (readlen <= 0) {
109
121
                        if (readlen < 0 && errno == EINTR) {
110
122
                                continue;
111
123
                        }
112
 
                        dropbear_exit("Error reading random source");
 
124
                        if (readlen == 0 && len == 0)
 
125
                        {
 
126
                                /* whole file was read as requested */
 
127
                                break;
 
128
                        }
 
129
                        goto out;
113
130
                }
114
 
                readpos += readlen;
115
 
        } while (readpos < buflen);
116
 
 
117
 
        close (readfd);
118
 
}
119
 
 
120
 
/* initialise the prng from /dev/(u)random or prngd */
 
131
                sha1_process(hs, readbuf, readlen);
 
132
                readcount += readlen;
 
133
        }
 
134
        ret = DROPBEAR_SUCCESS;
 
135
out:
 
136
        close(readfd);
 
137
        return ret;
 
138
}
 
139
 
 
140
void addrandom(char * buf, unsigned int len)
 
141
{
 
142
        hash_state hs;
 
143
 
 
144
        /* hash in the new seed data */
 
145
        sha1_init(&hs);
 
146
        /* existing state (zeroes on startup) */
 
147
        sha1_process(&hs, (void*)hashpool, sizeof(hashpool));
 
148
 
 
149
        /* new */
 
150
        sha1_process(&hs, buf, len);
 
151
        sha1_done(&hs, hashpool);
 
152
}
 
153
 
 
154
static void write_urandom()
 
155
{
 
156
#ifndef DROPBEAR_PRNGD_SOCKET
 
157
        /* This is opportunistic, don't worry about failure */
 
158
        unsigned char buf[INIT_SEED_SIZE];
 
159
        FILE *f = fopen(DROPBEAR_URANDOM_DEV, "w");
 
160
        if (!f) {
 
161
                return;
 
162
        }
 
163
        genrandom(buf, sizeof(buf));
 
164
        fwrite(buf, sizeof(buf), 1, f);
 
165
        fclose(f);
 
166
#endif
 
167
}
 
168
 
 
169
/* Initialise the prng from /dev/urandom or prngd. This function can
 
170
 * be called multiple times */
121
171
void seedrandom() {
122
172
                
123
 
        unsigned char readbuf[INIT_SEED_SIZE];
124
 
 
125
173
        hash_state hs;
126
174
 
127
 
        /* initialise so that things won't warn about
128
 
         * hashing an undefined buffer */
129
 
        if (!donerandinit) {
130
 
                m_burn(hashpool, sizeof(hashpool));
131
 
        }
132
 
 
133
 
        /* get the seed data */
134
 
        readrand(readbuf, sizeof(readbuf));
135
 
 
136
 
        /* hash in the new seed data */
137
 
        sha1_init(&hs);
138
 
        sha1_process(&hs, (void*)hashpool, sizeof(hashpool));
139
 
        sha1_process(&hs, (void*)readbuf, sizeof(readbuf));
140
 
        sha1_done(&hs, hashpool);
141
 
 
142
 
        counter = 0;
143
 
        donerandinit = 1;
144
 
}
145
 
 
146
 
/* hash the current random pool with some unique identifiers
147
 
 * for this process and point-in-time. this is used to separate
148
 
 * the random pools for fork()ed processes. */
149
 
void reseedrandom() {
150
 
 
151
175
        pid_t pid;
152
 
        hash_state hs;
153
176
        struct timeval tv;
154
 
 
155
 
        if (!donerandinit) {
156
 
                dropbear_exit("seedrandom not done");
157
 
        }
 
177
        clock_t clockval;
 
178
 
 
179
        /* hash in the new seed data */
 
180
        sha1_init(&hs);
 
181
        /* existing state */
 
182
        sha1_process(&hs, (void*)hashpool, sizeof(hashpool));
 
183
 
 
184
#ifdef DROPBEAR_PRNGD_SOCKET
 
185
        if (process_file(&hs, DROPBEAR_PRNGD_SOCKET, INIT_SEED_SIZE, 1) 
 
186
                        != DROPBEAR_SUCCESS) {
 
187
                dropbear_exit("Failure reading random device %s", 
 
188
                                DROPBEAR_PRNGD_SOCKET);
 
189
        }
 
190
#else
 
191
        /* non-blocking random source (probably /dev/urandom) */
 
192
        if (process_file(&hs, DROPBEAR_URANDOM_DEV, INIT_SEED_SIZE, 0) 
 
193
                        != DROPBEAR_SUCCESS) {
 
194
                dropbear_exit("Failure reading random device %s", 
 
195
                                DROPBEAR_URANDOM_DEV);
 
196
        }
 
197
#endif
 
198
 
 
199
        /* A few other sources to fall back on. 
 
200
         * Add more here for other platforms */
 
201
#ifdef __linux__
 
202
        /* Seems to be a reasonable source of entropy from timers. Possibly hard
 
203
         * for even local attackers to reproduce */
 
204
        process_file(&hs, "/proc/timer_list", 0, 0);
 
205
        /* Might help on systems with wireless */
 
206
        process_file(&hs, "/proc/interrupts", 0, 0);
 
207
 
 
208
        process_file(&hs, "/proc/loadavg", 0, 0);
 
209
        process_file(&hs, "/proc/sys/kernel/random/entropy_avail", 0, 0);
 
210
 
 
211
        /* Mostly network visible but useful in some situations.
 
212
         * Limit size to avoid slowdowns on systems with lots of routes */
 
213
        process_file(&hs, "/proc/net/netstat", 4096, 0);
 
214
        process_file(&hs, "/proc/net/dev", 4096, 0);
 
215
        process_file(&hs, "/proc/net/tcp", 4096, 0);
 
216
        /* Also includes interface lo */
 
217
        process_file(&hs, "/proc/net/rt_cache", 4096, 0);
 
218
        process_file(&hs, "/proc/vmstat", 0, 0);
 
219
#endif
158
220
 
159
221
        pid = getpid();
 
222
        sha1_process(&hs, (void*)&pid, sizeof(pid));
 
223
 
 
224
        // gettimeofday() doesn't completely fill out struct timeval on 
 
225
        // OS X (10.8.3), avoid valgrind warnings by clearing it first
 
226
        memset(&tv, 0x0, sizeof(tv));
160
227
        gettimeofday(&tv, NULL);
161
 
 
162
 
        sha1_init(&hs);
163
 
        sha1_process(&hs, (void*)hashpool, sizeof(hashpool));
164
 
        sha1_process(&hs, (void*)&pid, sizeof(pid));
165
228
        sha1_process(&hs, (void*)&tv, sizeof(tv));
 
229
 
 
230
        clockval = clock();
 
231
        sha1_process(&hs, (void*)&clockval, sizeof(clockval));
 
232
 
 
233
        /* When a private key is read by the client or server it will
 
234
         * be added to the hashpool - see runopts.c */
 
235
 
166
236
        sha1_done(&hs, hashpool);
 
237
 
 
238
        counter = 0;
 
239
        donerandinit = 1;
 
240
 
 
241
        /* Feed it all back into /dev/urandom - this might help if Dropbear
 
242
         * is running from inetd and gets new state each time */
 
243
        write_urandom();
167
244
}
168
245
 
169
246
/* return len bytes of pseudo-random data */