Example #1
0
/*
 * expand a filter (lookup or result)
 */
static void dict_ldap_expand_filter(char *ldapsource, char *filter,
				            char *value, VSTRING *out)
{
    char   *myname = "dict_ldap_expand_filter";
    char   *sub,
           *end;

    /*
     * Yes, replace all instances of %s with the address to look up. Replace
     * %u with the user portion, and %d with the domain portion.
     */
    sub = filter;
    end = sub + strlen(filter);
    while (sub < end) {

	/*
	 * Make sure it's %[sud] and not something else.  For backward
	 * compatibilty, treat anything other than %u or %d as %s, with a
	 * warning.
	 */
	if (*(sub) == '%') {
	    char   *u = value;
	    char   *p = strrchr(u, '@');

	    switch (*(sub + 1)) {
	    case 'd':
		if (p)
		    vstring_strcat(out, p + 1);
		break;
	    case 'u':
		if (p)
		    vstring_strncat(out, u, p - u);
		else
		    vstring_strcat(out, u);
		break;
	    default:
		msg_warn("%s: %s: Invalid filter substitution format '%%%c'!",
			 myname, ldapsource, *(sub + 1));
		/* fall through */
	    case 's':
		vstring_strcat(out, u);
		break;
	    }
	    sub++;
	} else
	    vstring_strncat(out, sub, 1);
	sub++;
    }
}
Example #2
0
VSTRING *quote_822_local_flags(VSTRING *dst, const char *mbox, int flags)
{
    const char *start;			/* first byte of localpart */
    const char *end;			/* first byte after localpart */
    const char *colon;

    /*
     * According to RFC 822, a local-part is a dot-string or a quoted-string.
     * We first see if the local-part is a dot-string. If it is not, we turn
     * it into a quoted-string. Anything else would be too painful. But
     * first, skip over any source route that precedes the local-part.
     */
    if (mbox[0] == '@' && (colon = strchr(mbox, ':')) != 0)
	start = colon + 1;
    else
	start = mbox;
    if ((end = strrchr(start, '@')) == 0)
	end = start + strlen(start);
    if ((flags & QUOTE_FLAG_APPEND) == 0)
	VSTRING_RESET(dst);
    if (is_822_dot_string(start, end, flags)) {
	return (vstring_strcat(dst, mbox));
    } else {
	vstring_strncat(dst, mbox, start - mbox);
	make_822_quoted_string(dst, start, end, flags & QUOTE_FLAG_8BITCLEAN);
	return (vstring_strcat(dst, end));
    }
}
Example #3
0
char   *casefoldx(int flags, VSTRING *dest, const char *src, ssize_t len)
{
    size_t  old_len;

#ifdef NO_EAI

    /*
     * ASCII mode only.
     */
    if (len < 0)
	len = strlen(src);
    if ((flags & CASEF_FLAG_APPEND) == 0)
	VSTRING_RESET(dest);
    old_len = VSTRING_LEN(dest);
    vstring_strncat(dest, src, len);
    lowercase(STR(dest) + old_len);
    return (STR(dest));
#else

    /*
     * Unicode mode.
     */
    const char myname[] = "casefold";
    static VSTRING *fold_buf = 0;
    static UCaseMap *csm = 0;
    UErrorCode error;
    ssize_t space_needed;
    int     n;

    /*
     * Handle special cases.
     */
    if (len < 0)
	len = strlen(src);
    if (dest == 0)
	dest = (fold_buf != 0 ? fold_buf : (fold_buf = vstring_alloc(100)));
    if ((flags & CASEF_FLAG_APPEND) == 0)
	VSTRING_RESET(dest);
    old_len = VSTRING_LEN(dest);

    /*
     * All-ASCII input, or ASCII mode only.
     */
    if ((flags & CASEF_FLAG_UTF8) == 0 || allascii(src)) {
	vstring_strncat(dest, src, len);
	lowercase(STR(dest) + old_len);
	return (STR(dest));
    }

    /*
     * ICU 4.8 ucasemap_utf8FoldCase() does not complain about UTF-8 syntax
     * errors. XXX Based on source-code review we conclude that non-UTF-8
     * bytes are copied verbatim, and experiments confirm this. Given that
     * this behavior is intentional, we assume that it will stay that way.
     */
#if 0
    if (valid_utf8_string(src, len) == 0) {
	if (err)
	    *err = "malformed UTF-8 or invalid codepoint";
	return (0);
    }
#endif

    /*
     * One-time initialization. With ICU 4.8 this works while chrooted.
     */
    if (csm == 0) {
	error = U_ZERO_ERROR;
	csm = ucasemap_open("en_US", U_FOLD_CASE_DEFAULT, &error);
	if (U_SUCCESS(error) == 0)
	    msg_fatal("ucasemap_open error: %s", u_errorName(error));
    }

    /*
     * Fold the input, adjusting the buffer size if needed. Safety: don't
     * loop forever.
     * 
     * Note: the requested amount of space for casemapped output (as reported
     * with space_needed below) does not include storage for the null
     * terminator. The terminator is written only when the output buffer is
     * large enough. This is why we overallocate space when the output does
     * not fit. But if the output fits exactly, then the ouput will be
     * unterminated, and we have to terminate the output ourselves.
     */
    for (n = 0; n < 3; n++) {
	error = U_ZERO_ERROR;
	space_needed = ucasemap_utf8FoldCase(csm, STR(dest) + old_len,
				     vstring_avail(dest), src, len, &error);
	if (U_SUCCESS(error)) {
	    VSTRING_AT_OFFSET(dest, old_len + space_needed);
	    if (vstring_avail(dest) == 0)	/* exact fit, no terminator */
		VSTRING_TERMINATE(dest);	/* add terminator */
	    break;
	} else if (error == U_BUFFER_OVERFLOW_ERROR) {
	    VSTRING_SPACE(dest, space_needed + 1);	/* for terminator */
	} else {
	    msg_fatal("%s: conversion error for \"%s\": %s",
		      myname, src, u_errorName(error));
	}
    }
    return (STR(dest));
#endif						/* NO_EAI */
}
Example #4
0
static const char *dict_ldap_lookup(DICT *dict, const char *name)
{
    char   *myname = "dict_ldap_lookup";
    DICT_LDAP *dict_ldap = (DICT_LDAP *) dict;
    LDAPMessage *res = 0;
    static VSTRING *result;
    struct timeval tv;
    VSTRING *escaped_name = 0,
           *filter_buf = 0;
    int     rc = 0;
    int     sizelimit;
    char   *sub,
           *end;

    dict_errno = 0;

    if (msg_verbose)
	msg_info("%s: In dict_ldap_lookup", myname);

    /*
     * If they specified a domain list for this map, then only search for
     * addresses in domains on the list. This can significantly reduce the
     * load on the LDAP server.
     */
    if (dict_ldap->domain) {
	const char *p = strrchr(name, '@');

	if (p == 0 || p == name ||
	    match_list_match(dict_ldap->domain, ++p) == 0) {
	    if (msg_verbose)
		msg_info("%s: domain of %s not found in domain list", myname,
			 name);
	    return (0);
	}
    }

    /*
     * Initialize the result holder.
     */
    if (result == 0)
	result = vstring_alloc(2);
    vstring_strcpy(result, "");

    /*
     * Because the connection may be shared and invalidated via queries for
     * another map, update private copy of "ld" from shared connection
     * container.
     */
    dict_ldap->ld = DICT_LDAP_CONN(dict_ldap)->conn_ld;

    /*
     * Connect to the LDAP server, if necessary.
     */
    if (dict_ldap->ld == NULL) {
	if (msg_verbose)
	    msg_info
		("%s: No existing connection for LDAP source %s, reopening",
		 myname, dict_ldap->ldapsource);

	dict_ldap_connect(dict_ldap);

	/*
	 * if dict_ldap_connect() set dict_errno, abort.
	 */
	if (dict_errno)
	    return (0);
    } else if (msg_verbose)
	msg_info("%s: Using existing connection for LDAP source %s",
		 myname, dict_ldap->ldapsource);

    /*
     * Connection caching, means that the connection handle may have the
     * wrong size limit. Re-adjust before each query. This is cheap, just
     * sets a field in the ldap connection handle. We also do this in the
     * connect code, because we sometimes reconnect (below) in the middle of
     * a query.
     */
    sizelimit = dict_ldap->size_limit ? dict_ldap->size_limit : LDAP_NO_LIMIT;
    if (ldap_set_option(dict_ldap->ld, LDAP_OPT_SIZELIMIT, &sizelimit)
	!= LDAP_OPT_SUCCESS)
	msg_warn("%s: %s: Unable to set query result size limit to %ld.",
		 myname, dict_ldap->ldapsource, dict_ldap->size_limit);

    /*
     * Prepare the query.
     */
    tv.tv_sec = dict_ldap->timeout;
    tv.tv_usec = 0;
    escaped_name = vstring_alloc(20);
    filter_buf = vstring_alloc(30);

    /*
     * If any characters in the supplied address should be escaped per RFC
     * 2254, do so. Thanks to Keith Stevenson and Wietse. And thanks to
     * Samuel Tardieu for spotting that wildcard searches were being done in
     * the first place, which prompted the ill-conceived lookup_wildcards
     * parameter and then this more comprehensive mechanism.
     */
    end = (char *) name + strlen((char *) name);
    sub = (char *) strpbrk((char *) name, "*()\\\0");
    if (sub && sub != end) {
	if (msg_verbose)
	    msg_info("%s: Found character(s) in %s that must be escaped",
		     myname, name);
	for (sub = (char *) name; sub != end; sub++) {
	    switch (*sub) {
	    case '*':
		vstring_strcat(escaped_name, "\\2a");
		break;
	    case '(':
		vstring_strcat(escaped_name, "\\28");
		break;
	    case ')':
		vstring_strcat(escaped_name, "\\29");
		break;
	    case '\\':
		vstring_strcat(escaped_name, "\\5c");
		break;
	    case '\0':
		vstring_strcat(escaped_name, "\\00");
		break;
	    default:
		vstring_strncat(escaped_name, sub, 1);
	    }
	}
	if (msg_verbose)
	    msg_info("%s: After escaping, it's %s", myname,
		     vstring_str(escaped_name));
    } else
	vstring_strcpy(escaped_name, (char *) name);

    /*
     * Does the supplied query_filter even include a substitution?
     */
    if ((char *) strchr(dict_ldap->query_filter, '%') == NULL) {

	/*
	 * No, log the fact and continue.
	 */
	msg_warn("%s: %s: Fixed query_filter %s is probably useless",
		 myname, dict_ldap->ldapsource, dict_ldap->query_filter);
	vstring_strcpy(filter_buf, dict_ldap->query_filter);
    } else {
	dict_ldap_expand_filter(dict_ldap->ldapsource, dict_ldap->query_filter,
				vstring_str(escaped_name), filter_buf);
    }

    /*
     * On to the search.
     */
    if (msg_verbose)
	msg_info("%s: Searching with filter %s", myname,
		 vstring_str(filter_buf));

    rc = ldap_search_st(dict_ldap->ld, dict_ldap->search_base,
			dict_ldap->scope,
			vstring_str(filter_buf),
			dict_ldap->result_attributes->argv,
			0, &tv, &res);

    if (rc == LDAP_SERVER_DOWN) {
	if (msg_verbose)
	    msg_info("%s: Lost connection for LDAP source %s, reopening",
		     myname, dict_ldap->ldapsource);

	ldap_unbind(dict_ldap->ld);
	dict_ldap->ld = DICT_LDAP_CONN(dict_ldap)->conn_ld = 0;
	dict_ldap_connect(dict_ldap);

	/*
	 * if dict_ldap_connect() set dict_errno, abort.
	 */
	if (dict_errno)
	    return (0);

	rc = ldap_search_st(dict_ldap->ld, dict_ldap->search_base,
			    dict_ldap->scope,
			    vstring_str(filter_buf),
			    dict_ldap->result_attributes->argv,
			    0, &tv, &res);

    }
    if (rc == LDAP_SUCCESS) {

	/*
	 * Search worked; extract the requested result_attribute.
	 */

	dict_ldap_get_values(dict_ldap, res, result);

	/*
	 * OpenLDAP's ldap_next_attribute returns a bogus
	 * LDAP_DECODING_ERROR; I'm ignoring that for now.
	 */

	rc = dict_ldap_get_errno(dict_ldap->ld);
	if (rc != LDAP_SUCCESS && rc != LDAP_DECODING_ERROR)
	    msg_warn
		("%s: Had some trouble with entries returned by search: %s",
		 myname, ldap_err2string(rc));

	if (msg_verbose)
	    msg_info("%s: Search returned %s", myname,
		     VSTRING_LEN(result) >
		     0 ? vstring_str(result) : "nothing");
    } else {

	/*
	 * Rats. The search didn't work.
	 */
	msg_warn("%s: Search error %d: %s ", myname, rc,
		 ldap_err2string(rc));

	/*
	 * Tear down the connection so it gets set up from scratch on the
	 * next lookup.
	 */
	ldap_unbind(dict_ldap->ld);
	dict_ldap->ld = DICT_LDAP_CONN(dict_ldap)->conn_ld = 0;

	/*
	 * And tell the caller to try again later.
	 */
	dict_errno = DICT_ERR_RETRY;
    }

    /*
     * Cleanup.
     */
    if (res != 0)
	ldap_msgfree(res);
    if (filter_buf != 0)
	vstring_free(filter_buf);
    if (escaped_name != 0)
	vstring_free(escaped_name);

    /*
     * If we had an error, return nothing, Otherwise, return the result, if
     * any.
     */
    return (VSTRING_LEN(result) > 0 && !dict_errno ? vstring_str(result) : 0);
}