Haraka 3.0.1 → 3.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (64) hide show
  1. package/Changes.md +56 -0
  2. package/Dockerfile +3 -3
  3. package/Plugins.md +5 -4
  4. package/README.md +4 -4
  5. package/TODO +1 -24
  6. package/config/access.domains +1 -1
  7. package/config/auth_flat_file.ini +1 -0
  8. package/config/auth_vpopmaild.ini +4 -2
  9. package/config/helo.checks.ini +1 -1
  10. package/config/outbound.ini +1 -1
  11. package/config/rabbitmq_amqplib.ini +8 -1
  12. package/connection.js +32 -10
  13. package/docs/Connection.md +1 -1
  14. package/docs/Outbound.md +6 -15
  15. package/docs/Plugins.md +46 -39
  16. package/docs/Transaction.md +1 -1
  17. package/docs/{plugins → deprecated}/connect.rdns_access.md +1 -1
  18. package/docs/{plugins → deprecated}/mail_from.access.md +1 -1
  19. package/docs/{plugins → deprecated}/rcpt_to.access.md +1 -1
  20. package/docs/plugins/auth/auth_vpopmaild.md +15 -19
  21. package/docs/plugins/auth/flat_file.md +23 -30
  22. package/docs/plugins/clamd.md +1 -1
  23. package/docs/plugins/queue/rabbitmq_amqplib.md +7 -0
  24. package/docs/plugins/queue/smtp_forward.md +16 -38
  25. package/docs/plugins/queue/smtp_proxy.md +9 -11
  26. package/docs/plugins/relay.md +2 -2
  27. package/outbound/hmail.js +2 -2
  28. package/outbound/queue.js +5 -0
  29. package/outbound/tls.js +1 -1
  30. package/package.json +31 -31
  31. package/plugins/auth/auth_base.js +27 -11
  32. package/plugins/auth/auth_vpopmaild.js +29 -19
  33. package/plugins/auth/flat_file.js +17 -12
  34. package/plugins/clamd.js +1 -0
  35. package/plugins/dns_list_base.js +3 -3
  36. package/plugins/helo.checks.js +15 -7
  37. package/plugins/queue/rabbitmq_amqplib.js +1 -1
  38. package/plugins/queue/smtp_forward.js +21 -15
  39. package/plugins/tls.js +1 -1
  40. package/plugins.js +1 -0
  41. package/tests/config/helo.checks.ini +52 -0
  42. package/tests/plugins/dns_list_base.js +41 -31
  43. package/tests/plugins/helo.checks.js +212 -239
  44. package/tests/plugins/queue/smtp_forward.js +36 -7
  45. package/tests/queue/multibyte +0 -0
  46. package/tests/queue/plain +0 -0
  47. package/transaction.js +1 -1
  48. package/config/lookup_rdns.strict.ini +0 -12
  49. package/config/lookup_rdns.strict.timeout +0 -1
  50. package/config/lookup_rdns.strict.whitelist +0 -1
  51. package/config/lookup_rdns.strict.whitelist_regex +0 -5
  52. package/config/rcpt_to.blocklist +0 -1
  53. package/config/rdns.allow_regexps +0 -0
  54. package/config/rdns.deny_regexps +0 -0
  55. package/config.js +0 -6
  56. package/coverage/lcov.info +0 -13863
  57. package/coverage/tmp/coverage-42958-1658373250585-0.json +0 -1
  58. package/coverage/tmp/coverage-42961-1658373250529-0.json +0 -1
  59. package/docs/plugins/relay_acl.md +0 -29
  60. package/docs/plugins/relay_all.md +0 -15
  61. package/docs/plugins/relay_force_routing.md +0 -33
  62. package/plugins/data.headers.js +0 -4
  63. package/plugins/relay_all.js +0 -13
  64. /package/docs/{plugins → deprecated}/rcpt_to.routes.md +0 -0
@@ -1,47 +1,40 @@
1
- auth/flat\_file
2
- ==============
1
+ # auth/flat\_file
3
2
 
4
- The `auth/flat_file` plugin allows you to create a file containing username
5
- and password combinations, and have relaying users authenticate from that
6
- file.
3
+ The `auth/flat_file` plugin allows you to create a file containing username and password combinations, and have relaying users authenticate from that file.
7
4
 
8
- Note that passwords are stored in clear-text, so this may not be a great idea
9
- for large scale systems. However the plugin would be a good start for someone
10
- looking to implement authentication using some other form of auth.
5
+ Note that passwords are stored in clear-text, so this may not be a great idea for large scale systems. However the plugin would be a good start for someone looking to implement authentication using some other form of auth.
11
6
 
