/contrib/bind9/lib/isc/unix/entropy.c

https://bitbucket.org/freebsd/freebsd-head/ · C · 605 lines · 413 code · 88 blank · 104 comment · 106 complexity · c2cca759a9d65683ce25ea850f2e97ab MD5 · raw file

  1. /*
  2. * Copyright (C) 2004-2008 Internet Systems Consortium, Inc. ("ISC")
  3. * Copyright (C) 2000-2003 Internet Software Consortium.
  4. *
  5. * Permission to use, copy, modify, and/or distribute this software for any
  6. * purpose with or without fee is hereby granted, provided that the above
  7. * copyright notice and this permission notice appear in all copies.
  8. *
  9. * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
  10. * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
  11. * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
  12. * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
  13. * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
  14. * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
  15. * PERFORMANCE OF THIS SOFTWARE.
  16. */
  17. /* $Id: entropy.c,v 1.82 2008/12/01 23:47:45 tbox Exp $ */
  18. /* \file unix/entropy.c
  19. * \brief
  20. * This is the system dependent part of the ISC entropy API.
  21. */
  22. #include <config.h>
  23. #include <sys/param.h> /* Openserver 5.0.6A and FD_SETSIZE */
  24. #include <sys/types.h>
  25. #include <sys/time.h>
  26. #include <sys/stat.h>
  27. #include <sys/socket.h>
  28. #include <sys/un.h>
  29. #ifdef HAVE_NANOSLEEP
  30. #include <time.h>
  31. #endif
  32. #include <unistd.h>
  33. #include <isc/platform.h>
  34. #include <isc/strerror.h>
  35. #ifdef ISC_PLATFORM_NEEDSYSSELECTH
  36. #include <sys/select.h>
  37. #endif
  38. #include "errno2result.h"
  39. /*%
  40. * There is only one variable in the entropy data structures that is not
  41. * system independent, but pulling the structure that uses it into this file
  42. * ultimately means pulling several other independent structures here also to
  43. * resolve their interdependencies. Thus only the problem variable's type
  44. * is defined here.
  45. */
  46. #define FILESOURCE_HANDLE_TYPE int
  47. typedef struct {
  48. int handle;
  49. enum {
  50. isc_usocketsource_disconnected,
  51. isc_usocketsource_connecting,
  52. isc_usocketsource_connected,
  53. isc_usocketsource_ndesired,
  54. isc_usocketsource_wrote,
  55. isc_usocketsource_reading
  56. } status;
  57. size_t sz_to_recv;
  58. } isc_entropyusocketsource_t;
  59. #include "../entropy.c"
  60. static unsigned int
  61. get_from_filesource(isc_entropysource_t *source, isc_uint32_t desired) {
  62. isc_entropy_t *ent = source->ent;
  63. unsigned char buf[128];
  64. int fd = source->sources.file.handle;
  65. ssize_t n, ndesired;
  66. unsigned int added;
  67. if (source->bad)
  68. return (0);
  69. desired = desired / 8 + (((desired & 0x07) > 0) ? 1 : 0);
  70. added = 0;
  71. while (desired > 0) {
  72. ndesired = ISC_MIN(desired, sizeof(buf));
  73. n = read(fd, buf, ndesired);
  74. if (n < 0) {
  75. if (errno == EAGAIN || errno == EINTR)
  76. goto out;
  77. goto err;
  78. }
  79. if (n == 0)
  80. goto err;
  81. entropypool_adddata(ent, buf, n, n * 8);
  82. added += n * 8;
  83. desired -= n;
  84. }
  85. goto out;
  86. err:
  87. (void)close(fd);
  88. source->sources.file.handle = -1;
  89. source->bad = ISC_TRUE;
  90. out:
  91. return (added);
  92. }
  93. static unsigned int
  94. get_from_usocketsource(isc_entropysource_t *source, isc_uint32_t desired) {
  95. isc_entropy_t *ent = source->ent;
  96. unsigned char buf[128];
  97. int fd = source->sources.usocket.handle;
  98. ssize_t n = 0, ndesired;
  99. unsigned int added;
  100. size_t sz_to_recv = source->sources.usocket.sz_to_recv;
  101. if (source->bad)
  102. return (0);
  103. desired = desired / 8 + (((desired & 0x07) > 0) ? 1 : 0);
  104. added = 0;
  105. while (desired > 0) {
  106. ndesired = ISC_MIN(desired, sizeof(buf));
  107. eagain_loop:
  108. switch ( source->sources.usocket.status ) {
  109. case isc_usocketsource_ndesired:
  110. buf[0] = ndesired;
  111. if ((n = sendto(fd, buf, 1, 0, NULL, 0)) < 0) {
  112. if (errno == EWOULDBLOCK || errno == EINTR ||
  113. errno == ECONNRESET)
  114. goto out;
  115. goto err;
  116. }
  117. INSIST(n == 1);
  118. source->sources.usocket.status =
  119. isc_usocketsource_wrote;
  120. goto eagain_loop;
  121. case isc_usocketsource_connecting:
  122. case isc_usocketsource_connected:
  123. buf[0] = 1;
  124. buf[1] = ndesired;
  125. if ((n = sendto(fd, buf, 2, 0, NULL, 0)) < 0) {
  126. if (errno == EWOULDBLOCK || errno == EINTR ||
  127. errno == ECONNRESET)
  128. goto out;
  129. goto err;
  130. }
  131. if (n == 1) {
  132. source->sources.usocket.status =
  133. isc_usocketsource_ndesired;
  134. goto eagain_loop;
  135. }
  136. INSIST(n == 2);
  137. source->sources.usocket.status =
  138. isc_usocketsource_wrote;
  139. /*FALLTHROUGH*/
  140. case isc_usocketsource_wrote:
  141. if (recvfrom(fd, buf, 1, 0, NULL, NULL) != 1) {
  142. if (errno == EAGAIN) {
  143. /*
  144. * The problem of EAGAIN (try again
  145. * later) is a major issue on HP-UX.
  146. * Solaris actually tries the recvfrom
  147. * call again, while HP-UX just dies.
  148. * This code is an attempt to let the
  149. * entropy pool fill back up (at least
  150. * that's what I think the problem is.)
  151. * We go to eagain_loop because if we
  152. * just "break", then the "desired"
  153. * amount gets borked.
  154. */
  155. #ifdef HAVE_NANOSLEEP
  156. struct timespec ts;
  157. ts.tv_sec = 0;
  158. ts.tv_nsec = 1000000;
  159. nanosleep(&ts, NULL);
  160. #else
  161. usleep(1000);
  162. #endif
  163. goto eagain_loop;
  164. }
  165. if (errno == EWOULDBLOCK || errno == EINTR)
  166. goto out;
  167. goto err;
  168. }
  169. source->sources.usocket.status =
  170. isc_usocketsource_reading;
  171. sz_to_recv = buf[0];
  172. source->sources.usocket.sz_to_recv = sz_to_recv;
  173. if (sz_to_recv > sizeof(buf))
  174. goto err;
  175. /*FALLTHROUGH*/
  176. case isc_usocketsource_reading:
  177. if (sz_to_recv != 0U) {
  178. n = recv(fd, buf, sz_to_recv, 0);
  179. if (n < 0) {
  180. if (errno == EWOULDBLOCK ||
  181. errno == EINTR)
  182. goto out;
  183. goto err;
  184. }
  185. } else
  186. n = 0;
  187. break;
  188. default:
  189. goto err;
  190. }
  191. if ((size_t)n != sz_to_recv)
  192. source->sources.usocket.sz_to_recv -= n;
  193. else
  194. source->sources.usocket.status =
  195. isc_usocketsource_connected;
  196. if (n == 0)
  197. goto out;
  198. entropypool_adddata(ent, buf, n, n * 8);
  199. added += n * 8;
  200. desired -= n;
  201. }
  202. goto out;
  203. err:
  204. close(fd);
  205. source->bad = ISC_TRUE;
  206. source->sources.usocket.status = isc_usocketsource_disconnected;
  207. source->sources.usocket.handle = -1;
  208. out:
  209. return (added);
  210. }
  211. /*
  212. * Poll each source, trying to get data from it to stuff into the entropy
  213. * pool.
  214. */
  215. static void
  216. fillpool(isc_entropy_t *ent, unsigned int desired, isc_boolean_t blocking) {
  217. unsigned int added;
  218. unsigned int remaining;
  219. unsigned int needed;
  220. unsigned int nsource;
  221. isc_entropysource_t *source;
  222. REQUIRE(VALID_ENTROPY(ent));
  223. needed = desired;
  224. /*
  225. * This logic is a little strange, so an explanation is in order.
  226. *
  227. * If needed is 0, it means we are being asked to "fill to whatever
  228. * we think is best." This means that if we have at least a
  229. * partially full pool (say, > 1/4th of the pool) we probably don't
  230. * need to add anything.
  231. *
  232. * Also, we will check to see if the "pseudo" count is too high.
  233. * If it is, try to mix in better data. Too high is currently
  234. * defined as 1/4th of the pool.
  235. *
  236. * Next, if we are asked to add a specific bit of entropy, make
  237. * certain that we will do so. Clamp how much we try to add to
  238. * (DIGEST_SIZE * 8 < needed < POOLBITS - entropy).
  239. *
  240. * Note that if we are in a blocking mode, we will only try to
  241. * get as much data as we need, not as much as we might want
  242. * to build up.
  243. */
  244. if (needed == 0) {
  245. REQUIRE(!blocking);
  246. if ((ent->pool.entropy >= RND_POOLBITS / 4)
  247. && (ent->pool.pseudo <= RND_POOLBITS / 4))
  248. return;
  249. needed = THRESHOLD_BITS * 4;
  250. } else {
  251. needed = ISC_MAX(needed, THRESHOLD_BITS);
  252. needed = ISC_MIN(needed, RND_POOLBITS);
  253. }
  254. /*
  255. * In any case, clamp how much we need to how much we can add.
  256. */
  257. needed = ISC_MIN(needed, RND_POOLBITS - ent->pool.entropy);
  258. /*
  259. * But wait! If we're not yet initialized, we need at least
  260. * THRESHOLD_BITS
  261. * of randomness.
  262. */
  263. if (ent->initialized < THRESHOLD_BITS)
  264. needed = ISC_MAX(needed, THRESHOLD_BITS - ent->initialized);
  265. /*
  266. * Poll each file source to see if we can read anything useful from
  267. * it. XXXMLG When where are multiple sources, we should keep a
  268. * record of which one we last used so we can start from it (or the
  269. * next one) to avoid letting some sources build up entropy while
  270. * others are always drained.
  271. */
  272. added = 0;
  273. remaining = needed;
  274. if (ent->nextsource == NULL) {
  275. ent->nextsource = ISC_LIST_HEAD(ent->sources);
  276. if (ent->nextsource == NULL)
  277. return;
  278. }
  279. source = ent->nextsource;
  280. again_file:
  281. for (nsource = 0; nsource < ent->nsources; nsource++) {
  282. unsigned int got;
  283. if (remaining == 0)
  284. break;
  285. got = 0;
  286. switch ( source->type ) {
  287. case ENTROPY_SOURCETYPE_FILE:
  288. got = get_from_filesource(source, remaining);
  289. break;
  290. case ENTROPY_SOURCETYPE_USOCKET:
  291. got = get_from_usocketsource(source, remaining);
  292. break;
  293. }
  294. added += got;
  295. remaining -= ISC_MIN(remaining, got);
  296. source = ISC_LIST_NEXT(source, link);
  297. if (source == NULL)
  298. source = ISC_LIST_HEAD(ent->sources);
  299. }
  300. ent->nextsource = source;
  301. if (blocking && remaining != 0) {
  302. int fds;
  303. fds = wait_for_sources(ent);
  304. if (fds > 0)
  305. goto again_file;
  306. }
  307. /*
  308. * Here, if there are bits remaining to be had and we can block,
  309. * check to see if we have a callback source. If so, call them.
  310. */
  311. source = ISC_LIST_HEAD(ent->sources);
  312. while ((remaining != 0) && (source != NULL)) {
  313. unsigned int got;
  314. got = 0;
  315. if (source->type == ENTROPY_SOURCETYPE_CALLBACK)
  316. got = get_from_callback(source, remaining, blocking);
  317. added += got;
  318. remaining -= ISC_MIN(remaining, got);
  319. if (added >= needed)
  320. break;
  321. source = ISC_LIST_NEXT(source, link);
  322. }
  323. /*
  324. * Mark as initialized if we've added enough data.
  325. */
  326. if (ent->initialized < THRESHOLD_BITS)
  327. ent->initialized += added;
  328. }
  329. static int
  330. wait_for_sources(isc_entropy_t *ent) {
  331. isc_entropysource_t *source;
  332. int maxfd, fd;
  333. int cc;
  334. fd_set reads;
  335. fd_set writes;
  336. maxfd = -1;
  337. FD_ZERO(&reads);
  338. FD_ZERO(&writes);
  339. source = ISC_LIST_HEAD(ent->sources);
  340. while (source != NULL) {
  341. if (source->type == ENTROPY_SOURCETYPE_FILE) {
  342. fd = source->sources.file.handle;
  343. if (fd >= 0) {
  344. maxfd = ISC_MAX(maxfd, fd);
  345. FD_SET(fd, &reads);
  346. }
  347. }
  348. if (source->type == ENTROPY_SOURCETYPE_USOCKET) {
  349. fd = source->sources.usocket.handle;
  350. if (fd >= 0) {
  351. switch (source->sources.usocket.status) {
  352. case isc_usocketsource_disconnected:
  353. break;
  354. case isc_usocketsource_connecting:
  355. case isc_usocketsource_connected:
  356. case isc_usocketsource_ndesired:
  357. maxfd = ISC_MAX(maxfd, fd);
  358. FD_SET(fd, &writes);
  359. break;
  360. case isc_usocketsource_wrote:
  361. case isc_usocketsource_reading:
  362. maxfd = ISC_MAX(maxfd, fd);
  363. FD_SET(fd, &reads);
  364. break;
  365. }
  366. }
  367. }
  368. source = ISC_LIST_NEXT(source, link);
  369. }
  370. if (maxfd < 0)
  371. return (-1);
  372. cc = select(maxfd + 1, &reads, &writes, NULL, NULL);
  373. if (cc < 0)
  374. return (-1);
  375. return (cc);
  376. }
  377. static void
  378. destroyfilesource(isc_entropyfilesource_t *source) {
  379. (void)close(source->handle);
  380. }
  381. static void
  382. destroyusocketsource(isc_entropyusocketsource_t *source) {
  383. close(source->handle);
  384. }
  385. /*
  386. * Make a fd non-blocking
  387. */
  388. static isc_result_t
  389. make_nonblock(int fd) {
  390. int ret;
  391. int flags;
  392. char strbuf[ISC_STRERRORSIZE];
  393. #ifdef USE_FIONBIO_IOCTL
  394. int on = 1;
  395. ret = ioctl(fd, FIONBIO, (char *)&on);
  396. #else
  397. flags = fcntl(fd, F_GETFL, 0);
  398. flags |= PORT_NONBLOCK;
  399. ret = fcntl(fd, F_SETFL, flags);
  400. #endif
  401. if (ret == -1) {
  402. isc__strerror(errno, strbuf, sizeof(strbuf));
  403. UNEXPECTED_ERROR(__FILE__, __LINE__,
  404. #ifdef USE_FIONBIO_IOCTL
  405. "ioctl(%d, FIONBIO, &on): %s", fd,
  406. #else
  407. "fcntl(%d, F_SETFL, %d): %s", fd, flags,
  408. #endif
  409. strbuf);
  410. return (ISC_R_UNEXPECTED);
  411. }
  412. return (ISC_R_SUCCESS);
  413. }
  414. isc_result_t
  415. isc_entropy_createfilesource(isc_entropy_t *ent, const char *fname) {
  416. int fd;
  417. struct stat _stat;
  418. isc_boolean_t is_usocket = ISC_FALSE;
  419. isc_boolean_t is_connected = ISC_FALSE;
  420. isc_result_t ret;
  421. isc_entropysource_t *source;
  422. REQUIRE(VALID_ENTROPY(ent));
  423. REQUIRE(fname != NULL);
  424. LOCK(&ent->lock);
  425. if (stat(fname, &_stat) < 0) {
  426. ret = isc__errno2result(errno);
  427. goto errout;
  428. }
  429. /*
  430. * Solaris 2.5.1 does not have support for sockets (S_IFSOCK),
  431. * but it does return type S_IFIFO (the OS believes that
  432. * the socket is a fifo). This may be an issue if we tell
  433. * the program to look at an actual FIFO as its source of
  434. * entropy.
  435. */
  436. #if defined(S_ISSOCK)
  437. if (S_ISSOCK(_stat.st_mode))
  438. is_usocket = ISC_TRUE;
  439. #endif
  440. #if defined(S_ISFIFO) && defined(sun)
  441. if (S_ISFIFO(_stat.st_mode))
  442. is_usocket = ISC_TRUE;
  443. #endif
  444. if (is_usocket)
  445. fd = socket(PF_UNIX, SOCK_STREAM, 0);
  446. else
  447. fd = open(fname, O_RDONLY | PORT_NONBLOCK, 0);
  448. if (fd < 0) {
  449. ret = isc__errno2result(errno);
  450. goto errout;
  451. }
  452. ret = make_nonblock(fd);
  453. if (ret != ISC_R_SUCCESS)
  454. goto closefd;
  455. if (is_usocket) {
  456. struct sockaddr_un sname;
  457. memset(&sname, 0, sizeof(sname));
  458. sname.sun_family = AF_UNIX;
  459. strncpy(sname.sun_path, fname, sizeof(sname.sun_path));
  460. sname.sun_path[sizeof(sname.sun_path)-1] = '0';
  461. #ifdef ISC_PLATFORM_HAVESALEN
  462. #if !defined(SUN_LEN)
  463. #define SUN_LEN(su) \
  464. (sizeof(*(su)) - sizeof((su)->sun_path) + strlen((su)->sun_path))
  465. #endif
  466. sname.sun_len = SUN_LEN(&sname);
  467. #endif
  468. if (connect(fd, (struct sockaddr *) &sname,
  469. sizeof(struct sockaddr_un)) < 0) {
  470. if (errno != EINPROGRESS) {
  471. ret = isc__errno2result(errno);
  472. goto closefd;
  473. }
  474. } else
  475. is_connected = ISC_TRUE;
  476. }
  477. source = isc_mem_get(ent->mctx, sizeof(isc_entropysource_t));
  478. if (source == NULL) {
  479. ret = ISC_R_NOMEMORY;
  480. goto closefd;
  481. }
  482. /*
  483. * From here down, no failures can occur.
  484. */
  485. source->magic = SOURCE_MAGIC;
  486. source->ent = ent;
  487. source->total = 0;
  488. source->bad = ISC_FALSE;
  489. memset(source->name, 0, sizeof(source->name));
  490. ISC_LINK_INIT(source, link);
  491. if (is_usocket) {
  492. source->sources.usocket.handle = fd;
  493. if (is_connected)
  494. source->sources.usocket.status =
  495. isc_usocketsource_connected;
  496. else
  497. source->sources.usocket.status =
  498. isc_usocketsource_connecting;
  499. source->sources.usocket.sz_to_recv = 0;
  500. source->type = ENTROPY_SOURCETYPE_USOCKET;
  501. } else {
  502. source->sources.file.handle = fd;
  503. source->type = ENTROPY_SOURCETYPE_FILE;
  504. }
  505. /*
  506. * Hook it into the entropy system.
  507. */
  508. ISC_LIST_APPEND(ent->sources, source, link);
  509. ent->nsources++;
  510. UNLOCK(&ent->lock);
  511. return (ISC_R_SUCCESS);
  512. closefd:
  513. (void)close(fd);
  514. errout:
  515. UNLOCK(&ent->lock);
  516. return (ret);
  517. }