Mercurial > projects > pwm
diff cmd.c @ 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 |
line wrap: on
line diff
--- 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);