Exemplo n.º 1
0
VSTRING *netstring_get_data(VSTREAM *stream, VSTRING *buf, ssize_t len)
{
    const char *myname = "netstring_get_data";

    /*
     * Allocate buffer space.
     */
    VSTRING_RESET(buf);
    VSTRING_SPACE(buf, len);

    /*
     * Read the payload and absorb the terminator.
     */
    if (vstream_fread(stream, STR(buf), len) != len)
	netstring_except(stream, vstream_ftimeout(stream) ?
			 NETSTRING_ERR_TIME : NETSTRING_ERR_EOF);
    if (msg_verbose > 1)
	msg_info("%s: read netstring data %.*s",
		 myname, (int) (len < 30 ? len : 30), STR(buf));
    netstring_get_terminator(stream);

    /*
     * Position the buffer.
     */
    VSTRING_AT_OFFSET(buf, len);
    return (buf);
}
Exemplo n.º 2
0
static void dict_mysql_quote(DICT *dict, const char *name, VSTRING *result)
{
    DICT_MYSQL *dict_mysql = (DICT_MYSQL *) dict;
    int     len = strlen(name);
    int     buflen;

    /*
     * We won't get integer overflows in 2*len + 1, because Postfix input
     * keys have reasonable size limits, better safe than sorry.
     */
    if (len > (INT_MAX - VSTRING_LEN(result) - 1) / 2)
	msg_panic("dict_mysql_quote: integer overflow in %lu+2*%d+1",
		  (unsigned long) VSTRING_LEN(result), len);
    buflen = 2 * len + 1;
    VSTRING_SPACE(result, buflen);

#if defined(MYSQL_VERSION_ID) && MYSQL_VERSION_ID >= 40000
    if (dict_mysql->active_host)
	mysql_real_escape_string(dict_mysql->active_host->db,
				 vstring_end(result), name, len);
    else
#endif
	mysql_escape_string(vstring_end(result), name, len);

    VSTRING_SKIP(result);
}
Exemplo n.º 3
0
VSTRING *vstring_memcat(VSTRING *vp, const char *src, ssize_t len)
{
    VSTRING_SPACE(vp, len);
    memcpy(vstring_end(vp), src, len);
    len += VSTRING_LEN(vp);
    VSTRING_AT_OFFSET(vp, len);
    return (vp);
}
Exemplo n.º 4
0
VSTRING *vstring_memcpy(VSTRING *vp, const char *src, ssize_t len)
{
    VSTRING_RESET(vp);

    VSTRING_SPACE(vp, len);
    memcpy(vstring_str(vp), src, len);
    VSTRING_AT_OFFSET(vp, len);
    return (vp);
}
int     xsasl_cyrus_server_first(XSASL_SERVER *xp, const char *sasl_method,
			          const char *init_response, VSTRING *reply)
{
    const char *myname = "xsasl_cyrus_server_first";
    XSASL_CYRUS_SERVER *server = (XSASL_CYRUS_SERVER *) xp;
    char   *dec_buffer;
    unsigned dec_length;
    unsigned reply_len;
    unsigned serveroutlen;
    int     sasl_status;
    SERVEROUT_TYPE serverout = 0;
    int     xsasl_status;

#if SASL_VERSION_MAJOR < 2
    const char *errstr = 0;

#endif

#define IFELSE(e1,e2,e3) ((e1) ? (e2) : (e3))

    if (msg_verbose)
	msg_info("%s: sasl_method %s%s%s", myname, sasl_method,
		 IFELSE(init_response, ", init_response ", ""),
		 IFELSE(init_response, init_response, ""));

    /*
     * SASL authentication protocol start-up. Process any initial client
     * response that was sent along in the AUTH command.
     */
    if (init_response) {
	reply_len = strlen(init_response);
	VSTRING_RESET(server->decoded);		/* Fix 200512 */
	VSTRING_SPACE(server->decoded, reply_len);
	if ((sasl_status = SASL_DECODE64(init_response, reply_len,
					 dec_buffer = STR(server->decoded),
					 vstring_avail(server->decoded),
					 &dec_length)) != SASL_OK) {
	    vstring_strcpy(reply, xsasl_cyrus_strerror(sasl_status));
	    return (XSASL_AUTH_FORM);
	}
	if (msg_verbose)
	    msg_info("%s: decoded initial response %s", myname, dec_buffer);
    } else {
	dec_buffer = 0;
	dec_length = 0;
    }
    sasl_status = SASL_SERVER_START(server->sasl_conn, sasl_method, dec_buffer,
				    dec_length, &serverout,
				    &serveroutlen, &errstr);
    xsasl_status = xsasl_cyrus_server_auth_response(sasl_status, serverout,
						    serveroutlen, reply);
#if SASL_VERSION_MAJOR < 2
    /* SASL version 1 doesn't free memory that it allocates. */
    free(serverout);
#endif
    return (xsasl_status);
}
Exemplo n.º 6
0
const char *psc_expand_lookup(const char *name, int unused_mode,
				        char *context)
{
    PSC_STATE *state = (PSC_STATE *) context;
    time_t  now;
    struct tm *lt;

    if (state->expand_buf == 0)
	state->expand_buf = vstring_alloc(10);

    if (msg_verbose > 1)
	msg_info("psc_expand_lookup: ${%s}", name);

#define STREQ(x,y)    (*(x) == *(y) && strcmp((x), (y)) == 0)
#define STREQN(x,y,n) (*(x) == *(y) && strncmp((x), (y), (n)) == 0)
#define CONST_LEN(x)  (sizeof(x) - 1)

    /*
     * Don't query main.cf parameters, as the result of expansion could
     * reveal system-internal information in server replies.
     * 
     * XXX: This said, multiple servers may be behind a single client-visible
     * name or IP address, and each may generate its own logs. Therefore, it
     * may be useful to expose the replying MTA id (myhostname) in the
     * contact footer, to identify the right logs. So while we don't expose
     * the raw configuration dictionary, we do expose "$myhostname" as
     * expanded in var_myhostname.
     * 
     * Return NULL only for non-existent names.
     */
    if (STREQ(name, MAIL_ATTR_SERVER_NAME)) {
	return (var_myhostname);
    } else if (STREQ(name, MAIL_ATTR_ACT_CLIENT_ADDR)) {
	return (state->smtp_client_addr);
    } else if (STREQ(name, MAIL_ATTR_ACT_CLIENT_PORT)) {
	return (state->smtp_client_port);
    } if (STREQ(name, MAIL_ATTR_LOCALTIME)) {
	if (time(&now) == (time_t) - 1)
	    msg_fatal("time lookup failed: %m");
	lt = localtime(&now);
	VSTRING_RESET(state->expand_buf);
	do {
	    VSTRING_SPACE(state->expand_buf, 100);
	} while (strftime(STR(state->expand_buf),
			  vstring_avail(state->expand_buf),
			  "%b %d %H:%M:%S", lt) == 0);
	return (STR(state->expand_buf));
    } else {
	msg_warn("unknown macro name \"%s\" in expansion request", name);
	return (0);
    }
}
Exemplo n.º 7
0
ARGV   *mail_addr_crunch(const char *string, const char *extension)
{
    VSTRING *extern_addr = vstring_alloc(100);
    VSTRING *canon_addr = vstring_alloc(100);
    ARGV   *argv = argv_alloc(1);
    TOK822 *tree;
    TOK822 **addr_list;
    TOK822 **tpp;
    char   *ratsign;
    ssize_t extlen;

    if (extension)
        extlen = strlen(extension);

#define STR(x) vstring_str(x)

    /*
     * Parse the string, rewrite each address to canonical form, and convert
     * the result to external (quoted) form. Optionally apply the extension
     * to each address found.
     *
     * XXX Workaround for the null address. This works for envelopes but
     * produces ugly results for message headers.
     */
    if (*string == 0 || strcmp(string, "<>") == 0)
        string = "\"\"";
    tree = tok822_parse(string);
    addr_list = tok822_grep(tree, TOK822_ADDR);
    for (tpp = addr_list; *tpp; tpp++) {
        tok822_externalize(extern_addr, tpp[0]->head, TOK822_STR_DEFL);
        canon_addr_external(canon_addr, STR(extern_addr));
        if (extension) {
            VSTRING_SPACE(canon_addr, extlen + 1);
            if ((ratsign = strrchr(STR(canon_addr), '@')) == 0) {
                vstring_strcat(canon_addr, extension);
            } else {
                memmove(ratsign + extlen, ratsign, strlen(ratsign) + 1);
                memcpy(ratsign, extension, extlen);
                VSTRING_SKIP(canon_addr);
            }
        }
        argv_add(argv, STR(canon_addr), ARGV_END);
    }
    argv_terminate(argv);
    myfree((char *) addr_list);
    tok822_free_tree(tree);
    vstring_free(canon_addr);
    vstring_free(extern_addr);
    return (argv);
}
static int xsasl_cyrus_server_next(XSASL_SERVER *xp, const char *request,
				           VSTRING *reply)
{
    const char *myname = "xsasl_cyrus_server_next";
    XSASL_CYRUS_SERVER *server = (XSASL_CYRUS_SERVER *) xp;
    unsigned dec_length;
    unsigned request_len;
    unsigned serveroutlen;
    int     sasl_status;
    SERVEROUT_TYPE serverout = 0;
    int     xsasl_status;

#if SASL_VERSION_MAJOR < 2
    const char *errstr = 0;

#endif

    request_len = strlen(request);
    VSTRING_RESET(server->decoded);		/* Fix 200512 */
    VSTRING_SPACE(server->decoded, request_len);
    if ((sasl_status = SASL_DECODE64(request, request_len,
				     STR(server->decoded),
				     vstring_avail(server->decoded),
				     &dec_length)) != SASL_OK) {
	vstring_strcpy(reply, xsasl_cyrus_strerror(sasl_status));
	return (XSASL_AUTH_FORM);
    }
    if (msg_verbose)
	msg_info("%s: decoded response: %.*s",
		 myname, (int) dec_length, STR(server->decoded));
    sasl_status = SASL_SERVER_STEP(server->sasl_conn, STR(server->decoded),
				   dec_length, &serverout,
				   &serveroutlen, &errstr);
    xsasl_status = xsasl_cyrus_server_auth_response(sasl_status, serverout,
						    serveroutlen, reply);
#if SASL_VERSION_MAJOR < 2
    /* SASL version 1 doesn't free memory that it allocates. */
    free(serverout);
#endif
    return (xsasl_status);
}
static int xsasl_cyrus_server_auth_response(int sasl_status,
					            SERVEROUT_TYPE serverout,
					            unsigned serveroutlen,
					            VSTRING *reply)
{
    const char *myname = "xsasl_cyrus_server_auth_response";
    unsigned enc_length;
    unsigned enc_length_out;

    /*
     * Encode the server first/next non-error response; otherwise return the
     * unencoded error text that corresponds to the SASL error status.
     * 
     * Regarding the hairy expression below: output from sasl_encode64() comes
     * in multiples of four bytes for each triple of input bytes, plus four
     * bytes for any incomplete last triple, plus one byte for the null
     * terminator.
     */
    if (sasl_status == SASL_OK) {
	vstring_strcpy(reply, "");
	return (XSASL_AUTH_DONE);
    } else if (sasl_status == SASL_CONTINUE) {
	if (msg_verbose)
	    msg_info("%s: uncoded server challenge: %.*s",
		     myname, (int) serveroutlen, serverout);
	enc_length = ((serveroutlen + 2) / 3) * 4 + 1;
	VSTRING_RESET(reply);			/* Fix 200512 */
	VSTRING_SPACE(reply, enc_length);
	if ((sasl_status = sasl_encode64(serverout, serveroutlen,
					 STR(reply), vstring_avail(reply),
					 &enc_length_out)) != SASL_OK)
	    msg_panic("%s: sasl_encode64 botch: %s",
		      myname, xsasl_cyrus_strerror(sasl_status));
	return (XSASL_AUTH_MORE);
    } else {
	if (sasl_status == SASL_NOUSER)		/* privacy */
	    sasl_status = SASL_BADAUTH;
	vstring_strcpy(reply, xsasl_cyrus_strerror(sasl_status));
	return (XSASL_AUTH_FAIL);
    }
}
Exemplo n.º 10
0
VSTRING *vstring_prepend(VSTRING *vp, const char *buf, ssize_t len)
{
    ssize_t new_len;

    /*
     * Sanity check.
     */
    if (len < 0)
	msg_panic("vstring_prepend: bad length %ld", (long) len);

    /*
     * Move the existing content and copy the new content.
     */
    new_len = VSTRING_LEN(vp) + len;
    VSTRING_SPACE(vp, len);
    memmove(vstring_str(vp) + len, vstring_str(vp), VSTRING_LEN(vp));
    memcpy(vstring_str(vp), buf, len);
    VSTRING_AT_OFFSET(vp, new_len);
    VSTRING_TERMINATE(vp);
    return (vp);
}
Exemplo n.º 11
0
VSTRING *vstring_sprintf_prepend(VSTRING *vp, const char *format,...)
{
    va_list ap;
    ssize_t old_len = VSTRING_LEN(vp);
    ssize_t result_len;

    /* Construct: old|new|free */
    va_start(ap, format);
    vp = vstring_vsprintf_append(vp, format, ap);
    va_end(ap);
    result_len = VSTRING_LEN(vp);

    /* Construct: old|new|old|free */
    VSTRING_SPACE(vp, old_len);
    vstring_memcat(vp, vstring_str(vp), old_len);

    /* Construct: new|old|free */
    memmove(vstring_str(vp), vstring_str(vp) + old_len, result_len);
    VSTRING_AT_OFFSET(vp, result_len);
    VSTRING_TERMINATE(vp);
    return (vp);
}
Exemplo n.º 12
0
VSTRING *vstring_insert(VSTRING *vp, ssize_t start, const char *buf, ssize_t len)
{
    ssize_t new_len;

    /*
     * Sanity check.
     */
    if (start < 0 || start >= VSTRING_LEN(vp))
	msg_panic("vstring_insert: bad start %ld", (long) start);
    if (len < 0)
	msg_panic("vstring_insert: bad length %ld", (long) len);

    /*
     * Move the existing content and copy the new content.
     */
    new_len = VSTRING_LEN(vp) + len;
    VSTRING_SPACE(vp, len);
    memmove(vstring_str(vp) + start + len, vstring_str(vp) + start,
	    VSTRING_LEN(vp) - start);
    memcpy(vstring_str(vp) + start, buf, len);
    VSTRING_AT_OFFSET(vp, new_len);
    VSTRING_TERMINATE(vp);
    return (vp);
}
Exemplo n.º 13
0
static int xsasl_cyrus_client_next(XSASL_CLIENT *xp, const char *server_reply,
				           VSTRING *client_reply)
{
    const char *myname = "xsasl_cyrus_client_next";
    XSASL_CYRUS_CLIENT *client = (XSASL_CYRUS_CLIENT *) xp;
    unsigned enc_length;
    unsigned enc_length_out;
    CLIENTOUT_TYPE clientout;
    unsigned clientoutlen;
    unsigned serverinlen;
    int     sasl_status;

    /*
     * Process a server challenge.
     */
    serverinlen = strlen(server_reply);
    VSTRING_RESET(client->decoded);		/* Fix 200512 */
    VSTRING_SPACE(client->decoded, serverinlen);
    if ((sasl_status = SASL_DECODE64(server_reply, serverinlen,
				     STR(client->decoded),
				     vstring_avail(client->decoded),
				     &enc_length)) != SASL_OK) {
	vstring_strcpy(client_reply, xsasl_cyrus_strerror(sasl_status));
	return (XSASL_AUTH_FORM);
    }
    if (msg_verbose)
	msg_info("%s: decoded challenge: %.*s",
		 myname, (int) enc_length, STR(client->decoded));
    sasl_status = sasl_client_step(client->sasl_conn, STR(client->decoded),
				   enc_length, NO_SASL_INTERACTION,
				   &clientout, &clientoutlen);
    if (sasl_status != SASL_OK && sasl_status != SASL_CONTINUE) {
	vstring_strcpy(client_reply, xsasl_cyrus_strerror(sasl_status));
	return (XSASL_AUTH_FAIL);
    }

    /*
     * Send a client response.
     */
    if (clientoutlen > 0) {
	if (msg_verbose)
	    msg_info("%s: uncoded client response %.*s",
		     myname, (int) clientoutlen, clientout);
	enc_length = ENCODE64_LENGTH(clientoutlen) + 1;
	VSTRING_RESET(client_reply);		/* Fix 200512 */
	VSTRING_SPACE(client_reply, enc_length);
	if ((sasl_status = sasl_encode64(clientout, clientoutlen,
					 STR(client_reply),
					 vstring_avail(client_reply),
					 &enc_length_out)) != SASL_OK)
	    msg_panic("%s: sasl_encode64 botch: %s",
		      myname, xsasl_cyrus_strerror(sasl_status));
#if SASL_VERSION_MAJOR < 2
	/* SASL version 1 doesn't free memory that it allocates. */
	free(clientout);
#endif
    } else {
	/* XXX Can't happen. */
	vstring_strcpy(client_reply, "");
    }
    return (XSASL_AUTH_OK);
}
Exemplo n.º 14
0
static int xsasl_cyrus_client_first(XSASL_CLIENT *xp,
				            const char *mechanism_list,
				            const char *username,
				            const char *password,
				            const char **mechanism,
				            VSTRING *init_resp)
{
    const char *myname = "xsasl_cyrus_client_first";
    XSASL_CYRUS_CLIENT *client = (XSASL_CYRUS_CLIENT *) xp;
    unsigned enc_length;
    unsigned enc_length_out;
    CLIENTOUT_TYPE clientout;
    unsigned clientoutlen;
    int     sasl_status;

#define NO_SASL_SECRET		0
#define NO_SASL_INTERACTION	0

    /*
     * Save the username and password for the call-backs.
     */
    if (client->username)
	myfree(client->username);
    client->username = mystrdup(username);
    if (client->password)
	myfree(client->password);
    client->password = mystrdup(password);

    /*
     * Start the client side authentication protocol.
     */
    sasl_status = SASL_CLIENT_START((sasl_conn_t *) client->sasl_conn,
				    mechanism_list,
				    NO_SASL_SECRET, NO_SASL_INTERACTION,
				    &clientout, &clientoutlen, mechanism);
    if (sasl_status != SASL_OK && sasl_status != SASL_CONTINUE) {
	vstring_strcpy(init_resp, xsasl_cyrus_strerror(sasl_status));
	return (XSASL_AUTH_FAIL);
    }

    /*
     * Generate the AUTH command and the optional initial client response.
     * sasl_encode64() produces four bytes for each complete or incomplete
     * triple of input bytes. Allocate an extra byte for string termination.
     */
#define ENCODE64_LENGTH(n)	((((n) + 2) / 3) * 4)

    if (clientoutlen > 0) {
	if (msg_verbose) {
	    escape(client->decoded, clientout, clientoutlen);
	    msg_info("%s: uncoded initial reply: %s",
		     myname, STR(client->decoded));
	}
	enc_length = ENCODE64_LENGTH(clientoutlen) + 1;
	VSTRING_RESET(init_resp);		/* Fix 200512 */
	VSTRING_SPACE(init_resp, enc_length);
	if ((sasl_status = sasl_encode64(clientout, clientoutlen,
					 STR(init_resp),
					 vstring_avail(init_resp),
					 &enc_length_out)) != SASL_OK)
	    msg_panic("%s: sasl_encode64 botch: %s",
		      myname, xsasl_cyrus_strerror(sasl_status));
	VSTRING_AT_OFFSET(init_resp, enc_length_out);	/* XXX */
#if SASL_VERSION_MAJOR < 2
	/* SASL version 1 doesn't free memory that it allocates. */
	free(clientout);
#endif
    } else {
	vstring_strcpy(init_resp, "");
    }
    return (XSASL_AUTH_OK);
}
Exemplo n.º 15
0
int     deliver_resolve_tree(LOCAL_STATE state, USER_ATTR usr_attr, TOK822 *addr)
{
    const char *myname = "deliver_resolve_tree";
    RESOLVE_REPLY reply;
    int     status;
    ssize_t ext_len;
    char   *ratsign;
    int     rcpt_delim;

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

    /*
     * Initialize.
     */
    resolve_clnt_init(&reply);

    /*
     * Rewrite the address to canonical form, just like the cleanup service
     * does. Then, resolve the address to (transport, nexhop, recipient),
     * just like the queue manager does. The only part missing here is the
     * virtual address substitution. Message forwarding fixes most of that.
     */
    tok822_rewrite(addr, REWRITE_CANON);
    tok822_resolve(addr, &reply);

    /*
     * First, a healthy portion of error handling.
     */
    if (reply.flags & RESOLVE_FLAG_FAIL) {
	dsb_simple(state.msg_attr.why, "4.3.0", "address resolver failure");
	status = defer_append(BOUNCE_FLAGS(state.request),
			      BOUNCE_ATTR(state.msg_attr));
    } else if (reply.flags & RESOLVE_FLAG_ERROR) {
	dsb_simple(state.msg_attr.why, "5.1.3",
		   "bad recipient address syntax: %s", STR(reply.recipient));
	status = bounce_append(BOUNCE_FLAGS(state.request),
			       BOUNCE_ATTR(state.msg_attr));
    } else {

	/*
	 * Splice in the optional unmatched address extension.
	 */
	if (state.msg_attr.unmatched) {
	    rcpt_delim = state.msg_attr.local[strlen(state.msg_attr.user)];
	    if ((ratsign = strrchr(STR(reply.recipient), '@')) == 0) {
		VSTRING_ADDCH(reply.recipient, rcpt_delim);
		vstring_strcat(reply.recipient, state.msg_attr.unmatched);
	    } else {
		ext_len = strlen(state.msg_attr.unmatched);
		VSTRING_SPACE(reply.recipient, ext_len + 2);
		if ((ratsign = strrchr(STR(reply.recipient), '@')) == 0)
		    msg_panic("%s: recipient @ botch", myname);
		memmove(ratsign + ext_len + 1, ratsign, strlen(ratsign) + 1);
		*ratsign = rcpt_delim;
		memcpy(ratsign + 1, state.msg_attr.unmatched, ext_len);
		VSTRING_SKIP(reply.recipient);
	    }
	}
	state.msg_attr.rcpt.address = STR(reply.recipient);

	/*
	 * Delivery to a local or non-local address. For a while there was
	 * some ugly code to force local recursive alias expansions on a host
	 * with no authority over the local domain, but that code was just
	 * too unclean.
	 */
	if (strcmp(state.msg_attr.relay, STR(reply.transport)) == 0) {
	    status = deliver_recipient(state, usr_attr);
	} else {
	    status = deliver_indirect(state);
	}
    }

    /*
     * Cleanup.
     */
    resolve_clnt_free(&reply);

    return (status);
}
Exemplo n.º 16
0
const char *mail_date(time_t when)
{
    static VSTRING *vp;
    struct tm *lt;
    struct tm gmt;
    int     gmtoff;

    /*
     * As if strftime() isn't expensive enough, we're dynamically adjusting
     * the size for the result, so we won't be surprised by long names etc.
     */
    if (vp == 0)
	vp = vstring_alloc(100);
    else
	VSTRING_RESET(vp);

    /*
     * POSIX does not require that struct tm has a tm_gmtoff field, so we
     * must compute the time offset from UTC by hand.
     * 
     * Starting with the difference in hours/minutes between 24-hour clocks,
     * adjust for differences in years, in yeardays, and in (leap) seconds.
     * 
     * Assume 0..23 hours in a day, 0..59 minutes in an hour. The library spec
     * has changed: we can no longer assume that there are 0..59 seconds in a
     * minute.
     */
    gmt = *gmtime(&when);
    lt = localtime(&when);
    gmtoff = (lt->tm_hour - gmt.tm_hour) * HOUR_MIN + lt->tm_min - gmt.tm_min;
    if (lt->tm_year < gmt.tm_year)
	gmtoff -= DAY_MIN;
    else if (lt->tm_year > gmt.tm_year)
	gmtoff += DAY_MIN;
    else if (lt->tm_yday < gmt.tm_yday)
	gmtoff -= DAY_MIN;
    else if (lt->tm_yday > gmt.tm_yday)
	gmtoff += DAY_MIN;
    if (lt->tm_sec <= gmt.tm_sec - MIN_SEC)
	gmtoff -= 1;
    else if (lt->tm_sec >= gmt.tm_sec + MIN_SEC)
	gmtoff += 1;

    /*
     * First, format the date and wall-clock time. XXX The %e format (day of
     * month, leading zero replaced by blank) isn't in my POSIX book, but
     * many vendors seem to support it.
     */
#ifdef MISSING_STRFTIME_E
#define STRFTIME_FMT "%a, %d %b %Y %H:%M:%S "
#else
#define STRFTIME_FMT "%a, %e %b %Y %H:%M:%S "
#endif

    while (strftime(vstring_end(vp), vstring_avail(vp), STRFTIME_FMT, lt) == 0)
	VSTRING_SPACE(vp, 100);
    VSTRING_SKIP(vp);

    /*
     * Then, add the UTC offset.
     */
    if (gmtoff < -DAY_MIN || gmtoff > DAY_MIN)
	msg_panic("UTC time offset %d is larger than one day", gmtoff);
    vstring_sprintf_append(vp, "%+03d%02d", (int) (gmtoff / HOUR_MIN),
			   (int) (abs(gmtoff) % HOUR_MIN));

    /*
     * Finally, add the time zone name.
     */
    while (strftime(vstring_end(vp), vstring_avail(vp), " (%Z)", lt) == 0)
	VSTRING_SPACE(vp, vstring_avail(vp) + 100);
    VSTRING_SKIP(vp);

    return (vstring_str(vp));
}
Exemplo n.º 17
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 */
}
Exemplo n.º 18
0
static int ial_siocgif(INET_ADDR_LIST *addr_list,
		               INET_ADDR_LIST *mask_list,
		               int af)
{
    const char *myname = "inet_addr_local[siocgif]";
    struct in_addr addr;
    struct ifconf ifc;
    struct ifreq *ifr;
    struct ifreq *ifr_mask;
    struct ifreq *the_end;
    int     sock;
    VSTRING *buf;

    /*
     * Get the network interface list. XXX The socket API appears to have no
     * function that returns the number of network interfaces, so we have to
     * guess how much space is needed to store the result.
     * 
     * On BSD-derived systems, ioctl SIOCGIFCONF returns as much information as
     * possible, leaving it up to the application to repeat the request with
     * a larger buffer if the result caused a tight fit.
     * 
     * Other systems, such as Solaris 2.5, generate an EINVAL error when the
     * buffer is too small for the entire result. Workaround: ignore EINVAL
     * errors and repeat the request with a larger buffer. The downside is
     * that the program can run out of memory due to a non-memory problem,
     * making it more difficult than necessary to diagnose the real problem.
     */
    sock = ial_socket(af);
    if (sock < 0)
	return (0);
    buf = vstring_alloc(1024);
    for (;;) {
	ifc.ifc_len = vstring_avail(buf);
	ifc.ifc_buf = vstring_str(buf);
	if (ioctl(sock, SIOCGIFCONF, (char *) &ifc) < 0) {
	    if (errno != EINVAL)
		msg_fatal("%s: ioctl SIOCGIFCONF: %m", myname);
	} else if (ifc.ifc_len < vstring_avail(buf) / 2)
	    break;
	VSTRING_SPACE(buf, vstring_avail(buf) * 2);
    }

    the_end = (struct ifreq *) (ifc.ifc_buf + ifc.ifc_len);
    for (ifr = ifc.ifc_req; ifr < the_end;) {
	if (ifr->ifr_addr.sa_family != af) {
	    ifr = NEXT_INTERFACE(ifr);
	    continue;
	}
	if (af == AF_INET) {
	    addr = ((struct sockaddr_in *) & ifr->ifr_addr)->sin_addr;
	    if (addr.s_addr != INADDR_ANY) {
		inet_addr_list_append(addr_list, &ifr->ifr_addr);
		if (mask_list) {
		    ifr_mask = (struct ifreq *) mymalloc(IFREQ_SIZE(ifr));
		    memcpy((void *) ifr_mask, (void *) ifr, IFREQ_SIZE(ifr));
		    if (ioctl(sock, SIOCGIFNETMASK, ifr_mask) < 0)
			msg_fatal("%s: ioctl SIOCGIFNETMASK: %m", myname);

		    /*
		     * Note that this SIOCGIFNETMASK has truly screwed up the
		     * contents of sa_len/sa_family. We must fix this
		     * manually to have correct addresses.   --dcs
		     */
		    ifr_mask->ifr_addr.sa_family = af;
#ifdef HAS_SA_LEN
		    ifr_mask->ifr_addr.sa_len = sizeof(struct sockaddr_in);
#endif
		    inet_addr_list_append(mask_list, &ifr_mask->ifr_addr);
		    myfree((void *) ifr_mask);
		}
	    }
	}
#ifdef HAS_IPV6
	else if (af == AF_INET6) {
	    struct sockaddr *sa;

	    sa = SOCK_ADDR_PTR(&ifr->ifr_addr);
	    if (!(IN6_IS_ADDR_UNSPECIFIED(&SOCK_ADDR_IN6_ADDR(sa)))) {
		inet_addr_list_append(addr_list, sa);
		if (mask_list) {
		    /* XXX Assume /128 for everything */
		    struct sockaddr_in6 mask6;

		    mask6 = *SOCK_ADDR_IN6_PTR(sa);
		    memset((void *) &mask6.sin6_addr, ~0,
			   sizeof(mask6.sin6_addr));
		    inet_addr_list_append(mask_list, SOCK_ADDR_PTR(&mask6));
		}
	    }
	}
#endif
	ifr = NEXT_INTERFACE(ifr);
    }
    vstring_free(buf);
    (void) close(sock);
    return (0);
}
Exemplo n.º 19
0
static int ial_siocglif(INET_ADDR_LIST *addr_list,
			        INET_ADDR_LIST *mask_list,
			        int af)
{
    const char *myname = "inet_addr_local[siocglif]";
    struct lifconf lifc;
    struct lifreq *lifr;
    struct lifreq *lifr_mask;
    struct lifreq *the_end;
    struct sockaddr *sa;
    int     sock;
    VSTRING *buf;

    /*
     * See also comments in ial_siocgif()
     */
    if (af != AF_INET && af != AF_INET6)
	msg_fatal("%s: address family was %d, must be AF_INET (%d) or "
		  "AF_INET6 (%d)", myname, af, AF_INET, AF_INET6);
    sock = ial_socket(af);
    if (sock < 0)
	return (0);
    buf = vstring_alloc(1024);
    for (;;) {
	memset(&lifc, 0, sizeof(lifc));
	lifc.lifc_family = AF_UNSPEC;		/* XXX Why??? */
	lifc.lifc_len = vstring_avail(buf);
	lifc.lifc_buf = vstring_str(buf);
	if (ioctl(sock, SIOCGLIFCONF, (char *) &lifc) < 0) {
	    if (errno != EINVAL)
		msg_fatal("%s: ioctl SIOCGLIFCONF: %m", myname);
	} else if (lifc.lifc_len < vstring_avail(buf) / 2)
	    break;
	VSTRING_SPACE(buf, vstring_avail(buf) * 2);
    }

    the_end = (struct lifreq *) (lifc.lifc_buf + lifc.lifc_len);
    for (lifr = lifc.lifc_req; lifr < the_end;) {
	sa = (struct sockaddr *) &lifr->lifr_addr;
	if (sa->sa_family != af) {
	    lifr = NEXT_INTERFACE(lifr);
	    continue;
	}
	if (af == AF_INET) {
	    if (SOCK_ADDR_IN_ADDR(sa).s_addr == INADDR_ANY) {
		lifr = NEXT_INTERFACE(lifr);
		continue;
	    }
#ifdef HAS_IPV6
	} else if (af == AF_INET6) {
	    if (IN6_IS_ADDR_UNSPECIFIED(&SOCK_ADDR_IN6_ADDR(sa))) {
		lifr = NEXT_INTERFACE(lifr);
		continue;
	    }
	}
#endif
	inet_addr_list_append(addr_list, sa);
	if (mask_list) {
	    lifr_mask = (struct lifreq *) mymalloc(sizeof(struct lifreq));
	    memcpy((void *) lifr_mask, (void *) lifr, sizeof(struct lifreq));
	    if (ioctl(sock, SIOCGLIFNETMASK, lifr_mask) < 0)
		msg_fatal("%s: ioctl(SIOCGLIFNETMASK): %m", myname);
	    /* XXX: Check whether sa_len/family are honoured --dcs */
	    inet_addr_list_append(mask_list,
				  (struct sockaddr *) &lifr_mask->lifr_addr);
	    myfree((void *) lifr_mask);
	}
	lifr = NEXT_INTERFACE(lifr);
    }
    vstring_free(buf);
    (void) close(sock);
    return (0);
}
Exemplo n.º 20
0
static void dict_pgsql_quote(DICT *dict, const char *name, VSTRING *result)
{
    DICT_PGSQL *dict_pgsql = (DICT_PGSQL *) dict;
    HOST   *active_host = dict_pgsql->active_host;
    char   *myname = "dict_pgsql_quote";
    size_t  len = strlen(name);
    size_t  buflen = 2 * len + 1;
    int     err = 1;

    if (active_host == 0)
	msg_panic("%s: bogus dict_pgsql->active_host", myname);

    /*
     * We won't get arithmetic overflows in 2*len + 1, because Postfix input
     * keys have reasonable size limits, better safe than sorry.
     */
    if (buflen <= len)
	msg_panic("%s: arithmetic overflow in 2*%lu+1",
		  myname, (unsigned long) len);

    /*
     * XXX Workaround: stop further processing when PQescapeStringConn()
     * (below) fails. A more proper fix requires invasive changes, not
     * suitable for a stable release.
     */
    if (active_host->stat == STATFAIL)
	return;

    /*
     * Escape the input string, using PQescapeStringConn(), because the older
     * PQescapeString() is not safe anymore, as stated by the documentation.
     * 
     * From current libpq (8.1.4) documentation:
     * 
     * PQescapeStringConn writes an escaped version of the from string to the to
     * buffer, escaping special characters so that they cannot cause any
     * harm, and adding a terminating zero byte.
     * 
     * ...
     * 
     * The parameter from points to the first character of the string that is to
     * be escaped, and the length parameter gives the number of bytes in this
     * string. A terminating zero byte is not required, and should not be
     * counted in length.
     * 
     * ...
     * 
     * (The parameter) to shall point to a buffer that is able to hold at least
     * one more byte than twice the value of length, otherwise the behavior
     * is undefined.
     * 
     * ...
     * 
     * If the error parameter is not NULL, then *error is set to zero on
     * success, nonzero on error ... The output string is still generated on
     * error, but it can be expected that the server will reject it as
     * malformed. On error, a suitable message is stored in the conn object,
     * whether or not error is NULL.
     */
    VSTRING_SPACE(result, buflen);
    PQescapeStringConn(active_host->db, vstring_end(result), name, len, &err);
    if (err == 0) {
	VSTRING_SKIP(result);
    } else {

	/*
	 * PQescapeStringConn() failed. According to the docs, we still have
	 * a valid, null-terminated output string, but we need not rely on
	 * this behavior.
	 */
	msg_warn("dict pgsql: (host %s) cannot escape input string: %s",
		 active_host->hostname, PQerrorMessage(active_host->db));
	active_host->stat = STATFAIL;
	VSTRING_TERMINATE(result);
    }
}
Exemplo n.º 21
0
static void tlsmgr_service(VSTREAM *client_stream, char *unused_service,
			           char **argv)
{
    static VSTRING *request = 0;
    static VSTRING *cache_type = 0;
    static VSTRING *cache_id = 0;
    static VSTRING *buffer = 0;
    int     len;
    static char wakeup[] = {		/* master wakeup request */
	TRIGGER_REQ_WAKEUP,
	0,
    };
    TLSMGR_SCACHE *ent;
    int     status = TLS_MGR_STAT_FAIL;

    /*
     * Sanity check. This service takes no command-line arguments.
     */
    if (argv[0])
	msg_fatal("unexpected command-line argument: %s", argv[0]);

    /*
     * Initialize. We're select threaded, so we can use static buffers.
     */
    if (request == 0) {
	request = vstring_alloc(10);
	cache_type = vstring_alloc(10);
	cache_id = vstring_alloc(10);
	buffer = vstring_alloc(10);
    }

    /*
     * This routine runs whenever a client connects to the socket dedicated
     * to the tlsmgr service (including wake up events sent by the master).
     * All connection-management stuff is handled by the common code in
     * multi_server.c.
     */
    if (tlsmgr_request_receive(client_stream, request) == 0) {

	/*
	 * Load session from cache.
	 */
	if (STREQ(STR(request), TLS_MGR_REQ_LOOKUP)) {
	    if (attr_scan(client_stream, ATTR_FLAG_STRICT,
			  ATTR_TYPE_STR, TLS_MGR_ATTR_CACHE_TYPE, cache_type,
			  ATTR_TYPE_STR, TLS_MGR_ATTR_CACHE_ID, cache_id,
			  ATTR_TYPE_END) == 2) {
		for (ent = cache_table; ent->cache_label; ++ent)
		    if (strcmp(ent->cache_label, STR(cache_type)) == 0)
			break;
		if (ent->cache_label == 0) {
		    msg_warn("bogus cache type \"%s\" in \"%s\" request",
			     STR(cache_type), TLS_MGR_REQ_LOOKUP);
		    VSTRING_RESET(buffer);
		} else if (ent->cache_info == 0) {

		    /*
		     * Cache type valid, but not enabled
		     */
		    VSTRING_RESET(buffer);
		} else {
		    status = tls_scache_lookup(ent->cache_info,
					       STR(cache_id), buffer) ?
			TLS_MGR_STAT_OK : TLS_MGR_STAT_ERR;
		}
	    }
	    attr_print(client_stream, ATTR_FLAG_NONE,
		       ATTR_TYPE_INT, MAIL_ATTR_STATUS, status,
		       ATTR_TYPE_DATA, TLS_MGR_ATTR_SESSION,
		       LEN(buffer), STR(buffer),
		       ATTR_TYPE_END);
	}

	/*
	 * Save session to cache.
	 */
	else if (STREQ(STR(request), TLS_MGR_REQ_UPDATE)) {
	    if (attr_scan(client_stream, ATTR_FLAG_STRICT,
			  ATTR_TYPE_STR, TLS_MGR_ATTR_CACHE_TYPE, cache_type,
			  ATTR_TYPE_STR, TLS_MGR_ATTR_CACHE_ID, cache_id,
			  ATTR_TYPE_DATA, TLS_MGR_ATTR_SESSION, buffer,
			  ATTR_TYPE_END) == 3) {
		for (ent = cache_table; ent->cache_label; ++ent)
		    if (strcmp(ent->cache_label, STR(cache_type)) == 0)
			break;
		if (ent->cache_label == 0) {
		    msg_warn("bogus cache type \"%s\" in \"%s\" request",
			     STR(cache_type), TLS_MGR_REQ_UPDATE);
		} else if (ent->cache_info != 0) {
		    status =
			tls_scache_update(ent->cache_info, STR(cache_id),
					  STR(buffer), LEN(buffer)) ?
			TLS_MGR_STAT_OK : TLS_MGR_STAT_ERR;
		}
	    }
	    attr_print(client_stream, ATTR_FLAG_NONE,
		       ATTR_TYPE_INT, MAIL_ATTR_STATUS, status,
		       ATTR_TYPE_END);
	}

	/*
	 * Delete session from cache.
	 */
	else if (STREQ(STR(request), TLS_MGR_REQ_DELETE)) {
	    if (attr_scan(client_stream, ATTR_FLAG_STRICT,
			  ATTR_TYPE_STR, TLS_MGR_ATTR_CACHE_TYPE, cache_type,
			  ATTR_TYPE_STR, TLS_MGR_ATTR_CACHE_ID, cache_id,
			  ATTR_TYPE_END) == 2) {
		for (ent = cache_table; ent->cache_label; ++ent)
		    if (strcmp(ent->cache_label, STR(cache_type)) == 0)
			break;
		if (ent->cache_label == 0) {
		    msg_warn("bogus cache type \"%s\" in \"%s\" request",
			     STR(cache_type), TLS_MGR_REQ_DELETE);
		} else if (ent->cache_info != 0) {
		    status = tls_scache_delete(ent->cache_info,
					       STR(cache_id)) ?
			TLS_MGR_STAT_OK : TLS_MGR_STAT_ERR;
		}
	    }
	    attr_print(client_stream, ATTR_FLAG_NONE,
		       ATTR_TYPE_INT, MAIL_ATTR_STATUS, status,
		       ATTR_TYPE_END);
	}

	/*
	 * RFC 5077 TLS session ticket keys
	 */
	else if (STREQ(STR(request), TLS_MGR_REQ_TKTKEY)) {
	    if (attr_scan(client_stream, ATTR_FLAG_STRICT,
			  ATTR_TYPE_DATA, TLS_MGR_ATTR_KEYNAME, buffer,
			  ATTR_TYPE_END) == 1) {
		if (LEN(buffer) != 0 && LEN(buffer) != TLS_TICKET_NAMELEN) {
		    msg_warn("invalid session ticket key name length: %ld",
			     (long) LEN(buffer));
		    VSTRING_RESET(buffer);
		} else if (*smtpd_cache.cache_timeout <= 0) {
		    status = TLS_MGR_STAT_ERR;
		    VSTRING_RESET(buffer);
		} else {
		    status = tlsmgr_key(buffer, *smtpd_cache.cache_timeout);
		}
	    }
	    attr_print(client_stream, ATTR_FLAG_NONE,
		       ATTR_TYPE_INT, MAIL_ATTR_STATUS, status,
		       ATTR_TYPE_DATA, TLS_MGR_ATTR_KEYBUF,
		       LEN(buffer), STR(buffer),
		       ATTR_TYPE_END);
	}

	/*
	 * Entropy request.
	 */
	else if (STREQ(STR(request), TLS_MGR_REQ_SEED)) {
	    if (attr_scan(client_stream, ATTR_FLAG_STRICT,
			  ATTR_TYPE_INT, TLS_MGR_ATTR_SIZE, &len,
			  ATTR_TYPE_END) == 1) {
		VSTRING_RESET(buffer);
		if (len <= 0 || len > 255) {
		    msg_warn("bogus seed length \"%d\" in \"%s\" request",
			     len, TLS_MGR_REQ_SEED);
		} else {
		    VSTRING_SPACE(buffer, len);
		    RAND_bytes((unsigned char *) STR(buffer), len);
		    VSTRING_AT_OFFSET(buffer, len);	/* XXX not part of the
							 * official interface */
		    status = TLS_MGR_STAT_OK;
		}
	    }
	    attr_print(client_stream, ATTR_FLAG_NONE,
		       ATTR_TYPE_INT, MAIL_ATTR_STATUS, status,
		       ATTR_TYPE_DATA, TLS_MGR_ATTR_SEED,
		       LEN(buffer), STR(buffer),
		       ATTR_TYPE_END);
	}

	/*
	 * Caching policy request.
	 */
	else if (STREQ(STR(request), TLS_MGR_REQ_POLICY)) {
	    int     cachable = 0;
	    int     timeout = 0;

	    if (attr_scan(client_stream, ATTR_FLAG_STRICT,
			  ATTR_TYPE_STR, TLS_MGR_ATTR_CACHE_TYPE, cache_type,
			  ATTR_TYPE_END) == 1) {
		for (ent = cache_table; ent->cache_label; ++ent)
		    if (strcmp(ent->cache_label, STR(cache_type)) == 0)
			break;
		if (ent->cache_label == 0) {
		    msg_warn("bogus cache type \"%s\" in \"%s\" request",
			     STR(cache_type), TLS_MGR_REQ_POLICY);
		} else {
		    cachable = (ent->cache_info != 0) ? 1 : 0;
		    timeout = *ent->cache_timeout;
		    status = TLS_MGR_STAT_OK;
		}
	    }
	    attr_print(client_stream, ATTR_FLAG_NONE,
		       ATTR_TYPE_INT, MAIL_ATTR_STATUS, status,
		       ATTR_TYPE_INT, TLS_MGR_ATTR_CACHABLE, cachable,
		       ATTR_TYPE_INT, TLS_MGR_ATTR_SESSTOUT, timeout,
		       ATTR_TYPE_END);
	}

	/*
	 * Master trigger. Normally, these triggers arrive only after some
	 * other process requested the tlsmgr's service. The purpose is to
	 * restart the tlsmgr after it aborted due to a fatal run-time error,
	 * so that it can continue its housekeeping even while nothing is
	 * using TLS.
	 * 
	 * XXX Which begs the question, if TLS isn't used often, do we need a
	 * tlsmgr background process? It could terminate when the session
	 * caches are empty.
	 */
	else if (STREQ(STR(request), wakeup)) {
	    if (msg_verbose)
		msg_info("received master trigger");
	    multi_server_disconnect(client_stream);
	    return;				/* NOT: vstream_fflush */
	}
    }

    /*
     * Protocol error.
     */
    else {
	attr_print(client_stream, ATTR_FLAG_NONE,
		   ATTR_TYPE_INT, MAIL_ATTR_STATUS, TLS_MGR_STAT_FAIL,
		   ATTR_TYPE_END);
    }
    vstream_fflush(client_stream);
}