12
- **Security** - it is recommended to switch to [auth-encfile][url-authencflat]
13
- to protect your user credentials.
7
+ **Security** - it is recommended to switch to [auth-encfile][url-authencflat] to protect your user credentials.
14
8
 
15
- **IMPORANT NOTE** - this plugin requires that STARTTLS be used via the tls plugin
16
- before it will advertise AUTH capabilities by the EHLO command. This is to
17
- improve security out-of-the-box. Localhost and any IP in RFC1918 ranges
18
- are automatically exempt from this rule.
9
+ **IMPORANT NOTE** - this plugin requires that STARTTLS be used via the tls plugin before it will advertise AUTH capabilities by the EHLO command. Localhost and IPs in RFC1918 ranges
10
+ are exempt from this rule.
19
11
 
20
- Configuration
21
- -------------
12
+ ## Configuration
22
13
 
23
- Configuration is stored in `config/auth_flat_file.ini` and uses the INI
24
- style formatting.
14
+ Configuration is stored in `config/auth_flat_file.ini`.
25
15
 
26
- Authentication methods are listed in the `[core]` section under `methods`
27
- parameter. Lists of authentification methods are comma separated. Currently
28
- supported methods are: `CRAM-MD5`, `PLAIN` and `LOGIN`. The `PLAIN`
29
- and `LOGIN` methods are not secure. That is why TLS is required before AUTH is
30
- offered.
16
+ * [core]methods
31
17
 
