Example #1
0
int     dict_load_file_xt(const char *dict_name, const char *path)
{
    VSTREAM *fp;
    struct stat st;
    time_t  before;
    time_t  after;

    /*
     * Read the file again if it is hot. This may result in reading a partial
     * parameter name when a file changes in the middle of a read.
     */
    for (before = time((time_t *) 0); /* see below */ ; before = after) {
	if ((fp = vstream_fopen(path, O_RDONLY, 0)) == 0)
	    return (0);
	dict_load_fp(dict_name, fp);
	if (fstat(vstream_fileno(fp), &st) < 0)
	    msg_fatal("fstat %s: %m", path);
	if (vstream_ferror(fp) || vstream_fclose(fp))
	    msg_fatal("read %s: %m", path);
	after = time((time_t *) 0);
	if (st.st_mtime < before - 1 || st.st_mtime > after)
	    break;
	if (msg_verbose > 1)
	    msg_info("pausing to let %s cool down", path);
	doze(300000);
    }
    return (1);
}
Example #2
0
void    set_master_ent()
{
    const char *myname = "set_master_ent";
    char   *disable;

    if (master_fp != 0)
	msg_panic("%s: configuration file still open", myname);
    if (master_path == 0)
	msg_panic("%s: no configuration file specified", myname);
    if ((master_fp = vstream_fopen(master_path, O_RDONLY, 0)) == 0)
	msg_fatal("open %s: %m", master_path);
    master_line_last = 0;
    if (master_disable != 0)
	msg_panic("%s: service disable list still exists", myname);
    if (inet_proto_info()->ai_family_list[0] == 0) {
	msg_warn("all network protocols are disabled (%s = %s)",
		 VAR_INET_PROTOCOLS, var_inet_protocols);
	msg_warn("disabling all type \"inet\" services in master.cf");
	disable = concatenate(MASTER_XPORT_NAME_INET, ",",
			      var_master_disable, (char *) 0);
	master_disable = match_service_init(disable);
	myfree(disable);
    } else
	master_disable = match_service_init(var_master_disable);
}
Example #3
0
void    load_file(const char *path, LOAD_FILE_FN action, void *context)
{
    VSTREAM *fp;
    struct stat st;
    time_t  before;
    time_t  after;

    /*
     * Read the file again if it is hot. This may result in reading a partial
     * parameter name or missing end marker when a file changes in the middle
     * of a read.
     */
    for (before = time((time_t *) 0); /* see below */ ; before = after) {
	if ((fp = vstream_fopen(path, O_RDONLY, 0)) == 0)
	    msg_fatal("open %s: %m", path);
	action(fp, context);
	if (fstat(vstream_fileno(fp), &st) < 0)
	    msg_fatal("fstat %s: %m", path);
	if (vstream_ferror(fp) || vstream_fclose(fp))
	    msg_fatal("read %s: %m", path);
	after = time((time_t *) 0);
	if (st.st_mtime < before - 1 || st.st_mtime > after)
	    break;
	if (msg_verbose)
	    msg_info("pausing to let %s cool down", path);
	doze(300000);
    }
}
Example #4
0
void    pcf_read_master(int fail_on_open_error)
{
    const char *myname = "pcf_read_master";
    char   *path;
    VSTRING *buf;
    VSTREAM *fp;
    const char *err;
    int     entry_count = 0;
    int     line_count;
    int     last_line = 0;

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

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

    /*
     * Initialize the in-memory master table.
     */
    pcf_master_table = (PCF_MASTER_ENT *) mymalloc(sizeof(*pcf_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 (readllines(buf, fp, &last_line, &line_count) != 0) {
	    pcf_master_table = (PCF_MASTER_ENT *) myrealloc((void *) pcf_master_table,
			     (entry_count + 2) * sizeof(*pcf_master_table));
	    if ((err = pcf_parse_master_entry(pcf_master_table + entry_count,
					      STR(buf))) != 0)
		msg_fatal("file %s: line %d: %s", path, line_count, err);
	    entry_count += 1;
	}
	vstream_fclose(fp);
	vstring_free(buf);
    }

    /*
     * Null-terminate the master table and clean up.
     */
    pcf_master_table[entry_count].argv = 0;
    myfree(path);
}
Example #5
0
DICT   *dict_cidr_open(const char *mapname, int open_flags, int dict_flags)
{
    DICT_CIDR *dict_cidr;
    VSTREAM *map_fp;
    VSTRING *line_buffer = vstring_alloc(100);
    VSTRING *why = vstring_alloc(100);
    DICT_CIDR_ENTRY *rule;
    DICT_CIDR_ENTRY *last_rule = 0;
    int     lineno = 0;

    /*
     * Sanity checks.
     */
    if (open_flags != O_RDONLY)
	msg_fatal("%s:%s map requires O_RDONLY access mode",
		  DICT_TYPE_CIDR, mapname);

    /*
     * XXX Eliminate unnecessary queries by setting a flag that says "this
     * map matches network addresses only".
     */
    dict_cidr = (DICT_CIDR *) dict_alloc(DICT_TYPE_CIDR, mapname,
					 sizeof(*dict_cidr));
    dict_cidr->dict.lookup = dict_cidr_lookup;
    dict_cidr->dict.close = dict_cidr_close;
    dict_cidr->dict.flags = dict_flags | DICT_FLAG_PATTERN;
    dict_cidr->head = 0;

    if ((map_fp = vstream_fopen(mapname, O_RDONLY, 0)) == 0)
	msg_fatal("open %s: %m", mapname);

    while (readlline(line_buffer, map_fp, &lineno)) {
	rule = dict_cidr_parse_rule(vstring_str(line_buffer), why);
	if (rule == 0) {
	    msg_warn("cidr map %s, line %d: %s: skipping this rule",
		     mapname, lineno, vstring_str(why));
	    continue;
	}
	if (last_rule == 0)
	    dict_cidr->head = rule;
	else
	    last_rule->cidr_info.next = &(rule->cidr_info);
	last_rule = rule;
    }

    /*
     * Clean up.
     */
    if (vstream_fclose(map_fp))
	msg_fatal("cidr map %s: read error: %m", mapname);
    vstring_free(line_buffer);
    vstring_free(why);

    return (DICT_DEBUG (&dict_cidr->dict));
}
Example #6
0
main(void)
{
    VSTRING *vp = vstring_alloc(1);
    VSTREAM *fp;

    if ((fp = vstream_fopen(TEXT_VSTREAM, O_RDONLY, 0)) == 0)
	msg_fatal("open %s: %m", TEXT_VSTREAM);
    while (vstring_fgets(vp, fp))
	vstream_fprintf(VSTREAM_OUT, "%s", vstring_str(vp));
    vstream_fclose(fp);
    vstream_fflush(VSTREAM_OUT);
    vstring_free(vp);
}
Example #7
0
static VSTREAM *safe_open_create(const char *path, int flags, int mode,
	            struct stat * st, uid_t user, uid_t group, VSTRING *why)
{
    VSTREAM *fp;

    /*
     * Create a non-existing file. This relies on O_CREAT | O_EXCL to not
     * follow symbolic links.
     */
    if ((fp = vstream_fopen(path, flags | (O_CREAT | O_EXCL), mode)) == 0) {
	vstring_sprintf(why, "cannot create file exclusively: %m");
	return (0);
    }

    /*
     * Optionally change ownership after creating a new file. If there is a
     * problem we should not attempt to delete the file. Something else may
     * have opened the file in the mean time.
     */
#define CHANGE_OWNER(user, group) (user != (uid_t) -1 || group != (gid_t) -1)

    if (CHANGE_OWNER(user, group)
	&& fchown(vstream_fileno(fp), user, group) < 0) {
	msg_warn("%s: cannot change file ownership: %m", path);
    }

    /*
     * Optionally look up the file attributes.
     */
    if (st != 0 && fstat(vstream_fileno(fp), st) < 0)
	msg_fatal("%s: bad open file status: %m", path);

    /*
     * We are almost there...
     */
    else {
	return (fp);
    }

    /*
     * End up here in case of trouble.
     */
    vstream_fclose(fp);
    return (0);
}
Example #8
0
void mail_conf_checkdir(const char *config_dir)
{
    VSTRING *buf;
    VSTREAM *fp;
    char   *path;
    char   *name;
    char   *value;
    char   *cp;
    int     found = 0;

    /*
     * If running set-[ug]id, require that a non-default configuration
     * directory name is blessed as a bona fide configuration directory in
     * the default main.cf file.
     */
    path = concatenate(DEF_CONFIG_DIR, "/", "main.cf", (char *) 0);
    if ((fp = vstream_fopen(path, O_RDONLY, 0)) == 0)
	msg_fatal("open file %s: %m", path);

    buf = vstring_alloc(1);
    while (found == 0 && readlline(buf, fp, (int *) 0)) {
	if (split_nameval(vstring_str(buf), &name, &value) == 0
	    && (strcmp(name, VAR_CONFIG_DIRS) == 0
		|| strcmp(name, VAR_MULTI_CONF_DIRS) == 0)) {
	    while (found == 0 && (cp = mystrtok(&value, CHARS_COMMA_SP)) != 0)
		if (strcmp(cp, config_dir) == 0)
		    found = 1;
	}
    }
    if (vstream_fclose(fp))
	msg_fatal("read file %s: %m", path);
    vstring_free(buf);

    if (found == 0) {
	msg_error("unauthorized configuration directory name: %s", config_dir);
	msg_fatal("specify \"%s = %s\" or \"%s = %s\" in %s",
		  VAR_CONFIG_DIRS, config_dir,
		  VAR_MULTI_CONF_DIRS, config_dir, path);
    }
    myfree(path);
}
Example #9
0
static void postmap(char *map_type, char *path_name, int postmap_flags,
		            int open_flags, int dict_flags)
{
    VSTREAM *NOCLOBBER source_fp;
    VSTRING *line_buffer;
    MKMAP  *mkmap;
    int     lineno;
    int     last_line;
    char   *key;
    char   *value;
    struct stat st;
    mode_t  saved_mask;

    /*
     * Initialize.
     */
    line_buffer = vstring_alloc(100);
    if ((open_flags & O_TRUNC) == 0) {
	/* Incremental mode. */
	source_fp = VSTREAM_IN;
	vstream_control(source_fp, CA_VSTREAM_CTL_PATH("stdin"), CA_VSTREAM_CTL_END);
    } else {
	/* Create database. */
	if (strcmp(map_type, DICT_TYPE_PROXY) == 0)
	    msg_fatal("can't create maps via the proxy service");
	dict_flags |= DICT_FLAG_BULK_UPDATE;
	if ((source_fp = vstream_fopen(path_name, O_RDONLY, 0)) == 0)
	    msg_fatal("open %s: %m", path_name);
    }
    if (fstat(vstream_fileno(source_fp), &st) < 0)
	msg_fatal("fstat %s: %m", path_name);

    /*
     * Turn off group/other read permissions as indicated in the source file.
     */
    if ((postmap_flags & POSTMAP_FLAG_SAVE_PERM) && S_ISREG(st.st_mode))
	saved_mask = umask(022 | (~st.st_mode & 077));

    /*
     * If running as root, run as the owner of the source file, so that the
     * result shows proper ownership, and so that a bug in postmap does not
     * allow privilege escalation.
     */
    if ((postmap_flags & POSTMAP_FLAG_AS_OWNER) && getuid() == 0
	&& (st.st_uid != geteuid() || st.st_gid != getegid()))
	set_eugid(st.st_uid, st.st_gid);

    /*
     * Open the database, optionally create it when it does not exist,
     * optionally truncate it when it does exist, and lock out any
     * spectators.
     */
    mkmap = mkmap_open(map_type, path_name, open_flags, dict_flags);

    /*
     * And restore the umask, in case it matters.
     */
    if ((postmap_flags & POSTMAP_FLAG_SAVE_PERM) && S_ISREG(st.st_mode))
	umask(saved_mask);

    /*
     * Trap "exceptions" so that we can restart a bulk-mode update after a
     * recoverable error.
     */
    for (;;) {
	if (dict_isjmp(mkmap->dict) != 0
	    && dict_setjmp(mkmap->dict) != 0
	    && vstream_fseek(source_fp, SEEK_SET, 0) < 0)
	    msg_fatal("seek %s: %m", VSTREAM_PATH(source_fp));

	/*
	 * Add records to the database.
	 */
	last_line = 0;
	while (readllines(line_buffer, source_fp, &last_line, &lineno)) {

	    /*
	     * First some UTF-8 checks sans casefolding.
	     */
	    if ((mkmap->dict->flags & DICT_FLAG_UTF8_ACTIVE)
		&& !allascii(STR(line_buffer))
		&& !valid_utf8_string(STR(line_buffer), LEN(line_buffer))) {
		msg_warn("%s, line %d: non-UTF-8 input \"%s\""
			 " -- ignoring this line",
			 VSTREAM_PATH(source_fp), lineno, STR(line_buffer));
		continue;
	    }

	    /*
	     * Split on the first whitespace character, then trim leading and
	     * trailing whitespace from key and value.
	     */
	    key = STR(line_buffer);
	    value = key + strcspn(key, CHARS_SPACE);
	    if (*value)
		*value++ = 0;
	    while (ISSPACE(*value))
		value++;
	    trimblanks(key, 0)[0] = 0;
	    trimblanks(value, 0)[0] = 0;

	    /*
	     * Enforce the "key whitespace value" format. Disallow missing
	     * keys or missing values.
	     */
	    if (*key == 0 || *value == 0) {
		msg_warn("%s, line %d: expected format: key whitespace value",
			 VSTREAM_PATH(source_fp), lineno);
		continue;
	    }
	    if (key[strlen(key) - 1] == ':')
		msg_warn("%s, line %d: record is in \"key: value\" format; is this an alias file?",
			 VSTREAM_PATH(source_fp), lineno);

	    /*
	     * Store the value under a case-insensitive key.
	     */
	    mkmap_append(mkmap, key, value);
	    if (mkmap->dict->error)
		msg_fatal("table %s:%s: write error: %m",
			  mkmap->dict->type, mkmap->dict->name);
	}
	break;
    }

    /*
     * Close the mapping database, and release the lock.
     */
    mkmap_close(mkmap);

    /*
     * Cleanup. We're about to terminate, but it is a good sanity check.
     */
    vstring_free(line_buffer);
    if (source_fp != VSTREAM_IN)
	vstream_fclose(source_fp);
}
Example #10
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);
}
Example #11
0
int     main(int argc, char **argv)
{
    SESSION *session;
    char   *host;
    char   *port;
    char   *path;
    int     path_len;
    int     sessions = 1;
    int     ch;
    int     i;
    char   *buf;
    const char *parse_err;
    struct addrinfo *res;
    int     aierr;
    const char *protocols = INET_PROTO_NAME_ALL;
    INET_PROTO_INFO *proto_info;
    char   *message_file = 0;

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

    signal(SIGPIPE, SIG_IGN);
    msg_vstream_init(argv[0], VSTREAM_ERR);

    /*
     * Parse JCL.
     */
    while ((ch = GETOPT(argc, argv, "46AcC:df:F:l:Lm:M:Nor:R:s:S:t:T:vw:")) > 0) {
        switch (ch) {
        case '4':
            protocols = INET_PROTO_NAME_IPV4;
            break;
        case '6':
            protocols = INET_PROTO_NAME_IPV6;
            break;
        case 'A':
            allow_reject = 1;
            break;
        case 'c':
            count++;
            break;
        case 'C':
            if ((connect_count = atoi(optarg)) <= 0)
                msg_fatal("bad connection count: %s", optarg);
            break;
        case 'd':
            disconnect = 0;
            break;
        case 'f':
            sender = optarg;
            break;
        case 'F':
            if (message_file == 0 && message_length > 0)
                msg_fatal("-l option cannot be used with -F");
            message_file = optarg;
            break;
        case 'l':
            if (message_file != 0)
                msg_fatal("-l option cannot be used with -F");
            if ((message_length = atoi(optarg)) <= 0)
                msg_fatal("bad message length: %s", optarg);
            break;
        case 'L':
            talk_lmtp = 1;
            break;
        case 'm':
            if ((message_count = atoi(optarg)) <= 0)
                msg_fatal("bad message count: %s", optarg);
            break;
        case 'M':
            if (*optarg == '[') {
                if (!valid_mailhost_literal(optarg, DO_GRIPE))
                    msg_fatal("bad address literal: %s", optarg);
            } else {
                if (!valid_hostname(optarg, DO_GRIPE))
                    msg_fatal("bad hostname: %s", optarg);
            }
            var_myhostname = optarg;
            break;
        case 'N':
            number_rcpts = 1;
            break;
        case 'o':
            send_helo_first = 0;
            send_headers = 0;
            break;
        case 'r':
            if ((recipients = atoi(optarg)) <= 0)
                msg_fatal("bad recipient count: %s", optarg);
            break;
        case 'R':
            if (fixed_delay > 0)
                msg_fatal("do not use -w and -R options at the same time");
            if ((random_delay = atoi(optarg)) <= 0)
                msg_fatal("bad random delay: %s", optarg);
            break;
        case 's':
            if ((sessions = atoi(optarg)) <= 0)
                msg_fatal("bad session count: %s", optarg);
            break;
        case 'S':
            subject = optarg;
            break;
        case 't':
            recipient = optarg;
            break;
        case 'T':
            if ((inet_windowsize = atoi(optarg)) <= 0)
                msg_fatal("bad TCP window size: %s", optarg);
            break;
        case 'v':
            msg_verbose++;
            break;
        case 'w':
            if (random_delay > 0)
                msg_fatal("do not use -w and -R options at the same time");
            if ((fixed_delay = atoi(optarg)) <= 0)
                msg_fatal("bad fixed delay: %s", optarg);
            break;
        default:
            usage(argv[0]);
        }
    }
    if (argc - optind != 1)
        usage(argv[0]);

    if (random_delay > 0)
        srand(getpid());

    /*
     * Initialize the message content, SMTP encoded. smtp_fputs() will append
     * another \r\n but we don't care.
     */
    if (message_file != 0) {
        VSTREAM *fp;
        VSTRING *buf = vstring_alloc(100);
        VSTRING *msg = vstring_alloc(100);

        if ((fp = vstream_fopen(message_file, O_RDONLY, 0)) == 0)
            msg_fatal("open %s: %m", message_file);
        while (vstring_get_nonl(buf, fp) != VSTREAM_EOF) {
            if (*vstring_str(buf) == '.')
                VSTRING_ADDCH(msg, '.');
            vstring_memcat(msg, vstring_str(buf), VSTRING_LEN(buf));
            vstring_memcat(msg, "\r\n", 2);
        }
        if (vstream_ferror(fp))
            msg_fatal("read %s: %m", message_file);
        vstream_fclose(fp);
        vstring_free(buf);
        message_length = VSTRING_LEN(msg);
        message_data = vstring_export(msg);
        send_headers = 0;
    } else if (message_length > 0) {
        message_data = mymalloc(message_length);
        memset(message_data, 'X', message_length);
        for (i = 80; i < message_length; i += 80) {
            message_data[i - 80] = "0123456789"[(i / 80) % 10];
            message_data[i - 2] = '\r';
            message_data[i - 1] = '\n';
        }
    }

    /*
     * Translate endpoint address to internal form.
     */
    proto_info = inet_proto_init("protocols", protocols);
    if (strncmp(argv[optind], "unix:", 5) == 0) {
        path = argv[optind] + 5;
        path_len = strlen(path);
        if (path_len >= (int) sizeof(sun.sun_path))
            msg_fatal("unix-domain name too long: %s", path);
        memset((char *) &sun, 0, sizeof(sun));
        sun.sun_family = AF_UNIX;
#ifdef HAS_SUN_LEN
        sun.sun_len = path_len + 1;
#endif
        memcpy(sun.sun_path, path, path_len);
        sa = (struct sockaddr *) & sun;
        sa_length = sizeof(sun);
    } else {
        if (strncmp(argv[optind], "inet:", 5) == 0)
            argv[optind] += 5;
        buf = mystrdup(argv[optind]);
        if ((parse_err = host_port(buf, &host, (char *) 0, &port, "smtp")) != 0)
            msg_fatal("%s: %s", argv[optind], parse_err);
        if ((aierr = hostname_to_sockaddr(host, port, SOCK_STREAM, &res)) != 0)
            msg_fatal("%s: %s", argv[optind], MAI_STRERROR(aierr));
        myfree(buf);
        sa = (struct sockaddr *) & ss;
        if (res->ai_addrlen > sizeof(ss))
            msg_fatal("address length %d > buffer length %d",
                      (int) res->ai_addrlen, (int) sizeof(ss));
        memcpy((char *) sa, res->ai_addr, res->ai_addrlen);
        sa_length = res->ai_addrlen;
#ifdef HAS_SA_LEN
        sa->sa_len = sa_length;
#endif
        freeaddrinfo(res);
    }

    /*
     * Make sure the SMTP server cannot run us out of memory by sending
     * never-ending lines of text.
     */
    if (buffer == 0) {
        buffer = vstring_alloc(100);
        vstring_ctl(buffer, VSTRING_CTL_MAXLEN, (ssize_t) var_line_limit, 0);
    }

    /*
     * Make sure we have sender and recipient addresses.
     */
    if (var_myhostname == 0)
        var_myhostname = get_hostname();
    if (sender == 0 || recipient == 0) {
        vstring_sprintf(buffer, "foo@%s", var_myhostname);
        defaddr = mystrdup(vstring_str(buffer));
        if (sender == 0)
            sender = defaddr;
        if (recipient == 0)
            recipient = defaddr;
    }

    /*
     * Start sessions.
     */
    while (sessions-- > 0) {
        session = (SESSION *) mymalloc(sizeof(*session));
        session->stream = 0;
        session->xfer_count = 0;
        session->connect_count = connect_count;
        session->next = 0;
        session_count++;
        startup(session);
    }
    for (;;) {
        event_loop(-1);
        if (session_count <= 0 && message_count <= 0) {
            if (count) {
                VSTREAM_PUTC('\n', VSTREAM_OUT);
                vstream_fflush(VSTREAM_OUT);
            }
            exit(0);
        }
    }
}
Example #12
0
static void postmap(char *map_type, char *path_name, int postmap_flags,
		            int open_flags, int dict_flags)
{
    VSTREAM *source_fp;
    VSTRING *line_buffer;
    MKMAP  *mkmap;
    int     lineno;
    char   *key;
    char   *value;
    struct stat st;
    mode_t  saved_mask;

    /*
     * Initialize.
     */
    line_buffer = vstring_alloc(100);
    if ((open_flags & O_TRUNC) == 0) {
	source_fp = VSTREAM_IN;
	vstream_control(source_fp, VSTREAM_CTL_PATH, "stdin", VSTREAM_CTL_END);
    } else if (strcmp(map_type, DICT_TYPE_PROXY) == 0) {
	msg_fatal("can't create maps via the proxy service");
    } else if ((source_fp = vstream_fopen(path_name, O_RDONLY, 0)) == 0) {
	msg_fatal("open %s: %m", path_name);
    }
    if (fstat(vstream_fileno(source_fp), &st) < 0)
	msg_fatal("fstat %s: %m", path_name);

    /*
     * Turn off group/other read permissions as indicated in the source file.
     */
    if ((postmap_flags & POSTMAP_FLAG_SAVE_PERM) && S_ISREG(st.st_mode))
	saved_mask = umask(022 | (~st.st_mode & 077));

    /*
     * If running as root, run as the owner of the source file, so that the
     * result shows proper ownership, and so that a bug in postmap does not
     * allow privilege escalation.
     */
    if ((postmap_flags & POSTMAP_FLAG_AS_OWNER) && getuid() == 0
	&& (st.st_uid != geteuid() || st.st_gid != getegid()))
	set_eugid(st.st_uid, st.st_gid);

    /*
     * Open the database, optionally create it when it does not exist,
     * optionally truncate it when it does exist, and lock out any
     * spectators.
     */
    mkmap = mkmap_open(map_type, path_name, open_flags, dict_flags);

    /*
     * And restore the umask, in case it matters.
     */
    if ((postmap_flags & POSTMAP_FLAG_SAVE_PERM) && S_ISREG(st.st_mode))
	umask(saved_mask);

    /*
     * Add records to the database.
     */
    lineno = 0;
    while (readlline(line_buffer, source_fp, &lineno)) {

	/*
	 * Split on the first whitespace character, then trim leading and
	 * trailing whitespace from key and value.
	 */
	key = STR(line_buffer);
	value = key + strcspn(key, " \t\r\n");
	if (*value)
	    *value++ = 0;
	while (ISSPACE(*value))
	    value++;
	trimblanks(key, 0)[0] = 0;
	trimblanks(value, 0)[0] = 0;

	/*
	 * Enforce the "key whitespace value" format. Disallow missing keys
	 * or missing values.
	 */
	if (*key == 0 || *value == 0) {
	    msg_warn("%s, line %d: expected format: key whitespace value",
		     VSTREAM_PATH(source_fp), lineno);
	    continue;
	}
	if (key[strlen(key) - 1] == ':')
	    msg_warn("%s, line %d: record is in \"key: value\" format; is this an alias file?",
		     VSTREAM_PATH(source_fp), lineno);

	/*
	 * Store the value under a case-insensitive key.
	 */
	mkmap_append(mkmap, key, value);
	if (mkmap->dict->error)
	    msg_fatal("table %s:%s: write error: %m",
		      mkmap->dict->type, mkmap->dict->name);
    }

    /*
     * Close the mapping database, and release the lock.
     */
    mkmap_close(mkmap);

    /*
     * Cleanup. We're about to terminate, but it is a good sanity check.
     */
    vstring_free(line_buffer);
    if (source_fp != VSTREAM_IN)
	vstream_fclose(source_fp);
}
Example #13
0
void    pcf_edit_master(int mode, int argc, char **argv)
{
    const char *myname = "pcf_edit_master";
    char   *path;
    EDIT_FILE *ep;
    VSTREAM *src;
    VSTREAM *dst;
    VSTRING *line_buf = vstring_alloc(100);
    VSTRING *parse_buf = vstring_alloc(100);
    int     lineno;
    PCF_MASTER_ENT *new_entry;
    VSTRING *full_entry_buf = vstring_alloc(100);
    char   *cp;
    char   *pattern;
    int     service_name_type_matched;
    const char *err;
    PCF_MASTER_EDIT_REQ *edit_reqs;
    PCF_MASTER_EDIT_REQ *req;
    int     num_reqs = argc;
    const char *edit_opts = "-Me, -Fe, -Pe, -X, or -#";
    char   *service_name;
    char   *service_type;

    /*
     * Sanity check.
     */
    if (num_reqs <= 0)
	msg_panic("%s: empty argument list", myname);

    /*
     * Preprocessing: split pattern=value, then split the pattern components.
     */
    edit_reqs = (PCF_MASTER_EDIT_REQ *) mymalloc(sizeof(*edit_reqs) * num_reqs);
    for (req = edit_reqs; *argv != 0; req++, argv++) {
	req->match_count = 0;
	req->raw_text = *argv;
	cp = req->parsed_text = mystrdup(req->raw_text);
	if (strchr(cp, '\n') != 0)
	    msg_fatal("%s accept no multi-line input", edit_opts);
	while (ISSPACE(*cp))
	    cp++;
	if (*cp == '#')
	    msg_fatal("%s accept no comment input", edit_opts);
	/* Separate the pattern from the value. */
	if (mode & PCF_EDIT_CONF) {
	    if ((err = split_nameval(cp, &pattern, &req->edit_value)) != 0)
		msg_fatal("%s: \"%s\"", err, req->raw_text);
	    if ((mode & PCF_MASTER_PARAM)
	    && req->edit_value[strcspn(req->edit_value, PCF_MASTER_BLANKS)])
		msg_fatal("whitespace in parameter value: \"%s\"",
			  req->raw_text);
	} else if (mode & (PCF_COMMENT_OUT | PCF_EDIT_EXCL)) {
	    if (strchr(cp, '=') != 0)
		msg_fatal("-X or -# requires names without value");
	    pattern = cp;
	    trimblanks(pattern, 0);
	    req->edit_value = 0;
	} else {
	    msg_panic("%s: unknown mode %d", myname, mode);
	}

#define PCF_MASTER_MASK (PCF_MASTER_ENTRY | PCF_MASTER_FLD | PCF_MASTER_PARAM)

	/*
	 * Split name/type or name/type/whatever pattern into components.
	 */
	switch (mode & PCF_MASTER_MASK) {
	case PCF_MASTER_ENTRY:
	    if ((req->service_pattern =
		 pcf_parse_service_pattern(pattern, 2, 2)) == 0)
		msg_fatal("-Me, -MX or -M# requires service_name/type");
	    break;
	case PCF_MASTER_FLD:
	    if ((req->service_pattern =
		 pcf_parse_service_pattern(pattern, 3, 3)) == 0)
		msg_fatal("-Fe or -FX requires service_name/type/field_name");
	    req->field_number =
		pcf_parse_field_pattern(req->service_pattern->argv[2]);
	    if (pcf_is_magic_field_pattern(req->field_number))
		msg_fatal("-Fe does not accept wild-card field name");
	    if ((mode & PCF_EDIT_CONF)
		&& req->field_number < PCF_MASTER_FLD_CMD
	    && req->edit_value[strcspn(req->edit_value, PCF_MASTER_BLANKS)])
		msg_fatal("-Fe does not accept whitespace in non-command field");
	    break;
	case PCF_MASTER_PARAM:
	    if ((req->service_pattern =
		 pcf_parse_service_pattern(pattern, 3, 3)) == 0)
		msg_fatal("-Pe or -PX requires service_name/type/parameter");
	    req->param_pattern = req->service_pattern->argv[2];
	    if (PCF_IS_MAGIC_PARAM_PATTERN(req->param_pattern))
		msg_fatal("-Pe does not accept wild-card parameter name");
	    if ((mode & PCF_EDIT_CONF)
	    && req->edit_value[strcspn(req->edit_value, PCF_MASTER_BLANKS)])
		msg_fatal("-Pe does not accept whitespace in parameter value");
	    break;
	default:
	    msg_panic("%s: unknown edit mode %d", myname, mode);
	}
    }

    /*
     * Open a temp file for the result. This uses a deterministic name so we
     * don't leave behind thrash with random names.
     */
    pcf_set_config_dir();
    path = concatenate(var_config_dir, "/", MASTER_CONF_FILE, (char *) 0);
    if ((ep = edit_file_open(path, O_CREAT | O_WRONLY, 0644)) == 0)
	msg_fatal("open %s%s: %m", path, EDIT_FILE_SUFFIX);
    dst = ep->tmp_fp;

    /*
     * Open the original file for input.
     */
    if ((src = vstream_fopen(path, O_RDONLY, 0)) == 0) {
	/* OK to delete, since we control the temp file name exclusively. */
	(void) unlink(ep->tmp_path);
	msg_fatal("open %s for reading: %m", path);
    }

    /*
     * Copy original file to temp file, while replacing service entries on
     * the fly.
     */
    service_name_type_matched = 0;
    new_entry = 0;
    lineno = 0;
    while ((cp = pcf_next_cf_line(parse_buf, src, dst, &lineno)) != 0) {
	vstring_strcpy(line_buf, STR(parse_buf));

	/*
	 * Copy, skip or replace continued text.
	 */
	if (cp > STR(parse_buf)) {
	    if (service_name_type_matched == 0)
		vstream_fputs(STR(line_buf), dst);
	    else if (mode & PCF_COMMENT_OUT)
		vstream_fprintf(dst, "#%s", STR(line_buf));
	}

	/*
	 * Copy or replace (start of) logical line.
	 */
	else {
	    service_name_type_matched = 0;

	    /*
	     * Parse out the service name and type.
	     */
	    if ((service_name = mystrtok(&cp, PCF_MASTER_BLANKS)) == 0
		|| (service_type = mystrtok(&cp, PCF_MASTER_BLANKS)) == 0)
		msg_fatal("file %s: line %d: specify service name and type "
			  "on the same line", path, lineno);
	    if (strchr(service_name, '='))
		msg_fatal("file %s: line %d: service name syntax \"%s\" is "
			  "unsupported with %s", path, lineno, service_name,
			  edit_opts);
	    if (service_type[strcspn(service_type, "=/")] != 0)
		msg_fatal("file %s: line %d: "
			"service type syntax \"%s\" is unsupported with %s",
			  path, lineno, service_type, edit_opts);

	    /*
	     * Match each service pattern.
	     */
	    for (req = edit_reqs; req < edit_reqs + num_reqs; req++) {
		if (PCF_MATCH_SERVICE_PATTERN(req->service_pattern,
					      service_name,
					      service_type)) {
		    service_name_type_matched = 1;	/* Sticky flag */
		    req->match_count += 1;

		    /*
		     * Generate replacement master.cf entries.
		     */
		    if ((mode & PCF_EDIT_CONF)
			|| ((mode & PCF_MASTER_PARAM) && (mode & PCF_EDIT_EXCL))) {
			switch (mode & PCF_MASTER_MASK) {

			    /*
			     * Replace master.cf entry field or parameter
			     * value.
			     */
			case PCF_MASTER_FLD:
			case PCF_MASTER_PARAM:
			    if (new_entry == 0) {
				/* Gobble up any continuation lines. */
				pcf_gobble_cf_line(full_entry_buf, line_buf,
						   src, dst, &lineno);
				new_entry = (PCF_MASTER_ENT *)
				    mymalloc(sizeof(*new_entry));
				if ((err = pcf_parse_master_entry(new_entry,
						 STR(full_entry_buf))) != 0)
				    msg_fatal("file %s: line %d: %s",
					      path, lineno, err);
			    }
			    if (mode & PCF_MASTER_FLD) {
				pcf_edit_master_field(new_entry,
						      req->field_number,
						      req->edit_value);
			    } else {
				pcf_edit_master_param(new_entry, mode,
						      req->param_pattern,
						      req->edit_value);
			    }
			    break;

			    /*
			     * Replace entire master.cf entry.
			     */
			case PCF_MASTER_ENTRY:
			    if (new_entry != 0)
				pcf_free_master_entry(new_entry);
			    new_entry = (PCF_MASTER_ENT *)
				mymalloc(sizeof(*new_entry));
			    if ((err = pcf_parse_master_entry(new_entry,
						     req->edit_value)) != 0)
				msg_fatal("%s: \"%s\"", err, req->raw_text);
			    break;
			default:
			    msg_panic("%s: unknown edit mode %d", myname, mode);
			}
		    }
		}
	    }

	    /*
	     * Pass through or replace the current input line.
	     */
	    if (new_entry) {
		pcf_print_master_entry(dst, PCF_FOLD_LINE, new_entry);
		pcf_free_master_entry(new_entry);
		new_entry = 0;
	    } else if (service_name_type_matched == 0) {
		vstream_fputs(STR(line_buf), dst);
	    } else if (mode & PCF_COMMENT_OUT) {
		vstream_fprintf(dst, "#%s", STR(line_buf));
	    }
	}
    }

    /*
     * Postprocessing: when editing entire service entries, generate new
     * entries for services not found. Otherwise (editing fields or
     * parameters), "service not found" is a fatal error.
     */
    for (req = edit_reqs; req < edit_reqs + num_reqs; req++) {
	if (req->match_count == 0) {
	    if ((mode & PCF_MASTER_ENTRY) && (mode & PCF_EDIT_CONF)) {
		new_entry = (PCF_MASTER_ENT *) mymalloc(sizeof(*new_entry));
		if ((err = pcf_parse_master_entry(new_entry, req->edit_value)) != 0)
		    msg_fatal("%s: \"%s\"", err, req->raw_text);
		pcf_print_master_entry(dst, PCF_FOLD_LINE, new_entry);
		pcf_free_master_entry(new_entry);
	    } else if ((mode & PCF_MASTER_ENTRY) == 0) {
		msg_warn("unmatched service_name/type: \"%s\"", req->raw_text);
	    }
	}
    }

    /*
     * When all is well, rename the temp file to the original one.
     */
    if (vstream_fclose(src))
	msg_fatal("read %s: %m", path);
    if (edit_file_close(ep) != 0)
	msg_fatal("close %s%s: %m", path, EDIT_FILE_SUFFIX);

    /*
     * Cleanup.
     */
    myfree(path);
    vstring_free(line_buf);
    vstring_free(parse_buf);
    vstring_free(full_entry_buf);
    for (req = edit_reqs; req < edit_reqs + num_reqs; req++) {
	argv_free(req->service_pattern);
	myfree(req->parsed_text);
    }
    myfree((char *) edit_reqs);
}
Example #14
0
static void postalias(char *map_type, char *path_name, int postalias_flags,
		              int open_flags, int dict_flags)
{
    VSTREAM *NOCLOBBER source_fp;
    VSTRING *line_buffer;
    MKMAP  *mkmap;
    int     lineno;
    int     last_line;
    VSTRING *key_buffer;
    VSTRING *value_buffer;
    TOK822 *tok_list;
    TOK822 *key_list;
    TOK822 *colon;
    TOK822 *value_list;
    struct stat st;
    mode_t  saved_mask;

    /*
     * Initialize.
     */
    line_buffer = vstring_alloc(100);
    key_buffer = vstring_alloc(100);
    value_buffer = vstring_alloc(100);
    if ((open_flags & O_TRUNC) == 0) {
	/* Incremental mode. */
	source_fp = VSTREAM_IN;
	vstream_control(source_fp, CA_VSTREAM_CTL_PATH("stdin"), CA_VSTREAM_CTL_END);
    } else {
	/* Create database. */
	if (strcmp(map_type, DICT_TYPE_PROXY) == 0)
	    msg_fatal("can't create maps via the proxy service");
	dict_flags |= DICT_FLAG_BULK_UPDATE;
	if ((source_fp = vstream_fopen(path_name, O_RDONLY, 0)) == 0)
	    msg_fatal("open %s: %m", path_name);
    }
    if (fstat(vstream_fileno(source_fp), &st) < 0)
	msg_fatal("fstat %s: %m", path_name);

    /*
     * Turn off group/other read permissions as indicated in the source file.
     */
    if ((postalias_flags & POSTALIAS_FLAG_SAVE_PERM) && S_ISREG(st.st_mode))
	saved_mask = umask(022 | (~st.st_mode & 077));

    /*
     * If running as root, run as the owner of the source file, so that the
     * result shows proper ownership, and so that a bug in postalias does not
     * allow privilege escalation.
     */
    if ((postalias_flags & POSTALIAS_FLAG_AS_OWNER) && getuid() == 0
	&& (st.st_uid != geteuid() || st.st_gid != getegid()))
	set_eugid(st.st_uid, st.st_gid);

    /*
     * Open the database, create it when it does not exist, truncate it when
     * it does exist, and lock out any spectators.
     */
    mkmap = mkmap_open(map_type, path_name, open_flags, dict_flags);

    /*
     * And restore the umask, in case it matters.
     */
    if ((postalias_flags & POSTALIAS_FLAG_SAVE_PERM) && S_ISREG(st.st_mode))
	umask(saved_mask);

    /*
     * Trap "exceptions" so that we can restart a bulk-mode update after a
     * recoverable error.
     */
    for (;;) {
	if (dict_isjmp(mkmap->dict) != 0
	    && dict_setjmp(mkmap->dict) != 0
	    && vstream_fseek(source_fp, SEEK_SET, 0) < 0)
	    msg_fatal("seek %s: %m", VSTREAM_PATH(source_fp));

	/*
	 * Add records to the database.
	 */
	last_line = 0;
	while (readllines(line_buffer, source_fp, &last_line, &lineno)) {

	    /*
	     * First some UTF-8 checks sans casefolding.
	     */
	    if ((mkmap->dict->flags & DICT_FLAG_UTF8_ACTIVE)
		&& !allascii(STR(line_buffer))
		&& !valid_utf8_string(STR(line_buffer), LEN(line_buffer))) {
		msg_warn("%s, line %d: non-UTF-8 input \"%s\""
			 " -- ignoring this line",
			 VSTREAM_PATH(source_fp), lineno, STR(line_buffer));
		continue;
	    }

	    /*
	     * Tokenize the input, so that we do the right thing when a
	     * quoted localpart contains special characters such as "@", ":"
	     * and so on.
	     */
	    if ((tok_list = tok822_scan(STR(line_buffer), (TOK822 **) 0)) == 0)
		continue;

	    /*
	     * Enforce the key:value format. Disallow missing keys,
	     * multi-address keys, or missing values. In order to specify an
	     * empty string or value, enclose it in double quotes.
	     */
	    if ((colon = tok822_find_type(tok_list, ':')) == 0
		|| colon->prev == 0 || colon->next == 0
		|| tok822_rfind_type(colon, ',')) {
		msg_warn("%s, line %d: need name:value pair",
			 VSTREAM_PATH(source_fp), lineno);
		tok822_free_tree(tok_list);
		continue;
	    }

	    /*
	     * Key must be local. XXX We should use the Postfix rewriting and
	     * resolving services to handle all address forms correctly.
	     * However, we can't count on the mail system being up when the
	     * alias database is being built, so we're guessing a bit.
	     */
	    if (tok822_rfind_type(colon, '@') || tok822_rfind_type(colon, '%')) {
		msg_warn("%s, line %d: name must be local",
			 VSTREAM_PATH(source_fp), lineno);
		tok822_free_tree(tok_list);
		continue;
	    }

	    /*
	     * Split the input into key and value parts, and convert from
	     * token representation back to string representation. Convert
	     * the key to internal (unquoted) form, because the resolver
	     * produces addresses in internal form. Convert the value to
	     * external (quoted) form, because it will have to be re-parsed
	     * upon lookup. Discard the token representation when done.
	     */
	    key_list = tok_list;
	    tok_list = 0;
	    value_list = tok822_cut_after(colon);
	    tok822_unlink(colon);
	    tok822_free(colon);

	    tok822_internalize(key_buffer, key_list, TOK822_STR_DEFL);
	    tok822_free_tree(key_list);

	    tok822_externalize(value_buffer, value_list, TOK822_STR_DEFL);
	    tok822_free_tree(value_list);

	    /*
	     * Store the value under a case-insensitive key.
	     */
	    mkmap_append(mkmap, STR(key_buffer), STR(value_buffer));
	    if (mkmap->dict->error)
		msg_fatal("table %s:%s: write error: %m",
			  mkmap->dict->type, mkmap->dict->name);
	}
	break;
    }

    /*
     * Update or append sendmail and NIS signatures.
     */
    if ((open_flags & O_TRUNC) == 0)
	mkmap->dict->flags |= DICT_FLAG_DUP_REPLACE;

    /*
     * Sendmail compatibility: add the @:@ signature to indicate that the
     * database is complete. This might be needed by NIS clients running
     * sendmail.
     */
    mkmap_append(mkmap, "@", "@");
    if (mkmap->dict->error)
	msg_fatal("table %s:%s: write error: %m",
		  mkmap->dict->type, mkmap->dict->name);

    /*
     * NIS compatibility: add time and master info. Unlike other information,
     * this information MUST be written without a trailing null appended to
     * key or value.
     */
    mkmap->dict->flags &= ~DICT_FLAG_TRY1NULL;
    mkmap->dict->flags |= DICT_FLAG_TRY0NULL;
    vstring_sprintf(value_buffer, "%010ld", (long) time((time_t *) 0));
#if (defined(HAS_NIS) || defined(HAS_NISPLUS))
    mkmap->dict->flags &= ~DICT_FLAG_FOLD_FIX;
    mkmap_append(mkmap, "YP_LAST_MODIFIED", STR(value_buffer));
    mkmap_append(mkmap, "YP_MASTER_NAME", var_myhostname);
#endif

    /*
     * Close the alias database, and release the lock.
     */
    mkmap_close(mkmap);

    /*
     * Cleanup. We're about to terminate, but it is a good sanity check.
     */
    vstring_free(value_buffer);
    vstring_free(key_buffer);
    vstring_free(line_buffer);
    if (source_fp != VSTREAM_IN)
	vstream_fclose(source_fp);
}
Example #15
0
int     deliver_maildir(LOCAL_STATE state, USER_ATTR usr_attr)
{
    const char *myname = "deliver_maildir";
    char   *newdir;
    char   *tmpdir;
    char   *curdir;
    char   *tmpfile;
    char   *newfile;
    DSN_BUF *why = state.msg_attr.why;
    VSTRING *buf;
    VSTREAM *dst;
    int     mail_copy_status;
    int     deliver_status;
    int     copy_flags;
    struct stat st;
    struct timeval starttime;

    GETTIMEOFDAY(&starttime);

    /*
     * Make verbose logging easier to understand.
     */
    state.level++;
    if (msg_verbose)
	MSG_LOG_STATE(myname, state);

    /*
     * Don't deliver trace-only requests.
     */
    if (DEL_REQ_TRACE_ONLY(state.request->flags)) {
	dsb_simple(why, "2.0.0", "delivers to maildir");
	return (sent(BOUNCE_FLAGS(state.request),
		     SENT_ATTR(state.msg_attr)));
    }

    /*
     * Initialize. Assume the operation will fail. Set the delivered
     * attribute to reflect the final recipient.
     */
    if (vstream_fseek(state.msg_attr.fp, state.msg_attr.offset, SEEK_SET) < 0)
	msg_fatal("seek message file %s: %m", VSTREAM_PATH(state.msg_attr.fp));
    state.msg_attr.delivered = state.msg_attr.rcpt.address;
    mail_copy_status = MAIL_COPY_STAT_WRITE;
    buf = vstring_alloc(100);

    copy_flags = MAIL_COPY_TOFILE | MAIL_COPY_RETURN_PATH
	| MAIL_COPY_DELIVERED | MAIL_COPY_ORIG_RCPT;

    newdir = concatenate(usr_attr.mailbox, "new/", (char *) 0);
    tmpdir = concatenate(usr_attr.mailbox, "tmp/", (char *) 0);
    curdir = concatenate(usr_attr.mailbox, "cur/", (char *) 0);

    /*
     * Create and write the file as the recipient, so that file quota work.
     * Create any missing directories on the fly. The file name is chosen
     * according to ftp://koobera.math.uic.edu/www/proto/maildir.html:
     * 
     * "A unique name has three pieces, separated by dots. On the left is the
     * result of time(). On the right is the result of gethostname(). In the
     * middle is something that doesn't repeat within one second on a single
     * host. I fork a new process for each delivery, so I just use the
     * process ID. If you're delivering several messages from one process,
     * use starttime.pid_count.host, where starttime is the time that your
     * process started, and count is the number of messages you've
     * delivered."
     * 
     * Well, that stopped working on fast machines, and on operating systems
     * that randomize process ID values. When creating a file in tmp/ we use
     * the process ID because it still is an exclusive resource. When moving
     * the file to new/ we use the device number and inode number. I do not
     * care if this breaks on a remote AFS file system, because people should
     * know better.
     * 
     * On January 26, 2003, http://cr.yp.to/proto/maildir.html said:
     * 
     * A unique name has three pieces, separated by dots. On the left is the
     * result of time() or the second counter from gettimeofday(). On the
     * right is the result of gethostname(). (To deal with invalid host
     * names, replace / with \057 and : with \072.) In the middle is a
     * delivery identifier, discussed below.
     * 
     * [...]
     * 
     * Modern delivery identifiers are created by concatenating enough of the
     * following strings to guarantee uniqueness:
     * 
     * [...]
     * 
     * In, where n is (in hexadecimal) the UNIX inode number of this file.
     * Unfortunately, inode numbers aren't always available through NFS.
     * 
     * Vn, where n is (in hexadecimal) the UNIX device number of this file.
     * Unfortunately, device numbers aren't always available through NFS.
     * (Device numbers are also not helpful with the standard UNIX
     * filesystem: a maildir has to be within a single UNIX device for link()
     * and rename() to work.)
     * 
     * Mn, where n is (in decimal) the microsecond counter from the same
     * gettimeofday() used for the left part of the unique name.
     * 
     * Pn, where n is (in decimal) the process ID.
     * 
     * [...]
     */
    set_eugid(usr_attr.uid, usr_attr.gid);
    vstring_sprintf(buf, "%lu.P%d.%s",
		 (unsigned long) starttime.tv_sec, var_pid, get_hostname());
    tmpfile = concatenate(tmpdir, STR(buf), (char *) 0);
    newfile = 0;
    if ((dst = vstream_fopen(tmpfile, O_WRONLY | O_CREAT | O_EXCL, 0600)) == 0
	&& (errno != ENOENT
	    || make_dirs(tmpdir, 0700) < 0
	    || (dst = vstream_fopen(tmpfile, O_WRONLY | O_CREAT | O_EXCL, 0600)) == 0)) {
	dsb_simple(why, mbox_dsn(errno, "4.2.0"),
		   "create maildir file %s: %m", tmpfile);
    } else if (fstat(vstream_fileno(dst), &st) < 0) {

	/*
	 * Coverity 200604: file descriptor leak in code that never executes.
	 * Code replaced by msg_fatal(), as it is not worthwhile to continue
	 * after an impossible error condition.
	 */
	msg_fatal("fstat %s: %m", tmpfile);
    } else {
	vstring_sprintf(buf, "%lu.V%lxI%lxM%lu.%s",
			(unsigned long) starttime.tv_sec,
			(unsigned long) st.st_dev,
			(unsigned long) st.st_ino,
			(unsigned long) starttime.tv_usec,
			get_hostname());
	newfile = concatenate(newdir, STR(buf), (char *) 0);
	if ((mail_copy_status = mail_copy(COPY_ATTR(state.msg_attr),
					  dst, copy_flags, "\n",
					  why)) == 0) {
	    if (sane_link(tmpfile, newfile) < 0
		&& (errno != ENOENT
		    || (make_dirs(curdir, 0700), make_dirs(newdir, 0700)) < 0
		    || sane_link(tmpfile, newfile) < 0)) {
		dsb_simple(why, mbox_dsn(errno, "4.2.0"),
			   "create maildir file %s: %m", newfile);
		mail_copy_status = MAIL_COPY_STAT_WRITE;
	    }
	}
	if (unlink(tmpfile) < 0)
	    msg_warn("remove %s: %m", tmpfile);
    }
    set_eugid(var_owner_uid, var_owner_gid);

    /*
     * The maildir location is controlled by the mail administrator. If
     * delivery fails, try again later. We would just bounce when the maildir
     * location possibly under user control.
     */
    if (mail_copy_status & MAIL_COPY_STAT_CORRUPT) {
	deliver_status = DEL_STAT_DEFER;
    } else if (mail_copy_status != 0) {
	if (errno == EACCES) {
	    msg_warn("maildir access problem for UID/GID=%lu/%lu: %s",
		     (long) usr_attr.uid, (long) usr_attr.gid,
		     STR(why->reason));
	    msg_warn("perhaps you need to create the maildirs in advance");
	}
	vstring_sprintf_prepend(why->reason, "maildir delivery failed: ");
	deliver_status =
	    (STR(why->status)[0] == '4' ?
	     defer_append : bounce_append)
	    (BOUNCE_FLAGS(state.request),
	     BOUNCE_ATTR(state.msg_attr));
    } else {
	dsb_simple(why, "2.0.0", "delivered to maildir");
	deliver_status = sent(BOUNCE_FLAGS(state.request),
			      SENT_ATTR(state.msg_attr));
    }
    vstring_free(buf);
    myfree(newdir);
    myfree(tmpdir);
    myfree(curdir);
    myfree(tmpfile);
    if (newfile)
	myfree(newfile);
    return (deliver_status);
}
Example #16
0
static VSTREAM *safe_open_exist(const char *path, int flags,
				        struct stat * fstat_st, VSTRING *why)
{
    struct stat local_statbuf;
    struct stat lstat_st;
    int     saved_errno;
    VSTREAM *fp;

