1 /* $OpenBSD: tls_bio_cb.c,v 1.21 2023/05/14 07:26:25 op Exp $ */ 2 /* 3 * Copyright (c) 2016 Tobias Pape <tobias@netshed.de> 4 * 5 * Permission to use, copy, modify, and distribute this software for any 6 * purpose with or without fee is hereby granted, provided that the above 7 * copyright notice and this permission notice appear in all copies. 8 * 9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 */ 17 18 #include <fcntl.h> 19 #include <stdlib.h> 20 #include <string.h> 21 #include <unistd.h> 22 23 #include <openssl/bio.h> 24 25 #include <tls.h> 26 #include "tls_internal.h" 27 28 static int bio_cb_write(BIO *bio, const char *buf, int num); 29 static int bio_cb_read(BIO *bio, char *buf, int size); 30 static int bio_cb_puts(BIO *bio, const char *str); 31 static long bio_cb_ctrl(BIO *bio, int cmd, long num, void *ptr); 32 33 static BIO_METHOD *bio_cb_method; 34 35 static pthread_mutex_t bio_cb_method_lock = PTHREAD_MUTEX_INITIALIZER; 36 37 static void 38 bio_cb_method_init(void) 39 { 40 BIO_METHOD *bio_method; 41 42 if (bio_cb_method != NULL) 43 return; 44 45 bio_method = BIO_meth_new(BIO_TYPE_MEM, "libtls_callbacks"); 46 if (bio_method == NULL) 47 return; 48 49 BIO_meth_set_write(bio_method, bio_cb_write); 50 BIO_meth_set_read(bio_method, bio_cb_read); 51 BIO_meth_set_puts(bio_method, bio_cb_puts); 52 BIO_meth_set_ctrl(bio_method, bio_cb_ctrl); 53 54 bio_cb_method = bio_method; 55 } 56 57 static BIO_METHOD * 58 bio_s_cb(void) 59 { 60 if (bio_cb_method != NULL) 61 return (bio_cb_method); 62 63 pthread_mutex_lock(&bio_cb_method_lock); 64 bio_cb_method_init(); 65 pthread_mutex_unlock(&bio_cb_method_lock); 66 67 return (bio_cb_method); 68 } 69 70 static int 71 bio_cb_puts(BIO *bio, const char *str) 72 { 73 return (bio_cb_write(bio, str, strlen(str))); 74 } 75 76 static long 77 bio_cb_ctrl(BIO *bio, int cmd, long num, void *ptr) 78 { 79 long ret = 1; 80 81 switch (cmd) { 82 case BIO_CTRL_GET_CLOSE: 83 ret = (long)BIO_get_shutdown(bio); 84 break; 85 case BIO_CTRL_SET_CLOSE: 86 BIO_set_shutdown(bio, (int)num); 87 break; 88 case BIO_CTRL_DUP: 89 case BIO_CTRL_FLUSH: 90 break; 91 case BIO_CTRL_INFO: 92 case BIO_CTRL_GET: 93 case BIO_CTRL_SET: 94 default: 95 ret = BIO_ctrl(BIO_next(bio), cmd, num, ptr); 96 } 97 98 return (ret); 99 } 100 101 static int 102 bio_cb_write(BIO *bio, const char *buf, int num) 103 { 104 struct tls *ctx = BIO_get_data(bio); 105 int rv; 106 107 BIO_clear_retry_flags(bio); 108 rv = (ctx->write_cb)(ctx, buf, num, ctx->cb_arg); 109 if (rv == TLS_WANT_POLLIN) { 110 BIO_set_retry_read(bio); 111 rv = -1; 112 } else if (rv == TLS_WANT_POLLOUT) { 113 BIO_set_retry_write(bio); 114 rv = -1; 115 } 116 return (rv); 117 } 118 119 static int 120 bio_cb_read(BIO *bio, char *buf, int size) 121 { 122 struct tls *ctx = BIO_get_data(bio); 123 int rv; 124 125 BIO_clear_retry_flags(bio); 126 rv = (ctx->read_cb)(ctx, buf, size, ctx->cb_arg); 127 if (rv == TLS_WANT_POLLIN) { 128 BIO_set_retry_read(bio); 129 rv = -1; 130 } else if (rv == TLS_WANT_POLLOUT) { 131 BIO_set_retry_write(bio); 132 rv = -1; 133 } 134 return (rv); 135 } 136 137 int 138 tls_set_cbs(struct tls *ctx, tls_read_cb read_cb, tls_write_cb write_cb, 139 void *cb_arg) 140 { 141 const BIO_METHOD *bio_cb; 142 BIO *bio; 143 int rv = -1; 144 145 if (read_cb == NULL || write_cb == NULL) { 146 tls_set_errorx(ctx, "no callbacks provided"); 147 goto err; 148 } 149 150 ctx->read_cb = read_cb; 151 ctx->write_cb = write_cb; 152 ctx->cb_arg = cb_arg; 153 154 if ((bio_cb = bio_s_cb()) == NULL) { 155 tls_set_errorx(ctx, "failed to create callback method"); 156 goto err; 157 } 158 if ((bio = BIO_new(bio_cb)) == NULL) { 159 tls_set_errorx(ctx, "failed to create callback i/o"); 160 goto err; 161 } 162 BIO_set_data(bio, ctx); 163 BIO_set_init(bio, 1); 164 165 SSL_set_bio(ctx->ssl_conn, bio, bio); 166 167 rv = 0; 168 169 err: 170 return (rv); 171 } 172