Expand PMF_FN_* macros.
[netbsd-mini2440.git] / external / ibm-public / postfix / dist / src / global / dict_proxy.c
bloba09387a7ae13847b1788ba56cdbf4a8abff02ae3
1 /* $NetBSD$ */
3 /*++
4 /* NAME
5 /* dict_proxy 3
6 /* SUMMARY
7 /* generic dictionary proxy client
8 /* SYNOPSIS
9 /* #include <dict_proxy.h>
11 /* DICT *dict_proxy_open(map, open_flags, dict_flags)
12 /* const char *map;
13 /* int open_flags;
14 /* int dict_flags;
15 /* DESCRIPTION
16 /* dict_proxy_open() relays read-only operations through
17 /* the Postfix proxymap server.
19 /* The \fIopen_flags\fR argument must specify O_RDONLY
20 /* or O_RDWR|O_CREAT. Depending on this, the client
21 /* connects to the proxymap multiserver or to the
22 /* proxywrite single updater.
24 /* The connection to the Postfix proxymap server is automatically
25 /* closed after $ipc_idle seconds of idle time, or after $ipc_ttl
26 /* seconds of activity.
27 /* SECURITY
28 /* The proxy map server is not meant to be a trusted process. Proxy
29 /* maps must not be used to look up security sensitive information
30 /* such as user/group IDs, output files, or external commands.
31 /* SEE ALSO
32 /* dict(3) generic dictionary manager
33 /* clnt_stream(3) client endpoint connection management
34 /* DIAGNOSTICS
35 /* Fatal errors: out of memory, unimplemented operation,
36 /* bad request parameter, map not approved for proxy access.
37 /* LICENSE
38 /* .ad
39 /* .fi
40 /* The Secure Mailer license must be distributed with this software.
41 /* AUTHOR(S)
42 /* Wietse Venema
43 /* IBM T.J. Watson Research
44 /* P.O. Box 704
45 /* Yorktown Heights, NY 10598, USA
46 /*--*/
48 /* System library. */
50 #include <sys_defs.h>
51 #include <errno.h>
52 #include <unistd.h>
54 /* Utility library. */
56 #include <msg.h>
57 #include <mymalloc.h>
58 #include <stringops.h>
59 #include <vstring.h>
60 #include <vstream.h>
61 #include <attr.h>
62 #include <dict.h>
64 /* Global library. */
66 #include <mail_proto.h>
67 #include <mail_params.h>
68 #include <clnt_stream.h>
69 #include <dict_proxy.h>
71 /* Application-specific. */
73 typedef struct {
74 DICT dict; /* generic members */
75 CLNT_STREAM *clnt; /* client handle (shared) */
76 const char *service; /* service name */
77 int in_flags; /* caller-specified flags */
78 VSTRING *result; /* storage */
79 } DICT_PROXY;
82 * SLMs.
84 #define STR(x) vstring_str(x)
85 #define VSTREQ(v,s) (strcmp(STR(v),s) == 0)
88 * All proxied maps of the same type share the same query/reply socket.
90 static CLNT_STREAM *proxymap_stream; /* read-only maps */
91 static CLNT_STREAM *proxywrite_stream; /* read-write maps */
93 /* dict_proxy_lookup - find table entry */
95 static const char *dict_proxy_lookup(DICT *dict, const char *key)
97 const char *myname = "dict_proxy_lookup";
98 DICT_PROXY *dict_proxy = (DICT_PROXY *) dict;
99 VSTREAM *stream;
100 int status;
101 int count = 0;
102 int request_flags;
105 * The client and server live in separate processes that may start and
106 * terminate independently. We cannot rely on a persistent connection,
107 * let alone on persistent state (such as a specific open table) that is
108 * associated with a specific connection. Each lookup needs to specify
109 * the table and the flags that were specified to dict_proxy_open().
111 VSTRING_RESET(dict_proxy->result);
112 VSTRING_TERMINATE(dict_proxy->result);
113 request_flags = (dict_proxy->in_flags & DICT_FLAG_RQST_MASK)
114 | (dict->flags & DICT_FLAG_RQST_MASK);
115 for (;;) {
116 stream = clnt_stream_access(dict_proxy->clnt);
117 errno = 0;
118 count += 1;
119 if (attr_print(stream, ATTR_FLAG_NONE,
120 ATTR_TYPE_STR, MAIL_ATTR_REQ, PROXY_REQ_LOOKUP,
121 ATTR_TYPE_STR, MAIL_ATTR_TABLE, dict->name,
122 ATTR_TYPE_INT, MAIL_ATTR_FLAGS, request_flags,
123 ATTR_TYPE_STR, MAIL_ATTR_KEY, key,
124 ATTR_TYPE_END) != 0
125 || vstream_fflush(stream)
126 || attr_scan(stream, ATTR_FLAG_STRICT,
127 ATTR_TYPE_INT, MAIL_ATTR_STATUS, &status,
128 ATTR_TYPE_STR, MAIL_ATTR_VALUE, dict_proxy->result,
129 ATTR_TYPE_END) != 2) {
130 if (msg_verbose || count > 1 || (errno && errno != EPIPE && errno != ENOENT))
131 msg_warn("%s: service %s: %m", myname, VSTREAM_PATH(stream));
132 } else {
133 if (msg_verbose)
134 msg_info("%s: table=%s flags=%s key=%s -> status=%d result=%s",
135 myname, dict->name,
136 dict_flags_str(request_flags), key,
137 status, STR(dict_proxy->result));
138 switch (status) {
139 case PROXY_STAT_BAD:
140 msg_fatal("%s lookup failed for table \"%s\" key \"%s\": "
141 "invalid request",
142 dict_proxy->service, dict->name, key);
143 case PROXY_STAT_DENY:
144 msg_fatal("%s service is not configured for table \"%s\"",
145 dict_proxy->service, dict->name);
146 case PROXY_STAT_OK:
147 return (STR(dict_proxy->result));
148 case PROXY_STAT_NOKEY:
149 dict_errno = 0;
150 return (0);
151 case PROXY_STAT_RETRY:
152 dict_errno = DICT_ERR_RETRY;
153 return (0);
154 default:
155 msg_warn("%s lookup failed for table \"%s\" key \"%s\": "
156 "unexpected reply status %d",
157 dict_proxy->service, dict->name, key, status);
160 clnt_stream_recover(dict_proxy->clnt);
161 sleep(1); /* XXX make configurable */
165 /* dict_proxy_update - update table entry */
167 static void dict_proxy_update(DICT *dict, const char *key, const char *value)
169 const char *myname = "dict_proxy_update";
170 DICT_PROXY *dict_proxy = (DICT_PROXY *) dict;
171 VSTREAM *stream;
172 int status;
173 int count = 0;
174 int request_flags;
177 * The client and server live in separate processes that may start and
178 * terminate independently. We cannot rely on a persistent connection,
179 * let alone on persistent state (such as a specific open table) that is
180 * associated with a specific connection. Each lookup needs to specify
181 * the table and the flags that were specified to dict_proxy_open().
183 request_flags = (dict_proxy->in_flags & DICT_FLAG_RQST_MASK)
184 | (dict->flags & DICT_FLAG_RQST_MASK);
185 for (;;) {
186 stream = clnt_stream_access(dict_proxy->clnt);
187 errno = 0;
188 count += 1;
189 if (attr_print(stream, ATTR_FLAG_NONE,
190 ATTR_TYPE_STR, MAIL_ATTR_REQ, PROXY_REQ_UPDATE,
191 ATTR_TYPE_STR, MAIL_ATTR_TABLE, dict->name,
192 ATTR_TYPE_INT, MAIL_ATTR_FLAGS, request_flags,
193 ATTR_TYPE_STR, MAIL_ATTR_KEY, key,
194 ATTR_TYPE_STR, MAIL_ATTR_VALUE, value,
195 ATTR_TYPE_END) != 0
196 || vstream_fflush(stream)
197 || attr_scan(stream, ATTR_FLAG_STRICT,
198 ATTR_TYPE_INT, MAIL_ATTR_STATUS, &status,
199 ATTR_TYPE_END) != 1) {
200 if (msg_verbose || count > 1 || (errno && errno != EPIPE && errno != ENOENT))
201 msg_warn("%s: service %s: %m", myname, VSTREAM_PATH(stream));
202 } else {
203 if (msg_verbose)
204 msg_info("%s: table=%s flags=%s key=%s value=%s -> status=%d",
205 myname, dict->name, dict_flags_str(request_flags),
206 key, value, status);
207 switch (status) {
208 case PROXY_STAT_BAD:
209 msg_fatal("%s update failed for table \"%s\" key \"%s\": "
210 "invalid request",
211 dict_proxy->service, dict->name, key);
212 case PROXY_STAT_DENY:
213 msg_fatal("%s update access is not configured for table \"%s\"",
214 dict_proxy->service, dict->name);
215 case PROXY_STAT_OK:
216 return;
217 default:
218 msg_warn("%s update failed for table \"%s\" key \"%s\": "
219 "unexpected reply status %d",
220 dict_proxy->service, dict->name, key, status);
223 clnt_stream_recover(dict_proxy->clnt);
224 sleep(1); /* XXX make configurable */
228 /* dict_proxy_delete - delete table entry */
230 static int dict_proxy_delete(DICT *dict, const char *key)
232 const char *myname = "dict_proxy_delete";
233 DICT_PROXY *dict_proxy = (DICT_PROXY *) dict;
234 VSTREAM *stream;
235 int status;
236 int count = 0;
237 int request_flags;
240 * The client and server live in separate processes that may start and
241 * terminate independently. We cannot rely on a persistent connection,
242 * let alone on persistent state (such as a specific open table) that is
243 * associated with a specific connection. Each lookup needs to specify
244 * the table and the flags that were specified to dict_proxy_open().
246 request_flags = (dict_proxy->in_flags & DICT_FLAG_RQST_MASK)
247 | (dict->flags & DICT_FLAG_RQST_MASK);
248 for (;;) {
249 stream = clnt_stream_access(dict_proxy->clnt);
250 errno = 0;
251 count += 1;
252 if (attr_print(stream, ATTR_FLAG_NONE,
253 ATTR_TYPE_STR, MAIL_ATTR_REQ, PROXY_REQ_DELETE,
254 ATTR_TYPE_STR, MAIL_ATTR_TABLE, dict->name,
255 ATTR_TYPE_INT, MAIL_ATTR_FLAGS, request_flags,
256 ATTR_TYPE_STR, MAIL_ATTR_KEY, key,
257 ATTR_TYPE_END) != 0
258 || vstream_fflush(stream)
259 || attr_scan(stream, ATTR_FLAG_STRICT,
260 ATTR_TYPE_INT, MAIL_ATTR_STATUS, &status,
261 ATTR_TYPE_END) != 1) {
262 if (msg_verbose || count > 1 || (errno && errno != EPIPE && errno !=
263 ENOENT))
264 msg_warn("%s: service %s: %m", myname, VSTREAM_PATH(stream));
265 } else {
266 if (msg_verbose)
267 msg_info("%s: table=%s flags=%s key=%s -> status=%d",
268 myname, dict->name, dict_flags_str(request_flags),
269 key, status);
270 switch (status) {
271 case PROXY_STAT_BAD:
272 msg_fatal("%s delete failed for table \"%s\" key \"%s\": "
273 "invalid request",
274 dict_proxy->service, dict->name, key);
275 case PROXY_STAT_DENY:
276 msg_fatal("%s update access is not configured for table \"%s\"",
277 dict_proxy->service, dict->name);
278 case PROXY_STAT_OK:
279 return 0;
280 case PROXY_STAT_NOKEY:
281 return 1;
282 default:
283 msg_warn("%s delete failed for table \"%s\" key \"%s\": "
284 "unexpected reply status %d",
285 dict_proxy->service, dict->name, key, status);
288 clnt_stream_recover(dict_proxy->clnt);
289 sleep(1); /* XXX make configurable */
293 /* dict_proxy_close - disconnect */
295 static void dict_proxy_close(DICT *dict)
297 DICT_PROXY *dict_proxy = (DICT_PROXY *) dict;
299 vstring_free(dict_proxy->result);
300 dict_free(dict);
303 /* dict_proxy_open - open remote map */
305 DICT *dict_proxy_open(const char *map, int open_flags, int dict_flags)
307 const char *myname = "dict_proxy_open";
308 DICT_PROXY *dict_proxy;
309 VSTREAM *stream;
310 int server_flags;
311 int status;
312 const char *service;
313 char *relative_path;
314 char *kludge = 0;
315 char *prefix;
316 CLNT_STREAM **pstream;
319 * If this map can't be proxied then we silently do a direct open. This
320 * allows sites to benefit from proxying the virtual mailbox maps without
321 * unnecessary pain.
323 if (dict_flags & DICT_FLAG_NO_PROXY)
324 return (dict_open(map, open_flags, dict_flags));
327 * Use a shared stream for proxied table lookups of the same type.
329 * XXX A complete implementation would also allow O_RDWR without O_CREAT.
330 * But we must not pass on every possible set of flags to the proxy
331 * server; only sets that make sense. For now, the flags are passed
332 * implicitly by choosing between the proxymap or proxywrite service.
334 * XXX Use absolute pathname to make this work from non-daemon processes.
336 if (open_flags == O_RDONLY) {
337 pstream = &proxymap_stream;
338 service = var_proxymap_service;
339 } else if (open_flags == (O_RDWR | O_CREAT)) {
340 pstream = &proxywrite_stream;
341 service = var_proxywrite_service;
342 } else
343 msg_fatal("%s: %s map open requires O_RDONLY or O_RDWR|O_CREAT mode",
344 map, DICT_TYPE_PROXY);
346 if (*pstream == 0) {
347 relative_path = concatenate(MAIL_CLASS_PRIVATE "/",
348 service, (char *) 0);
349 if (access(relative_path, F_OK) == 0)
350 prefix = MAIL_CLASS_PRIVATE;
351 else
352 prefix = kludge = concatenate(var_queue_dir, "/",
353 MAIL_CLASS_PRIVATE, (char *) 0);
354 *pstream = clnt_stream_create(prefix, service, var_ipc_idle_limit,
355 var_ipc_ttl_limit);
356 if (kludge)
357 myfree(kludge);
358 myfree(relative_path);
362 * Local initialization.
364 dict_proxy = (DICT_PROXY *)
365 dict_alloc(DICT_TYPE_PROXY, map, sizeof(*dict_proxy));
366 dict_proxy->dict.lookup = dict_proxy_lookup;
367 dict_proxy->dict.update = dict_proxy_update;
368 dict_proxy->dict.delete = dict_proxy_delete;
369 dict_proxy->dict.close = dict_proxy_close;
370 dict_proxy->in_flags = dict_flags;
371 dict_proxy->result = vstring_alloc(10);
372 dict_proxy->clnt = *pstream;
373 dict_proxy->service = service;
376 * Establish initial contact and get the map type specific flags.
378 * XXX Should retrieve flags from local instance.
380 for (;;) {
381 stream = clnt_stream_access(dict_proxy->clnt);
382 errno = 0;
383 if (attr_print(stream, ATTR_FLAG_NONE,
384 ATTR_TYPE_STR, MAIL_ATTR_REQ, PROXY_REQ_OPEN,
385 ATTR_TYPE_STR, MAIL_ATTR_TABLE, dict_proxy->dict.name,
386 ATTR_TYPE_INT, MAIL_ATTR_FLAGS, dict_proxy->in_flags,
387 ATTR_TYPE_END) != 0
388 || vstream_fflush(stream)
389 || attr_scan(stream, ATTR_FLAG_STRICT,
390 ATTR_TYPE_INT, MAIL_ATTR_STATUS, &status,
391 ATTR_TYPE_INT, MAIL_ATTR_FLAGS, &server_flags,
392 ATTR_TYPE_END) != 2) {
393 if (msg_verbose || (errno != EPIPE && errno != ENOENT))
394 msg_warn("%s: service %s: %m", VSTREAM_PATH(stream), myname);
395 } else {
396 if (msg_verbose)
397 msg_info("%s: connect to map=%s status=%d server_flags=%s",
398 myname, dict_proxy->dict.name, status,
399 dict_flags_str(server_flags));
400 switch (status) {
401 case PROXY_STAT_BAD:
402 msg_fatal("%s open failed for table \"%s\": invalid request",
403 dict_proxy->service, dict_proxy->dict.name);
404 case PROXY_STAT_DENY:
405 msg_fatal("%s service is not configured for table \"%s\"",
406 dict_proxy->service, dict_proxy->dict.name);
407 case PROXY_STAT_OK:
408 dict_proxy->dict.flags = dict_proxy->in_flags
409 | (server_flags & DICT_FLAG_IMPL_MASK);
410 return (DICT_DEBUG (&dict_proxy->dict));
411 default:
412 msg_warn("%s open failed for table \"%s\": unexpected status %d",
413 dict_proxy->service, dict_proxy->dict.name, status);
416 clnt_stream_recover(dict_proxy->clnt);
417 sleep(1); /* XXX make configurable */