/* getrandom.c - Libgcrypt Random Number client * Copyright (C) 2006 Free Software Foundation, Inc. * * Getrandom is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published * by the Free Software Foundation; either version 2 of the License, * or (at your option) any later version. * * Getrandom is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. */ #include #include #include #include #include #include #include #include #include #include #include #include #define PGM "getrandom" #define MYVERSION_LINE PGM " (Libgcrypt) " VERSION #define BUGREPORT_LINE "\nReport bugs to .\n" static void logit (const char *format, ...) { va_list arg_ptr; va_start (arg_ptr, format) ; fputs (PGM ": ", stderr); vfprintf (stderr, format, arg_ptr); putc ('\n', stderr); va_end (arg_ptr); } /* Send LENGTH bytes of BUFFER to file descriptor FD. Returns 0 on success or another value on write error. */ static int writen (int fd, const void *buffer, size_t length) { while (length) { ssize_t n; do n = write (fd, buffer, length); while (n < 0 && errno == EINTR); if (n < 0) { logit ("write error: %s", strerror (errno)); return -1; /* write error */ } length -= n; buffer = (const char *)buffer + n; } return 0; /* Okay */ } static void print_version (int with_help) { fputs (MYVERSION_LINE "\n" "Copyright (C) 2006 Free Software Foundation, Inc.\n" "License GPLv2+: GNU GPL version 2 or later " "\n" "This is free software: you are free to change and redistribute it.\n" "There is NO WARRANTY, to the extent permitted by law.\n", stdout); if (with_help) fputs ("\n" "Usage: " PGM " [OPTIONS] NBYTES\n" "Connect to libgcrypt's random number daemon and " "return random numbers" "\n" " --nonce Return weak random suitable for a nonce\n" " --very-strong Return very strong random\n" " --ping Send a ping\n" " --socket NAME Name of sockket to connect to\n" " --hex Return result as a hex dump\n" " --verbose Show what we are doing\n" " --version Print version of the program and exit\n" " --help Display this help and exit\n" BUGREPORT_LINE, stdout ); exit (0); } static int print_usage (void) { fputs ("usage: " PGM " [OPTIONS] NBYTES\n", stderr); fputs (" (use --help to display options)\n", stderr); exit (1); } int main (int argc, char **argv) { struct sockaddr_un *srvr_addr; socklen_t addrlen; int fd; int rc; unsigned char buffer[300]; int nleft, nread; const char *socketname = "/var/run/libgcrypt/S.gcryptrnd"; int do_ping = 0; int get_nonce = 0; int get_very_strong = 0; int req_nbytes, nbytes, n; int verbose = 0; int fail = 0; int do_hex = 0; if (argc) { argc--; argv++; } while (argc && **argv == '-' && (*argv)[1] == '-') { if (!(*argv)[2]) { argc--; argv++; break; } else if (!strcmp (*argv, "--version")) print_version (0); else if (!strcmp (*argv, "--help")) print_version (1); else if (!strcmp (*argv, "--socket") && argc > 1 ) { argc--; argv++; socketname = *argv; argc--; argv++; } else if (!strcmp (*argv, "--nonce")) { argc--; argv++; get_nonce = 1; } else if (!strcmp (*argv, "--very-strong")) { argc--; argv++; get_very_strong = 1; } else if (!strcmp (*argv, "--ping")) { argc--; argv++; do_ping = 1; } else if (!strcmp (*argv, "--hex")) { argc--; argv++; do_hex = 1; } else if (!strcmp (*argv, "--verbose")) { argc--; argv++; verbose = 1; } else print_usage (); } if (!argc && do_ping) ; /* This is allowed. */ else if (argc != 1) print_usage (); req_nbytes = argc? atoi (*argv) : 0; if (req_nbytes < 0) print_usage (); /* Create a socket. */ fd = socket (AF_UNIX, SOCK_STREAM, 0); if (fd == -1) { logit ("can't create socket: %s", strerror (errno)); exit (1); } srvr_addr = malloc (sizeof *srvr_addr); if (!srvr_addr) { logit ("malloc failed: %s", strerror (errno)); exit (1); } memset (srvr_addr, 0, sizeof *srvr_addr); srvr_addr->sun_family = AF_UNIX; if (strlen (socketname) + 1 >= sizeof (srvr_addr->sun_path)) { logit ("socket name `%s' too long", socketname); exit (1); } strcpy (srvr_addr->sun_path, socketname); addrlen = (offsetof (struct sockaddr_un, sun_path) + strlen (srvr_addr->sun_path) + 1); rc = connect (fd, (struct sockaddr*) srvr_addr, addrlen); if (rc == -1) { logit ("error connecting socket `%s': %s", srvr_addr->sun_path, strerror (errno)); close (fd); exit (1); } do { nbytes = req_nbytes > 255? 255 : req_nbytes; req_nbytes -= nbytes; buffer[0] = 3; if (do_ping) buffer[1] = 0; else if (get_nonce) buffer[1] = 10; else if (get_very_strong) buffer[1] = 12; else buffer[1] = 11; buffer[2] = nbytes; if (writen (fd, buffer, 3)) fail = 1; else { for (nleft=2, nread=0; nleft > 0; ) { do n = read (fd, buffer+nread, nleft); while (n < 0 && errno == EINTR); if (n < 0) { logit ("read error: %s", strerror (errno)); exit (1); } nleft -= n; nread += n; if (nread && buffer[0]) { logit ("server returned error code %d", buffer[0]); exit (1); } } if (verbose) logit ("received response with %d bytes of data", buffer[1]); if (buffer[1] < nbytes) { logit ("warning: server returned less bytes than requested"); fail = 1; } else if (buffer[1] > nbytes && !do_ping) { logit ("warning: server returned more bytes than requested"); fail = 1; } nbytes = buffer[1]; if (nbytes > sizeof buffer) { logit ("buffer too short to receive data"); exit (1); } for (nleft=nbytes, nread=0; nleft > 0; ) { do n = read (fd, buffer+nread, nleft); while (n < 0 && errno == EINTR); if (n < 0) { logit ("read error: %s", strerror (errno)); exit (1); } nleft -= n; nread += n; } if (do_hex) { for (n=0; n < nbytes; n++) { if (!n) ; else if (!(n % 16)) putchar ('\n'); else putchar (' '); printf ("%02X", buffer[n]); } if (nbytes) putchar ('\n'); } else { if (fwrite (buffer, nbytes, 1, stdout) != 1) { logit ("error writing to stdout: %s", strerror (errno)); fail = 1; } } } } while (!fail && req_nbytes); close (fd); free (srvr_addr); return fail? 1 : 0; }