int runcmd(bool force)
{
	char **argv;
	static char *envp[] = {
		"HOME=/",
		"PATH=/sbin:/bin:/usr/sbin:/usr/bin",
		NULL
	};
	int ret;

	argv = argv_split(GFP_KERNEL, restart_cmd, NULL);
	if (argv) {
		ret = call_usermodehelper(argv[0], argv, envp, UMH_WAIT_EXEC);
		argv_free(argv);
	} else {
		printk(KERN_WARNING "ak47 %s failed to allocate memory for \"%s\"\n",
					 __func__, restart_cmd);
		ret = -ENOMEM;
	}

	if (ret && force) {
		printk(KERN_WARNING "ak47 Failed to start \n ");
	}
printk("AK47 in  kthread runcmd exit\n");
	return ret;
}
Exemple #2
0
static void
erlang_parse_config(DICT_ERLANG *dict_erlang, const char *erlangcf)
{
    CFG_PARSER *p;
    char *nodes;

    p = dict_erlang->parser = cfg_parser_alloc(erlangcf);

    nodes = cfg_get_str(p, "nodes", "", 0, 0);
    dict_erlang->nodes = argv_split(nodes, " ,\t\r\n");
    myfree(nodes);

    dict_erlang->cookie = cfg_get_str(p, "cookie", "", 1, 0);
    dict_erlang->mod = cfg_get_str(p, "module", "", 1, 0);
    dict_erlang->fun = cfg_get_str(p, "function", "", 1, 0);

    dict_erlang->ctx = NULL;
    db_common_parse(&dict_erlang->dict, &dict_erlang->ctx, "%s", 1);
    db_common_parse_domain(p, dict_erlang->ctx);

    if (db_common_dict_partial(dict_erlang->ctx))
        dict_erlang->dict.flags |= DICT_FLAG_PATTERN;
    else
        dict_erlang->dict.flags |= DICT_FLAG_FIXED;
    if (dict_erlang->dict.flags & DICT_FLAG_FOLD_FIX)
        dict_erlang->dict.fold_buf = vstring_alloc(10);
}
static ssize_t lcd_reg_store(struct device *dev, struct device_attribute *attr, 
                                        const char *buf, size_t count)
{
    int argc; 
    char **args;
	int r,val;

    struct spi_device *spi = to_spi_device(dev);
 
    args = argv_split(GFP_KERNEL, buf, &argc);

    if (args == NULL) {
        dev_err(dev, "error getting arguments\n");
		return count;
    }

    if (argc==2) {
		r=simple_strtoul(*args, NULL, 0);
		args++;
		val=simple_strtoul(*args, NULL, 0);
		printk("set lcd panel spi reg %x = %x\n",r,val);
		spi_send(spi, r, val);
    }
	argv_free(args);

    return count;
}
static int __orderly_poweroff(bool force)
{
	char **argv;
	static char *envp[] = {
		"HOME=/",
		"PATH=/sbin:/bin:/usr/sbin:/usr/bin",
		NULL
	};
	int ret;

	argv = argv_split(GFP_KERNEL, poweroff_cmd, NULL);
	if (argv) {
		ret = call_usermodehelper(argv[0], argv, envp, UMH_WAIT_EXEC);
		argv_free(argv);
	} else {
		ret = -ENOMEM;
	}

	if (ret && force) {
		pr_warn("Failed to start orderly shutdown: forcing the issue\n");
		/*
		 * I guess this should try to kick off some daemon to sync and
		 * poweroff asap.  Or not even bother syncing if we're doing an
		 * emergency shutdown?
		 */
		emergency_sync();
		kernel_power_off();
	}

	return ret;
}
Exemple #5
0
static const char *parse_master_line(PC_MASTER_ENT *masterp, const char *buf)
{
    ARGV   *argv;

    /*
     * We can't use the master daemon's master_ent routines in their current
     * form. They convert everything to internal form, and they skip disabled
     * services.
     * 
     * The postconf command needs to show default fields as "-", and needs to
     * know about all service names so that it can generate service-dependent
     * parameter names (transport-dependent etc.).
     */
#define MASTER_BLANKS	" \t\r\n"		/* XXX */

    argv = argv_split(buf, MASTER_BLANKS);
    if (argv->argc < PC_MASTER_MIN_FIELDS) {
	argv_free(argv);
	return ("bad field count");
    }
    normalize_options(argv);
    masterp->name_space =
	concatenate(argv->argv[0], ".", argv->argv[1], (char *) 0);
    masterp->argv = argv;
    masterp->valid_names = 0;
    masterp->all_params = 0;
    return (0);
}
Exemple #6
0
ARGV   *pcf_parse_service_pattern(const char *pattern, int min_expr, int max_expr)
{
    ARGV   *argv;
    char  **cpp;

    /*
     * Work around argv_split() lameness.
     */
    if (*pattern == '/')
	return (0);
    argv = argv_split(pattern, PCF_NAMESP_SEP_STR);
    if (argv->argc < min_expr || argv->argc > max_expr) {
	argv_free(argv);
	return (0);
    }

    /*
     * Allow '*' only all by itself.
     */
    for (cpp = argv->argv; *cpp; cpp++) {
	if (!PCF_MATCH_ANY(*cpp) && strchr(*cpp, PCF_MATCH_WILDC_STR[0]) != 0) {
	    argv_free(argv);
	    return (0);
	}
    }

    /*
     * Provide defaults for missing fields.
     */
    while (argv->argc < max_expr)
	argv_add(argv, PCF_MATCH_WILDC_STR, ARGV_END);
    return (argv);
}
int     main(int argc, char **argv)
{
    ARGV   *types_argv;
    unsigned *types;
    char   *name;
    VSTRING *fqdn = vstring_alloc(100);
    VSTRING *why = vstring_alloc(100);
    int     rcode;
    DNS_RR *rr;
    int     i;

    msg_vstream_init(argv[0], VSTREAM_ERR);
    if (argc != 3)
	msg_fatal("usage: %s types name", argv[0]);
    types_argv = argv_split(argv[1], ", \t\r\n");
    types = (unsigned *) mymalloc(sizeof(*types) * (types_argv->argc + 1));
    for (i = 0; i < types_argv->argc; i++)
	if ((types[i] = dns_type(types_argv->argv[i])) == 0)
	    msg_fatal("invalid query type: %s", types_argv->argv[i]);
    types[i] = 0;
    argv_free(types_argv);
    name = argv[2];
    msg_verbose = 1;
    switch (dns_lookup_rv(name, RES_DEBUG | RES_USE_DNSSEC, &rr, fqdn, why,
			  &rcode, DNS_REQ_FLAG_NONE, types)) {
    default:
	msg_fatal("%s (rcode=%d)", vstring_str(why), rcode);
    case DNS_OK:
	printf("%s: fqdn: %s\n", name, vstring_str(fqdn));
	print_rr(rr);
	dns_rr_free(rr);
    }
    myfree((char *) types);
    exit(0);
}
Exemple #8
0
/*
 * allocate resources for a job.
 *
 * The job will consist of at least one app, e.g., "allocate
 * jobid=100 return=all timeout=10:app=0 np=5 N=2
 * node_list=vm2,vm3 flag=mandatory:app=1 N=2".
 *
 * IN:
 * 	new_fd: send allocation result to socket_fd
 * 	msg: resource requirement cmd
 */
