xref: /xnu-11215/bsd/dev/random/randomdev.c (revision 76e12aa3)
1 /*
2  * Copyright (c) 1999-2009 Apple, Inc. All rights reserved.
3  *
4  * @APPLE_OSREFERENCE_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. The rights granted to you under the License
10  * may not be used to create, or enable the creation or redistribution of,
11  * unlawful or unlicensed copies of an Apple operating system, or to
12  * circumvent, violate, or enable the circumvention or violation of, any
13  * terms of an Apple operating system software license agreement.
14  *
15  * Please obtain a copy of the License at
16  * http://www.opensource.apple.com/apsl/ and read it before using this file.
17  *
18  * The Original Code and all software distributed under the License are
19  * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20  * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21  * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22  * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23  * Please see the License for the specific language governing rights and
24  * limitations under the License.
25  *
26  * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
27  */
28 
29 
30 #include <sys/param.h>
31 #include <sys/systm.h>
32 #include <sys/proc.h>
33 #include <sys/errno.h>
34 #include <sys/ioctl.h>
35 #include <sys/conf.h>
36 #include <sys/fcntl.h>
37 #include <string.h>
38 #include <miscfs/devfs/devfs.h>
39 #include <kern/locks.h>
40 #include <kern/clock.h>
41 #include <sys/time.h>
42 #include <sys/malloc.h>
43 #include <sys/sysproto.h>
44 #include <sys/uio_internal.h>
45 
46 #include <dev/random/randomdev.h>
47 
48 #include <libkern/OSByteOrder.h>
49 #include <libkern/OSAtomic.h>
50 
51 #include <mach/mach_time.h>
52 
53 #define RANDOM_MAJOR  -1 /* let the kernel pick the device number */
54 #define RANDOM_MINOR   0
55 #define URANDOM_MINOR  1
56 
57 d_ioctl_t       random_ioctl;
58 
59 /*
60  * A struct describing which functions will get invoked for certain
61  * actions.
62  */
63 static struct cdevsw random_cdevsw =
64 {
65 	random_open,		/* open */
66 	random_close,		/* close */
67 	random_read,		/* read */
68 	random_write,		/* write */
69 	random_ioctl,		/* ioctl */
70 	(stop_fcn_t *)nulldev, /* stop */
71 	(reset_fcn_t *)nulldev, /* reset */
72 	NULL,				/* tty's */
73 	eno_select,			/* select */
74 	eno_mmap,			/* mmap */
75 	eno_strat,			/* strategy */
76 	eno_getc,			/* getc */
77 	eno_putc,			/* putc */
78 	0					/* type */
79 };
80 
81 
82 /*
83  * Called to initialize our device,
84  * and to register ourselves with devfs
85  */
86 void
87 random_init(void)
88 {
89 	int ret;
90 
91 	ret = cdevsw_add(RANDOM_MAJOR, &random_cdevsw);
92 	if (ret < 0) {
93 		panic("random_init: failed to allocate a major number!");
94 	}
95 
96 	devfs_make_node(makedev (ret, RANDOM_MINOR), DEVFS_CHAR,
97 		UID_ROOT, GID_WHEEL, 0666, "random", 0);
98 
99 	/*
100 	 * also make urandom
101 	 * (which is exactly the same thing in our context)
102 	 */
103 	devfs_make_node(makedev (ret, URANDOM_MINOR), DEVFS_CHAR,
104 		UID_ROOT, GID_WHEEL, 0666, "urandom", 0);
105 
106 }
107 
108 int
109 random_ioctl(	__unused dev_t dev, u_long cmd, __unused caddr_t data,
110 				__unused int flag, __unused struct proc *p  )
111 {
112 	switch (cmd) {
113 	case FIONBIO:
114 	case FIOASYNC:
115 		break;
116 	default:
117 		return ENODEV;
118 	}
119 
120 	return (0);
121 }
122 
123 /*
124  * Open the device.  Make sure init happened, and make sure the caller is
125  * authorized.
126  */
127 
128 int
129 random_open(__unused dev_t dev, int flags, __unused int devtype, __unused struct proc *p)
130 {
131 	/*
132 	 * if we are being opened for write,
133 	 * make sure that we have privledges do so
134 	 */
135 	if (flags & FWRITE) {
136 		if (securelevel >= 2)
137 			return (EPERM);
138 #ifndef __APPLE__
139 		if ((securelevel >= 1) && proc_suser(p))
140 			return (EPERM);
141 #endif	/* !__APPLE__ */
142 	}
143 
144 	return (0);
145 }
146 
147 
148 /*
149  * close the device.
150  */
151 
152 int
153 random_close(__unused dev_t dev, __unused int flags, __unused int mode, __unused struct proc *p)
154 {
155 	return (0);
156 }
157 
158 
159 /*
160  * Get entropic data from the Security Server, and use it to reseed the
161  * prng.
162  */
163 int
164 random_write (dev_t dev, struct uio *uio, __unused int ioflag)
165 {
166     int retCode = 0;
167     char rdBuffer[256];
168 
169     if (minor(dev) != RANDOM_MINOR)
170 	return EPERM;
171 
172     /* Security server is sending us entropy */
173 
174     while (uio_resid(uio) > 0 && retCode == 0) {
175         /* get the user's data */
176         int bytesToInput = MIN(uio_resid(uio),
177 			       (user_ssize_t) sizeof(rdBuffer));
178         retCode = uiomove(rdBuffer, bytesToInput, uio);
179         if (retCode != 0)
180 	    break;
181 	retCode = write_random(rdBuffer, bytesToInput);
182         if (retCode != 0)
183 	    break;
184     }
185 
186     return retCode;
187 }
188 
189 /*
190  * return data to the caller.  Results unpredictable.
191  */
192 int
193 random_read(__unused dev_t dev, struct uio *uio, __unused int ioflag)
194 {
195 	int retCode = 0;
196 	char buffer[512];
197 
198 	user_ssize_t bytes_remaining = uio_resid(uio);
199 	while (bytes_remaining > 0 && retCode == 0) {
200 		int bytesToRead = MIN(bytes_remaining,
201 				      (user_ssize_t) sizeof(buffer));
202 		read_random(buffer, bytesToRead);
203 
204 		retCode = uiomove(buffer, bytesToRead, uio);
205 		if (retCode != 0)
206 			break;
207 
208 		bytes_remaining = uio_resid(uio);
209 	}
210 
211 	return retCode;
212 }
213 
214 /*
215  * Return an u_int32_t pseudo-random number.
216  */
217 u_int32_t
218 RandomULong(void)
219 {
220 	u_int32_t buf;
221 	read_random(&buf, sizeof (buf));
222 	return (buf);
223 }
224 
225 
226 int
227 getentropy(__unused struct proc * p, struct getentropy_args *gap, __unused int * ret) {
228 	user_addr_t user_addr;
229 	uint32_t user_size;
230 	char buffer[256];
231 
232 	user_addr = (vm_map_offset_t)gap->buffer;
233 	user_size = gap->size;
234 	/* Can't request more than 256 random bytes
235 	 * at once. Complying with openbsd getentropy()
236 	 */
237 	if (user_size > sizeof(buffer)) {
238 		return EINVAL;
239 	}
240 	read_random(buffer, user_size);
241 	return copyout(buffer, user_addr, user_size);
242 }
243