diff options
-rw-r--r-- | sbin/isakmpd/DESIGN-NOTES | 39 | ||||
-rw-r--r-- | sbin/isakmpd/conf.c | 392 | ||||
-rw-r--r-- | sbin/isakmpd/conf.h | 10 | ||||
-rw-r--r-- | sbin/isakmpd/ui.c | 54 |
4 files changed, 427 insertions, 68 deletions
diff --git a/sbin/isakmpd/DESIGN-NOTES b/sbin/isakmpd/DESIGN-NOTES index 5937064de16..ecdfafbb24c 100644 --- a/sbin/isakmpd/DESIGN-NOTES +++ b/sbin/isakmpd/DESIGN-NOTES @@ -1,5 +1,5 @@ -$OpenBSD: DESIGN-NOTES,v 1.13 1999/07/17 21:54:39 niklas Exp $ -$EOM: DESIGN-NOTES,v 1.46 1999/07/17 20:44:07 niklas Exp $ +$OpenBSD: DESIGN-NOTES,v 1.14 1999/08/05 22:41:08 niklas Exp $ +$EOM: DESIGN-NOTES,v 1.47 1999/08/05 14:57:59 niklas Exp $ General coding conventions -------------------------- @@ -211,6 +211,7 @@ isakmpd.fifo. The commands are one-letter codes followed by arguments. For now, only five such commands are implemented: c connect Establish a connection with a peer +C configure Add or remove configuration entries. d delete Delete an SA given cookies and message-IDs D debug Change logging level for a debug class r report Report status information of the daemon @@ -230,6 +231,16 @@ D 0 99 The report command is just an "r", and results in a list of active exchanges and security associations. +The "C" command takes 3 subcommands: set, rm and rms, for adding and removing +entries + remove complete sections respectively. Examples: + +C set [Net-A]:Address=192.168.0.0 +C rm [Net-A]:Address +C rms [Net-A] + +All these commands are atomic, i.e. they are not collected into larger +transactions, which there should be a way to do, but currently isn't. + I am thinking about adding a "q" command for quit. In addition to giving commands over the FIFO, you may send signals to the @@ -299,6 +310,30 @@ case RECORD_A_SZ == RECORD_B_FIELD_F_OFF. All this data are collected in struct field arrays which makes it possible to symbolically print out entire payloads in readable form via field_dump_payload. +Configuration +------------- + +Internally isakmpd uses a section-tag-value triplet database for +configuration. Currently this happen to map really well to the +configuration file format, which on the other hand does not map +equally well to humans. It is envisioned that the configuration +database should be dynamically modifiable, and through a lot of +differnet mechanisms. Therefore we have designed an API for this +purpose. + +int conf_begin (); +int conf_set (int transaction, char *section, char *tag, char *value, + int override); +int conf_remove (int transaction, char *section, char *tag); +int conf_remove_section (int transaction, char *section); +int conf_end (int transaction, int commit); + +The caller will always be responsible for the memory management of the +passed strings, conf_set will copy the values, and not use the original +strings after it has returned. Return value will be zero on success and +non-zero otherwise. Note that the conf_remove* functions consider not +finding anything to remove as failure. + Identification -------------- diff --git a/sbin/isakmpd/conf.c b/sbin/isakmpd/conf.c index 1d79a0e1eac..66fdb32af04 100644 --- a/sbin/isakmpd/conf.c +++ b/sbin/isakmpd/conf.c @@ -1,5 +1,5 @@ -/* $OpenBSD: conf.c,v 1.9 1999/05/01 20:43:42 niklas Exp $ */ -/* $EOM: conf.c,v 1.18 1999/05/01 20:21:07 niklas Exp $ */ +/* $OpenBSD: conf.c,v 1.10 1999/08/05 22:41:08 niklas Exp $ */ +/* $EOM: conf.c,v 1.19 1999/08/05 14:57:59 niklas Exp $ */ /* * Copyright (c) 1998, 1999 Niklas Hallqvist. All rights reserved. @@ -51,10 +51,21 @@ #include "conf.h" #include "log.h" +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; +}; + +TAILQ_HEAD (conf_trans_head, conf_trans) conf_trans_queue; + /* * Radix-64 Encoding. */ - const u_int8_t bin2asc[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; @@ -86,40 +97,106 @@ struct conf_binding { }; char *conf_path = CONFIG_FILE; -LIST_HEAD (conf_bindings, conf_binding) conf_bindings; +LIST_HEAD (conf_bindings, conf_binding) conf_bindings[256]; -static off_t conf_sz; 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); + log_debug (LOG_MISC, 70, "[%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); + log_debug (LOG_MISC, 70, "[%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. - * XXX Should really be a hash table implementation. */ -static void -conf_set (char *section, char *line, int pos) +static int +conf_set_now (char *section, char *tag, char *value, int override) { - struct conf_binding *node; - int i; + struct conf_binding *node = 0; - node = malloc (sizeof *node); - if (!node) - log_fatal ("conf_set: out of memory"); - node->section = section; - node->tag = line; - for (i = 0; line[i] && i < pos; i++) - ; - line[i] = '\0'; - if (conf_get_str (section, line)) + if (override) + conf_remove_now (section, tag); + else if (conf_get_str (section, tag)) { log_print ("conf_set: duplicate tag [%s]:%s, ignoring...\n", section, - line); - return; + tag); + return 1; } - node->value = line + pos + 1 + strspn (line + pos + 1, " \t"); - LIST_INSERT_HEAD (&conf_bindings, node, link); - log_debug (LOG_MISC, 70, "(%s,%s)->%s", node->section, node->tag, + + node = calloc (1, sizeof *node); + if (!node) + { + log_error ("conf_set: calloc (1, %d) failed", sizeof *node); + return 1; + } + node->section = section; + node->tag = tag; + node->value = value; + + LIST_INSERT_HEAD (&conf_bindings[conf_hash (section)], node, link); + log_debug (LOG_MISC, 70, "[%s]:%s->%s", node->section, node->tag, node->value); + return 0; } /* @@ -127,7 +204,7 @@ conf_set (char *section, char *line, int pos) * headers and feed tag-value pairs into our configuration database. */ static void -conf_parse_line (char *line, size_t sz) +conf_parse_line (int trans, char *line, size_t sz) { char *cp = line; int i; @@ -170,7 +247,10 @@ conf_parse_line (char *line, size_t sz) ln); return; } - conf_set (section, line, i); + line[strcspn (line, " \t=")] = '\0'; + /* XXX Perhaps should we not ignore errors? */ + conf_set (trans, section, line, + line + i + 1 + strspn (line + i + 1, " \t"), 0); return; } @@ -184,24 +264,24 @@ conf_parse_line (char *line, size_t sz) /* Parse the mapped configuration file. */ static void -conf_parse (void) +conf_parse (int trans, char *buf, size_t sz) { - char *cp = conf_addr; - char *conf_end = conf_addr + conf_sz; + char *cp = buf; + char *bufend = buf + sz; char *line; line = cp; - while (cp < conf_end) + while (cp < bufend) { if (*cp == '\n') { /* Check for escaped newlines. */ - if (cp > conf_addr && *(cp - 1) == '\\') + if (cp > buf && *(cp - 1) == '\\') *(cp - 1) = *cp = ' '; else { *cp = '\0'; - conf_parse_line (line, cp - line); + conf_parse_line (trans, line, cp - line); line = cp + 1; } } @@ -211,43 +291,76 @@ conf_parse (void) log_print ("conf_parse: last line non-terminated, ignored."); } -/* Open the config file and map it into our address space, then parse it. */ void conf_init (void) { - int fd; - struct stat st; + int i; - /* - * Start by freeing potential existing configuration. - * - * XXX One could envision doing this late, surviving failures with just - * a warning log message that the new configuration did not get read - * and that the former one persists. - */ - if (conf_addr) - { - while (LIST_FIRST (&conf_bindings)) - LIST_REMOVE (LIST_FIRST (&conf_bindings), link); - free (conf_addr); - } + 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, i, trans; + struct stat st; + off_t sz; + char *new_conf_addr = 0; fd = open (conf_path, O_RDONLY); if (fd == -1) - log_fatal ("open (\"%s\", O_RDONLY)", conf_path); + { + log_error ("open (\"%s\", O_RDONLY) failed", conf_path); + return; + } if (fstat (fd, &st) == -1) - log_fatal ("fstat (%d, &st)", fd); - conf_sz = st.st_size; - conf_addr = malloc (conf_sz); - if (!conf_addr) - log_fatal ("malloc (%d)", conf_sz); + { + log_error ("fstat (%d, &st) failed", fd); + goto fail; + } + sz = st.st_size; + new_conf_addr = malloc (sz); + if (!new_conf_addr) + { + log_error ("malloc (%d) failed", sz); + goto fail; + } /* XXX I assume short reads won't happen here. */ - if (read (fd, conf_addr, conf_sz) != conf_sz) - log_fatal ("read (%d, %p, %d)", fd, conf_addr, conf_sz); + if (read (fd, new_conf_addr, sz) != sz) + { + log_error ("read (%d, %p, %d) failed", fd, new_conf_addr, sz); + goto fail; + } close (fd); - LIST_INIT (&conf_bindings); - conf_parse (); + trans = conf_begin (); + + /* XXX Should we not care about errors and rollback? */ + conf_parse (trans, new_conf_addr, sz); + + /* 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); } /* @@ -297,16 +410,17 @@ conf_get_str (char *section, char *tag) { struct conf_binding *cb; - for (cb = LIST_FIRST (&conf_bindings); cb; cb = LIST_NEXT (cb, link)) + 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) { - log_debug (LOG_MISC, 60, "conf_get_str: (%s, %s) -> %s", section, + log_debug (LOG_MISC, 60, "conf_get_str: [%s]:%s->%s", section, tag, cb->value); return cb->value; } log_debug (LOG_MISC, 60, - "conf_get_str: configuration value not found (%s, %s)", section, + "conf_get_str: configuration value not found [%s]:%s", section, tag); return 0; } @@ -373,7 +487,8 @@ conf_get_tag_list (char *section) goto cleanup; TAILQ_INIT (&list->fields); list->cnt = 0; - for (cb = LIST_FIRST (&conf_bindings); cb; cb = LIST_NEXT (cb, link)) + for (cb = LIST_FIRST (&conf_bindings[conf_hash (section)]); cb; + cb = LIST_NEXT (cb, link)) if (strcasecmp (section, cb->section) == 0) { list->cnt++; @@ -497,3 +612,156 @@ conf_free_list (struct conf_list *list) } 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) + { + log_error ("conf_trans_node: calloc (1, %d) failed", 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) +{ + struct conf_trans *node; + + node = conf_trans_node (transaction, CONF_SET); + if (!node) + return 1; + node->section = strdup (section); + if (!node->section) + { + log_error ("conf_set: strdup (\"%s\") failed", section); + goto fail; + } + node->tag = strdup (tag); + if (!node->tag) + { + log_error ("conf_set: strdup (\"%s\") failed", tag); + goto fail; + } + node->value = strdup (value); + if (!node->value) + { + log_error ("conf_set: strdup (\"%s\") failed", value); + goto fail; + } + node->override = override; + 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) + { + log_error ("conf_remove: strdup (\"%s\") failed", section); + goto fail; + } + node->tag = strdup (tag); + if (!node->tag) + { + log_error ("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) + { + log_error ("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); + break; + case CONF_REMOVE: + conf_remove_now (node->section, node->tag); + break; + case CONF_REMOVE_SECTION: + conf_remove_section_now (node->section); + break; + default: + log_print ("conf_end: unknown operation: %d", node->op); + } + TAILQ_REMOVE (&conf_trans_queue, node, link); + free (node); + } + } + return 0; +} diff --git a/sbin/isakmpd/conf.h b/sbin/isakmpd/conf.h index 71b6db90ec3..8e8578e7435 100644 --- a/sbin/isakmpd/conf.h +++ b/sbin/isakmpd/conf.h @@ -1,5 +1,5 @@ -/* $OpenBSD: conf.h,v 1.8 1999/07/18 09:33:21 niklas Exp $ */ -/* $EOM: conf.h,v 1.9 1999/07/18 09:20:27 niklas Exp $ */ +/* $OpenBSD: conf.h,v 1.9 1999/08/05 22:41:08 niklas Exp $ */ +/* $EOM: conf.h,v 1.10 1999/08/05 14:57:59 niklas Exp $ */ /* * Copyright (c) 1998, 1999 Niklas Hallqvist. All rights reserved. @@ -55,7 +55,9 @@ struct conf_list { extern char *conf_path; +extern int conf_begin (void); extern int conf_decode_base64 (u_int8_t *out, u_int32_t *len, u_char *buf); +extern int conf_end (int, int); extern void conf_free_list (struct conf_list *); extern int conf_get_line (FILE *, char *, u_int32_t); extern struct conf_list *conf_get_list (char *, char *); @@ -64,5 +66,9 @@ 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); #endif /* _CONF_H_ */ diff --git a/sbin/isakmpd/ui.c b/sbin/isakmpd/ui.c index 1cc2eaf64a6..08b9cbfb24d 100644 --- a/sbin/isakmpd/ui.c +++ b/sbin/isakmpd/ui.c @@ -1,5 +1,5 @@ -/* $OpenBSD: ui.c,v 1.9 1999/06/02 06:29:55 niklas Exp $ */ -/* $EOM: ui.c,v 1.33 1999/05/19 22:40:13 ho Exp $ */ +/* $OpenBSD: ui.c,v 1.10 1999/08/05 22:41:08 niklas Exp $ */ +/* $EOM: ui.c,v 1.34 1999/08/05 14:58:00 niklas Exp $ */ /* * Copyright (c) 1998, 1999 Niklas Hallqvist. All rights reserved. @@ -115,6 +115,52 @@ ui_teardown (char *cmd) sa_delete (sa, 1); } +/* + * Call the configuration API. + * XXX Error handling! How to do multi-line transactions? Too short arbitrary + * limit on the parameters? + */ +static void +ui_config (char *cmd) +{ + char subcmd[81], section[81], tag[81], value[81]; + int override, trans = 0; + + if (sscanf (cmd, "C %80s", subcmd) != 1) + goto fail; + + trans = conf_begin (); + if (strcasecmp (subcmd, "set") == 0) + { + if (sscanf (cmd, "C %*s [%80[^]]]:%80[^=]=%80s %d", section, tag, value, + &override) != 4) + goto fail; + conf_set (trans, section, tag, value, override); + } + else if (strcasecmp (cmd, "rm") == 0) + { + if (sscanf (cmd, "C %*s [%80[^]]]:%80s", section, tag) != 2) + goto fail; + conf_remove (trans, section, tag); + } + else if (strcasecmp (cmd, "rms") == 0) + { + if (sscanf (cmd, "C %*s [%80[^]]]", section) != 1) + goto fail; + conf_remove_section (trans, section); + } + else + goto fail; + + conf_end (trans, 1); + return; + + fail: + if (trans) + conf_end (trans, 0); + log_print ("ui_config: command \"%s\" malformed", cmd); +} + static void ui_delete (char *cmd) { @@ -191,6 +237,10 @@ ui_handle_command (char *line) ui_connect (line); break; + case 'C': + ui_config (line); + break; + case 'd': ui_delete (line); break; |