extern void allocate_job_op(slurm_fd_t new_fd, const char *msg)
{
	char orte_jobid[16] = "";
	char return_flag[16] = "";
	size_t job_timeout = 15; /* if not specified, by default */

	char send_buf[SIZE];
	char **app_argv = NULL, **tmp_app_argv;
	size_t app_timeout;
	uint32_t app_count = 1;
	char app_resp_msg[SIZE];
	char **all_resp_msg_argv = NULL, **tmp_all_resp_msg_argv;

	app_argv = argv_split(msg, ':');
	/* app_count dose not include the first part (job info) */
	app_count = argv_count(app_argv) - 1;
	/* app_argv will be freed */
	tmp_app_argv = app_argv;
	while (*tmp_app_argv) {
		if (strstr(*tmp_app_argv, "allocate")) {
			_parse_job_params(*tmp_app_argv, orte_jobid,
								return_flag, &job_timeout);
		} else if (strstr(*tmp_app_argv, "app")) {
			app_timeout = job_timeout / app_count;

			_allocate_app_op(*tmp_app_argv, app_timeout, app_resp_msg);

			if (0 == strcmp(return_flag, "all")
					&& 0 != strlen(app_resp_msg)) {
				argv_append_nosize(&all_resp_msg_argv, app_resp_msg);
			} else if (0 != strlen(app_resp_msg)) {
				/* if return_flag != "all",
				 * each app's allocation will be sent individually */
				sprintf(send_buf, "jobid=%s:%s", orte_jobid, app_resp_msg);
				info("BBB: send to client: %s", send_buf);
				send_reply(new_fd, send_buf);
			}
		}
		tmp_app_argv++;
	}
	/* free app_argv */
	argv_free(app_argv);

	if (0 == strcmp(return_flag, "all")) {
		sprintf(send_buf, "jobid=%s", orte_jobid);
		/* all_resp_msg_argv will be freed */
		tmp_all_resp_msg_argv = all_resp_msg_argv;
		while (*tmp_all_resp_msg_argv) {
			sprintf(send_buf, "%s:%s", send_buf, *tmp_all_resp_msg_argv);
			tmp_all_resp_msg_argv++;
		}
		/* free all_resp_msg_argv */
		argv_free(all_resp_msg_argv);

		info("BBB: send to client: %s", send_buf);
		send_reply(new_fd, send_buf);
	}
}
Exemple #9
0
/* Ktap Main Entry */
static int ktap_main(struct file *file, struct ktap_user_parm *uparm_ptr)
{
	unsigned long *buff = NULL;
	ktap_State *ks;
	Closure *cl;
	int argc;
	char **argv, *argstr;
	int ret;

	argstr = kmalloc(uparm_ptr->arglen, GFP_KERNEL);
	if (!argstr)
		return -ENOMEM;

	ret = copy_from_user(argstr, (void __user *)uparm_ptr->argstr,
			     uparm_ptr->arglen);
	if (ret < 0) {
		kfree(argstr);
		return -EFAULT;
	}

	argv = argv_split(GFP_KERNEL, argstr, &argc);
	if (!argv) {
		kfree(argstr);
		pr_err("out of memory");
		return -ENOMEM;
	}

	kfree(argstr);

	ret = load_trunk(uparm_ptr, &buff);
	if (ret) {
		pr_err("cannot load file %s\n", argv[0]);
		argv_free(argv);
		return ret;
	}

	ks = kp_newstate((ktap_State **)&file->private_data, argc, argv);

	argv_free(argv);
	if (unlikely(!ks)) {
		vfree(buff);
		return -ENOEXEC;
	}

	cl = kp_load(ks, (unsigned char *)buff);

	vfree(buff);

	if (cl) {
		/* optimize bytecode before excuting */
		kp_optimize_code(ks, 0, cl->l.p);
		kp_call(ks, ks->top - 1, 0);
	}

	kp_exit(ks);
	return 0;
}
Exemple #10
0
int http_transport_init(struct c2_transport *t)
{
	struct http_ctx *ctx = calloc(1, sizeof *ctx);
	if (ctx == NULL) {
		return -1;
	}

	ctx->t = t;
	ctx->uri = strdup(c2_transport_uri(t));
	if (ctx->uri == NULL) {
		goto err;
	}

	ctx->data.content_type = "application/octet-stream";
	ctx->opts.flags = HTTP_OPTS_SKIP_TLS_VALIDATION;

	char *ua = "Mozilla/5.0 (Windows NT 6.1; Trident/7.0; rv:11.0) like Gecko";
	char *args = strchr(ctx->uri, '|');
	if (args) {
		*args = '\0';
		if (strlen(++args)) {
			size_t argc = 0;
			char **argv = argv_split(args, NULL, &argc);
			for (size_t i = 0; i < argc; i++) {
				if (strcmp(argv[i], "--ua") == 0 && argv[i + 1]) {
					ua = argv[i + 1];
				}
			}
		}
	}

	ctx->data.num_headers = 1;
	ctx->headers[0] = strdup("Connection: close");
	if (asprintf(&ctx->headers[ctx->data.num_headers], "User-Agent: %s", ua) > 0) {
		ctx->data.num_headers++;
	}
	ctx->data.headers = ctx->headers;

	ctx->first_packet = 1;

	ev_init(&ctx->poll_timer, http_poll_timer_cb);
	ctx->poll_timer.data = ctx;

	ctx->egress = buffer_queue_new();
	if (ctx->egress == NULL) {
		goto err;
	}

	c2_transport_set_ctx(t, ctx);
	return 0;

err:
	http_ctx_free(ctx);
	return -1;
}
Exemple #11
0
int main(int argc, char * argv[]) {
	peopleHead = NULL;
	allMahHashes_employees = ht_create(65536);
	allMahHashes_guests = ht_create(65536);
	int32_t fileSize = 0;
	FILE* file = NULL;
	size_t bytes = 0;
	ssize_t read = 0;
	char * line = NULL;
	char interString[MAX * 4];

	//get Logreader arguements
	logread_args args = opt_parser(argc, argv, 1);
	//Verify integrity and hopefully the syntax should be right

	fileSize = fsize(args.logName);
	if (fileSize > 15) {
		unsigned int salt[] = { 12345, 54321 };
		FILE * encrypted_file = fopen(args.logName, "r");
		FILE * decrypted = fopen("tempblahman", "w+");
		do_crypt(encrypted_file, decrypted, DECRYPT, args.token,
				strlen(args.token), (unsigned char *) salt);
		rename("tempblahman", args.logName);
	} else {
		invalid();
	}

	file = fopen(args.logName, "r");
	//Line by line apply the options
	while ((read = getline(&line, &bytes, file)) != -1 && fileSize > 10) {

		int len = strlen(line);
		fileSize = fileSize - len;
		// RERUN COMMANDS CAUZE LOGIC!
		sprintf(interString, "./logappend %s", line);
		int tempc;
		char ** tempv = argv_split(interString, &tempc);
		logappend_args temp = opt_parser_log(tempc, tempv);
		buildDataStructs(&temp);
		bzero(interString, MAX * 4);
		argv_free(tempv);
		// FINISH LOGICZ
	}
	doBadThings(&args);

	unsigned int salt[] = { 12345, 54321 };
	FILE * decrypted_file = fopen(args.logName, "r");
	FILE * encrypted = fopen("tempblahman", "w+");
	do_crypt(decrypted_file, encrypted, ENCRYPT, args.token, strlen(args.token),
			(unsigned char *) salt);
	rename("tempblahman", args.logName);

	return 0;
}
Exemple #12
0
static int command_trace_probe(const char *buf)
{
	char **argv;
	int argc = 0, ret = 0;

	argv = argv_split(GFP_KERNEL, buf, &argc);
	if (!argv)
		return -ENOMEM;

	if (argc)
		ret = create_trace_probe(argc, argv);

	argv_free(argv);
	return ret;
}
/* Parse kprobe_events event into struct probe_point */
void parse_trace_kprobe_event(const char *str, struct probe_point *pp)
{
	char pr;
	char *p;
	int ret, i, argc;
	char **argv;

	pr_debug("Parsing kprobe_events: %s\n", str);
	argv = argv_split(str, &argc);
	if (!argv)
		die("argv_split failed.");
	if (argc < 2)
		semantic_error("Too less arguments.");

	/* Scan event and group name. */
	ret = sscanf(argv[0], "%c:%a[^/ \t]/%a[^ \t]",
		     &pr, (float *)(void *)&pp->group,
		     (float *)(void *)&pp->event);
	if (ret != 3)
		semantic_error("Failed to parse event name: %s", argv[0]);
	pr_debug("Group:%s Event:%s probe:%c\n", pp->group, pp->event, pr);

	pp->retprobe = (pr == 'r');

	/* Scan function name and offset */
	ret = sscanf(argv[1], "%a[^+]+%d", (float *)(void *)&pp->function,
		     &pp->offset);
	if (ret == 1)
		pp->offset = 0;

	/* kprobe_events doesn't have this information */
	pp->line = 0;
	pp->file = NULL;

	pp->nr_args = argc - 2;
	pp->args = zalloc(sizeof(char *) * pp->nr_args);
	for (i = 0; i < pp->nr_args; i++) {
		p = strchr(argv[i + 2], '=');
		if (p)	/* We don't need which register is assigned. */
			*p = '\0';
		pp->args[i] = strdup(argv[i + 2]);
		if (!pp->args[i])
			die("Failed to copy argument.");
	}

	argv_free(argv);
}
Exemple #14
0
NORETURN exec_command(const char *command)
{
    ARGV   *argv;

    /*
     * Character filter. In this particular case, we allow space and tab in
     * addition to the regular character set.
     */
    static char ok_chars[] = "1234567890!@%-_=+:,./\
abcdefghijklmnopqrstuvwxyz\
ABCDEFGHIJKLMNOPQRSTUVWXYZ" SPACE_TAB;

    /*
     * See if this command contains any shell magic characters.
     */
    if (command[strspn(command, ok_chars)] == 0
	&& command[strspn(command, SPACE_TAB)] != 0) {

	/*
	 * No shell meta characters found, so we can try to avoid the overhead
	 * of running a shell. Just split the command on whitespace and exec
	 * the result directly.
	 */
	argv = argv_split(command, SPACE_TAB);
	(void) execvp(argv->argv[0], argv->argv);

	/*
	 * Auch. Perhaps they're using some shell built-in command.
	 */
	if (errno != ENOENT || strchr(argv->argv[0], '/') != 0)
	    msg_fatal("execvp %s: %m", argv->argv[0]);

	/*
	 * Not really necessary, but...
	 */
	argv_free(argv);
    }

    /*
     * Pass the command to a shell.
     */
    (void) execl(_PATH_BSHELL, "sh", "-c", command, (char *) 0);
    msg_fatal("execl %s: %m", _PATH_BSHELL);
}
Exemple #15
0
static int run_cmd(const char *cmd)
{
	char **argv;
	static char *envp[] = {
		"HOME=/",
		"PATH=/sbin:/bin:/usr/sbin:/usr/bin",
		NULL
	};
	int ret;
	argv = argv_split(GFP_KERNEL, cmd, NULL);
	if (argv) {
		ret = call_usermodehelper(argv[0], argv, envp, UMH_WAIT_EXEC);
		argv_free(argv);
	} else {
		ret = -ENOMEM;
	}

	return ret;
}
Exemple #16
0
/**
 * orderly_poweroff - Trigger an orderly system poweroff
 * @force: force poweroff if command execution fails
 *
 * This may be called from any context to trigger a system shutdown.
 * If the orderly shutdown fails, it will force an immediate shutdown.
 */
