Download | Plain Text | No Line Numbers


  1. /*
  2.  * Copyright (c) 2016 Manuel Mausz
  3.  */
  4.  
  5. /* uncomment the line below to use nbdm or gdbm instead of BerkDB */
  6. //#define USE_NDBM
  7.  
  8. #define _GNU_SOURCE
  9. #include <stdlib.h>
  10. #include <stdio.h>
  11. #include <string.h>
  12. #include <fcntl.h>
  13. #include <ctype.h>
  14.  
  15. #if defined(USE_NDBM)
  16. # include <ndbm.h>
  17. #else
  18. # define DB_DBM_HSEARCH 1 /* use the dbm interface */
  19. # define HAVE_DBM /* for BerkDB 5.0 and later */
  20. # include <db.h>
  21. #endif
  22.  
  23. #define MAX_DOMAIN_LEN 253
  24. int match_me(const char *mail_from, size_t mail_from_len)
  25. {
  26. if (mail_from_len > MAX_DOMAIN_LEN)
  27. return 0;
  28.  
  29. FILE *fpme;
  30. if ((fpme = fopen("control/me", "r")) == NULL)
  31. return 0;
  32.  
  33. int match = 0;
  34. char me[MAX_DOMAIN_LEN + 1];
  35. if (fgets(me, sizeof(me), fpme) != NULL)
  36. match = (strncmp(mail_from, me, mail_from_len) == 0);
  37.  
  38. (void)fclose(fpme);
  39. return match;
  40. }
  41.  
  42. int match_hosts_db(char *mail_from, size_t mail_from_len,
  43. const char *user, size_t user_len)
  44. {
  45. const char *dbfile = getenv("MAILFROMHOSTSDB");
  46. if (dbfile == NULL)
  47. dbfile = "control/mailfromhosts";
  48.  
  49. DBM *dbm = dbm_open(dbfile, O_RDONLY, 0600);
  50. if (dbm == NULL)
  51. {
  52. perror("Unable to open database");
  53. return 0;
  54. }
  55.  
  56. /* domain match */
  57. datum key = {
  58. .dptr = mail_from,
  59. .dsize = mail_from_len,
  60. };
  61. datum data = dbm_fetch(dbm, key);
  62.  
  63. #ifdef ALLOW_SUBDOMAINS
  64. char *p;
  65. while (data.dptr == NULL && (p = strchr(key.dptr, '.')) != NULL)
  66. {
  67. /* parent domain match */
  68. p++;
  69. if (*p == '\0')
  70. break;
  71. key.dsize = mail_from_len - (p - mail_from);
  72. key.dptr = p;
  73. data = dbm_fetch(dbm, key);
  74. }
  75. #endif
  76.  
  77. /*
  78.   * check auth user and data.dptr relation
  79.   * auth user is <account>p<num>
  80.   * data.dptr is <account> (without \0)
  81.   */
  82. int match = (data.dptr != NULL && user_len >= data.dsize + 2
  83. && strncmp(data.dptr, user, data.dsize) == 0 && user[data.dsize] == 'p');
  84.  
  85. (void)dbm_close(dbm);
  86. return match;
  87. }
  88.  
  89. int main()
  90. {
  91. const char *user = getenv("SMTPAUTHUSER");
  92. size_t user_len;
  93. if (user == NULL || (user_len = strlen(user)) == 0)
  94. return 0;
  95.  
  96. const char *env_mail_from = getenv("SMTPMAILFROM");
  97. if (env_mail_from == NULL)
  98. return 0;
  99.  
  100. /* allow rfc 2298 */
  101. if (*env_mail_from == '\0')
  102. return 0;
  103.  
  104. char *domain = strrchr(env_mail_from, '@');
  105. if (domain == NULL)
  106. {
  107. fprintf(stderr, "mailfromhosts: %s %s\n", user, env_mail_from);
  108. puts("E553 5.1.7 Sender domain not allowed");
  109. return 0;
  110. }
  111.  
  112. size_t mail_from_len = strlen(domain + 1);
  113. if (mail_from_len > 0)
  114. {
  115. char *mail_from = strdup(domain + 1);
  116. for (char *p = mail_from; *p; ++p)
  117. *p = tolower(*p);
  118.  
  119. if (!match_hosts_db(mail_from, mail_from_len, user, user_len)
  120. && !match_me(mail_from, mail_from_len))
  121. {
  122. fprintf(stderr, "mailfromhosts: %s %s\n", user, env_mail_from);
  123. puts("E553 5.1.8 Sender domain not allowed");
  124. }
  125.  
  126. (void)free(mail_from);
  127. }
  128.  
  129. return 0;
  130. }
  131.