xref: /aosp_15_r20/external/libbrillo/brillo/streams/openssl_stream_bio.cc (revision 1a96fba65179ea7d3f56207137718607415c5953)
1 // Copyright 2015 The Chromium OS Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include <brillo/streams/openssl_stream_bio.h>
6 
7 #include <openssl/bio.h>
8 
9 #include <base/numerics/safe_conversions.h>
10 #include <brillo/streams/stream.h>
11 
12 namespace brillo {
13 
14 namespace {
15 
16 // TODO(crbug.com/984789): Remove once support for OpenSSL <1.1 is dropped.
17 #if OPENSSL_VERSION_NUMBER < 0x10100000L
BIO_set_data(BIO * a,void * ptr)18 static void BIO_set_data(BIO* a, void* ptr) {
19   a->ptr = ptr;
20 }
21 
BIO_get_data(BIO * a)22 static void* BIO_get_data(BIO* a) {
23   return a->ptr;
24 }
25 
BIO_set_init(BIO * a,int init)26 static void BIO_set_init(BIO* a, int init) {
27   a->init = init;
28 }
29 
BIO_get_init(BIO * a)30 static int BIO_get_init(BIO* a) {
31   return a->init;
32 }
33 
BIO_set_shutdown(BIO * a,int shut)34 static void BIO_set_shutdown(BIO* a, int shut) {
35   a->shutdown = shut;
36 }
37 #endif
38 
39 // Internal functions for implementing OpenSSL BIO on brillo::Stream.
stream_write(BIO * bio,const char * buf,int size)40 int stream_write(BIO* bio, const char* buf, int size) {
41   brillo::Stream* stream = static_cast<brillo::Stream*>(BIO_get_data(bio));
42   size_t written = 0;
43   BIO_clear_retry_flags(bio);
44   if (!stream->WriteNonBlocking(buf, size, &written, nullptr))
45     return -1;
46 
47   if (written == 0) {
48     // Socket's output buffer is full, try again later.
49     BIO_set_retry_write(bio);
50     return -1;
51   }
52   return base::checked_cast<int>(written);
53 }
54 
stream_read(BIO * bio,char * buf,int size)55 int stream_read(BIO* bio, char* buf, int size) {
56   brillo::Stream* stream = static_cast<brillo::Stream*>(BIO_get_data(bio));
57   size_t read = 0;
58   BIO_clear_retry_flags(bio);
59   bool eos = false;
60   if (!stream->ReadNonBlocking(buf, size, &read, &eos, nullptr))
61     return -1;
62 
63   if (read == 0 && !eos) {
64     // If no data is available on the socket and it is still not closed,
65     // ask OpenSSL to try again later.
66     BIO_set_retry_read(bio);
67     return -1;
68   }
69   return base::checked_cast<int>(read);
70 }
71 
72 // NOLINTNEXTLINE(runtime/int)
stream_ctrl(BIO * bio,int cmd,long,void *)73 long stream_ctrl(BIO* bio, int cmd, long /* num */, void* /* ptr */) {
74   if (cmd == BIO_CTRL_FLUSH) {
75     brillo::Stream* stream = static_cast<brillo::Stream*>(BIO_get_data(bio));
76     return stream->FlushBlocking(nullptr) ? 1 : 0;
77   }
78   return 0;
79 }
80 
stream_new(BIO * bio)81 int stream_new(BIO* bio) {
82   // By default do not close underlying stream on shutdown.
83   BIO_set_shutdown(bio, 0);
84   BIO_set_init(bio, 0);
85   return 1;
86 }
87 
stream_free(BIO * bio)88 int stream_free(BIO* bio) {
89   if (!bio)
90     return 0;
91 
92   if (BIO_get_init(bio)) {
93     BIO_set_data(bio, nullptr);
94     BIO_set_init(bio, 0);
95   }
96   return 1;
97 }
98 
99 #if OPENSSL_VERSION_NUMBER < 0x10100000L
100 // TODO(crbug.com/984789): Remove #ifdef once support for OpenSSL <1.1 is
101 // dropped.
102 
103 // BIO_METHOD structure describing the BIO built on top of brillo::Stream.
104 BIO_METHOD stream_method = {
105     0x7F | BIO_TYPE_SOURCE_SINK,  // type: 0x7F is an arbitrary unused type ID.
106     "stream",      // name
107     stream_write,  // write function
108     stream_read,   // read function
109     nullptr,       // puts function, not implemented
110     nullptr,       // gets function, not implemented
111     stream_ctrl,   // control function
112     stream_new,    // creation
113     stream_free,   // free
114     nullptr,       // callback function, not used
115 };
116 
stream_get_method()117 BIO_METHOD* stream_get_method() {
118   return &stream_method;
119 }
120 
121 #else
122 
stream_get_method()123 BIO_METHOD* stream_get_method() {
124   static BIO_METHOD* stream_method;
125 
126   if (!stream_method) {
127     stream_method = BIO_meth_new(BIO_get_new_index() | BIO_TYPE_SOURCE_SINK,
128                                  "stream");
129     BIO_meth_set_write(stream_method, stream_write);
130     BIO_meth_set_read(stream_method, stream_read);
131     BIO_meth_set_ctrl(stream_method, stream_ctrl);
132     BIO_meth_set_create(stream_method, stream_new);
133     BIO_meth_set_destroy(stream_method, stream_free);
134   }
135 
136   return stream_method;
137 }
138 
139 #endif
140 
141 }  // anonymous namespace
142 
BIO_new_stream(brillo::Stream * stream)143 BIO* BIO_new_stream(brillo::Stream* stream) {
144   BIO* bio = BIO_new(stream_get_method());
145   if (bio) {
146     BIO_set_data(bio, stream);
147     BIO_set_init(bio, 1);
148   }
149   return bio;
150 }
151 
152 }  // namespace brillo
153