diff options
Diffstat (limited to 'src/util/dict_nisplus.c')
-rw-r--r-- | src/util/dict_nisplus.c | 304 |
1 files changed, 304 insertions, 0 deletions
diff --git a/src/util/dict_nisplus.c b/src/util/dict_nisplus.c new file mode 100644 index 0000000..1f5e126 --- /dev/null +++ b/src/util/dict_nisplus.c @@ -0,0 +1,304 @@ +/*++ +/* NAME +/* dict_nisplus 3 +/* SUMMARY +/* dictionary manager interface to NIS+ maps +/* SYNOPSIS +/* #include <dict_nisplus.h> +/* +/* DICT *dict_nisplus_open(map, open_flags, dict_flags) +/* const char *map; +/* int dummy; +/* int dict_flags; +/* DESCRIPTION +/* dict_nisplus_open() makes the specified NIS+ map accessible via +/* the generic dictionary operations described in dict_open(3). +/* The \fIdummy\fR argument is not used. +/* SEE ALSO +/* dict(3) generic dictionary manager +/* DIAGNOSTICS +/* Fatal errors: +/* LICENSE +/* .ad +/* .fi +/* The Secure Mailer license must be distributed with this software. +/* AUTHOR(S) +/* Geoff Gibbs +/* UK-HGMP-RC +/* Hinxton +/* Cambridge +/* CB10 1SB, UK +/* +/* based on the code for dict_nis.c et al by :- +/* +/* Wietse Venema +/* IBM T.J. Watson Research +/* P.O. Box 704 +/* Yorktown Heights, NY 10598, USA +/*--*/ + +/* System library. */ + +#include <sys_defs.h> +#include <stdio.h> +#include <string.h> +#include <ctype.h> +#include <stdlib.h> +#ifdef HAS_NISPLUS +#include <rpcsvc/nis.h> /* for nis_list */ +#endif + +/* Utility library. */ + +#include <msg.h> +#include <mymalloc.h> +#include <vstring.h> +#include <stringops.h> +#include <dict.h> +#include <dict_nisplus.h> + +#ifdef HAS_NISPLUS + +/* Application-specific. */ + +typedef struct { + DICT dict; /* generic members */ + char *template; /* parsed query template */ + int column; /* NIS+ field number (start at 1) */ +} DICT_NISPLUS; + + /* + * Begin quote from nis+(1): + * + * The following text represents a context-free grammar that defines the + * set of legal NIS+ names. The terminals in this grammar are the + * characters `.' (dot), `[' (open bracket), `]' (close bracket), `,' + * (comma), `=' (equals) and whitespace. Angle brackets (`<' and `>'), + * which delineate non- terminals, are not part of the grammar. The + * character `|' (vertical bar) is used to separate alternate productions + * and should be read as ``this production OR this production''. + * + * name ::= . | <simple name> | <indexed name> + * + * simple name ::= <string>. | <string>.<simple name> + * + * indexed name ::= <search criterion>,<simple name> + * + * search criterion ::= [ <attribute list> ] + * + * attribute list ::= <attribute> | <attribute>,<attribute list> + * + * attribute ::= <string> = <string> + * + * string ::= ISO Latin 1 character set except the character + * '/' (slash). The initial character may not be a terminal character or + * the characters '@' (at), '+' (plus), or (`-') hyphen. + * + * Terminals that appear in strings must be quoted with `"' (double quote). + * The `"' character may be quoted by quoting it with itself `""'. + * + * End quote fron nis+(1). + * + * This NIS client always quotes the entire query string (the value part of + * [attribute=value],file.domain.) so the issue with initial characters + * should not be applicable. One wonders what restrictions are applicable + * when a string is quoted, but the manual doesn't specify what can appear + * between quotes, and we don't want to get burned. + */ + + /* + * SLMs. + */ +#define STR(x) vstring_str(x) + +/* dict_nisplus_lookup - find table entry */ + +static const char *dict_nisplus_lookup(DICT *dict, const char *key) +{ + const char *myname = "dict_nisplus_lookup"; + DICT_NISPLUS *dict_nisplus = (DICT_NISPLUS *) dict; + static VSTRING *quoted_key; + static VSTRING *query; + static VSTRING *retval; + nis_result *reply; + int count; + const char *cp; + int last_col; + int ch; + + dict->error = 0; + + /* + * Initialize. + */ + if (quoted_key == 0) { + query = vstring_alloc(100); + retval = vstring_alloc(100); + quoted_key = vstring_alloc(100); + } + + /* + * Optionally fold the key. + */ + if (dict->flags & DICT_FLAG_FOLD_FIX) { + if (dict->fold_buf == 0) + dict->fold_buf = vstring_alloc(10); + vstring_strcpy(dict->fold_buf, key); + key = lowercase(vstring_str(dict->fold_buf)); + } + + /* + * Check that the lookup key does not contain characters disallowed by + * nis+(1). + * + * XXX Many client implementations don't seem to care about disallowed + * characters. + */ + VSTRING_RESET(quoted_key); + VSTRING_ADDCH(quoted_key, '"'); + for (cp = key; (ch = *(unsigned const char *) cp) != 0; cp++) { + if ((ISASCII(ch) && !ISPRINT(ch)) || (ch > 126 && ch < 160)) { + msg_warn("map %s:%s: lookup key with non-printing character 0x%x:" + " ignoring this request", + dict->type, dict->name, ch); + return (0); + } else if (ch == '"') { + VSTRING_ADDCH(quoted_key, '"'); + } + VSTRING_ADDCH(quoted_key, ch); + } + VSTRING_ADDCH(quoted_key, '"'); + VSTRING_TERMINATE(quoted_key); + + /* + * Plug the key into the query template, which typically looks something + * like the following: [alias=%s],mail_aliases.org_dir.my.nisplus.domain. + * + * XXX The nis+ documentation defines a length limit for simple names like + * a.b.c., but defines no length limit for (the components of) indexed + * names such as [x=y],a.b.c. Our query length is limited because Postfix + * addresses (in envelopes or in headers) have a finite length. + */ + vstring_sprintf(query, dict_nisplus->template, STR(quoted_key)); + reply = nis_list(STR(query), FOLLOW_LINKS | FOLLOW_PATH, NULL, NULL); + + /* + * When lookup succeeds, the result may be ambiguous, or the requested + * column may not exist. + */ + if (reply->status == NIS_SUCCESS) { + if ((count = NIS_RES_NUMOBJ(reply)) != 1) { + msg_warn("ambiguous match (%d results) for %s in NIS+ map %s:" + " ignoring this request", + count, key, dict_nisplus->dict.name); + nis_freeresult(reply); + return (0); + } else { + last_col = NIS_RES_OBJECT(reply)->zo_data + .objdata_u.en_data.en_cols.en_cols_len - 1; + if (dict_nisplus->column > last_col) + msg_fatal("requested column %d > max column %d in table %s", + dict_nisplus->column, last_col, + dict_nisplus->dict.name); + vstring_strcpy(retval, + NIS_RES_OBJECT(reply)->zo_data.objdata_u + .en_data.en_cols.en_cols_val[dict_nisplus->column] + .ec_value.ec_value_val); + if (msg_verbose) + msg_info("%s: %s, column %d -> %s", myname, STR(query), + dict_nisplus->column, STR(retval)); + nis_freeresult(reply); + return (STR(retval)); + } + } + + /* + * When the NIS+ lookup fails for reasons other than "key not found", + * keep logging warnings, and hope that someone will eventually notice + * the problem and fix it. + */ + else { + if (reply->status != NIS_NOTFOUND + && reply->status != NIS_PARTIAL) { + msg_warn("lookup %s, NIS+ map %s: %s", + key, dict_nisplus->dict.name, + nis_sperrno(reply->status)); + dict->error = DICT_ERR_RETRY; + } else { + if (msg_verbose) + msg_info("%s: not found: query %s", myname, STR(query)); + } + nis_freeresult(reply); + return (0); + } +} + +/* dict_nisplus_close - close NISPLUS map */ + +static void dict_nisplus_close(DICT *dict) +{ + DICT_NISPLUS *dict_nisplus = (DICT_NISPLUS *) dict; + + myfree(dict_nisplus->template); + if (dict->fold_buf) + vstring_free(dict->fold_buf); + dict_free(dict); +} + +/* dict_nisplus_open - open NISPLUS map */ + +DICT *dict_nisplus_open(const char *map, int open_flags, int dict_flags) +{ + const char *myname = "dict_nisplus_open"; + DICT_NISPLUS *dict_nisplus; + char *col_field; + + /* + * Sanity check. + */ + if (open_flags != O_RDONLY) + return (dict_surrogate(DICT_TYPE_NISPLUS, map, open_flags, dict_flags, + "%s:%s map requires O_RDONLY access mode", + DICT_TYPE_NISPLUS, map)); + + /* + * Initialize. This is a read-only map with fixed strings, not with + * regular expressions. + */ + dict_nisplus = (DICT_NISPLUS *) + dict_alloc(DICT_TYPE_NISPLUS, map, sizeof(*dict_nisplus)); + dict_nisplus->dict.lookup = dict_nisplus_lookup; + dict_nisplus->dict.close = dict_nisplus_close; + dict_nisplus->dict.flags = dict_flags | DICT_FLAG_FIXED; + if (dict_flags & DICT_FLAG_FOLD_FIX) + dict_nisplus->dict.fold_buf = vstring_alloc(10); + dict_nisplus->dict.owner.status = DICT_OWNER_TRUSTED; + + /* + * Convert the query template into an indexed name and column number. The + * query template looks like: + * + * [attribute=%s;attribute=value...];simple.name.:column + * + * One instance of %s gets to be replaced by a version of the lookup key; + * other attributes must specify fixed values. The reason for using ';' + * is that the comma character is special in main.cf. When no column + * number is given at the end of the map name, we use a default column. + */ + dict_nisplus->template = mystrdup(map); + translit(dict_nisplus->template, ";", ","); + if ((col_field = strstr(dict_nisplus->template, ".:")) != 0) { + col_field[1] = 0; + col_field += 2; + if (!alldig(col_field) || (dict_nisplus->column = atoi(col_field)) < 1) + msg_fatal("bad column field in NIS+ map name: %s", map); + } else { + dict_nisplus->column = 1; + } + if (msg_verbose) + msg_info("%s: opened NIS+ table %s for column %d", + myname, dict_nisplus->template, dict_nisplus->column); + return (DICT_DEBUG (&dict_nisplus->dict)); +} + +#endif |