Browse Source

support implicit TLS for mail submission agents

Fixes #2048
implicittls_backport
Shivaram Lingamneni 1 year ago
parent
commit
71871ca1ef
4 changed files with 40 additions and 27 deletions
  1. 1
    0
      default.yaml
  2. 11
    5
      irc/email/email.go
  3. 27
    22
      irc/smtp/smtp.go
  4. 1
    0
      traditional.yaml

+ 1
- 0
default.yaml View File

@@ -414,6 +414,7 @@ accounts:
414 414
             #     port: 25
415 415
             #     username: "admin"
416 416
             #     password: "hunter2"
417
+            #     implicit-tls: false # TLS from the first byte, typically on port 465
417 418
             blacklist-regexes:
418 419
             #    - ".*@mailinator.com"
419 420
             timeout: 60s

+ 11
- 5
irc/email/email.go View File

@@ -24,10 +24,11 @@ var (
24 24
 )
25 25
 
26 26
 type MTAConfig struct {
27
-	Server   string
28
-	Port     int
29
-	Username string
30
-	Password string
27
+	Server      string
28
+	Port        int
29
+	Username    string
30
+	Password    string
31
+	ImplicitTLS bool `yaml:"implicit-tls"`
31 32
 }
32 33
 
33 34
 type MailtoConfig struct {
@@ -132,11 +133,13 @@ func SendMail(config MailtoConfig, recipient string, msg []byte) (err error) {
132 133
 
133 134
 	var addr string
134 135
 	var auth smtp.Auth
136
+	var implicitTLS bool
135 137
 	if !config.DirectSendingEnabled() {
136 138
 		addr = fmt.Sprintf("%s:%d", config.MTAReal.Server, config.MTAReal.Port)
137 139
 		if config.MTAReal.Username != "" && config.MTAReal.Password != "" {
138 140
 			auth = smtp.PlainAuth("", config.MTAReal.Username, config.MTAReal.Password, config.MTAReal.Server)
139 141
 		}
142
+		implicitTLS = config.MTAReal.ImplicitTLS
140 143
 	} else {
141 144
 		idx := strings.IndexByte(recipient, '@')
142 145
 		if idx == -1 {
@@ -149,5 +152,8 @@ func SendMail(config MailtoConfig, recipient string, msg []byte) (err error) {
149 152
 		addr = fmt.Sprintf("%s:smtp", mx)
150 153
 	}
151 154
 
152
-	return smtp.SendMail(addr, auth, config.HeloDomain, config.Sender, []string{recipient}, msg, config.RequireTLS, config.Timeout)
155
+	return smtp.SendMail(
156
+		addr, auth, config.HeloDomain, config.Sender, []string{recipient}, msg,
157
+		config.RequireTLS, implicitTLS, config.Timeout,
158
+	)
153 159
 }

+ 27
- 22
irc/smtp/smtp.go View File

@@ -55,14 +55,17 @@ type Client struct {
55 55
 
56 56
 // Dial returns a new Client connected to an SMTP server at addr.
57 57
 // The addr must include a port, as in "mail.example.com:smtp".
58
-func Dial(addr string, timeout time.Duration) (*Client, error) {
58
+func Dial(addr string, timeout time.Duration, implicitTLS bool) (*Client, error) {
59 59
 	var conn net.Conn
60 60
 	var err error
61
+	dialer := net.Dialer{
62
+		Timeout: timeout,
63
+	}
61 64
 	start := time.Now()
62
-	if timeout == 0 {
63
-		conn, err = net.Dial("tcp", addr)
65
+	if !implicitTLS {
66
+		conn, err = dialer.Dial("tcp", addr)
64 67
 	} else {
65
-		conn, err = net.DialTimeout("tcp", addr, timeout)
68
+		conn, err = tls.DialWithDialer(&dialer, "tcp", addr, nil)
66 69
 	}
67 70
 	if err != nil {
68 71
 		return nil, err
@@ -338,7 +341,7 @@ var testHookStartTLS func(*tls.Config) // nil, except for tests
338 341
 // functionality. Higher-level packages exist outside of the standard
339 342
 // library.
340 343
 // XXX: modified in Ergo to add `requireTLS`, `heloDomain`, and `timeout` arguments
341
-func SendMail(addr string, a Auth, heloDomain string, from string, to []string, msg []byte, requireTLS bool, timeout time.Duration) error {
344
+func SendMail(addr string, a Auth, heloDomain string, from string, to []string, msg []byte, requireTLS, implicitTLS bool, timeout time.Duration) error {
342 345
 	if err := validateLine(from); err != nil {
343 346
 		return err
344 347
 	}
@@ -347,7 +350,7 @@ func SendMail(addr string, a Auth, heloDomain string, from string, to []string,
347 350
 			return err
348 351
 		}
349 352
 	}
350
-	c, err := Dial(addr, timeout)
353
+	c, err := Dial(addr, timeout, implicitTLS)
351 354
 	if err != nil {
352 355
 		return err
353 356
 	}
@@ -355,23 +358,25 @@ func SendMail(addr string, a Auth, heloDomain string, from string, to []string,
355 358
 	if err = c.Hello(heloDomain); err != nil {
356 359
 		return err
357 360
 	}
358
-	if ok, _ := c.Extension("STARTTLS"); ok {
359
-		var config *tls.Config
360
-		if requireTLS {
361
-			config = &tls.Config{ServerName: c.serverName}
362
-		} else {
363
-			// if TLS isn't a hard requirement, don't verify the certificate either,
364
-			// since a MITM attacker could just remove the STARTTLS advertisement
365
-			config = &tls.Config{InsecureSkipVerify: true}
366
-		}
367
-		if testHookStartTLS != nil {
368
-			testHookStartTLS(config)
369
-		}
370
-		if err = c.StartTLS(config); err != nil {
371
-			return err
361
+	if !implicitTLS {
362
+		if ok, _ := c.Extension("STARTTLS"); ok {
363
+			var config *tls.Config
364
+			if requireTLS {
365
+				config = &tls.Config{ServerName: c.serverName}
366
+			} else {
367
+				// if TLS isn't a hard requirement, don't verify the certificate either,
368
+				// since a MITM attacker could just remove the STARTTLS advertisement
369
+				config = &tls.Config{InsecureSkipVerify: true}
370
+			}
371
+			if testHookStartTLS != nil {
372
+				testHookStartTLS(config)
373
+			}
374
+			if err = c.StartTLS(config); err != nil {
375
+				return err
376
+			}
377
+		} else if requireTLS {
378
+			return errors.New("TLS required, but not negotiated")
372 379
 		}
373
-	} else if requireTLS {
374
-		return errors.New("TLS required, but not negotiated")
375 380
 	}
376 381
 	if a != nil && c.ext != nil {
377 382
 		if _, ok := c.ext["AUTH"]; !ok {

+ 1
- 0
traditional.yaml View File

@@ -387,6 +387,7 @@ accounts:
387 387
             #     port: 25
388 388
             #     username: "admin"
389 389
             #     password: "hunter2"
390
+            #     implicit-tls: false # TLS from the first byte, typically on port 465
390 391
             blacklist-regexes:
391 392
             #    - ".*@mailinator.com"
392 393
             timeout: 60s

Loading…
Cancel
Save