1 | /* |
2 | * QEMU crypto TLS session support |
3 | * |
4 | * Copyright (c) 2015 Red Hat, Inc. |
5 | * |
6 | * This library is free software; you can redistribute it and/or |
7 | * modify it under the terms of the GNU Lesser General Public |
8 | * License as published by the Free Software Foundation; either |
9 | * version 2.1 of the License, or (at your option) any later version. |
10 | * |
11 | * This library is distributed in the hope that it will be useful, |
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
14 | * Lesser General Public License for more details. |
15 | * |
16 | * You should have received a copy of the GNU Lesser General Public |
17 | * License along with this library; if not, see <http://www.gnu.org/licenses/>. |
18 | * |
19 | */ |
20 | |
21 | #ifndef QCRYPTO_TLSSESSION_H |
22 | #define QCRYPTO_TLSSESSION_H |
23 | |
24 | #include "crypto/tlscreds.h" |
25 | |
26 | /** |
27 | * QCryptoTLSSession: |
28 | * |
29 | * The QCryptoTLSSession object encapsulates the |
30 | * logic to integrate with a TLS providing library such |
31 | * as GNUTLS, to setup and run TLS sessions. |
32 | * |
33 | * The API is designed such that it has no assumption about |
34 | * the type of transport it is running over. It may be a |
35 | * traditional TCP socket, or something else entirely. The |
36 | * only requirement is a full-duplex stream of some kind. |
37 | * |
38 | * <example> |
39 | * <title>Using TLS session objects</title> |
40 | * <programlisting> |
41 | * static ssize_t mysock_send(const char *buf, size_t len, |
42 | * void *opaque) |
43 | * { |
44 | * int fd = GPOINTER_TO_INT(opaque); |
45 | * |
46 | * return write(*fd, buf, len); |
47 | * } |
48 | * |
49 | * static ssize_t mysock_recv(const char *buf, size_t len, |
50 | * void *opaque) |
51 | * { |
52 | * int fd = GPOINTER_TO_INT(opaque); |
53 | * |
54 | * return read(*fd, buf, len); |
55 | * } |
56 | * |
57 | * static int mysock_run_tls(int sockfd, |
58 | * QCryptoTLSCreds *creds, |
59 | * Error *errp) |
60 | * { |
61 | * QCryptoTLSSession *sess; |
62 | * |
63 | * sess = qcrypto_tls_session_new(creds, |
64 | * "vnc.example.com", |
65 | * NULL, |
66 | * QCRYPTO_TLS_CREDS_ENDPOINT_CLIENT, |
67 | * errp); |
68 | * if (sess == NULL) { |
69 | * return -1; |
70 | * } |
71 | * |
72 | * qcrypto_tls_session_set_callbacks(sess, |
73 | * mysock_send, |
74 | * mysock_recv |
75 | * GINT_TO_POINTER(fd)); |
76 | * |
77 | * while (1) { |
78 | * if (qcrypto_tls_session_handshake(sess, errp) < 0) { |
79 | * qcrypto_tls_session_free(sess); |
80 | * return -1; |
81 | * } |
82 | * |
83 | * switch(qcrypto_tls_session_get_handshake_status(sess)) { |
84 | * case QCRYPTO_TLS_HANDSHAKE_COMPLETE: |
85 | * if (qcrypto_tls_session_check_credentials(sess, errp) < )) { |
86 | * qcrypto_tls_session_free(sess); |
87 | * return -1; |
88 | * } |
89 | * goto done; |
90 | * case QCRYPTO_TLS_HANDSHAKE_RECVING: |
91 | * ...wait for GIO_IN event on fd... |
92 | * break; |
93 | * case QCRYPTO_TLS_HANDSHAKE_SENDING: |
94 | * ...wait for GIO_OUT event on fd... |
95 | * break; |
96 | * } |
97 | * } |
98 | * done: |
99 | * |
100 | * ....send/recv payload data on sess... |
101 | * |
102 | * qcrypto_tls_session_free(sess): |
103 | * } |
104 | * </programlisting> |
105 | * </example> |
106 | */ |
107 | |
108 | typedef struct QCryptoTLSSession QCryptoTLSSession; |
109 | |
110 | |
111 | /** |
112 | * qcrypto_tls_session_new: |
113 | * @creds: pointer to a TLS credentials object |
114 | * @hostname: optional hostname to validate |
115 | * @aclname: optional ACL to validate peer credentials against |
116 | * @endpoint: role of the TLS session, client or server |
117 | * @errp: pointer to a NULL-initialized error object |
118 | * |
119 | * Create a new TLS session object that will be used to |
120 | * negotiate a TLS session over an arbitrary data channel. |
121 | * The session object can operate as either the server or |
122 | * client, according to the value of the @endpoint argument. |
123 | * |
124 | * For clients, the @hostname parameter should hold the full |
125 | * unmodified hostname as requested by the user. This will |
126 | * be used to verify the against the hostname reported in |
127 | * the server's credentials (aka x509 certificate). |
128 | * |
129 | * The @aclname parameter (optionally) specifies the name |
130 | * of an access control list that will be used to validate |
131 | * the peer's credentials. For x509 credentials, the ACL |
132 | * will be matched against the CommonName shown in the peer's |
133 | * certificate. If the session is acting as a server, setting |
134 | * an ACL will require that the client provide a validate |
135 | * x509 client certificate. |
136 | * |
137 | * After creating the session object, the I/O callbacks |
138 | * must be set using the qcrypto_tls_session_set_callbacks() |
139 | * method. A TLS handshake sequence must then be completed |
140 | * using qcrypto_tls_session_handshake(), before payload |
141 | * data is permitted to be sent/received. |
142 | * |
143 | * The session object must be released by calling |
144 | * qcrypto_tls_session_free() when no longer required |
145 | * |
146 | * Returns: a TLS session object, or NULL on error. |
147 | */ |
148 | QCryptoTLSSession *qcrypto_tls_session_new(QCryptoTLSCreds *creds, |
149 | const char *hostname, |
150 | const char *aclname, |
151 | QCryptoTLSCredsEndpoint endpoint, |
152 | Error **errp); |
153 | |
154 | /** |
155 | * qcrypto_tls_session_free: |
156 | * @sess: the TLS session object |
157 | * |
158 | * Release all memory associated with the TLS session |
159 | * object previously allocated by qcrypto_tls_session_new() |
160 | */ |
161 | void qcrypto_tls_session_free(QCryptoTLSSession *sess); |
162 | |
163 | G_DEFINE_AUTOPTR_CLEANUP_FUNC(QCryptoTLSSession, qcrypto_tls_session_free) |
164 | |
165 | /** |
166 | * qcrypto_tls_session_check_credentials: |
167 | * @sess: the TLS session object |
168 | * @errp: pointer to a NULL-initialized error object |
169 | * |
170 | * Validate the peer's credentials after a successful |
171 | * TLS handshake. It is an error to call this before |
172 | * qcrypto_tls_session_get_handshake_status() returns |
173 | * QCRYPTO_TLS_HANDSHAKE_COMPLETE |
174 | * |
175 | * Returns 0 if the credentials validated, -1 on error |
176 | */ |
177 | int qcrypto_tls_session_check_credentials(QCryptoTLSSession *sess, |
178 | Error **errp); |
179 | |
180 | typedef ssize_t (*QCryptoTLSSessionWriteFunc)(const char *buf, |
181 | size_t len, |
182 | void *opaque); |
183 | typedef ssize_t (*QCryptoTLSSessionReadFunc)(char *buf, |
184 | size_t len, |
185 | void *opaque); |
186 | |
187 | /** |
188 | * qcrypto_tls_session_set_callbacks: |
189 | * @sess: the TLS session object |
190 | * @writeFunc: callback for sending data |
191 | * @readFunc: callback to receiving data |
192 | * @opaque: data to pass to callbacks |
193 | * |
194 | * Sets the callback functions that are to be used for sending |
195 | * and receiving data on the underlying data channel. Typically |
196 | * the callbacks to write/read to/from a TCP socket, but there |
197 | * is no assumption made about the type of channel used. |
198 | * |
199 | * The @writeFunc callback will be passed the encrypted |
200 | * data to send to the remote peer. |
201 | * |
202 | * The @readFunc callback will be passed a pointer to fill |
203 | * with encrypted data received from the remote peer |
204 | */ |
205 | void qcrypto_tls_session_set_callbacks(QCryptoTLSSession *sess, |
206 | QCryptoTLSSessionWriteFunc writeFunc, |
207 | QCryptoTLSSessionReadFunc readFunc, |
208 | void *opaque); |
209 | |
210 | /** |
211 | * qcrypto_tls_session_write: |
212 | * @sess: the TLS session object |
213 | * @buf: the plain text to send |
214 | * @len: the length of @buf |
215 | * |
216 | * Encrypt @len bytes of the data in @buf and send |
217 | * it to the remote peer using the callback previously |
218 | * registered with qcrypto_tls_session_set_callbacks() |
219 | * |
220 | * It is an error to call this before |
221 | * qcrypto_tls_session_get_handshake_status() returns |
222 | * QCRYPTO_TLS_HANDSHAKE_COMPLETE |
223 | * |
224 | * Returns: the number of bytes sent, or -1 on error |
225 | */ |
226 | ssize_t qcrypto_tls_session_write(QCryptoTLSSession *sess, |
227 | const char *buf, |
228 | size_t len); |
229 | |
230 | /** |
231 | * qcrypto_tls_session_read: |
232 | * @sess: the TLS session object |
233 | * @buf: to fill with plain text received |
234 | * @len: the length of @buf |
235 | * |
236 | * Receive up to @len bytes of data from the remote peer |
237 | * using the callback previously registered with |
238 | * qcrypto_tls_session_set_callbacks(), decrypt it and |
239 | * store it in @buf. |
240 | * |
241 | * It is an error to call this before |
242 | * qcrypto_tls_session_get_handshake_status() returns |
243 | * QCRYPTO_TLS_HANDSHAKE_COMPLETE |
244 | * |
245 | * Returns: the number of bytes received, or -1 on error |
246 | */ |
247 | ssize_t qcrypto_tls_session_read(QCryptoTLSSession *sess, |
248 | char *buf, |
249 | size_t len); |
250 | |
251 | /** |
252 | * qcrypto_tls_session_handshake: |
253 | * @sess: the TLS session object |
254 | * @errp: pointer to a NULL-initialized error object |
255 | * |
256 | * Start, or continue, a TLS handshake sequence. If |
257 | * the underlying data channel is non-blocking, then |
258 | * this method may return control before the handshake |
259 | * is complete. On non-blocking channels the |
260 | * qcrypto_tls_session_get_handshake_status() method |
261 | * should be used to determine whether the handshake |
262 | * has completed, or is waiting to send or receive |
263 | * data. In the latter cases, the caller should setup |
264 | * an event loop watch and call this method again |
265 | * once the underlying data channel is ready to read |
266 | * or write again |
267 | */ |
268 | int qcrypto_tls_session_handshake(QCryptoTLSSession *sess, |
269 | Error **errp); |
270 | |
271 | typedef enum { |
272 | QCRYPTO_TLS_HANDSHAKE_COMPLETE, |
273 | QCRYPTO_TLS_HANDSHAKE_SENDING, |
274 | QCRYPTO_TLS_HANDSHAKE_RECVING, |
275 | } QCryptoTLSSessionHandshakeStatus; |
276 | |
277 | /** |
278 | * qcrypto_tls_session_get_handshake_status: |
279 | * @sess: the TLS session object |
280 | * |
281 | * Check the status of the TLS handshake. This |
282 | * is used with non-blocking data channels to |
283 | * determine whether the handshake is waiting |
284 | * to send or receive further data to/from the |
285 | * remote peer. |
286 | * |
287 | * Once this returns QCRYPTO_TLS_HANDSHAKE_COMPLETE |
288 | * it is permitted to send/receive payload data on |
289 | * the channel |
290 | */ |
291 | QCryptoTLSSessionHandshakeStatus |
292 | qcrypto_tls_session_get_handshake_status(QCryptoTLSSession *sess); |
293 | |
294 | /** |
295 | * qcrypto_tls_session_get_key_size: |
296 | * @sess: the TLS session object |
297 | * @errp: pointer to a NULL-initialized error object |
298 | * |
299 | * Check the size of the data channel encryption key |
300 | * |
301 | * Returns: the length in bytes of the encryption key |
302 | * or -1 on error |
303 | */ |
304 | int qcrypto_tls_session_get_key_size(QCryptoTLSSession *sess, |
305 | Error **errp); |
306 | |
307 | /** |
308 | * qcrypto_tls_session_get_peer_name: |
309 | * @sess: the TLS session object |
310 | * |
311 | * Get the identified name of the remote peer. If the |
312 | * TLS session was negotiated using x509 certificate |
313 | * credentials, this will return the CommonName from |
314 | * the peer's certificate. If no identified name is |
315 | * available it will return NULL. |
316 | * |
317 | * The returned data must be released with g_free() |
318 | * when no longer required. |
319 | * |
320 | * Returns: the peer's name or NULL. |
321 | */ |
322 | char *qcrypto_tls_session_get_peer_name(QCryptoTLSSession *sess); |
323 | |
324 | #endif /* QCRYPTO_TLSSESSION_H */ |
325 | |