/* * Copyright (c) 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: protected.c,v 1.16 2007/11/14 06:03:08 ca Exp $") #include "sm/error.h" #include "sm/assert.h" #include "sm/memops.h" #include "sm/ctype.h" #include "sm/io.h" #include "sm/net.h" #include "sm/rcb.h" #include "sm/map.h" #include "sm/mta.h" #include "sm/net.h" #include "sm/rfc2821.h" #include "smar.h" #include "log.h" #include "sm/reccom.h" /* ** Implementation of "protected recipients": ** access map: ** protectedrpct:address list of restrictions ** address is stored in ara_pa and its components are in the various ** ara_ fields (ara_detail, ara_domain_pa) ** envelope sender is stored in ara_pa2 ** client ip address is stored in ara_ipv4 ** the restrictions are in ara_rhs */ /* ** SMAR_PROT_LIST -- Check "List:" entry for "protected rcpt" ** ** Parameters: ** smar_ctx -- SMAR context ** smar_addr -- address context ** offset -- begin of List: address (without List:) ** (output: end of address) ** ** Returns: ** usual sm_error code ** if ok: smar_addr->ara_status should have result of lookup(s) ** ** Locking: smar_addr must be under control of the caller. ** ** Last code review: ** Last code change: */ static sm_ret_T smar_prot_list(smar_ctx_P smar_ctx, smar_addr_P smar_addr, uint *poffset) { sm_ret_T ret; int offset; smar_rcpts_P smar_rcpts; smar_rcpt_P smar_rcpt; sm_a2821_T a_rcpt; sm_a2821_P prcpt; SM_REQUIRE(poffset != NULL); prcpt = &a_rcpt; /* prcpt is just pointer to a_rcpt */ A2821_INIT_RP(prcpt, NULL); smar_rcpt = NULL; smar_rcpts = NULL; /* how to find the end of the address? parse it... */ a2821_free(prcpt); A2821_INIT_RP(prcpt, NULL); offset = t2821_scan((sm_rdstr_P) smar_addr->ara_rhs, prcpt, *poffset); if (sm_is_err(offset)) { ret = offset; sm_log_write(smar_ctx->smar_lctx, AR_LCAT_RESOLVER, AR_LMOD_RESOLVER, SM_LOG_ERROR, 3, "sev=ERROR, func=smar_prot_list, rhs=%.256S, t2821_scan=%m" , smar_addr->ara_rhs, ret); goto error; } ret = t2821_parse(prcpt, R2821_CORRECT); if (sm_is_err(ret)) { sm_log_write(smar_ctx->smar_lctx, AR_LCAT_RESOLVER, AR_LMOD_RESOLVER, SM_LOG_ERROR, 3, "sev=ERROR, func=smar_prot_list, rhs=%.256S, t2821_parse=%m" , smar_addr->ara_rhs, ret); goto error; } SM_ASSERT(offset >= 0); *poffset = (uint) offset; /* set new index to end of current address */ if (NULL == smar_addr->ara_pa2) { if (!SMAR_IS_FLAG(smar_ctx, SMAR_FL_PROTMAILCOMPL)) { sm_log_write(smar_ctx->smar_lctx, AR_LCAT_RESOLVER, AR_LMOD_RESOLVER, SM_LOG_ERROR, 4, "sev=ERROR, func=smar_prot_list, status=got list: restriction but allow_by=sender is not set"); /* NOT locked! */ SMAR_SET_FLAG(smar_ctx, SMAR_FL_PROTMAILCOMPL); } goto done; } if (SMARA_IS_FLAG(smar_addr, SMARA_FL_PA2EMPTY)) goto done; sm_str_clr(smar_addr->ara_str); ret = t2821_str(prcpt, smar_addr->ara_str, 0); if (sm_is_err(ret)) { SMAR_LEV_DPRINTF(3, (SMAR_DEBFP, "sev=DBG, func=smar_prot_list, t2821_str=%m\n", ret)); goto error; } /* ** Create these if necessary. ** Note: it's not necessary to create all the data, e.g., bht. */ smar_rcpts = smar_addr->ara_rcpts; smar_rcpt = smar_addr->ara_rcpt; if (NULL == smar_rcpts) { ret = smar_rcpts_new(smar_ctx, smar_addr->ara_smar_clt_ctx, &smar_addr->ara_rcpts); if (sm_is_err(ret)) goto error; smar_rcpts = smar_addr->ara_rcpts; } if (NULL == smar_rcpt) { ret = smar_rcpt_new(&smar_addr->ara_rcpt); if (sm_is_err(ret)) goto error; smar_rcpt = smar_addr->ara_rcpt; } SM_IS_SMAR_RCPT(smar_rcpt); SM_IS_SMAR_RCPTS(smar_rcpts); smar_rcpts->arrs_flags = SMARRS_FL_NOREC|SMARRS_FL_NOADD|SMARRS_FL_COMP; smar_rcpts->arrs_pa = smar_addr->ara_pa2; smar_rcpts->arrs_addr = smar_addr; smar_rcpt->arr_pa = smar_addr->ara_pa; SMARRQ_SET_FLAG(smar_rcpt, SMARRQ_FL_ALIAS); ret = smar_rcpt_expand(smar_addr->ara_rcpts, smar_addr->ara_rcpt, 0, 0, 0); sm_log_write(smar_ctx->smar_lctx, AR_LCAT_RESOLVER, AR_LMOD_RESOLVER, SM_LOG_INFO, 13, "sev=INFO, func=smar_prot_list, smar_rcpt_expand=%m, found=%d" , ret, SMARRS_IS_FLAG(smar_rcpts, SMARRS_FL_FOUND)); if (sm_is_success(ret) && SMARRS_IS_FLAG(smar_rcpts, SMARRS_FL_FOUND)) { smar_addr->ara_status = SMTP_R_OK; smar_addr->ara_mapres = SM_ACC_FOUND; } /* ** clean out smar_addr->ara_rcpts and smar_addr->ara_rcpt ??? ** for now just free them, reuse later on? ** check whether requirements for function are fulfilled! ** e.g, SMARR_FL_ORCPT */ done: if (smar_rcpts != NULL && smar_rcpts->arrs_pa != NULL) smar_rcpts->arrs_pa = NULL; if (smar_rcpt != NULL && smar_rcpt->arr_pa != NULL) smar_rcpt->arr_pa = NULL; (void) smar_rcpt_free(smar_addr->ara_rcpt, smar_addr->ara_rcpts); (void) smar_rcpts_free(smar_addr->ara_rcpts); smar_addr->ara_rcpts = NULL; smar_addr->ara_rcpt = NULL; a2821_free(prcpt); return ret; error: /* identical to "done:", merge those two? */ if (smar_rcpts != NULL && smar_rcpts->arrs_pa != NULL) smar_rcpts->arrs_pa = NULL; if (smar_rcpt != NULL && smar_rcpt->arr_pa != NULL) smar_rcpt->arr_pa = NULL; (void) smar_rcpt_free(smar_addr->ara_rcpt, smar_addr->ara_rcpts); (void) smar_rcpts_free(smar_addr->ara_rcpts); smar_addr->ara_rcpts = NULL; smar_addr->ara_rcpt = NULL; a2821_free(prcpt); return ret; } /* ** SMAR_PROT_FROM -- Check "from:" entry for "protected rcpt" ** ** Parameters: ** smar_ctx -- SMAR context ** smar_addr -- address context ** offset -- begin of From: address (without From:) ** (output: end of address) ** ** Returns: ** usual sm_error code ** if ok: smar_addr->ara_status should have result of lookup(s) ** ** Locking: smar_addr must be under control of the caller. ** ** Last code review: ** Last code change: */ static sm_ret_T smar_prot_from(smar_ctx_P smar_ctx, smar_addr_P smar_addr, uint *poffset) { sm_ret_T ret; int offset; sm_a2821_T a_rcpt; sm_a2821_P prcpt; SM_REQUIRE(poffset != NULL); prcpt = &a_rcpt; /* prcpt is just pointer to a_rcpt */ A2821_INIT_RP(prcpt, NULL); /* how to find the end of the address? parse it... */ a2821_free(prcpt); A2821_INIT_RP(prcpt, NULL); offset = t2821_scan((sm_rdstr_P) smar_addr->ara_rhs, prcpt, *poffset); if (sm_is_err(offset)) { ret = offset; sm_log_write(smar_ctx->smar_lctx, AR_LCAT_RESOLVER, AR_LMOD_RESOLVER, SM_LOG_ERROR, 3, "sev=ERROR, func=smar_prot_from, rhs=%.256S, t2821_scan=%m" , smar_addr->ara_rhs, ret); goto error; } ret = t2821_parse(prcpt, R2821_CORRECT|R2821_EMPTY); if (sm_is_err(ret)) { sm_log_write(smar_ctx->smar_lctx, AR_LCAT_RESOLVER, AR_LMOD_RESOLVER, SM_LOG_ERROR, 3, "sev=ERROR, func=smar_prot_list, rhs=%.256S, t2821_parse=%m" , smar_addr->ara_rhs, ret); goto error; } SM_ASSERT(offset >= 0); *poffset = (uint) offset; /* set new index to end of current address */ if (NULL == smar_addr->ara_pa2) { if (!SMAR_IS_FLAG(smar_ctx, SMAR_FL_PROTMAILCOMPL)) { sm_log_write(smar_ctx->smar_lctx, AR_LCAT_RESOLVER, AR_LMOD_RESOLVER, SM_LOG_ERROR, 4, "sev=ERROR, func=smar_prot_from, status=got from: restriction but allow_by=sender is not set"); /* NOT locked! */ SMAR_SET_FLAG(smar_ctx, SMAR_FL_PROTMAILCOMPL); } goto done; } sm_str_clr(smar_addr->ara_str); ret = t2821_str(prcpt, smar_addr->ara_str, (SMARA_IS_LFLAG(smar_addr, SMARA_LFL_PROTMAP) && !SMARA_IS_FLAG(smar_addr, SMARA_FL_PA2EMPTY)) ? T2821_FL_NOANGLE : 0); if (sm_is_err(ret)) { SMAR_LEV_DPRINTF(3, (SMAR_DEBFP, "sev=DBG, func=smar_prot_list, t2821_str=%m\n", ret)); goto error; } /* ** ara_str: parsed from:
(from rhs) ** need to parse ara_pa2 too (should be stored in smar_addr!) */ if (SMARA_IS_LFLAG(smar_addr, SMARA_LFL_PROTMAP) && !SMARA_IS_FLAG(smar_addr, SMARA_FL_PA2EMPTY)) { sm_str_clr(smar_addr->ara_rhs2); ret = sm_map_setopt(smar_addr->ara_str_map, SMPO_STR, smar_addr->ara_str, SMPO_END); if (sm_is_err(ret)) goto error; ret = sm_map_lookup_addr(smar_addr->ara_str_map, smar_addr->ara_user2, smar_addr->ara_detail2, smar_addr->ara_domain_pa2, NULL, smar_addr->ara_delim, /* Need to let caller (client) pass in other flags? */ SMMAP_LFL_ALL|SMMAP_LFL_NOAT| (SMARA_IS_LFLAG(smar_addr, SMARA_LFL_PROTIMPLDET) ? SMMAP_LFL_IMPLDET : 0), smar_addr->ara_rhs2); if (sm_is_success(ret)) { smar_addr->ara_status = SMTP_R_OK; smar_addr->ara_mapres = SM_ACC_FOUND; } else ret = SM_SUCCESS; } else if (sm_str_getlen(smar_addr->ara_pa2) == sm_str_getlen(smar_addr->ara_str) && strncmp((char *) sm_str_data(smar_addr->ara_pa2), (char *) sm_str_data(smar_addr->ara_str), sm_str_getlen(smar_addr->ara_str)) == 0) { smar_addr->ara_status = SMTP_R_OK; smar_addr->ara_mapres = SM_ACC_FOUND; } sm_log_write(smar_ctx->smar_lctx, AR_LCAT_RESOLVER, AR_LMOD_RESOLVER, SM_LOG_INFO, 13, "sev=INFO, func=smar_prot_from, status=%r" , smar_addr->ara_status); done: a2821_free(prcpt); return ret; error: a2821_free(prcpt); return ret; } /* ** SMAR_PROT_CLTADDR -- Check "cltaddr:" entry for "protected rcpt" ** ** Parameters: ** smar_ctx -- SMAR context ** smar_addr -- address context ** offset -- begin of cltaddr: address (without cltaddr:) ** (output: end of address) ** ** Returns: ** usual sm_error code ** if ok: smar_addr->ara_status should have result of lookup(s) ** ** Locking: smar_addr must be under control of the caller. ** ** Last code review: ** Last code change: */ static sm_ret_T smar_prot_cltaddr(smar_ctx_P smar_ctx, smar_addr_P smar_addr, uint *poffset) { sm_ret_T ret; uint l, offset; uchar uc; sm_str_P ipv4str; SM_REQUIRE(poffset != NULL); ret = SM_SUCCESS; ipv4str = NULL; offset = *poffset; l = sm_str_getlen(smar_addr->ara_rhs); sm_str_clr(smar_addr->ara_str); while (offset < l && sm_is_success(ret)) { uc = sm_str_rd_elem(smar_addr->ara_rhs, offset); if (!(ISDIGIT(uc) || uc == (uchar)'.')) break; ret = sm_str_put(smar_addr->ara_str, uc); ++offset; } if (offset > l || sm_is_err(ret)) goto error; ret = sm_str_term(smar_addr->ara_str); if (sm_is_err(ret)) goto error; /* SM_ASSERT(offset >= 0); */ *poffset = offset; /* set new index to end of current address */ if (!SMARA_IS_FLAG(smar_addr, SMARA_FL_RCVDIPV4)) { if (!SMAR_IS_FLAG(smar_ctx, SMAR_FL_PROTIPCOMPL)) { sm_log_write(smar_ctx->smar_lctx, AR_LCAT_RESOLVER, AR_LMOD_RESOLVER, SM_LOG_ERROR, 4, "sev=ERROR, func=smar_prot_cltaddr, status=got cltaddr: restriction but allow_by=client_ip is not set"); /* NOT locked! */ SMAR_SET_FLAG(smar_ctx, SMAR_FL_PROTIPCOMPL); } return SM_SUCCESS; } if (NULL == smar_addr->ara_ipv4_str) { ipv4str = sm_str_new(NULL, 18, 20); smar_addr->ara_ipv4_str = ipv4str; } else ipv4str = smar_addr->ara_ipv4_str; sm_str_clr(ipv4str); ret = sm_inet_ipv4str(smar_addr->ara_ipv4, ipv4str); if (sm_is_err(ret)) goto error; /* ** ara_str: parsed cltaddr:IP.ADD.RE.SS (from rhs) */ if (SMARA_IS_LFLAG(smar_addr, SMARA_LFL_PROTMAP)) { sm_str_clr(smar_addr->ara_rhs2); ret = sm_map_setopt(smar_addr->ara_str_map, SMPO_STR, smar_addr->ara_str, SMPO_END); if (sm_is_err(ret)) goto error; ret = sm_map_lookup_ip(smar_addr->ara_str_map, ipv4str, NULL, SMMAP_LFL_SUBNETS, smar_addr->ara_rhs2); if (sm_is_success(ret)) { smar_addr->ara_status = SMTP_R_OK; smar_addr->ara_mapres = SM_ACC_FOUND; } else ret = SM_SUCCESS; } else if (sm_str_getlen(ipv4str) == sm_str_getlen(smar_addr->ara_str) && strncmp((char *) sm_str_data(ipv4str), (char *) sm_str_data(smar_addr->ara_str), sm_str_getlen(smar_addr->ara_str)) == 0) { /* scan the IPv4 address and just compare those?? */ smar_addr->ara_status = SMTP_R_OK; smar_addr->ara_mapres = SM_ACC_FOUND; } sm_log_write(smar_ctx->smar_lctx, AR_LCAT_RESOLVER, AR_LMOD_RESOLVER, SM_LOG_INFO, 13, "sev=INFO, func=smar_prot_cltaddr, ipv4=%#S, str=%#S, status=%r" , ipv4str, smar_addr->ara_str, smar_addr->ara_status); return ret; error: if (sm_is_success(ret)) ret = sm_err_perm(EINVAL); /* XXX */ sm_log_write(smar_ctx->smar_lctx, AR_LCAT_RESOLVER, AR_LMOD_RESOLVER, SM_LOG_INFO, 13, "sev=INFO, func=smar_prot_cltaddr, ret=%m", ret); return ret; } /* ** SMAR_PROT_RHS -- Go through elements in the RHS of a "protected rcpt" ** ** 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) ** ** Locking: smar_addr must be under control of the caller. ** ** Last code review: ** Last code change: */ sm_ret_T smar_prot_rhs(smar_ctx_P smar_ctx, smar_addr_P smar_addr) { sm_ret_T ret; uint rhslen, rhsoff; char *rhsc; rhsc = (char *) sm_str_getdata(smar_addr->ara_rhs); if (NULL == rhsc) return sm_error_temp(SM_EM_AR, ENOMEM); rhslen = sm_str_getlen(smar_addr->ara_rhs); rhsoff = 0; ret = SM_SUCCESS; smar_addr->ara_status = SMTP_R_CONT; while (rhsoff < rhslen && smar_addr->ara_status == SMTP_R_CONT && sm_is_success(ret)) { while (rhsoff < rhslen && ISSPACE(*(rhsc + rhsoff))) ++rhsoff; if (rhsoff >= rhslen) break; if ((rhslen > RHS_LIST_TAG_LEN + rhsoff) && strncasecmp(rhsc + rhsoff, RHS_LIST_TAG, RHS_LIST_TAG_LEN) == 0) { rhsoff += RHS_LIST_TAG_LEN; ret = smar_prot_list(smar_ctx, smar_addr, &rhsoff); } else if ((rhslen > MAIL_TAG_LEN + rhsoff) && strncasecmp(rhsc + rhsoff, MAIL_TAG, MAIL_TAG_LEN) == 0) { rhsoff += MAIL_TAG_LEN; ret = smar_prot_from(smar_ctx, smar_addr, &rhsoff); } else if ((rhslen > CLT_A_TAG_LEN + rhsoff) && strncasecmp(rhsc + rhsoff, CLT_A_TAG, CLT_A_TAG_LEN) == 0) { rhsoff += CLT_A_TAG_LEN; ret = smar_prot_cltaddr(smar_ctx, smar_addr, &rhsoff); } else { sm_log_write(smar_ctx->smar_lctx, AR_LCAT_RESOLVER, AR_LMOD_RESOLVER, SM_LOG_ERROR, 8, "sev=ERROR, func=smar_prot_rhs, rhs=%.256S, status=unknown_entry" , smar_addr->ara_rhs); ret = sm_err_perm(EINVAL); smar_addr->ara_status = SMTP_R_TEMP; } } /* default: reject */ if (SMTP_R_CONT == smar_addr->ara_status) { /* set some status text to include ESC?? */ /* 5.7.2 for mailing list, 5.7.1 for others */ smar_addr->ara_status = SMTP_R_REJECT; smar_addr->ara_mapres = SM_ACC_FOUND; } return ret; }