📄 srvrsmtp.c
字号:
}
/* crude way to avoid denial-of-service attacks */
(void) checksmtpattack(&n_etrn, MAXETRNCOMMANDS, TRUE,
"ETRN", e);
/* do config file checking of the parameter */
if (rscheck("check_etrn", p, NULL, e, TRUE, FALSE, 4)
!= EX_OK || Errors > 0)
break;
if (LogLevel > 5)
sm_syslog(LOG_INFO, e->e_id,
"%.100s: ETRN %s",
CurSmtpClient,
shortenstring(p, MAXSHORTSTR));
id = p;
if (*id == '@')
id++;
else
*--id = '@';
if ((new = (QUEUE_CHAR *)malloc(sizeof(QUEUE_CHAR))) == NULL)
{
syserr("500 5.5.0 ETRN out of memory");
break;
}
new->queue_match = id;
new->queue_next = NULL;
QueueLimitRecipient = new;
ok = runqueue(TRUE, FALSE);
free(QueueLimitRecipient);
QueueLimitRecipient = NULL;
if (ok && Errors == 0)
message("250 2.0.0 Queuing for node %s started", p);
break;
case CMDHELP: /* help -- give user info */
help(p, e);
break;
case CMDNOOP: /* noop -- do nothing */
(void) checksmtpattack(&n_noop, MAXNOOPCOMMANDS, TRUE,
"NOOP", e);
message("250 2.0.0 OK");
break;
case CMDQUIT: /* quit -- leave mail */
message("221 2.0.0 %s closing connection", MyHostName);
/* arrange to ignore any current send list */
e->e_sendqueue = NULL;
# if STARTTLS
/* shutdown TLS connection */
if (tls_active)
{
(void) endtls(srv_ssl, "server");
tls_active = FALSE;
}
# endif /* STARTTLS */
# if SASL
if (authenticating == SASL_IS_AUTH)
{
sasl_dispose(&conn);
authenticating = SASL_NOT_AUTH;
}
# endif /* SASL */
doquit:
/* avoid future 050 messages */
disconnect(1, e);
# if _FFR_MILTER
/* close out milter filters */
milter_quit(e);
# endif /* _FFR_MILTER */
if (InChild)
ExitStat = EX_QUIT;
if (LogLevel > 4 && bitset(EF_LOGSENDER, e->e_flags))
logsender(e, NULL);
e->e_flags &= ~EF_LOGSENDER;
if (lognullconnection && LogLevel > 5)
{
char *d;
d = macvalue(macid("{daemon_name}", NULL), e);
if (d == NULL)
d = "stdin";
sm_syslog(LOG_INFO, NULL,
"%.100s did not issue MAIL/EXPN/VRFY/ETRN during connection to %s",
CurSmtpClient, d);
}
finis(TRUE, ExitStat);
/* NOTREACHED */
case CMDVERB: /* set verbose mode */
if (bitset(PRIV_NOEXPN, PrivacyFlags) ||
bitset(PRIV_NOVERB, PrivacyFlags))
{
/* this would give out the same info */
message("502 5.7.0 Verbose unavailable");
break;
}
(void) checksmtpattack(&n_noop, MAXNOOPCOMMANDS, TRUE,
"VERB", e);
Verbose = 1;
set_delivery_mode(SM_DELIVER, e);
message("250 2.0.0 Verbose mode");
break;
case CMDONEX: /* doing one transaction only */
(void) checksmtpattack(&n_noop, MAXNOOPCOMMANDS, TRUE,
"ONEX", e);
OneXact = TRUE;
message("250 2.0.0 Only one transaction");
break;
case CMDXUSR: /* initial (user) submission */
(void) checksmtpattack(&n_noop, MAXNOOPCOMMANDS, TRUE,
"XUSR", e);
define(macid("{daemon_flags}", NULL), "c u", CurEnv);
message("250 2.0.0 Initial submission");
break;
# if SMTPDEBUG
case CMDDBGQSHOW: /* show queues */
printf("Send Queue=");
printaddr(e->e_sendqueue, TRUE);
break;
case CMDDBGDEBUG: /* set debug mode */
tTsetup(tTdvect, sizeof tTdvect, "0-99.1");
tTflag(p);
message("200 2.0.0 Debug set");
break;
# else /* SMTPDEBUG */
case CMDDBGQSHOW: /* show queues */
case CMDDBGDEBUG: /* set debug mode */
# endif /* SMTPDEBUG */
case CMDLOGBOGUS: /* bogus command */
if (LogLevel > 0)
sm_syslog(LOG_CRIT, e->e_id,
"\"%s\" command from %.100s (%.100s)",
c->cmd_name, CurSmtpClient,
anynet_ntoa(&RealHostAddr));
/* FALLTHROUGH */
case CMDERROR: /* unknown command */
if (++badcommands > MAXBADCOMMANDS)
{
message("421 4.7.0 %s Too many bad commands; closing connection",
MyHostName);
/* arrange to ignore any current send list */
e->e_sendqueue = NULL;
goto doquit;
}
usrerr("500 5.5.1 Command unrecognized: \"%s\"",
shortenstring(inp, MAXSHORTSTR));
break;
case CMDUNIMPL:
usrerr("502 5.5.1 Command not implemented: \"%s\"",
shortenstring(inp, MAXSHORTSTR));
break;
default:
errno = 0;
syserr("500 5.5.0 smtp: unknown code %d", c->cmd_code);
break;
}
# if SASL
}
# endif /* SASL */
}
}
/*
** CHECKSMTPATTACK -- check for denial-of-service attack by repetition
**
** Parameters:
** pcounter -- pointer to a counter for this command.
** maxcount -- maximum value for this counter before we
** slow down.
** waitnow -- sleep now (in this routine)?
** cname -- command name for logging.
** e -- the current envelope.
**
** Returns:
** none.
**
** Side Effects:
** Slows down if we seem to be under attack.
*/
static time_t
checksmtpattack(pcounter, maxcount, waitnow, cname, e)
volatile int *pcounter;
int maxcount;
bool waitnow;
char *cname;
ENVELOPE *e;
{
if (++(*pcounter) >= maxcount)
{
time_t s;
if (*pcounter == maxcount && LogLevel > 5)
{
sm_syslog(LOG_INFO, e->e_id,
"%.100s: %.40s attack?",
CurSmtpClient, cname);
}
s = 1 << (*pcounter - maxcount);
if (s >= MAXTIMEOUT)
s = MAXTIMEOUT;
/* sleep at least 1 second before returning */
(void) sleep(*pcounter / maxcount);
s -= *pcounter / maxcount;
if (waitnow)
{
(void) sleep(s);
return(0);
}
return(s);
}
return((time_t) 0);
}
/*
** SKIPWORD -- skip a fixed word.
**
** Parameters:
** p -- place to start looking.
** w -- word to skip.
**
** Returns:
** p following w.
** NULL on error.
**
** Side Effects:
** clobbers the p data area.
*/
static char *
skipword(p, w)
register char *volatile p;
char *w;
{
register char *q;
char *firstp = p;
/* find beginning of word */
while (isascii(*p) && isspace(*p))
p++;
q = p;
/* find end of word */
while (*p != '\0' && *p != ':' && !(isascii(*p) && isspace(*p)))
p++;
while (isascii(*p) && isspace(*p))
*p++ = '\0';
if (*p != ':')
{
syntax:
usrerr("501 5.5.2 Syntax error in parameters scanning \"%s\"",
shortenstring(firstp, MAXSHORTSTR));
return NULL;
}
*p++ = '\0';
while (isascii(*p) && isspace(*p))
p++;
if (*p == '\0')
goto syntax;
/* see if the input word matches desired word */
if (strcasecmp(q, w))
goto syntax;
return p;
}
/*
** MAIL_ESMTP_ARGS -- process ESMTP arguments from MAIL line
**
** Parameters:
** kp -- the parameter key.
** vp -- the value of that parameter.
** e -- the envelope.
**
** Returns:
** none.
*/
static void
mail_esmtp_args(kp, vp, e)
char *kp;
char *vp;
ENVELOPE *e;
{
if (strcasecmp(kp, "size") == 0)
{
if (vp == NULL)
{
usrerr("501 5.5.2 SIZE requires a value");
/* NOTREACHED */
}
define(macid("{msg_size}", NULL), newstr(vp), e);
e->e_msgsize = strtol(vp, (char **) NULL, 10);
if (e->e_msgsize == LONG_MAX && errno == ERANGE)
{
usrerr("552 5.2.3 Message size exceeds maximum value");
/* NOTREACHED */
}
}
else if (strcasecmp(kp, "body") == 0)
{
if (vp == NULL)
{
usrerr("501 5.5.2 BODY requires a value");
/* NOTREACHED */
}
else if (strcasecmp(vp, "8bitmime") == 0)
{
SevenBitInput = FALSE;
}
else if (strcasecmp(vp, "7bit") == 0)
{
SevenBitInput = TRUE;
}
else
{
usrerr("501 5.5.4 Unknown BODY type %s",
vp);
/* NOTREACHED */
}
e->e_bodytype = newstr(vp);
}
else if (strcasecmp(kp, "envid") == 0)
{
if (bitset(PRIV_NORECEIPTS, PrivacyFlags))
{
usrerr("504 5.7.0 Sorry, ENVID not supported, we do not allow DSN");
/* NOTREACHED */
}
if (vp == NULL)
{
usrerr("501 5.5.2 ENVID requires a value");
/* NOTREACHED */
}
if (!xtextok(vp))
{
usrerr("501 5.5.4 Syntax error in ENVID parameter value");
/* NOTREACHED */
}
if (e->e_envid != NULL)
{
usrerr("501 5.5.0 Duplicate ENVID parameter");
/* NOTREACHED */
}
e->e_envid = newstr(vp);
define(macid("{dsn_envid}", NULL), newstr(vp), e);
}
else if (strcasecmp(kp, "ret") == 0)
{
if (bitset(PRIV_NORECEIPTS, PrivacyFlags))
{
usrerr("504 5.7.0 Sorry, RET not supported, we do not allow DSN");
/* NOTREACHED */
}
if (vp == NULL)
{
usrerr("501 5.5.2 RET requires a value");
/* NOTREACHED */
}
if (bitset(EF_RET_PARAM, e->e_flags))
{
usrerr("501 5.5.0 Duplicate RET parameter");
/* NOTREACHED */
}
e->e_flags |= EF_RET_PARAM;
if (strcasecmp(vp, "hdrs") == 0)
e->e_flags |= EF_NO_BODY_RETN;
else if (strcasecmp(vp, "full") != 0)
{
usrerr("501 5.5.2 Bad argument \"%s\" to RET", vp);
/* NOTREACHED */
}
define(macid("{dsn_ret}", NULL), newstr(vp), e);
}
# if SASL
else if (strcasecmp(kp, "auth") == 0)
{
int len;
char *q;
char *auth_param; /* the value of the AUTH=x */
bool saveQuickAbort = QuickAbort;
bool saveSuprErrs = SuprErrs;
char pbuf[256];
if (vp == NULL)
{
usrerr("501 5.5.2 AUTH= requires a value");
/* NOTREACHED */
}
if (e->e_auth_param != NULL)
{
usrerr("501 5.5.0 Duplicate AUTH parameter");
/* NOTREACHED */
}
if ((q = strchr(vp, ' ')) != NULL)
len = q - vp + 1;
else
len = strlen(vp) + 1;
auth_param = xalloc(len);
(void) strlcpy(auth_param, vp, len);
if (!xtextok(auth_param))
{
usrerr("501 5.5.4 Syntax error in AUTH parameter value");
/* just a warning? */
/* NOTREACHED */
}
/* XXX this might be cut off */
snprintf(pbuf, sizeof pbuf, "%s", xuntextify(auth_param));
/* xalloc() the buffer instead? */
/* XXX define this always or only if trusted? */
define(macid("{auth_author}", NULL), newstr(pbuf), e);
/*
** call Strust_auth to find out whether
** auth_param is acceptable (trusted)
** we shouldn't trust it if not authenticated
** (required by RFC, leave it to ruleset?)
*/
SuprErrs = TRUE;
QuickAbort = FALSE;
if (strcmp(auth_param, "<>") != 0 &&
(rscheck("trust_auth", pbuf, NULL, e, TRUE, FALSE, 10)
!= EX_OK || Errors > 0))
{
if (tTd(95, 8))
{
q = e->e_auth_param;
dprintf("auth=\"%.100s\" not trusted user=\"%.100s\"\n",
pbuf, (q == NULL) ? "" : q);
}
/* not trusted */
e->e_auth_param = newstr("<>");
}
else
{
if (tTd(95, 8))
dprintf("auth=\"%.100s\" trusted\n", pbuf);
e->e_auth_param = newstr(auth_param);
}
free(auth_param);
/* reset values */
Errors = 0;
QuickAbort = saveQuickAbort;
SuprErrs = saveSuprErrs;
}
# endif /* SASL */
else
{
usrerr("555 5.5.4 %s parameter unrecognized", kp);
/* NOTREACHED */
}
}
/*
** RCPT_ESMTP_ARGS -- process ESMTP arguments from RCPT line
**
** Parameters:
** a -- the address corresponding to the To: parameter.
** kp -- the parameter key.
** vp -- the value of that parameter.
** e -- the envelope.
**
** Returns:
** none.
*/
static void
rcpt_esmtp_args(a, kp, vp, e)
ADDRESS *a;
char *kp;
char *vp;
ENVELOPE *e;
{
if (strcasecmp(kp, "notify") == 0)
{
char *p;
if (bitset(PRIV_NORECEIPTS, PrivacyFlags))
{
usrerr("504 5.7.0 Sorry, NOTIFY not supported, we do not allow DSN");
/* NOTREACHED */
}
if (vp == NULL)
{
usrerr("501 5.5.2 NOTIFY requires a value");
/* NOTREACHED */
}
a->q_flags &= ~(QPINGONSUCCESS|QPINGONFAILURE|QPINGONDELAY);
a->q_flags |= QHASNOTIFY;
define(macid("{dsn_notify}", NULL), newstr(vp), e);
if (strcasecmp(vp, "never") == 0)
return;
for (p = vp; p != NULL; vp = p)
{
p = strchr(p, ',');
if (p != NULL)
*p++ = '\0';
if (strcasecmp(vp, "success") == 0)
a->q_flags |= QPINGONSUCCESS;
else if (strcasecmp(vp, "failure") == 0)
a->q_flags |= QPINGONFAILURE;
else if (strcasecmp(vp, "delay") == 0)
a->q_flags |= QPINGONDELAY;
else
{
usrerr("501 5.5.4 Bad argument \"%s\" to NOTIFY",
vp);
/* N
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -