diff --git a/postfix/HISTORY b/postfix/HISTORY index 83c7542ce..b3a77cf05 100644 --- a/postfix/HISTORY +++ b/postfix/HISTORY @@ -23917,11 +23917,14 @@ Apologies for any names omitted. message to the postscreen_pre_queue_limit. Problem reported by Michael Orlitzky. File: proto/POSTSCREEN_README.html. -20181228 +20181229 + + Explicit maps_file_find() and dict_file_lookup() methods + that decode base64 content. Decoding content is not built + into the dict->lookup() method, because that would complicate + the implementation of map nesting (inline, thash), map + composition (pipemap, unionmap), and map proxying. For + consistency, decoding base64 file content is also not built + into the maps_find() method. Files: util/dict.h. + util/dict_file.c, global/maps.[hc], postmap/postmap.c. - Cleanup: generic wrapper infrastructure for Postfix maps - (dictionaries) that is the basis for UTF8 checks and for - base64 decoding of lookup results. Files: global/mkmap_open.c, - postmap/postmap.c, util/dict.c, util/dict.h, util/dict_alloc.c, - util/dict_file.c, util/dict_open.c, util/dict_pipe.c, - util/dict_union.c, util/dict_utf8.c, util/dict_wrapper.c. diff --git a/postfix/WISHLIST b/postfix/WISHLIST index d72e537a6..edc77926a 100644 --- a/postfix/WISHLIST +++ b/postfix/WISHLIST @@ -1,8 +1,7 @@ Wish list: - Add sequence support to dictionary wrappers, and remove - ad-hoc decoding from the postmap command. What about invalid - base64? Warn and skip? + With DICT_FLAG_RHS_IS_FILE, should dict_update() open a + file? base64-encode the value? In smtpd(8) and postscreen(8), set the ehlo_discard_mask to ~0 so that STARTTLS, BDAT, DSN, etc. work only for clients diff --git a/postfix/src/global/mail_version.h b/postfix/src/global/mail_version.h index 882c9c471..012f65a77 100644 --- a/postfix/src/global/mail_version.h +++ b/postfix/src/global/mail_version.h @@ -20,7 +20,7 @@ * Patches change both the patchlevel and the release date. Snapshots have no * patchlevel; they change the release date only. */ -#define MAIL_RELEASE_DATE "20181228" +#define MAIL_RELEASE_DATE "20181229" #define MAIL_VERSION_NUMBER "3.4" #ifdef SNAPSHOT diff --git a/postfix/src/global/maps.c b/postfix/src/global/maps.c index 8a1dc9e88..7c84e9aa0 100644 --- a/postfix/src/global/maps.c +++ b/postfix/src/global/maps.c @@ -16,6 +16,11 @@ /* const char *key; /* int flags; /* +/* const char *maps_file_find(maps, key, flags) +/* MAPS *maps; +/* const char *key; +/* int flags; +/* /* MAPS *maps_free(maps) /* MAPS *maps; /* DESCRIPTION @@ -39,6 +44,10 @@ /* for example, DICT_FLAG_FIXED | DICT_FLAG_PATTERN selects /* dictionaries that have fixed keys or pattern keys. /* +/* maps_file_find() implements maps_find() but also decodes +/* the base64 lookup result. This requires that the maps are +/* opened with DICT_FLAG_SRC_RHS_IS_FILE. +/* /* maps_free() releases storage claimed by maps_create() /* and conveniently returns a null pointer. /* @@ -189,15 +198,81 @@ const char *maps_find(MAPS *maps, const char *name, int flags) maps->title, name); msg_warn("%s should return NO RESULT in case of NOT FOUND", maps->title); - maps->error = DICT_ERR_RETRY; + maps->error = DICT_ERR_CONFIG; return (0); } if (msg_verbose) - msg_info("%s: %s: %s: %s = %s", myname, maps->title, - *map_name, name, expansion); + msg_info("%s: %s: %s: %s = %.100s%s", myname, maps->title, + *map_name, name, expansion, + strlen(expansion) > 100 ? "..." : ""); return (expansion); } else if ((maps->error = dict->error) != 0) { - msg_warn("%s:%s lookup error for \"%.100s\"", + msg_warn("%s:%s lookup error for \"%s\"", + dict->type, dict->name, name); + break; + } + } + if (msg_verbose) + msg_info("%s: %s: %s: %s", myname, maps->title, name, maps->error ? + "search aborted" : "not found"); + return (0); +} + +/* maps_file_find - search a list of dictionaries and base64 decode */ + +const char *maps_file_find(MAPS *maps, const char *name, int flags) +{ + const char *myname = "maps_file_find"; + char **map_name; + const char *expansion; + DICT *dict; + VSTRING *unb64; + char *err; + + /* + * In case of return without map lookup (empty name or no maps). + */ + maps->error = 0; + + /* + * Temp. workaround, for buggy callers that pass zero-length keys when + * given partial addresses. + */ + if (*name == 0) + return (0); + + for (map_name = maps->argv->argv; *map_name; map_name++) { + if ((dict = dict_handle(*map_name)) == 0) + msg_panic("%s: dictionary not found: %s", myname, *map_name); + if ((dict->flags & DICT_FLAG_SRC_RHS_IS_FILE) == 0) + msg_panic("%s: %s: opened without DICT_FLAG_SRC_RHS_IS_FILE", + myname, maps->title); + if (flags != 0 && (dict->flags & flags) == 0) + continue; + if ((expansion = dict_get(dict, name)) != 0) { + if (*expansion == 0) { + msg_warn("%s lookup of %s returns an empty string result", + maps->title, name); + msg_warn("%s should return NO RESULT in case of NOT FOUND", + maps->title); + maps->error = DICT_ERR_CONFIG; + return (0); + } + if (msg_verbose) + msg_info("%s: %s: %s: %s = %.100s%s", myname, maps->title, + *map_name, name, expansion, + strlen(expansion) > 100 ? "..." : ""); + if ((unb64 = dict_file_from_b64(dict, expansion)) == 0) { + err = dict_file_get_error(dict); + msg_warn("table %s:%s: key %s: %s", + dict->type, dict->name, name, err); + myfree(err); + maps->error = DICT_ERR_CONFIG; + return (0); + } + return (vstring_str(unb64)); + } else if ((maps->error = dict->error) != 0) { + msg_warn("%s:%s lookup error for \"%s\"", dict->type, dict->name, name); break; } diff --git a/postfix/src/global/maps.h b/postfix/src/global/maps.h index ccf2c6ac2..04ee6dc93 100644 --- a/postfix/src/global/maps.h +++ b/postfix/src/global/maps.h @@ -27,6 +27,7 @@ typedef struct MAPS { extern MAPS *maps_create(const char *, const char *, int); extern const char *maps_find(MAPS *, const char *, int); +extern const char *maps_file_find(MAPS *, const char *, int); extern MAPS *maps_free(MAPS *); /* LICENSE diff --git a/postfix/src/global/mkmap_open.c b/postfix/src/global/mkmap_open.c index 32dfc9ea6..9d15eec30 100644 --- a/postfix/src/global/mkmap_open.c +++ b/postfix/src/global/mkmap_open.c @@ -301,12 +301,7 @@ MKMAP *mkmap_open(const char *type, const char *path, */ if ((mkmap->dict->flags & DICT_FLAG_UTF8_ACTIVE) == 0 && DICT_NEED_UTF8_ACTIVATION(util_utf8_enable, dict_flags)) - dict_utf8_wrapper_activate(mkmap->dict); - - /* Insert wrapper for base64 decoding file content. */ - if ((mkmap->dict->flags & DICT_FLAG_UNB64_ACTIVE) == 0 - && (mkmap->dict->flags & DICT_FLAG_SRC_RHS_IS_FILE) != 0) - dict_file_wrapper_activate(mkmap->dict); + mkmap->dict = dict_utf8_activate(mkmap->dict); /* * Resume signal delivery if multi-writer safe. diff --git a/postfix/src/postmap/file_test.ref b/postfix/src/postmap/file_test.ref index 1e808abbe..bc4815d53 100644 --- a/postfix/src/postmap/file_test.ref +++ b/postfix/src/postmap/file_test.ref @@ -11,4 +11,4 @@ file-2 this-is-file2 this-is-file1 this-is-file2 postmap: warning: table hash:file_test_map.db: key entry-4: malformed BASE64 value: postmap-entry-4 -postmap: fatal: table hash:file_test_map.db: query error: No such file or directory +postmap: fatal: table hash:file_test_map.db: query error diff --git a/postfix/src/postmap/postmap.c b/postfix/src/postmap/postmap.c index 207733573..9f368abb5 100644 --- a/postfix/src/postmap/postmap.c +++ b/postfix/src/postmap/postmap.c @@ -651,34 +651,30 @@ static int postmap_queries(VSTREAM *in, char **maps, const int map_count, dicts[n] = ((map_name = split_at(maps[n], ':')) != 0 ? dict_open3(maps[n], map_name, O_RDONLY, dict_flags) : dict_open3(var_db_type, maps[n], O_RDONLY, dict_flags)); - if ((value = dict_get(dicts[n], STR(keybuf))) != 0) { + value = ((dict_flags & DICT_FLAG_SRC_RHS_IS_FILE) ? + dict_file_lookup : dicts[n]->lookup) + (dicts[n], STR(keybuf)); + if (value != 0) { if (*value == 0) { msg_warn("table %s:%s: key %s: empty string result is not allowed", dicts[n]->type, dicts[n]->name, STR(keybuf)); msg_warn("table %s:%s should return NO RESULT in case of NOT FOUND", dicts[n]->type, dicts[n]->name); } -#if 0 - if (dict_flags & DICT_FLAG_SRC_RHS_IS_FILE) { - VSTRING *unb64; - char *err; - - if ((unb64 = dict_file_from_b64(dicts[n], value)) == 0) { - err = dict_file_get_error(dicts[n]); - msg_fatal("table %s:%s: key %s: %s", - dicts[n]->type, dicts[n]->name, - STR(keybuf), err); - } - value = STR(unb64); - } -#endif vstream_printf("%s %s\n", STR(keybuf), value); found = 1; break; } - if (dicts[n]->error) + switch (dicts[n]->error) { + case 0: + break; + case DICT_ERR_CONFIG: + msg_fatal("table %s:%s: query error", + dicts[n]->type, dicts[n]->name); + default: msg_fatal("table %s:%s: query error: %m", dicts[n]->type, dicts[n]->name); + } } } } else { @@ -752,31 +748,27 @@ static int postmap_query(const char *map_type, const char *map_name, const char *value; dict = dict_open3(map_type, map_name, O_RDONLY, dict_flags); - if ((value = dict_get(dict, key)) != 0) { + value = ((dict_flags & DICT_FLAG_SRC_RHS_IS_FILE) ? + dict_file_lookup : dict->lookup) (dict, key); + if (value != 0) { if (*value == 0) { msg_warn("table %s:%s: key %s: empty string result is not allowed", map_type, map_name, key); msg_warn("table %s:%s should return NO RESULT in case of NOT FOUND", map_type, map_name); } -#if 0 - if (dict_flags & DICT_FLAG_SRC_RHS_IS_FILE) { - VSTRING *unb64; - char *err; - - if ((unb64 = dict_file_from_b64(dict, value)) == 0) { - err = dict_file_get_error(dict); - msg_fatal("table %s:%s: key %s: %s", - dict->type, dict->name, - key, err); - } - value = STR(unb64); - } -#endif vstream_printf("%s\n", value); } - if (dict->error) - msg_fatal("table %s:%s: query error: %m", dict->type, dict->name); + switch (dict->error) { + case 0: + break; + case DICT_ERR_CONFIG: + msg_fatal("table %s:%s: query error", + dict->type, dict->name); + default: + msg_fatal("table %s:%s: query error: %m", + dict->type, dict->name); + } vstream_fflush(VSTREAM_OUT); dict_close(dict); return (value != 0); @@ -885,7 +877,6 @@ static void postmap_seq(const char *map_type, const char *map_name, msg_warn("table %s:%s should return NO RESULT in case of NOT FOUND", map_type, map_name); } -#if 1 if (dict_flags & DICT_FLAG_SRC_RHS_IS_FILE) { VSTRING *unb64; char *err; @@ -900,7 +891,6 @@ static void postmap_seq(const char *map_type, const char *map_name, } value = STR(unb64); } -#endif vstream_printf("%s %s\n", key, value); } if (dict->error) diff --git a/postfix/src/smtpd/smtpd_sasl_proto.c b/postfix/src/smtpd/smtpd_sasl_proto.c index 6706fc2b9..476752df7 100644 --- a/postfix/src/smtpd/smtpd_sasl_proto.c +++ b/postfix/src/smtpd/smtpd_sasl_proto.c @@ -7,7 +7,7 @@ /* #include "smtpd.h" /* #include "smtpd_sasl_proto.h" /* -/* void smtpd_sasl_auth_cmd(state, argc, argv) +/* int smtpd_sasl_auth_cmd(state, argc, argv) /* SMTPD_STATE *state; /* int argc; /* SMTPD_TOKEN *argv; diff --git a/postfix/src/util/Makefile.in b/postfix/src/util/Makefile.in index a1bd8bcba..f0715959c 100644 --- a/postfix/src/util/Makefile.in +++ b/postfix/src/util/Makefile.in @@ -40,8 +40,7 @@ SRCS = alldig.c allprint.c argv.c argv_split.c attr_clnt.c attr_print0.c \ poll_fd.c timecmp.c slmdb.c dict_pipe.c dict_random.c \ valid_utf8_hostname.c midna_domain.c argv_splitq.c balpar.c dict_union.c \ extpar.c dict_inline.c casefold.c dict_utf8.c strcasecmp_utf8.c \ - split_qnameval.c argv_attr_print.c argv_attr_scan.c dict_file.c \ - dict_wrapper.c + split_qnameval.c argv_attr_print.c argv_attr_scan.c dict_file.c OBJS = alldig.o allprint.o argv.o argv_split.o attr_clnt.o attr_print0.o \ attr_print64.o attr_print_plain.o attr_scan0.o attr_scan64.o \ attr_scan_plain.o auto_clnt.o base64_code.o basename.o binhash.o \ @@ -83,8 +82,7 @@ OBJS = alldig.o allprint.o argv.o argv_split.o attr_clnt.o attr_print0.o \ poll_fd.o timecmp.o $(NON_PLUGIN_MAP_OBJ) dict_pipe.o dict_random.o \ valid_utf8_hostname.o midna_domain.o argv_splitq.o balpar.o dict_union.o \ extpar.o dict_inline.o casefold.o dict_utf8.o strcasecmp_utf8.o \ - split_qnameval.o argv_attr_print.o argv_attr_scan.o dict_file.o \ - dict_wrapper.o + split_qnameval.o argv_attr_print.o argv_attr_scan.o dict_file.o # MAP_OBJ is for maps that may be dynamically loaded with dynamicmaps.cf. # When hard-linking these, makedefs sets NON_PLUGIN_MAP_OBJ=$(MAP_OBJ), # otherwise it sets the PLUGIN_* macros. @@ -1342,6 +1340,7 @@ dict_ht.o: sys_defs.h dict_ht.o: vbuf.h dict_ht.o: vstream.h dict_ht.o: vstring.h +dict_inline.cdict_static.o: dict_inline.cdict_static.c dict_inline.o: argv.h dict_inline.o: check_arg.h dict_inline.o: dict.h @@ -1648,18 +1647,6 @@ dict_utf8.o: sys_defs.h dict_utf8.o: vbuf.h dict_utf8.o: vstream.h dict_utf8.o: vstring.h -dict_wrapper.o: argv.h -dict_wrapper.o: check_arg.h -dict_wrapper.o: dict.h -dict_wrapper.o: dict_wrapper.c -dict_wrapper.o: msg.h -dict_wrapper.o: myflock.h -dict_wrapper.o: mymalloc.h -dict_wrapper.o: stringops.h -dict_wrapper.o: sys_defs.h -dict_wrapper.o: vbuf.h -dict_wrapper.o: vstream.h -dict_wrapper.o: vstring.h dir_forest.o: check_arg.h dir_forest.o: dir_forest.c dir_forest.o: dir_forest.h @@ -1917,6 +1904,8 @@ load_file.o: vbuf.h load_file.o: vstream.h load_file.o: warn_stat.h load_lib.o: load_lib.c +load_lib.o: load_lib.h +load_lib.o: msg.h load_lib.o: sys_defs.h lowercase.o: check_arg.h lowercase.o: lowercase.c diff --git a/postfix/src/util/dict.c b/postfix/src/util/dict.c index 84e413eb0..5d53860f1 100644 --- a/postfix/src/util/dict.c +++ b/postfix/src/util/dict.c @@ -640,7 +640,6 @@ static const NAME_MASK dict_mask[] = { "utf8_request", DICT_FLAG_UTF8_REQUEST, /* request UTF-8 activation */ "utf8_active", DICT_FLAG_UTF8_ACTIVE, /* UTF-8 is activated */ "src_rhs_is_file", DICT_FLAG_SRC_RHS_IS_FILE, /* value from file */ - "unb64_active", DICT_FLAG_UNB64_ACTIVE, /* base64 decode activated */ 0, }; diff --git a/postfix/src/util/dict.h b/postfix/src/util/dict.h index 8c03477e3..5cba86151 100644 --- a/postfix/src/util/dict.h +++ b/postfix/src/util/dict.h @@ -93,7 +93,7 @@ typedef struct DICT { DICT_OWNER owner; /* provenance */ int error; /* last operation only */ DICT_JMP_BUF *jbuf; /* exception handling */ - struct DICT_WRAPPER *wrapper; /* see below */ + struct DICT_UTF8_BACKUP *utf8_backup; /* see below */ struct VSTRING *file_buf; /* dict_file_to_buf() */ struct VSTRING *file_b64; /* dict_file_to_b64() */ } DICT; @@ -133,7 +133,6 @@ extern DICT *dict_debug(DICT *); #define DICT_FLAG_UTF8_ACTIVE (1<<20) /* UTF-8 proxy layer is present */ #define DICT_FLAG_SRC_RHS_IS_FILE \ (1<<21) /* Map source RHS is a file */ -#define DICT_FLAG_UNB64_ACTIVE (1<<22) /* File decode proxy layer is present */ #define DICT_FLAG_UTF8_MASK (DICT_FLAG_UTF8_REQUEST) @@ -252,29 +251,15 @@ extern int dict_flags_mask(const char *); extern void dict_type_override(DICT *, const char *); /* - * Wrappers for DICT methods. Usage: create an "trivial" wrapper object with - * dict_wrapper_alloc(), then for each method that requires special - * processing, specify a pointer to function that calls the 'next' wrapper's - * method of the same type, with the 'next' wrapper as the first argument - * (the 'self' pointer). + * Check and convert UTF-8 keys and values. */ -typedef struct DICT_WRAPPER { - const char *name; /* for literal constant */ - const char *(*lookup) (struct DICT_WRAPPER *, DICT *, const char *); - int (*update) (struct DICT_WRAPPER *, DICT *, const char *, const char *); - int (*delete) (struct DICT_WRAPPER *, DICT *, const char *); - struct DICT_WRAPPER *next; -} DICT_WRAPPER; +typedef struct DICT_UTF8_BACKUP { + const char *(*lookup) (struct DICT *, const char *); + int (*update) (struct DICT *, const char *, const char *); + int (*delete) (struct DICT *, const char *); +} DICT_UTF8_BACKUP; -extern void dict_wrapper_prepend(DICT *, DICT_WRAPPER *); -extern DICT_WRAPPER *dict_wrapper_alloc(ssize_t); -extern void dict_wrapper_free(DICT_WRAPPER *); - - /* - * Things that build on DICT_WRAPPER. - */ -extern void dict_utf8_wrapper_activate(DICT *); -extern void dict_file_wrapper_activate(DICT *); +extern DICT *dict_utf8_activate(DICT *); /* * Driver for interactive or scripted tests. @@ -327,6 +312,7 @@ extern struct VSTRING *dict_file_to_b64(DICT *, const char *); extern struct VSTRING *dict_file_from_b64(DICT *, const char *); extern char *dict_file_get_error(DICT *); extern void dict_file_purge_buffers(DICT *); +extern const char *dict_file_lookup(DICT *dict, const char *); /* LICENSE /* .ad diff --git a/postfix/src/util/dict_alloc.c b/postfix/src/util/dict_alloc.c index 384defbf6..3285a38e3 100644 --- a/postfix/src/util/dict_alloc.c +++ b/postfix/src/util/dict_alloc.c @@ -159,7 +159,7 @@ DICT *dict_alloc(const char *dict_type, const char *dict_name, ssize_t size) dict->owner.uid = INT_MAX; dict->error = DICT_ERR_NONE; dict->jbuf = 0; - dict->wrapper = 0; + dict->utf8_backup = 0; dict->file_buf = 0; dict->file_b64 = 0; return dict; @@ -173,8 +173,8 @@ void dict_free(DICT *dict) myfree(dict->name); if (dict->jbuf) myfree((void *) dict->jbuf); - if (dict->wrapper) - dict_wrapper_free(dict->wrapper); + if (dict->utf8_backup) + myfree((void *) dict->utf8_backup); if (dict->file_buf) vstring_free(dict->file_buf); if (dict->file_b64) diff --git a/postfix/src/util/dict_cidr_file.ref b/postfix/src/util/dict_cidr_file.ref index 537805e24..3e9e79205 100644 --- a/postfix/src/util/dict_cidr_file.ref +++ b/postfix/src/util/dict_cidr_file.ref @@ -1,10 +1,8 @@ ./dict_open: warning: cidr map dict_cidr_file.map, line 3: open dict_cidr_file3: No such file or directory: skipping this rule owner=untrusted (uid=USER) > get 1.1.1.1 -1.1.1.1=this-is-file1 - +1.1.1.1=dGhpcy1pcy1maWxlMQo= > get 2.2.2.2 -2.2.2.2=this-is-file2 - +2.2.2.2=dGhpcy1pcy1maWxlMgo= > get 3.3.3.3 3.3.3.3: not found diff --git a/postfix/src/util/dict_file.c b/postfix/src/util/dict_file.c index ebfbb3f16..ca4924c85 100644 --- a/postfix/src/util/dict_file.c +++ b/postfix/src/util/dict_file.c @@ -24,7 +24,7 @@ /* void dict_file_purge_buffers( /* DICT *dict) /* -/* void dict_file_wrapper_activate( +/* const char *dict_file_lookup( /* DICT *dict) /* DESCRIPTION /* dict_file_to_buf() reads the content of the specified files, @@ -48,10 +48,17 @@ /* it returns a desciption of the problem. Storage is owned /* by the caller. /* -/* dict_file_wrapper_activate() activates a wrapper that -/* automatically base64-decodes lookup results. +/* dict_file_lookup() wraps the dictionary lookup method and +/* decodes the base64 lookup result. The dictionary must be +/* opened with DICT_FLAG_SRC_RHS_IS_FILE. Sets dict->error to +/* DICT_ERR_CONFIG if the content is invalid. Decoding is not +/* built into the dict->lookup() method, because that would +/* complicate the implementation of map nesting (inline, thash), +/* map composition (pipemap, unionmap), and map proxying. /* DIAGNOSTICS -/* In case of error the result value is a null pointer, and +/* Panic: interface violation. +/* +/* In case of error the VSTRING result value is a null pointer, and /* an error description can be retrieved with dict_file_get_error(). /* The storage is owned by the caller. /* LICENSE @@ -199,56 +206,28 @@ void dict_file_purge_buffers(DICT *dict) } } -/* dict_file_wrapper_lookup - wrap the lookup method */ +/* dict_file_lookup - look up and decode dictionary entry */ -static const char *dict_file_wrapper_lookup(DICT_WRAPPER *wrapper, - DICT *dict, const char *key) +const char *dict_file_lookup(DICT *dict, const char *key) { - DICT_WRAPPER *next_wrapper; + const char myname[] = "dict_file_lookup"; const char *res; VSTRING *unb64; char *err; - next_wrapper = wrapper->next; - if ((res = next_wrapper->lookup(next_wrapper, dict, key)) != 0) { - if ((unb64 = dict_file_from_b64(dict, res)) == 0) { - err = dict_file_get_error(dict); - msg_warn("table %s:%s: key %s: %s", - dict->type, dict->name, key, err); - myfree(err); - dict->error = DICT_ERR_CONFIG; - res = 0; - } else { - res = vstring_str(unb64); - } + if ((dict->flags & DICT_FLAG_SRC_RHS_IS_FILE) == 0) + msg_panic("%s: dictionary opened without DICT_FLAG_SRC_RHS_IS_FILE", + myname); + if ((res = dict->lookup(dict, key)) == 0) + return (0); + if ((unb64 = dict_file_from_b64(dict, res)) == 0) { + err = dict_file_get_error(dict); + msg_warn("table %s:%s: key %s: %s", + dict->type, dict->name, + key, err); + myfree(err); + dict->error = DICT_ERR_CONFIG; + return (0); } - return (res); -} - -/* dict_file_wrapper_activate - wrap the lookup method */ - -void dict_file_wrapper_activate(DICT *dict) -{ - const char myname[] = "dict_file_wrapper_activate"; - DICT_WRAPPER *wrapper; - - /* - * Sanity check. - */ - if ((dict->flags & DICT_FLAG_UNB64_ACTIVE)) - msg_panic("%s: %s:%s Base64 decoding support is already activated", - myname, dict->type, dict->name); - - /* - * Interpose on the lookup method. - */ - wrapper = dict_wrapper_alloc(sizeof(*wrapper)); - wrapper->name = "file"; - wrapper->lookup = dict_file_wrapper_lookup; - dict_wrapper_prepend(dict, wrapper); - - /* - * Leave our mark. See sanity check above. - */ - dict->flags |= DICT_FLAG_UNB64_ACTIVE; + return STR(unb64); } diff --git a/postfix/src/util/dict_inline_file.ref b/postfix/src/util/dict_inline_file.ref index 2aed9bd57..9d49e95f4 100644 --- a/postfix/src/util/dict_inline_file.ref +++ b/postfix/src/util/dict_inline_file.ref @@ -5,10 +5,8 @@ owner=trusted (uid=2147483647) foo: error owner=trusted (uid=2147483647) > get file1 -file1=this-is-file1 - +file1=dGhpcy1pcy1maWxlMQo= > get file2 -file2=this-is-file2 - +file2=dGhpcy1pcy1maWxlMgo= > get file3 file3: not found diff --git a/postfix/src/util/dict_open.c b/postfix/src/util/dict_open.c index ad95f1255..ca8df67ee 100644 --- a/postfix/src/util/dict_open.c +++ b/postfix/src/util/dict_open.c @@ -479,16 +479,10 @@ DICT *dict_open3(const char *dict_type, const char *dict_name, msg_fatal("%s:%s: unable to get exclusive lock: %m", dict_type, dict_name); } - /* Insert wrapper for UTF-8 syntax checks and casefolding. */ + /* Last step: insert proxy for UTF-8 syntax checks and casefolding. */ if ((dict->flags & DICT_FLAG_UTF8_ACTIVE) == 0 && DICT_NEED_UTF8_ACTIVATION(util_utf8_enable, dict_flags)) - dict_utf8_wrapper_activate(dict); - - /* Insert wrapper for base64 decoding file content. */ - if ((dict->flags & DICT_FLAG_UNB64_ACTIVE) == 0 - && dict->flags & DICT_FLAG_SRC_RHS_IS_FILE) - dict_file_wrapper_activate(dict); - + dict = dict_utf8_activate(dict); return (dict); } diff --git a/postfix/src/util/dict_pcre_file.ref b/postfix/src/util/dict_pcre_file.ref index 7021c3370..727306d91 100644 --- a/postfix/src/util/dict_pcre_file.ref +++ b/postfix/src/util/dict_pcre_file.ref @@ -3,15 +3,10 @@ ./dict_open: warning: pcre map dict_pcre_file.map, line 6: empty pathname list: >>,<<': skipping this rule owner=untrusted (uid=USER) > get file1 -file1=this-is-file1 - +file1=dGhpcy1pcy1maWxlMQo= > get file2 -file2=this-is-file2 - +file2=dGhpcy1pcy1maWxlMgo= > get file3 file3: not found > get files12 -files12=this-is-file1 - -this-is-file2 - +files12=dGhpcy1pcy1maWxlMQoKdGhpcy1pcy1maWxlMgo= diff --git a/postfix/src/util/dict_pipe.c b/postfix/src/util/dict_pipe.c index 9940c4be6..8ce0faad7 100644 --- a/postfix/src/util/dict_pipe.c +++ b/postfix/src/util/dict_pipe.c @@ -166,8 +166,7 @@ DICT *dict_pipe_open(const char *name, int open_flags, int dict_flags) DICT_TYPE_PIPE, name, DICT_TYPE_PIPE)); if ((dict = dict_handle(dict_type_name)) == 0) - dict = dict_open(dict_type_name, open_flags, - dict_flags & ~DICT_FLAG_SRC_RHS_IS_FILE); + dict = dict_open(dict_type_name, open_flags, dict_flags); dict_register(dict_type_name, dict); DICT_OWNER_AGGREGATE_UPDATE(aggr_owner, dict->owner); if (cpp == argv->argv) diff --git a/postfix/src/util/dict_random_file.ref b/postfix/src/util/dict_random_file.ref index 7622c1f9c..cb0187379 100644 --- a/postfix/src/util/dict_random_file.ref +++ b/postfix/src/util/dict_random_file.ref @@ -5,8 +5,6 @@ owner=trusted (uid=2147483647) foo: error owner=trusted (uid=0) > get foo -foo=this-is-file1 - +foo=dGhpcy1pcy1maWxlMQo= > get bar -bar=this-is-file1 - +bar=dGhpcy1pcy1maWxlMQo= diff --git a/postfix/src/util/dict_regexp_file.ref b/postfix/src/util/dict_regexp_file.ref index 2a2ba7f52..221814362 100644 --- a/postfix/src/util/dict_regexp_file.ref +++ b/postfix/src/util/dict_regexp_file.ref @@ -1,10 +1,8 @@ ./dict_open: warning: regexp map dict_regexp_file.map, line 3: open dict_regexp_file3: No such file or directory: skipping this rule owner=untrusted (uid=USER) > get file1 -file1=this-is-file1 - +file1=dGhpcy1pcy1maWxlMQo= > get file2 -file2=this-is-file2 - +file2=dGhpcy1pcy1maWxlMgo= > get file3 file3: not found diff --git a/postfix/src/util/dict_static_file.ref b/postfix/src/util/dict_static_file.ref index 47048a297..259f9fb67 100644 --- a/postfix/src/util/dict_static_file.ref +++ b/postfix/src/util/dict_static_file.ref @@ -5,8 +5,6 @@ owner=trusted (uid=2147483647) foo: error owner=trusted (uid=2147483647) > get file1 -file1=this-is-file1 - +file1=dGhpcy1pcy1maWxlMQo= > get file2 -file2=this-is-file1 - +file2=dGhpcy1pcy1maWxlMQo= diff --git a/postfix/src/util/dict_union.c b/postfix/src/util/dict_union.c index 5c3b08af8..80df03b61 100644 --- a/postfix/src/util/dict_union.c +++ b/postfix/src/util/dict_union.c @@ -179,8 +179,7 @@ DICT *dict_union_open(const char *name, int open_flags, int dict_flags) DICT_TYPE_UNION, name, DICT_TYPE_UNION)); if ((dict = dict_handle(dict_type_name)) == 0) - dict = dict_open(dict_type_name, open_flags, - dict_flags & ~DICT_FLAG_SRC_RHS_IS_FILE); + dict = dict_open(dict_type_name, open_flags, dict_flags); dict_register(dict_type_name, dict); DICT_OWNER_AGGREGATE_UPDATE(aggr_owner, dict->owner); if (cpp == argv->argv) diff --git a/postfix/src/util/dict_utf8.c b/postfix/src/util/dict_utf8.c index e7dc47578..f1fc65a59 100644 --- a/postfix/src/util/dict_utf8.c +++ b/postfix/src/util/dict_utf8.c @@ -6,13 +6,13 @@ /* SYNOPSIS /* #include /* -/* void dict_utf8_wrapper_activate( +/* DICT *dict_utf8_activate( /* DICT *dict) /* DESCRIPTION -/* dict_utf8_wrapper_activate() wraps a dictionary's lookup/update/delete +/* dict_utf8_activate() wraps a dictionary's lookup/update/delete /* methods with code that enforces UTF-8 checks on keys and /* values, and that logs a warning when incorrect UTF-8 is -/* encountered. +/* encountered. The original dictionary handle becomes invalid. /* /* The wrapper code enforces a policy that maximizes application /* robustness (it avoids the need for new error-handling code @@ -22,6 +22,8 @@ /* skipped while reporting a non-error status, and lookup /* results that contain a non-UTF-8 value are blocked while /* reporting a configuration error. +/* BUGS +/* dict_utf8_activate() does not nest. /* LICENSE /* .ad /* .fi @@ -131,10 +133,9 @@ static int dict_utf8_check(const char *string, CONST_CHAR_STAR *err) /* dict_utf8_lookup - UTF-8 lookup method wrapper */ -static const char *dict_utf8_lookup(DICT_WRAPPER *wrapper, DICT *dict, - const char *key) +static const char *dict_utf8_lookup(DICT *dict, const char *key) { - DICT_WRAPPER *next_wrapper; + DICT_UTF8_BACKUP *backup; const char *utf8_err; const char *fold_res; const char *value; @@ -155,8 +156,8 @@ static const char *dict_utf8_lookup(DICT_WRAPPER *wrapper, DICT *dict, */ saved_flags = (dict->flags & DICT_FLAG_FOLD_ANY); dict->flags &= ~DICT_FLAG_FOLD_ANY; - next_wrapper = wrapper->next; - value = next_wrapper->lookup(next_wrapper, dict, fold_res); + backup = dict->utf8_backup; + value = backup->lookup(dict, fold_res); dict->flags |= saved_flags; /* @@ -174,10 +175,9 @@ static const char *dict_utf8_lookup(DICT_WRAPPER *wrapper, DICT *dict, /* dict_utf8_update - UTF-8 update method wrapper */ -static int dict_utf8_update(DICT_WRAPPER *wrapper, DICT *dict, - const char *key, const char *value) +static int dict_utf8_update(DICT *dict, const char *key, const char *value) { - DICT_WRAPPER *next_wrapper; + DICT_UTF8_BACKUP *backup; const char *utf8_err; const char *fold_res; int saved_flags; @@ -209,8 +209,8 @@ static int dict_utf8_update(DICT_WRAPPER *wrapper, DICT *dict, else { saved_flags = (dict->flags & DICT_FLAG_FOLD_ANY); dict->flags &= ~DICT_FLAG_FOLD_ANY; - next_wrapper = wrapper->next; - status = next_wrapper->update(next_wrapper, dict, fold_res, value); + backup = dict->utf8_backup; + status = backup->update(dict, fold_res, value); dict->flags |= saved_flags; return (status); } @@ -218,9 +218,9 @@ static int dict_utf8_update(DICT_WRAPPER *wrapper, DICT *dict, /* dict_utf8_delete - UTF-8 delete method wrapper */ -static int dict_utf8_delete(DICT_WRAPPER *wrapper, DICT *dict, const char *key) +static int dict_utf8_delete(DICT *dict, const char *key) { - DICT_WRAPPER *next_wrapper; + DICT_UTF8_BACKUP *backup; const char *utf8_err; const char *fold_res; int saved_flags; @@ -242,19 +242,19 @@ static int dict_utf8_delete(DICT_WRAPPER *wrapper, DICT *dict, const char *key) else { saved_flags = (dict->flags & DICT_FLAG_FOLD_ANY); dict->flags &= ~DICT_FLAG_FOLD_ANY; - next_wrapper = wrapper->next; - status = next_wrapper->delete(next_wrapper, dict, fold_res); + backup = dict->utf8_backup; + status = backup->delete(dict, fold_res); dict->flags |= saved_flags; return (status); } } -/* dict_utf8_wrapper_activate - wrap legacy dict object for UTF-8 processing */ +/* dict_utf8_activate - wrap a legacy dict object for UTF-8 processing */ -void dict_utf8_wrapper_activate(DICT *dict) +DICT *dict_utf8_activate(DICT *dict) { - const char myname[] = "dict_utf8_wrapper_activate"; - DICT_WRAPPER *wrapper; + const char myname[] = "dict_utf8_activate"; + DICT_UTF8_BACKUP *backup; /* * Sanity check. @@ -264,22 +264,37 @@ void dict_utf8_wrapper_activate(DICT *dict) if ((dict->flags & DICT_FLAG_UTF8_REQUEST) == 0) msg_panic("%s: %s:%s does not request Unicode support", myname, dict->type, dict->name); - if ((dict->flags & DICT_FLAG_UTF8_ACTIVE)) + if ((dict->flags & DICT_FLAG_UTF8_ACTIVE) || dict->utf8_backup != 0) msg_panic("%s: %s:%s Unicode support is already activated", myname, dict->type, dict->name); /* - * Interpose on the lookup/update/delete methods. + * Unlike dict_debug(3) we do not put a proxy dict object in front of the + * encapsulated object, because then we would have to bidirectionally + * propagate changes in the data members (errors, flags, jbuf, and so on) + * between proxy object and encapsulated object. + * + * Instead we attach ourselves behind the encapsulated dict object, and + * redirect some function pointers to ourselves. */ - wrapper = dict_wrapper_alloc(sizeof(*wrapper)); - wrapper->name = "utf8"; - wrapper->lookup = dict_utf8_lookup; - wrapper->update = dict_utf8_update; - wrapper->delete = dict_utf8_delete; - dict_wrapper_prepend(dict, wrapper); + backup = dict->utf8_backup = (DICT_UTF8_BACKUP *) mymalloc(sizeof(*backup)); + + /* + * Interpose on the lookup/update/delete methods. It is a conscious + * decision not to tinker with the iterator or destructor. + */ + backup->lookup = dict->lookup; + backup->update = dict->update; + backup->delete = dict->delete; + + dict->lookup = dict_utf8_lookup; + dict->update = dict_utf8_update; + dict->delete = dict_utf8_delete; /* * Leave our mark. See sanity check above. */ dict->flags |= DICT_FLAG_UTF8_ACTIVE; + + return (dict); } diff --git a/postfix/src/util/dict_wrapper.c b/postfix/src/util/dict_wrapper.c deleted file mode 100644 index 3a54c6b8e..000000000 --- a/postfix/src/util/dict_wrapper.c +++ /dev/null @@ -1,243 +0,0 @@ -/*++ -/* NAME -/* dict_wrapper 3 -/* SUMMARY -/* dictionary method wrappers -/* SYNOPSIS -/* #include -/* -/* void dict_wrapper_prepend( -/* DICT *dict, -/* DICT_WRAPPER *wrapper) -/* -/* DICT_WRAPPER *dict_wrapper_alloc( -/* ssize_t size) -/* -/* void dict_wrapper_free( -/* DICT_WRAPPER *wrapper) -/* DESCRIPTION -/* dict_wrapper_prepend() prepends the specified dictionary -/* lookup/update/delete wrappers to a chain that is evaluated -/* in reverse order. dict_wrapper_prepend() takes ownership -/* of the wrapper. -/* -/* dict_wrapper_alloc() allocates memory for a dictionary -/* wrapper object and initializes all wrapper methods to -/* empty (no override). -/* -/* dict_wrapper_free() destroys a chain of dictionary wrappers. -/* LICENSE -/* .ad -/* .fi -/* The Secure Mailer license must be distributed with this software. -/* AUTHOR(S) -/* Wietse Venema -/* Google, Inc. -/* 111 8th Avenue -/* New York, NY 10011, USA -/*--*/ - - /* - * System library. - */ -#include -#include - - /* - * Utility library. - */ -#include -#include -#include -#include -#include - - /* - * The final DICT_WRAPPER is installed first, and also contains the original - * DICT's methods. - */ -typedef struct { - DICT_WRAPPER wrapper; /* parent class, must be first */ - const char *(*saved_lookup) (DICT *, const char *); - int (*saved_update) (DICT *, const char *, const char *); - int (*saved_delete) (DICT *, const char *); -} DICT_FINAL_WRAPPER; - - /* - * Functions that override DICT methods, and that call into the head of - * the dict wrapper chain. - */ - -/* dict_wrapper_lookup - DICT method to call into wrapper chain head */ - -static const char *dict_wrapper_lookup(DICT *dict, const char *key) -{ - DICT_WRAPPER *head_wrapper = dict->wrapper; - - return (head_wrapper->lookup(head_wrapper, dict, key)); -} - -/* dict_wrapper_update - DICT method to call into wrapper chain head */ - -static int dict_wrapper_update(DICT *dict, const char *key, const char *value) -{ - DICT_WRAPPER *head_wrapper = dict->wrapper; - - return (head_wrapper->update(head_wrapper, dict, key, value)); -} - -/* dict_wrapper_delete - DICT method to call into wrapper chain head */ - -static int dict_wrapper_delete(DICT *dict, const char *key) -{ - DICT_WRAPPER *head_wrapper = dict->wrapper; - - return (head_wrapper->delete(head_wrapper, dict, key)); -} - - /* - * Empty methods for wrappers that override only some methods. These ensure - * that the next wrapper's methods are called with the right 'self' pointer. - */ - -/* empty_wrapper_lookup - wrapper method to call into next wrapper */ - -static const char *empty_wrapper_lookup(DICT_WRAPPER *wrapper, DICT *dict, - const char *key) -{ - DICT_WRAPPER *next_wrapper = wrapper->next; - - return (next_wrapper->lookup(next_wrapper, dict, key)); -} - -/* empty_wrapper_update - wrapper method to call into next wrapper */ - -static int empty_wrapper_update(DICT_WRAPPER *wrapper, DICT *dict, - const char *key, const char *value) -{ - DICT_WRAPPER *next_wrapper = wrapper->next; - - return (next_wrapper->update(next_wrapper, dict, key, value)); -} - -/* empty_wrapper_delete - wrapper method to call into next wrapper */ - -static int empty_wrapper_delete(DICT_WRAPPER *wrapper, DICT *dict, - const char *key) -{ - DICT_WRAPPER *next_wrapper = wrapper->next; - - return (next_wrapper->delete(next_wrapper, dict, key)); -} - - /* - * Wrapper methods for the final dict wrapper in the chain. These call into - * the saved DICT methods. - */ - -/* final_wrapper_lookup - wrapper method to call saved DICT method */ - -static const char *final_wrapper_lookup(DICT_WRAPPER *wrapper, DICT *dict, - const char *key) -{ - DICT_FINAL_WRAPPER *final_wrapper = (DICT_FINAL_WRAPPER *) wrapper; - - return (final_wrapper->saved_lookup(dict, key)); -} - -/* final_wrapper_update - wrapper method to call saved DICT method */ - -static int final_wrapper_update(DICT_WRAPPER *wrapper, DICT *dict, - const char *key, const char *value) -{ - DICT_FINAL_WRAPPER *final_wrapper = (DICT_FINAL_WRAPPER *) wrapper; - - return (final_wrapper->saved_update(dict, key, value)); -} - -/* final_wrapper_delete - wrapper method to call saved DICT method */ - -static int final_wrapper_delete(DICT_WRAPPER *wrapper, DICT *dict, - const char *key) -{ - DICT_FINAL_WRAPPER *final_wrapper = (DICT_FINAL_WRAPPER *) wrapper; - - return (final_wrapper->saved_delete(dict, key)); -} - - /* - * Finally, the functions that build the wrapper chain. - */ - -/* dict_wrapper_activate - wrap a DICT object for additional processing */ - -static void dict_wrapper_activate(DICT *dict) -{ - const char myname[] = "dict_wrapper_activate"; - DICT_FINAL_WRAPPER *final_wrapper; - - /* - * Sanity check. - */ - if (dict->wrapper != 0) - msg_panic("%s: %s:%s wrapper support is already activated", - myname, dict->type, dict->name); - - /* - * Install the final wrapper object that calls the original DICT's - * methods, and redirect DICT's method calls to ourselves. All other - * dictionary wrappers will be prepended to a chain that ends in the - * final wrapper object. - */ - final_wrapper = (DICT_FINAL_WRAPPER *) mymalloc(sizeof(*final_wrapper)); - final_wrapper->wrapper.name = "final"; - final_wrapper->wrapper.lookup = final_wrapper_lookup; - final_wrapper->wrapper.update = final_wrapper_update; - final_wrapper->wrapper.delete = final_wrapper_delete; - final_wrapper->wrapper.next = 0; - dict->wrapper = &final_wrapper->wrapper; - - /* - * Interpose on the DICT's lookup/update/delete methods. - */ - final_wrapper->saved_lookup = dict->lookup; - final_wrapper->saved_update = dict->update; - final_wrapper->saved_delete = dict->delete; - - dict->lookup = dict_wrapper_lookup; - dict->update = dict_wrapper_update; - dict->delete = dict_wrapper_delete; -} - -/* dict_wrapper_alloc - allocate and initialize dictionary wrapper */ - -DICT_WRAPPER *dict_wrapper_alloc(ssize_t size) -{ - DICT_WRAPPER *wrapper; - - wrapper = (DICT_WRAPPER *) mymalloc(size); - wrapper->lookup = empty_wrapper_lookup; - wrapper->update = empty_wrapper_update; - wrapper->delete = empty_wrapper_delete; - return (wrapper); -} - -/* dict_wrapper_prepend - prepend dict method overrides */ - -void dict_wrapper_prepend(DICT *dict, DICT_WRAPPER *wrapper) -{ - if (dict->wrapper == 0) - dict_wrapper_activate(dict); - - wrapper->next = dict->wrapper; - dict->wrapper = wrapper; -} - -/* dict_wrapper_free - wrapper destructor */ - -void dict_wrapper_free(DICT_WRAPPER *wrapper) -{ - if (wrapper->next) - dict_wrapper_free(wrapper->next); - myfree((void *) wrapper); -}