    /*
     * Open an existing file.
     */
    if ((fp = vstream_fopen(path, flags & ~(O_CREAT | O_EXCL), 0)) == 0) {
	saved_errno = errno;
	vstring_sprintf(why, "cannot open file: %m");
	errno = saved_errno;
	return (0);
    }

    /*
     * Examine the modes from the open file: it must have exactly one hard
     * link (so that someone can't lure us into clobbering a sensitive file
     * by making a hard link to it), and it must be a non-symlink file.
     */
    if (fstat_st == 0)
	fstat_st = &local_statbuf;
    if (fstat(vstream_fileno(fp), fstat_st) < 0) {
	msg_fatal("%s: bad open file status: %m", path);
    } else if (fstat_st->st_nlink != 1) {
	vstring_sprintf(why, "file has %d hard links",
			(int) fstat_st->st_nlink);
	errno = EPERM;
    } else if (S_ISDIR(fstat_st->st_mode)) {
	vstring_sprintf(why, "file is a directory");
	errno = EISDIR;
    }

    /*
     * Look up the file again, this time using lstat(). Compare the fstat()
     * (open file) modes with the lstat() modes. If there is any difference,
     * either we followed a symlink while opening an existing file, someone
     * quickly changed the number of hard links, or someone replaced the file
     * after the open() call. The link and mode tests aren't really necessary
     * in daemon processes. Set-uid programs, on the other hand, can be
     * slowed down by arbitrary amounts, and there it would make sense to
     * compare even more file attributes, such as the inode generation number
     * on systems that have one.
     * 
     * Grr. Solaris /dev/whatever is a symlink. We'll have to make an exception
     * for symlinks owned by root. NEVER, NEVER, make exceptions for symlinks
     * owned by a non-root user. This would open a security hole when
     * delivering mail to a world-writable mailbox directory.
     */
    else if (lstat(path, &lstat_st) < 0) {
	vstring_sprintf(why, "file status changed unexpectedly: %m");
	errno = EPERM;
    } else if (S_ISLNK(lstat_st.st_mode)) {
	if (lstat_st.st_uid == 0)
	    return (fp);
	vstring_sprintf(why, "file is a symbolic link");
	errno = EPERM;
    } else if (fstat_st->st_dev != lstat_st.st_dev
	       || fstat_st->st_ino != lstat_st.st_ino
#ifdef HAS_ST_GEN
	       || fstat_st->st_gen != lstat_st.st_gen
#endif
	       || fstat_st->st_nlink != lstat_st.st_nlink
	       || fstat_st->st_mode != lstat_st.st_mode) {
	vstring_sprintf(why, "file status changed unexpectedly");
	errno = EPERM;
    }

