1 /* $NetBSD: proxymap.c,v 1.2 2017/02/14 01:16:47 christos Exp $ */ 2 3 /*++ 4 /* NAME 5 /* proxymap 8 6 /* SUMMARY 7 /* Postfix lookup table proxy server 8 /* SYNOPSIS 9 /* \fBproxymap\fR [generic Postfix daemon options] 10 /* DESCRIPTION 11 /* The \fBproxymap\fR(8) server provides read-only or read-write 12 /* table lookup service to Postfix processes. These services are 13 /* implemented with distinct service names: \fBproxymap\fR and 14 /* \fBproxywrite\fR, respectively. The purpose of these services is: 15 /* .IP \(bu 16 /* To overcome chroot restrictions. For example, a chrooted SMTP 17 /* server needs access to the system passwd file in order to 18 /* reject mail for non-existent local addresses, but it is not 19 /* practical to maintain a copy of the passwd file in the chroot 20 /* jail. The solution: 21 /* .sp 22 /* .nf 23 /* local_recipient_maps = 24 /* proxy:unix:passwd.byname $alias_maps 25 /* .fi 26 /* .IP \(bu 27 /* To consolidate the number of open lookup tables by sharing 28 /* one open table among multiple processes. For example, making 29 /* mysql connections from every Postfix daemon process results 30 /* in "too many connections" errors. The solution: 31 /* .sp 32 /* .nf 33 /* virtual_alias_maps = 34 /* proxy:mysql:/etc/postfix/virtual_alias.cf 35 /* .fi 36 /* .sp 37 /* The total number of connections is limited by the number of 38 /* proxymap server processes. 39 /* .IP \(bu 40 /* To provide single-updater functionality for lookup tables 41 /* that do not reliably support multiple writers (i.e. all 42 /* file-based tables). 43 /* .PP 44 /* The \fBproxymap\fR(8) server implements the following requests: 45 /* .IP "\fBopen\fR \fImaptype:mapname flags\fR" 46 /* Open the table with type \fImaptype\fR and name \fImapname\fR, 47 /* as controlled by \fIflags\fR. The reply includes the \fImaptype\fR 48 /* dependent flags (to distinguish a fixed string table from a regular 49 /* expression table). 50 /* .IP "\fBlookup\fR \fImaptype:mapname flags key\fR" 51 /* Look up the data stored under the requested key. 52 /* The reply is the request completion status code and 53 /* the lookup result value. 54 /* The \fImaptype:mapname\fR and \fIflags\fR are the same 55 /* as with the \fBopen\fR request. 56 /* .IP "\fBupdate\fR \fImaptype:mapname flags key value\fR" 57 /* Update the data stored under the requested key. 58 /* The reply is the request completion status code. 59 /* The \fImaptype:mapname\fR and \fIflags\fR are the same 60 /* as with the \fBopen\fR request. 61 /* .sp 62 /* To implement single-updater maps, specify a process limit 63 /* of 1 in the master.cf file entry for the \fBproxywrite\fR 64 /* service. 65 /* .sp 66 /* This request is supported in Postfix 2.5 and later. 67 /* .IP "\fBdelete\fR \fImaptype:mapname flags key\fR" 68 /* Delete the data stored under the requested key. 69 /* The reply is the request completion status code. 70 /* The \fImaptype:mapname\fR and \fIflags\fR are the same 71 /* as with the \fBopen\fR request. 72 /* .sp 73 /* This request is supported in Postfix 2.5 and later. 74 /* .IP "\fBsequence\fR \fImaptype:mapname flags function\fR" 75 /* Iterate over the specified database. The \fIfunction\fR 76 /* is one of DICT_SEQ_FUN_FIRST or DICT_SEQ_FUN_NEXT. 77 /* The reply is the request completion status code and 78 /* a lookup key and result value, if found. 79 /* .sp 80 /* This request is supported in Postfix 2.9 and later. 81 /* .PP 82 /* The request completion status is one of OK, RETRY, NOKEY 83 /* (lookup failed because the key was not found), BAD (malformed 84 /* request) or DENY (the table is not approved for proxy read 85 /* or update access). 86 /* 87 /* There is no \fBclose\fR command, nor are tables implicitly closed 88 /* when a client disconnects. The purpose is to share tables among 89 /* multiple client processes. 90 /* SERVER PROCESS MANAGEMENT 91 /* .ad 92 /* .fi 93 /* \fBproxymap\fR(8) servers run under control by the Postfix 94 /* \fBmaster\fR(8) 95 /* server. Each server can handle multiple simultaneous connections. 96 /* When all servers are busy while a client connects, the \fBmaster\fR(8) 97 /* creates a new \fBproxymap\fR(8) server process, provided that the 98 /* process limit is not exceeded. 99 /* Each server terminates after serving at least \fB$max_use\fR clients 100 /* or after \fB$max_idle\fR seconds of idle time. 101 /* SECURITY 102 /* .ad 103 /* .fi 104 /* The \fBproxymap\fR(8) server opens only tables that are 105 /* approved via the \fBproxy_read_maps\fR or \fBproxy_write_maps\fR 106 /* configuration parameters, does not talk to 107 /* users, and can run at fixed low privilege, chrooted or not. 108 /* However, running the proxymap server chrooted severely limits 109 /* usability, because it can open only chrooted tables. 110 /* 111 /* The \fBproxymap\fR(8) server is not a trusted daemon process, and must 112 /* not be used to look up sensitive information such as UNIX user or 113 /* group IDs, mailbox file/directory names or external commands. 114 /* 115 /* In Postfix version 2.2 and later, the proxymap client recognizes 116 /* requests to access a table for security-sensitive purposes, 117 /* and opens the table directly. This allows the same main.cf 118 /* setting to be used by sensitive and non-sensitive processes. 119 /* 120 /* Postfix-writable data files should be stored under a dedicated 121 /* directory that is writable only by the Postfix mail system, 122 /* such as the Postfix-owned \fBdata_directory\fR. 123 /* 124 /* In particular, Postfix-writable files should never exist 125 /* in root-owned directories. That would open up a particular 126 /* type of security hole where ownership of a file or directory 127 /* does not match the provider of its content. 128 /* DIAGNOSTICS 129 /* Problems and transactions are logged to \fBsyslogd\fR(8). 130 /* BUGS 131 /* The \fBproxymap\fR(8) server provides service to multiple clients, 132 /* and must therefore not be used for tables that have high-latency 133 /* lookups. 134 /* 135 /* The \fBproxymap\fR(8) read-write service does not explicitly 136 /* close lookup tables (even if it did, this could not be relied on, 137 /* because the process may be terminated between table updates). 138 /* The read-write service should therefore not be used with tables that 139 /* leave persistent storage in an inconsistent state between 140 /* updates (for example, CDB). Tables that support "sync on 141 /* update" should be safe (for example, Berkeley DB) as should 142 /* tables that are implemented by a real DBMS. 143 /* CONFIGURATION PARAMETERS 144 /* .ad 145 /* .fi 146 /* On busy mail systems a long time may pass before 147 /* \fBproxymap\fR(8) relevant 148 /* changes to \fBmain.cf\fR are picked up. Use the command 149 /* "\fBpostfix reload\fR" to speed up a change. 150 /* 151 /* The text below provides only a parameter summary. See 152 /* \fBpostconf\fR(5) for more details including examples. 153 /* .IP "\fBconfig_directory (see 'postconf -d' output)\fR" 154 /* The default location of the Postfix main.cf and master.cf 155 /* configuration files. 156 /* .IP "\fBdata_directory (see 'postconf -d' output)\fR" 157 /* The directory with Postfix-writable data files (for example: 158 /* caches, pseudo-random numbers). 159 /* .IP "\fBdaemon_timeout (18000s)\fR" 160 /* How much time a Postfix daemon process may take to handle a 161 /* request before it is terminated by a built-in watchdog timer. 162 /* .IP "\fBipc_timeout (3600s)\fR" 163 /* The time limit for sending or receiving information over an internal 164 /* communication channel. 165 /* .IP "\fBmax_idle (100s)\fR" 166 /* The maximum amount of time that an idle Postfix daemon process waits 167 /* for an incoming connection before terminating voluntarily. 168 /* .IP "\fBmax_use (100)\fR" 169 /* The maximal number of incoming connections that a Postfix daemon 170 /* process will service before terminating voluntarily. 171 /* .IP "\fBprocess_id (read-only)\fR" 172 /* The process ID of a Postfix command or daemon process. 173 /* .IP "\fBprocess_name (read-only)\fR" 174 /* The process name of a Postfix command or daemon process. 175 /* .IP "\fBproxy_read_maps (see 'postconf -d' output)\fR" 176 /* The lookup tables that the \fBproxymap\fR(8) server is allowed to 177 /* access for the read-only service. 178 /* .PP 179 /* Available in Postfix 2.5 and later: 180 /* .IP "\fBdata_directory (see 'postconf -d' output)\fR" 181 /* The directory with Postfix-writable data files (for example: 182 /* caches, pseudo-random numbers). 183 /* .IP "\fBproxy_write_maps (see 'postconf -d' output)\fR" 184 /* The lookup tables that the \fBproxymap\fR(8) server is allowed to 185 /* access for the read-write service. 186 /* SEE ALSO 187 /* postconf(5), configuration parameters 188 /* master(5), generic daemon options 189 /* README FILES 190 /* .ad 191 /* .fi 192 /* Use "\fBpostconf readme_directory\fR" or 193 /* "\fBpostconf html_directory\fR" to locate this information. 194 /* .na 195 /* .nf 196 /* DATABASE_README, Postfix lookup table overview 197 /* LICENSE 198 /* .ad 199 /* .fi 200 /* The Secure Mailer license must be distributed with this software. 201 /* HISTORY 202 /* .ad 203 /* .fi 204 /* The proxymap service was introduced with Postfix 2.0. 205 /* AUTHOR(S) 206 /* Wietse Venema 207 /* IBM T.J. Watson Research 208 /* P.O. Box 704 209 /* Yorktown Heights, NY 10598, USA 210 /* 211 /* Wietse Venema 212 /* Google, Inc. 213 /* 111 8th Avenue 214 /* New York, NY 10011, USA 215 /*--*/ 216 217 /* System library. */ 218 219 #include <sys_defs.h> 220 #include <string.h> 221 #include <stdlib.h> 222 #include <unistd.h> 223 224 /* Utility library. */ 225 226 #include <msg.h> 227 #include <mymalloc.h> 228 #include <vstring.h> 229 #include <htable.h> 230 #include <stringops.h> 231 #include <dict.h> 232 233 /* Global library. */ 234 235 #include <mail_conf.h> 236 #include <mail_params.h> 237 #include <mail_version.h> 238 #include <mail_proto.h> 239 #include <dict_proxy.h> 240 241 /* Server skeleton. */ 242 243 #include <mail_server.h> 244 245 /* Application-specific. */ 246 247 /* 248 * XXX All but the last are needed here so that $name expansion dependencies 249 * aren't too broken. The fix is to gather all parameter default settings in 250 * one place. 251 */ 252 char *var_alias_maps; 253 char *var_local_rcpt_maps; 254 char *var_virt_alias_maps; 255 char *var_virt_alias_doms; 256 char *var_virt_mailbox_maps; 257 char *var_virt_mailbox_doms; 258 char *var_relay_rcpt_maps; 259 char *var_relay_domains; 260 char *var_canonical_maps; 261 char *var_send_canon_maps; 262 char *var_rcpt_canon_maps; 263 char *var_relocated_maps; 264 char *var_transport_maps; 265 char *var_verify_map; 266 char *var_smtpd_snd_auth_maps; 267 char *var_psc_cache_map; 268 char *var_proxy_read_maps; 269 char *var_proxy_write_maps; 270 271 /* 272 * The pre-approved, pre-parsed list of maps. 273 */ 274 static HTABLE *proxy_auth_maps; 275 276 /* 277 * Shared and static to reduce memory allocation overhead. 278 */ 279 static VSTRING *request; 280 static VSTRING *request_map; 281 static VSTRING *request_key; 282 static VSTRING *request_value; 283 static VSTRING *map_type_name_flags; 284 285 /* 286 * Are we a proxy writer or not? 287 */ 288 static int proxy_writer; 289 290 /* 291 * Silly little macros. 292 */ 293 #define STR(x) vstring_str(x) 294 #define VSTREQ(x,y) (strcmp(STR(x),y) == 0) 295 296 /* proxy_map_find - look up or open table */ 297 298 static DICT *proxy_map_find(const char *map_type_name, int request_flags, 299 int *statp) 300 { 301 DICT *dict; 302 303 #define PROXY_COLON DICT_TYPE_PROXY ":" 304 #define PROXY_COLON_LEN (sizeof(PROXY_COLON) - 1) 305 #define READ_OPEN_FLAGS O_RDONLY 306 #define WRITE_OPEN_FLAGS (O_RDWR | O_CREAT) 307 308 /* 309 * Canonicalize the map name. If the map is not on the approved list, 310 * deny the request. 311 */ 312 #define PROXY_MAP_FIND_ERROR_RETURN(x) { *statp = (x); return (0); } 313 314 while (strncmp(map_type_name, PROXY_COLON, PROXY_COLON_LEN) == 0) 315 map_type_name += PROXY_COLON_LEN; 316 /* XXX The following breaks with maps that have ':' in their name. */ 317 if (strchr(map_type_name, ':') == 0) 318 PROXY_MAP_FIND_ERROR_RETURN(PROXY_STAT_BAD); 319 if (htable_locate(proxy_auth_maps, map_type_name) == 0) { 320 msg_warn("request for unapproved table: \"%s\"", map_type_name); 321 msg_warn("to approve this table for %s access, list %s:%s in %s:%s", 322 proxy_writer == 0 ? "read-only" : "read-write", 323 DICT_TYPE_PROXY, map_type_name, MAIN_CONF_FILE, 324 proxy_writer == 0 ? VAR_PROXY_READ_MAPS : 325 VAR_PROXY_WRITE_MAPS); 326 PROXY_MAP_FIND_ERROR_RETURN(PROXY_STAT_DENY); 327 } 328 329 /* 330 * Open one instance of a map for each combination of name+flags. 331 * 332 * Assume that a map instance can be shared among clients with different 333 * paranoia flag settings and with different map lookup flag settings. 334 * 335 * XXX The open() flags are passed implicitly, via the selection of the 336 * service name. For a more sophisticated interface, appropriate subsets 337 * of open() flags should be received directly from the client. 338 */ 339 vstring_sprintf(map_type_name_flags, "%s:%s", map_type_name, 340 dict_flags_str(request_flags & DICT_FLAG_INST_MASK)); 341 if (msg_verbose) 342 msg_info("proxy_map_find: %s", STR(map_type_name_flags)); 343 if ((dict = dict_handle(STR(map_type_name_flags))) == 0) { 344 dict = dict_open(map_type_name, proxy_writer ? 345 WRITE_OPEN_FLAGS : READ_OPEN_FLAGS, 346 request_flags); 347 if (dict == 0) 348 msg_panic("proxy_map_find: dict_open null result"); 349 dict_register(STR(map_type_name_flags), dict); 350 } 351 dict->error = 0; 352 return (dict); 353 } 354 355 /* proxymap_sequence_service - remote sequence service */ 356 357 static void proxymap_sequence_service(VSTREAM *client_stream) 358 { 359 int request_flags; 360 DICT *dict; 361 int request_func; 362 const char *reply_key; 363 const char *reply_value; 364 int dict_status; 365 int reply_status; 366 367 /* 368 * Process the request. 369 */ 370 if (attr_scan(client_stream, ATTR_FLAG_STRICT, 371 RECV_ATTR_STR(MAIL_ATTR_TABLE, request_map), 372 RECV_ATTR_INT(MAIL_ATTR_FLAGS, &request_flags), 373 RECV_ATTR_INT(MAIL_ATTR_FUNC, &request_func), 374 ATTR_TYPE_END) != 3 375 || (request_func != DICT_SEQ_FUN_FIRST 376 && request_func != DICT_SEQ_FUN_NEXT)) { 377 reply_status = PROXY_STAT_BAD; 378 reply_key = reply_value = ""; 379 } else if ((dict = proxy_map_find(STR(request_map), request_flags, 380 &reply_status)) == 0) { 381 reply_key = reply_value = ""; 382 } else { 383 dict->flags = ((dict->flags & ~DICT_FLAG_RQST_MASK) 384 | (request_flags & DICT_FLAG_RQST_MASK)); 385 dict_status = dict_seq(dict, request_func, &reply_key, &reply_value); 386 if (dict_status == 0) { 387 reply_status = PROXY_STAT_OK; 388 } else if (dict->error == 0) { 389 reply_status = PROXY_STAT_NOKEY; 390 reply_key = reply_value = ""; 391 } else { 392 reply_status = (dict->error == DICT_ERR_RETRY ? 393 PROXY_STAT_RETRY : PROXY_STAT_CONFIG); 394 reply_key = reply_value = ""; 395 } 396 } 397 398 /* 399 * Respond to the client. 400 */ 401 attr_print(client_stream, ATTR_FLAG_NONE, 402 SEND_ATTR_INT(MAIL_ATTR_STATUS, reply_status), 403 SEND_ATTR_STR(MAIL_ATTR_KEY, reply_key), 404 SEND_ATTR_STR(MAIL_ATTR_VALUE, reply_value), 405 ATTR_TYPE_END); 406 } 407 408 /* proxymap_lookup_service - remote lookup service */ 409 410 static void proxymap_lookup_service(VSTREAM *client_stream) 411 { 412 int request_flags; 413 DICT *dict; 414 const char *reply_value; 415 int reply_status; 416 417 /* 418 * Process the request. 419 */ 420 if (attr_scan(client_stream, ATTR_FLAG_STRICT, 421 RECV_ATTR_STR(MAIL_ATTR_TABLE, request_map), 422 RECV_ATTR_INT(MAIL_ATTR_FLAGS, &request_flags), 423 RECV_ATTR_STR(MAIL_ATTR_KEY, request_key), 424 ATTR_TYPE_END) != 3) { 425 reply_status = PROXY_STAT_BAD; 426 reply_value = ""; 427 } else if ((dict = proxy_map_find(STR(request_map), request_flags, 428 &reply_status)) == 0) { 429 reply_value = ""; 430 } else if (dict->flags = ((dict->flags & ~DICT_FLAG_RQST_MASK) 431 | (request_flags & DICT_FLAG_RQST_MASK)), 432 (reply_value = dict_get(dict, STR(request_key))) != 0) { 433 reply_status = PROXY_STAT_OK; 434 } else if (dict->error == 0) { 435 reply_status = PROXY_STAT_NOKEY; 436 reply_value = ""; 437 } else { 438 reply_status = (dict->error == DICT_ERR_RETRY ? 439 PROXY_STAT_RETRY : PROXY_STAT_CONFIG); 440 reply_value = ""; 441 } 442 443 /* 444 * Respond to the client. 445 */ 446 attr_print(client_stream, ATTR_FLAG_NONE, 447 SEND_ATTR_INT(MAIL_ATTR_STATUS, reply_status), 448 SEND_ATTR_STR(MAIL_ATTR_VALUE, reply_value), 449 ATTR_TYPE_END); 450 } 451 452 /* proxymap_update_service - remote update service */ 453 454 static void proxymap_update_service(VSTREAM *client_stream) 455 { 456 int request_flags; 457 DICT *dict; 458 int dict_status; 459 int reply_status; 460 461 /* 462 * Process the request. 463 * 464 * XXX We don't close maps, so we must turn on synchronous update to ensure 465 * that the on-disk data is in a consistent state between updates. 466 * 467 * XXX We ignore duplicates, because the proxymap server would abort 468 * otherwise. 469 */ 470 if (attr_scan(client_stream, ATTR_FLAG_STRICT, 471 RECV_ATTR_STR(MAIL_ATTR_TABLE, request_map), 472 RECV_ATTR_INT(MAIL_ATTR_FLAGS, &request_flags), 473 RECV_ATTR_STR(MAIL_ATTR_KEY, request_key), 474 RECV_ATTR_STR(MAIL_ATTR_VALUE, request_value), 475 ATTR_TYPE_END) != 4) { 476 reply_status = PROXY_STAT_BAD; 477 } else if (proxy_writer == 0) { 478 msg_warn("refusing %s update request on non-%s service", 479 STR(request_map), MAIL_SERVICE_PROXYWRITE); 480 reply_status = PROXY_STAT_DENY; 481 } else if ((dict = proxy_map_find(STR(request_map), request_flags, 482 &reply_status)) == 0) { 483 /* void */ ; 484 } else { 485 dict->flags = ((dict->flags & ~DICT_FLAG_RQST_MASK) 486 | (request_flags & DICT_FLAG_RQST_MASK) 487 | DICT_FLAG_SYNC_UPDATE | DICT_FLAG_DUP_REPLACE); 488 dict_status = dict_put(dict, STR(request_key), STR(request_value)); 489 if (dict_status == 0) { 490 reply_status = PROXY_STAT_OK; 491 } else if (dict->error == 0) { 492 reply_status = PROXY_STAT_NOKEY; 493 } else { 494 reply_status = (dict->error == DICT_ERR_RETRY ? 495 PROXY_STAT_RETRY : PROXY_STAT_CONFIG); 496 } 497 } 498 499 /* 500 * Respond to the client. 501 */ 502 attr_print(client_stream, ATTR_FLAG_NONE, 503 SEND_ATTR_INT(MAIL_ATTR_STATUS, reply_status), 504 ATTR_TYPE_END); 505 } 506 507 /* proxymap_delete_service - remote delete service */ 508 509 static void proxymap_delete_service(VSTREAM *client_stream) 510 { 511 int request_flags; 512 DICT *dict; 513 int dict_status; 514 int reply_status; 515 516 /* 517 * Process the request. 518 * 519 * XXX We don't close maps, so we must turn on synchronous update to ensure 520 * that the on-disk data is in a consistent state between updates. 521 */ 522 if (attr_scan(client_stream, ATTR_FLAG_STRICT, 523 RECV_ATTR_STR(MAIL_ATTR_TABLE, request_map), 524 RECV_ATTR_INT(MAIL_ATTR_FLAGS, &request_flags), 525 RECV_ATTR_STR(MAIL_ATTR_KEY, request_key), 526 ATTR_TYPE_END) != 3) { 527 reply_status = PROXY_STAT_BAD; 528 } else if (proxy_writer == 0) { 529 msg_warn("refusing %s delete request on non-%s service", 530 STR(request_map), MAIL_SERVICE_PROXYWRITE); 531 reply_status = PROXY_STAT_DENY; 532 } else if ((dict = proxy_map_find(STR(request_map), request_flags, 533 &reply_status)) == 0) { 534 /* void */ ; 535 } else { 536 dict->flags = ((dict->flags & ~DICT_FLAG_RQST_MASK) 537 | (request_flags & DICT_FLAG_RQST_MASK) 538 | DICT_FLAG_SYNC_UPDATE); 539 dict_status = dict_del(dict, STR(request_key)); 540 if (dict_status == 0) { 541 reply_status = PROXY_STAT_OK; 542 } else if (dict->error == 0) { 543 reply_status = PROXY_STAT_NOKEY; 544 } else { 545 reply_status = (dict->error == DICT_ERR_RETRY ? 546 PROXY_STAT_RETRY : PROXY_STAT_CONFIG); 547 } 548 } 549 550 /* 551 * Respond to the client. 552 */ 553 attr_print(client_stream, ATTR_FLAG_NONE, 554 SEND_ATTR_INT(MAIL_ATTR_STATUS, reply_status), 555 ATTR_TYPE_END); 556 } 557 558 /* proxymap_open_service - open remote lookup table */ 559 560 static void proxymap_open_service(VSTREAM *client_stream) 561 { 562 int request_flags; 563 DICT *dict; 564 int reply_status; 565 int reply_flags; 566 567 /* 568 * Process the request. 569 */ 570 if (attr_scan(client_stream, ATTR_FLAG_STRICT, 571 RECV_ATTR_STR(MAIL_ATTR_TABLE, request_map), 572 RECV_ATTR_INT(MAIL_ATTR_FLAGS, &request_flags), 573 ATTR_TYPE_END) != 2) { 574 reply_status = PROXY_STAT_BAD; 575 reply_flags = 0; 576 } else if ((dict = proxy_map_find(STR(request_map), request_flags, 577 &reply_status)) == 0) { 578 reply_flags = 0; 579 } else { 580 reply_status = PROXY_STAT_OK; 581 reply_flags = dict->flags; 582 } 583 584 /* 585 * Respond to the client. 586 */ 587 attr_print(client_stream, ATTR_FLAG_NONE, 588 SEND_ATTR_INT(MAIL_ATTR_STATUS, reply_status), 589 SEND_ATTR_INT(MAIL_ATTR_FLAGS, reply_flags), 590 ATTR_TYPE_END); 591 } 592 593 /* proxymap_service - perform service for client */ 594 595 static void proxymap_service(VSTREAM *client_stream, char *unused_service, 596 char **argv) 597 { 598 599 /* 600 * Sanity check. This service takes no command-line arguments. 601 */ 602 if (argv[0]) 603 msg_fatal("unexpected command-line argument: %s", argv[0]); 604 605 /* 606 * Deadline enforcement. 607 */ 608 if (vstream_fstat(client_stream, VSTREAM_FLAG_DEADLINE) == 0) 609 vstream_control(client_stream, 610 CA_VSTREAM_CTL_TIMEOUT(1), 611 CA_VSTREAM_CTL_END); 612 613 /* 614 * This routine runs whenever a client connects to the socket dedicated 615 * to the proxymap service. All connection-management stuff is handled by 616 * the common code in multi_server.c. 617 */ 618 vstream_control(client_stream, 619 CA_VSTREAM_CTL_START_DEADLINE, 620 CA_VSTREAM_CTL_END); 621 if (attr_scan(client_stream, 622 ATTR_FLAG_MORE | ATTR_FLAG_STRICT, 623 RECV_ATTR_STR(MAIL_ATTR_REQ, request), 624 ATTR_TYPE_END) == 1) { 625 if (VSTREQ(request, PROXY_REQ_LOOKUP)) { 626 proxymap_lookup_service(client_stream); 627 } else if (VSTREQ(request, PROXY_REQ_UPDATE)) { 628 proxymap_update_service(client_stream); 629 } else if (VSTREQ(request, PROXY_REQ_DELETE)) { 630 proxymap_delete_service(client_stream); 631 } else if (VSTREQ(request, PROXY_REQ_SEQUENCE)) { 632 proxymap_sequence_service(client_stream); 633 } else if (VSTREQ(request, PROXY_REQ_OPEN)) { 634 proxymap_open_service(client_stream); 635 } else { 636 msg_warn("unrecognized request: \"%s\", ignored", STR(request)); 637 attr_print(client_stream, ATTR_FLAG_NONE, 638 SEND_ATTR_INT(MAIL_ATTR_STATUS, PROXY_STAT_BAD), 639 ATTR_TYPE_END); 640 } 641 } 642 vstream_control(client_stream, 643 CA_VSTREAM_CTL_START_DEADLINE, 644 CA_VSTREAM_CTL_END); 645 vstream_fflush(client_stream); 646 } 647 648 /* dict_proxy_open - intercept remote map request from inside library */ 649 650 DICT *dict_proxy_open(const char *map, int open_flags, int dict_flags) 651 { 652 if (msg_verbose) 653 msg_info("dict_proxy_open(%s, 0%o, 0%o) called from internal routine", 654 map, open_flags, dict_flags); 655 while (strncmp(map, PROXY_COLON, PROXY_COLON_LEN) == 0) 656 map += PROXY_COLON_LEN; 657 return (dict_open(map, open_flags, dict_flags)); 658 } 659 660 /* post_jail_init - initialization after privilege drop */ 661 662 static void post_jail_init(char *service_name, char **unused_argv) 663 { 664 const char *sep = CHARS_COMMA_SP; 665 const char *parens = CHARS_BRACE; 666 char *saved_filter; 667 char *bp; 668 char *type_name; 669 670 /* 671 * Are we proxy writer? 672 */ 673 if (strcmp(service_name, MAIL_SERVICE_PROXYWRITE) == 0) 674 proxy_writer = 1; 675 else if (strcmp(service_name, MAIL_SERVICE_PROXYMAP) != 0) 676 msg_fatal("service name must be one of %s or %s", 677 MAIL_SERVICE_PROXYMAP, MAIL_SERVICE_PROXYMAP); 678 679 /* 680 * Pre-allocate buffers. 681 */ 682 request = vstring_alloc(10); 683 request_map = vstring_alloc(10); 684 request_key = vstring_alloc(10); 685 request_value = vstring_alloc(10); 686 map_type_name_flags = vstring_alloc(10); 687 688 /* 689 * Prepare the pre-approved list of proxied tables. 690 */ 691 saved_filter = bp = mystrdup(proxy_writer ? var_proxy_write_maps : 692 var_proxy_read_maps); 693 proxy_auth_maps = htable_create(13); 694 while ((type_name = mystrtokq(&bp, sep, parens)) != 0) { 695 if (strncmp(type_name, PROXY_COLON, PROXY_COLON_LEN)) 696 continue; 697 do { 698 type_name += PROXY_COLON_LEN; 699 } while (!strncmp(type_name, PROXY_COLON, PROXY_COLON_LEN)); 700 if (strchr(type_name, ':') != 0 701 && htable_locate(proxy_auth_maps, type_name) == 0) 702 (void) htable_enter(proxy_auth_maps, type_name, (void *) 0); 703 } 704 myfree(saved_filter); 705 706 /* 707 * Never, ever, get killed by a master signal, as that could corrupt a 708 * persistent database when we're in the middle of an update. 709 */ 710 if (proxy_writer != 0) 711 setsid(); 712 } 713 714 /* pre_accept - see if tables have changed */ 715 716 static void pre_accept(char *unused_name, char **unused_argv) 717 { 718 const char *table; 719 720 if (proxy_writer == 0 && (table = dict_changed_name()) != 0) { 721 msg_info("table %s has changed -- restarting", table); 722 exit(0); 723 } 724 } 725 726 MAIL_VERSION_STAMP_DECLARE; 727 728 /* main - pass control to the multi-threaded skeleton */ 729 730 int main(int argc, char **argv) 731 { 732 static const CONFIG_STR_TABLE str_table[] = { 733 VAR_ALIAS_MAPS, DEF_ALIAS_MAPS, &var_alias_maps, 0, 0, 734 VAR_LOCAL_RCPT_MAPS, DEF_LOCAL_RCPT_MAPS, &var_local_rcpt_maps, 0, 0, 735 VAR_VIRT_ALIAS_MAPS, DEF_VIRT_ALIAS_MAPS, &var_virt_alias_maps, 0, 0, 736 VAR_VIRT_ALIAS_DOMS, DEF_VIRT_ALIAS_DOMS, &var_virt_alias_doms, 0, 0, 737 VAR_VIRT_MAILBOX_MAPS, DEF_VIRT_MAILBOX_MAPS, &var_virt_mailbox_maps, 0, 0, 738 VAR_VIRT_MAILBOX_DOMS, DEF_VIRT_MAILBOX_DOMS, &var_virt_mailbox_doms, 0, 0, 739 VAR_RELAY_RCPT_MAPS, DEF_RELAY_RCPT_MAPS, &var_relay_rcpt_maps, 0, 0, 740 VAR_RELAY_DOMAINS, DEF_RELAY_DOMAINS, &var_relay_domains, 0, 0, 741 VAR_CANONICAL_MAPS, DEF_CANONICAL_MAPS, &var_canonical_maps, 0, 0, 742 VAR_SEND_CANON_MAPS, DEF_SEND_CANON_MAPS, &var_send_canon_maps, 0, 0, 743 VAR_RCPT_CANON_MAPS, DEF_RCPT_CANON_MAPS, &var_rcpt_canon_maps, 0, 0, 744 VAR_RELOCATED_MAPS, DEF_RELOCATED_MAPS, &var_relocated_maps, 0, 0, 745 VAR_TRANSPORT_MAPS, DEF_TRANSPORT_MAPS, &var_transport_maps, 0, 0, 746 VAR_VERIFY_MAP, DEF_VERIFY_MAP, &var_verify_map, 0, 0, 747 VAR_SMTPD_SND_AUTH_MAPS, DEF_SMTPD_SND_AUTH_MAPS, &var_smtpd_snd_auth_maps, 0, 0, 748 VAR_PSC_CACHE_MAP, DEF_PSC_CACHE_MAP, &var_psc_cache_map, 0, 0, 749 /* The following two must be last for $mapname to work as expected. */ 750 VAR_PROXY_READ_MAPS, DEF_PROXY_READ_MAPS, &var_proxy_read_maps, 0, 0, 751 VAR_PROXY_WRITE_MAPS, DEF_PROXY_WRITE_MAPS, &var_proxy_write_maps, 0, 0, 752 0, 753 }; 754 755 /* 756 * Fingerprint executables and core dumps. 757 */ 758 MAIL_VERSION_STAMP_ALLOCATE; 759 760 multi_server_main(argc, argv, proxymap_service, 761 CA_MAIL_SERVER_STR_TABLE(str_table), 762 CA_MAIL_SERVER_POST_INIT(post_jail_init), 763 CA_MAIL_SERVER_PRE_ACCEPT(pre_accept), 764 /* XXX CA_MAIL_SERVER_SOLITARY if proxywrite */ 765 0); 766 } 767