New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

epee: add SSL support #4054

Open
wants to merge 5 commits into
base: master
from

Conversation

6 participants
@moneromooo-monero
Copy link
Contributor

moneromooo-monero commented Jun 25, 2018

P2P and RPC connections now have optional tranparent SSL.

An optional private key and certificate file can be passed,
using the --{p2p,rpc,daemon}-ssl-private-key and
--{p2p,rpc,daemon}-ssl-certificate options. Those have as
argument a path to a PEM format private private key and
certificate, respectively.
If not given, a temporary self signed certificate will be used.

SSL can be enabled or disabled using --{p2p,rpc}-ssl, which
accepts autodetect (default), disabled or enabled.

Access can be restricted to particular certificates using the
--{p2p,rpc}-ssl-allowed-certificates, which takes a list of
paths to PEM encoded certificates. This can allow a wallet to
connect to only the daemon they think they're connected to,
by forcing SSL and listing the paths to the known good
certificates.

To generate long term certificates:

openssl genrsa -out /tmp/KEY 4096
openssl req -new -key /tmp/KEY -out /tmp/REQ
openssl x509 -req -days 999999 -sha256 -in /tmp/REQ -signkey /tmp/KEY -out /tmp/CERT

/tmp/KEY is the private key, and /tmp/CERT is the certificate,
both in PEM format. /tmp/REQ can be removed. Adjust the last
command to set expiration date, etc, as needed. It doesn't
make a whole lot of sense for monero anyway, since most servers
will run with one time temporary self signed certificates anyway.

SSL support is transparent, so all communication is done on the
existing ports, with SSL autodetection. This means you can start
using an SSL daemon now, but you should not enforce SSL yet or
nothing will talk to you.

@vtnerd

This comment has been minimized.

Copy link
Contributor

vtnerd commented Jun 26, 2018

NACK.

Monero needs to avoid maintaining this feature for RPC communication, and leave it to outside projects. The SSL is not "transparent" because there is no way for the clients to automagically verify the certificates. Some user intervention is required, and its easier to leave the configuration hell to projects dedicated to this problem area.

If Monero needs "opportunistic encryption" for P2P communication, it should follow the corresponding Bitcoin BIP, use CurveCP, or come up with something similar. SSL is a beast for this purpose, especially since the P2P links are never going to be authenticated (if they were, I would use SSH tunneling or something else not SSL).

@moneromooo-monero

This comment has been minimized.

Copy link
Contributor

moneromooo-monero commented Jun 26, 2018

--{rpc,daemon,p2p}-allowed-certificates allows a client/server to authenticate the certs.

I do not understand what you mean by: The SSL is not "transparent".

If you leave it to using other tools, pretty much nooone will be using it and we'll just DoS ourselves (especially since both sides have to be using it, and I don't see how that'll happen for P2P).

@anonimal

This comment has been minimized.

Copy link
Contributor

anonimal commented Jun 26, 2018

@moneromooo-monero If I'm not mistaken, by transparent I think he means something more along the lines of CurveZMQ (which is something I've wanted to do with kovri reseed for a while now).

@moneromooo-monero

This comment has been minimized.

Copy link
Contributor

moneromooo-monero commented Jun 26, 2018

it should follow the corresponding Bitcoin BIP

I have found it. Can you explain why you think it is better to use this than SSL ?

@moneromooo-monero

This comment has been minimized.

Copy link
Contributor

moneromooo-monero commented Jun 26, 2018

About the other suggestions: CurveCP says it's not ready for use yet, and making up my own system... not really, it's a recipe for failure.

@vtnerd

This comment has been minimized.

Copy link
Contributor

vtnerd commented Jun 26, 2018

I do not understand what you mean by: The SSL is not "transparent".

The context was server SSL for RPC. The setup cannot be automatic because the server certificate cannot be verified by the client unless the server operator takes some outside action (signed by CA, distributing the certificate, etc). So encryption will always require some additional action and know-how (by at least the server operator). If Monero assumes that the SSL certificate is valid is on first use, its arguable worse than having no built-in SSL because the same target users may be unaware that normal privacy and authentication protections of SSL have been potentially removed via MitM. Combined with the many whizz-bang options of SSL, I still think its best to steer people towards software whose focus is solving this issue.

Perhaps by default the client could refuse connections whose certificates could not be verified? I'm still not sure due to the additional maintenance burden this places on the project (Monero is now maintaining a subset of stunnel features, etc.).

