diff options
Diffstat (limited to 'nslcd/hackers_parse.c')
-rw-r--r-- | nslcd/hackers_parse.c | 214 |
1 files changed, 214 insertions, 0 deletions
diff --git a/nslcd/hackers_parse.c b/nslcd/hackers_parse.c new file mode 100644 index 0000000..0146197 --- /dev/null +++ b/nslcd/hackers_parse.c @@ -0,0 +1,214 @@ +/* hackers_parse.c - load user data from hackers.git + * + * Copyright (C) 2014 Luke Shumaker + * + * This program 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 3 of the License, or + * (at your option) any later version. + * + * This program 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, see <http://www.gnu.org/licenses/>. + */ +#define _GNU_SOURCE + +#include <stdbool.h> +#include <stdio.h> +#include <yaml.h> + +/* These three are just for name2gid, which is surprisingly + * complicated. */ +#include <errno.h> +#include <grp.h> +#include <unistd.h> /* for sysconf(3) */ + +#include "hackers_parse.h" + +/* None of the malloc-ish calls are error checked. Bite me. */ + +#define DEFAULT_PASSWORD "!" + +#define ASSERT(expr) if (!(expr)) goto error + +/* Get a string value from a YAML scalar node */ +#define STR_VALUE(node) \ + ({ \ + ASSERT((node)->type == YAML_SCALAR_NODE); \ + ((char*)(node)->data.scalar.value); \ + }) + +/* Bitmask flags for the completion of the fields in + * 'struct passwd' (which is defined in <pwd.h>) */ +#define PW_NAME (1 << 0) +#define PW_PASSWD (1 << 1) +#define PW_UID (1 << 2) +#define PW_GID (1 << 3) +#define PW_GECOS (1 << 4) +#define PW_DIR (1 << 5) +#define PW_SHELL (1 << 6) +#define PW_ALL ((1 << 7) - 1) + +static +gid_t +name2gid(const char *name) { + gid_t gid; + ssize_t buflen = sysconf(_SC_GETGR_R_SIZE_MAX); + if (buflen < 1) + buflen = 256; + char *buf = malloc(buflen); + struct group grp; + struct group *ret; + while (getgrnam_r(name, &grp, buf, (size_t)buflen, &ret) < 0) { + if (errno == ERANGE) { + buflen += 256; + buf = realloc(buf, buflen); + } else { + gid = 0; + goto end; + } + } + if (ret == NULL) { + gid = 0; + goto end; + } + gid = ret->gr_gid; + end: + free(buf); + return gid; +} + +static +bool +is_numeric(const char *str) { + size_t i; + for (i = 0; str[i] != '\0'; i++) { + if ('0' > str[i] || str[i] > '9') + return false; + } + return (i > 0); +} + +uid_t +filename2uid(const char *filename) { + if (filename == NULL) + return 0; + + char *tmp = strdupa(filename); + char *bname = basename(tmp); + char *ext = strchr(bname, '.'); + if ((strcmp(ext, ".yml") != 0) || (ext != &bname[4])) + return 0; + ext[0] = '\0'; + uid_t uid = is_numeric(bname) ? atoi(bname) : 0; + return uid; +} + +int +load_user_password(struct passwd *user) { + char *filename = NULL; + FILE *file; + char *line = NULL; + ssize_t line_len; + size_t line_cap = 0; + + asprintf(&filename, "%s/.password", user->pw_dir); + if ((file = fopen(filename, "r")) == NULL) + goto nopassword; + // TODO: check permissions on 'file' + if ((line_len = getline(&line, &line_cap, file)) < 1) + goto nopassword; + if (line[line_len-1] == '\n') + line[--line_len] = '\0'; + free(filename); + fclose(file); + user->pw_passwd = line; + return 0; + nopassword: + free(filename); + free(line); + user->pw_passwd = strdup("!"); + return 0; +} + +#define NODE(id) yaml_document_get_node(&yaml_document, id) +int +load_user_yaml(const char *filename, struct passwd *user) { + int ret = -1; + unsigned char flags = 0; + + PASSWD_FREE(*user); + + FILE *yaml_file; + yaml_parser_t yaml_parser; ZERO(yaml_parser); + yaml_document_t yaml_document; ZERO(yaml_document); + + uid_t uid; + ASSERT((uid = user->pw_uid = filename2uid(filename)) > 0); + flags |= PW_UID; + + ASSERT((yaml_file = fopen(filename, "r")) != NULL); + ASSERT(yaml_parser_initialize(&yaml_parser)); + yaml_parser_set_input_file(&yaml_parser, yaml_file); + ASSERT(yaml_parser_load(&yaml_parser, &yaml_document)); + yaml_node_t *root = yaml_document_get_root_node(&yaml_document); + + ASSERT(root->type == YAML_MAPPING_NODE); + for (yaml_node_pair_t *pair = root->data.mapping.pairs.start; + pair < root->data.mapping.pairs.top; + pair++) { + yaml_node_t *key = NODE(pair->key); + yaml_node_t *val = NODE(pair->value); + if (strcmp("username", STR_VALUE(key))==0) { + user->pw_name = strdup(STR_VALUE(val)); + asprintf(&(user->pw_dir), "/home/%s", user->pw_name); + flags |= PW_NAME | PW_DIR; + } + if (strcmp("fullname", STR_VALUE(key))==0) { + user->pw_gecos = strdup(STR_VALUE(val)); + flags |= PW_GECOS; + } + if (strcmp("shell", STR_VALUE(key))==0) { + user->pw_shell = strdup(STR_VALUE(val)); + flags |= PW_SHELL; + } + if (strcmp("groups", STR_VALUE(key))==0) { + if (val->type != YAML_SEQUENCE_NODE) + goto error; + for (yaml_node_item_t *itemp = val->data.sequence.items.start; + itemp < val->data.sequence.items.top; + itemp++) { + yaml_node_t *item = NODE(*itemp); + if (itemp == val->data.sequence.items.start) { + /* primary group */ + char *grp_name = STR_VALUE(item); + ASSERT((user->pw_gid = name2gid(grp_name)) > 0); + flags |= PW_GID; + } else { + /* secondary group */ + // TODO: do something here + } + } + } + } + if (flags & PW_DIR) { + if (load_user_password(user) < 0) + goto error; + flags |= PW_PASSWD; + } + ASSERT(flags == PW_ALL); + ret = 0; + goto end; + error: + PASSWD_FREE(*user); + user->pw_uid = uid; + end: + yaml_document_delete(&yaml_document); + yaml_parser_delete(&yaml_parser); + fclose(yaml_file); + return ret; +} |