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