Postfix3.3.1
dict_nisplus.c
[詳解]
1 /*++
2 /* NAME
3 /* dict_nisplus 3
4 /* SUMMARY
5 /* dictionary manager interface to NIS+ maps
6 /* SYNOPSIS
7 /* #include <dict_nisplus.h>
8 /*
9 /* DICT *dict_nisplus_open(map, open_flags, dict_flags)
10 /* const char *map;
11 /* int dummy;
12 /* int dict_flags;
13 /* DESCRIPTION
14 /* dict_nisplus_open() makes the specified NIS+ map accessible via
15 /* the generic dictionary operations described in dict_open(3).
16 /* The \fIdummy\fR argument is not used.
17 /* SEE ALSO
18 /* dict(3) generic dictionary manager
19 /* DIAGNOSTICS
20 /* Fatal errors:
21 /* LICENSE
22 /* .ad
23 /* .fi
24 /* The Secure Mailer license must be distributed with this software.
25 /* AUTHOR(S)
26 /* Geoff Gibbs
27 /* UK-HGMP-RC
28 /* Hinxton
29 /* Cambridge
30 /* CB10 1SB, UK
31 /*
32 /* based on the code for dict_nis.c et al by :-
33 /*
34 /* Wietse Venema
35 /* IBM T.J. Watson Research
36 /* P.O. Box 704
37 /* Yorktown Heights, NY 10598, USA
38 /*--*/
39 
40 /* System library. */
41 
42 #include <sys_defs.h>
43 #include <stdio.h>
44 #include <string.h>
45 #include <ctype.h>
46 #include <stdlib.h>
47 #ifdef HAS_NISPLUS
48 #include <rpcsvc/nis.h> /* for nis_list */
49 #endif
50 
51 /* Utility library. */
52 
53 #include <msg.h>
54 #include <mymalloc.h>
55 #include <vstring.h>
56 #include <stringops.h>
57 #include <dict.h>
58 #include <dict_nisplus.h>
59 
60 #ifdef HAS_NISPLUS
61 
62 /* Application-specific. */
63 
64 typedef struct {
65  DICT dict; /* generic members */
66  char *template; /* parsed query template */
67  int column; /* NIS+ field number (start at 1) */
68 } DICT_NISPLUS;
69 
70  /*
71  * Begin quote from nis+(1):
72  *
73  * The following text represents a context-free grammar that defines the
74  * set of legal NIS+ names. The terminals in this grammar are the
75  * characters `.' (dot), `[' (open bracket), `]' (close bracket), `,'
76  * (comma), `=' (equals) and whitespace. Angle brackets (`<' and `>'),
77  * which delineate non- terminals, are not part of the grammar. The
78  * character `|' (vertical bar) is used to separate alternate productions
79  * and should be read as ``this production OR this production''.
80  *
81  * name ::= . | <simple name> | <indexed name>
82  *
83  * simple name ::= <string>. | <string>.<simple name>
84  *
85  * indexed name ::= <search criterion>,<simple name>
86  *
87  * search criterion ::= [ <attribute list> ]
88  *
89  * attribute list ::= <attribute> | <attribute>,<attribute list>
90  *
91  * attribute ::= <string> = <string>
92  *
93  * string ::= ISO Latin 1 character set except the character
94  * '/' (slash). The initial character may not be a terminal character or
95  * the characters '@' (at), '+' (plus), or (`-') hyphen.
96  *
97  * Terminals that appear in strings must be quoted with `"' (double quote).
98  * The `"' character may be quoted by quoting it with itself `""'.
99  *
100  * End quote fron nis+(1).
101  *
102  * This NIS client always quotes the entire query string (the value part of
103  * [attribute=value],file.domain.) so the issue with initial characters
104  * should not be applicable. One wonders what restrictions are applicable
105  * when a string is quoted, but the manual doesn't specify what can appear
106  * between quotes, and we don't want to get burned.
107  */
108 
109  /*
110  * SLMs.
111  */
112 #define STR(x) vstring_str(x)
113 
114 /* dict_nisplus_lookup - find table entry */
115 
116 static const char *dict_nisplus_lookup(DICT *dict, const char *key)
117 {
118  const char *myname = "dict_nisplus_lookup";
119  DICT_NISPLUS *dict_nisplus = (DICT_NISPLUS *) dict;
120  static VSTRING *quoted_key;
121  static VSTRING *query;
122  static VSTRING *retval;
123  nis_result *reply;
124  int count;
125  const char *cp;
126  int last_col;
127  int ch;
128 
129  dict->error = 0;
130 
131  /*
132  * Initialize.
133  */
134  if (quoted_key == 0) {
135  query = vstring_alloc(100);
136  retval = vstring_alloc(100);
137  quoted_key = vstring_alloc(100);
138  }
139 
140  /*
141  * Optionally fold the key.
142  */
143  if (dict->flags & DICT_FLAG_FOLD_FIX) {
144  if (dict->fold_buf == 0)
145  dict->fold_buf = vstring_alloc(10);
146  vstring_strcpy(dict->fold_buf, key);
147  key = lowercase(vstring_str(dict->fold_buf));
148  }
149 
150  /*
151  * Check that the lookup key does not contain characters disallowed by
152  * nis+(1).
153  *
154  * XXX Many client implementations don't seem to care about disallowed
155  * characters.
156  */
157  VSTRING_RESET(quoted_key);
158  VSTRING_ADDCH(quoted_key, '"');
159  for (cp = key; (ch = *(unsigned const char *) cp) != 0; cp++) {
160  if ((ISASCII(ch) && !ISPRINT(ch)) || (ch > 126 && ch < 160)) {
161  msg_warn("map %s:%s: lookup key with non-printing character 0x%x:"
162  " ignoring this request",
163  dict->type, dict->name, ch);
164  return (0);
165  } else if (ch == '"') {
166  VSTRING_ADDCH(quoted_key, '"');
167  }
168  VSTRING_ADDCH(quoted_key, ch);
169  }
170  VSTRING_ADDCH(quoted_key, '"');
171  VSTRING_TERMINATE(quoted_key);
172 
173  /*
174  * Plug the key into the query template, which typically looks something
175  * like the following: [alias=%s],mail_aliases.org_dir.my.nisplus.domain.
176  *
177  * XXX The nis+ documentation defines a length limit for simple names like
178  * a.b.c., but defines no length limit for (the components of) indexed
179  * names such as [x=y],a.b.c. Our query length is limited because Postfix
180  * addresses (in envelopes or in headers) have a finite length.
181  */
182  vstring_sprintf(query, dict_nisplus->template, STR(quoted_key));
183  reply = nis_list(STR(query), FOLLOW_LINKS | FOLLOW_PATH, NULL, NULL);
184 
185  /*
186  * When lookup succeeds, the result may be ambiguous, or the requested
187  * column may not exist.
188  */
189  if (reply->status == NIS_SUCCESS) {
190  if ((count = NIS_RES_NUMOBJ(reply)) != 1) {
191  msg_warn("ambiguous match (%d results) for %s in NIS+ map %s:"
192  " ignoring this request",
193  count, key, dict_nisplus->dict.name);
194  nis_freeresult(reply);
195  return (0);
196  } else {
197  last_col = NIS_RES_OBJECT(reply)->zo_data
198  .objdata_u.en_data.en_cols.en_cols_len - 1;
199  if (dict_nisplus->column > last_col)
200  msg_fatal("requested column %d > max column %d in table %s",
201  dict_nisplus->column, last_col,
202  dict_nisplus->dict.name);
203  vstring_strcpy(retval,
204  NIS_RES_OBJECT(reply)->zo_data.objdata_u
205  .en_data.en_cols.en_cols_val[dict_nisplus->column]
206  .ec_value.ec_value_val);
207  if (msg_verbose)
208  msg_info("%s: %s, column %d -> %s", myname, STR(query),
209  dict_nisplus->column, STR(retval));
210  nis_freeresult(reply);
211  return (STR(retval));
212  }
213  }
214 
215  /*
216  * When the NIS+ lookup fails for reasons other than "key not found",
217  * keep logging warnings, and hope that someone will eventually notice
218  * the problem and fix it.
219  */
220  else {
221  if (reply->status != NIS_NOTFOUND
222  && reply->status != NIS_PARTIAL) {
223  msg_warn("lookup %s, NIS+ map %s: %s",
224  key, dict_nisplus->dict.name,
225  nis_sperrno(reply->status));
226  dict->error = DICT_ERR_RETRY;
227  } else {
228  if (msg_verbose)
229  msg_info("%s: not found: query %s", myname, STR(query));
230  }
231  nis_freeresult(reply);
232  return (0);
233  }
234 }
235 
236 /* dict_nisplus_close - close NISPLUS map */
237 
238 static void dict_nisplus_close(DICT *dict)
239 {
240  DICT_NISPLUS *dict_nisplus = (DICT_NISPLUS *) dict;
241 
242  myfree(dict_nisplus->template);
243  if (dict->fold_buf)
244  vstring_free(dict->fold_buf);
245  dict_free(dict);
246 }
247 
248 /* dict_nisplus_open - open NISPLUS map */
249 
250 DICT *dict_nisplus_open(const char *map, int open_flags, int dict_flags)
251 {
252  const char *myname = "dict_nisplus_open";
253  DICT_NISPLUS *dict_nisplus;
254  char *col_field;
255 
256  /*
257  * Sanity check.
258  */
259  if (open_flags != O_RDONLY)
260  return (dict_surrogate(DICT_TYPE_NISPLUS, map, open_flags, dict_flags,
261  "%s:%s map requires O_RDONLY access mode",
262  DICT_TYPE_NISPLUS, map));
263 
264  /*
265  * Initialize. This is a read-only map with fixed strings, not with
266  * regular expressions.
267  */
268  dict_nisplus = (DICT_NISPLUS *)
269  dict_alloc(DICT_TYPE_NISPLUS, map, sizeof(*dict_nisplus));
270  dict_nisplus->dict.lookup = dict_nisplus_lookup;
271  dict_nisplus->dict.close = dict_nisplus_close;
272  dict_nisplus->dict.flags = dict_flags | DICT_FLAG_FIXED;
273  if (dict_flags & DICT_FLAG_FOLD_FIX)
274  dict_nisplus->dict.fold_buf = vstring_alloc(10);
275  dict_nisplus->dict.owner.status = DICT_OWNER_TRUSTED;
276 
277  /*
278  * Convert the query template into an indexed name and column number. The
279  * query template looks like:
280  *
281  * [attribute=%s;attribute=value...];simple.name.:column
282  *
283  * One instance of %s gets to be replaced by a version of the lookup key;
284  * other attributes must specify fixed values. The reason for using ';'
285  * is that the comma character is special in main.cf. When no column
286  * number is given at the end of the map name, we use a default column.
287  */
288  dict_nisplus->template = mystrdup(map);
289  translit(dict_nisplus->template, ";", ",");
290  if ((col_field = strstr(dict_nisplus->template, ".:")) != 0) {
291  col_field[1] = 0;
292  col_field += 2;
293  if (!alldig(col_field) || (dict_nisplus->column = atoi(col_field)) < 1)
294  msg_fatal("bad column field in NIS+ map name: %s", map);
295  } else {
296  dict_nisplus->column = 1;
297  }
298  if (msg_verbose)
299  msg_info("%s: opened NIS+ table %s for column %d",
300  myname, dict_nisplus->template, dict_nisplus->column);
301  return (DICT_DEBUG (&dict_nisplus->dict));
302 }
303 
304 #endif
int msg_verbose
Definition: msg.c:177
void myfree(void *ptr)
Definition: mymalloc.c:207
char * mystrdup(const char *str)
Definition: mymalloc.c:225
DICT * dict_nisplus_open(const char *, int, int)
#define vstring_str(vp)
Definition: vstring.h:71
char * name
Definition: dict.h:80
#define DICT_FLAG_FIXED
Definition: dict.h:114
int flags
Definition: dict.h:81
#define DICT_ERR_RETRY
Definition: dict.h:178
#define ISASCII(c)
Definition: sys_defs.h:1743
char * translit(char *, const char *, const char *)
Definition: translit.c:40
int alldig(const char *string)
Definition: alldig.c:38
#define DICT_FLAG_FOLD_FIX
Definition: dict.h:124
VSTRING * vstring_strcpy(VSTRING *vp, const char *src)
Definition: vstring.c:431
#define VSTRING_TERMINATE(vp)
Definition: vstring.h:74
Definition: dict.h:78
char * type
Definition: dict.h:79
#define VSTRING_ADDCH(vp, ch)
Definition: vstring.h:81
#define DICT_OWNER_TRUSTED
Definition: dict.h:46
#define VSTRING_RESET(vp)
Definition: vstring.h:77
#define STR(x)
Definition: anvil.c:518
void msg_warn(const char *fmt,...)
Definition: msg.c:215
VSTRING * vstring_alloc(ssize_t len)
Definition: vstring.c:353
#define DICT_TYPE_NISPLUS
Definition: dict_nisplus.h:22
int error
Definition: dict.h:94
VSTRING * vstring_sprintf(VSTRING *vp, const char *format,...)
Definition: vstring.c:602
char * lowercase(char *string)
Definition: lowercase.c:34
const char *(* lookup)(struct DICT *, const char *)
Definition: dict.h:82
NORETURN msg_fatal(const char *fmt,...)
Definition: msg.c:249
void dict_free(DICT *)
Definition: dict_alloc.c:163
#define ISPRINT(c)
Definition: sys_defs.h:1751
VSTRING * vstring_free(VSTRING *vp)
Definition: vstring.c:380
DICT * dict_alloc(const char *, const char *, ssize_t)
Definition: dict_alloc.c:135
VSTRING * fold_buf
Definition: dict.h:92
DICT * dict_surrogate(const char *dict_type, const char *dict_name, int open_flags, int dict_flags, const char *fmt,...)
void msg_info(const char *fmt,...)
Definition: msg.c:199