Line data Source code
1 : /*
2 : * Distributed under the OpenDDS License.
3 : * See: http://www.OpenDDS.org/license.html
4 : */
5 :
6 : #include "SignedDocument.h"
7 :
8 : #include "Err.h"
9 :
10 : #include <dds/DCPS/security/CommonUtilities.h>
11 :
12 : #include <dds/DCPS/Definitions.h>
13 :
14 : #include <openssl/pem.h>
15 : #include <openssl/x509.h>
16 :
17 : #include <cstring>
18 : #include <sstream>
19 : #include <iterator>
20 : #include <algorithm>
21 : #include <fstream>
22 :
23 : OPENDDS_BEGIN_VERSIONED_NAMESPACE_DECL
24 :
25 : namespace OpenDDS {
26 : namespace Security {
27 : namespace SSL {
28 :
29 : namespace {
30 : const char* const default_filename = "<no filename: not loaded>";
31 : const char* const data_filename = "<no filename: data uri>";
32 : }
33 :
34 52 : SignedDocument::SignedDocument()
35 52 : : original_()
36 52 : , content_()
37 52 : , verified_(false)
38 104 : , filename_(default_filename)
39 : {
40 52 : }
41 :
42 11 : SignedDocument::SignedDocument(const DDS::OctetSeq& src)
43 11 : : original_(src)
44 11 : , content_()
45 11 : , verified_(false)
46 22 : , filename_(data_filename)
47 : {
48 11 : }
49 :
50 65 : SignedDocument::~SignedDocument()
51 : {
52 65 : }
53 :
54 51 : bool SignedDocument::load(const std::string& uri, DDS::Security::SecurityException& ex)
55 : {
56 : using namespace CommonUtilities;
57 :
58 51 : original_.length(0);
59 51 : content_.clear();
60 51 : verified_ = false;
61 51 : filename_ = default_filename;
62 :
63 51 : URI uri_info(uri);
64 :
65 51 : switch (uri_info.scheme) {
66 51 : case URI::URI_FILE: {
67 51 : load_file(uri_info.everything_else);
68 51 : break;
69 : }
70 0 : case URI::URI_DATA:
71 0 : original_.length(static_cast<unsigned int>(uri_info.everything_else.length() + 1));
72 0 : std::memcpy(original_.get_buffer(), uri_info.everything_else.c_str(), uri_info.everything_else.length() + 1);
73 0 : filename_ = data_filename;
74 0 : break;
75 :
76 0 : case URI::URI_PKCS11:
77 : case URI::URI_UNKNOWN:
78 : default:
79 0 : ACE_ERROR((LM_WARNING,
80 : "(%P|%t) SSL::SignedDocument::load: WARNING: Unsupported URI scheme\n"));
81 0 : break;
82 : }
83 :
84 51 : if (original_.length() == 0) {
85 0 : std::stringstream msg;
86 : msg << "SSL::SignedDocument::load: WARNING: Failed to load document supplied "
87 0 : "with URI '" << uri << "'";
88 0 : set_security_error(ex, -1, 0, msg.str().c_str());
89 0 : return false;
90 0 : }
91 :
92 51 : return true;
93 51 : }
94 :
95 : class StackOfX509 {
96 : public:
97 52 : StackOfX509()
98 52 : : certs_(sk_X509_new_null())
99 52 : {}
100 :
101 52 : ~StackOfX509()
102 : {
103 52 : if (certs_) {
104 52 : sk_X509_free(certs_);
105 : }
106 52 : }
107 :
108 52 : STACK_OF(X509)* certs() const { return certs_; }
109 52 : operator bool() const {return certs_;}
110 :
111 52 : bool push(const Certificate& certificate)
112 : {
113 52 : if (sk_X509_push(certs_, certificate.x509()) != 1) {
114 0 : OPENDDS_SSL_LOG_ERR("sk_X509_push failed");
115 0 : return false;
116 : }
117 :
118 52 : return true;
119 : }
120 :
121 : private:
122 : // No copy.
123 : StackOfX509(const StackOfX509&);
124 : STACK_OF(X509)* certs_;
125 : };
126 :
127 : class X509Store {
128 : public:
129 : X509Store()
130 : : store_(X509_STORE_new())
131 : {
132 : if (!store_) {
133 : OPENDDS_SSL_LOG_ERR("X509_STORE_new failed");
134 : }
135 : }
136 :
137 : ~X509Store()
138 : {
139 : if (store_) {
140 : X509_STORE_free(store_);
141 : }
142 : }
143 :
144 0 : X509_STORE* store() const { return store_; }
145 : operator bool() const {return store_;}
146 :
147 : bool add_cert(const Certificate& certificate)
148 : {
149 : if (X509_STORE_add_cert(store_, certificate.x509()) != 1) {
150 : OPENDDS_SSL_LOG_ERR("X509_STORE_add_cert failed");
151 : return false;
152 : }
153 :
154 : return true;
155 : }
156 :
157 : private:
158 : // No copy.
159 : X509Store(const X509Store&);
160 : X509_STORE* store_;
161 : };
162 :
163 : class Bio {
164 : public:
165 156 : Bio()
166 156 : : bio_(0)
167 156 : {}
168 :
169 104 : bool new_mem()
170 : {
171 104 : OPENDDS_ASSERT(!bio_);
172 104 : bio_ = BIO_new(BIO_s_mem());
173 104 : if (!bio_) {
174 0 : OPENDDS_SSL_LOG_ERR("BIO_new failed");
175 0 : return false;
176 : }
177 :
178 104 : return true;
179 : }
180 :
181 156 : ~Bio()
182 : {
183 156 : if (bio_) {
184 156 : BIO_free(bio_);
185 : }
186 156 : }
187 :
188 104 : BIO* bio() const { return bio_; }
189 104 : BIO*& bio() { return bio_; }
190 : operator bool() const {return bio_;}
191 :
192 52 : bool write(const void* data, int dlen)
193 : {
194 52 : if (BIO_write(bio_, data, dlen) != dlen) {
195 0 : OPENDDS_SSL_LOG_ERR("BIO_write failed");
196 0 : return false;
197 : }
198 :
199 52 : return true;
200 : }
201 :
202 52 : long get_mem_data(char **pp)
203 : {
204 52 : const long size = BIO_get_mem_data(bio_, pp);
205 52 : if (size < 0) {
206 0 : OPENDDS_SSL_LOG_ERR("BIO_get_mem_data failed");
207 : }
208 :
209 52 : return size;
210 : }
211 :
212 : private:
213 : // No copy.
214 : Bio(const Bio&);
215 : BIO* bio_;
216 : };
217 :
218 : class PKCS7Doc {
219 : public:
220 52 : PKCS7Doc(PKCS7* doc)
221 52 : : doc_(doc)
222 52 : {}
223 :
224 52 : ~PKCS7Doc()
225 : {
226 52 : if (doc_) {
227 52 : PKCS7_free(doc_);
228 : }
229 52 : }
230 :
231 52 : operator bool() const { return doc_; }
232 :
233 52 : bool verify(const StackOfX509* certs,
234 : const X509Store* store,
235 : const Bio& indata,
236 : const Bio& outdata,
237 : int flags)
238 : {
239 52 : if (PKCS7_verify(doc_, certs ? certs->certs() : 0, store ? store->store() : 0,
240 52 : indata.bio(), outdata.bio(), flags) != 1) {
241 0 : OPENDDS_SSL_LOG_ERR("SMIME_read_PKCS7 failed");
242 0 : return false;
243 : }
244 :
245 52 : return true;
246 : }
247 :
248 : private:
249 : // No copy.
250 : PKCS7Doc(const PKCS7Doc&);
251 : PKCS7* doc_;
252 : };
253 :
254 52 : bool SignedDocument::verify(const Certificate& ca)
255 : {
256 52 : content_.clear();
257 52 : verified_ = false;
258 :
259 52 : StackOfX509 certs;
260 52 : if (!certs) {
261 0 : return false;
262 : }
263 :
264 52 : if (!certs.push(ca)) {
265 0 : return false;
266 : }
267 :
268 52 : Bio filebuf;
269 52 : if (!filebuf.new_mem()) {
270 0 : return false;
271 : }
272 :
273 52 : if (!filebuf.write(original_.get_buffer(), original_.length())) {
274 0 : return false;
275 : }
276 :
277 52 : Bio bcont;
278 52 : PKCS7Doc doc(SMIME_read_PKCS7(filebuf.bio(), &bcont.bio()));
279 52 : if (!doc) {
280 0 : OPENDDS_SSL_LOG_ERR("SMIME_read_PKCS7 failed");
281 0 : return false;
282 : }
283 :
284 52 : Bio content;
285 52 : if (!content.new_mem()) {
286 0 : return false;
287 : }
288 :
289 52 : if (!doc.verify(&certs, 0, bcont, content, PKCS7_TEXT | PKCS7_NOVERIFY | PKCS7_NOINTERN)) {
290 0 : return false;
291 : }
292 :
293 52 : char* p = 0;
294 52 : long size = content.get_mem_data(&p);
295 52 : if (size < 0) {
296 0 : return false;
297 : }
298 :
299 52 : content_ = std::string(p, size);
300 52 : verified_ = true;
301 :
302 52 : return verified_;
303 52 : }
304 :
305 51 : void SignedDocument::load_file(const std::string& path)
306 : {
307 51 : filename_ = path;
308 :
309 : #ifdef ACE_ANDROID
310 : CORBA::Octet *buffer;
311 :
312 : char b[1024];
313 : FILE* fp = ACE_OS::fopen(path.c_str(), "rb");
314 :
315 : int n;
316 : int i = 0;
317 : while (!feof(fp)) {
318 : n = ACE_OS::fread(&b, 1, 1024, fp);
319 : i += n;
320 :
321 : original_.length(i + 1); // +1 for null byte at end of cert
322 : buffer = original_.get_buffer();
323 : ACE_OS::memcpy(buffer + i - n, b, n);
324 : }
325 :
326 : ACE_OS::fclose(fp);
327 :
328 : // To appease the other DDS security implementations which
329 : // append a null byte at the end of the cert.
330 : buffer[i + 1] = 0u;
331 :
332 : #else
333 51 : std::ifstream in(path.c_str(), std::ios::binary);
334 :
335 51 : if (!in) {
336 0 : ACE_ERROR((LM_WARNING,
337 : "(%P|%t) SignedDocument::PKCS7_from_SMIME_file:"
338 : "WARNING: Failed to load file '%C'; '%m'\n",
339 : path.c_str()));
340 0 : return;
341 : }
342 :
343 51 : const std::ifstream::pos_type begin = in.tellg();
344 51 : in.seekg(0, std::ios::end);
345 51 : const std::ifstream::pos_type end = in.tellg();
346 51 : in.seekg(0, std::ios::beg);
347 :
348 51 : original_.length(static_cast<CORBA::ULong>(end - begin + 1));
349 51 : in.read(reinterpret_cast<char*>(original_.get_buffer()), end - begin);
350 :
351 51 : if (!in) {
352 0 : ACE_ERROR((LM_WARNING,
353 : "(%P|%t) SignedDocument::PKCS7_from_SMIME_file:"
354 : "WARNING: Failed to load file '%C'; '%m'\n",
355 : path.c_str()));
356 0 : return;
357 : }
358 :
359 : // To appease the other DDS security implementations
360 51 : original_[original_.length() - 1] = 0u;
361 : #endif
362 51 : }
363 :
364 6 : bool SignedDocument::operator==(const SignedDocument& other) const
365 : {
366 6 : return original_ == other.original_ && content_ == other.content_ && verified_ == other.verified_;
367 : }
368 :
369 : } // namespace SSL
370 : } // namespace Security
371 : } // namespace OpenDDS
372 :
373 : OPENDDS_END_VERSIONED_NAMESPACE_DECL
|