Download | Plain Text | Line Numbers
/*
* Copyright (c) 2016 Manuel Mausz
*/
/* uncomment the line below to use nbdm or gdbm instead of BerkDB */
//#define USE_NDBM
#define _GNU_SOURCE
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <fcntl.h>
#include <ctype.h>
#if defined(USE_NDBM)
# include <ndbm.h>
#else
# define DB_DBM_HSEARCH 1 /* use the dbm interface */
# define HAVE_DBM /* for BerkDB 5.0 and later */
# include <db.h>
#endif
#define MAX_DOMAIN_LEN 253
int match_me(const char *mail_from, size_t mail_from_len)
{
if (mail_from_len > MAX_DOMAIN_LEN)
return 0;
FILE *fpme;
if ((fpme = fopen("control/me", "r")) == NULL)
return 0;
int match = 0;
char me[MAX_DOMAIN_LEN + 1];
if (fgets(me, sizeof(me), fpme) != NULL)
match = (strncmp(mail_from, me, mail_from_len) == 0);
(void)fclose(fpme);
return match;
}
int match_hosts_db(char *mail_from, size_t mail_from_len,
const char *user, size_t user_len)
{
const char *dbfile = getenv("MAILFROMHOSTSDB");
if (dbfile == NULL)
dbfile = "control/mailfromhosts";
DBM *dbm = dbm_open(dbfile, O_RDONLY, 0600);
if (dbm == NULL)
{
perror("Unable to open database");
return 0;
}
/* domain match */
datum key = {
.dptr = mail_from,
.dsize = mail_from_len,
};
datum data = dbm_fetch(dbm, key);
#ifdef ALLOW_SUBDOMAINS
char *p;
while (data.dptr == NULL && (p = strchr(key.dptr, '.')) != NULL)
{
/* parent domain match */
p++;
if (*p == '\0')
break;
key.dsize = mail_from_len - (p - mail_from);
key.dptr = p;
data = dbm_fetch(dbm, key);
}
#endif
/*
* check auth user and data.dptr relation
* auth user is <account>p<num>
* data.dptr is <account> (without \0)
*/
int match = (data.dptr != NULL && user_len >= data.dsize + 2
&& strncmp(data.dptr, user, data.dsize) == 0 && user[data.dsize] == 'p');
(void)dbm_close(dbm);
return match;
}
int main()
{
const char *user = getenv("SMTPAUTHUSER");
size_t user_len;
if (user == NULL || (user_len = strlen(user)) == 0)
return 0;
const char *env_mail_from = getenv("SMTPMAILFROM");
if (env_mail_from == NULL)
return 0;
/* allow rfc 2298 */
if (*env_mail_from == '\0')
return 0;
char *domain = strrchr(env_mail_from, '@');
if (domain == NULL)
{
fprintf(stderr, "mailfromhosts: %s %s\n", user, env_mail_from);
puts("E553 5.1.7 Sender domain not allowed");
return 0;
}
size_t mail_from_len = strlen(domain + 1);
if (mail_from_len > 0)
{
char *mail_from = strdup(domain + 1);
for (char *p = mail_from; *p; ++p)
*p = tolower(*p);
if (!match_hosts_db(mail_from, mail_from_len, user, user_len)
&& !match_me(mail_from, mail_from_len))
{
fprintf(stderr, "mailfromhosts: %s %s\n", user, env_mail_from);
puts("E553 5.1.8 Sender domain not allowed");
}
(void)free(mail_from);
}
return 0;
}