/*
* 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:<address> (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;
}
syntax highlighted by Code2HTML, v. 0.9.1