Beispiel #1
0
void    rewrite_tree(RWR_CONTEXT *context, TOK822 *tree)
{
    TOK822 *colon;
    TOK822 *domain;
    TOK822 *bang;
    TOK822 *local;
    VSTRING *vstringval;

    /*
     * XXX If you change this module, quote_822_local.c, or tok822_parse.c,
     * be sure to re-run the tests under "make rewrite_clnt_test" and "make
     * resolve_clnt_test" in the global directory.
     */

    /*
     * Sanity check.
     */
    if (tree->head == 0)
	msg_panic("rewrite_tree: empty tree");

    /*
     * An empty address is a special case.
     */
    if (tree->head == tree->tail
	&& tree->tail->type == TOK822_QSTRING
	&& VSTRING_LEN(tree->tail->vstr) == 0)
	return;

    /*
     * Treat a lone @ as if it were an empty address.
     */
    if (tree->head == tree->tail
	&& tree->tail->type == '@') {
	tok822_free_tree(tok822_sub_keep_before(tree, tree->tail));
	tok822_sub_append(tree, tok822_alloc(TOK822_QSTRING, ""));
	return;
    }

    /*
     * Strip source route.
     */
    if (tree->head->type == '@'
	&& (colon = tok822_find_type(tree->head, ':')) != 0
	&& colon != tree->tail)
	tok822_free_tree(tok822_sub_keep_after(tree, colon));

    /*
     * Optionally, transform address forms without @.
     */
    if ((domain = tok822_rfind_type(tree->tail, '@')) == 0) {

	/*
	 * Swap domain!user to user@domain.
	 */
	if (var_swap_bangpath != 0
	    && (bang = tok822_find_type(tree->head, '!')) != 0) {
	    tok822_sub_keep_before(tree, bang);
	    local = tok822_cut_after(bang);
	    tok822_free(bang);
	    tok822_sub_prepend(tree, tok822_alloc('@', (char *) 0));
	    if (local)
		tok822_sub_prepend(tree, local);
	}

	/*
	 * Promote user%domain to user@domain.
	 */
	else if (var_percent_hack != 0
		 && (domain = tok822_rfind_type(tree->tail, '%')) != 0) {
	    domain->type = '@';
	}

	/*
	 * Append missing @origin
	 */
	else if (var_append_at_myorigin != 0
		 && REW_PARAM_VALUE(context->origin) != 0
		 && REW_PARAM_VALUE(context->origin)[0] != 0) {
	    domain = tok822_sub_append(tree, tok822_alloc('@', (char *) 0));
	    tok822_sub_append(tree, tok822_scan(REW_PARAM_VALUE(context->origin),
						(TOK822 **) 0));
	}
    }

    /*
     * Append missing .domain, but leave broken forms ending in @ alone. This
     * merely makes diagnostics more accurate by leaving bogus addresses
     * alone.
     * 
     * Backwards-compatibility warning: warn for "user@localhost" when there is
     * no "localhost" in mydestination or in any other address class with an
     * explicit domain list.
     */
    if (var_append_dot_mydomain != 0
	&& REW_PARAM_VALUE(context->domain) != 0
	&& REW_PARAM_VALUE(context->domain)[0] != 0
	&& (domain = tok822_rfind_type(tree->tail, '@')) != 0
	&& domain != tree->tail
	&& tok822_find_type(domain, TOK822_DOMLIT) == 0
	&& tok822_find_type(domain, '.') == 0) {
	if (warn_compat_break_app_dot_mydomain
	    && (vstringval = domain->next->vstr) != 0) {
	    if (strcasecmp(vstring_str(vstringval), "localhost") != 0) {
		msg_info("using backwards-compatible default setting "
			 VAR_APP_DOT_MYDOMAIN "=yes to rewrite \"%s\" to "
			 "\"%s.%s\"", vstring_str(vstringval),
			 vstring_str(vstringval), var_mydomain);
	    } else if (resolve_class("localhost") == RESOLVE_CLASS_DEFAULT) {
		msg_info("using backwards-compatible default setting "
			 VAR_APP_DOT_MYDOMAIN "=yes to rewrite \"%s\" to "
			 "\"%s.%s\"; please add \"localhost\" to "
			 "mydestination or other address class",
			 vstring_str(vstringval), vstring_str(vstringval),
			 var_mydomain);
	    }
	}
	tok822_sub_append(tree, tok822_alloc('.', (char *) 0));
	tok822_sub_append(tree, tok822_scan(REW_PARAM_VALUE(context->domain),
					    (TOK822 **) 0));
    }

    /*
     * Strip trailing dot at end of domain, but not dot-dot or @-dot. This
     * merely makes diagnostics more accurate by leaving bogus addresses
     * alone.
     */
    if (tree->tail->type == '.'
	&& tree->tail->prev
	&& tree->tail->prev->type != '.'
	&& tree->tail->prev->type != '@')
	tok822_free_tree(tok822_sub_keep_before(tree, tree->tail));
}
Beispiel #2
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);
}