1/* getrandom.c - Libgcrypt Random Number client
2 * Copyright (C) 2006 Free Software Foundation, Inc.
3 *
4 * Getrandom is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published
6 * by the Free Software Foundation; either version 2 of the License,
7 * or (at your option) any later version.
8 *
9 * Getrandom is distributed in the hope that it will be useful, but
10 * WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12 * General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
17 * 02110-1301, USA.
18 */
19
20#include <config.h>
21#include <stdio.h>
22#include <stddef.h>
23#include <stdlib.h>
24#include <assert.h>
25#include <sys/types.h>
26#include <sys/stat.h>
27#include <stdarg.h>
28#include <sys/socket.h>
29#include <sys/un.h>
30#include <unistd.h>
31#include <errno.h>
32
33#define PGM "getrandom"
34#define MYVERSION_LINE PGM " (Libgcrypt) " VERSION
35#define BUGREPORT_LINE "\nReport bugs to <bug-libgcrypt@gnupg.org>.\n"
36
37
38static void
39logit (const char *format, ...)
40{
41  va_list arg_ptr;
42
43  va_start (arg_ptr, format) ;
44  fputs (PGM ": ", stderr);
45  vfprintf (stderr, format, arg_ptr);
46  putc ('\n', stderr);
47  va_end (arg_ptr);
48}
49
50
51/* Send LENGTH bytes of BUFFER to file descriptor FD.  Returns 0 on
52   success or another value on write error. */
53static int
54writen (int fd, const void *buffer, size_t length)
55{
56  while (length)
57    {
58      ssize_t n;
59
60      do
61        n = write (fd, buffer, length);
62      while (n < 0 && errno == EINTR);
63      if (n < 0)
64         {
65           logit ("write error: %s", strerror (errno));
66           return -1; /* write error */
67         }
68      length -= n;
69      buffer = (const char *)buffer + n;
70    }
71  return 0;  /* Okay */
72}
73
74
75
76
77static void
78print_version (int with_help)
79{
80  fputs (MYVERSION_LINE "\n"
81         "Copyright (C) 2006 Free Software Foundation, Inc.\n"
82         "License GPLv2+: GNU GPL version 2 or later "
83         "<http://www.gnu.org/licenses/old-licenses/gpl-2.0.html>\n"
84         "This is free software: you are free to change and redistribute it.\n"
85         "There is NO WARRANTY, to the extent permitted by law.\n",
86         stdout);
87
88  if (with_help)
89    fputs ("\n"
90           "Usage: " PGM " [OPTIONS] NBYTES\n"
91           "Connect to libgcrypt's random number daemon and "
92           "return random numbers"
93           "\n"
94           "  --nonce       Return weak random suitable for a nonce\n"
95           "  --very-strong Return very strong random\n"
96           "  --ping        Send a ping\n"
97           "  --socket NAME Name of sockket to connect to\n"
98           "  --hex         Return result as a hex dump\n"
99           "  --verbose     Show what we are doing\n"
100           "  --version     Print version of the program and exit\n"
101           "  --help        Display this help and exit\n"
102           BUGREPORT_LINE, stdout );
103
104  exit (0);
105}
106
107static int
108print_usage (void)
109{
110  fputs ("usage: " PGM " [OPTIONS] NBYTES\n", stderr);
111  fputs ("       (use --help to display options)\n", stderr);
112  exit (1);
113}
114
115
116int
117main (int argc, char **argv)
118{
119  struct sockaddr_un *srvr_addr;
120  socklen_t addrlen;
121  int fd;
122  int rc;
123  unsigned char buffer[300];
124  int nleft, nread;
125  const char *socketname = "/var/run/libgcrypt/S.gcryptrnd";
126  int do_ping = 0;
127  int get_nonce = 0;
128  int get_very_strong = 0;
129  int req_nbytes, nbytes, n;
130  int verbose = 0;
131  int fail = 0;
132  int do_hex = 0;
133
134  if (argc)
135    {
136      argc--; argv++;
137    }
138  while (argc && **argv == '-' && (*argv)[1] == '-')
139    {
140      if (!(*argv)[2])
141        {
142          argc--; argv++;
143          break;
144        }
145      else if (!strcmp (*argv, "--version"))
146        print_version (0);
147      else if (!strcmp (*argv, "--help"))
148        print_version (1);
149      else if (!strcmp (*argv, "--socket") && argc > 1 )
150        {
151          argc--; argv++;
152          socketname = *argv;
153          argc--; argv++;
154        }
155      else if (!strcmp (*argv, "--nonce"))
156        {
157          argc--; argv++;
158          get_nonce = 1;
159        }
160      else if (!strcmp (*argv, "--very-strong"))
161        {
162          argc--; argv++;
163          get_very_strong = 1;
164        }
165      else if (!strcmp (*argv, "--ping"))
166        {
167          argc--; argv++;
168          do_ping = 1;
169        }
170      else if (!strcmp (*argv, "--hex"))
171        {
172          argc--; argv++;
173          do_hex = 1;
174        }
175      else if (!strcmp (*argv, "--verbose"))
176        {
177          argc--; argv++;
178          verbose = 1;
179        }
180      else
181        print_usage ();
182    }
183
184
185  if (!argc && do_ping)
186    ; /* This is allowed. */
187  else if (argc != 1)
188    print_usage ();
189  req_nbytes = argc? atoi (*argv) : 0;
190
191  if (req_nbytes < 0)
192    print_usage ();
193
194  /* Create a socket. */
195  fd = socket (AF_UNIX, SOCK_STREAM, 0);
196  if (fd == -1)
197    {
198      logit ("can't create socket: %s", strerror (errno));
199      exit (1);
200    }
201  srvr_addr = malloc (sizeof *srvr_addr);
202  if (!srvr_addr)
203    {
204      logit ("malloc failed: %s", strerror (errno));
205      exit (1);
206    }
207  memset (srvr_addr, 0, sizeof *srvr_addr);
208  srvr_addr->sun_family = AF_UNIX;
209  if (strlen (socketname) + 1 >= sizeof (srvr_addr->sun_path))
210    {
211      logit ("socket name `%s' too long", socketname);
212      exit (1);
213    }
214  strcpy (srvr_addr->sun_path, socketname);
215  addrlen = (offsetof (struct sockaddr_un, sun_path)
216             + strlen (srvr_addr->sun_path) + 1);
217  rc = connect (fd, (struct sockaddr*) srvr_addr, addrlen);
218  if (rc == -1)
219    {
220      logit ("error connecting socket `%s': %s",
221             srvr_addr->sun_path, strerror (errno));
222      close (fd);
223      exit (1);
224    }
225
226  do
227    {
228      nbytes = req_nbytes > 255? 255 : req_nbytes;
229      req_nbytes -= nbytes;
230
231      buffer[0] = 3;
232      if (do_ping)
233        buffer[1] = 0;
234      else if (get_nonce)
235        buffer[1] = 10;
236      else if (get_very_strong)
237        buffer[1] = 12;
238      else
239        buffer[1] = 11;
240      buffer[2] = nbytes;
241      if (writen (fd, buffer, 3))
242        fail = 1;
243      else
244        {
245          for (nleft=2, nread=0; nleft > 0; )
246            {
247              do
248                n = read (fd, buffer+nread, nleft);
249              while (n < 0 && errno == EINTR);
250              if (n < 0)
251                {
252                  logit ("read error: %s", strerror (errno));
253                  exit (1);
254                }
255              nleft -= n;
256              nread += n;
257              if (nread && buffer[0])
258                {
259                  logit ("server returned error code %d", buffer[0]);
260                  exit (1);
261                }
262            }
263          if (verbose)
264            logit ("received response with %d bytes of data", buffer[1]);
265          if (buffer[1] < nbytes)
266            {
267              logit ("warning: server returned less bytes than requested");
268              fail = 1;
269            }
270          else if (buffer[1] > nbytes && !do_ping)
271            {
272              logit ("warning: server returned more bytes than requested");
273              fail = 1;
274            }
275          nbytes = buffer[1];
276          if (nbytes > sizeof buffer)
277            {
278              logit ("buffer too short to receive data");
279              exit (1);
280            }
281
282          for (nleft=nbytes, nread=0; nleft > 0; )
283            {
284              do
285                n = read (fd, buffer+nread, nleft);
286              while (n < 0 && errno == EINTR);
287              if (n < 0)
288                {
289                  logit ("read error: %s", strerror (errno));
290                  exit (1);
291                }
292              nleft -= n;
293              nread += n;
294            }
295
296          if (do_hex)
297            {
298              for (n=0; n < nbytes; n++)
299                {
300                  if (!n)
301                    ;
302                  else if (!(n % 16))
303                    putchar ('\n');
304                  else
305                    putchar (' ');
306                  printf ("%02X", buffer[n]);
307                }
308              if (nbytes)
309                putchar ('\n');
310            }
311          else
312            {
313              if (fwrite (buffer, nbytes, 1, stdout) != 1)
314                {
315                  logit ("error writing to stdout: %s", strerror (errno));
316                  fail = 1;
317                }
318            }
319        }
320    }
321  while (!fail && req_nbytes);
322
323  close (fd);
324  free (srvr_addr);
325  return fail? 1 : 0;
326}
327