1 /* TLS I/O streams */
2 
3 #if HAVE_CONFIG_H
4 # include <config.h>
5 #endif
6 #include <stdlib.h>
7 #include <gnutls/gnutls.h>
8 #include <mailutils/stream.h>
9 #include <mailutils/errno.h>
10 #include <mailutils/property.h>
11 #include <mailutils/sys/tls-stream.h>
12 
13 static int
_tls_io_close(mu_stream_t stream)14 _tls_io_close (mu_stream_t stream)
15 {
16   struct _mu_tls_io_stream *sp = (struct _mu_tls_io_stream *) stream;
17 
18   return mu_stream_close (sp->transport);
19 }
20 
21 static void
_tls_io_done(struct _mu_stream * stream)22 _tls_io_done (struct _mu_stream *stream)
23 {
24   struct _mu_tls_io_stream *sp = (struct _mu_tls_io_stream *) stream;
25   mu_stream_unref (sp->transport);
26 }
27 
28 static int
_tls_io_flush(struct _mu_stream * stream)29 _tls_io_flush (struct _mu_stream *stream)
30 {
31   struct _mu_tls_io_stream *sp = (struct _mu_tls_io_stream *) stream;
32   return mu_stream_flush (sp->transport);
33 }
34 
35 static int
_tls_io_read(struct _mu_stream * stream,char * buf,size_t bufsize,size_t * pnread)36 _tls_io_read (struct _mu_stream *stream, char *buf, size_t bufsize,
37 	      size_t *pnread)
38 {
39   struct _mu_tls_io_stream *sp = (struct _mu_tls_io_stream *) stream;
40   ssize_t rc;
41 
42   if (sp->up->state != state_open)
43     return EINVAL;
44   do
45     rc = gnutls_record_recv (sp->up->session, buf, bufsize);
46   while (rc == GNUTLS_E_AGAIN || rc == GNUTLS_E_INTERRUPTED);
47   if (rc >= 0)
48     {
49       *pnread = rc;
50       return 0;
51     }
52   sp->up->tls_err = rc;
53   return EIO;
54 }
55 
56 static int
_tls_io_write(struct _mu_stream * stream,const char * buf,size_t bufsize,size_t * pnwrite)57 _tls_io_write (struct _mu_stream *stream, const char *buf, size_t bufsize,
58 	    size_t *pnwrite)
59 {
60   struct _mu_tls_io_stream *sp = (struct _mu_tls_io_stream *) stream;
61   ssize_t rc;
62 
63   if (sp->up->state != state_open)
64     return EINVAL;
65 
66   /* gnutls_record_send() docs say:
67        If the EINTR is returned by the internal push function (write())
68        then GNUTLS_E_INTERRUPTED, will be returned. If GNUTLS_E_INTERRUPTED or
69        GNUTLS_E_AGAIN is returned you must call this function again, with the
70        same parameters. Otherwise the write operation will be
71        corrupted and the connection will be terminated. */
72 
73   do
74     rc = gnutls_record_send (sp->up->session, buf, bufsize);
75   while (rc == GNUTLS_E_INTERRUPTED || rc == GNUTLS_E_AGAIN);
76 
77   if (rc < 0)
78     {
79       sp->up->tls_err = rc;
80       return EIO;
81     }
82 
83   *pnwrite = rc;
84 
85   return 0;
86 }
87 
88 static int
_tls_rd_wait(struct _mu_stream * stream,int * pflags,struct timeval * tvp)89 _tls_rd_wait (struct _mu_stream *stream, int *pflags, struct timeval *tvp)
90 {
91   struct _mu_tls_io_stream *sp = (struct _mu_tls_io_stream *) stream;
92   int rc = EINVAL;
93 
94   if (*pflags == MU_STREAM_READY_RD)
95     rc = mu_stream_wait (sp->transport, pflags, tvp);
96   return rc;
97 }
98 
99 static int
_tls_wr_wait(struct _mu_stream * stream,int * pflags,struct timeval * tvp)100 _tls_wr_wait (struct _mu_stream *stream, int *pflags, struct timeval *tvp)
101 {
102   struct _mu_tls_io_stream *sp = (struct _mu_tls_io_stream *) stream;
103   int rc = EINVAL;
104 
105   if (*pflags == MU_STREAM_READY_WR)
106     rc = mu_stream_wait (sp->transport, pflags, tvp);
107   return rc;
108 }
109 
110 static int
get_cipher_info(gnutls_session_t session,mu_property_t * pprop)111 get_cipher_info (gnutls_session_t session, mu_property_t *pprop)
112 {
113   mu_property_t prop;
114   const char *s;
115   int rc;
116 
117   if (!pprop)
118     return EINVAL;
119 
120   rc = mu_property_create_init (&prop, mu_assoc_property_init, NULL);
121   if (rc)
122     return rc;
123 
124   s = gnutls_protocol_get_name (gnutls_protocol_get_version (session));
125   mu_property_set_value (prop, "protocol", s, 1);
126 
127   s = gnutls_cipher_get_name (gnutls_cipher_get (session));
128   mu_property_set_value (prop, "cipher", s, 1);
129 
130   s = gnutls_mac_get_name (gnutls_mac_get (session));
131   mu_property_set_value (prop, "mac", s, 1);
132 
133   *pprop = prop;
134 
135   return 0;
136 }
137 
138 static int
_tls_io_ioctl(struct _mu_stream * stream,int code,int opcode,void * arg)139 _tls_io_ioctl (struct _mu_stream *stream, int code, int opcode, void *arg)
140 {
141   struct _mu_tls_io_stream *sp = (struct _mu_tls_io_stream *) stream;
142 
143   switch (code)
144     {
145     case MU_IOCTL_TRANSPORT:
146       if (!arg)
147 	return EINVAL;
148       else
149 	{
150 	  mu_transport_t *ptrans = arg;
151 	  switch (opcode)
152 	    {
153 	    case MU_IOCTL_OP_GET:
154 	      ptrans[0] = (mu_transport_t) sp->transport;
155 	      ptrans[1] = NULL;
156 	      break;
157 
158 	    case MU_IOCTL_OP_SET:
159 	      return ENOSYS;
160 
161 	    default:
162 	      return EINVAL;
163 	    }
164 	}
165       break;
166 
167     case MU_IOCTL_TLSSTREAM:
168       switch (opcode)
169 	{
170 	case MU_IOCTL_TLS_GET_CIPHER_INFO:
171 	  return get_cipher_info (sp->up->session, arg);
172 
173 	default:
174 	  return EINVAL;
175 	}
176       break;
177 
178     default:
179       return mu_stream_ioctl (sp->transport, code, opcode, arg);
180     }
181   return 0;
182 }
183 
184 int
mu_tls_io_stream_create(mu_stream_t * pstream,mu_stream_t transport,int flags,struct _mu_tls_stream * master)185 mu_tls_io_stream_create (mu_stream_t *pstream,
186 			 mu_stream_t transport, int flags,
187 			 struct _mu_tls_stream *master)
188 {
189   struct _mu_tls_io_stream *sp;
190 
191   sp = (struct _mu_tls_io_stream *)
192     _mu_stream_create (sizeof (*sp), (flags & MU_STREAM_RDWR) | _MU_STR_OPEN);
193   if (!sp)
194     return ENOMEM;
195 
196   if (flags & MU_STREAM_READ)
197     {
198       sp->stream.read = _tls_io_read;
199       sp->stream.wait = _tls_rd_wait;
200       mu_stream_set_buffer ((mu_stream_t) sp, mu_buffer_full, 0);
201     }
202   else
203     {
204       sp->stream.write = _tls_io_write;
205       sp->stream.wait = _tls_wr_wait;
206       mu_stream_set_buffer ((mu_stream_t) sp, mu_buffer_line, 0);
207     }
208   sp->stream.flush = _tls_io_flush;
209   sp->stream.close = _tls_io_close;
210   sp->stream.done = _tls_io_done;
211   sp->stream.ctl = _tls_io_ioctl;
212 
213   mu_stream_ref (transport);
214   sp->transport = transport;
215   sp->up = master;
216   *pstream = (mu_stream_t) sp;
217   return 0;
218 }
219