| /* | 
 |  * QEMU crypto TLS session support | 
 |  * | 
 |  * Copyright (c) 2015 Red Hat, Inc. | 
 |  * | 
 |  * This library is free software; you can redistribute it and/or | 
 |  * modify it under the terms of the GNU Lesser General Public | 
 |  * License as published by the Free Software Foundation; either | 
 |  * version 2.1 of the License, or (at your option) any later version. | 
 |  * | 
 |  * This library is distributed in the hope that it will be useful, | 
 |  * but WITHOUT ANY WARRANTY; without even the implied warranty of | 
 |  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU | 
 |  * Lesser General Public License for more details. | 
 |  * | 
 |  * You should have received a copy of the GNU Lesser General Public | 
 |  * License along with this library; if not, see <http://www.gnu.org/licenses/>. | 
 |  * | 
 |  */ | 
 |  | 
 | #ifndef QCRYPTO_TLSSESSION_H | 
 | #define QCRYPTO_TLSSESSION_H | 
 |  | 
 | #include "crypto/tlscreds.h" | 
 |  | 
 | /** | 
 |  * QCryptoTLSSession: | 
 |  * | 
 |  * The QCryptoTLSSession object encapsulates the | 
 |  * logic to integrate with a TLS providing library such | 
 |  * as GNUTLS, to setup and run TLS sessions. | 
 |  * | 
 |  * The API is designed such that it has no assumption about | 
 |  * the type of transport it is running over. It may be a | 
 |  * traditional TCP socket, or something else entirely. The | 
 |  * only requirement is a full-duplex stream of some kind. | 
 |  * | 
 |  * <example> | 
 |  *   <title>Using TLS session objects</title> | 
 |  *   <programlisting> | 
 |  * static ssize_t mysock_send(const char *buf, size_t len, | 
 |  *                            void *opaque) | 
 |  * { | 
 |  *    int fd = GPOINTER_TO_INT(opaque); | 
 |  * | 
 |  *    return write(*fd, buf, len); | 
 |  * } | 
 |  * | 
 |  * static ssize_t mysock_recv(const char *buf, size_t len, | 
 |  *                            void *opaque) | 
 |  * { | 
 |  *    int fd = GPOINTER_TO_INT(opaque); | 
 |  * | 
 |  *    return read(*fd, buf, len); | 
 |  * } | 
 |  * | 
 |  * static int mysock_run_tls(int sockfd, | 
 |  *                           QCryptoTLSCreds *creds, | 
 |  *                           Error **errp) | 
 |  * { | 
 |  *    QCryptoTLSSession *sess; | 
 |  * | 
 |  *    sess = qcrypto_tls_session_new(creds, | 
 |  *                                   "vnc.example.com", | 
 |  *                                   NULL, | 
 |  *                                   QCRYPTO_TLS_CREDS_ENDPOINT_CLIENT, | 
 |  *                                   errp); | 
 |  *    if (sess == NULL) { | 
 |  *       return -1; | 
 |  *    } | 
 |  * | 
 |  *    qcrypto_tls_session_set_callbacks(sess, | 
 |  *                                      mysock_send, | 
 |  *                                      mysock_recv | 
 |  *                                      GINT_TO_POINTER(fd)); | 
 |  * | 
 |  *    while (1) { | 
 |  *       if (qcrypto_tls_session_handshake(sess, errp) < 0) { | 
 |  *           qcrypto_tls_session_free(sess); | 
 |  *           return -1; | 
 |  *       } | 
 |  * | 
 |  *       switch(qcrypto_tls_session_get_handshake_status(sess)) { | 
 |  *       case QCRYPTO_TLS_HANDSHAKE_COMPLETE: | 
 |  *           if (qcrypto_tls_session_check_credentials(sess, errp) < )) { | 
 |  *               qcrypto_tls_session_free(sess); | 
 |  *               return -1; | 
 |  *           } | 
 |  *           goto done; | 
 |  *       case QCRYPTO_TLS_HANDSHAKE_RECVING: | 
 |  *           ...wait for GIO_IN event on fd... | 
 |  *           break; | 
 |  *       case QCRYPTO_TLS_HANDSHAKE_SENDING: | 
 |  *           ...wait for GIO_OUT event on fd... | 
 |  *           break; | 
 |  *       } | 
 |  *    } | 
 |  *   done: | 
 |  * | 
 |  *    ....send/recv payload data on sess... | 
 |  * | 
 |  *    qcrypto_tls_session_free(sess): | 
 |  * } | 
 |  *   </programlisting> | 
 |  * </example> | 
 |  */ | 
 |  | 
 | typedef struct QCryptoTLSSession QCryptoTLSSession; | 
 |  | 
 | #define QCRYPTO_TLS_SESSION_ERR_BLOCK -2 | 
 |  | 
 | /** | 
 |  * qcrypto_tls_session_new: | 
 |  * @creds: pointer to a TLS credentials object | 
 |  * @hostname: optional hostname to validate | 
 |  * @aclname: optional ACL to validate peer credentials against | 
 |  * @endpoint: role of the TLS session, client or server | 
 |  * @errp: pointer to a NULL-initialized error object | 
 |  * | 
 |  * Create a new TLS session object that will be used to | 
 |  * negotiate a TLS session over an arbitrary data channel. | 
 |  * The session object can operate as either the server or | 
 |  * client, according to the value of the @endpoint argument. | 
 |  * | 
 |  * For clients, the @hostname parameter should hold the full | 
 |  * unmodified hostname as requested by the user. This will | 
 |  * be used to verify the against the hostname reported in | 
 |  * the server's credentials (aka x509 certificate). | 
 |  * | 
 |  * The @aclname parameter (optionally) specifies the name | 
 |  * of an access control list that will be used to validate | 
 |  * the peer's credentials. For x509 credentials, the ACL | 
 |  * will be matched against the CommonName shown in the peer's | 
 |  * certificate. If the session is acting as a server, setting | 
 |  * an ACL will require that the client provide a validate | 
 |  * x509 client certificate. | 
 |  * | 
 |  * After creating the session object, the I/O callbacks | 
 |  * must be set using the qcrypto_tls_session_set_callbacks() | 
 |  * method. A TLS handshake sequence must then be completed | 
 |  * using qcrypto_tls_session_handshake(), before payload | 
 |  * data is permitted to be sent/received. | 
 |  * | 
 |  * The session object must be released by calling | 
 |  * qcrypto_tls_session_free() when no longer required | 
 |  * | 
 |  * Returns: a TLS session object, or NULL on error. | 
 |  */ | 
 | QCryptoTLSSession *qcrypto_tls_session_new(QCryptoTLSCreds *creds, | 
 |                                            const char *hostname, | 
 |                                            const char *aclname, | 
 |                                            QCryptoTLSCredsEndpoint endpoint, | 
 |                                            Error **errp); | 
 |  | 
 | /** | 
 |  * qcrypto_tls_session_free: | 
 |  * @sess: the TLS session object | 
 |  * | 
 |  * Release all memory associated with the TLS session | 
 |  * object previously allocated by qcrypto_tls_session_new() | 
 |  */ | 
 | void qcrypto_tls_session_free(QCryptoTLSSession *sess); | 
 |  | 
 | G_DEFINE_AUTOPTR_CLEANUP_FUNC(QCryptoTLSSession, qcrypto_tls_session_free) | 
 |  | 
 | /** | 
 |  * qcrypto_tls_session_check_credentials: | 
 |  * @sess: the TLS session object | 
 |  * @errp: pointer to a NULL-initialized error object | 
 |  * | 
 |  * Validate the peer's credentials after a successful | 
 |  * TLS handshake. It is an error to call this before | 
 |  * qcrypto_tls_session_get_handshake_status() returns | 
 |  * QCRYPTO_TLS_HANDSHAKE_COMPLETE | 
 |  * | 
 |  * Returns 0 if the credentials validated, -1 on error | 
 |  */ | 
 | int qcrypto_tls_session_check_credentials(QCryptoTLSSession *sess, | 
 |                                           Error **errp); | 
 |  | 
 | /* | 
 |  * These must return QCRYPTO_TLS_SESSION_ERR_BLOCK if the I/O | 
 |  * would block, but on other errors, must fill 'errp' | 
 |  */ | 
 | typedef ssize_t (*QCryptoTLSSessionWriteFunc)(const char *buf, | 
 |                                               size_t len, | 
 |                                               void *opaque, | 
 |                                               Error **errp); | 
 | typedef ssize_t (*QCryptoTLSSessionReadFunc)(char *buf, | 
 |                                              size_t len, | 
 |                                              void *opaque, | 
 |                                              Error **errp); | 
 |  | 
 | /** | 
 |  * qcrypto_tls_session_set_callbacks: | 
 |  * @sess: the TLS session object | 
 |  * @writeFunc: callback for sending data | 
 |  * @readFunc: callback to receiving data | 
 |  * @opaque: data to pass to callbacks | 
 |  * | 
 |  * Sets the callback functions that are to be used for sending | 
 |  * and receiving data on the underlying data channel. Typically | 
 |  * the callbacks to write/read to/from a TCP socket, but there | 
 |  * is no assumption made about the type of channel used. | 
 |  * | 
 |  * The @writeFunc callback will be passed the encrypted | 
 |  * data to send to the remote peer. | 
 |  * | 
 |  * The @readFunc callback will be passed a pointer to fill | 
 |  * with encrypted data received from the remote peer | 
 |  */ | 
 | void qcrypto_tls_session_set_callbacks(QCryptoTLSSession *sess, | 
 |                                        QCryptoTLSSessionWriteFunc writeFunc, | 
 |                                        QCryptoTLSSessionReadFunc readFunc, | 
 |                                        void *opaque); | 
 |  | 
 | /** | 
 |  * qcrypto_tls_session_write: | 
 |  * @sess: the TLS session object | 
 |  * @buf: the plain text to send | 
 |  * @len: the length of @buf | 
 |  * @errp: pointer to hold returned error object | 
 |  * | 
 |  * Encrypt @len bytes of the data in @buf and send | 
 |  * it to the remote peer using the callback previously | 
 |  * registered with qcrypto_tls_session_set_callbacks() | 
 |  * | 
 |  * It is an error to call this before | 
 |  * qcrypto_tls_session_get_handshake_status() returns | 
 |  * QCRYPTO_TLS_HANDSHAKE_COMPLETE | 
 |  * | 
 |  * Returns: the number of bytes sent, | 
 |  * or QCRYPTO_TLS_SESSION_ERR_BLOCK if the write would block, | 
 |  * or -1 on error. | 
 |  */ | 
 | ssize_t qcrypto_tls_session_write(QCryptoTLSSession *sess, | 
 |                                   const char *buf, | 
 |                                   size_t len, | 
 |                                   Error **errp); | 
 |  | 
 | /** | 
 |  * qcrypto_tls_session_read: | 
 |  * @sess: the TLS session object | 
 |  * @buf: to fill with plain text received | 
 |  * @len: the length of @buf | 
 |  * @gracefulTermination: treat premature termination as graceful EOF | 
 |  * @errp: pointer to hold returned error object | 
 |  * | 
 |  * Receive up to @len bytes of data from the remote peer | 
 |  * using the callback previously registered with | 
 |  * qcrypto_tls_session_set_callbacks(), decrypt it and | 
 |  * store it in @buf. | 
 |  * | 
 |  * If @gracefulTermination is true, then a premature termination | 
 |  * of the TLS session will be treated as indicating EOF, as | 
 |  * opposed to an error. | 
 |  * | 
 |  * It is an error to call this before | 
 |  * qcrypto_tls_session_get_handshake_status() returns | 
 |  * QCRYPTO_TLS_HANDSHAKE_COMPLETE | 
 |  * | 
 |  * Returns: the number of bytes received, | 
 |  * or QCRYPTO_TLS_SESSION_ERR_BLOCK if the receive would block, | 
 |  * or -1 on error. | 
 |  */ | 
 | ssize_t qcrypto_tls_session_read(QCryptoTLSSession *sess, | 
 |                                  char *buf, | 
 |                                  size_t len, | 
 |                                  bool gracefulTermination, | 
 |                                  Error **errp); | 
 |  | 
 | /** | 
 |  * qcrypto_tls_session_check_pending: | 
 |  * @sess: the TLS session object | 
 |  * | 
 |  * Check if there are unread data in the TLS buffers that have | 
 |  * already been read from the underlying data source. | 
 |  * | 
 |  * Returns: the number of bytes available or zero | 
 |  */ | 
 | size_t qcrypto_tls_session_check_pending(QCryptoTLSSession *sess); | 
 |  | 
 | /** | 
 |  * qcrypto_tls_session_handshake: | 
 |  * @sess: the TLS session object | 
 |  * @errp: pointer to a NULL-initialized error object | 
 |  * | 
 |  * Start, or continue, a TLS handshake sequence. If | 
 |  * the underlying data channel is non-blocking, then | 
 |  * this method may return control before the handshake | 
 |  * is complete. On non-blocking channels the | 
 |  * qcrypto_tls_session_get_handshake_status() method | 
 |  * should be used to determine whether the handshake | 
 |  * has completed, or is waiting to send or receive | 
 |  * data. In the latter cases, the caller should setup | 
 |  * an event loop watch and call this method again | 
 |  * once the underlying data channel is ready to read | 
 |  * or write again | 
 |  */ | 
 | int qcrypto_tls_session_handshake(QCryptoTLSSession *sess, | 
 |                                   Error **errp); | 
 |  | 
 | typedef enum { | 
 |     QCRYPTO_TLS_HANDSHAKE_COMPLETE, | 
 |     QCRYPTO_TLS_HANDSHAKE_SENDING, | 
 |     QCRYPTO_TLS_HANDSHAKE_RECVING, | 
 | } QCryptoTLSSessionHandshakeStatus; | 
 |  | 
 | /** | 
 |  * qcrypto_tls_session_get_handshake_status: | 
 |  * @sess: the TLS session object | 
 |  * | 
 |  * Check the status of the TLS handshake. This | 
 |  * is used with non-blocking data channels to | 
 |  * determine whether the handshake is waiting | 
 |  * to send or receive further data to/from the | 
 |  * remote peer. | 
 |  * | 
 |  * Once this returns QCRYPTO_TLS_HANDSHAKE_COMPLETE | 
 |  * it is permitted to send/receive payload data on | 
 |  * the channel | 
 |  */ | 
 | QCryptoTLSSessionHandshakeStatus | 
 | qcrypto_tls_session_get_handshake_status(QCryptoTLSSession *sess); | 
 |  | 
 | /** | 
 |  * qcrypto_tls_session_get_key_size: | 
 |  * @sess: the TLS session object | 
 |  * @errp: pointer to a NULL-initialized error object | 
 |  * | 
 |  * Check the size of the data channel encryption key | 
 |  * | 
 |  * Returns: the length in bytes of the encryption key | 
 |  * or -1 on error | 
 |  */ | 
 | int qcrypto_tls_session_get_key_size(QCryptoTLSSession *sess, | 
 |                                      Error **errp); | 
 |  | 
 | /** | 
 |  * qcrypto_tls_session_get_peer_name: | 
 |  * @sess: the TLS session object | 
 |  * | 
 |  * Get the identified name of the remote peer. If the | 
 |  * TLS session was negotiated using x509 certificate | 
 |  * credentials, this will return the CommonName from | 
 |  * the peer's certificate. If no identified name is | 
 |  * available it will return NULL. | 
 |  * | 
 |  * The returned data must be released with g_free() | 
 |  * when no longer required. | 
 |  * | 
 |  * Returns: the peer's name or NULL. | 
 |  */ | 
 | char *qcrypto_tls_session_get_peer_name(QCryptoTLSSession *sess); | 
 |  | 
 | #endif /* QCRYPTO_TLSSESSION_H */ |