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); }
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); }
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); }
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); }
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); } }
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); } }
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); }
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); }
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); }
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); }
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); }
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); }
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)); }
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 */ }
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); }
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); }
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); } }
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); }