1/* Hurdish implementation of getrandom
2 Copyright (C) 2016-2024 Free Software Foundation, Inc.
3 This file is part of the GNU C Library.
4
5 The GNU C Library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Lesser General Public
7 License as published by the Free Software Foundation; either
8 version 2.1 of the License, or (at your option) any later version.
9
10 The GNU C Library is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Lesser General Public License for more details.
14
15 You should have received a copy of the GNU Lesser General Public
16 License along with the GNU C Library; if not, see
17 <https://www.gnu.org/licenses/>. */
18
19#include <hurd.h>
20#include <sys/random.h>
21#include <fcntl.h>
22
23__libc_rwlock_define_initialized (static, lock);
24static file_t random_server, random_server_nonblock,
25 urandom_server, urandom_server_nonblock;
26
27extern char *__trivfs_server_name __attribute__((weak));
28
29/* Write up to LENGTH bytes of randomness starting at BUFFER.
30 Return the number of bytes written, or -1 on error. */
31ssize_t
32__getrandom (void *buffer, size_t length, unsigned int flags)
33{
34 const char *random_source = "/dev/urandom";
35 int open_flags = O_RDONLY;
36 file_t server, *cached_server;
37 error_t err;
38 char *data = buffer;
39 mach_msg_type_number_t nread = length;
40
41 switch (flags)
42 {
43 case 0:
44 cached_server = &urandom_server;
45 break;
46 case GRND_RANDOM:
47 cached_server = &random_server;
48 break;
49 case GRND_NONBLOCK:
50 cached_server = &urandom_server_nonblock;
51 break;
52 case GRND_RANDOM | GRND_NONBLOCK:
53 cached_server = &random_server_nonblock;
54 break;
55 default:
56 return __hurd_fail (EINVAL);
57 }
58
59 if (flags & GRND_RANDOM)
60 random_source = "/dev/random";
61 if (flags & GRND_NONBLOCK)
62 open_flags |= O_NONBLOCK;
63 /* No point in passing either O_NOCTTY, O_IGNORE_CTTY, or O_CLOEXEC
64 to file_name_lookup, since we're not making an fd. */
65
66 if (&__trivfs_server_name && __trivfs_server_name
67 && __trivfs_server_name[0] == 'r'
68 && __trivfs_server_name[1] == 'a'
69 && __trivfs_server_name[2] == 'n'
70 && __trivfs_server_name[3] == 'd'
71 && __trivfs_server_name[4] == 'o'
72 && __trivfs_server_name[5] == 'm'
73 && __trivfs_server_name[6] == '\0')
74 /* We are random, don't try to read ourselves! */
75 return length;
76
77again:
78 __libc_rwlock_rdlock (lock);
79 server = *cached_server;
80 if (MACH_PORT_VALID (server))
81 /* Attempt to read some random data using this port. */
82 err = __io_read (server, &data, &nread, -1, length);
83 else
84 err = MACH_SEND_INVALID_DEST;
85 __libc_rwlock_unlock (lock);
86
87 if (err == MACH_SEND_INVALID_DEST || err == MIG_SERVER_DIED)
88 {
89 file_t oldserver = server;
90 mach_port_urefs_t urefs;
91
92 /* Slow path: the cached port didn't work, or there was no
93 cached port in the first place. */
94
95 __libc_rwlock_wrlock (lock);
96 server = *cached_server;
97 if (server != oldserver)
98 {
99 /* Someone else must have refetched the port while we were
100 waiting for the lock. */
101 __libc_rwlock_unlock (lock);
102 goto again;
103 }
104
105 if (MACH_PORT_VALID (server))
106 {
107 /* It could be that someone else has refetched the port and
108 it got the very same name. So check whether it is a send
109 right (and not a dead name). */
110 err = __mach_port_get_refs (__mach_task_self (), server,
111 MACH_PORT_RIGHT_SEND, &urefs);
112 if (!err && urefs > 0)
113 {
114 __libc_rwlock_unlock (lock);
115 goto again;
116 }
117
118 /* Now we're sure that it's dead. */
119 __mach_port_deallocate (__mach_task_self (), server);
120 }
121
122 server = *cached_server = __file_name_lookup (random_source,
123 open_flags, 0);
124 __libc_rwlock_unlock (lock);
125 if (!MACH_PORT_VALID (server))
126 {
127 if (errno == ENOENT)
128 /* No translator set up, we won't have support for it. */
129 errno = ENOSYS;
130 /* No luck. */
131 return -1;
132 }
133
134 goto again;
135 }
136
137 if (err)
138 return __hurd_fail (err);
139
140 if (data != buffer)
141 {
142 if (nread > length)
143 {
144 __vm_deallocate (__mach_task_self (), (vm_address_t) data, nread);
145 return __hurd_fail (EGRATUITOUS);
146 }
147 memcpy (buffer, data, nread);
148 __vm_deallocate (__mach_task_self (), (vm_address_t) data, nread);
149 }
150
151 return nread;
152}
153
154libc_hidden_def (__getrandom)
155weak_alias (__getrandom, getrandom)
156

source code of glibc/sysdeps/mach/hurd/getrandom.c