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