    /*
     * We are almost there...
     */
    else {
	return (fp);
    }

    /*
     * End up here in case of fstat()/lstat() problems or inconsistencies.
     */
    vstream_fclose(fp);
    return (0);
}
Example #17
0
DICT   *dict_pcre_open(const char *mapname, int open_flags, int dict_flags)
{
    DICT_PCRE *dict_pcre;
    VSTREAM *map_fp;
    struct stat st;
    VSTRING *line_buffer;
    DICT_PCRE_RULE *last_rule = 0;
    DICT_PCRE_RULE *rule;
    int     lineno = 0;
    int     nesting = 0;
    char   *p;

    /*
     * Sanity checks.
     */
    if (open_flags != O_RDONLY)
	return (dict_surrogate(DICT_TYPE_PCRE, mapname, open_flags, dict_flags,
			       "%s:%s map requires O_RDONLY access mode",
			       DICT_TYPE_PCRE, mapname));

    /*
     * Open the configuration file.
     */
    if ((map_fp = vstream_fopen(mapname, O_RDONLY, 0)) == 0)
	return (dict_surrogate(DICT_TYPE_PCRE, mapname, open_flags, dict_flags,
			       "open %s: %m", mapname));
    if (fstat(vstream_fileno(map_fp), &st) < 0)
	msg_fatal("fstat %s: %m", mapname);

    line_buffer = vstring_alloc(100);

    dict_pcre = (DICT_PCRE *) dict_alloc(DICT_TYPE_PCRE, mapname,
					 sizeof(*dict_pcre));
    dict_pcre->dict.lookup = dict_pcre_lookup;
    dict_pcre->dict.close = dict_pcre_close;
    dict_pcre->dict.flags = dict_flags | DICT_FLAG_PATTERN;
    if (dict_flags & DICT_FLAG_FOLD_MUL)
	dict_pcre->dict.fold_buf = vstring_alloc(10);
    dict_pcre->head = 0;
    dict_pcre->expansion_buf = 0;

    if (dict_pcre_init == 0) {
	pcre_malloc = (void *(*) (size_t)) mymalloc;
	pcre_free = (void (*) (void *)) myfree;
	dict_pcre_init = 1;
    }
    dict_pcre->dict.owner.uid = st.st_uid;
    dict_pcre->dict.owner.status = (st.st_uid != 0);

    /*
     * Parse the pcre table.
     */
    while (readlline(line_buffer, map_fp, &lineno)) {
	p = vstring_str(line_buffer);
	trimblanks(p, 0)[0] = 0;		/* Trim space at end */
	if (*p == 0)
	    continue;
	rule = dict_pcre_parse_rule(mapname, lineno, p, nesting, dict_flags);
	if (rule == 0)
	    continue;
	if (rule->op == DICT_PCRE_OP_IF) {
	    nesting++;
	} else if (rule->op == DICT_PCRE_OP_ENDIF) {
	    nesting--;
	}
	if (last_rule == 0)
	    dict_pcre->head = rule;
	else
	    last_rule->next = rule;
	last_rule = rule;
    }

    if (nesting)
	msg_warn("pcre map %s, line %d: more IFs than ENDIFs",
		 mapname, lineno);

    vstring_free(line_buffer);
    vstream_fclose(map_fp);

    return (DICT_DEBUG (&dict_pcre->dict));
}
Example #18
0
EDIT_FILE *edit_file_open(const char *path, int flags, mode_t mode)
{
    struct stat before_lock;
    struct stat after_lock;
    int     saved_errno;
    EDIT_FILE *ep;

    /*
     * Initialize. Do not bother to optimize for the error case.
     */
    EDIT_FILE_ALLOC(ep, path, mode);

    /*
     * As long as the output file can be opened under the temporary pathname,
     * this code can loop or block forever.
     * 
     * Applications that are concerned about deadlock should protect the
     * edit_file_open() call with a watchdog timer.
     */
    for ( /* void */ ; /* void */ ; (void) vstream_fclose(ep->tmp_fp)) {

	/*
	 * Try to open the output file under the temporary pathname. This
	 * succeeds or fails immediately. To avoid creating a shared-lock DOS
	 * opportunity after we crash, we create the output file with no
	 * group or other permissions, and set the final permissions at the
	 * end (this is one reason why we try to get exclusive control over
	 * the output file instead of the original file). We postpone file
	 * truncation until we have obtained exclusive control over the file
	 * content and temporary pathname. If the open operation fails, we
	 * give up immediately. The caller can retry the call if desirable.
	 * 
	 * XXX If we replace the vstream_fopen() call by safe_open(), then we
	 * should replace the stat() call below by lstat().
	 */
	if ((ep->tmp_fp = vstream_fopen(ep->tmp_path, flags & ~(O_TRUNC),
				    EDIT_FILE_MODE)) == 0) {
	    saved_errno = errno;
	    EDIT_FILE_FREE(ep);
	    errno = saved_errno;
	    return (0);
	}

	/*
	 * At this point we may have opened an existing output file that was
	 * already locked. Try to lock the open file exclusively. This may
	 * take some time.
	 */
	if (myflock(vstream_fileno(ep->tmp_fp), INTERNAL_LOCK,
		    MYFLOCK_OP_EXCLUSIVE) < 0)
	    msg_fatal("lock %s: %m", ep->tmp_path);

	/*
	 * At this point we have an exclusive lock, but some other process
	 * may have renamed or removed the output file while we were waiting
	 * for the lock. If that is the case, back out and try again.
	 */
	if (fstat(vstream_fileno(ep->tmp_fp), &before_lock) < 0)
	    msg_fatal("open %s: %m", ep->tmp_path);
	if (stat(ep->tmp_path, &after_lock) < 0
	    || before_lock.st_dev != after_lock.st_dev
	    || before_lock.st_ino != after_lock.st_ino
#ifdef HAS_ST_GEN
	    || before_lock.st_gen != after_lock.st_gen
#endif
	/* No need to compare st_rdev or st_nlink here. */
	    ) {
	    continue;
	}

	/*
	 * At this point we have exclusive control over the output file
	 * content and its temporary pathname (within the rules of the
	 * cooperative protocol). But wait, there is more.
	 * 
	 * There are many opportunies for trouble when opening a pre-existing
	 * output file. Here are just a few.
	 * 
	 * - Victor observes that a system crash in the middle of the
	 * final-phase rename() operation may result in the output file
	 * having both the temporary pathname and the final pathname. In that
	 * case we must not write to the output file.
	 * 
	 * - Wietse observes that crashes may also leave the output file in
	 * other inconsistent states. To avoid permission-related trouble, we
	 * simply refuse to work with an output file that has the wrong
	 * temporary permissions. This won't stop the shared-lock DOS if we
	 * crash after changing the file permissions, though.
	 * 
	 * To work around these crash-related problems, remove the temporary
	 * pathname, back out, and try again.
	 */
	if (!S_ISREG(after_lock.st_mode)
#ifndef EDIT_FILE_REUSE_AFTER_CRASH
	    || after_lock.st_size > 0
#endif
	    || after_lock.st_nlink > 1
	    || (after_lock.st_mode & FILE_PERM_MASK) != EDIT_FILE_MODE) {
	    if (unlink(ep->tmp_path) < 0 && errno != ENOENT)
		msg_fatal("unlink %s: %m", ep->tmp_path);
	    continue;
	}

	/*
	 * Settle the final details.
	 */
#ifdef EDIT_FILE_REUSE_AFTER_CRASH
	if (ftruncate(vstream_fileno(ep->tmp_fp), 0) < 0)
	    msg_fatal("truncate %s: %m", ep->tmp_path);
#endif
	return (ep);
    }
}
Example #19
0
int     main(int argc, char **argv)
{
    VSTRING *buffer;
    VSTREAM *fp;
    int     ch;
    int     fd;
    struct stat st;
    int     flags = 0;
    static char *queue_names[] = {
	MAIL_QUEUE_MAILDROP,
	MAIL_QUEUE_INCOMING,
	MAIL_QUEUE_ACTIVE,
	MAIL_QUEUE_DEFERRED,
	MAIL_QUEUE_HOLD,
	MAIL_QUEUE_SAVED,
	0,
    };
    char  **cpp;
    int     tries;

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

    /*
     * To minimize confusion, make sure that the standard file descriptors
     * are open before opening anything else. XXX Work around for 44BSD where
     * fstat can return EBADF on an open file descriptor.
     */
    for (fd = 0; fd < 3; fd++)
	if (fstat(fd, &st) == -1
	    && (close(fd), open("/dev/null", O_RDWR, 0)) != fd)
	    msg_fatal("open /dev/null: %m");

    /*
     * Set up logging.
     */
    msg_vstream_init(argv[0], VSTREAM_ERR);

    /*
     * Parse JCL.
     */
    while ((ch = GETOPT(argc, argv, "bc:dehoqv")) > 0) {
	switch (ch) {
	case 'b':
	    flags |= PC_FLAG_PRINT_BODY;
	    break;
	case 'c':
	    if (setenv(CONF_ENV_PATH, optarg, 1) < 0)
		msg_fatal("out of memory");
	    break;
	case 'd':
	    flags |= PC_FLAG_PRINT_RTYPE_DEC;
	    break;
	case 'e':
	    flags |= PC_FLAG_PRINT_ENV;
	    break;
	case 'h':
	    flags |= PC_FLAG_PRINT_HEADER;
	    break;
	case 'o':
	    flags |= PC_FLAG_PRINT_OFFSET;
	    break;
	case 'q':
	    flags |= PC_FLAG_SEARCH_QUEUE;
	    break;
	case 'v':
	    msg_verbose++;
	    break;
	default:
	    usage(argv[0]);
	}
    }
    if ((flags & PC_MASK_PRINT_ALL) == 0)
	flags |= PC_MASK_PRINT_ALL;

    /*
     * Further initialization...
     */
    mail_conf_read();

    /*
     * Initialize.
     */
    buffer = vstring_alloc(10);

    /*
     * If no file names are given, copy stdin.
     */
    if (argc == optind) {
	vstream_control(VSTREAM_IN,
			VSTREAM_CTL_PATH, "stdin",
			VSTREAM_CTL_END);
	postcat(VSTREAM_IN, buffer, flags);
    }

    /*
     * Copy the named queue files in the specified order.
     */
    else if (flags & PC_FLAG_SEARCH_QUEUE) {
	if (chdir(var_queue_dir))
	    msg_fatal("chdir %s: %m", var_queue_dir);
	while (optind < argc) {
	    if (!mail_queue_id_ok(argv[optind]))
		msg_fatal("bad mail queue ID: %s", argv[optind]);
	    for (fp = 0, tries = 0; fp == 0 && tries < 2; tries++)
		for (cpp = queue_names; fp == 0 && *cpp != 0; cpp++)
		    fp = mail_queue_open(*cpp, argv[optind], O_RDONLY, 0);
	    if (fp == 0)
		msg_fatal("open queue file %s: %m", argv[optind]);
	    postcat(fp, buffer, flags);
	    if (vstream_fclose(fp))
		msg_warn("close %s: %m", argv[optind]);
	    optind++;
	}
    }

    /*
     * Copy the named files in the specified order.
     */
    else {
	while (optind < argc) {
	    if ((fp = vstream_fopen(argv[optind], O_RDONLY, 0)) == 0)
		msg_fatal("open %s: %m", argv[optind]);
	    postcat(fp, buffer, flags);
	    if (vstream_fclose(fp))
		msg_warn("close %s: %m", argv[optind]);
	    optind++;
	}
    }

    /*
     * Clean up.
     */
    vstring_free(buffer);
    exit(0);
}
Example #20
0
void    edit_parameters(int mode, int argc, char **argv)
{
    char   *path;
    EDIT_FILE *ep;
    VSTREAM *src;
    VSTREAM *dst;
    VSTRING *buf = vstring_alloc(100);
    VSTRING *key = vstring_alloc(10);
    char   *cp;
    char   *edit_key;
    char   *edit_val;
    HTABLE *table;
    struct cvalue {
	char   *value;
	int     found;
    };
    struct cvalue *cvalue;
    HTABLE_INFO **ht_info;
    HTABLE_INFO **ht;
    int     interesting;
    const char *err;

    /*
     * Store command-line parameters for quick lookup.
     */
    table = htable_create(argc);
    while ((cp = *argv++) != 0) {
	if (strchr(cp, '\n') != 0)
	    msg_fatal("-e or -# accepts no multi-line input");
	while (ISSPACE(*cp))
	    cp++;
	if (*cp == '#')
	    msg_fatal("-e or -# accepts no comment input");
	if (mode & EDIT_MAIN) {
	    if ((err = split_nameval(cp, &edit_key, &edit_val)) != 0)
		msg_fatal("%s: \"%s\"", err, cp);
	} else if (mode & COMMENT_OUT) {
	    if (*cp == 0)
		msg_fatal("-# requires non-blank parameter names");
	    if (strchr(cp, '=') != 0)
		msg_fatal("-# requires parameter names only");
	    edit_key = mystrdup(cp);
	    trimblanks(edit_key, 0);
	    edit_val = 0;
	} else {
	    msg_panic("edit_parameters: unknown mode %d", mode);
	}
	cvalue = (struct cvalue *) mymalloc(sizeof(*cvalue));
	cvalue->value = edit_val;
	cvalue->found = 0;
	htable_enter(table, edit_key, (char *) cvalue);
    }

    /*
     * Open a temp file for the result. This uses a deterministic name so we
     * don't leave behind thrash with random names.
     */
    set_config_dir();
    path = concatenate(var_config_dir, "/", MAIN_CONF_FILE, (char *) 0);
    if ((ep = edit_file_open(path, O_CREAT | O_WRONLY, 0644)) == 0)
	msg_fatal("open %s%s: %m", path, EDIT_FILE_SUFFIX);
    dst = ep->tmp_fp;

    /*
     * Open the original file for input.
     */
    if ((src = vstream_fopen(path, O_RDONLY, 0)) == 0) {
	/* OK to delete, since we control the temp file name exclusively. */
	(void) unlink(ep->tmp_path);
	msg_fatal("open %s for reading: %m", path);
    }

    /*
     * Copy original file to temp file, while replacing parameters on the
     * fly. Issue warnings for names found multiple times.
     */
#define STR(x) vstring_str(x)

    interesting = 0;
    while (vstring_get(buf, src) != VSTREAM_EOF) {
	for (cp = STR(buf); ISSPACE(*cp) /* including newline */ ; cp++)
	     /* void */ ;
	/* Copy comment, all-whitespace, or empty line. */
	if (*cp == '#' || *cp == 0) {
	    vstream_fputs(STR(buf), dst);
	}
	/* Copy, skip or replace continued text. */
	else if (cp > STR(buf)) {
	    if (interesting == 0)
		vstream_fputs(STR(buf), dst);
	    else if (mode & COMMENT_OUT)
		vstream_fprintf(dst, "#%s", STR(buf));
	}
	/* Copy or replace start of logical line. */
	else {
	    vstring_strncpy(key, cp, strcspn(cp, " \t\r\n="));
	    cvalue = (struct cvalue *) htable_find(table, STR(key));
	    if ((interesting = !!cvalue) != 0) {
		if (cvalue->found++ == 1)
		    msg_warn("%s: multiple entries for \"%s\"", path, STR(key));
		if (mode & EDIT_MAIN)
		    vstream_fprintf(dst, "%s = %s\n", STR(key), cvalue->value);
		else if (mode & COMMENT_OUT)
		    vstream_fprintf(dst, "#%s", cp);
		else
		    msg_panic("edit_parameters: unknown mode %d", mode);
	    } else {
		vstream_fputs(STR(buf), dst);
	    }
	}
    }

    /*
     * Generate new entries for parameters that were not found.
     */
    if (mode & EDIT_MAIN) {
	for (ht_info = ht = htable_list(table); *ht; ht++) {
	    cvalue = (struct cvalue *) ht[0]->value;
	    if (cvalue->found == 0)
		vstream_fprintf(dst, "%s = %s\n", ht[0]->key, cvalue->value);
	}
	myfree((char *) ht_info);
    }

    /*
     * When all is well, rename the temp file to the original one.
     */
    if (vstream_fclose(src))
	msg_fatal("read %s: %m", path);
    if (edit_file_close(ep) != 0)
	msg_fatal("close %s%s: %m", path, EDIT_FILE_SUFFIX);

    /*
     * Cleanup.
     */
    myfree(path);
    vstring_free(buf);
    vstring_free(key);
    htable_free(table, myfree);
}
Example #21
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);
}
Example #22
0
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);
}
Example #23
0
DICT   *dict_thash_open(const char *path, int open_flags, int dict_flags)
{
    DICT_THASH *dict_thash;
    VSTREAM *fp = 0;
    struct stat st;
    time_t  before;
    time_t  after;
    VSTRING *line_buffer = 0;
    int     lineno;
    char   *key;
    char   *value;
    HTABLE *table;
    HTABLE_INFO *ht;

    /*
     * Let the optimizer worry about eliminating redundant code.
     */
#define DICT_THASH_OPEN_RETURN(d) { \
	DICT *__d = (d); \
	if (fp != 0) \
	    vstream_fclose(fp); \
	if (line_buffer != 0) \
	    vstring_free(line_buffer); \
	return (__d); \
    } while (0)

    /*
     * Sanity checks.
     */
    if (open_flags != O_RDONLY)
	DICT_THASH_OPEN_RETURN(dict_surrogate(DICT_TYPE_THASH, path,
					      open_flags, dict_flags,
				  "%s:%s map requires O_RDONLY access mode",
					      DICT_TYPE_THASH, path));

    /*
     * Read the flat text file into in-memory hash. Read the file again if it
     * may have changed while we were reading.
     */
    for (before = time((time_t *) 0); /* see below */ ; before = after) {
	if ((fp = vstream_fopen(path, open_flags, 0644)) == 0) {
	    DICT_THASH_OPEN_RETURN(dict_surrogate(DICT_TYPE_THASH, path,
						  open_flags, dict_flags,
					     "open database %s: %m", path));
	}
	if (line_buffer == 0)
	    line_buffer = vstring_alloc(100);
	lineno = 0;
	table = htable_create(13);
	while (readlline(line_buffer, fp, &lineno)) {

	    /*
	     * Split on the first whitespace character, then trim leading and
	     * trailing whitespace from key and value.
	     */
	    key = STR(line_buffer);
	    value = key + strcspn(key, " \t\r\n");
	    if (*value)
		*value++ = 0;
	    while (ISSPACE(*value))
		value++;
	    trimblanks(key, 0)[0] = 0;
	    trimblanks(value, 0)[0] = 0;

	    /*
	     * Enforce the "key whitespace value" format. Disallow missing
	     * keys or missing values.
	     */
	    if (*key == 0 || *value == 0) {
		msg_warn("%s, line %d: expected format: key whitespace value"
			 " -- ignoring this line", path, lineno);
		continue;
	    }
	    if (key[strlen(key) - 1] == ':')
		msg_warn("%s, line %d: record is in \"key: value\" format;"
			 " is this an alias file?", path, lineno);

	    /*
	     * Optionally fold the key.
	     */
	    if (dict_flags & DICT_FLAG_FOLD_FIX)
		lowercase(key);

	    /*
	     * Store the value under the key. Handle duplicates
	     * appropriately.
	     */
	    if ((ht = htable_locate(table, key)) != 0) {
		if (dict_flags & DICT_FLAG_DUP_IGNORE) {
		     /* void */ ;
		} else if (dict_flags & DICT_FLAG_DUP_REPLACE) {
		    myfree(ht->value);
		    ht->value = mystrdup(value);
		} else if (dict_flags & DICT_FLAG_DUP_WARN) {
		    msg_warn("%s, line %d: duplicate entry: \"%s\"",
			     path, lineno, key);
		} else {
		    msg_fatal("%s, line %d: duplicate entry: \"%s\"",
			      path, lineno, key);
		}
	    } else {
		htable_enter(table, key, mystrdup(value));
	    }
	}

	/*
	 * See if the source file is hot.
	 */
	if (fstat(vstream_fileno(fp), &st) < 0)
	    msg_fatal("fstat %s: %m", path);
	if (vstream_fclose(fp))
	    msg_fatal("read %s: %m", path);
	fp = 0;					/* DICT_THASH_OPEN_RETURN() */
	after = time((time_t *) 0);
	if (st.st_mtime < before - 1 || st.st_mtime > after)
	    break;

	/*
	 * Yes, it is hot. Discard the result and read the file again.
	 */
	htable_free(table, myfree);
	if (msg_verbose > 1)
	    msg_info("pausing to let file %s cool down", path);
	doze(300000);
    }

    /*
     * Create the in-memory table.
     */
    dict_thash = (DICT_THASH *)
	dict_alloc(DICT_TYPE_THASH, path, sizeof(*dict_thash));
    dict_thash->dict.lookup = dict_thash_lookup;
    dict_thash->dict.sequence = dict_thash_sequence;
    dict_thash->dict.close = dict_thash_close;
    dict_thash->dict.flags = dict_flags | DICT_FLAG_DUP_WARN | DICT_FLAG_FIXED;
    if (dict_flags & DICT_FLAG_FOLD_FIX)
	dict_thash->dict.fold_buf = vstring_alloc(10);
    dict_thash->info = 0;
    dict_thash->table = table;
    dict_thash->dict.owner.uid = st.st_uid;
    dict_thash->dict.owner.status = (st.st_uid != 0);

    DICT_THASH_OPEN_RETURN(DICT_DEBUG (&dict_thash->dict));
}
Example #24
0
DICT   *dict_cidr_open(const char *mapname, int open_flags, int dict_flags)
{
    const char myname[] = "dict_cidr_open";
    DICT_CIDR *dict_cidr;
    VSTREAM *map_fp = 0;
    struct stat st;
    VSTRING *line_buffer = 0;
    VSTRING *why = 0;
    DICT_CIDR_ENTRY *rule;
    DICT_CIDR_ENTRY *last_rule = 0;
    int     last_line = 0;
    int     lineno;
    int     nesting = 0;
    DICT_CIDR_ENTRY **rule_stack = 0;
    MVECT   mvect;

    /*
     * Let the optimizer worry about eliminating redundant code.
     */
#define DICT_CIDR_OPEN_RETURN(d) do { \
	DICT *__d = (d); \
	if (map_fp != 0 && vstream_fclose(map_fp)) \
	    msg_fatal("cidr map %s: read error: %m", mapname); \
	if (line_buffer != 0) \
	    vstring_free(line_buffer); \
	if (why != 0) \
	    vstring_free(why); \
	return (__d); \
    } while (0)

    /*
     * Sanity checks.
     */
    if (open_flags != O_RDONLY)
	DICT_CIDR_OPEN_RETURN(dict_surrogate(DICT_TYPE_CIDR, mapname,
					     open_flags, dict_flags,
				  "%s:%s map requires O_RDONLY access mode",
					     DICT_TYPE_CIDR, mapname));

    /*
     * Open the configuration file.
     */
    if ((map_fp = vstream_fopen(mapname, O_RDONLY, 0)) == 0)
	DICT_CIDR_OPEN_RETURN(dict_surrogate(DICT_TYPE_CIDR, mapname,
					     open_flags, dict_flags,
					     "open %s: %m", mapname));
    if (fstat(vstream_fileno(map_fp), &st) < 0)
	msg_fatal("fstat %s: %m", mapname);

    line_buffer = vstring_alloc(100);
    why = vstring_alloc(100);

    /*
     * XXX Eliminate unnecessary queries by setting a flag that says "this
     * map matches network addresses only".
     */
    dict_cidr = (DICT_CIDR *) dict_alloc(DICT_TYPE_CIDR, mapname,
					 sizeof(*dict_cidr));
    dict_cidr->dict.lookup = dict_cidr_lookup;
    dict_cidr->dict.close = dict_cidr_close;
    dict_cidr->dict.flags = dict_flags | DICT_FLAG_PATTERN;
    dict_cidr->head = 0;

    dict_cidr->dict.owner.uid = st.st_uid;
    dict_cidr->dict.owner.status = (st.st_uid != 0);

    while (readllines(line_buffer, map_fp, &last_line, &lineno)) {
	rule = dict_cidr_parse_rule(vstring_str(line_buffer), lineno,
				    nesting, why);
	if (rule == 0) {
	    msg_warn("cidr map %s, line %d: %s: skipping this rule",
		     mapname, lineno, vstring_str(why));
	    continue;
	}
	if (rule->cidr_info.op == CIDR_MATCH_OP_IF) {
	    if (rule_stack == 0)
		rule_stack = (DICT_CIDR_ENTRY **) mvect_alloc(&mvect,
					   sizeof(*rule_stack), nesting + 1,
						(MVECT_FN) 0, (MVECT_FN) 0);
	    else
		rule_stack =
		    (DICT_CIDR_ENTRY **) mvect_realloc(&mvect, nesting + 1);
	    rule_stack[nesting] = rule;
	    nesting++;
	} else if (rule->cidr_info.op == CIDR_MATCH_OP_ENDIF) {
	    DICT_CIDR_ENTRY *if_rule;

	    if (nesting-- <= 0)
		/* Already handled in dict_cidr_parse_rule(). */
		msg_panic("%s: ENDIF without IF", myname);
	    if_rule = rule_stack[nesting];
	    if (if_rule->cidr_info.op != CIDR_MATCH_OP_IF)
		msg_panic("%s: unexpected rule stack element type %d",
			  myname, if_rule->cidr_info.op);
	    if_rule->cidr_info.block_end = &(rule->cidr_info);
	}
	if (last_rule == 0)
	    dict_cidr->head = rule;
	else
	    last_rule->cidr_info.next = &(rule->cidr_info);
	last_rule = rule;
    }

    while (nesting-- > 0)
	msg_warn("cidr map %s, line %d: IF has no matching ENDIF",
		 mapname, rule_stack[nesting]->lineno);

    if (rule_stack)
	(void) mvect_free(&mvect);

    DICT_CIDR_OPEN_RETURN(DICT_DEBUG (&dict_cidr->dict));
}