Home | History | Annotate | Line # | Download | only in xdm
      1 /*
      2  * Copyright (c) 1995,1999 Theo de Raadt.  All rights reserved.
      3  * Copyright (c) 2001-2002 Damien Miller.  All rights reserved.
      4  *
      5  * Redistribution and use in source and binary forms, with or without
      6  * modification, are permitted provided that the following conditions
      7  * are met:
      8  * 1. Redistributions of source code must retain the above copyright
      9  *    notice, this list of conditions and the following disclaimer.
     10  * 2. Redistributions in binary form must reproduce the above copyright
     11  *    notice, this list of conditions and the following disclaimer in the
     12  *    documentation and/or other materials provided with the distribution.
     13  *
     14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
     15  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
     16  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
     17  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
     18  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
     19  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     20  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     21  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
     23  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     24  */
     25 
     26 #include <sys/types.h>
     27 #include <sys/socket.h>
     28 #include <X11/Xos.h>
     29 #include <sys/un.h>
     30 #include <netinet/in.h>
     31 #include <errno.h>
     32 #include <signal.h>
     33 #include <stdio.h>
     34 #include <string.h>
     35 #include <unistd.h>
     36 
     37 #include "dm_auth.h"
     38 #include "dm_error.h"
     39 
     40 #ifndef INADDR_LOOPBACK
     41 # define INADDR_LOOPBACK 0x7F000001U
     42 #endif
     43 
     44 static ssize_t atomicio(ssize_t (*)(int, void *, size_t), int, void *, size_t);
     45 
     46 static ssize_t
     47 voidwrite(int d, void *buf, size_t nbytes)
     48 {
     49 	return write(d, buf, nbytes);
     50 }
     51 
     52 #ifndef offsetof
     53 # define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
     54 #endif
     55 
     56 /*
     57  * Collect 'len' bytes of entropy into 'buf' from PRNGD/EGD daemon
     58  * listening either on 'tcp_port', or via Unix domain socket at *
     59  * 'socket_path'.
     60  * Either a non-zero tcp_port or a non-null socket_path must be
     61  * supplied.
     62  * Returns 0 on success, -1 on error
     63  */
     64 int
     65 get_prngd_bytes(char *buf, int len,
     66     unsigned short tcp_port, char *socket_path)
     67 {
     68 	int fd, addr_len, rval, errors;
     69 	char msg[2];
     70 	struct sockaddr *addr;
     71 	struct sockaddr_in addr_in;
     72 	struct sockaddr_un addr_un;
     73 	int af;
     74 	void (*old_sigpipe)(int);
     75 
     76 	/* Sanity checks */
     77 	if (socket_path == NULL && tcp_port == 0) {
     78 		LogError("get_random_prngd: "
     79 		    "You must specify a port or a socket\n");
     80 		return -1;
     81 	}
     82 	if (socket_path != NULL &&
     83 	    strlen(socket_path) >= sizeof(addr_un.sun_path)) {
     84 		LogError("get_random_prngd: Random pool path is too long\n");
     85 		return -1;
     86 	}
     87 	if (len > 255) {
     88 		LogError("get_random_prngd: "
     89 		    "Too many bytes to read from PRNGD\n");
     90 		return -1;
     91 	}
     92 
     93 	memset(&addr_in, '\0', sizeof(addr));
     94 
     95 	if (tcp_port != 0) {
     96 		af = addr_in.sin_family = AF_INET;
     97 		addr_in.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
     98 		addr_in.sin_port = htons(tcp_port);
     99 		addr_len = sizeof(addr_in);
    100 		addr = (struct sockaddr *)&addr_in;
    101 	} else {
    102 		af = addr_un.sun_family = AF_UNIX;
    103 		strncpy(addr_un.sun_path, socket_path,
    104 		    sizeof(addr_un.sun_path));
    105 		addr_len = offsetof(struct sockaddr_un, sun_path) +
    106 		    strlen(socket_path) + 1;
    107 		addr = (struct sockaddr *)&addr_un;
    108 	}
    109 
    110 	old_sigpipe = signal(SIGPIPE, SIG_IGN);
    111 
    112 	errors = 0;
    113 	rval = -1;
    114 reopen:
    115 	fd = socket(af, SOCK_STREAM, 0);
    116 	if (fd == -1) {
    117 		LogInfo("Couldn't create socket: %s\n", strerror(errno));
    118 		goto done;
    119 	}
    120 
    121 	if (connect(fd, (struct sockaddr*)addr, addr_len) == -1) {
    122 		if (af == AF_INET) {
    123 			LogInfo("Couldn't connect to PRNGD port %d: %s\n",
    124 			    tcp_port, strerror(errno));
    125 		} else {
    126 			LogInfo("Couldn't connect to PRNGD socket"
    127 			    " \"%s\": %s\n",
    128 			    addr_un.sun_path, strerror(errno));
    129 		}
    130 		goto done;
    131 	}
    132 
    133 	/* Send blocking read request to PRNGD */
    134 	msg[0] = 0x02;
    135 	msg[1] = len;
    136 
    137 	if (atomicio(voidwrite, fd, msg, sizeof(msg)) != sizeof(msg)) {
    138 		if (errno == EPIPE && errors < 10) {
    139 			close(fd);
    140 			errors++;
    141 			goto reopen;
    142 		}
    143 		LogInfo("Couldn't write to PRNGD socket: %s\n",
    144 		    strerror(errno));
    145 		goto done;
    146 	}
    147 
    148 	if (atomicio(read, fd, buf, len) != len) {
    149 		if (errno == EPIPE && errors < 10) {
    150 			close(fd);
    151 			errors++;
    152 			goto reopen;
    153 		}
    154 		LogInfo("Couldn't read from PRNGD socket: %s\n",
    155 		    strerror(errno));
    156 		goto done;
    157 	}
    158 
    159 	rval = 0;
    160 done:
    161 	signal(SIGPIPE, old_sigpipe);
    162 	if (fd != -1)
    163 		close(fd);
    164 	return rval;
    165 }
    166 
    167 /*
    168  * ensure all of data on socket comes through. f==read || f==write
    169  */
    170 static ssize_t
    171 atomicio(ssize_t (*f)(int, void *, size_t), int fd, void *_s, size_t n)
    172 {
    173 	char *s = _s;
    174 	ssize_t res, pos = 0;
    175 
    176 	while (n > pos) {
    177 		res = (f) (fd, s + pos, n - pos);
    178 		switch (res) {
    179 		case -1:
    180 #ifdef EWOULDBLOCK
    181 			if (errno == EINTR || errno == EAGAIN
    182 			    || errno == EWOULDBLOCK)
    183 #else
    184 			if (errno == EINTR || errno == EAGAIN)
    185 #endif
    186 				continue;
    187 		case 0:
    188 			return (res);
    189 		default:
    190 			pos += res;
    191 		}
    192 	}
    193 	return (pos);
    194 }
    195