/* * Copyright (c) 2003-2005 Sendmail, Inc. and its suppliers. * All rights reserved. * * By using this file, you agree to the terms and conditions set * forth in the LICENSE file which can be found at the top level of * the sendmail distribution. */ #include "sm/generic.h" SM_RCSID("@(#)$Id: addr.c,v 1.78 2007/11/14 06:03:08 ca Exp $") #include "sm/error.h" #include "sm/assert.h" #include "sm/io.h" #include "sm/net.h" #include "sm/rcb.h" #include "sm/mta.h" #include "sm/rfc2821.h" #include "smar.h" #include "log.h" #include "sm/reccom.h" /* ** Note: it might be useful to keep a small "cache" of these struct's ** around for reuse to avoid all those alloc()/free() calls. */ /* ** SMAR_ADDR_NEW -- Create a SMAR ADDR context ** ** Parameters: ** smar_clt_ctx -- SMAR client context ** psmar_addr -- (pointer to) SMAR ADDR context (output) ** ** Returns: ** usual sm_error code ** ** Last code review: 2004-03-11 17:57:46 ** Last code change: */ sm_ret_T smar_addr_new(smar_clt_ctx_P smar_clt_ctx, smar_addr_P *psmar_addr) { smar_addr_P smar_addr; SM_REQUIRE(psmar_addr != NULL); smar_addr = (smar_addr_P) sm_zalloc(sizeof(*smar_addr)); if (NULL == smar_addr) return sm_error_temp(SM_EM_AR, ENOMEM); smar_addr->ara_smar_clt_ctx = smar_clt_ctx; smar_addr->ara_rpool = sm_rpool_new(NULL); if (NULL == smar_addr->ara_rpool) { sm_free_size(smar_addr, sizeof(*smar_addr)); return sm_error_temp(SM_EM_AR, ENOMEM); } #if 0 /* set larger poolsize? */ sm_rpool_setsizes(smar_addr->ara_rpool, poolsize, 0, 0); #endif smar_addr->sm_magic = SM_SMAR_ADDR_MAGIC; *psmar_addr = smar_addr; return SM_SUCCESS; } /* ** SMAR_ADDR_FREE -- Free a SMAR ADDR context ** ** Parameters: ** smar_addr -- SMAR ADDR context ** ** Returns: ** usual sm_error code ** ** Locking: none, smar_addr must be under control of caller ** ** Last code review: 2004-03-11 18:00:01 ** Last code change: 2006-03-31 06:24:40 */ sm_ret_T smar_addr_free(smar_addr_P smar_addr) { int r; if (NULL == smar_addr) return SM_SUCCESS; SM_IS_SMAR_ADDR(smar_addr); /* free other data... */ SM_STR_FREE(smar_addr->ara_pa); SM_STR_FREE(smar_addr->ara_pa2); SM_STR_FREE(smar_addr->ara_str); SM_STR_FREE(smar_addr->ara_tag); SM_STR_FREE(smar_addr->ara_detail); SM_STR_FREE(smar_addr->ara_domain_pa); SM_STR_FREE(smar_addr->ara_rhs); SM_STR_FREE(smar_addr->ara_user2); SM_STR_FREE(smar_addr->ara_detail2); SM_STR_FREE(smar_addr->ara_domain_pa2); SM_STR_FREE(smar_addr->ara_rhs2); SM_STR_FREE(smar_addr->ara_rhs_conf); SM_STR_FREE(smar_addr->ara_ipv4_str); SM_CSTR_FREE(smar_addr->ara_rvrs_hostname); for (r = 0; r < SM_MAX_DNSBL; r++) SM_FREE(smar_addr->ara_dnsblres[r].sdbr_ipv4s); if (smar_addr->ara_rcbe != NULL) { sm_rcbe_free(smar_addr->ara_rcbe); smar_addr->ara_rcbe = NULL; } if (smar_addr->ara_str_map != NULL) { sm_map_close(smar_addr->ara_str_map, 0); smar_addr->ara_str_map = NULL; } if (SMARA_IS_FLAG(smar_addr, SMARA_FL_HASMUT)) { r = pthread_mutex_destroy(&smar_addr->ara_mutex); SM_ASSERT(0 == r); SMARA_CLR_FLAG(smar_addr, SMARA_FL_HASMUT); } if (SMARA_IS_FLAG(smar_addr, SMARA_FL_HASCOND)) { r = pthread_cond_destroy(&smar_addr->ara_cond); SM_ASSERT(0 == r); SMARA_CLR_FLAG(smar_addr, SMARA_FL_HASCOND); } /* ** Note: do not free ** smar_addr->ara_rcpt nor smar_addr->ara_rcpts ** here! */ sm_rpool_delete(smar_addr->ara_rpool); smar_addr->sm_magic = SM_MAGIC_NULL; sm_free_size(smar_addr, sizeof(*smar_addr)); return SM_SUCCESS; } /* ** SMAR_ADDR_RE -- Send reply for an address check ** ** Parameters: ** smar_addr -- address context ** off -- possible offset in return string ** ** Returns: ** usual sm_error code ** ** Locking: none, smar_addr must be under control of caller ** ** Last code review: 2004-03-11 18:00:35 ** Last code change: */ sm_ret_T smar_addr_re(smar_addr_P smar_addr, uint off) { sm_rcb_P rcb; sm_ret_T ret; SM_IS_SMAR_ADDR(smar_addr); rcb = &smar_addr->ara_rcbe->rcbe_rcb; ret = sm_rcb_putv(rcb, RCB_PUTV_FIRST, SM_RCBV_INT, RT_PROT_VER, PROT_VER_RT, SM_RCBV_INT, RT_A2S_ID, smar_addr->ara_id, SM_RCBV_BUF, RT_A2S_TAID, smar_addr->ara_taid, SMTP_STID_SIZE, /* dummy for consistency */ SM_RCBV_INT, RT_A2S_MAP_RES, SM_ACC_FOUND, SM_RCBV_INT, RT_A2S_RCPT_ST, smar_addr->ara_status|smar_addr->ara_rflags, SM_RCBV_END); if (ret == SM_SUCCESS && smar_addr->ara_rhs != NULL && sm_str_getlen(smar_addr->ara_rhs) > 3) { if (off > 0) { SM_ASSERT(sm_str_getlen(smar_addr->ara_rhs) > off); ret = sm_rcb_putv(rcb, RCB_PUTV_NONE, SM_RCBV_BUF, RT_A2S_STATT, sm_str_getdata(smar_addr->ara_rhs) + off, sm_str_getlen(smar_addr->ara_rhs) - off, SM_RCBV_END); } else { ret = sm_rcb_putv(rcb, RCB_PUTV_NONE, SM_RCBV_STR, RT_A2S_STATT, smar_addr->ara_rhs, SM_RCBV_END); } } return ret; } /* ** SMAR_ADDR_CHK -- Check address ** If an address maps (via mt) to LMTP (see hack), then check ** whether the local part is in the "alias" or "local user" map. ** ** Parameters: ** smar_ctx -- SMAR context ** smar_addr -- address context ** ** Returns: ** usual sm_error code ** if ok: smar_addr->ara_status should have result of lookup(s) ** ** Called by: ** ** Locking: smar_addr must be under control of the caller ** ** Last code review: 2004-03-11 18:06:39; see comments ** Last code change: */ sm_ret_T smar_addr_chk(smar_ctx_P smar_ctx, smar_addr_P smar_addr) { sm_ret_T ret; smar_clt_ctx_P smar_clt_ctx; char *ipv4s; int flags; bool islocal, hasdetail; uchar delim; sm_str_P str, rhs, detail, domain, mtstr; sm_a2821_T a_rcpt, a_domain, a_local; sm_a2821_P pdomain, plocal; SM_IS_SMAR_ADDR(smar_addr); SM_IS_SMAR_CTX(smar_ctx); smar_clt_ctx = smar_addr->ara_smar_clt_ctx; SM_IS_SMAR_CLT_CTX(smar_clt_ctx); ret = SM_SUCCESS; A2821_INIT_RP(&a_rcpt, smar_addr->ara_rpool); A2821_INIT_RP(&a_domain, smar_addr->ara_rpool); A2821_INIT_RP(&a_local, smar_addr->ara_rpool); pdomain = &a_domain; /* pdomain is just pointer to a_domain! */ plocal = &a_local; /* plocal is just pointer to a_local! */ smar_addr->ara_status = SMTP_R_REJECT; str = rhs = detail = domain = mtstr = NULL; islocal = false; hasdetail = false; ret = t2821_scan((sm_rdstr_P) smar_addr->ara_pa, &a_rcpt, 0); if (sm_is_err(ret)) goto done; flags = R2821_CORRECT & ~R2821_AT; ret = t2821_parse(&a_rcpt, flags); if (sm_is_err(ret)) goto done; /* fixme: should these be part of smar_addr? */ str = sm_str_new(smar_addr->ara_rpool, MAXADDRLEN, MAXADDRLEN + 2); if (NULL == str) goto done; /* temp fail? */ domain = sm_str_new(smar_addr->ara_rpool, MAXADDRLEN, MAXADDRLEN + 2); if (NULL == domain) goto done; /* temp fail? */ detail = sm_str_new(smar_addr->ara_rpool, MAXADDRLEN >> 1, MAXADDRLEN); if (NULL == detail) goto done; /* temp fail? */ rhs = sm_str_new(smar_addr->ara_rpool, MAXADDRLEN, MAXADDRLEN + 2); if (NULL == rhs) goto done; /* temp fail? */ /* fixme: use different length? */ mtstr = sm_str_new(smar_addr->ara_rpool, MAXADDRLEN, MAXADDRLEN + 2); if (NULL == mtstr) goto done; /* temp fail? */ hasdetail = t2821_parts(&a_rcpt, smar_ctx->smar_cnf.smar_cnf_addr_delim, true, str, detail, NULL, &delim); if (sm_is_err(hasdetail)) { SMAR_LEV_DPRINTF(5, (SMAR_DEBFP, "func=smar_addr_chk, t2821_parts=%r\n", hasdetail)); goto done; } /* extract domain out of rcpt_a ... and look it up */ ret = t2821_extract_domain(smar_addr->ara_rpool, &a_rcpt, &pdomain); if (sm_is_err(ret)) { if (ret == sm_error_perm(SM_EM_ADDR, R2821_ERR_FQDN)) islocal = true; } else { ret = t2821_str(pdomain, domain, 0); if (sm_is_err(ret)) goto done; sm_str2lower(domain); sm_str_clr(mtstr); if (SMAR_MT_FULL_LOOKUP(smar_ctx)) { uint32_t lfl; lfl = smar_ctx->smar_mt_lfl; if (hasdetail <= 0) lfl &= ~SMMAP_LFL_DET; ret = sm_map_lookup_addr(smar_ctx->smar_mt_map, str, (hasdetail > 0) ? detail : NULL, domain, /* tag */ NULL, delim, lfl, mtstr); } else ret = sm_map_lookup(smar_ctx->smar_mt_map, 0, domain, mtstr); if (SM_SUCCESS == ret) ipv4s = (char *) sm_str_getdata(mtstr); else ipv4s = NULL; SMAR_LEV_DPRINTF(3, (SMAR_DEBFP, "func=smar_addr_chk, domain=%S, ipv4s=%.256s\n", domain, ipv4s)); /* HACK ahead, see also smtpc/smtpc.c */ if (ipv4s == NULL || (strcmp(ipv4s, LMTP_IPV4_S2) != 0 && strcmp(ipv4s, LMTP_MT) != 0)) { /* entry not found: "CONTINUE" further checks */ smar_addr->ara_status = SMTP_R_CONT; } else islocal = true; } if (islocal) { /* ** Extract localpart and look it up to decide whether it ** should be accepted. */ /* ** Check localpart. ** another map is required, or some different format, e.g., ** To:localpart@ OK ** should be used (which, however, causes problems if ** a different map like passwd is used). ** Specify some bdb map? ** We could simply use aliases for now: ** if it exists: ok, otherwise: reject ** aliases can then also be used for the next step. ** ToDo: Enhance dealing with temporary errors. */ #if 0 /* lower case local parts! */ sm_str2lower(str); sm_str2lower(detail); #endif ret = smar_addr_lu(smar_ctx, str, delim, hasdetail > 0 ? detail : NULL, domain, rhs, &smar_addr->ara_status); if (SM_IS_FLAG(smar_addr->ara_ltype, SMARA_LT_RCPT_LCL_R)) SMAR_SET_RFL(smar_addr, SMAR_R_LOC_RCPT); } done: SMAR_LEV_DPRINTF(4, (SMAR_DEBFP, "func=smar_addr_chk, where=reply, ret=%r, status=%d\n", ret, smar_addr->ara_status)); a2821_free(&a_domain); a2821_free(&a_rcpt); SM_STR_FREE(str); SM_STR_FREE(domain); SM_STR_FREE(detail); SM_STR_FREE(rhs); SM_STR_FREE(mtstr); return SM_SUCCESS; } /* ** SMAR_ADDR_CHECK -- Check address; send reply ** If an address maps (via mt) to LMTP (see hack), then check ** whether the local part is in the "alias" or "local user" map. ** ** Parameters: ** smar_ctx -- SMAR context ** smar_addr -- address context ** ** Returns: ** usual sm_error code ** ** Called by: smar_react() ** ** Locking: smar_addr has been created by smar_react(), ** this is the only function that has been given access ** ** Last code review: ** Last code change: */ sm_ret_T smar_addr_check(smar_ctx_P smar_ctx, smar_addr_P smar_addr) { sm_ret_T ret; smar_clt_ctx_P smar_clt_ctx; SM_IS_SMAR_ADDR(smar_addr); SM_IS_SMAR_CTX(smar_ctx); smar_clt_ctx = smar_addr->ara_smar_clt_ctx; SM_IS_SMAR_CLT_CTX(smar_clt_ctx); ret = smar_addr_chk(smar_ctx, smar_addr); if (sm_is_err(ret)) goto error; if (smar_addr->ara_status == SMTP_R_CONT && SM_IS_FLAG(smar_addr->ara_ltype, SMARA_LT_RCPT_ACC|SMARA_LT_RCPT_PROT)) { /* NOTE: This does double work, e.g., scanning, parsing */ ret = smar_access_chk(smar_ctx, smar_addr); if (sm_is_err(ret)) goto error; } SMAR_LEV_DPRINTF(4, (SMAR_DEBFP, "func=smar_addr_check, where=reply, ret=%r, status=%d, ltype=%#x\n", ret, smar_addr->ara_status, smar_addr->ara_ltype)); ret = smar_addr_re(smar_addr, smar_addr->ara_rhstagoff); ret = sm_rcbcom_endrep(&smar_clt_ctx->smac_com_ctx, smar_clt_ctx->smac_com_ctx.rcbcom_tsk, false, &smar_addr->ara_rcbe); if (sm_is_err(ret)) goto error; smar_addr_free(smar_addr); return ret; error: smar_addr_free(smar_addr); sm_log_write(smar_ctx->smar_lctx, AR_LCAT_RESOLVER, AR_LMOD_RESOLVER, SM_LOG_ERROR, 0, "sev=ERROR, func=smar_addr_check, ret=%m", ret); return ret; }