From 021751e2e70c0d629bf196065816949e38b158d9 Mon Sep 17 00:00:00 2001 From: neilbrown Date: Wed, 15 Sep 2004 22:41:54 +0000 Subject: [PATCH] Add the idmapd files... --- utils/idmapd/Makefile | 12 + utils/idmapd/atomicio.c | 63 +++ utils/idmapd/cfg.c | 889 +++++++++++++++++++++++++++++++++++ utils/idmapd/cfg.h | 67 +++ utils/idmapd/idmapd.c | 769 ++++++++++++++++++++++++++++++ utils/idmapd/idmapd.conf | 10 + utils/idmapd/idmapd.conf.man | 74 +++ utils/idmapd/idmapd.man | 94 ++++ utils/idmapd/nfs_idmap.h | 71 +++ utils/idmapd/queue.h | 499 ++++++++++++++++++++ utils/idmapd/setproctitle.c | 110 +++++ utils/idmapd/strlcat.c | 77 +++ utils/idmapd/strlcpy.c | 73 +++ 13 files changed, 2808 insertions(+) create mode 100644 utils/idmapd/Makefile create mode 100644 utils/idmapd/atomicio.c create mode 100644 utils/idmapd/cfg.c create mode 100644 utils/idmapd/cfg.h create mode 100644 utils/idmapd/idmapd.c create mode 100644 utils/idmapd/idmapd.conf create mode 100644 utils/idmapd/idmapd.conf.man create mode 100644 utils/idmapd/idmapd.man create mode 100644 utils/idmapd/nfs_idmap.h create mode 100644 utils/idmapd/queue.h create mode 100644 utils/idmapd/setproctitle.c create mode 100644 utils/idmapd/strlcat.c create mode 100644 utils/idmapd/strlcpy.c diff --git a/utils/idmapd/Makefile b/utils/idmapd/Makefile new file mode 100644 index 0000000..14f6cc1 --- /dev/null +++ b/utils/idmapd/Makefile @@ -0,0 +1,12 @@ +# +# Makefile for rpc.idmapd +# + +PROGRAM = idmapd +PREFIX = rpc. +OBJS = atomicio.o cfg.o idmapd.o setproctitle.o strlcat.o strlcpy.o +LIBS = -levent -lnfsidmap +MAN8 = idmapd +MAN5 = idmapd.conf + +include $(TOP)rules.mk diff --git a/utils/idmapd/atomicio.c b/utils/idmapd/atomicio.c new file mode 100644 index 0000000..93171a7 --- /dev/null +++ b/utils/idmapd/atomicio.c @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2002 Marius Aamodt Eriksen + * Copyright (c) 1995,1999 Theo de Raadt. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif /* HAVE_CONFIG_H */ + +/* + * ensure all of data on socket comes through. f==read || f==write + */ +ssize_t +atomicio(f, fd, _s, n) + ssize_t (*f) (); + int fd; + void *_s; + size_t n; +{ + char *s = _s; + ssize_t res, pos = 0; + + while (n > pos) { + res = (f) (fd, s + pos, n - pos); + switch (res) { + case -1: + if (errno == EINTR || errno == EAGAIN) + continue; + case 0: + if (pos != 0) + return (pos); + return (res); + default: + pos += res; + } + } + return (pos); +} diff --git a/utils/idmapd/cfg.c b/utils/idmapd/cfg.c new file mode 100644 index 0000000..b22a7c9 --- /dev/null +++ b/utils/idmapd/cfg.c @@ -0,0 +1,889 @@ +/* $OpenBSD: conf.c,v 1.55 2003/06/03 14:28:16 ho Exp $ */ +/* $EOM: conf.c,v 1.48 2000/12/04 02:04:29 angelos Exp $ */ + +/* + * Copyright (c) 1998, 1999, 2000, 2001 Niklas Hallqvist. All rights reserved. + * Copyright (c) 2000, 2001, 2002 Håkan Olsson. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * This code was written under funding by Ericsson Radio Systems. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "cfg.h" + +static void conf_load_defaults (int); +#if 0 +static int conf_find_trans_xf (int, char *); +#endif + +size_t strlcpy(char *, const char *, size_t); + +struct conf_trans { + TAILQ_ENTRY (conf_trans) link; + int trans; + enum conf_op { CONF_SET, CONF_REMOVE, CONF_REMOVE_SECTION } op; + char *section; + char *tag; + char *value; + int override; + int is_default; +}; + +TAILQ_HEAD (conf_trans_head, conf_trans) conf_trans_queue; + +/* + * Radix-64 Encoding. + */ +const u_int8_t bin2asc[] + = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + +const u_int8_t asc2bin[] = +{ + 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 62, 255, 255, 255, 63, + 52, 53, 54, 55, 56, 57, 58, 59, + 60, 61, 255, 255, 255, 255, 255, 255, + 255, 0, 1, 2, 3, 4, 5, 6, + 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, + 23, 24, 25, 255, 255, 255, 255, 255, + 255, 26, 27, 28, 29, 30, 31, 32, + 33, 34, 35, 36, 37, 38, 39, 40, + 41, 42, 43, 44, 45, 46, 47, 48, + 49, 50, 51, 255, 255, 255, 255, 255 +}; + +struct conf_binding { + LIST_ENTRY (conf_binding) link; + char *section; + char *tag; + char *value; + int is_default; +}; + +char *conf_path; +LIST_HEAD (conf_bindings, conf_binding) conf_bindings[256]; + +static char *conf_addr; + +static __inline__ u_int8_t +conf_hash (char *s) +{ + u_int8_t hash = 0; + + while (*s) + { + hash = ((hash << 1) | (hash >> 7)) ^ tolower (*s); + s++; + } + return hash; +} + +/* + * Insert a tag-value combination from LINE (the equal sign is at POS) + */ +static int +conf_remove_now (char *section, char *tag) +{ + struct conf_binding *cb, *next; + + for (cb = LIST_FIRST (&conf_bindings[conf_hash (section)]); cb; cb = next) + { + next = LIST_NEXT (cb, link); + if (strcasecmp (cb->section, section) == 0 + && strcasecmp (cb->tag, tag) == 0) + { + LIST_REMOVE (cb, link); + warnx("[%s]:%s->%s removed", section, tag, cb->value); + free (cb->section); + free (cb->tag); + free (cb->value); + free (cb); + return 0; + } + } + return 1; +} + +static int +conf_remove_section_now (char *section) +{ + struct conf_binding *cb, *next; + int unseen = 1; + + for (cb = LIST_FIRST (&conf_bindings[conf_hash (section)]); cb; cb = next) + { + next = LIST_NEXT (cb, link); + if (strcasecmp (cb->section, section) == 0) + { + unseen = 0; + LIST_REMOVE (cb, link); + warnx("[%s]:%s->%s removed", section, cb->tag, cb->value); + free (cb->section); + free (cb->tag); + free (cb->value); + free (cb); + } + } + return unseen; +} + +/* + * Insert a tag-value combination from LINE (the equal sign is at POS) + * into SECTION of our configuration database. + */ +static int +conf_set_now (char *section, char *tag, char *value, int override, + int is_default) +{ + struct conf_binding *node = 0; + + if (override) + conf_remove_now (section, tag); + else if (conf_get_str (section, tag)) + { + if (!is_default) + warnx("conf_set: duplicate tag [%s]:%s, ignoring...\n", section, tag); + return 1; + } + + node = calloc (1, sizeof *node); + if (!node) + { + warnx("conf_set: calloc (1, %lu) failed", (unsigned long)sizeof *node); + return 1; + } + node->section = strdup (section); + node->tag = strdup (tag); + node->value = strdup (value); + node->is_default = is_default; + + LIST_INSERT_HEAD (&conf_bindings[conf_hash (section)], node, link); + return 0; +} + +/* + * Parse the line LINE of SZ bytes. Skip Comments, recognize section + * headers and feed tag-value pairs into our configuration database. + */ +static void +conf_parse_line (int trans, char *line, size_t sz) +{ + char *val; + size_t i; + int j; + static char *section = 0; + static int ln = 0; + + ln++; + + /* Lines starting with '#' or ';' are comments. */ + if (*line == '#' || *line == ';') + return; + + /* '[section]' parsing... */ + if (*line == '[') + { + for (i = 1; i < sz; i++) + if (line[i] == ']') + break; + if (section) + free (section); + if (i == sz) + { + warnx("conf_parse_line: %d:" + "non-matched ']', ignoring until next section", ln); + section = 0; + return; + } + section = malloc (i); + if (!section) + { + warnx("conf_parse_line: %d: malloc (%lu) failed", ln, + (unsigned long)i); + return; + } + strlcpy (section, line + 1, i); + return; + } + + /* Deal with assignments. */ + for (i = 0; i < sz; i++) + if (line[i] == '=') + { + /* If no section, we are ignoring the lines. */ + if (!section) + { + warnx("conf_parse_line: %d: ignoring line due to no section", ln); + return; + } + line[strcspn (line, " \t=")] = '\0'; + val = line + i + 1 + strspn (line + i + 1, " \t"); + /* Skip trailing whitespace, if any */ + for (j = sz - (val - line) - 1; j > 0 && isspace (val[j]); j--) + val[j] = '\0'; + /* XXX Perhaps should we not ignore errors? */ + conf_set (trans, section, line, val, 0, 0); + return; + } + + /* Other non-empty lines are weird. */ + i = strspn (line, " \t"); + if (line[i]) + warnx("conf_parse_line: %d: syntax error", ln); + + return; +} + +/* Parse the mapped configuration file. */ +static void +conf_parse (int trans, char *buf, size_t sz) +{ + char *cp = buf; + char *bufend = buf + sz; + char *line; + + line = cp; + while (cp < bufend) + { + if (*cp == '\n') + { + /* Check for escaped newlines. */ + if (cp > buf && *(cp - 1) == '\\') + *(cp - 1) = *cp = ' '; + else + { + *cp = '\0'; + conf_parse_line (trans, line, cp - line); + line = cp + 1; + } + } + cp++; + } + if (cp != line) + warnx("conf_parse: last line non-terminated, ignored."); +} + +static void +conf_load_defaults (int tr) +{ + /* No defaults */ + return; +} + +void +conf_init (void) +{ + unsigned int i; + + for (i = 0; i < sizeof conf_bindings / sizeof conf_bindings[0]; i++) + LIST_INIT (&conf_bindings[i]); + TAILQ_INIT (&conf_trans_queue); + conf_reinit (); +} + +/* Open the config file and map it into our address space, then parse it. */ +void +conf_reinit (void) +{ + struct conf_binding *cb = 0; + int fd, trans; + unsigned int i; + size_t sz; + char *new_conf_addr = 0; + struct stat sb; + + if ((stat (conf_path, &sb) == 0) || (errno != ENOENT)) + { + sz = sb.st_size; + fd = open (conf_path, O_RDONLY, 0); + if (fd == -1) + { + warnx("conf_reinit: open (\"%s\", O_RDONLY) failed", conf_path); + return; + } + + new_conf_addr = malloc (sz); + if (!new_conf_addr) + { + warnx("conf_reinit: malloc (%lu) failed", (unsigned long)sz); + goto fail; + } + + /* XXX I assume short reads won't happen here. */ + if (read (fd, new_conf_addr, sz) != (int)sz) + { + warnx("conf_reinit: read (%d, %p, %lu) failed", + fd, new_conf_addr, (unsigned long)sz); + goto fail; + } + close (fd); + + trans = conf_begin (); + + /* XXX Should we not care about errors and rollback? */ + conf_parse (trans, new_conf_addr, sz); + } + else + trans = conf_begin (); + + /* Load default configuration values. */ + conf_load_defaults (trans); + + /* Free potential existing configuration. */ + if (conf_addr) + { + for (i = 0; i < sizeof conf_bindings / sizeof conf_bindings[0]; i++) + for (cb = LIST_FIRST (&conf_bindings[i]); cb; + cb = LIST_FIRST (&conf_bindings[i])) + conf_remove_now (cb->section, cb->tag); + free (conf_addr); + } + + conf_end (trans, 1); + conf_addr = new_conf_addr; + return; + + fail: + if (new_conf_addr) + free (new_conf_addr); + close (fd); +} + +/* + * Return the numeric value denoted by TAG in section SECTION or DEF + * if that tag does not exist. + */ +int +conf_get_num (char *section, char *tag, int def) +{ + char *value = conf_get_str (section, tag); + + if (value) + return atoi (value); + return def; +} + +/* Validate X according to the range denoted by TAG in section SECTION. */ +int +conf_match_num (char *section, char *tag, int x) +{ + char *value = conf_get_str (section, tag); + int val, min, max, n; + + if (!value) + return 0; + n = sscanf (value, "%d,%d:%d", &val, &min, &max); + switch (n) + { + case 1: + warnx("conf_match_num: %s:%s %d==%d?", section, tag, val, x); + return x == val; + case 3: + warnx("conf_match_num: %s:%s %d<=%d<=%d?", section, tag, min, x, max); + return min <= x && max >= x; + default: + warnx("conf_match_num: section %s tag %s: invalid number spec %s", + section, tag, value); + } + return 0; +} + +/* Return the string value denoted by TAG in section SECTION. */ +char * +conf_get_str (char *section, char *tag) +{ + struct conf_binding *cb; + + for (cb = LIST_FIRST (&conf_bindings[conf_hash (section)]); cb; + cb = LIST_NEXT (cb, link)) + if (strcasecmp (section, cb->section) == 0 + && strcasecmp (tag, cb->tag) == 0) + { + return cb->value; + } + return 0; +} + +/* + * Build a list of string values out of the comma separated value denoted by + * TAG in SECTION. + */ +struct conf_list * +conf_get_list (char *section, char *tag) +{ + char *liststr = 0, *p, *field, *t; + struct conf_list *list = 0; + struct conf_list_node *node; + + list = malloc (sizeof *list); + if (!list) + goto cleanup; + TAILQ_INIT (&list->fields); + list->cnt = 0; + liststr = conf_get_str (section, tag); + if (!liststr) + goto cleanup; + liststr = strdup (liststr); + if (!liststr) + goto cleanup; + p = liststr; + while ((field = strsep (&p, ",")) != NULL) + { + /* Skip leading whitespace */ + while (isspace (*field)) + field++; + /* Skip trailing whitespace */ + if (p) + for (t = p - 1; t > field && isspace (*t); t--) + *t = '\0'; + if (*field == '\0') + { + warnx("conf_get_list: empty field, ignoring..."); + continue; + } + list->cnt++; + node = calloc (1, sizeof *node); + if (!node) + goto cleanup; + node->field = strdup (field); + if (!node->field) + goto cleanup; + TAILQ_INSERT_TAIL (&list->fields, node, link); + } + free (liststr); + return list; + + cleanup: + if (list) + conf_free_list (list); + if (liststr) + free (liststr); + return 0; +} + +struct conf_list * +conf_get_tag_list (char *section) +{ + struct conf_list *list = 0; + struct conf_list_node *node; + struct conf_binding *cb; + + list = malloc (sizeof *list); + if (!list) + goto cleanup; + TAILQ_INIT (&list->fields); + list->cnt = 0; + for (cb = LIST_FIRST (&conf_bindings[conf_hash (section)]); cb; + cb = LIST_NEXT (cb, link)) + if (strcasecmp (section, cb->section) == 0) + { + list->cnt++; + node = calloc (1, sizeof *node); + if (!node) + goto cleanup; + node->field = strdup (cb->tag); + if (!node->field) + goto cleanup; + TAILQ_INSERT_TAIL (&list->fields, node, link); + } + return list; + + cleanup: + if (list) + conf_free_list (list); + return 0; +} + +/* Decode a PEM encoded buffer. */ +int +conf_decode_base64 (u_int8_t *out, u_int32_t *len, u_char *buf) +{ + u_int32_t c = 0; + u_int8_t c1, c2, c3, c4; + + while (*buf) + { + if (*buf > 127 || (c1 = asc2bin[*buf]) == 255) + return 0; + buf++; + + if (*buf > 127 || (c2 = asc2bin[*buf]) == 255) + return 0; + buf++; + + if (*buf == '=') + { + c3 = c4 = 0; + c++; + + /* Check last four bit */ + if (c2 & 0xF) + return 0; + + if (strcmp ((char *)buf, "==") == 0) + buf++; + else + return 0; + } + else if (*buf > 127 || (c3 = asc2bin[*buf]) == 255) + return 0; + else + { + if (*++buf == '=') + { + c4 = 0; + c += 2; + + /* Check last two bit */ + if (c3 & 3) + return 0; + + if (strcmp ((char *)buf, "=")) + return 0; + + } + else if (*buf > 127 || (c4 = asc2bin[*buf]) == 255) + return 0; + else + c += 3; + } + + buf++; + *out++ = (c1 << 2) | (c2 >> 4); + *out++ = (c2 << 4) | (c3 >> 2); + *out++ = (c3 << 6) | c4; + } + + *len = c; + return 1; + +} + +void +conf_free_list (struct conf_list *list) +{ + struct conf_list_node *node = TAILQ_FIRST (&list->fields); + + while (node) + { + TAILQ_REMOVE (&list->fields, node, link); + if (node->field) + free (node->field); + free (node); + node = TAILQ_FIRST (&list->fields); + } + free (list); +} + +int +conf_begin (void) +{ + static int seq = 0; + + return ++seq; +} + +static struct conf_trans * +conf_trans_node (int transaction, enum conf_op op) +{ + struct conf_trans *node; + + node = calloc (1, sizeof *node); + if (!node) + { + warnx("conf_trans_node: calloc (1, %lu) failed", + (unsigned long)sizeof *node); + return 0; + } + node->trans = transaction; + node->op = op; + TAILQ_INSERT_TAIL (&conf_trans_queue, node, link); + return node; +} + +/* Queue a set operation. */ +int +conf_set (int transaction, char *section, char *tag, char *value, int override, + int is_default) +{ + struct conf_trans *node; + + node = conf_trans_node (transaction, CONF_SET); + if (!node) + return 1; + node->section = strdup (section); + if (!node->section) + { + warnx("conf_set: strdup (\"%s\") failed", section); + goto fail; + } + node->tag = strdup (tag); + if (!node->tag) + { + warnx("conf_set: strdup (\"%s\") failed", tag); + goto fail; + } + node->value = strdup (value); + if (!node->value) + { + warnx("conf_set: strdup (\"%s\") failed", value); + goto fail; + } + node->override = override; + node->is_default = is_default; + return 0; + + fail: + if (node->tag) + free (node->tag); + if (node->section) + free (node->section); + if (node) + free (node); + return 1; +} + +/* Queue a remove operation. */ +int +conf_remove (int transaction, char *section, char *tag) +{ + struct conf_trans *node; + + node = conf_trans_node (transaction, CONF_REMOVE); + if (!node) + goto fail; + node->section = strdup (section); + if (!node->section) + { + warnx("conf_remove: strdup (\"%s\") failed", section); + goto fail; + } + node->tag = strdup (tag); + if (!node->tag) + { + warnx("conf_remove: strdup (\"%s\") failed", tag); + goto fail; + } + return 0; + + fail: + if (node->section) + free (node->section); + if (node) + free (node); + return 1; +} + +/* Queue a remove section operation. */ +int +conf_remove_section (int transaction, char *section) +{ + struct conf_trans *node; + + node = conf_trans_node (transaction, CONF_REMOVE_SECTION); + if (!node) + goto fail; + node->section = strdup (section); + if (!node->section) + { + warnx("conf_remove_section: strdup (\"%s\") failed", section); + goto fail; + } + return 0; + + fail: + if (node) + free (node); + return 1; +} + +/* Execute all queued operations for this transaction. Cleanup. */ +int +conf_end (int transaction, int commit) +{ + struct conf_trans *node, *next; + + for (node = TAILQ_FIRST (&conf_trans_queue); node; node = next) + { + next = TAILQ_NEXT (node, link); + if (node->trans == transaction) + { + if (commit) + switch (node->op) + { + case CONF_SET: + conf_set_now (node->section, node->tag, node->value, + node->override, node->is_default); + break; + case CONF_REMOVE: + conf_remove_now (node->section, node->tag); + break; + case CONF_REMOVE_SECTION: + conf_remove_section_now (node->section); + break; + default: + warnx("conf_end: unknown operation: %d", node->op); + } + TAILQ_REMOVE (&conf_trans_queue, node, link); + if (node->section) + free (node->section); + if (node->tag) + free (node->tag); + if (node->value) + free (node->value); + free (node); + } + } + return 0; +} + +/* + * Dump running configuration upon SIGUSR1. + * Configuration is "stored in reverse order", so reverse it again. + */ +struct dumper { + char *s, *v; + struct dumper *next; +}; + +static void +conf_report_dump (struct dumper *node) +{ + /* Recursive, cleanup when we're done. */ + + if (node->next) + conf_report_dump (node->next); + + if (node->v) + warnx("%s=\t%s", node->s, node->v); + else if (node->s) + { + warnx("%s", node->s); + if (strlen (node->s) > 0) + free (node->s); + } + + free (node); +} + +void +conf_report (void) +{ + struct conf_binding *cb, *last = 0; + unsigned int i, len; + char *current_section = (char *)0; + struct dumper *dumper, *dnode; + + dumper = dnode = (struct dumper *)calloc (1, sizeof *dumper); + if (!dumper) + goto mem_fail; + + warnx("conf_report: dumping running configuration"); + + for (i = 0; i < sizeof conf_bindings / sizeof conf_bindings[0]; i++) + for (cb = LIST_FIRST (&conf_bindings[i]); cb; + cb = LIST_NEXT (cb, link)) + { + if (!cb->is_default) + { + /* Dump this entry. */ + if (!current_section || strcmp (cb->section, current_section)) + { + if (current_section) + { + len = strlen (current_section) + 3; + dnode->s = malloc (len); + if (!dnode->s) + goto mem_fail; + + snprintf (dnode->s, len, "[%s]", current_section); + dnode->next + = (struct dumper *)calloc (1, sizeof (struct dumper)); + dnode = dnode->next; + if (!dnode) + goto mem_fail; + + dnode->s = ""; + dnode->next + = (struct dumper *)calloc (1, sizeof (struct dumper)); + dnode = dnode->next; + if (!dnode) + goto mem_fail; + } + current_section = cb->section; + } + dnode->s = cb->tag; + dnode->v = cb->value; + dnode->next = (struct dumper *)calloc (1, sizeof (struct dumper)); + dnode = dnode->next; + if (!dnode) + goto mem_fail; + last = cb; + } + } + + if (last) + { + len = strlen (last->section) + 3; + dnode->s = malloc (len); + if (!dnode->s) + goto mem_fail; + snprintf (dnode->s, len, "[%s]", last->section); + } + + conf_report_dump (dumper); + + return; + + mem_fail: + warnx("conf_report: malloc/calloc failed"); + while ((dnode = dumper) != 0) + { + dumper = dumper->next; + if (dnode->s) + free (dnode->s); + free (dnode); + } + return; +} diff --git a/utils/idmapd/cfg.h b/utils/idmapd/cfg.h new file mode 100644 index 0000000..c1ca940 --- /dev/null +++ b/utils/idmapd/cfg.h @@ -0,0 +1,67 @@ +/* $OpenBSD: conf.h,v 1.30 2004/06/25 20:25:34 hshoexer Exp $ */ +/* $EOM: conf.h,v 1.13 2000/09/18 00:01:47 ho Exp $ */ + +/* + * Copyright (c) 1998, 1999, 2001 Niklas Hallqvist. All rights reserved. + * Copyright (c) 2000, 2003 Håkan Olsson. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * This code was written under funding by Ericsson Radio Systems. + */ + +#ifndef _CONF_H_ +#define _CONF_H_ + +#include "queue.h" + +struct conf_list_node { + TAILQ_ENTRY(conf_list_node) link; + char *field; +}; + +struct conf_list { + size_t cnt; + TAILQ_HEAD(conf_list_fields_head, conf_list_node) fields; +}; + +extern char *conf_path; + +extern int conf_begin(void); +extern int conf_decode_base64(u_int8_t *, u_int32_t *, u_char *); +extern int conf_end(int, int); +extern void conf_free_list(struct conf_list *); +extern struct sockaddr *conf_get_address(char *, char *); +extern struct conf_list *conf_get_list(char *, char *); +extern struct conf_list *conf_get_tag_list(char *); +extern int conf_get_num(char *, char *, int); +extern char *conf_get_str(char *, char *); +extern void conf_init(void); +extern int conf_match_num(char *, char *, int); +extern void conf_reinit(void); +extern int conf_remove(int, char *, char *); +extern int conf_remove_section(int, char *); +extern int conf_set(int, char *, char *, char *, int, int); +extern void conf_report(void); + +#endif /* _CONF_H_ */ diff --git a/utils/idmapd/idmapd.c b/utils/idmapd/idmapd.c new file mode 100644 index 0000000..ee117b6 --- /dev/null +++ b/utils/idmapd/idmapd.c @@ -0,0 +1,769 @@ +/* + * idmapd.c + * + * Userland daemon for idmap. + * + * Copyright (c) 2002 The Regents of the University of Michigan. + * All rights reserved. + * + * Marius Aamodt Eriksen + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include +#include + +#include "nfs_idmap.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif /* HAVE_CONFIG_H */ + +#include "cfg.h" +#include "queue.h" +#include "nfslib.h" + +#ifndef PIPEFS_DIR +#define PIPEFS_DIR "/var/lib/nfs/rpc_pipefs/" +#endif + +#ifndef NFSD_DIR +#define NFSD_DIR "/proc/net/rpc" +#endif + +#ifndef NFS4NOBODY_USER +#define NFS4NOBODY_USER "nobody" +#endif + +#ifndef NFS4NOBODY_GROUP +#define NFS4NOBODY_GROUP "nobody" +#endif + +/* From Niels */ +#define CONF_SAVE(w, f) do { \ + char *p = f; \ + if (p != NULL) \ + (w) = p; \ +} while (0) + +#define IC_IDNAME 1 +#define IC_NAMEID 2 +struct idmap_client { + int ic_fd; + int ic_dirfd; + char ic_clid[30]; + char ic_path[PATH_MAX]; + int ic_scanned; + struct event ic_event; + char *ic_id; + short ic_which; + TAILQ_ENTRY(idmap_client) ic_next; +}; + +TAILQ_HEAD(idmap_clientq, idmap_client); + +static void dirscancb(int, short, void *); +static void clntscancb(int, short, void *); +static int nfsopen(struct idmap_client *); +static void nfscb(int, short, void *); +static void nfsdcb(int, short, void *); +static int validateascii(char *, u_int32_t); +static int addfield(char **, ssize_t *, char *); +static int getfield(char **, char *, size_t); + +static void imconv(struct idmap_client *, struct idmap_msg *); +static void idtonameres(struct idmap_msg *); +static void nametoidres(struct idmap_msg *); + +static int nfsdopen(char *); +static int nfsdopenone(struct idmap_client *, short, char *); + +size_t strlcat(char *, const char *, size_t); +size_t strlcpy(char *, const char *, size_t); +ssize_t atomicio(ssize_t (*)(), int, void *, size_t); +int daemon(int, int); + +static int verbose = 0; +static char domain[512]; +static char pipefsdir[PATH_MAX]; +static char *nobodyuser, *nobodygroup; +static uid_t nobodyuid; +static gid_t nobodygid; +static struct idmap_client nfsd_ic[2]; + +/* Used by cfg.c */ +char *conf_path; + +int +main(int argc, char **argv) +{ + int fd = 0, opt, fg = 0, nfsdret = -1; + struct idmap_clientq icq; + struct event rootdirev, clntdirev; + struct event initialize; + struct passwd *pw; + struct group *gr; + struct stat sb; + char *xpipefsdir = NULL; + char *xdomain = NULL; + int serverstart = 1, clientstart = 1; + + conf_path = _PATH_IDMAPDCONF; + nobodyuser = NFS4NOBODY_USER; + nobodygroup = NFS4NOBODY_GROUP; + strlcpy(pipefsdir, PIPEFS_DIR, sizeof(pipefsdir)); + +#define GETOPTSTR "vfd:p:U:G:c:CS" + opterr=0; /* Turn off error messages */ + while ((opt = getopt(argc, argv, GETOPTSTR)) != -1) { + if (opt == 'c') + conf_path = optarg; + if (opt == '?') { + if (strchr(GETOPTSTR, optopt)) + errx(1, "'-%c' option requires an argument.", optopt); + else + errx(1, "'-%c' is an invalid argument.", optopt); + } + } + optind = 1; + + if (stat(conf_path, &sb) == -1 && (errno == ENOENT || errno == EACCES)) { + warn("Skipping configuration file \"%s\"", conf_path); + } else { + conf_init(); + verbose = conf_get_num("General", "Verbosity", 0); + CONF_SAVE(xpipefsdir, conf_get_str("General", "Pipefs-Directory")); + CONF_SAVE(xdomain, conf_get_str("General", "Domain")); + if (xpipefsdir != NULL) + strlcpy(pipefsdir, xpipefsdir, sizeof(pipefsdir)); + if (xdomain != NULL) + strlcpy(domain, xdomain, sizeof(domain)); + CONF_SAVE(nobodyuser, conf_get_str("Mapping", "Nobody-User")); + CONF_SAVE(nobodygroup, conf_get_str("Mapping", "Nobody-Group")); + } + + while ((opt = getopt(argc, argv, GETOPTSTR)) != -1) + switch (opt) { + case 'v': + verbose++; + break; + case 'f': + fg = 1; + break; + case 'd': + strlcpy(domain, optarg, sizeof(domain)); + break; + case 'p': + strlcpy(pipefsdir, optarg, sizeof(pipefsdir)); + break; + case 'U': + nobodyuser = optarg; + break; + case 'G': + nobodygroup = optarg; + break; + case 'C': + serverstart = 0; + break; + case 'S': + clientstart = 0; + break; + default: + break; + } + + if (!serverstart && !clientstart) + errx(1, "it is illegal to specify both -C and -S"); + + strncat(pipefsdir, "/nfs", sizeof(pipefsdir)); + + if (domain[0] == '\0') { + struct hostent *he; + char hname[64], *c; + + if (gethostname(hname, sizeof(hname)) == -1) + errx(1, "Error getting hostname"); + + if ((he = gethostbyname(hname)) == NULL) + errx(1, "Error resolving hostname: %s", hname); + + if ((c = strchr(he->h_name, '.')) == NULL || *++c == '\0') + errx(1, "Error resolving domain, " + "please use the -d switch"); + + strlcpy(domain, c, sizeof(domain)); + } + + if ((pw = getpwnam(nobodyuser)) == NULL) + errx(1, "Could not find user \"%s\"", nobodyuser); + nobodyuid = pw->pw_uid; + + if ((gr = getgrnam(nobodygroup)) == NULL) + errx(1, "Could not find group \"%s\"", nobodygroup); + nobodygid = gr->gr_gid; + + if (strlen(domain) == 0) + errx(1, "Invalid domain; please specify with -d switch"); + + if (verbose > 2) + warnx("Using domain \"%s\"", domain); + + if (!fg) + daemon(0, 0); + + event_init(); + + if (serverstart) + nfsdret = nfsdopen(NFSD_DIR); + + if (clientstart) { + struct timeval now = { + .tv_sec = 0, + .tv_usec = 0, + }; + + if ((fd = open(pipefsdir, O_RDONLY)) == -1) + err(1, "open(%s)", pipefsdir); + + if (fcntl(fd, F_SETSIG, SIGUSR1) == -1) + err(1, "fcntl(%s)", pipefsdir); + if (fcntl(fd, F_NOTIFY, + DN_CREATE | DN_DELETE | DN_MODIFY | DN_MULTISHOT) == -1) + err(1, "fcntl(%s)", pipefsdir); + + TAILQ_INIT(&icq); + + /* These events are persistent */ + signal_set(&rootdirev, SIGUSR1, dirscancb, &icq); + signal_add(&rootdirev, NULL); + signal_set(&clntdirev, SIGUSR2, clntscancb, &icq); + signal_add(&clntdirev, NULL); + + /* Fetch current state */ + /* (Delay till start of event_dispatch to avoid possibly losing + * a SIGUSR1 between here and the call to event_dispatch().) */ + evtimer_set(&initialize, dirscancb, &icq); + evtimer_add(&initialize, &now); + } + + if (nfsdret != 0 && fd == 0) + errx(1, "Neither NFS client nor NFSd found"); + + event_dispatch(); + /* NOTREACHED */ + return 1; +} + +static void +dirscancb(int fd, short which, void *data) +{ + int nent, i; + struct dirent **ents; + struct idmap_client *ic; + char path[PATH_MAX]; + struct idmap_clientq *icq = data; + + nent = scandir(pipefsdir, &ents, NULL, alphasort); + if (nent == -1) { + warn("scandir(%s)", pipefsdir); + return; + } + + for (i = 0; i < nent; i++) { + if (ents[i]->d_reclen > 4 && + strncmp(ents[i]->d_name, "clnt", 4) == 0) { + TAILQ_FOREACH(ic, icq, ic_next) + if (strcmp(ents[i]->d_name + 4, ic->ic_clid) == 0) + break; + if (ic != NULL) + goto next; + + if ((ic = calloc(1, sizeof(*ic))) == NULL) + return; + strlcpy(ic->ic_clid, ents[i]->d_name + 4, + sizeof(ic->ic_clid)); + path[0] = '\0'; + snprintf(path, sizeof(path), "%s/%s", + pipefsdir, ents[i]->d_name); + + if ((ic->ic_dirfd = open(path, O_RDONLY, 0)) == -1) { + warn("open(%s)", path); + free(ic); + return; + } + + strlcat(path, "/idmap", sizeof(path)); + strlcpy(ic->ic_path, path, sizeof(ic->ic_path)); + + if (verbose > 0) + warnx("New client: %s", ic->ic_clid); + + if (nfsopen(ic) == -1) { + close(ic->ic_dirfd); + free(ic); + return; + } + + ic->ic_id = "Client"; + + TAILQ_INSERT_TAIL(icq, ic, ic_next); + + next: + ic->ic_scanned = 1; + } + } + + TAILQ_FOREACH(ic, icq, ic_next) { + if (!ic->ic_scanned) { + event_del(&ic->ic_event); + close(ic->ic_fd); + close(ic->ic_dirfd); + TAILQ_REMOVE(icq, ic, ic_next); + if (verbose > 0) { + warnx("Stale client: %s", ic->ic_clid); + warnx("\t-> closed %s", ic->ic_path); + } + free(ic); + } else + ic->ic_scanned = 0; + } + return; +} + +static void +clntscancb(int fd, short which, void *data) +{ + struct idmap_clientq *icq = data; + struct idmap_client *ic; + + TAILQ_FOREACH(ic, icq, ic_next) + if (ic->ic_fd == -1 && nfsopen(ic) == -1) { + close(ic->ic_dirfd); + TAILQ_REMOVE(icq, ic, ic_next); + free(ic); + } +} + +static void +nfsdcb(int fd, short which, void *data) +{ + struct idmap_client *ic = data; + struct idmap_msg im; + u_char buf[IDMAP_MAXMSGSZ + 1]; + size_t len, bsiz; + char *bp, typebuf[IDMAP_MAXMSGSZ], + buf1[IDMAP_MAXMSGSZ], authbuf[IDMAP_MAXMSGSZ], *p; + + if (which != EV_READ) + goto out; + + if ((len = read(ic->ic_fd, buf, sizeof(buf))) == -1) { + if (verbose > 0) + warn("read(%s)", ic->ic_path); + goto out; + } + + /* Get rid of newline and terminate buffer*/ + buf[len - 1] = '\0'; + bp = buf; + + memset(&im, 0, sizeof(im)); + + /* Authentication name -- ignored for now*/ + if (getfield(&bp, authbuf, sizeof(authbuf)) == -1) + return; + + if (getfield(&bp, typebuf, sizeof(typebuf)) == -1) + return; + + im.im_type = strcmp(typebuf, "user") == 0 ? + IDMAP_TYPE_USER : IDMAP_TYPE_GROUP; + + switch (ic->ic_which) { + case IC_NAMEID: + im.im_conv = IDMAP_CONV_NAMETOID; + if (getfield(&bp, im.im_name, sizeof(im.im_name)) == -1) + return; + break; + case IC_IDNAME: + im.im_conv = IDMAP_CONV_IDTONAME; + if (getfield(&bp, buf1, sizeof(buf1)) == -1) + return; + if ((im.im_id = strtoul(buf1, (char **)NULL, 10)) == ULONG_MAX && + errno == ERANGE) + return; + + break; + default: + warnx("Unknown which type %d", ic->ic_which); + return; + } + + imconv(ic, &im); + + buf[0] = '\0'; + bp = buf; + bsiz = sizeof(buf); + + /* Authentication name */ + addfield(&bp, &bsiz, authbuf); + + switch (ic->ic_which) { + case IC_NAMEID: + /* Type */ + p = im.im_type == IDMAP_TYPE_USER ? "user" : "group"; + addfield(&bp, &bsiz, p); + /* Name */ + addfield(&bp, &bsiz, im.im_name); +#define NFSD_EXPIRY 300 /* seconds */ + /* expiry */ + snprintf(buf1, sizeof(buf1), "%lu", time(NULL) + NFSD_EXPIRY); + addfield(&bp, &bsiz, buf1); + /* ID */ + snprintf(buf1, sizeof(buf1), "%u", im.im_id); + addfield(&bp, &bsiz, buf1); + + //if (bsiz == sizeof(buf)) /* XXX */ + + bp[-1] = '\n'; + + break; + case IC_IDNAME: + /* Type */ + p = im.im_type == IDMAP_TYPE_USER ? "user" : "group"; + addfield(&bp, &bsiz, p); + /* ID */ + snprintf(buf1, sizeof(buf1), "%u", im.im_id); + addfield(&bp, &bsiz, buf1); + /* expiry */ + snprintf(buf1, sizeof(buf1), "%lu", time(NULL) + NFSD_EXPIRY); + addfield(&bp, &bsiz, buf1); + /* Name */ + addfield(&bp, &bsiz, im.im_name); + + bp[-1] = '\n'; + + break; + default: + warnx("Unknown which type %d", ic->ic_which); + return; + } + + bsiz = sizeof(buf) - bsiz; + + if (atomicio(write, ic->ic_fd, buf, bsiz) != bsiz && verbose > 0) + warn("write(%s)", ic->ic_path); + +out: + event_add(&ic->ic_event, NULL); +} + +static void +imconv(struct idmap_client *ic, struct idmap_msg *im) +{ + switch (im->im_conv) { + case IDMAP_CONV_IDTONAME: + idtonameres(im); + if (verbose > 1) + warnx("%s %s: (%s) id \"%d\" -> name \"%s\"", + ic->ic_id, ic->ic_clid, + im->im_type == IDMAP_TYPE_USER ? "user" : "group", + im->im_id, im->im_name); + break; + case IDMAP_CONV_NAMETOID: + if (validateascii(im->im_name, sizeof(im->im_name)) == -1) { + im->im_status |= IDMAP_STATUS_INVALIDMSG; + return; + } + nametoidres(im); + if (verbose > 1) + warnx("%s %s: (%s) name \"%s\" -> id \"%d\"", + ic->ic_id, ic->ic_clid, + im->im_type == IDMAP_TYPE_USER ? "user" : "group", + im->im_name, im->im_id); + break; + default: + warnx("Invalid conversion type (%d) in message", im->im_conv); + im->im_status |= IDMAP_STATUS_INVALIDMSG; + break; + } +} + +static void +nfscb(int fd, short which, void *data) +{ + struct idmap_client *ic = data; + struct idmap_msg im; + + if (which != EV_READ) + goto out; + + if (atomicio(read, ic->ic_fd, &im, sizeof(im)) != sizeof(im)) { + if (verbose > 0) + warn("read(%s)", ic->ic_path); + if (errno == EPIPE) + return; + goto out; + } + + imconv(ic, &im); + + if (atomicio(write, ic->ic_fd, &im, sizeof(im)) != sizeof(im)) + warn("write(%s)", ic->ic_path); +out: + event_add(&ic->ic_event, NULL); +} + +static int +nfsdopen(char *path) +{ + return ((nfsdopenone(&nfsd_ic[0], IC_NAMEID, path) == 0 && + nfsdopenone(&nfsd_ic[1], IC_IDNAME, path) == 0) ? 0 : -1); +} + +static int +nfsdopenone(struct idmap_client *ic, short which, char *path) +{ + char *whichstr; + + whichstr = which == IC_IDNAME ? "idtoname" : "nametoid"; + snprintf(ic->ic_path, sizeof(ic->ic_path), + "%s/nfs4.%s/channel", path, whichstr); + if ((ic->ic_fd = open(ic->ic_path, O_RDWR, 0)) == -1) { + warn("%s", ic->ic_path); + return (-1); + } + + event_set(&ic->ic_event, ic->ic_fd, EV_READ, nfsdcb, ic); + event_add(&ic->ic_event, NULL); + + ic->ic_which = which; + ic->ic_id = "Server"; + strlcpy(ic->ic_clid, domain, sizeof(ic->ic_clid)); + + if (verbose > 0) + warnx("Opened %s", ic->ic_path); + + return (0); +} + +static int +nfsopen(struct idmap_client *ic) +{ + if ((ic->ic_fd = open(ic->ic_path, O_RDWR, 0)) == -1) { + switch (errno) { + case ENOENT: + fcntl(ic->ic_dirfd, F_SETSIG, SIGUSR2); + fcntl(ic->ic_dirfd, F_NOTIFY, + DN_CREATE | DN_DELETE | DN_MULTISHOT); + break; + default: + warn("open(%s)", ic->ic_path); + return (-1); + } + } else { + event_set(&ic->ic_event, ic->ic_fd, EV_READ, nfscb, ic); + event_add(&ic->ic_event, NULL); + fcntl(ic->ic_dirfd, F_SETSIG, 0); + fcntl(ic->ic_dirfd, F_NOTIFY, 0); + if (verbose > 0) + warnx("Opened %s", ic->ic_path); + } + + return (0); +} + +static int write_name(char *dest, char *localname, char *domain, size_t len) +{ + if (strlen(localname) + 1 + strlen(domain) + 1 > len) { + return -ENOMEM; /* XXX: Is there an -ETOOLONG? */ + } + strcpy(dest, localname); + strcat(dest, "@"); + strcat(dest, domain); + return 0; +} + +static void +idtonameres(struct idmap_msg *im) +{ + int ret = 0; + + switch (im->im_type) { + case IDMAP_TYPE_USER: + ret = nfs4_uid_to_name(im->im_id, domain, im->im_name, + sizeof(im->im_name)); + if (ret) + write_name(im->im_name, nobodyuser, domain, + sizeof(im->im_name)); + break; + case IDMAP_TYPE_GROUP: + ret = nfs4_gid_to_name(im->im_id, domain, im->im_name, + sizeof(im->im_name)); + if (ret) + write_name(im->im_name, nobodygroup, domain, + sizeof(im->im_name)); + break; + } + /* XXX Hack? would rather return failure instead of writing nobody + * as above, but kernel seems not to deal well with that as of + * 2.6.8-rc3. */ + im->im_status = IDMAP_STATUS_SUCCESS; +} + +static void +nametoidres(struct idmap_msg *im) +{ + int ret = 0; + + switch (im->im_type) { + case IDMAP_TYPE_USER: + ret = nfs4_name_to_uid(im->im_name, &im->im_id); + if (ret) + im->im_id = nobodyuid; + break; + case IDMAP_TYPE_GROUP: + ret = nfs4_name_to_gid(im->im_name, &im->im_id); + if (ret) + im->im_id = nobodygid; + break; + } + /* XXX Hack? would rather return failure instead of writing nobody + * as above, but kernel seems not to deal well with that as of + * 2.6.8-rc3. */ + im->im_status = IDMAP_STATUS_SUCCESS; +} + +static int +validateascii(char *string, u_int32_t len) +{ + int i; + + for (i = 0; i < len; i++) { + if (string[i] == '\0') + break; + + if (string[i] & 0x80) + return (-1); + } + + if (string[i] != '\0') + return (-1); + + return (i + 1); +} + +static int +addfield(char **bpp, ssize_t *bsizp, char *fld) +{ + char ch, *bp = *bpp; + ssize_t bsiz = *bsizp; + + while ((ch = *fld++) != '\0' && bsiz > 0) { + switch(ch) { + case ' ': + case '\t': + case '\n': + case '\\': + if (bsiz >= 4) { + bp += snprintf(bp, bsiz, "\\%03o", ch); + bsiz -= 4; + } + break; + default: + *bp++ = ch; + bsiz--; + break; + } + } + + if (bsiz < 1 || ch != '\0') + return (-1); + + *bp++ = ' '; + bsiz--; + + *bpp = bp; + *bsizp = bsiz; + + return (0); +} + +static int +getfield(char **bpp, char *fld, size_t fldsz) +{ + char *bp; + u_int val, n; + + while ((bp = strsep(bpp, " ")) != NULL && bp[0] == '\0') + ; + + if (bp == NULL || bp[0] == '\0' || bp[0] == '\n') + return (-1); + + while (*bp != '\0' && fldsz > 1) { + if (*bp == '\\') { + if ((n = sscanf(bp, "\\%03o", &val)) != 1) + return (-1); + if (val > (char)-1) + return (-1); + *fld++ = (char)val; + bp += 4; + } else { + *fld++ = *bp; + bp++; + } + fldsz--; + } + + if (*bp != '\0') + return (-1); + *fld = '\0'; + + return (0); +} diff --git a/utils/idmapd/idmapd.conf b/utils/idmapd/idmapd.conf new file mode 100644 index 0000000..acafd4b --- /dev/null +++ b/utils/idmapd/idmapd.conf @@ -0,0 +1,10 @@ +[General] + +Verbosity = 0 +Pipefs-Directory = /var/lib/nfs/rpc_pipefs +Domain = localdomain + +[Mapping] + +Nobody-User = nobody +Nobody-Group = nobody diff --git a/utils/idmapd/idmapd.conf.man b/utils/idmapd/idmapd.conf.man new file mode 100644 index 0000000..f4ea200 --- /dev/null +++ b/utils/idmapd/idmapd.conf.man @@ -0,0 +1,74 @@ +.\" $OpenBSD: mdoc.template,v 1.6 2001/02/03 08:22:44 niklas Exp $ +.\" +.\" The following requests are required for all man pages. +.Dd July 16, 2003 +.Dt idmapd.conf 5 +.Os +.Sh NAME +.Nm idmapd.conf +.Nd configuration file for idmapd, the NFSv4 ID <-> Name Mapper +.Sh SYNOPSIS +Configuration file for idmapd, the NFSv4 ID <-> Name Mapper +.Sh DESCRIPTION +The +.Nm +configuration file has two sections, initiated by the strings +[General] and [Mapping]. Each section may contain lines of the form + +.Dl variable = value + +The variables allowed in the General section are +.Va Verbosity, +.Va Pipefs-Directory, +and +.Va Domain, +whose values have the same effect as the arguments to the +.Fl v, +.Fl p, +and +.Fl d +commandline options, respectively. The variables allowed in the +Mapping section are +.Va Nobody-User +and +.Va Nobody-Group, +which have the same effect as the +.Fl U +and +.Fl G +commandline options. + +.Sh EXAMPLES + +An example +.Pa /etc/idmapd.conf +file: + +.Bd -literal +[General] + +Verbosity = 0 +Pipefs-Directory = /var/lib/nfs/rpc_pipefs +Domain = localdomain + +[Mapping] + +Nobody-User = nobody +Nobody-Group = nobody +.Ed + +.Sh SEE ALSO +.Xr idmapd 8 +.\".Sh SEE ALSO +.\".Xr nylon.conf 4 +.\" .Sh COMPATIBILITY +.\".Sh STANDARDS +.\".Sh ACKNOWLEDGEMENTS +.Sh AUTHORS +The idmapd software has been developed by Marius Aamodt Eriksen +.Aq marius@citi.umich.edu . +.\" .Sh HISTORY +.\".Sh BUGS +.\"Please report any bugs to Marius Aamodt Eriksen +.\".Aq marius@monkey.org . +.\" .Sh CAVEATS diff --git a/utils/idmapd/idmapd.man b/utils/idmapd/idmapd.man new file mode 100644 index 0000000..58ea9f2 --- /dev/null +++ b/utils/idmapd/idmapd.man @@ -0,0 +1,94 @@ +.\" $OpenBSD: mdoc.template,v 1.6 2001/02/03 08:22:44 niklas Exp $ +.\" +.\" The following requests are required for all man pages. +.Dd February 3, 2003 +.Dt RPC.IDMAPD 8 +.Os +.Sh NAME +.Nm rpc.idmapd +.Nd NFSv4 ID <-> Name Mapper +.Sh SYNOPSIS +.\" For a program: program [-abc] file ... +.Nm rpc.idmapd +.Op Fl v +.Op Fl f +.Op Fl d Ar domain +.Op Fl p Ar path +.Op Fl U Ar username +.Op Fl G Ar groupname +.Op Fl c Ar path +.Sh DESCRIPTION +.Nm +is the NFSv4 ID <-> name mapping daemon. It provides functionality to +the NFSv4 kernel client and server, to which it communicates via +upcalls, by translating user and group IDs to names, and vice versa. +.Pp +The options are as follows: +.Bl -tag -width Ds_imagedir +.It Fl v +Increases the verbosity level (can be specified multiple times). +.It Fl f +Runs +.Nm +in the foreground and prints all output to the terminal. +.It Fl d Ar domain +Set domain to +.Ar domain . +This is used internally by NFSv4 and is typically assigned by the +system administrator. By default, +.Ar domain +is set to be the FQDN of the host, minus the hostname. +.It Fl p Ar path +Specifies the location of the RPC pipefs to be +.Ar path . +The default value is \&"/var/lib/nfs/rpc_pipefs\&". +.It Fl U Ar username +Specifies the NFSv4 nobody user to be +.Ar username . +The default value is \&"nobody\&". +.It Fl G Ar groupname +Specifies the NFSv4 nobody group to be +.Ar groupname . +The default value is \&"nobody\&". +.It Fl c Ar path +Use configuration file +.Ar path . +.It Fl C +Client-only: perform no idmapping for any NFS server, even if one is detected. +.It Fl S +Server-only: perform no idmapping for any NFS client, even if one is detected. +.El +.Sh EXAMPLES +.Cm rpc.idmapd -d \&"citi.umich.edu\&" -f -vvv +.Pp +Runs +.Nm +with the domain \&"citi.umich.edu\&" in the foreground, printing all +messages to console, and with a verbosity level of 3. +.\" This next request is for sections 2 and 3 function return values only. +.\" .Sh RETURN VALUES +.\" The next request is for sections 2 and 3 error and signal handling only. +.\" .Sh ERRORS +.\" This next request is for section 4 only. +.\" .Sh DIAGNOSTICS +.\" This next request is for sections 1, 6, 7 & 8 only. +.\" .Sh ENVIRONMENT +.Sh FILES +.Pa /etc/idmapd.conf +.Sh SEE ALSO +.Xr idmapd.conf 5 +.\".Sh SEE ALSO +.\".Xr nylon.conf 4 +.\" .Sh COMPATIBILITY +.\".Sh STANDARDS +.\".Sh ACKNOWLEDGEMENTS +.Sh AUTHORS +The +.Nm +software has been developed by Marius Aamodt Eriksen +.Aq marius@citi.umich.edu . +.\" .Sh HISTORY +.\".Sh BUGS +.\"Please report any bugs to Marius Aamodt Eriksen +.\".Aq marius@monkey.org . +.\" .Sh CAVEATS diff --git a/utils/idmapd/nfs_idmap.h b/utils/idmapd/nfs_idmap.h new file mode 100644 index 0000000..5ec7cf1 --- /dev/null +++ b/utils/idmapd/nfs_idmap.h @@ -0,0 +1,71 @@ +/* + * include/linux/nfs_idmap.h + * + * UID and GID to name mapping for clients. + * + * Copyright (c) 2002 The Regents of the University of Michigan. + * All rights reserved. + * + * Marius Aamodt Eriksen + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef NFS_IDMAP_H +#define NFS_IDMAP_H + +/* XXX from bits/utmp.h */ +#define IDMAP_NAMESZ 128 + +#define IDMAP_TYPE_USER 0 +#define IDMAP_TYPE_GROUP 1 + +#define IDMAP_CONV_IDTONAME 0 +#define IDMAP_CONV_NAMETOID 1 + +#define IDMAP_STATUS_INVALIDMSG 0x01 +#define IDMAP_STATUS_AGAIN 0x02 +#define IDMAP_STATUS_LOOKUPFAIL 0x04 +#define IDMAP_STATUS_SUCCESS 0x08 + +#define IDMAP_MAXMSGSZ 256 + +struct idmap_msg { + u_int8_t im_type; + u_int8_t im_conv; + char im_name[IDMAP_NAMESZ]; + u_int32_t im_id; + u_int8_t im_status; +}; + +#ifdef __KERNEL__ +void *nfs_idmap_new(struct nfs_server *); +void nfs_idmap_delete(struct nfs_server *); +int nfs_idmap_id(struct nfs_server *, u_int8_t, char *, u_int, uid_t *); +int nfs_idmap_name(struct nfs_server *, u_int8_t, uid_t, char *, u_int *); +#endif /* __KERNEL__ */ + +#endif /* NFS_IDMAP_H */ diff --git a/utils/idmapd/queue.h b/utils/idmapd/queue.h new file mode 100644 index 0000000..2823fe7 --- /dev/null +++ b/utils/idmapd/queue.h @@ -0,0 +1,499 @@ +/* $OpenBSD: queue.h,v 1.22 2001/06/23 04:39:35 angelos Exp $ */ +/* $NetBSD: queue.h,v 1.11 1996/05/16 05:17:14 mycroft Exp $ */ + +/* + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)queue.h 8.5 (Berkeley) 8/20/94 + */ + +#ifndef _SYS_QUEUE_H_ +#define _SYS_QUEUE_H_ + +/* + * This file defines five types of data structures: singly-linked lists, + * lists, simple queues, tail queues, and circular queues. + * + * + * A singly-linked list is headed by a single forward pointer. The elements + * are singly linked for minimum space and pointer manipulation overhead at + * the expense of O(n) removal for arbitrary elements. New elements can be + * added to the list after an existing element or at the head of the list. + * Elements being removed from the head of the list should use the explicit + * macro for this purpose for optimum efficiency. A singly-linked list may + * only be traversed in the forward direction. Singly-linked lists are ideal + * for applications with large datasets and few or no removals or for + * implementing a LIFO queue. + * + * A list is headed by a single forward pointer (or an array of forward + * pointers for a hash table header). The elements are doubly linked + * so that an arbitrary element can be removed without a need to + * traverse the list. New elements can be added to the list before + * or after an existing element or at the head of the list. A list + * may only be traversed in the forward direction. + * + * A simple queue is headed by a pair of pointers, one the head of the + * list and the other to the tail of the list. The elements are singly + * linked to save space, so elements can only be removed from the + * head of the list. New elements can be added to the list before or after + * an existing element, at the head of the list, or at the end of the + * list. A simple queue may only be traversed in the forward direction. + * + * A tail queue is headed by a pair of pointers, one to the head of the + * list and the other to the tail of the list. The elements are doubly + * linked so that an arbitrary element can be removed without a need to + * traverse the list. New elements can be added to the list before or + * after an existing element, at the head of the list, or at the end of + * the list. A tail queue may be traversed in either direction. + * + * A circle queue is headed by a pair of pointers, one to the head of the + * list and the other to the tail of the list. The elements are doubly + * linked so that an arbitrary element can be removed without a need to + * traverse the list. New elements can be added to the list before or after + * an existing element, at the head of the list, or at the end of the list. + * A circle queue may be traversed in either direction, but has a more + * complex end of list detection. + * + * For details on the use of these macros, see the queue(3) manual page. + */ + +/* + * Singly-linked List definitions. + */ +#define SLIST_HEAD(name, type) \ +struct name { \ + struct type *slh_first; /* first element */ \ +} + +#define SLIST_HEAD_INITIALIZER(head) \ + { NULL } + +#define SLIST_ENTRY(type) \ +struct { \ + struct type *sle_next; /* next element */ \ +} + +/* + * Singly-linked List access methods. + */ +#define SLIST_FIRST(head) ((head)->slh_first) +#define SLIST_END(head) NULL +#define SLIST_EMPTY(head) (SLIST_FIRST(head) == SLIST_END(head)) +#define SLIST_NEXT(elm, field) ((elm)->field.sle_next) + +#define SLIST_FOREACH(var, head, field) \ + for((var) = SLIST_FIRST(head); \ + (var) != SLIST_END(head); \ + (var) = SLIST_NEXT(var, field)) + +/* + * Singly-linked List functions. + */ +#define SLIST_INIT(head) { \ + SLIST_FIRST(head) = SLIST_END(head); \ +} + +#define SLIST_INSERT_AFTER(slistelm, elm, field) do { \ + (elm)->field.sle_next = (slistelm)->field.sle_next; \ + (slistelm)->field.sle_next = (elm); \ +} while (0) + +#define SLIST_INSERT_HEAD(head, elm, field) do { \ + (elm)->field.sle_next = (head)->slh_first; \ + (head)->slh_first = (elm); \ +} while (0) + +#define SLIST_REMOVE_HEAD(head, field) do { \ + (head)->slh_first = (head)->slh_first->field.sle_next; \ +} while (0) + +#define SLIST_REMOVE(head, elm, type, field) do { \ + if ((head)->slh_first == (elm)) { \ + SLIST_REMOVE_HEAD((head), field); \ + } \ + else { \ + struct type *curelm = (head)->slh_first; \ + while( curelm->field.sle_next != (elm) ) \ + curelm = curelm->field.sle_next; \ + curelm->field.sle_next = \ + curelm->field.sle_next->field.sle_next; \ + } \ +} while (0) + +/* + * List definitions. + */ +#define LIST_HEAD(name, type) \ +struct name { \ + struct type *lh_first; /* first element */ \ +} + +#define LIST_HEAD_INITIALIZER(head) \ + { NULL } + +#define LIST_ENTRY(type) \ +struct { \ + struct type *le_next; /* next element */ \ + struct type **le_prev; /* address of previous next element */ \ +} + +/* + * List access methods + */ +#define LIST_FIRST(head) ((head)->lh_first) +#define LIST_END(head) NULL +#define LIST_EMPTY(head) (LIST_FIRST(head) == LIST_END(head)) +#define LIST_NEXT(elm, field) ((elm)->field.le_next) + +#define LIST_FOREACH(var, head, field) \ + for((var) = LIST_FIRST(head); \ + (var)!= LIST_END(head); \ + (var) = LIST_NEXT(var, field)) + +/* + * List functions. + */ +#define LIST_INIT(head) do { \ + LIST_FIRST(head) = LIST_END(head); \ +} while (0) + +#define LIST_INSERT_AFTER(listelm, elm, field) do { \ + if (((elm)->field.le_next = (listelm)->field.le_next) != NULL) \ + (listelm)->field.le_next->field.le_prev = \ + &(elm)->field.le_next; \ + (listelm)->field.le_next = (elm); \ + (elm)->field.le_prev = &(listelm)->field.le_next; \ +} while (0) + +#define LIST_INSERT_BEFORE(listelm, elm, field) do { \ + (elm)->field.le_prev = (listelm)->field.le_prev; \ + (elm)->field.le_next = (listelm); \ + *(listelm)->field.le_prev = (elm); \ + (listelm)->field.le_prev = &(elm)->field.le_next; \ +} while (0) + +#define LIST_INSERT_HEAD(head, elm, field) do { \ + if (((elm)->field.le_next = (head)->lh_first) != NULL) \ + (head)->lh_first->field.le_prev = &(elm)->field.le_next;\ + (head)->lh_first = (elm); \ + (elm)->field.le_prev = &(head)->lh_first; \ +} while (0) + +#define LIST_REMOVE(elm, field) do { \ + if ((elm)->field.le_next != NULL) \ + (elm)->field.le_next->field.le_prev = \ + (elm)->field.le_prev; \ + *(elm)->field.le_prev = (elm)->field.le_next; \ +} while (0) + +#define LIST_REPLACE(elm, elm2, field) do { \ + if (((elm2)->field.le_next = (elm)->field.le_next) != NULL) \ + (elm2)->field.le_next->field.le_prev = \ + &(elm2)->field.le_next; \ + (elm2)->field.le_prev = (elm)->field.le_prev; \ + *(elm2)->field.le_prev = (elm2); \ +} while (0) + +/* + * Simple queue definitions. + */ +#define SIMPLEQ_HEAD(name, type) \ +struct name { \ + struct type *sqh_first; /* first element */ \ + struct type **sqh_last; /* addr of last next element */ \ +} + +#define SIMPLEQ_HEAD_INITIALIZER(head) \ + { NULL, &(head).sqh_first } + +#define SIMPLEQ_ENTRY(type) \ +struct { \ + struct type *sqe_next; /* next element */ \ +} + +/* + * Simple queue access methods. + */ +#define SIMPLEQ_FIRST(head) ((head)->sqh_first) +#define SIMPLEQ_END(head) NULL +#define SIMPLEQ_EMPTY(head) (SIMPLEQ_FIRST(head) == SIMPLEQ_END(head)) +#define SIMPLEQ_NEXT(elm, field) ((elm)->field.sqe_next) + +#define SIMPLEQ_FOREACH(var, head, field) \ + for((var) = SIMPLEQ_FIRST(head); \ + (var) != SIMPLEQ_END(head); \ + (var) = SIMPLEQ_NEXT(var, field)) + +/* + * Simple queue functions. + */ +#define SIMPLEQ_INIT(head) do { \ + (head)->sqh_first = NULL; \ + (head)->sqh_last = &(head)->sqh_first; \ +} while (0) + +#define SIMPLEQ_INSERT_HEAD(head, elm, field) do { \ + if (((elm)->field.sqe_next = (head)->sqh_first) == NULL) \ + (head)->sqh_last = &(elm)->field.sqe_next; \ + (head)->sqh_first = (elm); \ +} while (0) + +#define SIMPLEQ_INSERT_TAIL(head, elm, field) do { \ + (elm)->field.sqe_next = NULL; \ + *(head)->sqh_last = (elm); \ + (head)->sqh_last = &(elm)->field.sqe_next; \ +} while (0) + +#define SIMPLEQ_INSERT_AFTER(head, listelm, elm, field) do { \ + if (((elm)->field.sqe_next = (listelm)->field.sqe_next) == NULL)\ + (head)->sqh_last = &(elm)->field.sqe_next; \ + (listelm)->field.sqe_next = (elm); \ +} while (0) + +#define SIMPLEQ_REMOVE_HEAD(head, elm, field) do { \ + if (((head)->sqh_first = (elm)->field.sqe_next) == NULL) \ + (head)->sqh_last = &(head)->sqh_first; \ +} while (0) + +/* + * Tail queue definitions. + */ +#define TAILQ_HEAD(name, type) \ +struct name { \ + struct type *tqh_first; /* first element */ \ + struct type **tqh_last; /* addr of last next element */ \ +} + +#define TAILQ_HEAD_INITIALIZER(head) \ + { NULL, &(head).tqh_first } + +#define TAILQ_ENTRY(type) \ +struct { \ + struct type *tqe_next; /* next element */ \ + struct type **tqe_prev; /* address of previous next element */ \ +} + +/* + * tail queue access methods + */ +#define TAILQ_FIRST(head) ((head)->tqh_first) +#define TAILQ_END(head) NULL +#define TAILQ_NEXT(elm, field) ((elm)->field.tqe_next) +#define TAILQ_LAST(head, headname) \ + (*(((struct headname *)((head)->tqh_last))->tqh_last)) +/* XXX */ +#define TAILQ_PREV(elm, headname, field) \ + (*(((struct headname *)((elm)->field.tqe_prev))->tqh_last)) +#define TAILQ_EMPTY(head) \ + (TAILQ_FIRST(head) == TAILQ_END(head)) + +#define TAILQ_FOREACH(var, head, field) \ + for((var) = TAILQ_FIRST(head); \ + (var) != TAILQ_END(head); \ + (var) = TAILQ_NEXT(var, field)) + +#define TAILQ_FOREACH_REVERSE(var, head, field, headname) \ + for((var) = TAILQ_LAST(head, headname); \ + (var) != TAILQ_END(head); \ + (var) = TAILQ_PREV(var, headname, field)) + +/* + * Tail queue functions. + */ +#define TAILQ_INIT(head) do { \ + (head)->tqh_first = NULL; \ + (head)->tqh_last = &(head)->tqh_first; \ +} while (0) + +#define TAILQ_INSERT_HEAD(head, elm, field) do { \ + if (((elm)->field.tqe_next = (head)->tqh_first) != NULL) \ + (head)->tqh_first->field.tqe_prev = \ + &(elm)->field.tqe_next; \ + else \ + (head)->tqh_last = &(elm)->field.tqe_next; \ + (head)->tqh_first = (elm); \ + (elm)->field.tqe_prev = &(head)->tqh_first; \ +} while (0) + +#define TAILQ_INSERT_TAIL(head, elm, field) do { \ + (elm)->field.tqe_next = NULL; \ + (elm)->field.tqe_prev = (head)->tqh_last; \ + *(head)->tqh_last = (elm); \ + (head)->tqh_last = &(elm)->field.tqe_next; \ +} while (0) + +#define TAILQ_INSERT_AFTER(head, listelm, elm, field) do { \ + if (((elm)->field.tqe_next = (listelm)->field.tqe_next) != NULL)\ + (elm)->field.tqe_next->field.tqe_prev = \ + &(elm)->field.tqe_next; \ + else \ + (head)->tqh_last = &(elm)->field.tqe_next; \ + (listelm)->field.tqe_next = (elm); \ + (elm)->field.tqe_prev = &(listelm)->field.tqe_next; \ +} while (0) + +#define TAILQ_INSERT_BEFORE(listelm, elm, field) do { \ + (elm)->field.tqe_prev = (listelm)->field.tqe_prev; \ + (elm)->field.tqe_next = (listelm); \ + *(listelm)->field.tqe_prev = (elm); \ + (listelm)->field.tqe_prev = &(elm)->field.tqe_next; \ +} while (0) + +#define TAILQ_REMOVE(head, elm, field) do { \ + if (((elm)->field.tqe_next) != NULL) \ + (elm)->field.tqe_next->field.tqe_prev = \ + (elm)->field.tqe_prev; \ + else \ + (head)->tqh_last = (elm)->field.tqe_prev; \ + *(elm)->field.tqe_prev = (elm)->field.tqe_next; \ +} while (0) + +#define TAILQ_REPLACE(head, elm, elm2, field) do { \ + if (((elm2)->field.tqe_next = (elm)->field.tqe_next) != NULL) \ + (elm2)->field.tqe_next->field.tqe_prev = \ + &(elm2)->field.tqe_next; \ + else \ + (head)->tqh_last = &(elm2)->field.tqe_next; \ + (elm2)->field.tqe_prev = (elm)->field.tqe_prev; \ + *(elm2)->field.tqe_prev = (elm2); \ +} while (0) + +/* + * Circular queue definitions. + */ +#define CIRCLEQ_HEAD(name, type) \ +struct name { \ + struct type *cqh_first; /* first element */ \ + struct type *cqh_last; /* last element */ \ +} + +#define CIRCLEQ_HEAD_INITIALIZER(head) \ + { CIRCLEQ_END(&head), CIRCLEQ_END(&head) } + +#define CIRCLEQ_ENTRY(type) \ +struct { \ + struct type *cqe_next; /* next element */ \ + struct type *cqe_prev; /* previous element */ \ +} + +/* + * Circular queue access methods + */ +#define CIRCLEQ_FIRST(head) ((head)->cqh_first) +#define CIRCLEQ_LAST(head) ((head)->cqh_last) +#define CIRCLEQ_END(head) ((void *)(head)) +#define CIRCLEQ_NEXT(elm, field) ((elm)->field.cqe_next) +#define CIRCLEQ_PREV(elm, field) ((elm)->field.cqe_prev) +#define CIRCLEQ_EMPTY(head) \ + (CIRCLEQ_FIRST(head) == CIRCLEQ_END(head)) + +#define CIRCLEQ_FOREACH(var, head, field) \ + for((var) = CIRCLEQ_FIRST(head); \ + (var) != CIRCLEQ_END(head); \ + (var) = CIRCLEQ_NEXT(var, field)) + +#define CIRCLEQ_FOREACH_REVERSE(var, head, field) \ + for((var) = CIRCLEQ_LAST(head); \ + (var) != CIRCLEQ_END(head); \ + (var) = CIRCLEQ_PREV(var, field)) + +/* + * Circular queue functions. + */ +#define CIRCLEQ_INIT(head) do { \ + (head)->cqh_first = CIRCLEQ_END(head); \ + (head)->cqh_last = CIRCLEQ_END(head); \ +} while (0) + +#define CIRCLEQ_INSERT_AFTER(head, listelm, elm, field) do { \ + (elm)->field.cqe_next = (listelm)->field.cqe_next; \ + (elm)->field.cqe_prev = (listelm); \ + if ((listelm)->field.cqe_next == CIRCLEQ_END(head)) \ + (head)->cqh_last = (elm); \ + else \ + (listelm)->field.cqe_next->field.cqe_prev = (elm); \ + (listelm)->field.cqe_next = (elm); \ +} while (0) + +#define CIRCLEQ_INSERT_BEFORE(head, listelm, elm, field) do { \ + (elm)->field.cqe_next = (listelm); \ + (elm)->field.cqe_prev = (listelm)->field.cqe_prev; \ + if ((listelm)->field.cqe_prev == CIRCLEQ_END(head)) \ + (head)->cqh_first = (elm); \ + else \ + (listelm)->field.cqe_prev->field.cqe_next = (elm); \ + (listelm)->field.cqe_prev = (elm); \ +} while (0) + +#define CIRCLEQ_INSERT_HEAD(head, elm, field) do { \ + (elm)->field.cqe_next = (head)->cqh_first; \ + (elm)->field.cqe_prev = CIRCLEQ_END(head); \ + if ((head)->cqh_last == CIRCLEQ_END(head)) \ + (head)->cqh_last = (elm); \ + else \ + (head)->cqh_first->field.cqe_prev = (elm); \ + (head)->cqh_first = (elm); \ +} while (0) + +#define CIRCLEQ_INSERT_TAIL(head, elm, field) do { \ + (elm)->field.cqe_next = CIRCLEQ_END(head); \ + (elm)->field.cqe_prev = (head)->cqh_last; \ + if ((head)->cqh_first == CIRCLEQ_END(head)) \ + (head)->cqh_first = (elm); \ + else \ + (head)->cqh_last->field.cqe_next = (elm); \ + (head)->cqh_last = (elm); \ +} while (0) + +#define CIRCLEQ_REMOVE(head, elm, field) do { \ + if ((elm)->field.cqe_next == CIRCLEQ_END(head)) \ + (head)->cqh_last = (elm)->field.cqe_prev; \ + else \ + (elm)->field.cqe_next->field.cqe_prev = \ + (elm)->field.cqe_prev; \ + if ((elm)->field.cqe_prev == CIRCLEQ_END(head)) \ + (head)->cqh_first = (elm)->field.cqe_next; \ + else \ + (elm)->field.cqe_prev->field.cqe_next = \ + (elm)->field.cqe_next; \ +} while (0) + +#define CIRCLEQ_REPLACE(head, elm, elm2, field) do { \ + if (((elm2)->field.cqe_next = (elm)->field.cqe_next) == \ + CIRCLEQ_END(head)) \ + (head).cqh_last = (elm2); \ + else \ + (elm2)->field.cqe_next->field.cqe_prev = (elm2); \ + if (((elm2)->field.cqe_prev = (elm)->field.cqe_prev) == \ + CIRCLEQ_END(head)) \ + (head).cqh_first = (elm2); \ + else \ + (elm2)->field.cqe_prev->field.cqe_next = (elm2); \ +} while (0) + +#endif /* !_SYS_QUEUE_H_ */ diff --git a/utils/idmapd/setproctitle.c b/utils/idmapd/setproctitle.c new file mode 100644 index 0000000..0679f4e --- /dev/null +++ b/utils/idmapd/setproctitle.c @@ -0,0 +1,110 @@ +/* + * Modified for OpenSSH by Kevin Steves + * October 2000 + */ + +/* + * Copyright (c) 1994, 1995 Christopher G. Demetriou + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Christopher G. Demetriou + * for the NetBSD Project. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#if defined(LIBC_SCCS) && !defined(lint) +static char rcsid[] = "$OpenBSD: setproctitle.c,v 1.8 2001/11/06 19:21:40 art Exp $"; +#endif /* LIBC_SCCS and not lint */ + +#include + +#include +#include +#include + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif /* HAVE_CONFIG_H */ + +#ifndef HAVE_SETPROCTITLE + +#define SPT_NONE 0 +#define SPT_PSTAT 1 + +#ifndef SPT_TYPE +#define SPT_TYPE SPT_NONE +#endif + +#if SPT_TYPE == SPT_PSTAT +#include +#include +#endif /* SPT_TYPE == SPT_PSTAT */ + +#define MAX_PROCTITLE 2048 + +extern char *__progname; + +/* + * Set Process Title (SPT) defines. Modeled after sendmail's + * SPT type definition strategy. + * + * SPT_TYPE: + * + * SPT_NONE: Don't set the process title. Default. + * SPT_PSTAT: Use pstat(PSTAT_SETCMD). HP-UX specific. + */ + +void +setproctitle(const char *fmt, ...) +{ +#if SPT_TYPE != SPT_NONE + va_list ap; + + char buf[MAX_PROCTITLE]; + size_t used; + +#if SPT_TYPE == SPT_PSTAT + union pstun pst; +#endif /* SPT_TYPE == SPT_PSTAT */ + + va_start(ap, fmt); + if (fmt != NULL) { + used = snprintf(buf, MAX_PROCTITLE, "%s: ", __progname); + if (used >= MAX_PROCTITLE) + used = MAX_PROCTITLE - 1; + (void)vsnprintf(buf + used, MAX_PROCTITLE - used, fmt, ap); + } else + (void)snprintf(buf, MAX_PROCTITLE, "%s", __progname); + va_end(ap); + used = strlen(buf); + +#if SPT_TYPE == SPT_PSTAT + pst.pst_command = buf; + pstat(PSTAT_SETCMD, pst, used, 0, 0); +#endif /* SPT_TYPE == SPT_PSTAT */ + +#endif /* SPT_TYPE != SPT_NONE */ +} +#endif /* HAVE_SETPROCTITLE */ diff --git a/utils/idmapd/strlcat.c b/utils/idmapd/strlcat.c new file mode 100644 index 0000000..2c8b20a --- /dev/null +++ b/utils/idmapd/strlcat.c @@ -0,0 +1,77 @@ +/* $OpenBSD: strlcat.c,v 1.8 2001/05/13 15:40:15 deraadt Exp $ */ + +/* + * Copyright (c) 1998 Todd C. Miller + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#if defined(LIBC_SCCS) && !defined(lint) +static char *rcsid = "$OpenBSD: strlcat.c,v 1.8 2001/05/13 15:40:15 deraadt Exp $"; +#endif /* LIBC_SCCS and not lint */ + +#include +#include + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif /* HAVE_CONFIG_H */ + +/* + * Appends src to string dst of size siz (unlike strncat, siz is the + * full size of dst, not space left). At most siz-1 characters + * will be copied. Always NUL terminates (unless siz <= strlen(dst)). + * Returns strlen(src) + MIN(siz, strlen(initial dst)). + * If retval >= siz, truncation occurred. + */ +size_t +strlcat(dst, src, siz) + char *dst; + const char *src; + size_t siz; +{ + register char *d = dst; + register const char *s = src; + register size_t n = siz; + size_t dlen; + + /* Find the end of dst and adjust bytes left but don't go past end */ + while (n-- != 0 && *d != '\0') + d++; + dlen = d - dst; + n = siz - dlen; + + if (n == 0) + return(dlen + strlen(s)); + while (*s != '\0') { + if (n != 1) { + *d++ = *s; + n--; + } + s++; + } + *d = '\0'; + + return(dlen + (s - src)); /* count does not include NUL */ +} diff --git a/utils/idmapd/strlcpy.c b/utils/idmapd/strlcpy.c new file mode 100644 index 0000000..528d4f1 --- /dev/null +++ b/utils/idmapd/strlcpy.c @@ -0,0 +1,73 @@ +/* $OpenBSD: strlcpy.c,v 1.5 2001/05/13 15:40:16 deraadt Exp $ */ + +/* + * Copyright (c) 1998 Todd C. Miller + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#if defined(LIBC_SCCS) && !defined(lint) +static char *rcsid = "$OpenBSD: strlcpy.c,v 1.5 2001/05/13 15:40:16 deraadt Exp $"; +#endif /* LIBC_SCCS and not lint */ + +#include +#include + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif /* HAVE_CONFIG_H */ + +/* + * Copy src to string dst of size siz. At most siz-1 characters + * will be copied. Always NUL terminates (unless siz == 0). + * Returns strlen(src); if retval >= siz, truncation occurred. + */ +size_t +strlcpy(dst, src, siz) + char *dst; + const char *src; + size_t siz; +{ + register char *d = dst; + register const char *s = src; + register size_t n = siz; + + /* Copy as many bytes as will fit */ + if (n != 0 && --n != 0) { + do { + if ((*d++ = *s++) == 0) + break; + } while (--n != 0); + } + + /* Not enough room in dst, add NUL and traverse rest of src */ + if (n == 0) { + if (siz != 0) + *d = '\0'; /* NUL-terminate dst */ + while (*s++) + ; + } + + return(s - src - 1); /* count does not include NUL */ +} -- 2.39.5