]> git.saurik.com Git - apple/securityd.git/blob - src/entropy.cpp
securityd-55016.tar.gz
[apple/securityd.git] / src / entropy.cpp
1 /*
2 * Copyright (c) 2000-2004 Apple Computer, Inc. All Rights Reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24
25 //
26 // EntropyManager - manage entropy on the system.
27 //
28 // Here is our mission:
29 // (1) On startup, read the entropy file and seed it into the RNG for initial use
30 // (2) Periodically, collect entropy from the system and seed it into the RNG
31 // (3) Once in a while, take entropy from the RNG and write it to the entropy file
32 // for use across reboots.
33 //
34 // This class will fail to operate if the process has (and retains) root privileges.
35 // We re-open the entropy file on each use so that we don't work with a "phantom"
36 // file that some fool administrator removed yesterday.
37 //
38 #include "entropy.h"
39 #include "dtrace.h"
40 #include <sys/sysctl.h>
41 #include <mach/clock_types.h>
42 #include <mach/mach_time.h>
43 #include <errno.h>
44 #include <security_utilities/logging.h>
45 #include <sys/sysctl.h>
46 #include <security_utilities/debugging.h>
47
48 /* when true, action() called every 15 seconds */
49 #define ENTROPY_QUICK_UPDATE 0
50 #if ENTROPY_QUICK_UPDATE
51 #define COLLECT_INTERVAL 15
52 #else
53 #define COLLECT_INTERVAL collectInterval
54 #endif //ENTROPY_QUICK_UPDATE
55
56 using namespace UnixPlusPlus;
57
58
59 //
60 // During construction, we perform initial entropy file recovery.
61 //
62 EntropyManager::EntropyManager(MachPlusPlus::MachServer &srv, const char *entropyFile)
63 : DevRandomGenerator(true), server(srv),
64 mEntropyFilePath(entropyFile), mNextUpdate(Time::now())
65 {
66 // Read the entropy file and seed the RNG. It is not an error if we can't find one.
67 try {
68 AutoFileDesc oldEntropyFile(entropyFile, O_RDONLY);
69 char buffer[entropyFileSize];
70 if (size_t size = oldEntropyFile.read(buffer))
71 addEntropy(buffer, size);
72 } catch (...) { }
73
74 // go through a collect/update/reschedule cycle immediately
75 action();
76 }
77
78
79 //
80 // Timer action
81 //
82 void EntropyManager::action()
83 {
84 collectEntropy();
85 updateEntropyFile();
86
87 server.setTimer(this, Time::Interval(COLLECT_INTERVAL)); // drifting reschedule (desired)
88 }
89
90
91 //
92 // Collect system timings and seed into the RNG.
93 // Note that the sysctl will block until the buffer is full or the timeout expires.
94 // We currently use a 1ms timeout, which almost always fills the buffer and
95 // does not provide enough of a delay to worry about it. If we ever get worried,
96 // we could call longTermActivity on the server object to get another thread going.
97 //
98
99 void EntropyManager::collectEntropy()
100 {
101 SECURITYD_ENTROPY_COLLECT();
102
103 int mib[4];
104 mib[0] = CTL_KERN;
105 mib[1] = KERN_KDEBUG;
106 mib[2] = KERN_KDGETENTROPY;
107 mib[3] = 1; // milliseconds maximum delay
108
109 /*
110 The kernel doesn't follow the specification for sysctl.
111 The documentation clearly states that the oldlenp and
112 newlenp point to the size of the buffer to be processed.
113 Unfortunately, the KDGETENTROPY call wants the NUMBER of
114 mach_timespec_t values in the buffer, not the buffer size.
115 */
116
117 int numTimings = timingsToCollect + 1;
118 mach_timespec_t buffer[numTimings];
119
120 int result;
121
122 size_t size = timingsToCollect;
123
124 result = sysctl(mib,4, buffer, &size, NULL, 0);
125 if (result == -1) {
126 Syslog::alert("entropy measurement returned no entropy (errno=%d)", errno);
127 }
128 else if (size == 0)
129 {
130 Syslog::alert("entropy measurement returned no entropy.");
131 }
132
133 // add a little more jitter to the buffer
134 mach_timebase_info_data_t sTimebaseInfo;
135 mach_timebase_info(&sTimebaseInfo);
136 double seconds = (double) mach_absolute_time() * sTimebaseInfo.numer / sTimebaseInfo.denom * 1000000000.0;
137 buffer[numTimings].tv_sec = (int) seconds;
138 buffer[numTimings].tv_nsec = (int) ((seconds - buffer[numTimings].tv_sec) * 1000000000.0);
139
140 /*
141 Yes, we don't check to see if we filled the entire buffer.
142 In entropy collection, something is better than nothing.
143 Please don't fiddle with this code unless you really
144 have determined that you know what you are doing.
145 */
146
147 SECURITYD_ENTROPY_SEED((void *)buffer, sizeof(buffer));
148 addEntropy(buffer, sizeof(buffer));
149 }
150
151
152 //
153 // (Re)write the entropy file with random data pulled from the RNG
154 //
155 void EntropyManager::updateEntropyFile()
156 {
157 if (Time::now() >= mNextUpdate) {
158 try {
159 SECURITYD_ENTROPY_SAVE((char *)mEntropyFilePath.c_str());
160 mNextUpdate = Time::now() + Time::Interval(updateInterval);
161 secdebug("entropy", "updating %s", mEntropyFilePath.c_str());
162 char buffer[entropyFileSize];
163 random(buffer, entropyFileSize);
164 AutoFileDesc entropyFile(mEntropyFilePath.c_str(), O_WRONLY | O_TRUNC | O_CREAT, 0600);
165 if (entropyFile.write(buffer) != entropyFileSize)
166 Syslog::warning("short write on entropy file %s", mEntropyFilePath.c_str());
167 } catch (...) {
168 Syslog::warning("error writing entropy file %s", mEntropyFilePath.c_str());
169 }
170 }
171 }
172