EDIT: Monero already warns users about binding to an external interface and steers them towards stunnel. So they are making a conscious decision to ignore that advice currently. And complaints about setup on these outside processes is entirely the point - to do it "correctly" is difficult.

@moneromooo-monero If I'm not mistaken, by transparent I think he means something more along the lines of CurveZMQ (which is something I've wanted to do with kovri reseed for a while now).

This still has a similar issue as above (typical key distribution), but its certainly more simple than SSL and uses crypto that Monero relies on already. There is no key revocation, but that never worked for SSL (one of the many complicating anti-features). Also, this reminds me, isn't the HTTP RPC deprecated? I suppose that isn't practical?

If you leave it to using other tools, pretty much nooone will be using it and we'll just DoS ourselves (especially since both sides have to be using it, and I don't see how that'll happen for P2P).

DoS ourselves? I do not understand what that means in this context.

For P2P I wasn't against some opportunistic encryption scheme, just against using SSL for this purpose. I don't think its the correct protocol - we'd want something as simple as possible for this purpose IMO. Or perhaps we should reach out to the Bitcoin Core developers to see why they developed their own protocol for this purpose.

@moneromooo-monero

This comment has been minimized.

Copy link
Contributor

moneromooo-monero commented Jun 27, 2018

The setup cannot be automatic because the server certificate cannot be verified by the client unless the server operator takes some outside action (signed by CA, distributing the certificate, etc).

That is preposterous. You are claiming the user using --daemon-allowed-certificates is taking an outside action, wherewas getting a CA to sign a certificate is not. Of course a server/client can't verify a certificate without any action. --daemon-allowed-certificates just happens to be a very simple way to do this.

If Monero assumes that the SSL certificate is valid is on first use, its arguable worse than having no built-in SSL

Only the MITM node(s) can read/modify the traffic, as opposed to anything else on the path. And that's only if you decide to accept any cert.

Perhaps by default the client could refuse connections whose certificates could not be verified?

That seems maybe like a good idea.

isn't the HTTP RPC deprecated?

No, it was supposed to become deprecated once the 0MQ version was ready, but it never was and noone seems to care about it so it's bitrotting atm.

DoS ourselves? I do not understand what that means in this context.

If you place the bar too high so close to nobody encrypts their links, you end up with a totally cleartext network. And with stunnel etc the bar is probably already too high.

@vtnerd

This comment has been minimized.

Copy link
Contributor

vtnerd commented Jun 27, 2018

That is preposterous. You are claiming the user using --daemon-allowed-certificates is taking an outside action, wherewas getting a CA to sign a certificate is not. Of course a server/client can't verify a certificate without any action. --daemon-allowed-certificates just happens to be a very simple way to do this.

That is not what I am claiming - my initial comment was very clear but I will try again.

I am claiming that proper SSL protection cannot be done automatically by the person running the RPC server. Some user intervention is required, unless the implementation attempts "trust on first use" which has a tiny window for a MitM. This approach has less security than the CA approach, but it may not be obvious to the average user. Or maybe we determine it does not matter. But maintaining server SSL code, when epee needs to be refactored into cpp files or completely written, is a tough sell because there are solutions to this that are usable right now. And we already have a mechanism for steering people to these solutions.

Only the MITM node(s) can read/modify the traffic, as opposed to anything else on the path. And that's only if you decide to accept any cert.

Would a typical user who sees "RPC now has SSL" expect that caveat? Part of the goal of --confirm-external-bind and recommending stunnel was educating people running the RPC server how to do it semi-properly (to the extent SSL sort of works if allowing root CAs with SSH being significantly better if available).

And as for the second part "if you decide to accept any cert" - that comment shows my entire point. The "easiest" mode to avoid this situation is getting a signed cert. If the RPC server operator went through that trouble, why can't they figure out an outside process for handling SSL?

No, it was supposed to become deprecated once the 0MQ version was ready, but it never was and noone seems to care about it so it's bitrotting atm.

It is in use by the mymonero backend already, and will be again for the open source edition. The switch occurred because there were some binary only endpoints that required the C++ deserialization code, wheres 0MQ used JSON for everything. So its certainly usable, and pub/sub is desirable in many cases as well. It also has the advantage of a built-in background thread for processing all comms, so the next round of blocks can be fetched by the 0MQ I/O thread, etc (although we could do this with a major epee re-write/factor).

There are some other built-in modes for having multiple daemons service requests, etc., which can also be done with HTTP proxing. But is nice to just drop all of the overhead of HTTP ...

