Mercurial > projects > pwm
changeset 12:8768fbd09bc5
Add generatepassword command to generate random passwords
Refactor and generalize handling of named arguments.
author | Guido Berhoerster <guido+pwm@berhoerster.name> |
---|---|
date | Thu, 03 Aug 2017 10:22:07 +0200 |
parents | 85bce13237cf |
children | cf81eb0c2d5a |
files | Makefile cmd.c pw.c pw.h pwm.1.xml rand-arc4random.c rand-dev-random.c rand-getrandom.c rand.h |
diffstat | 9 files changed, 676 insertions(+), 20 deletions(-) [+] |
line wrap: on
line diff
--- a/Makefile Mon Jul 31 09:20:21 2017 +0200 +++ b/Makefile Thu Aug 03 10:22:07 2017 +0200 @@ -77,49 +77,64 @@ OS_RELEASE := $(shell uname -r) ifeq ($(OS_NAME),Linux) + HAVE_ARC4RANDOM ?= 0 HAVE_ASPRINTF ?= 1 HAVE_ERR_H ?= 1 + HAVE_GETRANDOM ?= 0 HAVE_READPASSPHRASE_H ?= 0 HAVE_SETPROGNAME ?= 0 HAVE_SYS_TREE_H ?= 0 else ifneq ($(findstring $(OS_NAME),FreeBSD DragonFly),) + HAVE_ARC4RANDOM ?= 1 HAVE_ASPRINTF ?= 1 HAVE_ERR_H ?= 1 + HAVE_GETRANDOM ?= 0 HAVE_READPASSPHRASE_H ?= 1 HAVE_SETPROGNAME ?= 1 HAVE_SYS_TREE_H ?= 1 else ifeq ($(OS_NAME),NetBSD) + HAVE_ARC4RANDOM ?= 1 HAVE_ASPRINTF ?= 1 HAVE_ERR_H ?= 1 + HAVE_GETRANDOM ?= 0 HAVE_READPASSPHRASE_H ?= 0 HAVE_SYS_TREE_H ?= 1 HAVE_SETPROGNAME ?= 1 else ifeq ($(OS_NAME),OpenBSD) + HAVE_ARC4RANDOM ?= 1 HAVE_ASPRINTF ?= 1 HAVE_ERR_H ?= 1 + HAVE_GETRANDOM ?= 0 HAVE_READPASSPHRASE_H ?= 1 HAVE_SYS_TREE_H ?= 1 HAVE_SETPROGNAME ?= 1 else ifeq ($(OS_NAME),SunOS) ifeq ($(OS_RELEASE),5.10) + HAVE_ARC4RANDOM ?= 0 HAVE_ASPRINTF ?= 0 HAVE_ERR_H ?= 0 + HAVE_GETRANDOM ?= 0 else + HAVE_ARC4RANDOM ?= 1 HAVE_ASPRINTF ?= 1 HAVE_ERR_H ?= 1 + HAVE_GETRANDOM ?= 1 endif HAVE_READPASSPHRASE_H ?= 0 HAVE_SYS_TREE_H ?= 0 HAVE_SETPROGNAME ?= 0 else + HAVE_ARC4RANDOM ?= 0 HAVE_ASPRINTF ?= 0 HAVE_ERR_H ?= 0 + HAVE_GETRANDOM ?= 0 HAVE_READPASSPHRASE_H ?= 0 HAVE_SETPROGNAME ?= 0 HAVE_SYS_TREE_H ?= 0 endif OBJS = cmd.o \ + pw.o \ pwfile.o \ pwm.o \ tok.o \ @@ -141,6 +156,15 @@ else OBJS += compat/asprintf.o endif +ifeq ($(HAVE_ARC4RANDOM),1) + XCPPFLAGS += -DHAVE_ARC4RANDOM + OBJS += rand-arc4random.o +else ifeq ($(HAVE_GETRANDOM),1) + XCPPFLAGS += -DHAVE_GETRANDOM + OBJS += rand-getrandom.o +else + OBJS += rand-dev-random.o +endif ifeq ($(HAVE_ERR_H),1) XCPPFLAGS += -DHAVE_ERR_H else
--- a/cmd.c Mon Jul 31 09:20:21 2017 +0200 +++ b/cmd.c Thu Aug 03 10:22:07 2017 +0200 @@ -23,6 +23,7 @@ #include "compat.h" +#include <ctype.h> #ifdef HAVE_ERR_H #include <err.h> #endif /* HAVE_ERR_H */ @@ -38,12 +39,18 @@ #include <unistd.h> #include "cmd.h" +#include "pw.h" #include "pwfile.h" #include "util.h" #define TIME_FORMAT "%Y-%m-%dT%TZ" #define TIME_SIZE (4 + 1 + 2 + 1 + 2 + 1 + 8 + 1 + 1) +#define CHARS_DIGIT "0123456789" +#define CHARS_UPPER "ABCDEFGHIJKLMNOPQRSTUVWXYZ" +#define CHARS_LOWER "abcdefghijklmnopqrstuvwxyz" +#define CHARS_PUNCT "!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~" + enum field_type { FIELD_UNKNOWN = -1, FIELD_GROUP, @@ -56,10 +63,30 @@ FIELD_CTIME }; +enum cmd_generatepassword_arg_type { + CMD_GP_ARG_UNKNOWN = -1, + CMD_GP_ARG_LEN, + CMD_GP_ARG_CHARS, + CMD_GP_ARG_CHARCLASS +}; + +enum charclass_type { + CHARCLASS_UNKNOWN = -1, + CHARCLASS_DIGIT, + CHARCLASS_UPPER, + CHARCLASS_LOWER, + CHARCLASS_PUNCT, + CHARCLASS_ALPHA, + CHARCLASS_ALNUM, + CHARCLASS_XDIGIT, + CHARCLASS_GRAPH +}; + static enum cmd_return cmd_info(struct pwm_ctx *, int, char *[]); static enum cmd_return cmd_list(struct pwm_ctx *, int, char *[]); static enum cmd_return cmd_create(struct pwm_ctx *, int, char *[]); static enum cmd_return cmd_modify(struct pwm_ctx *, int, char *[]); +static enum cmd_return cmd_generatepassword(struct pwm_ctx *, int, char *[]); static enum cmd_return cmd_remove(struct pwm_ctx *, int, char *[]); static enum cmd_return cmd_show(struct pwm_ctx *, int, char *[]); static enum cmd_return cmd_pipe(struct pwm_ctx *, int, char *[]); @@ -70,7 +97,7 @@ static enum cmd_return cmd_write(struct pwm_ctx *, int, char *[]); static enum cmd_return cmd_quit(struct pwm_ctx *, int, char *[]); -static const char *field_names[] = { +static const char *field_namev[] = { "group", "title", "username", @@ -78,7 +105,8 @@ "notes", "url", "ctime", - "mtime" + "mtime", + NULL }; static const char *field_labels[] = { @@ -92,12 +120,45 @@ "Modified: " }; +static const char *cmd_generatepassword_argv[] = { + "len", + "char", + "charclass", + NULL +}; + +static const char *charclass_namev[] = { + "digit", + "upper", + "lower", + "punct", + "alpha", + "alnum", + "xdigit", + "graph", + NULL +}; + +static const char *charclass_values[] = { + CHARS_DIGIT, + CHARS_UPPER, + CHARS_LOWER, + CHARS_PUNCT, + CHARS_UPPER CHARS_LOWER, + CHARS_DIGIT CHARS_UPPER CHARS_LOWER, + CHARS_DIGIT "abcdef", + CHARS_DIGIT CHARS_UPPER CHARS_LOWER CHARS_PUNCT +}; + static struct cmd cmds[] = { { "i", "info", "info", "Show metadata information about the current file", cmd_info }, { "ls", "list", "list [field~regex ...]", "List entries", cmd_list }, { "c", "create", "create field=value ...", "Create entry", cmd_create }, { "m", "modify", "modify id field=value ...", "Modify entry", cmd_modify }, + { "gp", "generatepassword", "generatepassword [id] [len=n] [chars=n:chars] " + "[charclass=n:class] ...", "Randomly generate a password", + cmd_generatepassword }, { "rm", "remove", "remove id", "Delete entry", cmd_remove }, { "s", "show", "show id field", "Show entry", cmd_show }, { "p", "pipe", "pipe id field command", "Pipe entry to external command", @@ -114,24 +175,24 @@ { 0 } }; -static enum field_type -parse_field(char *field_arg, int sep, char **valuep) +static int +parse_arg(char *arg, const char *namev[], int sep, char **valuep) { - int i; - size_t field_name_len; + size_t i; + size_t name_len; - for (i = 0; i < (int)COUNTOF(field_names); i++) { - field_name_len = strlen(field_names[i]); - if ((strncmp(field_names[i], field_arg, field_name_len) == 0) && - (field_arg[field_name_len] == sep)) { + for (i = 0; namev[i] != NULL; i++) { + name_len = strlen(namev[i]); + if ((strncmp(namev[i], arg, name_len) == 0) && + (arg[name_len] == sep)) { if (valuep != NULL) { - *valuep = field_arg + field_name_len + 1; + *valuep = arg + name_len + 1; } return (i); } } - return (FIELD_UNKNOWN); + return (-1); } static int @@ -203,7 +264,7 @@ struct record *record; for (i = 1; i < argc; i++) { - type = parse_field(argv[i], '~', &value); + type = parse_arg(argv[i], field_namev, '~', &value); if (type == FIELD_UNKNOWN) { fprintf(stderr, "bad field name \"%s\"\n", argv[i]); goto out; @@ -319,7 +380,7 @@ } for (i = 1; i < argc; i++) { - type = parse_field(argv[i], '=', &value); + type = parse_arg(argv[i], field_namev, '=', &value); if (type == FIELD_UNKNOWN) { fprintf(stderr, "bad field assignment \"%s\"\n", argv[i]); @@ -377,7 +438,7 @@ } for (i = 2; i < argc; i++) { - type = parse_field(argv[i], '=', &value); + type = parse_arg(argv[i], field_namev, '=', &value); if (type == FIELD_UNKNOWN) { fprintf(stderr, "bad field assignment \"%s\"\n", argv[i]); @@ -418,6 +479,146 @@ } static enum cmd_return +cmd_generatepassword(struct pwm_ctx *ctx, int argc, char *argv[]) +{ + enum cmd_return retval = CMD_ERR; + unsigned int id = 0; + int i = 1; + char *value = NULL; + long x; + char *p; + size_t password_len = 16; + size_t chars_min; + const char *chars; + struct pw_char_group *char_groupv = NULL; + size_t char_groupv_len = 0; + int charclass; + size_t j; + char password[PWS3_MAX_PASSWORD_LEN + 1] = { 0 }; + + /* check if first argument is an id */ + if ((argc > 1) && (parse_id(argv[1], &id) == 0)) { + i++; + } + + for (; i < argc; i++) { + switch (parse_arg(argv[i], cmd_generatepassword_argv, '=', + &value)) { + case CMD_GP_ARG_LEN: + errno = 0; + x = strtol(value, &p, 10); + if ((errno != 0) || (*value == '\0') || (*p != '\0') || + (x > PWS3_MAX_PASSWORD_LEN) || (x <= 0)) { + fprintf(stderr, "invalid password length " + "\"%s\"\n", argv[i]); + goto out; + } + password_len = x; + break; + case CMD_GP_ARG_CHARS: + errno = 0; + x = strtol(value, &p, 10); + if ((errno != 0) || (*value == '\0') || (*p != ':') || + (x < 0) || (x > PWS3_MAX_PASSWORD_LEN)) { + fprintf(stderr, "invalid minimum number of " + "characters \"%s\"\n", argv[i]); + goto out; + } + chars_min = x; + + chars = ++p; + while (*p != '\0') { + if (!isascii(*p) || !isprint(*p)) { + fprintf(stderr, "invalid character in " + "character group \"%s\"\n", + argv[i]); + goto out; + } + p++; + } + + char_groupv = xrealloc(char_groupv, + sizeof (struct pw_char_group) * (char_groupv_len + + 1)); + char_groupv[char_groupv_len].chars = chars; + char_groupv[char_groupv_len].chars_min = chars_min; + char_groupv_len++; + break; + case CMD_GP_ARG_CHARCLASS: + errno = 0; + x = strtol(value, &p, 10); + if ((errno != 0) || (*value == '\0') || (*p != ':') || + (x < 0) || (x > PWS3_MAX_PASSWORD_LEN)) { + fprintf(stderr, "invalid minimum number of " + "characters \"%s\"\n", argv[i]); + goto out; + } + chars_min = x; + + charclass = parse_arg(++p, charclass_namev, '\0', NULL); + if (charclass < 0) { + fprintf(stderr, "unknown character class " + "\"%s\"\n", argv[i]); + goto out; + } + chars = charclass_values[charclass]; + char_groupv = xrealloc(char_groupv, + sizeof (struct pw_char_group) * (char_groupv_len + + 1)); + char_groupv[char_groupv_len].chars = chars; + char_groupv[char_groupv_len].chars_min = chars_min; + char_groupv_len++; + break; + default: + fprintf(stderr, "invalid argument \"%s\"\n", argv[i]); + retval = CMD_USAGE; + goto out; + } + } + + for (j = 0; j < char_groupv_len; j++) { + if (char_groupv[j].chars_min > password_len) { + fprintf(stderr, "invalid minimum number of " + "characters \"%zu:%s\"\n", char_groupv[j].chars_min, + char_groupv[j].chars); + goto out; + } + } + + if (char_groupv_len == 0) { + /* use defaults */ + char_groupv = xmalloc(sizeof (struct pw_char_group)); + char_groupv[0].chars = charclass_values[CHARCLASS_GRAPH]; + char_groupv[0].chars_min = 0; + char_groupv_len++; + } + + if (pw_genrandom(char_groupv, char_groupv_len, password, + password_len) != 0) { + fprintf(stderr, "failed to generate password that meets the " + "given constraints\n"); + goto out; + } + + if (id != 0) { + if (pwfile_modify_record(ctx, id, + &(struct record){ .password = password }) != 0) { + fprintf(stderr, "record %u does not exist\n", id); + goto out; + } + } else { + printf("%s\n", password); + } + + retval = CMD_OK; + +out: + free(char_groupv); + + return (retval); +} + +static enum cmd_return cmd_remove(struct pwm_ctx *ctx, int argc, char *argv[]) { unsigned int id; @@ -518,7 +719,7 @@ struct record *record; int i; enum field_type type; - int fields[COUNTOF(field_names)] = { 0 }; + int fields[COUNTOF(field_namev) - 1] = { 0 }; if (argc < 2) { return (CMD_USAGE); @@ -530,7 +731,7 @@ } for (i = 2; i < argc; i++) { - type = parse_field(argv[i], '\0', NULL); + type = parse_arg(argv[i], field_namev, '\0', NULL); if (type < 0) { fprintf(stderr, "bad field name \"%s\"\n", argv[i]); return (CMD_ERR); @@ -556,7 +757,7 @@ unsigned int id; struct record *record = NULL; enum field_type type; - int fields[COUNTOF(field_names)] = { 0 }; + int fields[COUNTOF(field_namev) - 1] = { 0 }; FILE *fp = NULL; if (argc != 4) { @@ -568,7 +769,7 @@ return (CMD_ERR); } - type = parse_field(argv[2], '\0', NULL); + type = parse_arg(argv[2], field_namev, '\0', NULL); if (type < 0) { fprintf(stderr, "bad field name \"%s\"\n", argv[2]); return (CMD_ERR);
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/pw.c Thu Aug 03 10:22:07 2017 +0200 @@ -0,0 +1,100 @@ +/* + * Copyright (C) 2017 Guido Berhoerster <guido+pwm@berhoerster.name> + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include "compat.h" + +#ifdef HAVE_ERR_H +#include <err.h> +#endif /* HAVE_ERR_H */ +#include <pws.h> +#include <stdint.h> +#include <string.h> + +#include "pw.h" +#include "rand.h" +#include "util.h" + +int +pw_genrandom(struct pw_char_group groups[], size_t groups_len, char *password, + size_t password_len) +{ + int retval = -1; + char *password_buf = NULL; + size_t *group_matches = NULL; + size_t i; + size_t chars_len = 0; + char *chars; + size_t j; + uint32_t r; + size_t k; + + password_buf = xmalloc(password_len + 1); + password_buf[password_len] = '\0'; + + group_matches = xmalloc(groups_len * sizeof (size_t)); + + for (i = 0; i < groups_len; i++) { + chars_len += strlen(groups[i].chars); + } + + chars = xmalloc(chars_len + 1); + chars[0] = '\0'; + for (i = 0; i < groups_len; i++) { + strcat(chars, groups[i].chars); + } + + for (k = 0; k < 100000; k++) { + memset(group_matches, 0, groups_len * sizeof (size_t)); + + for (j = 0; j < password_len; j++) { + r = rand_uniform(chars_len); + password_buf[j] = chars[r]; + + for (i = 0; i < groups_len; i++) { + if (strchr(groups[i].chars, chars[r]) != NULL) { + group_matches[i]++; + break; + } + } + } + + for (i = 0; i < groups_len; i++) { + if (group_matches[i] < groups[i].chars_min) { + /* try again */ + break; + } + } + if (i == groups_len) { + /* password meets all constraints */ + strcpy(password, password_buf); + retval = 0; + break; + } + } + + free(chars); + free(group_matches); + free(password_buf); + + return (retval); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/pw.h Thu Aug 03 10:22:07 2017 +0200 @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2017 Guido Berhoerster <guido+pwm@berhoerster.name> + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef PW_H +#define PW_H + +struct pw_char_group { + const char *chars; + size_t chars_min; +}; + +int pw_genrandom(struct pw_char_group [], size_t, char *, size_t); + +#endif /* !PW_H */
--- a/pwm.1.xml Mon Jul 31 09:20:21 2017 +0200 +++ b/pwm.1.xml Thu Aug 03 10:22:07 2017 +0200 @@ -34,7 +34,7 @@ <email>guido+pwm@berhoerster.name</email> <personblurb/> </author> - <date>31 July, 2017</date> + <date>3 August, 2017</date> </info> <refmeta> <refentrytitle>pwm</refentrytitle> @@ -393,6 +393,54 @@ </listitem> </varlistentry> <varlistentry> + <term>Generate a random password</term> + <listitem> + <cmdsynopsis> + <command>generatepassword</command> + <arg choice="opt"> + <replaceable>id</replaceable> + </arg> + <arg choice="opt"> + len=<replaceable>n</replaceable> + </arg> + <arg choice="opt" rep="repeat"> + chars=<replaceable>n</replaceable>:<replaceable>chars</replaceable> + </arg> + <arg choice="opt" rep="repeat"> + charclass=<replaceable>n</replaceable>:<replaceable>class</replaceable> + </arg> + </cmdsynopsis> + <cmdsynopsis> + <command>gp</command> + <arg choice="opt"> + <replaceable>id</replaceable> + </arg> + <arg choice="opt"> + len=<replaceable>n</replaceable> + </arg> + <arg choice="opt" rep="repeat"> + chars=<replaceable>n</replaceable>:<replaceable>chars</replaceable> + </arg> + <arg choice="opt" rep="repeat"> + charclass=<replaceable>n</replaceable>:<replaceable>class</replaceable> + </arg> + <sbr/> + </cmdsynopsis> + <para>Randomly generate a new password according to the specified + constraints. The <literal>len</literal> argument sets the length of + the generated password to <replaceable>n</replaceable> characters. + The <literal>chars</literal> argument constrains the password to + <replaceable>n</replaceable> from the set of characters + <replaceable>chars</replaceable>. Similarly, the + <literal>charclass</literal> argument to + <replaceable>n</replaceable> characters from the extended regular + expression character class <replaceable>class</replaceable>. + Multiple <literal>char</literal> and <literal>charclass</literal> + arguments may be specified, in which case the generated passwords + match all of them.</para> + </listitem> + </varlistentry> + <varlistentry> <term>Change the master password</term> <listitem> <cmdsynopsis>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/rand-arc4random.c Thu Aug 03 10:22:07 2017 +0200 @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2017 Guido Berhoerster <guido+pwm@berhoerster.name> + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include <stdlib.h> + +#include "rand.h" + +void +rand_buf(void *buf, size_t buf_size) +{ + arc4random_buf(buf, buf_size); +} + +uint32_t +rand_random(void) +{ + return (arc4random()); +} + +uint32_t +rand_uniform(uint32_t upper_bound) +{ + return (arc4random_uniform(upper_bound)); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/rand-dev-random.c Thu Aug 03 10:22:07 2017 +0200 @@ -0,0 +1,95 @@ +/* + * Copyright (C) 2017 Guido Berhoerster <guido+pwm@berhoerster.name> + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include "compat.h" + +#ifdef HAVE_ERR_H +#include <err.h> +#endif /* HAVE_ERR_H */ +#include <errno.h> +#include <fcntl.h> +#include <sys/stat.h> +#include <unistd.h> + +#include "rand.h" + +#ifdef __linux__ +#define PATH_DEV_RANDOM "/dev/urandom" +#else +#define PATH_DEV_RANDOM "/dev/random" +#endif /* __linux__ */ + +void +rand_buf(void *buf, size_t buf_size) +{ + unsigned char *p = buf; + int fd; + ssize_t nread; + size_t nsize = buf_size; + + do { + fd = open(PATH_DEV_RANDOM, O_RDONLY); + } while ((fd < 0) && (errno == EINTR)); + if (fd < 0) { + err(1, "open"); + } + + while (nsize > 0) { + nread = read(fd, p, nsize); + if (nread < 0) { + if (errno == EINTR) { + continue; + } + err(1, "read"); + } + p += nread; + nsize -= nread; + } + + while ((close(fd) < 0) && (errno == EINTR)); +} + +uint32_t +rand_random(void) +{ + uint32_t x; + + rand_buf(&x, sizeof (x)); + + return (x); +} + +/* random number between 0 and upper_bound - 1 without modulo bias */ +uint32_t +rand_uniform(uint32_t upper_bound) +{ + uint32_t r; + /* (2^32 - upper_bound) % upper_bound */ + uint32_t threshold = -upper_bound % upper_bound; + + do { + r = rand_random(); + } while (r < threshold); + + return (r % upper_bound); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/rand-getrandom.c Thu Aug 03 10:22:07 2017 +0200 @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2017 Guido Berhoerster <guido+pwm@berhoerster.name> + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include "compat.h" + +#include <stdlib.h> +/* glibc and Solaris 11 */ +#include <sys/random.h> + +#include "rand.h" + +#define RANDOM_CHUNK 256U + +void +rand_buf(void *buf, size_t buf_size) +{ + unsigned char *p = buf; + ssize_t nread; + size_t nsize = buf_size; + + while (nsize > 0) { + nread = getrandom(p, nsize, 0); + if (nread < 0) { + if (errno == EINTR) { + continue; + } + err(1, "read"); + } + nsize -= nread; + p += nread; + } +} + +uint32_t +rand_random(void) +{ + uint32_t r; + + getrandom(r, sizeof (uint32_t), 0); + + return (r); +} + +/* random number between 0 and upper_bound - 1 without modulo bias */ +uint32_t +rand_uniform(uint32_t upper_bound) +{ + uint32_t r; + /* (2^32 - upper_bound) % upper_bound */ + uint32_t threshold = -upper_bound % upper_bound; + + do { + r = rand_random(); + } while (r < threshold); + + return (r % upper_bound); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/rand.h Thu Aug 03 10:22:07 2017 +0200 @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2017 Guido Berhoerster <guido+pwm@berhoerster.name> + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef RAND_H +#define RAND_H + +#include <stdint.h> + +void rand_buf(void *, size_t); +uint32_t rand_random(void); +uint32_t rand_uniform(uint32_t); + +#endif /* !RAND_H */