-
Notifications
You must be signed in to change notification settings - Fork 361
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
HTTPD output: Added support for HTTP Basic authentication #1968
Conversation
@@ -102,6 +112,15 @@ HttpdClient::HandleLine(const char *line) noexcept | |||
return true; | |||
} | |||
|
|||
if (StringEqualsCaseASCII(line, "Authorization: Basic ", 21)) { | |||
auto encoded_pwd_length = strlen(line) - 21; | |||
size_t debase64_size = CalculateBase64OutputSize(encoded_pwd_length); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is redundant code - there's a DecodeBase64()
overload which returns AllocatedArray
. You can then cast this buffer to std::string_view
(e.g. via ToStringView()
from util/SpanCast.hxx
) and operate on this buffer.
This is not only simpler and doesn't add redundant code, but is also faster because it reduces the two strlen()
calls in your code to just one.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fair point. Should be done, hopefully correctly.
doc/mpdconf.example
Outdated
@@ -283,6 +283,7 @@ input { | |||
# bitrate "128" # do not define if quality is defined | |||
# format "44100:16:1" | |||
# max_clients "0" # optional 0=no limit | |||
# password "secretPassword" # protect stream with HTTP Basic authentication (user can be anything) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
user can be anything
Note that username cannot contain :
, since then splitting username and password would result in part of the username being considered as part of the password.
Also, according to RFC 7617:
The user-id and password MUST NOT contain any control characters (see "CTL" in Appendix B.1 of [RFC5234]).
Perhaps we should enforce some hardcoded username, like mpd
, to avoid misuse?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've explicitly written in documentation can contain all printable characters except colon (the code does not check it, though, but I don't think it should be necessary).
I'm sorry to be that guy but I really don't understand why you'd bother with the massive potential headache of supporting this "security" in mpd when it would be easier and safer for users to use an existing reverse-proxy server. They'd get something well tested with support for a variety of auth mechanisms. Am I missing something? |
The whole idea of the Of course, this may replicate features that are available with more complex solutions, but MPD's For those people who want to make a stream available but want some protection, like an unencrypted password on an unencrypted HTTP connection - then it's fine to add this feature. It's weak, but for some people, it's just the right amount of security. Sure, somebody could sniff the password from the wire, and then they can listen to the stream. But the MPD user can't be accused of copyright violation by broadcasting copyrighted works - it's not a broadcast if it's protected, even if the protection is weak. I understand your complaint, and I know this plugin and this PR is very imperfect, but it suits some kind of MPD users. Not me, and not you. But that's okay. |
That's fair enough. My only further comment is that it's not obvious how secure this is trying to be. Maybe a note in the appropriate documentation would help. We obviously can't rely on GitHub comments. |
Agree. The presence of this feature should not pretend it's truly secure, when it cannot possibly be (because the transport is not secure). |
Basic authentication is fairly secure when used in conjunction with https. |
Looks like @Mrkvak is not interested in fixing the issues, and I only wasted my time with the code review. |
Damn, sorrry, I've totally forgot this is still open. |
@kingosticks @MaxKellermann basically explained my motivation behind this. If someone sniffs the password, they can also sniff the audio stream. My goal was to have MPD not publicly broadcasting when accessible from network/internet. Nothing more, nothing less. I think all feedback (thanks a lot for the review, by the way) was implemented, I've tested the code and it seems to be working. |
Ouch, the PR branch now contains merge commits. This is a big mess that cannot be reviewed. Please fix this. |
358c240
to
d1ad6d1
Compare
Sigh... I guess that's my fault for trying to use github gui. Should be rebased on MPD/master without any pesky merge commits. |
There are now commits which obviously fix mistakes in earlier commits of this PR, e.g. "working version". I don't want to read known-bad commits and find all those mistakes again, only to see later that those mistakes have been fixed already. If you find a mistake in a commit that is not yet merged, please amend the commit instead of adding a fixup commit. |
d1ad6d1
to
01c8d5c
Compare
Squashed |
#include "net/SocketError.hxx" | ||
#include "net/UniqueSocketDescriptor.hxx" | ||
#include "util/SpanCast.hxx" | ||
#include "util/StringCompare.hxx" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why include this header?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Missed leftover, sorry.
Actually no, it's to include StringAfterPrefixIgnoreCase
@@ -41,6 +50,10 @@ HttpdClient::BeginResponse() noexcept | |||
{ | |||
assert(state != State::RESPONSE); | |||
|
|||
if (!head_method) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why the head_method
check?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think it's necessary to password protect HEAD requests, is it?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You wrote extra code to implement a special case to allow HEAD requests. What justifies this extra complexity?
Your answer isn't good because extra complexity should be justified, not the other way round.
/** | ||
* Should we reject this request as unauthorized? | ||
*/ | ||
bool should_reject_unauthorized = false; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This field should only exist when the feature is available
@@ -169,7 +200,8 @@ HttpdClient::HttpdClient(HttpdOutput &_httpd, UniqueSocketDescriptor _fd, | |||
bool _metadata_supported) | |||
:BufferedSocket(_fd.Release(), _loop), | |||
httpd(_httpd), | |||
metadata_supported(_metadata_supported) | |||
metadata_supported(_metadata_supported), | |||
provided_password{""} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What's the point of this initializer?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think it's needed after the last simplification, sorry I missed it.
/** | ||
* Provided password (using HTTP basic auth) | ||
*/ | ||
std::string provided_password; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This field should only exist when the feature is available
/** | ||
* The configured password. | ||
*/ | ||
char const *password; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This field should only exist when the feature is available and it should be const
"If you find a mistake in a commit that is not yet merged, please amend the commit instead of adding a fixup commit." |
I want to have a publicly available streaming MPD server available from internet. However I want to protect the output with password - if not for anything else then for legal reasons (the moment someone other than me opens the stream I break the law).
This adds new optional "password" option for "httpd" output in mpd.conf. If it is specified and non-empty, it requires HTTP Basic authentication with arbitrary user and set password.
(It's been some time since I've wrote anything in C++, so please check the code if I haven't made any stupid mistakes. I've tested it and it seems to be working as intended in both cases.)