If you place the bar too high so close to nobody encrypts their links, you end up with a totally cleartext network. And with stunnel etc the bar is probably already too high.

What is the goal of encrypting the P2P links? The data is ultimately public, and its difficult to hide that you are running a Monero node due to the majority of nodes using the standard ports + many inbound connections (if allowed). The one advantage I can think of is concealing the origin IP for a transaction - someone with lots of passive surveillance has an advantage. But transaction privacy is one of the primary uses for Kovri.

Also you completely ignored the claim about SSL being a complicated protocol. Monero does tons of custom cryptographic operations already, and an "opportunistic encryption" scheme like BIP? needs : public key swapping, ECDH, keccack shared result, chacha20+poly1305. Monero does all of those things in various places throughout the code already, with the exception of poly1305. We should at least reach out to Bitcoin developers to see why they decided to not use SSL for this identical feature.

@hyc

This comment has been minimized.

Copy link
Contributor

hyc commented Jun 27, 2018

We should at least reach out to Bitcoin developers to see why they decided to not use SSL for this identical feature.

Definitely need to know why. That BIP is next to useless - it mentions a couple MITM attack scenarios in its Motivation section but doesn't defend against any of them. What's the point of publishing a proposal that explicitly doesn't solve the problems it identifies?

@moneromooo-monero

This comment has been minimized.

Copy link
Contributor

moneromooo-monero commented Jun 27, 2018

That is not what I am claiming - my initial comment was very clear but I will try again.

Right, sorry, I'd misread that as vs CA. But the "knowing the other side is really who you think it is" is not possible without doing anything in the first place AFAIK, whether this is specifying a key (or a set of keys) to trust, or asking a third party to decide for you.

If you have a problem with the default to accept any cert, it can be changed for RPC (though for P2P I think it makes sense to accept any by default).

It is in use by the mymonero backend already

OK, I thought there was a pending patch by tewinget that was bitrotting. Is that optional then, or did you not need this part ?

What is the goal of encrypting the P2P links?

Hiding traffic. Including whay you mentioned. If you say "you can still work something out this way, so we should do nothing yet", then I do not agree. As for Kovri, it's still not ready.

Also you completely ignored the claim about SSL being a complicated protocol

No. I agree with this.
I asked on #bitcoin-wizards for a set pros/cons.

@anonimal

This comment has been minimized.

Copy link
Contributor

anonimal commented Jun 27, 2018

As for Kovri, it's still not ready.

Not entirely. For RPC usage, you can build tunnels manually with the config file. For P2P, if monero had socks proxy support, you could use kovri socks proxy.

@vtnerd

This comment has been minimized.

Copy link
Contributor

vtnerd commented Jun 27, 2018

Right, sorry, I'd misread that as vs CA. But the "knowing the other side is really who you think it is" is not possible without doing anything in the first place AFAIK, whether this is specifying a key (or a set of keys) to trust, or asking a third party to decide for you.

If you have a problem with the default to accept any cert, it can be changed for RPC (though for P2P I think it makes sense to accept any by default).

What specific threat (to RPC) is this code is trying to mitigate? Simply to force the attacker to be active instead of passive? Or to prevent MitM? Obviously authentication and privacy are highly desirable in the RPC context.

The target audience is a user whose node operator couldn't setup an SSL proxy for them. Does this target audience understand the risks? Its such a weird target audience (giving the total context).

My next criticism is that this implementation is trust-on-connect. It likely needs to be trust-on-first-use for this target audience, to make the MitM attack more difficult (and therefore mitigating the risk for them even more), but that adds to the complexity of the implementation.

EDIT: I wanted to make it clear that I am still against adding SSL to the RPC server until specific threat(s) and/or targeted users are identified. The current implementation is simply a response to the complaint that "RPC should have SSL" as if this can be done properly by magic. The only scenario that this seems to benefit is when a node operator is unable, unwilling, or unaware of the benefits to providing a SSL proxy. And the most value that the project can add is eliminating completely passive monitoring, but nothing more (because if the user can setup certs, they can setup a SSL proxy with better MitM prevention than trust-on-connect or a more complex to maintain trust-only-on-first-use).

Definitely need to know why. That BIP is next to useless - it mentions a couple MITM attack scenarios in its Motivation section but doesn't defend against any of them. What's the point of publishing a proposal that explicitly doesn't solve the problems it identifies?

