1 | /* |
2 | * QEMU Random Number Generator Backend |
3 | * |
4 | * Copyright IBM, Corp. 2012 |
5 | * |
6 | * Authors: |
7 | * Anthony Liguori <aliguori@us.ibm.com> |
8 | * |
9 | * This work is licensed under the terms of the GNU GPL, version 2 or later. |
10 | * See the COPYING file in the top-level directory. |
11 | */ |
12 | |
13 | #include "qemu/osdep.h" |
14 | #include "sysemu/rng-random.h" |
15 | #include "sysemu/rng.h" |
16 | #include "qapi/error.h" |
17 | #include "qapi/qmp/qerror.h" |
18 | #include "qemu/main-loop.h" |
19 | #include "qemu/module.h" |
20 | |
21 | struct RngRandom |
22 | { |
23 | RngBackend parent; |
24 | |
25 | int fd; |
26 | char *filename; |
27 | }; |
28 | |
29 | /** |
30 | * A simple and incomplete backend to request entropy from /dev/random. |
31 | * |
32 | * This backend exposes an additional "filename" property that can be used to |
33 | * set the filename to use to open the backend. |
34 | */ |
35 | |
36 | static void entropy_available(void *opaque) |
37 | { |
38 | RngRandom *s = RNG_RANDOM(opaque); |
39 | |
40 | while (!QSIMPLEQ_EMPTY(&s->parent.requests)) { |
41 | RngRequest *req = QSIMPLEQ_FIRST(&s->parent.requests); |
42 | ssize_t len; |
43 | |
44 | len = read(s->fd, req->data, req->size); |
45 | if (len < 0 && errno == EAGAIN) { |
46 | return; |
47 | } |
48 | g_assert(len != -1); |
49 | |
50 | req->receive_entropy(req->opaque, req->data, len); |
51 | |
52 | rng_backend_finalize_request(&s->parent, req); |
53 | } |
54 | |
55 | /* We've drained all requests, the fd handler can be reset. */ |
56 | qemu_set_fd_handler(s->fd, NULL, NULL, NULL); |
57 | } |
58 | |
59 | static void rng_random_request_entropy(RngBackend *b, RngRequest *req) |
60 | { |
61 | RngRandom *s = RNG_RANDOM(b); |
62 | |
63 | if (QSIMPLEQ_EMPTY(&s->parent.requests)) { |
64 | /* If there are no pending requests yet, we need to |
65 | * install our fd handler. */ |
66 | qemu_set_fd_handler(s->fd, entropy_available, NULL, s); |
67 | } |
68 | } |
69 | |
70 | static void rng_random_opened(RngBackend *b, Error **errp) |
71 | { |
72 | RngRandom *s = RNG_RANDOM(b); |
73 | |
74 | if (s->filename == NULL) { |
75 | error_setg(errp, QERR_INVALID_PARAMETER_VALUE, |
76 | "filename" , "a valid filename" ); |
77 | } else { |
78 | s->fd = qemu_open(s->filename, O_RDONLY | O_NONBLOCK); |
79 | if (s->fd == -1) { |
80 | error_setg_file_open(errp, errno, s->filename); |
81 | } |
82 | } |
83 | } |
84 | |
85 | static char *rng_random_get_filename(Object *obj, Error **errp) |
86 | { |
87 | RngRandom *s = RNG_RANDOM(obj); |
88 | |
89 | return g_strdup(s->filename); |
90 | } |
91 | |
92 | static void rng_random_set_filename(Object *obj, const char *filename, |
93 | Error **errp) |
94 | { |
95 | RngBackend *b = RNG_BACKEND(obj); |
96 | RngRandom *s = RNG_RANDOM(obj); |
97 | |
98 | if (b->opened) { |
99 | error_setg(errp, QERR_PERMISSION_DENIED); |
100 | return; |
101 | } |
102 | |
103 | g_free(s->filename); |
104 | s->filename = g_strdup(filename); |
105 | } |
106 | |
107 | static void rng_random_init(Object *obj) |
108 | { |
109 | RngRandom *s = RNG_RANDOM(obj); |
110 | |
111 | object_property_add_str(obj, "filename" , |
112 | rng_random_get_filename, |
113 | rng_random_set_filename, |
114 | NULL); |
115 | |
116 | s->filename = g_strdup("/dev/urandom" ); |
117 | s->fd = -1; |
118 | } |
119 | |
120 | static void rng_random_finalize(Object *obj) |
121 | { |
122 | RngRandom *s = RNG_RANDOM(obj); |
123 | |
124 | if (s->fd != -1) { |
125 | qemu_set_fd_handler(s->fd, NULL, NULL, NULL); |
126 | qemu_close(s->fd); |
127 | } |
128 | |
129 | g_free(s->filename); |
130 | } |
131 | |
132 | static void rng_random_class_init(ObjectClass *klass, void *data) |
133 | { |
134 | RngBackendClass *rbc = RNG_BACKEND_CLASS(klass); |
135 | |
136 | rbc->request_entropy = rng_random_request_entropy; |
137 | rbc->opened = rng_random_opened; |
138 | } |
139 | |
140 | static const TypeInfo rng_random_info = { |
141 | .name = TYPE_RNG_RANDOM, |
142 | .parent = TYPE_RNG_BACKEND, |
143 | .instance_size = sizeof(RngRandom), |
144 | .class_init = rng_random_class_init, |
145 | .instance_init = rng_random_init, |
146 | .instance_finalize = rng_random_finalize, |
147 | }; |
148 | |
149 | static void register_types(void) |
150 | { |
151 | type_register_static(&rng_random_info); |
152 | } |
153 | |
154 | type_init(register_types); |
155 | |