1 #include "mailfront.h"
2 #include <stdlib.h>
3 #include <unistd.h>
4 #include <bglibs/iobuf.h>
5
6 static RESPONSE(start, 220, "2.0.0 Ready to start TLS");
7 static RESPONSE(earlytalker, 500, "5.5.1 Unexpected pipelined commands following STARTTLS");
8
9 static int tls_available = 0;
10
init(void)11 static const response* init(void)
12 {
13 tls_available = getenv("UCSPITLS") != 0;
14 return 0;
15 }
16
helo(str * hostname,str * capabilities)17 static const response* helo(str* hostname, str* capabilities)
18 {
19 if (tls_available)
20 if (!str_cats(capabilities, "STARTTLS\n")) return &resp_oom;
21 return 0;
22 (void)hostname;
23 }
24
starttls(void)25 static int starttls(void)
26 {
27 int fd;
28 char *fdstr;
29 int extrachars = 0;
30 char c;
31
32 /* STARTTLS must be the last command in a pipeline, otherwise we can
33 * create a security risk (see CVE-2011-0411). Close input and
34 * check for any extra pipelined commands, so we can give an error
35 * message. Note that this will cause an error on the filehandle,
36 * since we have closed it. */
37 close(0);
38
39 while (!ibuf_eof(&inbuf) && !ibuf_error(&inbuf)) {
40 if (ibuf_getc(&inbuf, &c))
41 ++extrachars;
42 }
43
44 if (!(fdstr=getenv("SSLCTLFD")))
45 return 0;
46 if ((fd = atoi(fdstr)) <= 0)
47 return 0;
48 if (write(fd, "y", 1) < 1)
49 return 0;
50
51 if (!(fdstr=getenv("SSLREADFD")))
52 return 0;
53 if ((fd = atoi(fdstr)) <= 0)
54 return 0;
55 if (dup2(fd, 0) != 0)
56 return 0;
57
58 if (!(fdstr=getenv("SSLWRITEFD")))
59 return 0;
60 if ((fd = atoi(fdstr)) <= 0)
61 return 0;
62 if (dup2(fd, 1) != 1)
63 return 0;
64
65 /* Re-initialize stdin and clear input buffer */
66 ibuf_init(&inbuf,0,0,IOBUF_NEEDSCLOSE, 4096);
67
68 if (extrachars)
69 respond(&resp_earlytalker);
70
71 return 1;
72 }
73
enabled(void)74 static int enabled(void)
75 {
76 return tls_available;
77 }
78
cmd_STARTTLS(void)79 static int cmd_STARTTLS(void)
80 {
81 if (!respond(&resp_start))
82 return 0;
83
84 if (!starttls())
85 return 0;
86
87 tls_available = 0;
88 session_setnum("tls_state", 1);
89
90 /* Remove UCSPITLS from environment to indicate it no longer available. */
91 unsetenv("UCSPITLS");
92 return 1;
93 }
94
95 static const struct command commands[] = {
96 { "STARTTLS", .fn_enabled = enabled, .fn_noparam = cmd_STARTTLS },
97 { .name = 0 }
98 };
99
100 struct plugin plugin = {
101 .version = PLUGIN_VERSION,
102 .commands = commands,
103 .init = init,
104 .helo = helo,
105 };
106