This is why I've been pushing back against encryption for P2P communication - TLS cannot provide some magic solution against MitM in this situation either. We need to identify specific threat(s) that can be prevented by adding TLS, otherwise this is adding to the attack surface without benefit. One of the Bitcoin developers argued that an adversary would be unaware which links had known authentication keys, and therefore risked exposure if they attempted a MitM. This wouldn't necessarily help if a specific target (user) was chosen, and we still need to identify specific threat(s) to sending unencrypted data over the P2P links.

I (and @moneromooo-monero over IRC) have come up with passively tracking the origin of a transaction or block as potential threats. Also, I realized that the IP peerlist is exchanged over P2P; a passive adversary not near either node can get a peek at the connection topology of those nodes (an adversary passively monitoring near either node will always be aware of the topology). Transaction and block origin attacks still have active techniques - which is why Kovri is on the roadmap. Peerlist exposure can always be exposed through an active attack or passive attack (but in less situations if encrypted).

It is not immediately obvious that encrypting the Monero P2P links is worth the risk, given that we are still pursuing Kovri for the biggest metadata/information leaks. And OpenSSL has had issues specifically with the TLS code in the recent past. Pushing more of the attack surface to Kovri means that a really bad bug can take down the Kovri network, but not the Monero network.

If the benefits of TLS outweigh the negatives, the project should still identify techniques to make the attack surface small as possible. My initial thoughts on this are: (1) require the newest TLS version only, (2) consider disabling heartbeats, (3) disable session resumption, (4) whitelist ciphers and public key methods.

At this point, I'm still strongly in favor of letting Kovri do this for P2P data. The benefits are limited. Or the discussion needs to be more than just jam in "SSL support" as its too vague for the importance of the P2P communication channels.

OK, I thought there was a pending patch by tewinget that was bitrotting. Is that optional then, or did you not need this part ?

The implementation works, but some of the serialization paths need some patching. Tewinget was trying to add "push" notifications to 0MQ through its pub/sub sockets for new blocks + mempool changes. Currently implementations have to poll over HTTP or 0MQ for this information. Push notifications over HTTP (long polling) is kind of crummy but possible. 0MQ always has less overhead to parse and serialize the protocol.

@moneromooo-monero

This comment has been minimized.

Copy link
Contributor

moneromooo-monero commented Jun 30, 2018

I asked on #bitcoin-wizards for a set pros/cons.

There was no reply, save for some guy who was sarcastic without attack surface.

@vtnerd

This comment has been minimized.

Copy link
Contributor

vtnerd commented Jun 30, 2018

I asked on #bitcoin-wizards for a set pros/cons.

There was no reply, save for some guy who was sarcastic without attack surface.

This is a misrepresentation of what he said. He was not being sarcastic in his response. He was somewhat non-specific in his criticisms, primarily stating that he felt like SSL has a large attack surface. The only elaboration was on his experience with the X.509 format - he felt like his prior usage of that format was a "mistake".

@moneromooo-monero

This comment has been minimized.

Copy link
Contributor

moneromooo-monero commented Jun 30, 2018

Whatever. I read "why would anyone want to use SSL" or whatever the exact words were, as sarcastic, maybe it was not. It's not the point anyway. I'll make another patch using that BIP system, but not sure when atm.

And rereading what I wrote, I meant about attack surface above.

@moneromooo-monero

This comment has been minimized.

Copy link
Contributor

moneromooo-monero commented Jun 30, 2018

Though this system can't really work for RPC connections, since the clients will be random other programs like curl, JSON RPC libraries... Which gets us back to the idea of using stunnel ending up with almost nobody bothering.

@moneromooo-monero

This comment has been minimized.

Copy link
Contributor

moneromooo-monero commented Jun 30, 2018

Also related, I got this:

==7972==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x62900004f258 at pc 0x58d94486f622 bp 0x77ef29aaaaf0 sp 0x77ef29aaa2a0
READ of size 52025 at 0x62900004f258 thread T3
#0 0x58d94486f621 in __interceptor_memcpy.part.45 (/home/user/src/bitmonero/build/debug/bin/monerod+0x494621)
#1 0x77f0146f6359 (/lib64/libcrypto.so.10+0x121359)
#2 0x77f0146eeb2a in BIO_write (/lib64/libcrypto.so.10+0x119b2a)
#3 0x77f014d63cc1 in ssl3_write_pending (/lib64/libssl.so.10+0x2acc1)
#4 0x77f014d643b7 in ssl3_write_bytes (/lib64/libssl.so.10+0x2b3b7)
#5 0x58d944c87a66 in boost::asio::ssl::detail::engine::do_write(void*, unsigned long) /home/user/boost_1_65_1_install/include/boost/asio/ssl/detail/impl/engine.ipp:314:10

