1 /* $OpenBSD: tls_bio_cb.c,v 1.20 2022/01/10 23:39:48 tb 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 <unistd.h> 21 22 #include <openssl/bio.h> 23 24 #include <tls.h> 25 #include "tls_internal.h" 26 27 static int bio_cb_write(BIO *bio, const char *buf, int num); 28 static int bio_cb_read(BIO *bio, char *buf, int size); 29 static int bio_cb_puts(BIO *bio, const char *str); 30 static long bio_cb_ctrl(BIO *bio, int cmd, long num, void *ptr); 31 32 static BIO_METHOD *bio_cb_method; 33 34 static pthread_mutex_t bio_cb_method_lock = PTHREAD_MUTEX_INITIALIZER; 35 36 static void 37 bio_cb_method_init(void) 38 { 39 BIO_METHOD *bio_method; 40 41 if (bio_cb_method != NULL) 42 return; 43 44 bio_method = BIO_meth_new(BIO_TYPE_MEM, "libtls_callbacks"); 45 if (bio_method == NULL) 46 return; 47 48 BIO_meth_set_write(bio_method, bio_cb_write); 49 BIO_meth_set_read(bio_method, bio_cb_read); 50 BIO_meth_set_puts(bio_method, bio_cb_puts); 51 BIO_meth_set_ctrl(bio_method, bio_cb_ctrl); 52 53 bio_cb_method = bio_method; 54 } 55 56 static BIO_METHOD * 57 bio_s_cb(void) 58 { 59 if (bio_cb_method != NULL) 60 return (bio_cb_method); 61 62 pthread_mutex_lock(&bio_cb_method_lock); 63 bio_cb_method_init(); 64 pthread_mutex_unlock(&bio_cb_method_lock); 65 66 return (bio_cb_method); 67 } 68 69 static int 70 bio_cb_puts(BIO *bio, const char *str) 71 { 72 return (bio_cb_write(bio, str, strlen(str))); 73 } 74 75 static long 76 bio_cb_ctrl(BIO *bio, int cmd, long num, void *ptr) 77 { 78 long ret = 1; 79 80 switch (cmd) { 81 case BIO_CTRL_GET_CLOSE: 82 ret = (long)BIO_get_shutdown(bio); 83 break; 84 case BIO_CTRL_SET_CLOSE: 85 BIO_set_shutdown(bio, (int)num); 86 break; 87 case BIO_CTRL_DUP: 88 case BIO_CTRL_FLUSH: 89 break; 90 case BIO_CTRL_INFO: 91 case BIO_CTRL_GET: 92 case BIO_CTRL_SET: 93 default: 94 ret = BIO_ctrl(BIO_next(bio), cmd, num, ptr); 95 } 96 97 return (ret); 98 } 99 100 static int 101 bio_cb_write(BIO *bio, const char *buf, int num) 102 { 103 struct tls *ctx = BIO_get_data(bio); 104 int rv; 105 106 BIO_clear_retry_flags(bio); 107 rv = (ctx->write_cb)(ctx, buf, num, ctx->cb_arg); 108 if (rv == TLS_WANT_POLLIN) { 109 BIO_set_retry_read(bio); 110 rv = -1; 111 } else if (rv == TLS_WANT_POLLOUT) { 112 BIO_set_retry_write(bio); 113 rv = -1; 114 } 115 return (rv); 116 } 117 118 static int 119 bio_cb_read(BIO *bio, char *buf, int size) 120 { 121 struct tls *ctx = BIO_get_data(bio); 122 int rv; 123 124 BIO_clear_retry_flags(bio); 125 rv = (ctx->read_cb)(ctx, buf, size, ctx->cb_arg); 126 if (rv == TLS_WANT_POLLIN) { 127 BIO_set_retry_read(bio); 128 rv = -1; 129 } else if (rv == TLS_WANT_POLLOUT) { 130 BIO_set_retry_write(bio); 131 rv = -1; 132 } 133 return (rv); 134 } 135 136 int 137 tls_set_cbs(struct tls *ctx, tls_read_cb read_cb, tls_write_cb write_cb, 138 void *cb_arg) 139 { 140 const BIO_METHOD *bio_cb; 141 BIO *bio; 142 int rv = -1; 143 144 if (read_cb == NULL || write_cb == NULL) { 145 tls_set_errorx(ctx, "no callbacks provided"); 146 goto err; 147 } 148 149 ctx->read_cb = read_cb; 150 ctx->write_cb = write_cb; 151 ctx->cb_arg = cb_arg; 152 153 if ((bio_cb = bio_s_cb()) == NULL) { 154 tls_set_errorx(ctx, "failed to create callback method"); 155 goto err; 156 } 157 if ((bio = BIO_new(bio_cb)) == NULL) { 158 tls_set_errorx(ctx, "failed to create callback i/o"); 159 goto err; 160 } 161 BIO_set_data(bio, ctx); 162 BIO_set_init(bio, 1); 163 164 SSL_set_bio(ctx->ssl_conn, bio, bio); 165 166 rv = 0; 167 168 err: 169 return (rv); 170 } 171