int orderly_poweroff(bool force)
{
	int argc;
	char **argv = argv_split(GFP_ATOMIC, poweroff_cmd, &argc);
	static char *envp[] = {
		"HOME=/",
		"PATH=/sbin:/bin:/usr/sbin:/usr/bin",
		NULL
	};
	int ret = -ENOMEM;
	struct subprocess_info *info;

	if (argv == NULL) {
		printk(KERN_WARNING "%s failed to allocate memory for \"%s\"\n",
		       __func__, poweroff_cmd);
		goto out;
	}

	info = call_usermodehelper_setup(argv[0], argv, envp);
	if (info == NULL) {
		argv_free(argv);
		goto out;
	}

	call_usermodehelper_setcleanup(info, argv_cleanup);

	ret = call_usermodehelper_exec(info, UMH_NO_WAIT);

  out:
	if (ret && force) {
		printk(KERN_WARNING "Failed to start orderly shutdown: "
		       "forcing the issue\n");

		/* I guess this should try to kick off some daemon to
		   sync and poweroff asap.  Or not even bother syncing
		   if we're doing an emergency shutdown? */
		emergency_sync();
		kernel_power_off();
	}

	return ret;
}
Exemple #17
0
void    psc_dnsbl_init(void)
{
    const char *myname = "psc_dnsbl_init";
    ARGV   *dnsbl_site = argv_split(var_psc_dnsbl_sites, CHARS_COMMA_SP);
    char  **cpp;

    /*
     * Sanity check.
     */
    if (dnsbl_site_cache != 0)
	msg_panic("%s: called more than once", myname);

    /*
     * pre-compute the DNSBLOG socket name.
     */
    psc_dnsbl_service = concatenate(MAIL_CLASS_PRIVATE, "/",
				    var_dnsblog_service, (char *) 0);

    /*
     * Prepare for quick iteration when sending out queries to all DNSBL
     * servers, and for quick lookup when a reply arrives from a specific
     * DNSBL server.
     */
    dnsbl_site_cache = htable_create(13);
    for (cpp = dnsbl_site->argv; *cpp; cpp++)
	psc_dnsbl_add_site(*cpp);
    argv_free(dnsbl_site);
    dnsbl_site_list = htable_list(dnsbl_site_cache);

    /*
     * The per-client blocklist score.
     */
    dnsbl_score_cache = htable_create(13);

    /*
     * Space for ad-hoc DNSBLOG server request/reply parameters.
     */
    reply_client = vstring_alloc(100);
    reply_dnsbl = vstring_alloc(100);
    reply_addr = vstring_alloc(100);
}
Exemple #18
0
int     main(int unused_argc, char **unused_argv)
{
    VSTRING *buf = vstring_alloc(1);
    ARGV   *argv;
    struct action *ap;
    int     interactive = isatty(0);

    endp_prop = vstring_alloc(1);
    dest_prop = vstring_alloc(1);

    vstream_fileno(VSTREAM_ERR) = 1;

    while (get_buffer(buf, VSTREAM_IN, interactive) != VSTREAM_EOF) {
	argv = argv_split(STR(buf), CHARS_SPACE);
	if (argv->argc > 0 && argv->argv[0][0] != '#') {
	    msg_verbose = verbose_level;
	    for (ap = actions; ap->command != 0; ap++) {
		if (strcmp(ap->command, argv->argv[0]) == 0) {
		    if ((ap->flags & FLAG_NEED_CACHE) != 0 && scache == 0)
			msg_error("no session cache");
		    else
			ap->action(argv);
		    break;
		}
	    }
	    msg_verbose = 0;
	    if (ap->command == 0)
		msg_error("bad command: %s", argv->argv[0]);
	}
	argv_free(argv);
    }
    scache_free(scache);
    vstring_free(endp_prop);
    vstring_free(dest_prop);
    vstring_free(buf);
    exit(0);
}
/* Parse perf-probe event definition */
void parse_perf_probe_event(const char *str, struct probe_point *pp,
			    bool *need_dwarf)
{
	char **argv;
	int argc, i;

	*need_dwarf = false;

	argv = argv_split(str, &argc);
	if (!argv)
		die("argv_split failed.");
	if (argc > MAX_PROBE_ARGS + 1)
		semantic_error("Too many arguments");

	/* Parse probe point */
	parse_perf_probe_probepoint(argv[0], pp);
	if (pp->file || pp->line || pp->lazy_line)
		*need_dwarf = true;

	/* Copy arguments and ensure return probe has no C argument */
	pp->nr_args = argc - 1;
	pp->args = zalloc(sizeof(char *) * pp->nr_args);
	for (i = 0; i < pp->nr_args; i++) {
		pp->args[i] = strdup(argv[i + 1]);
		if (!pp->args[i])
			die("Failed to copy argument.");
		if (is_c_varname(pp->args[i])) {
			if (pp->retprobe)
				semantic_error("You can't specify local"
						" variable for kretprobe");
			*need_dwarf = true;
		}
	}

	argv_free(argv);
}
Exemple #20
0
void    read_master(int fail_on_open_error)
{
    const char *myname = "read_master";
    char   *path;
    VSTRING *buf;
    ARGV   *argv;
    VSTREAM *fp;
    int     entry_count = 0;
    int     line_count = 0;

    /*
     * Sanity check.
     */
    if (master_table != 0)
	msg_panic("%s: master table is already initialized", myname);

    /*
     * Get the location of master.cf.
     */
    if (var_config_dir == 0)
	set_config_dir();
    path = concatenate(var_config_dir, "/", MASTER_CONF_FILE, (char *) 0);

    /*
     * We can't use the master daemon's master_ent routines in their current
     * form. They convert everything to internal form, and they skip disabled
     * services.
     * 
     * The postconf command needs to show default fields as "-", and needs to
     * know about all service names so that it can generate service-dependent
     * parameter names (transport-dependent etc.).
     */
#define MASTER_BLANKS	" \t\r\n"		/* XXX */

    /*
     * Initialize the in-memory master table.
     */
    master_table = (PC_MASTER_ENT *) mymalloc(sizeof(*master_table));

    /*
     * Skip blank lines and comment lines. Degrade gracefully if master.cf is
     * not available, and master.cf is not the primary target.
     */
    if ((fp = vstream_fopen(path, O_RDONLY, 0)) == 0) {
	if (fail_on_open_error)
	    msg_fatal("open %s: %m", path);
	msg_warn("open %s: %m", path);
    } else {
	buf = vstring_alloc(100);
	while (readlline(buf, fp, &line_count) != 0) {
	    master_table = (PC_MASTER_ENT *) myrealloc((char *) master_table,
				 (entry_count + 2) * sizeof(*master_table));
	    argv = argv_split(STR(buf), MASTER_BLANKS);
	    if (argv->argc < PC_MASTER_MIN_FIELDS)
		msg_fatal("file %s: line %d: bad field count",
			  path, line_count);
	    normalize_options(argv);
	    master_table[entry_count].name_space =
		concatenate(argv->argv[0], ".", argv->argv[1], (char *) 0);
	    master_table[entry_count].argv = argv;
	    master_table[entry_count].valid_names = 0;
	    master_table[entry_count].all_params = 0;
	    entry_count += 1;
	}
	vstream_fclose(fp);
	vstring_free(buf);
    }

    /*
     * Null-terminate the master table and clean up.
     */
    master_table[entry_count].argv = 0;
    myfree(path);
}
Exemple #21
0
int     main(int unused_ac, char **av)
{
    VSTRING *inbuf = vstring_alloc(10);
    int     status;
    ARGV   *argv = 0;

    msg_vstream_init(av[0], VSTREAM_ERR);

    msg_verbose = 3;

    mail_conf_read();
    msg_info("using config files in %s", var_config_dir);

    if (chdir(var_queue_dir) < 0)
	msg_fatal("chdir %s: %m", var_queue_dir);

    while (vstring_fgets_nonl(inbuf, VSTREAM_IN)) {
	argv = argv_split(STR(inbuf), " \t\r\n");
	if (argv->argc == 0) {
	    argv_free(argv);
	    continue;
	}
#define COMMAND(argv, str, len) \
    (strcasecmp(argv->argv[0], str) == 0 && argv->argc == len)

	if (COMMAND(argv, "policy", 2)) {
	    int     cachable;
	    int     timeout;

	    status = tls_mgr_policy(argv->argv[1], &cachable, &timeout);
	    vstream_printf("status=%d cachable=%d timeout=%d\n",
			   status, cachable, timeout);
	} else if (COMMAND(argv, "seed", 2)) {
	    VSTRING *buf = vstring_alloc(10);
	    VSTRING *hex = vstring_alloc(10);
	    int     len = atoi(argv->argv[1]);

	    status = tls_mgr_seed(buf, len);
	    hex_encode(hex, STR(buf), LEN(buf));
	    vstream_printf("status=%d seed=%s\n", status, STR(hex));
	    vstring_free(hex);
	    vstring_free(buf);
	} else if (COMMAND(argv, "lookup", 3)) {
	    VSTRING *buf = vstring_alloc(10);

	    status = tls_mgr_lookup(argv->argv[1], argv->argv[2], buf);
	    vstream_printf("status=%d session=%.*s\n",
			   status, LEN(buf), STR(buf));
	    vstring_free(buf);
	} else if (COMMAND(argv, "update", 4)) {
	    status = tls_mgr_update(argv->argv[1], argv->argv[2],
				    argv->argv[3], strlen(argv->argv[3]));
	    vstream_printf("status=%d\n", status);
	} else if (COMMAND(argv, "delete", 3)) {
	    status = tls_mgr_delete(argv->argv[1], argv->argv[2]);
	    vstream_printf("status=%d\n", status);
	} else {
	    vstream_printf("usage:\n"
			   "seed byte_count\n"
			   "policy smtpd|smtp|lmtp\n"
			   "lookup smtpd|smtp|lmtp cache_id\n"
			   "update smtpd|smtp|lmtp cache_id session\n"
			   "delete smtpd|smtp|lmtp cache_id\n");
	}
	vstream_fflush(VSTREAM_OUT);
	argv_free(argv);
    }

    vstring_free(inbuf);
    return (0);
}
Exemple #22
0
int     main(int argc, char **argv)
{
    MILTERS *milters = 0;
    char   *conn_macros, *helo_macros, *mail_macros, *rcpt_macros;
    char   *data_macros, *eoh_macros, *eod_macros, *unk_macros;
    VSTRING *inbuf = vstring_alloc(100);
    char   *bufp;
    char   *cmd;
    int     ch;
    int     istty = isatty(vstream_fileno(VSTREAM_IN));

    conn_macros = helo_macros = mail_macros = rcpt_macros = data_macros
	= eoh_macros = eod_macros = unk_macros = "";

    msg_vstream_init(argv[0], VSTREAM_ERR);
    while ((ch = GETOPT(argc, argv, "V:v")) > 0) {
	switch (ch) {
	default:
	    msg_fatal("usage: %s [-a action] [-p protocol] [-v]", argv[0]);
	case 'a':
	    var_milt_def_action = optarg;
	    break;
	case 'p':
	    var_milt_protocol = optarg;
	    break;
	case 'v':
	    msg_verbose++;
	    break;
	}
    }
    optind = OPTIND;

    for (;;) {
	const char *resp = 0;
	ARGV   *argv;
	char  **args;

	if (istty) {
	    vstream_printf("- ");
	    vstream_fflush(VSTREAM_OUT);
	}
	if (vstring_fgets_nonl(inbuf, VSTREAM_IN) <= 0)
	    break;
	bufp = vstring_str(inbuf);
	if (!istty) {
	    vstream_printf("> %s\n", bufp);
	    vstream_fflush(VSTREAM_OUT);
	}
	if (*bufp == '#')
	    continue;
	cmd = mystrtok(&bufp, " ");
	if (cmd == 0) {
	    usage();
	    continue;
	}
	argv = argv_split(bufp, " ");
	args = argv->argv;
	if (strcmp(cmd, "create") == 0 && argv->argc == 1) {
	    if (milters != 0) {
		msg_warn("deleting existing milters");
		milter_free(milters);
	    }
	    milters = milter_create(args[0], var_milt_conn_time,
				    var_milt_cmd_time, var_milt_msg_time,
				    var_milt_protocol, var_milt_def_action,
				    conn_macros, helo_macros, mail_macros,
				    rcpt_macros, data_macros, eoh_macros,
				    eod_macros, unk_macros);
	} else if (strcmp(cmd, "free") == 0 && argv->argc == 0) {
	    if (milters == 0) {
		msg_warn("no milters");
		continue;
	    }
	    milter_free(milters);
	    milters = 0;
	} else if (strcmp(cmd, "connect") == 0 && argv->argc == 4) {
	    if (milters == 0) {
		msg_warn("no milters");
		continue;
	    }
	    resp = milter_conn_event(milters, args[0], args[1], args[2],
				 strcmp(args[3], "AF_INET") == 0 ? AF_INET :
			       strcmp(args[3], "AF_INET6") == 0 ? AF_INET6 :
				 strcmp(args[3], "AF_UNIX") == 0 ? AF_UNIX :
				     AF_UNSPEC);
	} else if (strcmp(cmd, "helo") == 0 && argv->argc == 1) {
	    if (milters == 0) {
		msg_warn("no milters");
		continue;
	    }
	    resp = milter_helo_event(milters, args[0], 0);
	} else if (strcmp(cmd, "ehlo") == 0 && argv->argc == 1) {
	    if (milters == 0) {
		msg_warn("no milters");
		continue;
	    }
	    resp = milter_helo_event(milters, args[0], 1);
	} else if (strcmp(cmd, "mail") == 0 && argv->argc > 0) {
	    if (milters == 0) {
		msg_warn("no milters");
		continue;
	    }
	    resp = milter_mail_event(milters, (const char **) args);
	} else if (strcmp(cmd, "rcpt") == 0 && argv->argc > 0) {
	    if (milters == 0) {
		msg_warn("no milters");
		continue;
	    }
	    resp = milter_rcpt_event(milters, 0, (const char **) args);
	} else if (strcmp(cmd, "unknown") == 0 && argv->argc > 0) {
	    if (milters == 0) {
		msg_warn("no milters");
		continue;
	    }
	    resp = milter_unknown_event(milters, args[0]);
	} else if (strcmp(cmd, "data") == 0 && argv->argc == 0) {
	    if (milters == 0) {
		msg_warn("no milters");
		continue;
	    }
	    resp = milter_data_event(milters);
	} else if (strcmp(cmd, "disconnect") == 0 && argv->argc == 0) {
	    if (milters == 0) {
		msg_warn("no milters");
		continue;
	    }
	    milter_disc_event(milters);
	} else {
	    usage();
	}
	if (resp != 0)
	    msg_info("%s", resp);
	argv_free(argv);
    }
    if (milters != 0)
	milter_free(milters);
    vstring_free(inbuf);
    return (0);
}
Exemple #23
0
void dict_open_dlinfo(const char *path)
{
    char    *myname="dict_open_dlinfo";
    VSTREAM *conf_fp=vstream_fopen(path,O_RDONLY,0);
    VSTRING *buf = vstring_alloc(100);
    char    *cp;
    ARGV    *argv;
    MVECT    vector;
    int      nelm=0;
    int      linenum=0;

    dict_dlinfo=(DLINFO*)mvect_alloc(&vector,sizeof(DLINFO),3,NULL,NULL);

    if (!conf_fp) {
	msg_warn("%s: cannot open %s.  No dynamic maps will be allowed.",
		myname, path);
    } else {
	while (vstring_get_nonl(buf,conf_fp) != VSTREAM_EOF) {
	    cp = vstring_str(buf);
	    linenum++;
	    if (*cp == '#' || *cp == '\0')
		continue;
	    argv = argv_split(cp, " \t");
	    if (argv->argc != 3 && argv->argc != 4) {
		msg_fatal("%s: Expected \"pattern .so-name open-function [mkmap-function]\" at line %d",
			  myname, linenum);
	    }
	    if (STREQ(argv->argv[0],"*")) {
		msg_warn("%s: wildcard dynamic map entry no longer supported.",
			  myname);
		continue;
	    }
	    if (argv->argv[1][0] != '/') {
		msg_fatal("%s: .so name must begin with a \"/\" at line %d",
			  myname, linenum);
	    }
	    if (nelm >= vector.nelm) {
		dict_dlinfo=(DLINFO*)mvect_realloc(&vector,vector.nelm+3);
	    }
	    dict_dlinfo[nelm].pattern  = mystrdup(argv->argv[0]);
	    dict_dlinfo[nelm].soname   = mystrdup(argv->argv[1]);
	    dict_dlinfo[nelm].openfunc = mystrdup(argv->argv[2]);
	    if (argv->argc==4)
		dict_dlinfo[nelm].mkmapfunc = mystrdup(argv->argv[3]);
	    else
		dict_dlinfo[nelm].mkmapfunc = NULL;
	    nelm++;
	    argv_free(argv);
	}
    }
    if (nelm >= vector.nelm) {
	dict_dlinfo=(DLINFO*)mvect_realloc(&vector,vector.nelm+1);
    }
    dict_dlinfo[nelm].pattern  = NULL;
    dict_dlinfo[nelm].soname   = NULL;
    dict_dlinfo[nelm].openfunc = NULL;
    dict_dlinfo[nelm].mkmapfunc = NULL;
    if (conf_fp)
	vstream_fclose(conf_fp);
    vstring_free(buf);
}
Exemple #24
0
int     main(int argc, char **argv)
{
    DICT_CACHE_TEST *test_job;
    VSTRING *inbuf = vstring_alloc(100);
    char   *bufp;
    ARGV   *args;
    DICT_CACHE *cache = 0;
    int     stdin_is_tty;

    msg_vstream_init(argv[0], VSTREAM_ERR);
    if (argc != 1)
	usage(argv[0]);


    test_job = create_requests(DICT_CACHE_SREQ_LIMIT);

    stdin_is_tty = isatty(0);

    for (;;) {
	if (stdin_is_tty) {
	    vstream_printf("> ");
	    vstream_fflush(VSTREAM_OUT);
	}
	if (vstring_fgets_nonl(inbuf, VSTREAM_IN) == 0)
	    break;
	bufp = vstring_str(inbuf);
	if (!stdin_is_tty) {
	    vstream_printf("> %s\n", bufp);
	    vstream_fflush(VSTREAM_OUT);
	}
	if (*bufp == '#')
	    continue;
	args = argv_split(bufp, DELIMS);
	if (argc == 0) {
	    vstream_printf("usage: %s\n", USAGE);
	    vstream_fflush(VSTREAM_OUT);
	    continue;
	}
	if (strcmp(args->argv[0], "verbose") == 0 && args->argc == 2) {
	    msg_verbose = atoi(args->argv[1]);
	} else if (strcmp(args->argv[0], "elapsed") == 0 && args->argc == 2) {
	    show_elapsed = atoi(args->argv[1]);
#ifdef HAS_LMDB
	} else if (strcmp(args->argv[0], "lmdb_map_size") == 0 && args->argc == 2) {
	    dict_lmdb_map_size = atol(args->argv[1]);
#endif
	} else if (strcmp(args->argv[0], "cache") == 0 && args->argc == 2) {
	    if (cache)
		dict_cache_close(cache);
	    cache = dict_cache_open(args->argv[1], O_CREAT | O_RDWR,
				    DICT_CACHE_OPEN_FLAGS);
	} else if (strcmp(args->argv[0], "reset") == 0 && args->argc == 1) {
	    reset_requests(test_job);
	} else if (strcmp(args->argv[0], "run") == 0 && args->argc == 1) {
	    run_requests(test_job, cache, inbuf);
	} else if (strcmp(args->argv[0], "status") == 0 && args->argc == 1) {
	    show_status(test_job, cache);
	} else {
	    add_request(test_job, args);
	}
	vstream_fflush(VSTREAM_OUT);
	argv_free(args);
    }

    vstring_free(inbuf);
    free_requests(test_job);
    if (cache)
	dict_cache_close(cache);
    return (0);
}
Exemple #25
0
int     main(int argc, char **argv)
{
    static VSTREAM *lock_fp;
    static VSTREAM *data_lock_fp;
    VSTRING *lock_path;
    VSTRING *data_lock_path;
    off_t   inherited_limit;
    int     debug_me = 0;
    int     ch;
    int     fd;
    int     n;
    int     test_lock = 0;
    VSTRING *why;
    WATCHDOG *watchdog;
    ARGV   *import_env;

    /*
     * Fingerprint executables and core dumps.
     */
    MAIL_VERSION_STAMP_ALLOCATE;

    /*
     * Initialize.
     */
    umask(077);					/* never fails! */

    /*
     * Process environment options as early as we can.
     */
    if (getenv(CONF_ENV_VERB))
	msg_verbose = 1;
    if (getenv(CONF_ENV_DEBUG))
	debug_me = 1;

    /*
     * Don't die when a process goes away unexpectedly.
     */
    signal(SIGPIPE, SIG_IGN);

    /*
     * Strip and save the process name for diagnostics etc.
     */
    var_procname = mystrdup(basename(argv[0]));

    /*
     * When running a child process, don't leak any open files that were
     * leaked to us by our own (privileged) parent process. Descriptors 0-2
     * are taken care of after we have initialized error logging.
     * 
     * Some systems such as AIX have a huge per-process open file limit. In
     * those cases, limit the search for potential file descriptor leaks to
     * just the first couple hundred.
     * 
     * The Debian post-installation script passes an open file descriptor into
     * the master process and waits forever for someone to close it. Because
     * of this we have to close descriptors > 2, and pray that doing so does
     * not break things.
     */
    closefrom(3);

    /*
     * Initialize logging and exit handler.
     */
    msg_syslog_init(mail_task(var_procname), LOG_PID, LOG_FACILITY);

    /*
     * Check the Postfix library version as soon as we enable logging.
     */
    MAIL_VERSION_CHECK;

    /*
     * The mail system must be run by the superuser so it can revoke
     * privileges for selected operations. That's right - it takes privileges
     * to toss privileges.
     */
    if (getuid() != 0)
	msg_fatal("the master command is reserved for the superuser");
    if (unsafe() != 0)
	msg_fatal("the master command must not run as a set-uid process");

    /*
     * Process JCL.
     */
    while ((ch = GETOPT(argc, argv, "c:Dde:tv")) > 0) {
	switch (ch) {
	case 'c':
	    if (setenv(CONF_ENV_PATH, optarg, 1) < 0)
		msg_fatal("out of memory");
	    break;
	case 'd':
	    master_detach = 0;
	    break;
	case 'e':
	    event_request_timer(master_exit_event, (char *) 0, atoi(optarg));
	    break;
	case 'D':
	    debug_me = 1;
	    break;
	case 't':
	    test_lock = 1;
	    break;
	case 'v':
	    msg_verbose++;
	    break;
	default:
	    usage(argv[0]);
	    /* NOTREACHED */
	}
    }

    /*
     * This program takes no other arguments.
     */
    if (argc > optind)
	usage(argv[0]);

    /*
     * If started from a terminal, get rid of any tty association. This also
     * means that all errors and warnings must go to the syslog daemon.
     */
    if (master_detach)
	for (fd = 0; fd < 3; fd++) {
	    (void) close(fd);
	    if (open("/dev/null", O_RDWR, 0) != fd)
		msg_fatal("open /dev/null: %m");
	}

    /*
     * Run in a separate process group, so that "postfix stop" can terminate
     * all MTA processes cleanly. Give up if we can't separate from our
     * parent process. We're not supposed to blow away the parent.
     */
    if (debug_me == 0 && master_detach != 0 && setsid() == -1 && getsid(0) != getpid())
	msg_fatal("unable to set session and process group ID: %m");

    /*
     * Make some room for plumbing with file descriptors. XXX This breaks
     * when a service listens on many ports. In order to do this right we
     * must change the master-child interface so that descriptors do not need
     * to have fixed numbers.
     * 
     * In a child we need two descriptors for the flow control pipe, one for
     * child->master status updates and at least one for listening.
     */
    for (n = 0; n < 5; n++) {
	if (close_on_exec(dup(0), CLOSE_ON_EXEC) < 0)
	    msg_fatal("dup(0): %m");
    }

    /*
     * Final initializations. Unfortunately, we must read the global Postfix
     * configuration file after doing command-line processing, so that we get
     * consistent results when we SIGHUP the server to reload configuration
     * files.
     */
    master_vars_init();

    /*
     * In case of multi-protocol support. This needs to be done because
     * master does not invoke mail_params_init() (it was written before that
     * code existed).
     */
    (void) inet_proto_init(VAR_INET_PROTOCOLS, var_inet_protocols);

    /*
     * Environment import filter, to enforce consistent behavior whether
     * Postfix is started by hand, or at system boot time.
     */
    import_env = argv_split(var_import_environ, ", \t\r\n");
    clean_env(import_env->argv);
    argv_free(import_env);

    if ((inherited_limit = get_file_limit()) < 0)
	set_file_limit(OFF_T_MAX);

    if (chdir(var_queue_dir))
	msg_fatal("chdir %s: %m", var_queue_dir);

    /*
     * Lock down the master.pid file. In test mode, no file means that it
     * isn't locked.
     */
    lock_path = vstring_alloc(10);
    data_lock_path = vstring_alloc(10);
    why = vstring_alloc(10);

    vstring_sprintf(lock_path, "%s/%s.pid", DEF_PID_DIR, var_procname);
    if (test_lock && access(vstring_str(lock_path), F_OK) < 0)
	exit(0);
    lock_fp = open_lock(vstring_str(lock_path), O_RDWR | O_CREAT, 0644, why);
    if (test_lock)
	exit(lock_fp ? 0 : 1);
    if (lock_fp == 0)
	msg_fatal("open lock file %s: %s",
		  vstring_str(lock_path), vstring_str(why));
    vstream_fprintf(lock_fp, "%*lu\n", (int) sizeof(unsigned long) * 4,
		    (unsigned long) var_pid);
    if (vstream_fflush(lock_fp))
	msg_fatal("cannot update lock file %s: %m", vstring_str(lock_path));
    close_on_exec(vstream_fileno(lock_fp), CLOSE_ON_EXEC);

    /*
     * Lock down the Postfix-writable data directory.
     */
    vstring_sprintf(data_lock_path, "%s/%s.lock", var_data_dir, var_procname);
    set_eugid(var_owner_uid, var_owner_gid);
    data_lock_fp =
	open_lock(vstring_str(data_lock_path), O_RDWR | O_CREAT, 0644, why);
    set_ugid(getuid(), getgid());
    if (data_lock_fp == 0)
	msg_fatal("open lock file %s: %s",
		  vstring_str(data_lock_path), vstring_str(why));
    vstream_fprintf(data_lock_fp, "%*lu\n", (int) sizeof(unsigned long) * 4,
		    (unsigned long) var_pid);
    if (vstream_fflush(data_lock_fp))
	msg_fatal("cannot update lock file %s: %m", vstring_str(data_lock_path));
    close_on_exec(vstream_fileno(data_lock_fp), CLOSE_ON_EXEC);

    /*
     * Clean up.
     */
    vstring_free(why);
    vstring_free(lock_path);
    vstring_free(data_lock_path);

    /*
     * Optionally start the debugger on ourself.
     */
    if (debug_me)
	debug_process();

    /*
     * Finish initialization, last part. We must process configuration files
     * after processing command-line parameters, so that we get consistent
     * results when we SIGHUP the server to reload configuration files.
     */
    master_config();
    master_sigsetup();
    master_flow_init();
    msg_info("daemon started -- version %s, configuration %s",
	     var_mail_version, var_config_dir);

    /*
     * Process events. The event handler will execute the read/write/timer
     * action routines. Whenever something has happened, see if we received
     * any signal in the mean time. Although the master process appears to do
     * multiple things at the same time, it really is all a single thread, so
     * that there are no concurrency conflicts within the master process.
     */
#define MASTER_WATCHDOG_TIME	1000

    watchdog = watchdog_create(MASTER_WATCHDOG_TIME, (WATCHDOG_FN) 0, (char *) 0);
    for (;;) {
#ifdef HAS_VOLATILE_LOCKS
	if (myflock(vstream_fileno(lock_fp), INTERNAL_LOCK,
		    MYFLOCK_OP_EXCLUSIVE) < 0)
	    msg_fatal("refresh exclusive lock: %m");
	if (myflock(vstream_fileno(data_lock_fp), INTERNAL_LOCK,
		    MYFLOCK_OP_EXCLUSIVE) < 0)
	    msg_fatal("refresh exclusive lock: %m");
#endif
	watchdog_start(watchdog);		/* same as trigger servers */
	event_loop(MASTER_WATCHDOG_TIME / 2);
	if (master_gotsighup) {
	    msg_info("reload -- version %s, configuration %s",
		     var_mail_version, var_config_dir);
	    master_gotsighup = 0;		/* this first */
	    master_vars_init();			/* then this */
	    master_refresh();			/* then this */
	}
	if (master_gotsigchld) {
	    if (msg_verbose)
		msg_info("got sigchld");
	    master_gotsigchld = 0;		/* this first */
	    master_reap_child();		/* then this */
	}
    }
}
Exemple #26
0
int     main(int unused_ac, char **av)
{
    ACL_VSTRING *inbuf = acl_vstring_alloc(10);
    int     status;
    ARGV   *argv = 0;
    ACL_EVENT *eventp = acl_event_new_select(1, 0);

    acl_msg_verbose = 3;

    mail_conf_read();
    acl_msg_info("using config files in %s", var_config_dir);

    if (chdir(var_queue_dir) < 0)
	acl_msg_fatal("chdir %s: %s", var_queue_dir, acl_last_serror());
    tls_mgr_open(eventp);

    while (acl_vstring_fgets_nonl(inbuf, ACL_VSTREAM_IN)) {
	argv = argv_split(STR(inbuf), " \t\r\n");
	if (argv->argc == 0) {
	    argv_free(argv);
	    continue;
	}

#define COMMAND(argv, str, len) \
    (strcasecmp(argv->argv[0], str) == 0 && argv->argc == len)

	if (COMMAND(argv, "policy", 2)) {
	    int     cachable;

	    status = tls_mgr_policy(argv->argv[1], &cachable);
	    acl_vstream_printf("status=%d cachable=%d\n", status, cachable);
	} else if (COMMAND(argv, "seed", 2)) {
	    ACL_VSTRING *buf = acl_vstring_alloc(10);
	    ACL_VSTRING *hex = acl_vstring_alloc(10);
	    int     len = atoi(argv->argv[1]);

	    status = tls_mgr_seed(buf, len);
	    hex_encode(hex, STR(buf), LEN(buf));
	    acl_vstream_printf("status=%d seed=%s\n", status, STR(hex));
	    acl_vstring_free(hex);
	    acl_vstring_free(buf);
	} else if (COMMAND(argv, "lookup", 3)) {
	    ACL_VSTRING *buf = acl_vstring_alloc(10);

	    status = tls_mgr_lookup(argv->argv[1], argv->argv[2], buf);
	    acl_vstream_printf("status=%d session=%.*s\n",
			   status, LEN(buf), STR(buf));
	    acl_vstring_free(buf);
	} else if (COMMAND(argv, "update", 4)) {
	    status = tls_mgr_update(argv->argv[1], argv->argv[2],
				    argv->argv[3], strlen(argv->argv[3]));
	    acl_vstream_printf("status=%d\n", status);
	} else if (COMMAND(argv, "delete", 3)) {
	    status = tls_mgr_delete(argv->argv[1], argv->argv[2]);
	    acl_vstream_printf("status=%d\n", status);
	} else {
	    acl_vstream_printf("usage:\n"
			   "seed byte_count\n"
			   "policy smtpd|smtp|lmtp\n"
			   "lookup smtpd|smtp|lmtp cache_id\n"
			   "update smtpd|smtp|lmtp cache_id session\n"
			   "delete smtpd|smtp|lmtp cache_id\n");
	}
	acl_vstream_fflush(ACL_VSTREAM_OUT);
	argv_free(argv);
    }

    acl_vstring_free(inbuf);
    acl_event_free(eventp);
    return (0);
}
Exemple #27
0
static void qmgr_message_resolve(QMGR_MESSAGE *message)
{
    static ARGV *defer_xport_argv;
    RECIPIENT_LIST list = message->rcpt_list;
    RECIPIENT *recipient;
    QMGR_TRANSPORT *transport = 0;
    QMGR_QUEUE *queue = 0;
    RESOLVE_REPLY reply;
    VSTRING *queue_name;
    char   *at;
    char  **cpp;
    char   *nexthop;
    ssize_t len;
    int     status;
    DSN     dsn;
    MSG_STATS stats;
    DSN    *saved_dsn;

#define STREQ(x,y)	(strcmp(x,y) == 0)
#define STR		vstring_str
#define LEN		VSTRING_LEN

    resolve_clnt_init(&reply);
    queue_name = vstring_alloc(1);
    for (recipient = list.info; recipient < list.info + list.len; recipient++) {

	/*
	 * Redirect overrides all else. But only once (per entire message).
	 * For consistency with the remainder of Postfix, rewrite the address
	 * to canonical form before resolving it.
	 */
	if (message->redirect_addr) {
	    if (recipient > list.info) {
		recipient->u.queue = 0;
		continue;
	    }
	    message->rcpt_offset = 0;
	    message->rcpt_unread = 0;

	    rewrite_clnt_internal(REWRITE_CANON, message->redirect_addr,
				  reply.recipient);
	    RECIPIENT_UPDATE(recipient->address, STR(reply.recipient));
	    if (qmgr_resolve_one(message, recipient,
				 recipient->address, &reply) < 0)
		continue;
	    if (!STREQ(recipient->address, STR(reply.recipient)))
		RECIPIENT_UPDATE(recipient->address, STR(reply.recipient));
	}

	/*
	 * Content filtering overrides the address resolver.
	 * 
	 * XXX Bypass content_filter inspection for user-generated probes
	 * (sendmail -bv). MTA-generated probes never have the "please filter
	 * me" bits turned on, but we handle them here anyway for the sake of
	 * future proofing.
	 */
#define FILTER_WITHOUT_NEXTHOP(filter, next) \
	(((next) = split_at((filter), ':')) == 0 || *(next) == 0)

#define RCPT_WITHOUT_DOMAIN(rcpt, next) \
	((next = strrchr(rcpt, '@')) == 0 || *++(next) == 0)

	else if (message->filter_xport
		 && (message->tflags & DEL_REQ_TRACE_ONLY_MASK) == 0) {
	    reply.flags = 0;
	    vstring_strcpy(reply.transport, message->filter_xport);
	    if (FILTER_WITHOUT_NEXTHOP(STR(reply.transport), nexthop)
		&& *(nexthop = var_def_filter_nexthop) == 0
		&& RCPT_WITHOUT_DOMAIN(recipient->address, nexthop))
		nexthop = var_myhostname;
	    vstring_strcpy(reply.nexthop, nexthop);
	    vstring_strcpy(reply.recipient, recipient->address);
	}

	/*
	 * Resolve the destination to (transport, nexthop, address). The
	 * result address may differ from the one specified by the sender.
	 */
	else {
	    if (qmgr_resolve_one(message, recipient,
				 recipient->address, &reply) < 0)
		continue;
	    if (!STREQ(recipient->address, STR(reply.recipient)))
		RECIPIENT_UPDATE(recipient->address, STR(reply.recipient));
	}

	/*
	 * Bounce null recipients. This should never happen, but is most
	 * likely the result of a fault in a different program, so aborting
	 * the queue manager process does not help.
	 */
	if (recipient->address[0] == 0) {
	    QMGR_REDIRECT(&reply, MAIL_SERVICE_ERROR,
			  "5.1.3 null recipient address");
	}

	/*
	 * Discard mail to the local double bounce address here, so this
	 * system can run without a local delivery agent. They'd still have
	 * to configure something for mail directed to the local postmaster,
	 * though, but that is an RFC requirement anyway.
	 * 
	 * XXX This lookup should be done in the resolver, and the mail should
	 * be directed to a general-purpose null delivery agent.
	 */
	if (reply.flags & RESOLVE_CLASS_LOCAL) {
	    at = strrchr(STR(reply.recipient), '@');
	    len = (at ? (at - STR(reply.recipient))
		   : strlen(STR(reply.recipient)));
	    if (strncasecmp(STR(reply.recipient), var_double_bounce_sender,
			    len) == 0
		&& !var_double_bounce_sender[len]) {
		status = sent(message->tflags, message->queue_id,
			      QMGR_MSG_STATS(&stats, message), recipient,
			      "none", DSN_SIMPLE(&dsn, "2.0.0",
			"undeliverable postmaster notification discarded"));
		if (status == 0) {
		    deliver_completed(message->fp, recipient->offset);
#if 0
		    /* It's the default verification probe sender address. */
		    msg_warn("%s: undeliverable postmaster notification discarded",
			     message->queue_id);
#endif
		} else
		    message->flags |= status;
		continue;
	    }
	}

	/*
	 * Optionally defer deliveries over specific transports, unless the
	 * restriction is lifted temporarily.
	 */
	if (*var_defer_xports && (message->qflags & QMGR_FLUSH_DFXP) == 0) {
	    if (defer_xport_argv == 0)
		defer_xport_argv = argv_split(var_defer_xports, CHARS_COMMA_SP);
	    for (cpp = defer_xport_argv->argv; *cpp; cpp++)
		if (strcmp(*cpp, STR(reply.transport)) == 0)
		    break;
	    if (*cpp) {
		QMGR_REDIRECT(&reply, MAIL_SERVICE_RETRY,
			      "4.3.2 deferred transport");
	    }
	}

	/*
	 * Look up or instantiate the proper transport.
	 */
	if (transport == 0 || !STREQ(transport->name, STR(reply.transport))) {
	    if ((transport = qmgr_transport_find(STR(reply.transport))) == 0)
		transport = qmgr_transport_create(STR(reply.transport));
	    queue = 0;
	}

	/*
	 * This message is being flushed. If need-be unthrottle the
	 * transport.
	 */
	if ((message->qflags & QMGR_FLUSH_EACH) != 0
	    && QMGR_TRANSPORT_THROTTLED(transport))
	    qmgr_transport_unthrottle(transport);

	/*
	 * This transport is dead. Defer delivery to this recipient.
	 */
	if (QMGR_TRANSPORT_THROTTLED(transport)) {
	    saved_dsn = transport->dsn;
	    if ((transport = qmgr_error_transport(MAIL_SERVICE_RETRY)) != 0) {
		nexthop = qmgr_error_nexthop(saved_dsn);
		vstring_strcpy(reply.nexthop, nexthop);
		myfree(nexthop);
		queue = 0;
	    } else {
		qmgr_defer_recipient(message, recipient, saved_dsn);
		continue;
	    }
	}

	/*
	 * The nexthop destination provides the default name for the
	 * per-destination queue. When the delivery agent accepts only one
	 * recipient per delivery, give each recipient its own queue, so that
	 * deliveries to different recipients of the same message can happen
	 * in parallel, and so that we can enforce per-recipient concurrency
	 * limits and prevent one recipient from tying up all the delivery
	 * agent resources. We use recipient@nexthop as queue name rather
	 * than the actual recipient domain name, so that one recipient in
	 * multiple equivalent domains cannot evade the per-recipient
	 * concurrency limit. Split the address on the recipient delimiter if
	 * one is defined, so that extended addresses don't get extra
	 * delivery slots.
	 * 
	 * Fold the result to lower case so that we don't have multiple queues
	 * for the same name.
	 * 
	 * Important! All recipients in a queue must have the same nexthop
	 * value. It is OK to have multiple queues with the same nexthop
	 * value, but only when those queues are named after recipients.
	 * 
	 * The single-recipient code below was written for local(8) like
	 * delivery agents, and assumes that all domains that deliver to the
	 * same (transport + nexthop) are aliases for $nexthop. Delivery
	 * concurrency is changed from per-domain into per-recipient, by
	 * changing the queue name from nexthop into localpart@nexthop.
	 * 
	 * XXX This assumption is incorrect when different destinations share
	 * the same (transport + nexthop). In reality, such transports are
	 * rarely configured to use single-recipient deliveries. The fix is
	 * to decouple the per-destination recipient limit from the
	 * per-destination concurrency.
	 */
	vstring_strcpy(queue_name, STR(reply.nexthop));
	if (strcmp(transport->name, MAIL_SERVICE_ERROR) != 0
	    && strcmp(transport->name, MAIL_SERVICE_RETRY) != 0
	    && transport->recipient_limit == 1) {
	    /* Copy the recipient localpart. */
	    at = strrchr(STR(reply.recipient), '@');
	    len = (at ? (at - STR(reply.recipient))
		   : strlen(STR(reply.recipient)));
	    vstring_strncpy(queue_name, STR(reply.recipient), len);
	    /* Remove the address extension from the recipient localpart. */
	    if (*var_rcpt_delim && split_addr(STR(queue_name), var_rcpt_delim))
		vstring_truncate(queue_name, strlen(STR(queue_name)));
	    /* Assume the recipient domain is equivalent to nexthop. */
	    vstring_sprintf_append(queue_name, "@%s", STR(reply.nexthop));
	}
	lowercase(STR(queue_name));

	/*
	 * This transport is alive. Find or instantiate a queue for this
	 * recipient.
	 */
	if (queue == 0 || !STREQ(queue->name, STR(queue_name))) {
	    if ((queue = qmgr_queue_find(transport, STR(queue_name))) == 0)
		queue = qmgr_queue_create(transport, STR(queue_name),
					  STR(reply.nexthop));
	}

	/*
	 * This message is being flushed. If need-be unthrottle the queue.
	 */
	if ((message->qflags & QMGR_FLUSH_EACH) != 0
	    && QMGR_QUEUE_THROTTLED(queue))
	    qmgr_queue_unthrottle(queue);

	/*
	 * This queue is dead. Defer delivery to this recipient.
	 */
	if (QMGR_QUEUE_THROTTLED(queue)) {
	    saved_dsn = queue->dsn;
	    if ((queue = qmgr_error_queue(MAIL_SERVICE_RETRY, saved_dsn)) == 0) {
		qmgr_defer_recipient(message, recipient, saved_dsn);
		continue;
	    }
	}

	/*
	 * This queue is alive. Bind this recipient to this queue instance.
	 */
	recipient->u.queue = queue;
    }
    resolve_clnt_free(&reply);
    vstring_free(queue_name);
}
Exemple #28
0
static void psc_dnsbl_receive(int event, void *context)
{
    const char *myname = "psc_dnsbl_receive";
    VSTREAM *stream = (VSTREAM *) context;
    PSC_DNSBL_SCORE *score;
    PSC_DNSBL_HEAD *head;
    PSC_DNSBL_SITE *site;
    ARGV   *reply_argv;
    int     request_id;
    int     dnsbl_ttl;

    PSC_CLEAR_EVENT_REQUEST(vstream_fileno(stream), psc_dnsbl_receive, context);

    /*
     * Receive the DNSBL lookup result.
     * 
     * This is preliminary code to explore the field. Later, DNSBL lookup will
     * be handled by an UDP-based DNS client that is built directly into some
     * Postfix daemon.
     * 
     * Don't bother looking up the blocklist score when the client IP address is
     * not listed at the DNSBL.
     * 
     * Don't panic when the blocklist score no longer exists. It may be deleted
     * when the client triggers a "drop" action after pregreet, when the
     * client does not pregreet and the DNSBL reply arrives late, or when the
     * client triggers a "drop" action after hanging up.
     */
    if (event == EVENT_READ
	&& attr_scan(stream,
		     ATTR_FLAG_STRICT,
		     RECV_ATTR_STR(MAIL_ATTR_RBL_DOMAIN, reply_dnsbl),
		     RECV_ATTR_STR(MAIL_ATTR_ACT_CLIENT_ADDR, reply_client),
		     RECV_ATTR_INT(MAIL_ATTR_LABEL, &request_id),
		     RECV_ATTR_STR(MAIL_ATTR_RBL_ADDR, reply_addr),
		     RECV_ATTR_INT(MAIL_ATTR_TTL, &dnsbl_ttl),
		     ATTR_TYPE_END) == 5
	&& (score = (PSC_DNSBL_SCORE *)
	    htable_find(dnsbl_score_cache, STR(reply_client))) != 0
	&& score->request_id == request_id) {

	/*
	 * Run this response past all applicable DNSBL filters and update the
	 * blocklist score for this client IP address.
	 * 
	 * Don't panic when the DNSBL domain name is not found. The DNSBLOG
	 * server may be messed up.
	 */
	if (msg_verbose > 1)
	    msg_info("%s: client=\"%s\" score=%d domain=\"%s\" reply=\"%d %s\"",
		     myname, STR(reply_client), score->total,
		     STR(reply_dnsbl), dnsbl_ttl, STR(reply_addr));
	head = (PSC_DNSBL_HEAD *)
	    htable_find(dnsbl_site_cache, STR(reply_dnsbl));
	if (head == 0) {
	    /* Bogus domain. Do nothing. */
	} else if (*STR(reply_addr) != 0) {
	    /* DNS reputation record(s) found. */
	    reply_argv = 0;
	    for (site = head->first; site != 0; site = site->next) {
		if (site->byte_codes == 0
		    || psc_dnsbl_match(site->byte_codes, reply_argv ? reply_argv :
			 (reply_argv = argv_split(STR(reply_addr), " ")))) {
		    if (score->dnsbl_name == 0
			|| score->dnsbl_weight < site->weight) {
			score->dnsbl_name = head->safe_dnsbl;
			score->dnsbl_weight = site->weight;
		    }
		    score->total += site->weight;
		    if (msg_verbose > 1)
			msg_info("%s: filter=\"%s\" weight=%d score=%d",
			       myname, site->filter ? site->filter : "null",
				 site->weight, score->total);
		}
		/* As with dnsblog(8), a value < 0 means no reply TTL. */
		if (site->weight > 0) {
		    if (score->fail_ttl < 0 || score->fail_ttl > dnsbl_ttl)
			score->fail_ttl = dnsbl_ttl;
		} else {
		    if (score->pass_ttl < 0 || score->pass_ttl > dnsbl_ttl)
			score->pass_ttl = dnsbl_ttl;
		}
	    }
	    if (reply_argv != 0)
		argv_free(reply_argv);
	} else {
	    /* No DNS reputation record found. */
	    for (site = head->first; site != 0; site = site->next) {
		/* As with dnsblog(8), a value < 0 means no reply TTL. */
		if (site->weight > 0) {
		    if (score->pass_ttl < 0 || score->pass_ttl > dnsbl_ttl)
			score->pass_ttl = dnsbl_ttl;
		} else {
		    if (score->fail_ttl < 0 || score->fail_ttl > dnsbl_ttl)
			score->fail_ttl = dnsbl_ttl;
		}
	    }
	}

	/*
	 * Notify the requestor(s) that the result is ready to be picked up.
	 * If this call isn't made, clients have to sit out the entire
	 * pre-handshake delay.
	 */
	score->pending_lookups -= 1;
	if (score->pending_lookups == 0)
	    PSC_CALL_BACK_NOTIFY(score, PSC_NULL_EVENT);
    } else if (event == EVENT_TIME) {
	msg_warn("dnsblog reply timeout %ds for %s",
		 var_psc_dnsbl_tmout, (char *) vstream_context(stream));
    }
    /* Here, score may be a null pointer. */
    vstream_fclose(stream);
}
Exemple #29
0
static int tls_policy_lookup_one(SMTP_SESSION *session, int *site_level,
				         const char *site_name,
				         const char *site_class)
{
    const char *lookup;
    char   *policy;
    char   *saved_policy;
    char   *tok;
    const char *err;
    char   *name;
    char   *val;
    static VSTRING *cbuf;

#undef FREE_RETURN
#define FREE_RETURN(x) do { myfree(saved_policy); return (x); } while (0)

    if ((lookup = maps_find(tls_policy, site_name, 0)) == 0) {
	if (tls_policy->error) {
	    msg_fatal("%s: %s lookup error for %s",
		      session->state->request->queue_id,
		      tls_policy->title, site_name);
	    /* XXX session->stream has no longjmp context yet. */
	}
	return (0);
    }
    if (cbuf == 0)
	cbuf = vstring_alloc(10);

#define WHERE \
    vstring_str(vstring_sprintf(cbuf, "TLS policy table, %s \"%s\"", \
		site_class, site_name))

    saved_policy = policy = mystrdup(lookup);

    if ((tok = mystrtok(&policy, "\t\n\r ,")) == 0) {
	msg_warn("%s: invalid empty policy", WHERE);
	*site_level = TLS_LEV_INVALID;
	FREE_RETURN(1);				/* No further lookups */
    }
    *site_level = tls_level_lookup(tok);
    if (*site_level == TLS_LEV_INVALID) {
	/* tls_level_lookup() logs no warning. */
	msg_warn("%s: invalid security level \"%s\"", WHERE, tok);
	FREE_RETURN(1);				/* No further lookups */
    }

    /*
     * Warn about ignored attributes when TLS is disabled.
     */
    if (*site_level < TLS_LEV_MAY) {
	while ((tok = mystrtok(&policy, "\t\n\r ,")) != 0)
	    msg_warn("%s: ignoring attribute \"%s\" with TLS disabled",
		     WHERE, tok);
	FREE_RETURN(1);
    }

    /*
     * Errors in attributes may have security consequences, don't ignore
     * errors that can degrade security.
     */
    while ((tok = mystrtok(&policy, "\t\n\r ,")) != 0) {
	if ((err = split_nameval(tok, &name, &val)) != 0) {
	    *site_level = TLS_LEV_INVALID;
	    msg_warn("%s: malformed attribute/value pair \"%s\": %s",
		     WHERE, tok, err);
	    break;
	}
	/* Only one instance per policy. */
	if (!strcasecmp(name, "ciphers")) {
	    if (*val == 0) {
		msg_warn("%s: attribute \"%s\" has empty value", WHERE, name);
		*site_level = TLS_LEV_INVALID;
		break;
	    }
	    if (session->tls_grade) {
		msg_warn("%s: attribute \"%s\" is specified multiple times",
			 WHERE, name);
		*site_level = TLS_LEV_INVALID;
		break;
	    }
	    session->tls_grade = mystrdup(val);
	    continue;
	}
	/* Only one instance per policy. */
	if (!strcasecmp(name, "protocols")) {
	    if (session->tls_protocols) {
		msg_warn("%s: attribute \"%s\" is specified multiple times",
			 WHERE, name);
		*site_level = TLS_LEV_INVALID;
		break;
	    }
	    session->tls_protocols = mystrdup(val);
	    continue;
	}
	/* Multiple instance(s) per policy. */
	if (!strcasecmp(name, "match")) {
	    char   *delim = *site_level == TLS_LEV_FPRINT ? "|" : ":";

	    if (*site_level <= TLS_LEV_ENCRYPT) {
		msg_warn("%s: attribute \"%s\" invalid at security level \"%s\"",
			 WHERE, name, policy_name(*site_level));
		*site_level = TLS_LEV_INVALID;
		break;
	    }
	    if (*val == 0) {
		msg_warn("%s: attribute \"%s\" has empty value", WHERE, name);
		*site_level = TLS_LEV_INVALID;
		break;
	    }
	    if (session->tls_matchargv == 0)
		session->tls_matchargv = argv_split(val, delim);
	    else
		argv_split_append(session->tls_matchargv, val, delim);
	    continue;
	}
	/* Only one instance per policy. */
	if (!strcasecmp(name, "exclude")) {
	    if (session->tls_exclusions) {
		msg_warn("%s: attribute \"%s\" is specified multiple times",
			 WHERE, name);
		*site_level = TLS_LEV_INVALID;
		break;
	    }
	    session->tls_exclusions = vstring_strcpy(vstring_alloc(10), val);
	    continue;
	} else {
	    msg_warn("%s: invalid attribute name: \"%s\"", WHERE, name);
	    *site_level = TLS_LEV_INVALID;
	    break;
	}
    }
    FREE_RETURN(1);
}
Exemple #30
0
static void session_tls_init(SMTP_SESSION *session, const char *dest,
			             const char *host, int flags)
{
    const char *myname = "session_tls_init";
    int     global_level;
    int     site_level;

    /*
     * Initialize all TLS related session properties.
     */
    session->tls_context = 0;
    session->tls_nexthop = 0;
    session->tls_level = TLS_LEV_NONE;
    session->tls_retry_plain = 0;
    session->tls_protocols = 0;
    session->tls_grade = 0;
    session->tls_exclusions = 0;
    session->tls_matchargv = 0;

    /*
     * Compute the global TLS policy. This is the default policy level when
     * no per-site policy exists. It also is used to override a wild-card
     * per-site policy.
     */
    if (*var_smtp_tls_level) {
	/* Require that var_smtp_tls_level is sanitized upon startup. */
	global_level = tls_level_lookup(var_smtp_tls_level);
	if (global_level == TLS_LEV_INVALID)
	    msg_panic("%s: invalid TLS security level: \"%s\"",
		      myname, var_smtp_tls_level);
    } else if (var_smtp_enforce_tls) {
	global_level = var_smtp_tls_enforce_peername ?
	    TLS_LEV_VERIFY : TLS_LEV_ENCRYPT;
    } else {
	global_level = var_smtp_use_tls ?
	    TLS_LEV_MAY : TLS_LEV_NONE;
    }
    if (msg_verbose)
	msg_info("%s TLS level: %s", "global", policy_name(global_level));

    /*
     * Compute the per-site TLS enforcement level. For compatibility with the
     * original TLS patch, this algorithm is gives equal precedence to host
     * and next-hop policies.
     */
    site_level = TLS_LEV_NOTFOUND;

    if (tls_policy) {
	tls_policy_lookup(session, &site_level, dest, "next-hop destination");
    } else if (tls_per_site) {
	tls_site_lookup(&site_level, dest, "next-hop destination");
	if (strcasecmp(dest, host) != 0)
	    tls_site_lookup(&site_level, host, "server hostname");
	if (msg_verbose)
	    msg_info("%s TLS level: %s", "site", policy_name(site_level));

	/*
	 * Override a wild-card per-site policy with a more specific global
	 * policy.
	 * 
	 * With the original TLS patch, 1) a per-site ENCRYPT could not override
	 * a global VERIFY, and 2) a combined per-site (NONE+MAY) policy
	 * produced inconsistent results: it changed a global VERIFY into
	 * NONE, while producing MAY with all weaker global policy settings.
	 * 
	 * With the current implementation, a combined per-site (NONE+MAY)
	 * consistently overrides global policy with NONE, and global policy
	 * can override only a per-site MAY wildcard. That is, specific
	 * policies consistently override wildcard policies, and
	 * (non-wildcard) per-site policies consistently override global
	 * policies.
	 */
	if (site_level == TLS_LEV_MAY && global_level > TLS_LEV_MAY)
	    site_level = global_level;
    }
    if (site_level == TLS_LEV_NOTFOUND)
	session->tls_level = global_level;
    else
	session->tls_level = site_level;

    /*
     * Use main.cf protocols setting if not set in per-destination table.
     */
    if (session->tls_level > TLS_LEV_NONE && session->tls_protocols == 0)
	session->tls_protocols =
	    mystrdup((session->tls_level == TLS_LEV_MAY) ?
		     var_smtp_tls_proto : var_smtp_tls_mand_proto);

    /*
     * Compute cipher grade (if set in per-destination table, else
     * set_cipher() uses main.cf settings) and security level dependent
     * cipher exclusion list.
     */
    set_cipher_grade(session);

    /*
     * Use main.cf cert_match setting if not set in per-destination table.
     */
    if (session->tls_matchargv == 0) {
	switch (session->tls_level) {
	case TLS_LEV_INVALID:
	case TLS_LEV_NONE:
	case TLS_LEV_MAY:
	case TLS_LEV_ENCRYPT:
	    break;
	case TLS_LEV_FPRINT:
	    session->tls_matchargv =
		argv_split(var_smtp_tls_fpt_cmatch, "\t\n\r, |");
	    break;
	case TLS_LEV_VERIFY:
	    session->tls_matchargv =
		argv_split(var_smtp_tls_vfy_cmatch, "\t\n\r, :");
	    break;
	case TLS_LEV_SECURE:
	    session->tls_matchargv =
		argv_split(var_smtp_tls_sec_cmatch, "\t\n\r, :");
	    break;
	default:
	    msg_panic("unexpected TLS security level: %d",
		      session->tls_level);
	}
    }
    if (msg_verbose && (tls_policy || tls_per_site))
	msg_info("%s TLS level: %s", "effective",
		 policy_name(session->tls_level));
}