@moneromooo-monero

This comment has been minimized.

Copy link
Contributor

moneromooo-monero commented Jun 30, 2018

Or maybe I can make a SSH tunneling tool/script that's easy to use with monero...

@vtnerd

This comment has been minimized.

Copy link
Contributor

vtnerd commented Jul 1, 2018

Though this system can't really work for RPC connections, since the clients will be random other programs like curl, JSON RPC libraries

I don't understand. Do you mean many libraries will reject the connection for having unverifiable certificates?

Which gets us back to the idea of using stunnel ending up with almost nobody bothering.

Or maybe I can make a SSH tunneling tool/script that's easy to use with monero...

Do you think if we provide a built-in solution for encryption/authentication, that this same user group will perform some kind of authentication (inspecting certificates or signing them)? The authentication part is important for these RPC channels too. This is what I'm doubting, that this same user group will go through with that effort. SSH proxies in particular are easy to setup (tons of online tutorials) and SSH mitigates poor certificate verification by storing it (trust on first use). GnuTLS also has a method for storing pubkeys, specifically for trust on first use. I'm not sure I like that though, should be application specific ...

And another thing that influences my thinking on this topic - what do we expect the typical (or even lowest) user experience level for running a remote RPC connection? This isn't the node+GUI user, which needs to work properly even with low user experience levels.

==7972==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x62900004f258 at pc0x58d94486f622 bp 0x77ef29aaaaf0 sp 0x77ef29aaa2a0
READ of size 52025 at 0x62900004f258 thread T3
#0 0x58d94486f621 in __interceptor_memcpy.part.45 (/home/user/src/bitmonero/build/debug/bin/monerod+0x494621)
#1 0x77f0146f6359 (/lib64/libcrypto.so.10+0x121359)
#2 0x77f0146eeb2a in BIO_write (/lib64/libcrypto.so.10+0x119b2a)
#3 0x77f014d63cc1 in ssl3_write_pending (/lib64/libssl.so.10+0x2acc1)
#4 0x77f014d643b7 in ssl3_write_bytes (/lib64/libssl.so.10+0x2b3b7)
#/5 0x58d944c87a66 in boost::asio::ssl::detail::engine::do_write(void*, unsigned long) /home/user/boost_1_65_1_install/include/boost/asio/ssl/detail/impl/engine.ipp:314:10

boost::asio async operations assume that the buffer is valid until the callback is invoked. Usually these sorts of issues orginate with that. Although this says heap buffer overflow and not use after free.

@moneromooo-monero

This comment has been minimized.

Copy link
Contributor

moneromooo-monero commented Jul 1, 2018

Though this system can't really work for RPC connections, since the clients will be random other >>programs like curl, JSON RPC libraries

I don't understand. Do you mean many libraries will reject the connection for having unverifiable
certificates?

I mean if we use the Bitcoin system, some of the RPC connections coming from things like curl or JSON RPC libraries will have to be modified to add this manually. As opposed to the daemon RPC coming from the wallet, which can be pre-modified to support this. It mean it makes the monero RPC really hard to use. And if this encryption is optional, it will again push people to use unencrypted links.

Do you think if we provide a built-in solution for encryption/authentication, that this same user group will perform some kind of authentication

Why not ? If the default is to restrict to the pubkeys/certs given on the command line, it'd take work to do it wrong, no ?

@anonimal

This comment has been minimized.

Copy link
Contributor

anonimal commented Jul 2, 2018

Or maybe I can make a SSH tunneling tool/script that's easy to use with monero...

Or help with kovri development 😄 😉

@moneromooo-monero

This comment has been minimized.

Copy link
Contributor

moneromooo-monero commented Jul 2, 2018

I don't intend to spend months or years on this :P

@moneromooo-monero

This comment has been minimized.

Copy link
Contributor

moneromooo-monero commented Nov 4, 2018

Sadly unwanted.

@moneromooo-monero

This comment has been minimized.

Copy link
Contributor

moneromooo-monero commented Jan 10, 2019

I think this should go in after all. While we'll hopefully have Tor hidden service support for RPC, this still requires Tor to run, while this patch does not need any further tools.

I fixed a use after free bug, might be the cause of the corruption above. I've not seen corruption again so far.

