📄 usersmtp.c
字号:
** file.
*/
sff |= SFF_MUSTOWN;
#else /* _FFR_ALLOW_SASLINFO */
if (safe)
sff |= SFF_OPENASROOT;
#endif /* _FFR_ALLOW_SASLINFO */
f = safefopen(filename, O_RDONLY, 0, sff);
}
if (f == NULL)
{
syserr("readauth: cannot open %s", filename);
return "";
}
lc = 0;
while (lc < l && fgets(buf, sizeof buf, f) != NULL)
{
if (buf[0] != '#')
lc++;
}
(void) fclose(f);
if (pid > 0)
(void) waitfor(pid);
if (lc < l)
{
if (LogLevel >= 9)
sm_syslog(LOG_WARNING, NOQID, "SASL: error: can't read %s from %s",
sasl_info_name[l], filename);
return "";
}
lc = strlen(buf) - 1;
if (lc >= 0)
buf[lc] = '\0';
if (tTd(95, 6))
dprintf("readauth(%s, %d) = '%s'\n", filename, l, buf);
return buf;
}
# ifndef __attribute__
# define __attribute__(x)
# endif /* ! __attribute__ */
static int getsimple __P((void *, int, const char **, unsigned *));
static int getsecret __P((sasl_conn_t *, void *, int, sasl_secret_t **));
static int saslgetrealm __P((void *, int, const char **, const char **));
static sasl_callback_t callbacks[] =
{
{ SASL_CB_GETREALM, &saslgetrealm, NULL },
# define CB_GETREALM_IDX 0
{ SASL_CB_PASS, &getsecret, NULL },
# define CB_PASS_IDX 1
{ SASL_CB_USER, &getsimple, NULL },
# define CB_USER_IDX 2
{ SASL_CB_AUTHNAME, &getsimple, NULL },
# define CB_AUTHNAME_IDX 3
{ SASL_CB_VERIFYFILE, &safesaslfile, NULL },
{ SASL_CB_LIST_END, NULL, NULL }
};
/*
** GETSIMPLE -- callback to get userid or authid
**
** Parameters:
** context -- unused
** id -- what to do
** result -- (pointer to) result
** len -- (pointer to) length of result
**
** Returns:
** OK/failure values
*/
static int
getsimple(context, id, result, len)
void *context __attribute__((unused));
int id;
const char **result;
unsigned *len;
{
char *h;
# if SASL > 10509
int addrealm;
static int addedrealm = FALSE;
# endif /* SASL > 10509 */
static char *user = NULL;
static char *authid = NULL;
if (result == NULL)
return SASL_BADPARAM;
switch (id)
{
case SASL_CB_USER:
if (user == NULL)
{
h = readauth(SASL_USER, SASLInfo, TRUE);
user = newstr(h);
}
*result = user;
if (tTd(95, 5))
dprintf("AUTH username '%s'\n", *result);
if (len != NULL)
*len = user ? strlen(user) : 0;
break;
case SASL_CB_AUTHNAME:
# if SASL > 10509
/* XXX maybe other mechanisms too?! */
addrealm = context != NULL &&
strcasecmp(context, "CRAM-MD5") == 0;
if (addedrealm != addrealm && authid != NULL)
{
# if SASL > 10522
/*
** digest-md5 prior to 1.5.23 doesn't copy the
** value it gets from the callback, but free()s
** it later on
** workaround: don't free() it here
** this can cause a memory leak!
*/
free(authid);
# endif /* SASL > 10522 */
authid = NULL;
addedrealm = addrealm;
}
# endif /* SASL > 10509 */
if (authid == NULL)
{
h = readauth(SASL_AUTHID, SASLInfo, TRUE);
# if SASL > 10509
if (addrealm && strchr(h, '@') == NULL)
{
size_t l;
char *realm;
realm = callbacks[CB_GETREALM_IDX].context;
l = strlen(h) + strlen(realm) + 2;
authid = xalloc(l);
snprintf(authid, l, "%s@%s", h, realm);
}
else
# endif /* SASL > 10509 */
authid = newstr(h);
}
*result = authid;
if (tTd(95, 5))
dprintf("AUTH authid '%s'\n", *result);
if (len != NULL)
*len = authid ? strlen(authid) : 0;
break;
case SASL_CB_LANGUAGE:
*result = NULL;
if (len != NULL)
*len = 0;
break;
default:
return SASL_BADPARAM;
}
return SASL_OK;
}
/*
** GETSECRET -- callback to get password
**
** Parameters:
** conn -- connection information
** context -- unused
** id -- what to do
** psecret -- (pointer to) result
**
** Returns:
** OK/failure values
*/
static int
getsecret(conn, context, id, psecret)
sasl_conn_t *conn;
void *context __attribute__((unused));
int id;
sasl_secret_t **psecret;
{
char *h;
int len;
static char *authpass = NULL;
if (conn == NULL || psecret == NULL || id != SASL_CB_PASS)
return SASL_BADPARAM;
if (authpass == NULL)
{
h = readauth(SASL_PASSWORD, SASLInfo, TRUE);
authpass = newstr(h);
}
len = strlen(authpass);
*psecret = (sasl_secret_t *) malloc(sizeof(sasl_secret_t) + len + 1);
if (*psecret == NULL)
return SASL_FAIL;
(void) strlcpy((*psecret)->data, authpass, len + 1);
(*psecret)->len = len;
return SASL_OK;
}
/*
** SAFESASLFILE -- callback for sasl: is file safe?
**
** Parameters:
** context -- pointer to context between invocations (unused)
** file -- name of file to check
** type -- type of file to check
**
** Returns:
** SASL_OK: file can be used
** SASL_CONTINUE: don't use file
** SASL_FAIL: failure (not used here)
**
*/
int
# if SASL > 10515
safesaslfile(context, file, type)
# else /* SASL > 10515 */
safesaslfile(context, file)
# endif /* SASL > 10515 */
void *context;
char *file;
# if SASL > 10515
int type;
# endif /* SASL > 10515 */
{
long sff;
int r;
char *p;
if (file == NULL || *file == '\0')
return SASL_OK;
sff = SFF_SAFEDIRPATH|SFF_NOWLINK|SFF_NOGWFILES|SFF_NOWWFILES|SFF_ROOTOK;
if ((p = strrchr(file, '/')) == NULL)
p = file;
else
++p;
# if SASL <= 10515
/* everything beside libs and .conf files must not be readable */
r = strlen(p);
if ((r <= 3 || strncmp(p, "lib", 3) != 0) &&
(r <= 5 || strncmp(p + r - 5, ".conf", 5) != 0)
# if _FFR_UNSAFE_SASL
&& !bitnset(DBS_GROUPREADABLESASLFILE, DontBlameSendmail)
# endif /* _FFR_UNSAFE_SASL */
)
sff |= SFF_NORFILES;
# else /* SASL > 10515 */
/* files containing passwords should be not readable */
if (type == SASL_VRFY_PASSWD)
{
# if _FFR_UNSAFE_SASL
if (bitnset(DBS_GROUPREADABLESASLFILE, DontBlameSendmail))
sff |= SFF_NOWRFILES;
else
# endif /* _FFR_UNSAFE_SASL */
sff |= SFF_NORFILES;
}
# endif /* SASL <= 10515 */
if ((r = safefile(file, RunAsUid, RunAsGid, RunAsUserName, sff,
S_IRUSR, NULL)) == 0)
return SASL_OK;
if (LogLevel >= 11 || (r != ENOENT && LogLevel >= 9))
sm_syslog(LOG_WARNING, NOQID, "error: safesasl(%s) failed: %s",
file, errstring(r));
return SASL_CONTINUE;
}
/*
** SASLGETREALM -- return the realm for SASL
**
** return the realm for the client
**
** Parameters:
** context -- context shared between invocations
** here: realm to return
** availrealms -- list of available realms
** {realm, realm, ...}
** result -- pointer to result
**
** Returns:
** failure/success
*/
static int
saslgetrealm(context, id, availrealms, result)
void *context;
int id;
const char **availrealms;
const char **result;
{
if (LogLevel > 12)
sm_syslog(LOG_INFO, NOQID, "saslgetrealm: realm %s available realms %s",
context == NULL ? "<No Context>" : (char *) context,
(availrealms == NULL || *availrealms == NULL) ? "<No Realms>" : *availrealms);
if (context == NULL)
return SASL_FAIL;
/* check whether context is in list? */
if (availrealms != NULL && *availrealms != NULL)
{
if (iteminlist(context, (char *)(*availrealms + 1), " ,}") ==
NULL)
{
if (LogLevel > 8)
sm_syslog(LOG_ERR, NOQID,
"saslgetrealm: realm %s not in list %s",
context, *availrealms);
return SASL_FAIL;
}
}
*result = (char *)context;
return SASL_OK;
}
/*
** ITEMINLIST -- does item appear in list?
**
** Check whether item appears in list (which must be separated by a
** character in delim) as a "word", i.e. it must appear at the begin
** of the list or after a space, and it must end with a space or the
** end of the list.
**
** Parameters:
** item -- item to search.
** list -- list of items.
** delim -- list of delimiters.
**
** Returns:
** pointer to occurrence (NULL if not found).
*/
char *
iteminlist(item, list, delim)
char *item;
char *list;
char *delim;
{
char *s;
int len;
if (list == NULL || *list == '\0')
return NULL;
if (item == NULL || *item == '\0')
return NULL;
s = list;
len = strlen(item);
while (s != NULL && *s != '\0')
{
if (strncasecmp(s, item, len) == 0 &&
(s[len] == '\0' || strchr(delim, s[len]) != NULL))
return s;
s = strpbrk(s, delim);
if (s != NULL)
while (*++s == ' ')
continue;
}
return NULL;
}
/*
** REMOVEMECH -- remove item [rem] from list [list]
**
** Parameters:
** rem -- item to remove
** list -- list of items
**
** Returns:
** pointer to new list (NULL in case of error).
*/
char *
removemech(rem, list)
char *rem;
char *list;
{
char *ret;
char *needle;
int len;
if (list == NULL)
return NULL;
if (rem == NULL || *rem == '\0')
{
/* take out what? */
return NULL;
}
/* find the item in the list */
if ((needle = iteminlist(rem, list, " ")) == NULL)
{
/* not in there: return original */
return list;
}
/* length of string without rem */
len = strlen(list) - strlen(rem);
if (len == 0)
{
ret = xalloc(1); /* XXX leaked */
*ret = '\0';
return ret;
}
ret = xalloc(len); /* XXX leaked */
memset(ret, '\0', len);
/* copy from start to removed item */
memcpy(ret, list, needle - list);
/* length of rest of string past removed item */
len = strlen(needle) - strlen(rem) - 1;
if (len > 0)
{
/* not last item -- copy into string */
memcpy(ret + (needle - list),
list + (needle - list) + strlen(rem) + 1,
len);
}
else
ret[(needle - list) - 1] = '\0';
return ret;
}
/*
** INTERSECT -- create the intersection between two lists
**
** Parameters:
** s1, s2 -- lists of items (separated by single blanks).
**
** Returns:
** the intersection of both lists.
*/
char *
intersect(s1, s2)
char *s1, *s2;
{
char *hr, *h1, *h, *res;
int l1, l2, rl;
if (s1 == NULL || s2 == NULL) /* NULL string(s) -> NULL result */
return NULL;
l1 = strlen(s1);
l2 = strlen(s2);
rl = min(l1, l2);
res = (char *)malloc(rl + 1);
if (res == NULL)
return NULL;
*res = '\0';
if (rl == 0) /* at least one string empty? */
return res;
hr = res;
h1 = s1;
h = s1;
/* walk through s1 */
while (h != NULL && *h1 != '\0')
{
/* is there something after the current word? */
if ((h = strchr(h1, ' ')) != NULL)
*h = '\0';
l1 = strlen(h1);
/* does the current word appear in s2 ? */
if (iteminlist(h1, s2, " ") != NULL)
{
/* add a blank if not first item */
if (hr != res)
*hr++ = ' ';
/* copy the item */
memcpy(hr, h1, l1);
/* advance pointer in result list */
hr += l1;
*hr = '\0';
}
if (h != NULL)
{
/* there are more items */
*h = ' ';
h1 = h + 1;
}
}
return res;
}
/*
** ATTEMPTAUTH -- try to AUTHenticate using one mechanism
**
** Parameters:
** m -- the mailer.
** mci -- the mailer connection structure.
** e -- the envelope (including the sender to specify).
** mechused - filled in with mechanism used
**
** Returns:
** EX_OK/EX_TEMPFAIL
*/
int
attemptauth(m, mci, e, mechused)
MAILER *m;
MCI *mci;
ENVELOPE *e;
char **mechused;
{
int saslresult, smtpresult;
sasl_external_properties_t ssf;
sasl_interact_t *client_interact = NULL;
char *out;
unsigned int outlen;
static char *mechusing;
sasl_security_properties_t ssp;
char in64[MAXOUTLEN];
# if NETINET
extern SOCKADDR CurHostAddr;
# endif /* NETINET */
*mechused = NULL;
if (mci->mci_conn != NULL)
{
sasl_dispose(&(mci->mci_conn));
/* just in case, sasl_dispose() should take care of it */
mci->mci_conn = NULL;
}
/* make a new client sasl connection */
saslresult = sasl_client_new(bitnset(M_LMTP, m->m_flags) ? "lmtp"
: "smtp",
CurHostName, NULL, 0, &mci->mci_conn);
/* set properties */
(void) memset(&ssp, '\0', sizeof ssp);
# if SFIO
/* XXX should these be options settable via .cf ? */
/* ssp.min_ssf = 0; is default due to memset() */
{
ssp.max_ssf = INT_MAX;
ssp.maxbufsize = MAXOUTLEN;
# if 0
ssp.security_flags = SASL_SEC_NOPLAINTEXT;
# endif /* 0 */
}
# endif /* SFIO */
saslresult = sasl_setprop(mci->mci_conn, SASL_SEC_PROPS, &ssp);
if (saslresult != SASL_OK)
return EX_TEMPFAIL;
/* external security strength factor, authentication id */
ssf.ssf = 0;
ssf.auth_id = NULL;
# if _FFR_EXT_MECH
out = macvalue(macid("{cert_subject}", NULL), e);
if (out != NULL && *out != '\0')
ssf.auth_id = out;
out = macvalue(macid("{cipher_bits}", NULL), e);
if (out != NULL && *out != '\0')
ssf.ssf = atoi(out);
# endif /* _FFR_EXT_MECH */
saslresult = sasl_setprop(mci->mci_conn, SASL_SSF_EXTERNAL, &ssf);
if (saslresult != SASL_OK)
return EX_TEMPFAIL;
# if NETINET
/* set local/remote ipv4 addresses */
if (mci->mci_out != NULL && CurHostAddr.sa.sa_family == AF_INET)
{
SOCKADDR_LEN_T addrsize;
struct sockaddr_in saddr_l;
if (sasl_setprop(mci->mci_conn, SASL_IP_REMOTE,
(struct sockaddr_in *) &CurHostAddr)
!= SASL_OK)
return EX_TEMPFAIL;
addrsize = sizeof(struct sockaddr_in);
if (getsockname(fileno(mci->mci_out),
(struct sockaddr *) &saddr_l, &addrsize) != 0)
{
if (sasl_setprop(mci->mci_conn, SASL_IP_LOCAL,
&saddr_l) != SASL_OK)
return EX_TEMPFAIL;
}
}
# endif /* NETINET */
/* start client side of sasl */
saslresult = sasl_client_start(mci->mci_conn, mci->mci_saslcap,
NULL, &client_interact,
&out, &outlen,
(const char **)&mechusing);
callbacks[CB_AUTHNAME_IDX].context = mechusing;
if (saslresult != SASL_OK && saslresult != SASL_CONTINUE)
{
# if SFIO
if (saslresult == SASL_NOMECH && LogLevel > 8)
{
sm_syslog(LOG_NOTICE, e->e_id,
"available AUTH mechanisms do not fulfill requirements");
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -