/*++
/* NAME
/* dict_proxy 3
/* SUMMARY
/* generic dictionary proxy client
/* SYNOPSIS
/* #include <dict_proxy.h>
/*
/* DICT *dict_proxy_open(map, open_flags, dict_flags)
/* const char *map;
/* int open_flags;
/* int dict_flags;
/* DESCRIPTION
/* dict_proxy_open() relays read-only or read-write operations
/* through the Postfix proxymap server.
/*
/* The \fIopen_flags\fR argument must specify O_RDONLY
/* or O_RDWR. Depending on this, the client
/* connects to the proxymap multiserver or to the
/* proxywrite single updater.
/*
/* The connection to the Postfix proxymap server is automatically
/* closed after $ipc_idle seconds of idle time, or after $ipc_ttl
/* seconds of activity.
/* SECURITY
/* The proxy map server is not meant to be a trusted process. Proxy
/* maps must not be used to look up security sensitive information
/* such as user/group IDs, output files, or external commands.
/* SEE ALSO
/* dict(3) generic dictionary manager
/* clnt_stream(3) client endpoint connection management
/* DIAGNOSTICS
/* Fatal errors: out of memory, unimplemented operation,
/* bad request parameter, map not approved for proxy access.
/* LICENSE
/* .ad
/* .fi
/* The Secure Mailer license must be distributed with this software.
/* AUTHOR(S)
/* Wietse Venema
/* IBM T.J. Watson Research
/* P.O. Box 704
/* Yorktown Heights, NY 10598, USA
/*
/* Wietse Venema
/* Google, Inc.
/* 111 8th Avenue
/* New York, NY 10011, USA
/*--*/
/* System library. */
#include <sys_defs.h>
#include <errno.h>
#include <unistd.h>
/* Utility library. */
#include <msg.h>
#include <mymalloc.h>
#include <stringops.h>
#include <vstring.h>
#include <vstream.h>
#include <attr.h>
#include <dict.h>
/* Global library. */
#include <mail_proto.h>
#include <mail_params.h>
#include <clnt_stream.h>
#include <dict_proxy.h>
/* Application-specific. */
typedef struct {
DICT dict; /* generic members */
CLNT_STREAM *clnt; /* client handle (shared) */
const char *service; /* service name */
int inst_flags; /* saved dict flags */
VSTRING *reskey; /* result key storage */
VSTRING *result; /* storage */
} DICT_PROXY;
/*
* SLMs .
*/
#define STR(x) vstring_str(x)
#define VSTREQ(v,s) (strcmp(STR(v),s) == 0 )
/*
* All proxied maps of the same type share the same query / reply socket .
*/
static CLNT_STREAM *proxymap_stream; /* read-only maps */
static CLNT_STREAM *proxywrite_stream; /* read-write maps */
/* dict_proxy_handshake - receive server protocol announcement */
static int dict_proxy_handshake(VSTREAM *stream)
{
return (attr_scan(stream, ATTR_FLAG_STRICT,
RECV_ATTR_STREQ(MAIL_ATTR_PROTO, MAIL_ATTR_PROTO_PROXYMAP),
ATTR_TYPE_END));
}
/* dict_proxy_sequence - find first/next entry */
static int dict_proxy_sequence(DICT *dict, int function,
const char **key, const char **value)
{
const char *myname = "dict_proxy_sequence" ;
DICT_PROXY *dict_proxy = (DICT_PROXY *) dict;
VSTREAM *stream;
int status;
int count = 0 ;
int inst_flags;
int request_flags;
/*
* The client and server live in separate processes that may start and
* terminate independently . We cannot rely on a persistent connection ,
* let alone on persistent state ( such as a specific open table ) that is
* associated with a specific connection . Each lookup needs to specify
* the table and the flags that were specified to dict_proxy_open ( ) .
*/
VSTRING_RESET(dict_proxy->reskey);
VSTRING_TERMINATE(dict_proxy->reskey);
VSTRING_RESET(dict_proxy->result);
VSTRING_TERMINATE(dict_proxy->result);
inst_flags = dict_proxy->inst_flags;
request_flags = dict->flags;
for (;;) {
stream = clnt_stream_access(dict_proxy->clnt);
errno = 0 ;
count += 1 ;
if (stream == 0
|| attr_print(stream, ATTR_FLAG_NONE,
SEND_ATTR_STR(MAIL_ATTR_REQ, PROXY_REQ_SEQUENCE),
SEND_ATTR_STR(MAIL_ATTR_TABLE, dict->name),
SEND_ATTR_INT(MAIL_ATTR_INST_FLAGS, inst_flags),
SEND_ATTR_INT(MAIL_ATTR_FLAGS, request_flags),
SEND_ATTR_INT(MAIL_ATTR_FUNC, function),
ATTR_TYPE_END) != 0
|| vstream_fflush(stream)
|| attr_scan(stream, ATTR_FLAG_STRICT,
RECV_ATTR_INT(MAIL_ATTR_STATUS, &status),
RECV_ATTR_INT(MAIL_ATTR_FLAGS, &dict->flags),
RECV_ATTR_STR(MAIL_ATTR_KEY, dict_proxy->reskey),
RECV_ATTR_STR(MAIL_ATTR_VALUE, dict_proxy->result),
ATTR_TYPE_END) != 4 ) {
if (msg_verbose || count > 1 || (errno && errno != EPIPE && errno != ENOENT))
msg_warn("%s: service %s: %m" , myname, dict_proxy->service);
} else {
if (msg_verbose)
msg_info("%s: table=%s flags=%s func=%d -> status=%d key=%s val=%s" ,
myname, dict->name, dict_flags_str(request_flags),
function, status, STR(dict_proxy->reskey),
STR(dict_proxy->result));
switch (status) {
case PROXY_STAT_BAD:
msg_fatal("%s sequence failed for table \" %s\" function %d: "
"invalid request" ,
dict_proxy->service, dict->name, function);
case PROXY_STAT_DENY:
msg_fatal("%s service is not configured for table \" %s\"" ,
dict_proxy->service, dict->name);
case PROXY_STAT_OK:
*key = STR(dict_proxy->reskey);
*value = STR(dict_proxy->result);
DICT_ERR_VAL_RETURN(dict, DICT_ERR_NONE, DICT_STAT_SUCCESS);
case PROXY_STAT_NOKEY:
*key = *value = 0 ;
DICT_ERR_VAL_RETURN(dict, DICT_ERR_NONE, DICT_STAT_FAIL);
case PROXY_STAT_RETRY:
*key = *value = 0 ;
DICT_ERR_VAL_RETURN(dict, DICT_ERR_RETRY, DICT_STAT_ERROR);
case PROXY_STAT_CONFIG:
*key = *value = 0 ;
DICT_ERR_VAL_RETURN(dict, DICT_ERR_CONFIG, DICT_STAT_ERROR);
default :
msg_warn("%s sequence failed for table \" %s\" function %d: "
"unexpected reply status %d" ,
dict_proxy->service, dict->name, function, status);
}
}
clnt_stream_recover(dict_proxy->clnt);
sleep(1 ); /* XXX make configurable */
}
}
/* dict_proxy_lookup - find table entry */
static const char *dict_proxy_lookup(DICT *dict, const char *key)
{
const char *myname = "dict_proxy_lookup" ;
DICT_PROXY *dict_proxy = (DICT_PROXY *) dict;
VSTREAM *stream;
int status;
int count = 0 ;
int inst_flags;
int request_flags;
/*
* The client and server live in separate processes that may start and
* terminate independently . We cannot rely on a persistent connection ,
* let alone on persistent state ( such as a specific open table ) that is
* associated with a specific connection . Each lookup needs to specify
* the table and the flags that were specified to dict_proxy_open ( ) .
*/
VSTRING_RESET(dict_proxy->result);
VSTRING_TERMINATE(dict_proxy->result);
inst_flags = dict_proxy->inst_flags;
request_flags = dict->flags;
for (;;) {
stream = clnt_stream_access(dict_proxy->clnt);
errno = 0 ;
count += 1 ;
if (stream == 0
|| attr_print(stream, ATTR_FLAG_NONE,
SEND_ATTR_STR(MAIL_ATTR_REQ, PROXY_REQ_LOOKUP),
SEND_ATTR_STR(MAIL_ATTR_TABLE, dict->name),
SEND_ATTR_INT(MAIL_ATTR_INST_FLAGS, inst_flags),
SEND_ATTR_INT(MAIL_ATTR_FLAGS, request_flags),
SEND_ATTR_STR(MAIL_ATTR_KEY, key),
ATTR_TYPE_END) != 0
|| vstream_fflush(stream)
|| attr_scan(stream, ATTR_FLAG_STRICT,
RECV_ATTR_INT(MAIL_ATTR_STATUS, &status),
RECV_ATTR_INT(MAIL_ATTR_FLAGS, &dict->flags),
RECV_ATTR_STR(MAIL_ATTR_VALUE, dict_proxy->result),
ATTR_TYPE_END) != 3 ) {
if (msg_verbose || count > 1 || (errno && errno != EPIPE && errno != ENOENT))
msg_warn("%s: service %s: %m" , myname, dict_proxy->service);
} else {
if (msg_verbose)
msg_info("%s: table=%s flags=%s key=%s -> status=%d result=%s" ,
myname, dict->name,
dict_flags_str(request_flags), key,
status, STR(dict_proxy->result));
switch (status) {
case PROXY_STAT_BAD:
msg_fatal("%s lookup failed for table \" %s\" key \" %s\": "
"invalid request" ,
dict_proxy->service, dict->name, key);
case PROXY_STAT_DENY:
msg_fatal("%s service is not configured for table \" %s\"" ,
dict_proxy->service, dict->name);
case PROXY_STAT_OK:
DICT_ERR_VAL_RETURN(dict, DICT_ERR_NONE, STR(dict_proxy->result));
case PROXY_STAT_NOKEY:
DICT_ERR_VAL_RETURN(dict, DICT_ERR_NONE, (char *) 0 );
case PROXY_STAT_RETRY:
DICT_ERR_VAL_RETURN(dict, DICT_ERR_RETRY, (char *) 0 );
case PROXY_STAT_CONFIG:
DICT_ERR_VAL_RETURN(dict, DICT_ERR_CONFIG, (char *) 0 );
default :
msg_warn("%s lookup failed for table \" %s\" key \" %s\": "
"unexpected reply status %d" ,
dict_proxy->service, dict->name, key, status);
}
}
clnt_stream_recover(dict_proxy->clnt);
sleep(1 ); /* XXX make configurable */
}
}
/* dict_proxy_update - update table entry */
static int dict_proxy_update(DICT *dict, const char *key, const char *value)
{
const char *myname = "dict_proxy_update" ;
DICT_PROXY *dict_proxy = (DICT_PROXY *) dict;
VSTREAM *stream;
int status;
int count = 0 ;
int inst_flags;
int request_flags;
/*
* The client and server live in separate processes that may start and
* terminate independently . We cannot rely on a persistent connection ,
* let alone on persistent state ( such as a specific open table ) that is
* associated with a specific connection . Each lookup needs to specify
* the table and the flags that were specified to dict_proxy_open ( ) .
*/
inst_flags = dict_proxy->inst_flags;
request_flags = dict->flags;
for (;;) {
stream = clnt_stream_access(dict_proxy->clnt);
errno = 0 ;
count += 1 ;
if (stream == 0
|| attr_print(stream, ATTR_FLAG_NONE,
SEND_ATTR_STR(MAIL_ATTR_REQ, PROXY_REQ_UPDATE),
SEND_ATTR_STR(MAIL_ATTR_TABLE, dict->name),
SEND_ATTR_INT(MAIL_ATTR_INST_FLAGS, inst_flags),
SEND_ATTR_INT(MAIL_ATTR_FLAGS, request_flags),
SEND_ATTR_STR(MAIL_ATTR_KEY, key),
SEND_ATTR_STR(MAIL_ATTR_VALUE, value),
ATTR_TYPE_END) != 0
|| vstream_fflush(stream)
|| attr_scan(stream, ATTR_FLAG_STRICT,
RECV_ATTR_INT(MAIL_ATTR_STATUS, &status),
RECV_ATTR_INT(MAIL_ATTR_FLAGS, &dict->flags),
ATTR_TYPE_END) != 2 ) {
if (msg_verbose || count > 1 || (errno && errno != EPIPE && errno != ENOENT))
msg_warn("%s: service %s: %m" , myname, dict_proxy->service);
} else {
if (msg_verbose)
msg_info("%s: table=%s flags=%s key=%s value=%s -> status=%d" ,
myname, dict->name, dict_flags_str(request_flags),
key, value, status);
switch (status) {
case PROXY_STAT_BAD:
msg_fatal("%s update failed for table \" %s\" key \" %s\": "
"invalid request" ,
dict_proxy->service, dict->name, key);
case PROXY_STAT_DENY:
msg_fatal("%s update access is not configured for table \" %s\"" ,
dict_proxy->service, dict->name);
case PROXY_STAT_OK:
DICT_ERR_VAL_RETURN(dict, DICT_ERR_NONE, DICT_STAT_SUCCESS);
case PROXY_STAT_NOKEY:
DICT_ERR_VAL_RETURN(dict, DICT_ERR_NONE, DICT_STAT_FAIL);
case PROXY_STAT_RETRY:
DICT_ERR_VAL_RETURN(dict, DICT_ERR_RETRY, DICT_STAT_ERROR);
case PROXY_STAT_CONFIG:
DICT_ERR_VAL_RETURN(dict, DICT_ERR_CONFIG, DICT_STAT_ERROR);
default :
msg_warn("%s update failed for table \" %s\" key \" %s\": "
"unexpected reply status %d" ,
dict_proxy->service, dict->name, key, status);
}
}
clnt_stream_recover(dict_proxy->clnt);
sleep(1 ); /* XXX make configurable */
}
}
/* dict_proxy_delete - delete table entry */
static int dict_proxy_delete(DICT *dict, const char *key)
{
const char *myname = "dict_proxy_delete" ;
DICT_PROXY *dict_proxy = (DICT_PROXY *) dict;
VSTREAM *stream;
int status;
int count = 0 ;
int inst_flags;
int request_flags;
/*
* The client and server live in separate processes that may start and
* terminate independently . We cannot rely on a persistent connection ,
* let alone on persistent state ( such as a specific open table ) that is
* associated with a specific connection . Each lookup needs to specify
* the table and the flags that were specified to dict_proxy_open ( ) .
*/
inst_flags = dict_proxy->inst_flags;
request_flags = dict->flags;
for (;;) {
stream = clnt_stream_access(dict_proxy->clnt);
errno = 0 ;
count += 1 ;
if (stream == 0
|| attr_print(stream, ATTR_FLAG_NONE,
SEND_ATTR_STR(MAIL_ATTR_REQ, PROXY_REQ_DELETE),
SEND_ATTR_STR(MAIL_ATTR_TABLE, dict->name),
SEND_ATTR_INT(MAIL_ATTR_INST_FLAGS, inst_flags),
SEND_ATTR_INT(MAIL_ATTR_FLAGS, request_flags),
SEND_ATTR_STR(MAIL_ATTR_KEY, key),
ATTR_TYPE_END) != 0
|| vstream_fflush(stream)
|| attr_scan(stream, ATTR_FLAG_STRICT,
RECV_ATTR_INT(MAIL_ATTR_STATUS, &status),
RECV_ATTR_INT(MAIL_ATTR_FLAGS, &dict->flags),
ATTR_TYPE_END) != 2 ) {
if (msg_verbose || count > 1 || (errno && errno != EPIPE && errno !=
ENOENT))
msg_warn("%s: service %s: %m" , myname, dict_proxy->service);
} else {
if (msg_verbose)
msg_info("%s: table=%s flags=%s key=%s -> status=%d" ,
myname, dict->name, dict_flags_str(request_flags),
key, status);
switch (status) {
case PROXY_STAT_BAD:
msg_fatal("%s delete failed for table \" %s\" key \" %s\": "
"invalid request" ,
dict_proxy->service, dict->name, key);
case PROXY_STAT_DENY:
msg_fatal("%s update access is not configured for table \" %s\"" ,
dict_proxy->service, dict->name);
case PROXY_STAT_OK:
DICT_ERR_VAL_RETURN(dict, DICT_ERR_NONE, DICT_STAT_SUCCESS);
case PROXY_STAT_NOKEY:
DICT_ERR_VAL_RETURN(dict, DICT_ERR_NONE, DICT_STAT_FAIL);
case PROXY_STAT_RETRY:
DICT_ERR_VAL_RETURN(dict, DICT_ERR_RETRY, DICT_STAT_ERROR);
case PROXY_STAT_CONFIG:
DICT_ERR_VAL_RETURN(dict, DICT_ERR_CONFIG, DICT_STAT_ERROR);
default :
msg_warn("%s delete failed for table \" %s\" key \" %s\": "
"unexpected reply status %d" ,
dict_proxy->service, dict->name, key, status);
}
}
clnt_stream_recover(dict_proxy->clnt);
sleep(1 ); /* XXX make configurable */
}
}
/* dict_proxy_close - disconnect */
static void dict_proxy_close(DICT *dict)
{
DICT_PROXY *dict_proxy = (DICT_PROXY *) dict;
vstring_free(dict_proxy->reskey);
vstring_free(dict_proxy->result);
dict_free(dict);
}
/* dict_proxy_open - open remote map */
DICT *dict_proxy_open(const char *map, int open_flags, int dict_flags)
{
const char *myname = "dict_proxy_open" ;
DICT_PROXY *dict_proxy;
VSTREAM *stream;
int server_flags;
int status;
const char *service;
char *relative_path;
char *kludge = 0 ;
char *prefix;
CLNT_STREAM **pstream;
/*
* If this map can ' t be proxied then we silently do a direct open . This
* allows sites to benefit from proxying the virtual mailbox maps without
* unnecessary pain .
*/
if (dict_flags & DICT_FLAG_NO_PROXY)
return (dict_open(map, open_flags, dict_flags));
/*
* Use a shared stream for proxied table lookups of the same type .
*
* XXX A complete implementation would also allow O_RDWR without O_CREAT .
* But we must not pass on every possible set of flags to the proxy
* server ; only sets that make sense . For now , the flags are passed
* implicitly by choosing between the proxymap or proxywrite service .
*
* XXX Use absolute pathname to make this work from non - daemon processes .
*/
if (open_flags == O_RDONLY) {
pstream = &proxymap_stream;
service = var_proxymap_service;
} else if ((open_flags & O_RDWR) == O_RDWR) {
pstream = &proxywrite_stream;
service = var_proxywrite_service;
} else
msg_fatal("%s: %s map open requires O_RDONLY or O_RDWR mode" ,
map, DICT_TYPE_PROXY);
if (*pstream == 0 ) {
relative_path = concatenate(MAIL_CLASS_PRIVATE "/" ,
service, (char *) 0 );
if (access(relative_path, F_OK) == 0 )
prefix = MAIL_CLASS_PRIVATE;
else
prefix = kludge = concatenate(var_queue_dir, "/" ,
MAIL_CLASS_PRIVATE, (char *) 0 );
*pstream = clnt_stream_create(prefix, service, var_ipc_idle_limit,
var_ipc_ttl_limit,
dict_proxy_handshake);
if (kludge)
myfree(kludge);
myfree(relative_path);
}
/*
* Local initialization .
*/
dict_proxy = (DICT_PROXY *)
dict_alloc(DICT_TYPE_PROXY, map, sizeof (*dict_proxy));
dict_proxy->dict.lookup = dict_proxy_lookup;
dict_proxy->dict.update = dict_proxy_update;
dict_proxy->dict.delete = dict_proxy_delete;
dict_proxy->dict.sequence = dict_proxy_sequence;
dict_proxy->dict.close = dict_proxy_close;
dict_proxy->inst_flags = dict_flags;
dict_proxy->reskey = vstring_alloc(10 );
dict_proxy->result = vstring_alloc(10 );
dict_proxy->clnt = *pstream;
dict_proxy->service = service;
#define DICT_PROXY_ERR_RETURN(d) do { \
DICT *_d = (d); \
dict_proxy_close(&dict_proxy->dict); \
return (_d); \
} while (0 )
/*
* Establish initial contact and get the map type specific flags .
*/
for (;;) {
stream = clnt_stream_access(dict_proxy->clnt);
errno = 0 ;
if (stream == 0
|| attr_print(stream, ATTR_FLAG_NONE,
SEND_ATTR_STR(MAIL_ATTR_REQ, PROXY_REQ_OPEN),
SEND_ATTR_STR(MAIL_ATTR_TABLE, dict_proxy->dict.name),
SEND_ATTR_INT(MAIL_ATTR_INST_FLAGS, dict_proxy->inst_flags),
ATTR_TYPE_END) != 0
|| vstream_fflush(stream)
|| attr_scan(stream, ATTR_FLAG_STRICT,
RECV_ATTR_INT(MAIL_ATTR_STATUS, &status),
RECV_ATTR_INT(MAIL_ATTR_FLAGS, &server_flags),
ATTR_TYPE_END) != 2 ) {
if (msg_verbose || (errno != EPIPE && errno != ENOENT))
msg_warn("%s: service %s: %m" , myname, dict_proxy->service);
} else {
if (msg_verbose)
msg_info("%s: connect to map=%s status=%d server_flags=%s" ,
myname, dict_proxy->dict.name, status,
dict_flags_str(server_flags));
switch (status) {
case PROXY_STAT_BAD:
DICT_PROXY_ERR_RETURN(dict_surrogate(DICT_TYPE_PROXY,
dict_proxy->dict.name, open_flags, dict_flags,
"%s open failed for table \" %s\": invalid request" ,
dict_proxy->service, dict_proxy->dict.name));
case PROXY_STAT_DENY:
DICT_PROXY_ERR_RETURN(dict_surrogate(DICT_TYPE_PROXY,
dict_proxy->dict.name, open_flags, dict_flags,
"%s service is not configured for table \" %s\"" ,
dict_proxy->service, dict_proxy->dict.name));
case PROXY_STAT_OK:
dict_proxy->dict.flags = server_flags;
return (&dict_proxy->dict);
default :
msg_warn("%s open failed for table \" %s\": unexpected status %d" ,
dict_proxy->service, dict_proxy->dict.name, status);
}
}
clnt_stream_recover(dict_proxy->clnt);
sleep(1 ); /* XXX make configurable */
}
}
Messung V0.5 in Prozent C=71 H=95 G=83
¤ Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.0.16Bemerkung:
(vorverarbeitet am 2026-08-08)
¤
*Bot Zugriff