I changed the default to disallow SSL connections if no allowed cert is specified on the command line.
SSL support for P2P is removed. Some config setup is done to reduce attack surface, but this is all from looking up the internet, so if someone has experience with this, please share.

@moneromooo-monero moneromooo-monero force-pushed the moneromooo-monero:ssl branch from 535c72d to 91931cc Jan 10, 2019

X509_gmtime_adj(X509_get_notBefore(cert), 0);
X509_gmtime_adj(X509_get_notAfter(cert), 3600 * 24 * 365 * 10); // 10 years
X509_set_pubkey(cert, pkey);
X509_NAME *name = X509_get_subject_name(cert);

This comment has been minimized.

@hyc

hyc Jan 11, 2019

Contributor

This cert was just created, so its subject_name is empty. Do we want to put some kind of non-empty name in here?

This comment has been minimized.

@omartijn

omartijn Jan 11, 2019

That's a good question. Perhaps something with boost::asio::hostname() ?

This comment has been minimized.

@moneromooo-monero

moneromooo-monero Jan 11, 2019

Contributor

I would definitely not want my hostname to leak through a SSL certificate. What is that name used for in practice ?

This comment has been minimized.

@hyc

hyc Jan 11, 2019

Contributor

The name in a server cert usually corresponds to its hostname, and clients will verify that the name in the cert matches the name the client tried to connect to. This is one of the typical means of detecting spoofing attacks. Before progressing any further, we should define what is the threat that SSL is being used to protect against?

This comment has been minimized.

@moneromooo-monero

moneromooo-monero Jan 11, 2019

Contributor

