]>
Commit | Line | Data |
---|---|---|
e3c25c4a | 1 | /* fhandler_random.cc: code to access /dev/random and /dev/urandom |
1c0c369b | 2 | |
40afcae3 | 3 | Copyright 2000, 2001, 2002, 2003, 2004, 2005, 2007, 2009, 2011 Red Hat, Inc. |
1c0c369b CV |
4 | |
5 | Written by Corinna Vinschen (vinschen@cygnus.com) | |
6 | ||
7 | This file is part of Cygwin. | |
8 | ||
9 | This software is a copyrighted work licensed under the terms of the | |
10 | Cygwin license. Please consult the file "CYGWIN_LICENSE" for | |
11 | details. */ | |
12 | ||
4c8d72de | 13 | #include "winsup.h" |
04cb518d | 14 | #include <unistd.h> |
9e2baf8d | 15 | #include "cygerrno.h" |
7ac61736 | 16 | #include "path.h" |
bccd5e0d | 17 | #include "fhandler.h" |
56a19715 CF |
18 | #include "sync.h" |
19 | #include "dtable.h" | |
20 | #include "cygheap.h" | |
21 | #include "child_info.h" | |
1c0c369b CV |
22 | |
23 | #define RANDOM 8 | |
24 | #define URANDOM 9 | |
25 | ||
9602ffc3 | 26 | #define PSEUDO_MULTIPLIER (6364136223846793005LL) |
b0e82b74 | 27 | #define PSEUDO_SHIFTVAL (21) |
9602ffc3 | 28 | |
7ac61736 CF |
29 | fhandler_dev_random::fhandler_dev_random () |
30 | : fhandler_base (), crypt_prov ((HCRYPTPROV) NULL) | |
1c0c369b | 31 | { |
1c0c369b CV |
32 | } |
33 | ||
34 | int | |
7ac61736 | 35 | fhandler_dev_random::open (int flags, mode_t) |
1c0c369b | 36 | { |
1bc9effd | 37 | set_flags ((flags & ~O_TEXT) | O_BINARY); |
56551a9b | 38 | nohandle (true); |
f3ea62a8 | 39 | set_open_status (); |
04cb518d | 40 | dummy_offset = 0; |
1c0c369b CV |
41 | return 1; |
42 | } | |
43 | ||
2e008fb9 | 44 | bool |
9602ffc3 CV |
45 | fhandler_dev_random::crypt_gen_random (void *ptr, size_t len) |
46 | { | |
47 | if (!crypt_prov | |
40afcae3 CV |
48 | && !CryptAcquireContextW (&crypt_prov, NULL, MS_DEF_PROV_W, PROV_RSA_FULL, |
49 | CRYPT_VERIFYCONTEXT | CRYPT_MACHINE_KEYSET) | |
50 | && !CryptAcquireContextW (&crypt_prov, NULL, MS_DEF_PROV_W, PROV_RSA_FULL, | |
51 | CRYPT_VERIFYCONTEXT | CRYPT_MACHINE_KEYSET | |
52 | | CRYPT_NEWKEYSET)) | |
9602ffc3 CV |
53 | { |
54 | debug_printf ("%E = CryptAquireContext()"); | |
2e008fb9 | 55 | return false; |
9602ffc3 CV |
56 | } |
57 | if (!CryptGenRandom (crypt_prov, len, (BYTE *)ptr)) | |
58 | { | |
59 | debug_printf ("%E = CryptGenRandom()"); | |
2e008fb9 | 60 | return false; |
9602ffc3 | 61 | } |
2e008fb9 | 62 | return true; |
9602ffc3 CV |
63 | } |
64 | ||
1c0c369b | 65 | int |
9602ffc3 | 66 | fhandler_dev_random::pseudo_write (const void *ptr, size_t len) |
1c0c369b | 67 | { |
9602ffc3 CV |
68 | /* Use buffer to mess up the pseudo random number generator. */ |
69 | for (size_t i = 0; i < len; ++i) | |
70 | pseudo = (pseudo + ((unsigned char *)ptr)[i]) * PSEUDO_MULTIPLIER + 1; | |
1c0c369b CV |
71 | return len; |
72 | } | |
73 | ||
43c23d4b | 74 | ssize_t __stdcall |
9602ffc3 | 75 | fhandler_dev_random::write (const void *ptr, size_t len) |
1c0c369b CV |
76 | { |
77 | if (!len) | |
78 | return 0; | |
9602ffc3 | 79 | if (!ptr) |
1c0c369b | 80 | { |
9602ffc3 | 81 | set_errno (EINVAL); |
1c0c369b CV |
82 | return -1; |
83 | } | |
9602ffc3 CV |
84 | |
85 | /* Limit len to a value <= 512 since we don't want to overact. | |
86 | Copy to local buffer because CryptGenRandom violates const. */ | |
87 | unsigned char buf[512]; | |
88 | size_t limited_len = len <= 512 ? len : 512; | |
89 | memcpy (buf, ptr, limited_len); | |
90 | ||
91 | /* Mess up system entropy source. Return error if device is /dev/random. */ | |
7ac61736 | 92 | if (!crypt_gen_random (buf, limited_len) && dev () == FH_RANDOM) |
1c0c369b CV |
93 | { |
94 | __seterrno (); | |
95 | return -1; | |
96 | } | |
9602ffc3 CV |
97 | /* Mess up the pseudo random number generator. */ |
98 | pseudo_write (buf, limited_len); | |
1c0c369b CV |
99 | return len; |
100 | } | |
101 | ||
9602ffc3 CV |
102 | int |
103 | fhandler_dev_random::pseudo_read (void *ptr, size_t len) | |
104 | { | |
105 | /* Use pseudo random number generator as fallback entropy source. | |
106 | This multiplier was obtained from Knuth, D.E., "The Art of | |
107 | Computer Programming," Vol 2, Seminumerical Algorithms, Third | |
108 | Edition, Addison-Wesley, 1998, p. 106 (line 26) & p. 108 */ | |
109 | for (size_t i = 0; i < len; ++i) | |
110 | { | |
111 | pseudo = pseudo * PSEUDO_MULTIPLIER + 1; | |
112 | ((unsigned char *)ptr)[i] = (pseudo >> PSEUDO_SHIFTVAL) & UCHAR_MAX; | |
113 | } | |
114 | return len; | |
115 | } | |
116 | ||
8bce0d72 CF |
117 | void __stdcall |
118 | fhandler_dev_random::read (void *ptr, size_t& len) | |
9602ffc3 CV |
119 | { |
120 | if (!len) | |
8bce0d72 CF |
121 | return; |
122 | ||
9602ffc3 CV |
123 | if (!ptr) |
124 | { | |
125 | set_errno (EINVAL); | |
6cce721b | 126 | len = (size_t) -1; |
8bce0d72 | 127 | return; |
9602ffc3 CV |
128 | } |
129 | ||
130 | if (crypt_gen_random (ptr, len)) | |
8bce0d72 CF |
131 | return; |
132 | ||
9602ffc3 CV |
133 | /* If device is /dev/urandom, use pseudo number generator as fallback. |
134 | Don't do this for /dev/random since it's intended for uses that need | |
135 | very high quality randomness. */ | |
7ac61736 | 136 | if (dev () == FH_URANDOM) |
8bce0d72 CF |
137 | { |
138 | len = pseudo_read (ptr, len); | |
139 | return; | |
140 | } | |
9602ffc3 CV |
141 | |
142 | __seterrno (); | |
6cce721b | 143 | len = (size_t) -1; |
9602ffc3 CV |
144 | } |
145 | ||
61522196 CV |
146 | off_t |
147 | fhandler_dev_random::lseek (off_t off, int whence) | |
1c0c369b | 148 | { |
04cb518d CV |
149 | /* As on Linux, fake being able to set an offset. The fact that neither |
150 | reading nor writing changes the dummy offset is also the same as on | |
151 | Linux (tested with kernel 2.6.23). */ | |
61522196 | 152 | off_t new_off; |
04cb518d CV |
153 | |
154 | switch (whence) | |
155 | { | |
156 | case SEEK_SET: | |
157 | new_off = off; | |
158 | break; | |
159 | case SEEK_CUR: | |
160 | new_off = dummy_offset + off; | |
161 | break; | |
162 | default: | |
163 | set_errno (EINVAL); | |
61522196 | 164 | return (off_t) -1; |
04cb518d | 165 | } |
53be6f3d CV |
166 | if (new_off < 0) |
167 | { | |
168 | set_errno (EINVAL); | |
61522196 | 169 | return (off_t) -1; |
53be6f3d | 170 | } |
04cb518d | 171 | return dummy_offset = new_off; |
1c0c369b CV |
172 | } |
173 | ||
174 | int | |
2f9ae2ed | 175 | fhandler_dev_random::close () |
1c0c369b | 176 | { |
56a19715 | 177 | if (!have_execed && crypt_prov) |
1c0c369b | 178 | while (!CryptReleaseContext (crypt_prov, 0) |
b0e82b74 | 179 | && GetLastError () == ERROR_BUSY) |
c90e1cf1 | 180 | Sleep (10); |
1c0c369b CV |
181 | return 0; |
182 | } | |
183 | ||
184 | int | |
23771fa1 | 185 | fhandler_dev_random::dup (fhandler_base *child, int) |
1c0c369b CV |
186 | { |
187 | fhandler_dev_random *fhr = (fhandler_dev_random *) child; | |
1c0c369b CV |
188 | fhr->crypt_prov = (HCRYPTPROV)NULL; |
189 | return 0; | |
190 | } |