summaryrefslogtreecommitdiff
path: root/nslcd/hackers_parse.c
diff options
context:
space:
mode:
Diffstat (limited to 'nslcd/hackers_parse.c')
-rw-r--r--nslcd/hackers_parse.c214
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;
+}