AFAIK that name matching's only useful when using CAs. Here, we don't, we give monero the cert we expect to see. No spoofing possible (unless the cert's private key was compromised).

SSL is now used only for RPC. The point is to hide the data from network level snoopers (or people who'd modify the data on the way). And generally add some more encrypted traffic to piss off grab-everything snoops :)

For those people who use a round robin RPC server, they'll have to be set to accept any cert, but they're not expecting to connect to any particular server anyway, so nothing lost. They still gain privacy from their ISP (unless that ISP actively MITMs).

This comment has been minimized.

@hyc

hyc Jan 11, 2019

Contributor

we give monero the cert we expect to see.

I'm missing something here, then. This code generates a cert on the fly, on the monerod. So how does the client "expect" to see this? There's no protection against MITM at all, since any 3rd party could intercept and use their own generated cert. If active MITM is not something we're defending against, we can just skip all of this and use an anonymous Diffie-Hellman ciphersuite for the SSL session.

This comment has been minimized.

@fuwa0529

fuwa0529 Jan 11, 2019

It seems to be only used when certificate_path is empty, so when no certificate was generated, this code protects against passive eavesdropping, when given, active MITM.

This comment has been minimized.

@moneromooo-monero

moneromooo-monero Jan 11, 2019

Contributor

Yes, as above. Additionally, if you use --*-allow-any-cert, it will accept any. Otherwise it will require a cert that's in the --*-allowed-certificates list.

The idea is that a public RPC daemon nay establish a known identity (optional) and accept any cert, a private daemon that one wants to access through the internet will establish a know identity and require a particular cert, a wallet using a third party node will let a random cert be generated (avoids fingerprinting), and a wallet using a private node will have its own cert (which the private node will expect).

@moneromooo-monero moneromooo-monero force-pushed the moneromooo-monero:ssl branch from 91931cc to d58bc29 Jan 11, 2019

}

// detect SSL - we require all bytes to be there on the first recv for simplicity
if (is_ssl((const unsigned char*)buffer_.data(), bytes_transferred))

This comment has been minimized.

@vtnerd

vtnerd Jan 11, 2019

Contributor

As the comments indicate, this fails if the TLS message is split over TCP packets, so its broken.

This comment has been minimized.

@vtnerd

vtnerd Jan 11, 2019

Contributor

I had this queued up ages ago, and didn't delete it. Ignore for now.

This comment has been minimized.

@moneromooo-monero

moneromooo-monero Jan 11, 2019

Contributor

I intended to, but actually it's not too difficult to fix, though I can't seem to be able to test this, sending a char at a time with a second's delay still ends up with boost buffering all.

m_ctx.set_default_verify_paths();
m_ctx.context.set_options(boost::asio::ssl::context::default_workarounds | boost::asio::ssl::context::no_sslv2);
m_ctx.context.set_default_verify_paths();
m_ssl_socket.~stream();

This comment has been minimized.

@vtnerd

vtnerd Jan 11, 2019

Contributor

Assign a default constructed object to m_ssl_socket. The current code might be implementation defined.

This comment has been minimized.

@moneromooo-monero

moneromooo-monero Jan 11, 2019

Contributor

Interesting. I have no idea why I did it that way...
Why is it implementation defined btw ?
And you meant the ctor call below, right, not default ?

This comment has been minimized.

@moneromooo-monero

moneromooo-monero Jan 11, 2019

Contributor

Ah. I found why:

/home/user/src/bitmonero/contrib/epee/include/net/net_helper.h:140:18: error: object of type 'boost::asio::ssl::stream<boost::asio::basic_stream_socket<boost::asio::ip::tcp, boost::asio::stream_socket_serviceboost::asio::ip::tcp > >' cannot be assigned because its copy assignment operator is implicitly deleted

This comment has been minimized.

@vtnerd

vtnerd Jan 12, 2019

Contributor

If either the destructor or constructor throws, m_ssl_socket is in an indeterminate state and UB will occur the next time the object is touched (during destruction, for instance). It's fairly tricky to code around this - this is yet-another sneakily weird edge case within the language.

I know something similar is done in epee, but its something that should likely be updated too.

This comment has been minimized.

@vtnerd

vtnerd Jan 12, 2019

Contributor

It seems like there has to be a better way to reset state of the object, if not boost::optional will do what you want. Or you'll manually have to use std::aligned_storage and use some bool to track state.

This comment has been minimized.

@moneromooo-monero

moneromooo-monero Jan 13, 2019

Contributor

That all sounds very unlikely, no ?

This comment has been minimized.

@vtnerd

vtnerd Jan 14, 2019

Contributor

The documentation does not list any possible exceptions, however neither function is marked as noexcept. I'm not sure about the ASIO developer, but several Boost developers view noexcept as a permanent API specification. Therefore the lack of noexcept means some future release could throw. And I'm not entirely certain that these can't throw now; the constructor and destructor sequences are platform specific (due to internal boost::asio::ip::tcp::socket).

If there is no other good technique for resetting state (there doesn't appear to be), UB can be removed with heap allocation, boost::optional, or a custom std::aligned_storage. Leaving the potential of UB doesn't seem worth it considering the fixes are relatively easy, and two of them can avoid additional allocations.

This comment has been minimized.

@moneromooo-monero

moneromooo-monero Jan 15, 2019

Contributor

OK, I'll add a boost::optional here then.

This comment has been minimized.

@moneromooo-monero

moneromooo-monero Jan 15, 2019

Contributor

Actually, a shared_ptr seems even better AFAICT. I'll do that and see if anything complains.

This comment has been minimized.

@moneromooo-monero

moneromooo-monero Jan 15, 2019

Contributor

Ah, nevermind, that's what you said via heap allocation above. So I'll do that.

@@ -110,25 +111,34 @@ namespace net_utils
catch(...) { /* ignore */ }
}

inline void set_ssl(epee::net_utils::ssl_support_t ssl_support = epee::net_utils::e_ssl_support_autodetect, const std::string &private_key = std::string(), const std::string &certificate = std::string(), const std::list<std::string> &allowed_certificates = std::list<std::string>(), bool allow_any_cert = false)

This comment has been minimized.

@vtnerd

vtnerd Jan 11, 2019

Contributor

I have simultaneously been working on updating client SSL stuff here since the proxy code has to touch this very same portion of code. Then I realized that this PR almost certainly changed this function (yup).

I started to use a boost::variant to specify the parameters. This allowed for specifying the behavior and values in the same object. For instance, setting the last parameter to true (in this patch) ignores several of the other parameters entirely which is weird. The code I was working with looks something like:

#include <boost/variant/variant_fwd.hpp>

namespace ssl
{
   struct none_
   {};
   constexpr const none_ none{};

   struct any_certificate_
   {};
   constexpr const any_certificate_ any_certificate{};

   struct default_certificates_
   {};
   constexpr const default_certificates_ default_certificates{};

    struct certificate_file
    {
        std::string path;
    };

   using mode = boost::variant<none_, any_certificate_, default_certificates_, certificate_file>;
}

// client
#include <boost/variant/variant.hpp>
m_http_client.connect(address, port, timeout, ssl::default_certificates);
m_http_client.connect(address, port, timeout, ssl::certificate_file{"/etc/server/file.pem"});

Also, if multiple certs are removed then it becomes much easier because there is built-in functionality with asio::ssl/openssl to use a single cert. Since one certificate can sign multiple server certificates, when would this functionality be used? It seems easier to omit the logic (and thus any potential bugs/memory leaks).

This comment has been minimized.

@moneromooo-monero

moneromooo-monero Jan 11, 2019

Contributor

OK, seems fair enough. The param list is a bit unwieldy indeed.

If I remove the multiple cert thing, then not much code is saved, and it forces the user to select the right cert for every run if they usually use more than one peer. Given it's C++ containers, I really do not expects leaks either.

This comment has been minimized.

@vtnerd

vtnerd Jan 12, 2019

Contributor

I meant the usage of openssl calls. create_ssl_certificate should need some frees at the end, and there are a number of places where a std::string constructor can throw and leak memory. The latter scenario is unlikely at least.

This comment has been minimized.

@moneromooo-monero

moneromooo-monero Jan 13, 2019

Contributor

Sure, but using this as an argument for supporting only one allowed certificate seems reaching quite a bit. If there are leaks on error paths, let's fix them.

This comment has been minimized.

@vtnerd

vtnerd Jan 15, 2019

Contributor

I looked up the documentation for SSL_CTX_load_verify_locations, and multiple certificates can be put into a single file. ASIO doesn't forward to this function, it would be have to be called manually. Seems easier than risking bugs in iterating through a list manually.

This comment has been minimized.

@moneromooo-monero

moneromooo-monero Jan 15, 2019

Contributor

What code are you suggesting replacing ? The only code I see looping through allowed certificates is:

  std::list ssl_allowed_certificates;
  for (const std::string &path: daemon_ssl_allowed_certificates)
  {
      ssl_allowed_certificates.push_back({});
      if (!epee::file_io_utils::load_file_to_string(path, ssl_allowed_certificates.back()))
      {
        MERROR("Failed to load certificate: " << path);
        ssl_allowed_certificates.back() = std::string();
      }
  } 

but that doesn't quite seem right to me.

@moneromooo-monero moneromooo-monero force-pushed the moneromooo-monero:ssl branch from d58bc29 to cf298a1 Jan 11, 2019

@moneromooo-monero

This comment has been minimized.

Copy link
Contributor

moneromooo-monero commented Jan 15, 2019

If you know which cipher specs are good to whitelist, go ahead. Here, they're all reported as strong by nmap, but there's still more than a dozen, and I'm not sure whether it's machine dependent or not.

moneromooo-monero added some commits Jun 14, 2018

epee: add SSL support
P2P and RPC connections now have optional tranparent SSL.

An optional private key and certificate file can be passed,
using the --{p2p,rpc,daemon}-ssl-private-key and
--{p2p,rpc,daemon}-ssl-certificate options. Those have as
argument a path to a PEM format private private key and
certificate, respectively.
If not given, a temporary self signed certificate will be used.

SSL can be enabled or disabled using --{p2p,rpc}-ssl, which
accepts autodetect (default), disabled or enabled.

Access can be restricted to particular certificates using the
--{p2p,rpc}-ssl-allowed-certificates, which takes a list of
paths to PEM encoded certificates. This can allow a wallet to
connect to only the daemon they think they're connected to,
by forcing SSL and listing the paths to the known good
certificates.

To generate long term certificates:

openssl genrsa -out /tmp/KEY 4096
openssl req -new -key /tmp/KEY -out /tmp/REQ
openssl x509 -req -days 999999 -sha256 -in /tmp/REQ -signkey /tmp/KEY -out /tmp/CERT

/tmp/KEY is the private key, and /tmp/CERT is the certificate,
both in PEM format. /tmp/REQ can be removed. Adjust the last
command to set expiration date, etc, as needed. It doesn't
make a whole lot of sense for monero anyway, since most servers
will run with one time temporary self signed certificates anyway.

SSL support is transparent, so all communication is done on the
existing ports, with SSL autodetection. This means you can start
using an SSL daemon now, but you should not enforce SSL yet or
nothing will talk to you.
ssl: default to not accepting any cert but the ones on the allowed list
This means not giving an allowed list (ie, the default command line)
will reject all SSL connections

@moneromooo-monero moneromooo-monero force-pushed the moneromooo-monero:ssl branch from cf298a1 to 3582b16 Jan 15, 2019

@moneromooo-monero moneromooo-monero force-pushed the moneromooo-monero:ssl branch from 3582b16 to fd0dc8e Jan 15, 2019

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment