SERVER_ACL *server_acl_parse(const char *extern_acl, const char *origin) { char *saved_acl = mystrdup(extern_acl); SERVER_ACL *intern_acl = argv_alloc(1); char *bp = saved_acl; char *acl; #define STREQ(x,y) (strcasecmp((x), (y)) == 0) #define STRNE(x,y) (strcasecmp((x), (y)) != 0) /* * Nested tables are not allowed. Tables are opened before entering the * chroot jail, while access lists are evaluated after entering the * chroot jail. */ while ((acl = mystrtok(&bp, SERVER_ACL_SEPARATORS)) != 0) { if (strchr(acl, ':') != 0) { if (strchr(origin, ':') != 0) { msg_warn("table %s: lookup result \"%s\" is not allowed" " -- ignoring remainder of access list", origin, acl); argv_add(intern_acl, SERVER_ACL_NAME_DUNNO, (char *) 0); break; } else { if (dict_handle(acl) == 0) dict_register(acl, dict_open(acl, O_RDONLY, DICT_FLAG_LOCK | DICT_FLAG_FOLD_FIX)); } } argv_add(intern_acl, acl, (char *) 0); } argv_terminate(intern_acl); /* * Cleanup. */ myfree(saved_acl); return (intern_acl); }
static void pre_init(char *unused_name, char **unused_argv) { int use_tls; static const NAME_CODE addr_pref_map[] = { INET_PROTO_NAME_IPV6, SMTP_MISC_FLAG_PREF_IPV6, INET_PROTO_NAME_IPV4, SMTP_MISC_FLAG_PREF_IPV4, INET_PROTO_NAME_ANY, 0, 0, -1, }; /* * Turn on per-peer debugging. */ debug_peer_init(); /* * SASL initialization. */ if (var_smtp_sasl_enable) #ifdef USE_SASL_AUTH smtp_sasl_initialize(); #else msg_warn("%s is true, but SASL support is not compiled in", VAR_SMTP_SASL_ENABLE); #endif if (*var_smtp_tls_level != 0) switch (tls_level_lookup(var_smtp_tls_level)) { case TLS_LEV_SECURE: case TLS_LEV_VERIFY: case TLS_LEV_FPRINT: case TLS_LEV_ENCRYPT: var_smtp_use_tls = var_smtp_enforce_tls = 1; break; case TLS_LEV_MAY: var_smtp_use_tls = 1; var_smtp_enforce_tls = 0; break; case TLS_LEV_NONE: var_smtp_use_tls = var_smtp_enforce_tls = 0; break; default: /* tls_level_lookup() logs no warning. */ /* session_tls_init() assumes that var_smtp_tls_level is sane. */ msg_fatal("Invalid TLS level \"%s\"", var_smtp_tls_level); } use_tls = (var_smtp_use_tls || var_smtp_enforce_tls); /* * Initialize the TLS data before entering the chroot jail */ if (use_tls || var_smtp_tls_per_site[0] || var_smtp_tls_policy[0]) { #ifdef USE_TLS TLS_CLIENT_INIT_PROPS props; int using_smtp = (strcmp(var_procname, "smtp") == 0); /* * We get stronger type safety and a cleaner interface by combining * the various parameters into a single tls_client_props structure. * * Large parameter lists are error-prone, so we emulate a language * feature that C does not have natively: named parameter lists. */ smtp_tls_ctx = TLS_CLIENT_INIT(&props, log_param = using_smtp ? VAR_SMTP_TLS_LOGLEVEL : VAR_LMTP_TLS_LOGLEVEL, log_level = var_smtp_tls_loglevel, verifydepth = var_smtp_tls_scert_vd, cache_type = using_smtp ? TLS_MGR_SCACHE_SMTP : TLS_MGR_SCACHE_LMTP, cert_file = var_smtp_tls_cert_file, key_file = var_smtp_tls_key_file, dcert_file = var_smtp_tls_dcert_file, dkey_file = var_smtp_tls_dkey_file, eccert_file = var_smtp_tls_eccert_file, eckey_file = var_smtp_tls_eckey_file, CAfile = var_smtp_tls_CAfile, CApath = var_smtp_tls_CApath, fpt_dgst = var_smtp_tls_fpt_dgst); smtp_tls_list_init(); #else msg_warn("TLS has been selected, but TLS support is not compiled in"); #endif } /* * Flush client. */ flush_init(); /* * Session cache domain list. */ if (*var_smtp_cache_dest) smtp_cache_dest = string_list_init(MATCH_FLAG_RETURN, var_smtp_cache_dest); /* * EHLO keyword filter. */ if (*var_smtp_ehlo_dis_maps) smtp_ehlo_dis_maps = maps_create(VAR_SMTP_EHLO_DIS_MAPS, var_smtp_ehlo_dis_maps, DICT_FLAG_LOCK); /* * PIX bug workarounds. */ if (*var_smtp_pix_bug_maps) smtp_pix_bug_maps = maps_create(VAR_SMTP_PIX_BUG_MAPS, var_smtp_pix_bug_maps, DICT_FLAG_LOCK); /* * Generic maps. */ if (*var_prop_extension) smtp_ext_prop_mask = ext_prop_mask(VAR_PROP_EXTENSION, var_prop_extension); if (*var_smtp_generic_maps) smtp_generic_maps = maps_create(VAR_SMTP_GENERIC_MAPS, var_smtp_generic_maps, DICT_FLAG_LOCK | DICT_FLAG_FOLD_FIX); /* * Header/body checks. */ smtp_header_checks = hbc_header_checks_create( VAR_SMTP_HEAD_CHKS, var_smtp_head_chks, VAR_SMTP_MIME_CHKS, var_smtp_mime_chks, VAR_SMTP_NEST_CHKS, var_smtp_nest_chks, smtp_hbc_callbacks); smtp_body_checks = hbc_body_checks_create( VAR_SMTP_BODY_CHKS, var_smtp_body_chks, smtp_hbc_callbacks); /* * Server reply filter. */ if (*var_smtp_resp_filter) smtp_chat_resp_filter = dict_open(var_smtp_resp_filter, O_RDONLY, DICT_FLAG_LOCK | DICT_FLAG_FOLD_FIX); /* * Address family preference. */ if (*var_smtp_addr_pref) { smtp_addr_pref = name_code(addr_pref_map, NAME_CODE_FLAG_NONE, var_smtp_addr_pref); if (smtp_addr_pref < 0) msg_fatal("bad %s value: %s", VAR_SMTP_ADDR_PREF, var_smtp_addr_pref); } }
void dict_test(int argc, char **argv) { VSTRING *keybuf = vstring_alloc(1); VSTRING *inbuf = vstring_alloc(1); DICT *dict; char *dict_name; int open_flags; char *bufp; char *cmd; const char *key; const char *value; int ch; int dict_flags = 0; int n; int rc; #define USAGE "verbose|del key|get key|put key=value|first|next|masks|flags" signal(SIGPIPE, SIG_IGN); msg_vstream_init(argv[0], VSTREAM_ERR); while ((ch = GETOPT(argc, argv, "v")) > 0) { switch (ch) { default: usage(argv[0]); case 'v': msg_verbose++; break; } } optind = OPTIND; if (argc - optind < 2) usage(argv[0]); if (strcasecmp(argv[optind + 1], "create") == 0) open_flags = O_CREAT | O_RDWR | O_TRUNC; else if (strcasecmp(argv[optind + 1], "write") == 0) open_flags = O_RDWR; else if (strcasecmp(argv[optind + 1], "read") == 0) open_flags = O_RDONLY; else msg_fatal("unknown access mode: %s", argv[2]); for (n = 2; argv[optind + n]; n++) dict_flags |= dict_flags_mask(argv[optind + 2]); if ((dict_flags & DICT_FLAG_OPEN_LOCK) == 0) dict_flags |= DICT_FLAG_LOCK; if ((dict_flags & (DICT_FLAG_DUP_WARN | DICT_FLAG_DUP_IGNORE)) == 0) dict_flags |= DICT_FLAG_DUP_REPLACE; vstream_fflush(VSTREAM_OUT); dict_name = argv[optind]; dict_allow_surrogate = 1; dict = dict_open(dict_name, open_flags, dict_flags); dict_register(dict_name, dict); while (vstring_fgets_nonl(inbuf, VSTREAM_IN)) { bufp = vstring_str(inbuf); if (!isatty(0)) { vstream_printf("> %s\n", bufp); vstream_fflush(VSTREAM_OUT); } if (*bufp == '#') continue; if ((cmd = mystrtok(&bufp, " ")) == 0) { vstream_printf("usage: %s\n", USAGE); vstream_fflush(VSTREAM_OUT); continue; } if (dict_changed_name()) msg_warn("dictionary has changed"); key = *bufp ? vstring_str(unescape(keybuf, mystrtok(&bufp, " ="))) : 0; value = mystrtok(&bufp, " ="); if (strcmp(cmd, "verbose") == 0 && !key) { msg_verbose++; } else if (strcmp(cmd, "del") == 0 && key && !value) { if ((rc = dict_del(dict, key)) > 0) vstream_printf("%s: not found\n", key); else if (rc < 0) vstream_printf("%s: error\n", key); else vstream_printf("%s: deleted\n", key); } else if (strcmp(cmd, "get") == 0 && key && !value) { if ((value = dict_get(dict, key)) == 0) { vstream_printf("%s: %s\n", key, dict->error ? "error" : "not found"); } else { vstream_printf("%s=%s\n", key, value); } } else if (strcmp(cmd, "put") == 0 && key && value) { if (dict_put(dict, key, value) != 0) vstream_printf("%s: %s\n", key, dict->error ? "error" : "not updated"); else vstream_printf("%s=%s\n", key, value); } else if (strcmp(cmd, "first") == 0 && !key && !value) { if (dict_seq(dict, DICT_SEQ_FUN_FIRST, &key, &value) == 0) vstream_printf("%s=%s\n", key, value); else vstream_printf("%s\n", dict->error ? "error" : "not found"); } else if (strcmp(cmd, "next") == 0 && !key && !value) { if (dict_seq(dict, DICT_SEQ_FUN_NEXT, &key, &value) == 0) vstream_printf("%s=%s\n", key, value); else vstream_printf("%s\n", dict->error ? "error" : "not found"); } else if (strcmp(cmd, "flags") == 0 && !key && !value) { vstream_printf("dict flags %s\n", dict_flags_str(dict->flags)); } else if (strcmp(cmd, "masks") == 0 && !key && !value) { vstream_printf("DICT_FLAG_IMPL_MASK %s\n", dict_flags_str(DICT_FLAG_IMPL_MASK)); vstream_printf("DICT_FLAG_PARANOID %s\n", dict_flags_str(DICT_FLAG_PARANOID)); vstream_printf("DICT_FLAG_RQST_MASK %s\n", dict_flags_str(DICT_FLAG_RQST_MASK)); vstream_printf("DICT_FLAG_INST_MASK %s\n", dict_flags_str(DICT_FLAG_INST_MASK)); } else { vstream_printf("usage: %s\n", USAGE); } vstream_fflush(VSTREAM_OUT); } vstring_free(keybuf); vstring_free(inbuf); dict_close(dict); }
int main(int argc, char **argv) { VSTRING *keybuf = vstring_alloc(1); VSTRING *inbuf = vstring_alloc(1); DICT *dict; char *dict_name; int open_flags; char *bufp; char *cmd; const char *key; const char *value; int ch; int dict_flags = DICT_FLAG_LOCK | DICT_FLAG_DUP_REPLACE; int n; signal(SIGPIPE, SIG_IGN); msg_vstream_init(argv[0], VSTREAM_ERR); while ((ch = GETOPT(argc, argv, "v")) > 0) { switch (ch) { default: usage(argv[0]); case 'v': msg_verbose++; break; } } optind = OPTIND; if (argc - optind < 2) usage(argv[0]); if (strcasecmp(argv[optind + 1], "create") == 0) open_flags = O_CREAT | O_RDWR | O_TRUNC; else if (strcasecmp(argv[optind + 1], "write") == 0) open_flags = O_RDWR; else if (strcasecmp(argv[optind + 1], "read") == 0) open_flags = O_RDONLY; else msg_fatal("unknown access mode: %s", argv[2]); for (n = 2; argv[optind + n]; n++) { if (strcasecmp(argv[optind + 2], "fold") == 0) dict_flags |= DICT_FLAG_FOLD_ANY; else if (strcasecmp(argv[optind + 2], "sync") == 0) dict_flags |= DICT_FLAG_SYNC_UPDATE; else usage(argv[0]); } dict_name = argv[optind]; dict = dict_open(dict_name, open_flags, dict_flags); dict_register(dict_name, dict); while (vstring_fgets_nonl(inbuf, VSTREAM_IN)) { bufp = vstring_str(inbuf); if (!isatty(0)) { vstream_printf("> %s\n", bufp); vstream_fflush(VSTREAM_OUT); } if (*bufp == '#') continue; if ((cmd = mystrtok(&bufp, " ")) == 0) { vstream_printf("usage: del key|get key|put key=value|first|next\n"); vstream_fflush(VSTREAM_OUT); continue; } if (dict_changed_name()) msg_warn("dictionary has changed"); key = *bufp ? vstring_str(unescape(keybuf, mystrtok(&bufp, " ="))) : 0; value = mystrtok(&bufp, " ="); if (strcmp(cmd, "del") == 0 && key && !value) { if (dict_del(dict, key)) vstream_printf("%s: not found\n", key); else vstream_printf("%s: deleted\n", key); } else if (strcmp(cmd, "get") == 0 && key && !value) { if ((value = dict_get(dict, key)) == 0) { vstream_printf("%s: %s\n", key, dict_errno == DICT_ERR_RETRY ? "soft error" : "not found"); } else { vstream_printf("%s=%s\n", key, value); } } else if (strcmp(cmd, "put") == 0 && key && value) { dict_put(dict, key, value); vstream_printf("%s=%s\n", key, value); } else if (strcmp(cmd, "first") == 0 && !key && !value) { if (dict_seq(dict, DICT_SEQ_FUN_FIRST, &key, &value) == 0) vstream_printf("%s=%s\n", key, value); else vstream_printf("%s\n", dict_errno == DICT_ERR_RETRY ? "soft error" : "not found"); } else if (strcmp(cmd, "next") == 0 && !key && !value) { if (dict_seq(dict, DICT_SEQ_FUN_NEXT, &key, &value) == 0) vstream_printf("%s=%s\n", key, value); else vstream_printf("%s\n", dict_errno == DICT_ERR_RETRY ? "soft error" : "not found"); } else { vstream_printf("usage: del key|get key|put key=value|first|next\n"); } vstream_fflush(VSTREAM_OUT); } vstring_free(keybuf); vstring_free(inbuf); dict_close(dict); return (0); }
int main(int argc, char** argv) { int c; char* p; uid_t uid, gid, client_gid, root_gid; struct passwd* pw; struct group* gr; struct stat st; int localsocket = 1; /* * compilerwarnung: "client_gid may be used uninitialized" * sowas will man ja nun wirklich nicht ... */ uid = gid = client_gid = root_gid = 0; while ((c = getopt(argc, argv, "bc:d:hfg:k:m:n:s:t:u:vx")) > 0) { switch (c) { case 'b': /* break contentheader */ logmsg(LOG_INFO, "option -b is ignored for compatibily reasons, you may remove it safely"); break; case 'c': /* clientgroup */ opt_clientgroup = optarg; if ((gr = getgrnam(opt_clientgroup)) == NULL) { logmsg(LOG_ERR, "unknown clientgroup: getgrnam(%s) failed", opt_group); exit(EX_DATAERR); } client_gid = gr->gr_gid; break; case 'd': /* Loglevel */ opt_loglevel = (int) strtoul(optarg, &p, 10); if (p != NULL && *p != '\0') { printf("debug-level is not valid integer: %s\n", optarg); exit(EX_DATAERR); } p = NULL; if (opt_loglevel < 0 || opt_loglevel > 7) { printf("loglevel out of range 0..7: %i\n", opt_loglevel); exit(EX_DATAERR); } break; case 'f': /* signer from header, not from envelope */ opt_signerfromheader = 1; break; case 'g': /* group */ opt_group = optarg; if ((gr = getgrnam(opt_group)) == NULL) { printf("unknown group: getgrnam(%s) failed", opt_group); exit(EX_DATAERR); } break; case 'k': /* keepdir */ opt_keepdir = optarg; if (stat(opt_keepdir, &st) < 0) { printf("directory to keep data: %s: %s", opt_keepdir, strerror(errno)); exit(EX_DATAERR); } if (!S_ISDIR(st.st_mode)) { printf("directory to keep data: %s is not a directory", opt_keepdir); exit(EX_DATAERR); } /* Zugriffsrechte werden spaeter geprueft, wenn zur richtigen uid gewechselt wurde */ break; case '?': /* help */ case 'h': usage(); exit(EX_OK); case 'm': /* Signingtable cdbfilename */ opt_signingtable = optarg; break; case 'n': /* Modetable cdbfilename */ opt_modetable = optarg; break; case 's': /* Miltersocket */ opt_miltersocket = optarg; break; case 't': /* Timeout */ opt_timeout = (int) strtoul(optarg, &p, 10); if (p != NULL && *p != '\0') { printf("timeout is not valid integer: %s\n", optarg); exit(EX_DATAERR); } p = NULL; if (opt_timeout < 0 ) { printf("negative milter connection timeout: %i\n", opt_timeout); exit(EX_DATAERR); } break; case 'u': /* user */ opt_user = optarg; /* get passwd/group entries for opt_user and opt_group */ if ((pw = getpwnam(opt_user)) == NULL) { logmsg(LOG_ERR, "unknown user: getpwnam(%s) failed", opt_user); exit(EX_DATAERR); } break; case 'v': /* Version */ version(); exit(EX_OK); case 'x': /* add X-Header */ opt_addxheader = (int) !opt_addxheader; break; default: usage(); exit(EX_USAGE); } } /* open syslog an say helo */ openlog(STR_PROGNAME, LOG_PID, LOG_MAIL); logmsg(LOG_NOTICE, "starting %s %s listening on %s, loglevel %i", STR_PROGNAME, STR_PROGVERSION, opt_miltersocket, opt_loglevel); /* force a new processgroup */ if ((setsid() == -1)) logmsg(LOG_DEBUG, "ignoring that setsid() failed"); if (opt_timeout > 0 && smfi_settimeout(opt_timeout) != MI_SUCCESS) { logmsg(LOG_ERR, "could not set milter timeout"); exit(EX_SOFTWARE); } logmsg(LOG_INFO, "miltertimeout set to %i", opt_timeout); if (smfi_setconn(opt_miltersocket) != MI_SUCCESS) { logmsg(LOG_ERR, "could not set milter socket"); exit(EX_SOFTWARE); } if (smfi_register(callbacks) != MI_SUCCESS) { logmsg(LOG_ERR, "could not register milter"); exit(EX_SOFTWARE); } /* * User- und Gruppennamen stehen nun fest. Testen, ob es diese gibt * und uid / gid ermitteln */ if ((pw = getpwnam(opt_user)) == NULL) { logmsg(LOG_ERR, "unknown user: getpwnam(%s) failed", opt_user); exit(EX_DATAERR); } uid = pw->pw_uid; if ((gr = getgrnam(opt_group)) == NULL) { logmsg(LOG_ERR, "unknown group: getgrnam(%s) failed", opt_group); exit(EX_SOFTWARE); } gid = gr->gr_gid; /* wenn nicht als Parameter angegeben, gehört ein Unix-Socket erstmal der gleichen Gruppe */ if (opt_clientgroup == NULL) client_gid = gid; /* wenn inet in optarg gefunden wird *und* das auch noch direkt am Anfang * dann ist's kein lokaler socket */ if (((p = strstr(opt_miltersocket, "inet")) != NULL) && opt_miltersocket == p) localsocket = 0; if (localsocket == 1) { /* den Socket oeffnen */ if (smfi_opensocket(REMOVE_EXISTING_SOCKETS) != MI_SUCCESS) { logmsg(LOG_ERR, "could not open milter socket %s", opt_miltersocket); exit(EX_SOFTWARE); } /* testen, ob's den Socket nun gibt */ p = opt_miltersocket + strlen("local:"); if (stat(p, &st) < 0) { p = opt_miltersocket + strlen("unix:"); if (stat(p, &st) < 0) { logmsg(LOG_ERR, "miltersocket does not exist: %m", strerror(errno)); exit(EX_DATAERR); } } /* gid der Gruppe root */ if ((gr = getgrnam("root")) == NULL) { logmsg(LOG_ERR, "unknown rootgroup: getgrnam(root) failed"); exit(EX_SOFTWARE); } root_gid = gr->gr_gid; /* clientgroup muss != root und != opt_group sein */ if ((client_gid == gid) || (client_gid == root_gid)) { logmsg(LOG_ERR, "clientgroup %s must be neither %s nor %s", opt_clientgroup, "root", opt_group); exit(EX_DATAERR); } /* nun die Rechte setzen */ if (chown(p, uid, client_gid) != 0) { logmsg(LOG_ERR, "chown(%s, %i, %i) failed: %m", p, uid, client_gid, strerror(errno)); exit(EX_SOFTWARE); } if (chmod(p, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP) != 0) { logmsg(LOG_ERR, "chmod(%s, 0660) failed: %m", p, strerror(errno)); exit(EX_SOFTWARE); } logmsg(LOG_INFO, "changed socket %s to owner/group: %i/%i, mode: 0660", opt_miltersocket, uid, client_gid); } /* gid/uid setzen */ if (setgid(gid) != 0) { logmsg(LOG_ERR, "setgid(%i) failed: %s", gr->gr_gid, strerror(errno)); exit(EX_SOFTWARE); } if (setuid(uid) != 0) { logmsg(LOG_ERR, "setuid(%i) failed: %s", pw->pw_uid, strerror(errno)); exit(EX_SOFTWARE); } /* aktuelle uid/gid pruefen und loggen */ uid = getuid(); gid = getgid(); if (uid == 0 || gid == 0) { logmsg(LOG_ERR, "too much priveleges, %s will not start under root", STR_PROGNAME); exit(EX_DATAERR); } logmsg(LOG_INFO, "running as uid: %i, gid: %i", (int) uid, (int) gid); if (opt_keepdir != NULL) { if (S_IRWXO & st.st_mode) { logmsg(LOG_ERR, "directory to keep data: %s: permissions too open: remove any access for other", opt_keepdir); exit(EX_DATAERR); } if (access(opt_keepdir, R_OK) < 0 && errno == EACCES) { logmsg(LOG_ERR, "directory to keep data: %s: permissions too strong: no read access", opt_keepdir); exit(EX_DATAERR); } if (access(opt_keepdir, W_OK) < 0 && errno == EACCES) { logmsg(LOG_ERR, "directory to keep data: %s: permissions too strong: no write access", opt_keepdir); exit(EX_DATAERR); } if (access(opt_keepdir, X_OK) < 0 && errno == EACCES) { logmsg(LOG_ERR, "directory to keep data: %s: permissions too strong: no execute access", opt_keepdir); exit(EX_DATAERR); } logmsg(LOG_INFO, "directory to keep data: %s", opt_keepdir); } dict_open(opt_signingtable, &dict_signingtable); if (opt_modetable) dict_open(opt_modetable, &dict_modetable); /* initialize OpenSSL */ SSL_library_init(); OpenSSL_add_all_algorithms(); /* get meaningful error messages */ SSL_load_error_strings(); ERR_load_crypto_strings(); /* Statistik initialisieren */ init_stats(); /* Signal-Handler fuer SIGALRM */ signal(SIGALRM, sig_handler); /* Run milter */ if ((c = smfi_main()) != MI_SUCCESS) logmsg(LOG_ERR, "Milter startup failed"); else logmsg(LOG_NOTICE, "stopping %s %s listening on %s", STR_PROGNAME, STR_PROGVERSION, opt_miltersocket); dict_close(&dict_signingtable); if (opt_modetable) dict_close(&dict_modetable); /* cleanup OpenSSL */ ERR_free_strings(); EVP_cleanup(); output_stats(); #ifdef DMALLOC dmalloc_log_stats(); dmalloc_log_unfreed(); dmalloc_shutdown(); #endif exit(c); }
static void pre_jail_init(char *unused_name, char **unused_argv) { VSTRING *redirect; /* * Open read-only maps before dropping privilege, for consistency with * other Postfix daemons. */ psc_acl_pre_jail_init(var_mynetworks, VAR_PSC_ACL); if (*var_psc_acl) psc_acl = psc_acl_parse(var_psc_acl, VAR_PSC_ACL); /* Ignore smtpd_forbid_cmds lookup errors. Non-critical feature. */ if (*var_psc_forbid_cmds) psc_forbid_cmds = string_list_init(MATCH_FLAG_RETURN, var_psc_forbid_cmds); if (*var_psc_dnsbl_reply) psc_dnsbl_reply = dict_open(var_psc_dnsbl_reply, O_RDONLY, DICT_FLAG_DUP_WARN); /* * Never, ever, get killed by a master signal, as that would corrupt the * database when we're in the middle of an update. */ if (setsid() < 0) msg_warn("setsid: %m"); /* * Security: don't create root-owned files that contain untrusted data. * And don't create Postfix-owned files in root-owned directories, * either. We want a correct relationship between (file or directory) * ownership and (file or directory) content. To open files before going * to jail, temporarily drop root privileges. */ SAVE_AND_SET_EUGID(var_owner_uid, var_owner_gid); redirect = vstring_alloc(100); /* * Keep state in persistent external map. As a safety measure we sync the * database on each update. This hurts on LINUX file systems that sync * all dirty disk blocks whenever any application invokes fsync(). * * Start the cache maintenance pseudo thread after dropping privileges. */ #define PSC_DICT_OPEN_FLAGS (DICT_FLAG_DUP_REPLACE | DICT_FLAG_SYNC_UPDATE | \ DICT_FLAG_OPEN_LOCK) if (*var_psc_cache_map) psc_cache_map = dict_cache_open(data_redirect_map(redirect, var_psc_cache_map), O_CREAT | O_RDWR, PSC_DICT_OPEN_FLAGS); /* * Clean up and restore privilege. */ vstring_free(redirect); RESTORE_SAVED_EUGID(); /* * Initialize the dummy SMTP engine. */ psc_smtpd_pre_jail_init(); }
static ARGV *match_list_parse(ARGV *list, char *string, int init_match) { const char *myname = "match_list_parse"; VSTRING *buf = vstring_alloc(10); VSTREAM *fp; const char *delim = " ,\t\r\n"; char *bp = string; char *start; char *item; char *map_type_name_flags; int match; #define OPEN_FLAGS O_RDONLY #define DICT_FLAGS (DICT_FLAG_LOCK | DICT_FLAG_FOLD_FIX) #define STR(x) vstring_str(x) /* * /filename contents are expanded in-line. To support !/filename we * prepend the negation operator to each item from the file. */ while ((start = mystrtokq(&bp, delim, "{}")) != 0) { if (*start == '#') { msg_warn("%s: comment at end of line is not supported: %s %s", myname, start, bp); break; } for (match = init_match, item = start; *item == '!'; item++) match = !match; if (*item == 0) msg_fatal("%s: no pattern after '!'", myname); if (*item == '/') { /* /file/name */ if ((fp = vstream_fopen(item, O_RDONLY, 0)) == 0) { vstring_sprintf(buf, "%s:%s", DICT_TYPE_NOFILE, item); /* XXX Should increment existing map refcount. */ if (dict_handle(STR(buf)) == 0) dict_register(STR(buf), dict_surrogate(DICT_TYPE_NOFILE, item, OPEN_FLAGS, DICT_FLAGS, "open file %s: %m", item)); argv_add(list, STR(buf), (char *) 0); } else { while (vstring_fgets(buf, fp)) if (vstring_str(buf)[0] != '#') list = match_list_parse(list, vstring_str(buf), match); if (vstream_fclose(fp)) msg_fatal("%s: read file %s: %m", myname, item); } } else if (MATCH_DICTIONARY(item)) { /* type:table */ vstring_sprintf(buf, "%s%s(%o,%s)", match ? "" : "!", item, OPEN_FLAGS, dict_flags_str(DICT_FLAGS)); map_type_name_flags = STR(buf) + (match == 0); /* XXX Should increment existing map refcount. */ if (dict_handle(map_type_name_flags) == 0) dict_register(map_type_name_flags, dict_open(item, OPEN_FLAGS, DICT_FLAGS)); argv_add(list, STR(buf), (char *) 0); } else { /* other pattern */ argv_add(list, match ? item : STR(vstring_sprintf(buf, "!%s", item)), (char *) 0); } } vstring_free(buf); return (list); }
DICT *dict_union_open(const char *name, int open_flags, int dict_flags) { static const char myname[] = "dict_union_open"; DICT_UNION *dict_union; char *saved_name = 0; char *dict_type_name; ARGV *argv = 0; char **cpp; DICT *dict; int match_flags = 0; struct DICT_OWNER aggr_owner; size_t len; /* * Clarity first. Let the optimizer worry about redundant code. */ #define DICT_UNION_RETURN(x) do { \ if (saved_name != 0) \ myfree(saved_name); \ if (argv != 0) \ argv_free(argv); \ return (x); \ } while (0) /* * Sanity checks. */ if (open_flags != O_RDONLY) DICT_UNION_RETURN(dict_surrogate(DICT_TYPE_UNION, name, open_flags, dict_flags, "%s:%s map requires O_RDONLY access mode", DICT_TYPE_UNION, name)); /* * Split the table name into its constituent parts. */ if ((len = balpar(name, CHARS_BRACE)) == 0 || name[len] != 0 || *(saved_name = mystrndup(name + 1, len - 2)) == 0 || ((argv = argv_splitq(saved_name, CHARS_COMMA_SP, CHARS_BRACE)), (argv->argc == 0))) DICT_UNION_RETURN(dict_surrogate(DICT_TYPE_UNION, name, open_flags, dict_flags, "bad syntax: \"%s:%s\"; " "need \"%s:{type:name...}\"", DICT_TYPE_UNION, name, DICT_TYPE_UNION)); /* * The least-trusted table in the set determines the over-all trust * level. The first table determines the pattern-matching flags. */ DICT_OWNER_AGGREGATE_INIT(aggr_owner); for (cpp = argv->argv; (dict_type_name = *cpp) != 0; cpp++) { if (msg_verbose) msg_info("%s: %s", myname, dict_type_name); if (strchr(dict_type_name, ':') == 0) DICT_UNION_RETURN(dict_surrogate(DICT_TYPE_UNION, name, open_flags, dict_flags, "bad syntax: \"%s:%s\"; " "need \"%s:{type:name...}\"", DICT_TYPE_UNION, name, DICT_TYPE_UNION)); if ((dict = dict_handle(dict_type_name)) == 0) dict = dict_open(dict_type_name, open_flags, dict_flags); dict_register(dict_type_name, dict); DICT_OWNER_AGGREGATE_UPDATE(aggr_owner, dict->owner); if (cpp == argv->argv) match_flags = dict->flags & (DICT_FLAG_FIXED | DICT_FLAG_PATTERN); } /* * Bundle up the result. */ dict_union = (DICT_UNION *) dict_alloc(DICT_TYPE_UNION, name, sizeof(*dict_union)); dict_union->dict.lookup = dict_union_lookup; dict_union->dict.close = dict_union_close; dict_union->dict.flags = dict_flags | match_flags; dict_union->dict.owner = aggr_owner; dict_union->re_buf = vstring_alloc(100); dict_union->map_union = argv; argv = 0; DICT_UNION_RETURN(DICT_DEBUG (&dict_union->dict)); }