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