32
- Example:
18
+ Authentication methods are listed in the `[core]methods` parameter. Authentification methods are comma separated. Currently supported methods are: `CRAM-MD5`, `PLAIN` and `LOGIN`. The `PLAIN` and `LOGIN` methods are insecure and require TLS to be enabled.
19
+
20
+ * [core]constrain_sender: (default: true). For outbound messages (due to successful AUTH), constrain the envelope sender (MAIL FROM) to the same domain as the authenticated user. This setting, combined with `rate_rcpt_sender` in the [limit](https://github.com/haraka/haraka-plugin-limit) plugin can dramatically reduce the amount of backscatter and spam sent when an email account is compromised.
33
21
 
34
- [core]
35
- methods=PLAIN,LOGIN,CRAM-MD5
22
+ Example:
36
23
 
24
+ ```ini
25
+ [core]
26
+ methods=PLAIN,LOGIN,CRAM-MD5
27
+ constrain_sender=true
28
+ ```
37
29
 
38
30
  Users are stored in the `[users]` section.
39
31
 
40
32
  Example:
41
33
 
42
- [users]
43
- user1=password1
44
- user@domain.com=password2
45
-
34
+ ```ini
35
+ [users]
36
+ user1=password1
37
+ user@domain.com=password2
38
+ ```
46
39
 
47
40
  [url-authencflat]: https://github.com/AuspeXeu/haraka-plugin-auth-enc-file
@@ -133,7 +133,7 @@ meeting following criteria.
133
133
  via regexp by enclosing the pattern in //.
134
134
 
135
135
  To negate a match (e.g. reject if it matches), prefix the match with !.
136
- Negative matches are always tested fist.
136
+ Negative matches are always tested first.
137
137
 
138
138
  Example:
139
139
 
@@ -36,5 +36,12 @@ Configuration
36
36
  durable = true
37
37
  autoDelete = false
38
38
 
39
+ ; Optional queue arguments
40
+ ; More information about x-arguments can be found at https://www.rabbitmq.com/queues.html#optional-arguments
41
+ [queue_args]
42
+ x-dead-letter-exchange =
43
+ x-dead-letter-routing-key = emails_dlq
44
+ x-overflow = reject-publish
45
+ x-queue-type = quorum
39
46
 
40
47
  More information about RabbitMQ can be found at https://www.rabbitmq.com/
@@ -1,27 +1,18 @@
1
- queue/smtp\_forward
1
+ # queue/smtp\_forward
2
2
  ==================
3
3
 
4
- This plugin delivers to another mail server. This is a common setup when you
5
- want to have a mail server with a solid pedigree of outbound delivery to
6
- other hosts, and inbound delivery to users.
4
+ This plugin delivers to another mail server. This is a common setup when you want to have a mail server with a solid pedigree of outbound delivery to other hosts, and inbound delivery to users.
7
5
 
8
- In comparison to `queue/smtp_proxy`, this plugin waits until queue time to
9
- attempt the ongoing connection. This can be a benefit in reducing connections
10
- to your inbound mail server when you have content filtering (such as
11
- spamassassin) enabled. A possible downside is that it also delays recipient
12
- validation that the ongoing mail server may provide until queue time.
6
+ In comparison to `queue/smtp_proxy`, this plugin waits until queue time to attempt the ongoing connection. This can be a benefit in reducing connections to your inbound mail server when you have content filtering (such as spamassassin) enabled. A possible downside is that it also delays recipient validation that the ongoing mail server may provide until queue time.
13
7
 
14
- Configuration
8
+ ## Configuration
15
9
  -------------
16
10
 
17
- * smtp\_forward.ini
18
-
19
- Configuration is stored in this file in the following keys:
11
+ Configuration is stored in smtp\_forward.ini in the following keys:
20
12
 
21
13
  * enable\_outbound=[true]
22
14
 
23
- SMTP forward outbound messages (set to false to enable Haraka's separate
24
- Outbound mail routing (MX based delivery)).
15
+ SMTP forward outbound messages (set to false to enable Haraka's separate Outbound mail routing (MX based delivery)).
25
16
 
26
17
  * host=HOST
27
18
 
@@ -33,14 +24,11 @@ Configuration
33
24
 
34
25
  * connect\_timeout=SECONDS
35
26
 
36
- The maximum amount of time to wait when creating a new connection
37
- to the host. Default: 30 seconds.
27
+ The maximum amount of time to wait when creating a new connection to the host. Default: 30 seconds.
38
28
 
39
29
  * timeout=SECONDS
40
30
 
41
- The amount of seconds to let a backend connection live idle in the
42
- connection pool. This should always be less than the global plugin
43
- timeout, which should in turn be less than the connection timeout.
31
+ The amount of seconds to let a backend connection live idle in the connection pool. This should always be less than the global plugin timeout, which should in turn be less than the connection timeout.
44
32
 
45
33
  * max\_connections=NUMBER
46
34
 
@@ -48,12 +36,9 @@ Configuration
48
36
 
49
37
  * enable\_tls=[true]
50
38
 
51
- Enable TLS with the forward host (if supported). TLS uses options
52
- from the tls plugin. If key and cert are provided in the the outbound section of the tls plugin,
53
- that certificate will be used as a TLS Client Certificate.
39
+ Enable TLS with the forward host (if supported). TLS uses options from the tls plugin. If key and cert are provided in the the outbound section of the tls plugin, that certificate will be used as a TLS Client Certificate.
54
40
 
55
- This option controls the use of TLS via `STARTTLS`. This plugin does not work with
56
- SMTP over TLS.
41
+ This option controls the use of TLS via `STARTTLS`. This plugin does not work with SMTP over TLS.
57
42
 
58
43
  * auth\_type=[plain\|login]
59
44
 
@@ -69,9 +54,7 @@ Configuration
69
54
 
70
55
  * queue
71
56
 
72
- Which queue plugin to use. Default: undefined. The default bahavior is to
73
- use smtp_forward for inbound connections and outbound for relaying
74
- connections. This option is used for complex mail routes.
57
+ Which queue plugin to use. Default: undefined. The default bahavior is to use smtp_forward for inbound connections and outbound for relaying connections. This option is used for complex mail routes.
75
58
 
76
59
  * check_sender=false
77
60
 
@@ -86,18 +69,13 @@ Configuration
86
69
 
87
70
  # Per-Domain Configuration
88
71
 
89
- More specific forward routes for domains can be defined. The domain is
90
- chosen based on the value of the `domain_selector` config variable.
72
+ More specific forward routes for domains can be defined. The domain is chosen based on the value of the `domain_selector` config variable.
91
73
 
92
- When `domain_selector` is set to `rcpt_to` (the default), more specific
93
- routes are only honored for SMTP connections with a single recipient or SMTP
94
- connections where every recipient host is identical.
74
+ When `domain_selector` is set to `rcpt_to` (the default), more specific routes are only honored for SMTP connections with a single recipient or SMTP connections where every recipient host is identical.
95
75
 
96
- When `domain_selector` is set to `mail_from`, the domain of the MAIL FROM
97
- address is used.
76
+ When `domain_selector` is set to `mail_from`, the domain of the MAIL FROM address is used.
98
77
 
99
- enable\_outbound can be set or unset on a per-domain level to enable or disable
100
- forwarding for specific domains.
78
+ enable\_outbound can be set or unset on a per-domain level to enable or disable forwarding for specific domains.
101
79
 
102
80
  # default SMTP host
103
81
  host=1.2.3.4
@@ -122,4 +100,4 @@ forwarding for specific domains.
122
100
 
123
101
  # Split host forward routing
124
102
 
125
- When an incoming email transaction has multiple recipients with different forward routes, recipients to subsequent forward routes are deferred. Example: an incoming email transaction has recipients user@example1.com, user@example2.com, and user@example3.com. The first two recipients will be accepted (they share the same forward destination) and the latter will be deferred. It will arrive in a future delivery attempt by the remote.
103
+ When an incoming email transaction has multiple recipients with different forward routes, recipients to subsequent forward routes are deferred. Example: an incoming email transaction has recipients user@example1.com, user@example2.com, and user@example3.com. The first two recipients will be accepted (they share the same forward destination) and the latter will be deferred. It will arrive in a future delivery attempt by the remote.
@@ -1,4 +1,4 @@
1
- queue/smtp\_proxy
1
+ # queue/smtp\_proxy
2
2
  ================
3
3
 
4
4
  This plugin delivers to another mail server. This is a common setup when you
@@ -12,14 +12,12 @@ particular one important facility to some setups is recipient filtering.
12
12
  However be aware that other than connect and HELO-time filtering, you will
13
13
  have as many connections to your ongoing SMTP server as you have to Haraka.
14
14
 
15
- Configuration
15
+ ## Configuration
16
16
  -------------
17
17
 
18
- * smtp\_proxy.ini
19
-
20
- Configuration is stored in this file in the following keys:
18
+ Configuration is stored in smtp\_proxy.ini in the following keys:
21
19
 
22
- * enable\_outbound=[true]
20
+ * enable\_outbound=[true]
23
21
 
24
22
  SMTP proxy outbound messages (set to false to enable Haraka's
25
23
  separate Outbound mail routing (MX based delivery)).
@@ -27,9 +25,9 @@ Configuration
27
25
  * host=HOST
28
26
 
29
27
  The host to connect to.
30
-
28
+
31
29
  * port=PORT
32
-
30
+
33
31
  The port to connect to.
34
32
 
35
33
  * connect\_timeout=SECONDS
@@ -38,17 +36,17 @@ Configuration
38
36
  to the host. Default if unspecified is 30 seconds.
39
37
 
40
38
  * timeout=SECONDS
41
-
39
+
42
40
  The amount of seconds to let a backend connection live idle in the
43
41
  proxy pool. This should always be less than the global plugin timeout,
44
42
  which should in turn be less than the connection timeout.
45
43
 
46
44
  * max\_connections=NUMBER
47
-
45
+
48
46
  Maximum number of connections to create at any given time.
49
47
 
50
48
  * enable\_tls=[true|yes|1]
51
-
49
+
52
50
  Enable TLS with the forward host (if supported). TLS uses options from
53
51
  the tls plugin.
54
52
 
@@ -8,7 +8,7 @@ This **relay** plugin provides Haraka with options for managing relay permission
8
8
 
9
9
  ## Authentication
10
10
 
11
- One way to enable relaying is [authentication](http://haraka.github.io/manual.html) via the auth plugins. Successful authentication enables relaying during _that_ SMTP connection. To securely offer SMTP AUTH, the [tls](http://haraka.github.io/manual/plugins/tls.html) plugin and at least one auth plugin must be enabled and properly configured. When that requirement is met, the AUTH SMTP extension will be advertised to SMTP clients.
11
+ One way to enable relaying is authentication via the [auth plugins](http://haraka.github.io/plugins). Successful authentication enables relaying during _that_ SMTP connection. To securely offer SMTP AUTH, the [tls](http://haraka.github.io/plugins/tls) plugin and at least one auth plugin must be enabled and properly configured. When that requirement is met, the AUTH SMTP extension will be advertised to SMTP clients.
12
12
 
13
13
  % nc mail.example.com 587
14
14
  220 mail.example.com ESMTP Haraka 2.4.0 ready
@@ -118,7 +118,7 @@ Example:
118
118
  [domains]
119
119
  test.com = { "action": "accept" }
120
120
 
121
- I think of *accept* as the equivalent of qmail's *rcpthosts*, or a misplaced Haraka `rcpt_to.*` plugin. The *accept* mechanism is another way to tell Haraka that a particular domain is one we accept mail for. The difference between this and the [rcpt_to.in_host_list](http://haraka.github.io/manual/plugins/rcpt_to.in_host_list.html) plugin is that this one also enables relaying.
121
+ Think of *accept* as the equivalent of qmail's *rcpthosts*, or a misplaced Haraka `rcpt_to.*` plugin. The *accept* mechanism is another way to tell Haraka that a particular domain is one we accept mail for. The difference between this and the [rcpt_to.in_host_list](http://haraka.github.io/plugins/rcpt_to.in_host_list) plugin is that this one also enables relaying.
122
122
 
123
123
  * continue (mails are subject to further checks)
124
124
 
package/outbound/hmail.js CHANGED
@@ -103,7 +103,7 @@ class HMailItem extends events.EventEmitter {
103
103
 
104
104
  this._stream_bytes_from(this.path, {start: 4, end: todo_len + 3}, (err2, todo_bytes) => {
105
105
  if (todo_bytes.length !== todo_len) {
106
- const wrongLength = `Didn't find right amount of data in todo!: ${err2}`;
106
+ const wrongLength = `Didn't find right amount of data in todo!: ${err2} ${this.path}`;
107
107
  this.logcrit(wrongLength);
108
108
  fs.rename(this.path, path.join(queue_dir, `error.${this.filename}`), (err3) => {
109
109
  if (err3) {
@@ -1062,7 +1062,7 @@ class HMailItem extends events.EventEmitter {
1062
1062
  "\r": '#10',
1063
1063
  "\n": '#13'
1064
1064
  };
1065
- const escape_pattern = new RegExp(`[${Object.keys(escaped_chars).join()}]`, 'g');
1065
+ const escape_pattern = new RegExp(`[${Object.keys(escaped_chars).join('')}]`, 'g');
1066
1066
 
1067
1067
  bounce_msg_html_.forEach(line => {
1068
1068
  line = line.replace(/\{(\w+)\}/g, (i, word) => {
package/outbound/queue.js CHANGED
@@ -103,6 +103,11 @@ exports.read_parts = file => {
103
103
  return false;
104
104
  }
105
105
 
106
+ if (file.startsWith('error.')) {
107
+ logger.logwarn(`[outbound] 'Skipping' error file in queue folder: ${file}`);
108
+ return false;
109
+ }
110
+
106
111
  const parts = _qfile.parts(file);
107
112
  if (!parts) {
108
113
  logger.logerror(`[outbound] Unrecognized file in queue folder: ${file}`);
package/outbound/tls.js CHANGED
@@ -95,7 +95,7 @@ class OutboundTLS {
95
95
 
96
96
  logger.lognotice(this, `TLS connection failed. Marking ${host} as non-TLS for ${expiry} seconds`);
97
97
 
98
- this.db.setex(dbkey, expiry, (new Date()).toISOString())
98
+ this.db.setEx(dbkey, expiry, (new Date()).toISOString())
99
99
  .then(cb)
100
100
  .catch(err => {
101
101
  logger.logerror(this, `Redis returned error: ${err}`);
package/package.json CHANGED
@@ -9,7 +9,7 @@
9
9
  "server",
10
10
  "email"
11
11
  ],
12
- "version": "3.0.1",
12
+ "version": "3.0.3",
13
13
  "homepage": "http://haraka.github.io",
14
14
  "repository": {
15
15
  "type": "git",
@@ -20,28 +20,28 @@
20
20
  "node": ">=16"
21
21
  },
22
22
  "dependencies": {
23
- "address-rfc2821": "^2.0.1",
23
+ "address-rfc2821": "^2.1.1",
24
24
  "address-rfc2822": "^2.1.0",
25
- "async": "^3.2.4",
25
+ "async": "^3.2.5",
26
26
  "daemon": "~1.1.0",
27
- "ipaddr.js": "~2.0.1",
28
- "node-gyp": "^9.3.1",
29
- "nopt": "~7.0.0",
27
+ "ipaddr.js": "~2.1.0",
28
+ "node-gyp": "^10.0.1",
29
+ "nopt": "~7.2.0",
30
30
  "npid": "~0.4.0",
31
- "semver": "~7.3.8",
32
- "sprintf-js": "~1.1.2",
31
+ "semver": "~7.6.0",
32
+ "sprintf-js": "~1.1.3",
33
33
  "haraka-config": "^1.1.0",
34
34
  "haraka-constants": "^1.0.6",
35
35
  "haraka-dsn": "^1.0.4",
36
36
  "haraka-email-message": "^1.2.0",
37
37
  "haraka-message-stream": "^1.2.0",
38
- "haraka-net-utils": "^1.5.0",
38
+ "haraka-net-utils": "^1.5.3",
39
39
  "haraka-notes": "^1.0.6",
40
40
  "haraka-plugin-attachment": "^1.0.7",
41
- "haraka-plugin-spf": "1.2.0",
42
- "haraka-plugin-redis": "^2.0.5",
43
- "haraka-results": "^2.2.2",
44
- "haraka-tld": "^1.1.0",
41
+ "haraka-plugin-spf": "1.2.4",
42
+ "haraka-plugin-redis": "^2.0.6",
43
+ "haraka-results": "^2.2.3",
44
+ "haraka-tld": "^1.2.0",
45
45
  "haraka-utils": "^1.0.3",
46
46
  "openssl-wrapper": "^0.3.4",
47
47
  "sockaddr": "^1.0.1"
@@ -49,36 +49,36 @@
49
49
  "optionalDependencies": {
50
50
  "haraka-plugin-access": "^1.1.5",
51
51
  "haraka-plugin-aliases": "^1.0.1",
52
- "haraka-plugin-asn": "^2.0.1",
53
- "haraka-plugin-auth-ldap": "^1.0.2",
54
- "haraka-plugin-dcc": "^1.0.1",
55
- "haraka-plugin-elasticsearch": "^1.0.6",
52
+ "haraka-plugin-asn": "^2.0.2",
53
+ "haraka-plugin-auth-ldap": "^1.1.0",
54
+ "haraka-plugin-dcc": "^1.0.2",
55
+ "haraka-plugin-elasticsearch": "^8.0.2",
56
56
  "haraka-plugin-fcrdns": "^1.1.0",
57
57
  "haraka-plugin-graph": "^1.0.5",
58
58
  "haraka-plugin-geoip": "^1.0.17",
59
59
  "haraka-plugin-headers": "^1.0.3",
60
- "haraka-plugin-karma": "^2.1.0",
61
- "haraka-plugin-limit": "^1.1.0",
60
+ "haraka-plugin-karma": "^2.1.2",
61
+ "haraka-plugin-limit": "^1.1.1",
62
62
  "haraka-plugin-p0f": "^1.0.9",
63
- "haraka-plugin-qmail-deliverable": "^1.1.1",
64
- "haraka-plugin-known-senders": "^1.0.8",
65
- "haraka-plugin-rcpt-ldap": "^1.0.0",
66
- "haraka-plugin-recipient-routes": "^1.0.4",
67
- "haraka-plugin-rspamd": "^1.2.0",
68
- "haraka-plugin-syslog": "^1.0.3",
69
- "haraka-plugin-uribl": "^1.0.6",
63
+ "haraka-plugin-qmail-deliverable": "^1.2.1",
64
+ "haraka-plugin-known-senders": "^1.0.9",
65
+ "haraka-plugin-rcpt-ldap": "^1.1.0",
66
+ "haraka-plugin-recipient-routes": "^1.2.0",
67
+ "haraka-plugin-rspamd": "^1.3.1",
68
+ "haraka-plugin-syslog": "^1.0.5",
69
+ "haraka-plugin-uribl": "^1.0.8",
70
70
  "haraka-plugin-watch": "^2.0.2",
71
71
  "ocsp": "~1.2.0",
72
- "redis": "~4.5.1",
72
+ "redis": "~4.6.11",
73
73
  "tmp": "~0.2.1"
74
74
  },
75
75
  "devDependencies": {
76
- "nodeunit-x": "^0.15.0",
77
- "haraka-test-fixtures": "^1.2.1",
76
+ "nodeunit-x": "^0.16.0",
77
+ "haraka-test-fixtures": "^1.3.3",
78
78
  "mock-require": "^3.0.3",
79
- "eslint": "^8.27.0",
79
+ "eslint": "^8.56.0",
80
80
  "eslint-plugin-haraka": "^1.0.15",
81
- "nodemailer": "^6.7.7"
81
+ "nodemailer": "^6.9.9"
82
82
  },
83
83
  "bugs": {
84
84
  "mail": "haraka.mail@gmail.com",
@@ -5,7 +5,10 @@
5
5
  // Note: You can disable setting `connection.notes.auth_passwd` by `plugin.blankout_password = true`
6
6
 
7
7
  const crypto = require('crypto');
8
- const utils = require('haraka-utils');
8
+
9
+ const tlds = require('haraka-tld')
10
+ const utils = require('haraka-utils');
11
+
9
12
  const AUTH_COMMAND = 'AUTH';
10
13
  const AUTH_METHOD_CRAM_MD5 = 'CRAM-MD5';
11
14
  const AUTH_METHOD_PLAIN = 'PLAIN';
@@ -15,7 +18,7 @@ const LOGIN_STRING2 = 'UGFzc3dvcmQ6'; //Password: base64 coded
15
18
 
16
19
  exports.hook_capabilities = (next, connection) => {
17
20
  // Don't offer AUTH capabilities unless session is encrypted
18
- if (!connection.tls.enabled) { return next(); }
21
+ if (!connection.tls.enabled) return next();
19
22
 
20
23
  const methods = [ 'PLAIN', 'LOGIN', 'CRAM-MD5' ];
21
24
  connection.capabilities.push(`AUTH ${methods.join(' ')}`);
@@ -47,9 +50,7 @@ exports.hook_unrecognized_command = function (next, connection, params) {
47
50
 
48
51
  exports.check_plain_passwd = function (connection, user, passwd, cb) {
49
52
  function callback (plain_pw) {
50
- if (plain_pw === null ) return cb(false);
51
- if (plain_pw !== passwd) return cb(false);
52
- cb(true);
53
+ cb(plain_pw === null ? false : plain_pw === passwd);
53
54
  }
54
55
  if (this.get_plain_passwd.length == 2) {
55
56
  this.get_plain_passwd(user, callback);
@@ -71,7 +72,7 @@ exports.check_cram_md5_passwd = function (connection, user, passwd, cb) {
71
72
 
72
73
  if (hmac.digest('hex') === passwd) return cb(true);
73
74
 
74
- return cb(false);
75
+ cb(false);
75
76
  }
76
77
  if (this.get_plain_passwd.length == 2) {
77
78
  this.get_plain_passwd(user, callback);
@@ -117,7 +118,7 @@ exports.check_user = function (next, connection, credentials, method) {
117
118
  connection.auth_results(`auth=pass (${method.toLowerCase()})`);
118
119
  connection.notes.auth_user = credentials[0];
119
120
  if (!plugin.blankout_password) connection.notes.auth_passwd = credentials[1];
120
- return next(OK);
121
+ next(OK);
121
122
  });
122
123
  return;
123
124
  }
@@ -125,9 +126,7 @@ exports.check_user = function (next, connection, credentials, method) {
125
126
  if (!connection.notes.auth_fails) connection.notes.auth_fails = 0;
126
127
 
127
128
  connection.notes.auth_fails++;
128
- connection.results.add({name: 'auth'}, {
129
- fail:`${plugin.name}/${method}`,
130
- });
129
+ connection.results.add({name: 'auth'}, { fail:`${plugin.name}/${method}` });
131
130
 
132
131
  let delay = Math.pow(2, connection.notes.auth_fails - 1);
133
132
  if (plugin.timeout && delay >= plugin.timeout) {
@@ -230,7 +229,7 @@ exports.auth_cram_md5 = function (next, connection, params) {
230
229
  return this.check_user(next, connection, credentials, AUTH_METHOD_CRAM_MD5);
231
230
  }
232
231
 
233
- const ticket = `<${this.hexi(Math.floor(Math.random() * 1000000))}. ${this.hexi(Date.now())}@${connection.local.host}>`;
232
+ const ticket = `<${this.hexi(Math.floor(Math.random() * 1000000))}.${this.hexi(Date.now())}@${connection.local.host}>`;
234
233
 
235
234
  connection.loginfo(this, `ticket: ${ticket}`);
236
235
  connection.respond(334, utils.base64(ticket), () => {
@@ -240,3 +239,20 @@ exports.auth_cram_md5 = function (next, connection, params) {
240
239
  }
241
240
 
242
241
  exports.hexi = number => String(Math.abs(parseInt(number)).toString(16))
242
+
243
+ exports.constrain_sender = function (next, connection, params) {
244
+ const au = connection.results.get('auth')?.user
245
+ if (!au) return next()
246
+
247
+ const ad = /@/.test(au) ? au.split('@').pop() : au
248
+ const ed = params[0].host
249
+
250
+ if (!ad || !ed) return next()
251
+
252
+ const auth_od = tlds.get_organizational_domain(ad)
253
+ const envelope_od = tlds.get_organizational_domain(ed)
254
+
255
+ if (auth_od === envelope_od) return next()
256
+
257
+ next(DENY, `Envelope domain '${envelope_od}' doesn't match AUTH domain '${auth_od}'`)
258
+ }
@@ -4,25 +4,36 @@ const net = require('net');
4
4
 
5
5
  exports.register = function () {
6
6
  this.inherits('auth/auth_base');
7
- this.load_vpop_ini();
7
+ this.blankout_password=true
8
+
9
+ this.load_vpopmaild_ini();
10
+
11
+ if (this.cfg.main.constrain_sender) {
12
+ this.register_hook('mail', 'constrain_sender')
13
+ }
8
14
  }
9
15
 
10
- exports.load_vpop_ini = function () {
11
- this.cfg = this.config.get('auth_vpopmaild.ini', () => {
12
- this.load_vpop_ini();
16
+ exports.load_vpopmaild_ini = function () {
17
+ this.cfg = this.config.get('auth_vpopmaild.ini', {
18
+ booleans: [
19
+ '+main.constrain_sender',
20
+ ]
21
+ },
22
+ () => {
23
+ this.load_vpopmaild_ini();
13
24
  });
14
25
  }
15
26
 
16
27
  exports.hook_capabilities = function (next, connection) {
17
- if (!connection.tls.enabled) { return next(); }
28
+ if (!connection.tls.enabled) return next();
18
29
 
19
30
  const methods = [ 'PLAIN', 'LOGIN' ];
20
- if (this.cfg.main.sysadmin) { methods.push('CRAM-MD5'); }
31
+ if (this.cfg.main.sysadmin) methods.push('CRAM-MD5');
21
32
 
22
33
  connection.capabilities.push(`AUTH ${methods.join(' ')}`);
23
34
  connection.notes.allowed_auth_methods = methods;
24
35
 
25
- return next();
36
+ next();
26
37
  }
27
38
 
28
39
  exports.check_plain_passwd = function (connection, user, passwd, cb) {
@@ -49,11 +60,12 @@ exports.check_plain_passwd = function (connection, user, passwd, cb) {
49
60
  }
50
61
  socket.end(); // disconnect
51
62
  }
52
- });
63
+ })
64
+
53
65
  socket.on('end', () => {
54
66
  connection.loginfo(this, `AUTH user="${user}" success=${auth_success}`);
55
- return cb(auth_success);
56
- });
67
+ cb(auth_success);
68
+ })
57
69
  }
58
70
 
59
71
  exports.get_sock_opts = function (user) {
@@ -66,13 +78,11 @@ exports.get_sock_opts = function (user) {
66
78
 
67
79
  const domain = (user.split('@'))[1];
68
80
  let sect = this.cfg.main;
69
- if (domain && this.cfg[domain]) {
70
- sect = this.cfg[domain];
71
- }
81
+ if (domain && this.cfg[domain]) sect = this.cfg[domain];
72
82
 
73
- if (sect.port) { this.sock_opts.port = sect.port; }
74
- if (sect.host) { this.sock_opts.host = sect.host; }
75
- if (sect.sysadmin) { this.sock_opts.sysadmin = sect.sysadmin; }
83
+ if (sect.port) this.sock_opts.port = sect.port;
84
+ if (sect.host) this.sock_opts.host = sect.host;
85
+ if (sect.sysadmin) this.sock_opts.sysadmin = sect.sysadmin;
76
86
 
77
87
  this.logdebug(`sock: ${this.sock_opts.host}:${this.sock_opts.port}`);
78
88
  return this.sock_opts;
@@ -89,14 +99,14 @@ exports.get_vpopmaild_socket = function (user) {
89
99
  socket.on('timeout', () => {
90
100
  this.logerror("vpopmaild connection timed out");
91
101
  socket.end();
92
- });
102
+ })
93
103
  socket.on('error', err => {
94
104
  this.logerror(`vpopmaild connection failed: ${err}`);
95
105
  socket.end();
96
- });
106
+ })
97
107
  socket.on('connect', () => {
98
108
  this.logdebug('vpopmail connected');
99
- });
109
+ })
100
110
  return socket;
101
111
  }
102
112
 
@@ -3,26 +3,32 @@
3
3
  exports.register = function () {
4
4
  this.inherits('auth/auth_base');
5
5
  this.load_flat_ini();
6
+
7
+ if (this.cfg.core.constrain_sender) {
8
+ this.register_hook('mail', 'constrain_sender')
9
+ }
6
10
  }
7
11
 
8
12
  exports.load_flat_ini = function () {
9
- this.cfg = this.config.get('auth_flat_file.ini', () => {
13
+ this.cfg = this.config.get('auth_flat_file.ini', {
14
+ booleans: [
15
+ '+core.constrain_sender',
16
+ ]
17
+ },
18
+ () => {
10
19
  this.load_flat_ini();
11
20
  });
21
+
22
+ if (this.cfg.users === undefined) this.cfg.users = {}
12
23
  }
13
24
 
14
25
  exports.hook_capabilities = function (next, connection) {
15
- // don't allow AUTH unless private IP or encrypted
16
26
  if (!connection.remote.is_private && !connection.tls.enabled) {
17
- connection.logdebug(this,
18
- "Auth disabled for insecure public connection");
27
+ connection.logdebug(this, "Auth disabled for insecure public connection");
19
28
  return next();
20
29
  }
21
30
 
22
- let methods = null;
23
- if (this.cfg.core?.methods ) {
24
- methods = this.cfg.core.methods.split(',');
25
- }
31
+ const methods = this.cfg.core?.methods ? this.cfg.core.methods.split(',') : null
26
32
  if (methods && methods.length > 0) {
27
33
  connection.capabilities.push(`AUTH ${methods.join(' ')}`);
28
34
  connection.notes.allowed_auth_methods = methods;
@@ -31,8 +37,7 @@ exports.hook_capabilities = function (next, connection) {
31
37
  }
32
38
 
33
39
  exports.get_plain_passwd = function (user, connection, cb) {
34
- if (this.cfg.users[user]) {
35
- return cb(this.cfg.users[user].toString());
36
- }
37
- return cb();
40
+ if (this.cfg.users[user]) return cb(this.cfg.users[user].toString());
41
+
42
+ cb();
38
43
  }