Skip to content

Conversation

kostasrim
Copy link
Contributor

  1. Extends AUTH command to authenticate ACL users
  2. Add tests

@kostasrim kostasrim requested review from romange and dranikpg August 24, 2023 10:07
@kostasrim kostasrim self-assigned this Aug 24, 2023
Base automatically changed from acl_part_4_add_acl_set_user to main August 24, 2023 10:24
}

return user->second.HasPassword(password);
return {user->second.HasPassword(password) && user->second.IsActive(), user->first};
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
return {user->second.HasPassword(password) && user->second.IsActive(), user->first};
return {user->second.IsActive() && user->second.HasPassword(password), user->first};

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Personal preference, but I don't mind the suggestion. I think the second is more likely than the first but oh well 🤷

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

From a security perspective I don't want us to check the password if the user is disabled. It doesn't really matter but it's a bad practice IMO

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hmmmm, what could go wrong?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

'Reject illegal queries as soon as you can know they're illegal' is just a good security principal, even if it doesn't have immediate bad consequences.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes but you don't always engineer around security in mind -- any obsession over one principle can backfire pretty fast, imagine if C++ was designed with this in mind, no UB etc, it would be monstrous (and I don't bite the arguments about Rust being a better safe alternative this is so easy to debunk). That being said for execution paths that are guaranteed to be handled I don't see any problem. Again, I like to consider suggestions and since you think that this is a good principle I happily applied it 😄

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is code that handles authentication. It HAS to be designed with security in mind.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤣

@@ -196,6 +196,8 @@ class ConnectionContext : public facade::ConnectionContext {
// Reference to a FlowInfo for this connection if from a master to a replica.
FlowInfo* replication_flow;

std::optional<std::string_view> authed_username;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IIUC, the underlying std::string_view lives in the user registry and can be de-allocated when that user is deleted.

There's an issue in general of what happens to connections when their user is deleted. But this especially looks like a UAF bug waiting to happen.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nope. That's not true.

  1. You can't change the username of a user once its created.
  2. You can only delete, but when that happens you first evict && close already opened connections so the lifetime semantics are guaranteed -- you will never access the name of the user once its released

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, but please document this next to the field.


auto [cat, add] = MaybeParseAclCategory(command);
if (!cat) {
return ErrorReply(absl::StrCat("Unrecognized paramter", command));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
return ErrorReply(absl::StrCat("Unrecognized paramter", command));
return ErrorReply(absl::StrCat("Unrecognized parameter", command));

return ErrorReply(absl::StrCat("Unrecognized paramter", command));
}

auto* acl_field = add ? &req.plus_acl_categories : &req.minus_acl_categories;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a bit silly, but do we want to check no category appears both as a minus and as a plus?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is from the previous PR. What do you mean no caregory? You mean NONE ?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

like what happens if I do ACL SETUSER +@JSON -@JSON

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So I thought about this before, and for this specific case there won't be an issue because the ADD set is applied Before the REMOVE set and I thought that this redundancy is fine. However, now I am thinking about it, for the reverse case this is not true, that is -@JSON +@JSON won't do what is expected and therefore I think we should only allow a group once per SET USER command (and probably this will require some special handling for some categories, example is -@JSON +@ALL).

Another solution is to apply them one by one in order which is slightly slower (since I can apply them all at once) but I don't expect it will affect the performance and IMO I think this is a better solution since it covers the ordering and we can allow +@JSON -@JSON and vice versa.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I will address this in a separate PR since this is about the AUTH command

Copy link
Contributor

@dranikpg dranikpg Aug 24, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wait... What operations are permitted? If the string is SSO, then any operations on the map (rehashing, shrinking, etc) will invalidate the string_view 🤨

using RegistryType = absl::flat_hash_map<std::string, User>;

PS: I wanted to put this comment below the std::optional<std::string_view> authed_username; discussion 😬

return ErrorReply(absl::StrCat("Unrecognized paramter", command));
}

auto* acl_field = add ? &req.plus_acl_categories : &req.minus_acl_categories;
Copy link
Contributor

@dranikpg dranikpg Aug 24, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wait... What operations are permitted? If the string is SSO, then any operations on the map (rehashing, shrinking, etc) will invalidate the string_view 🤨

using RegistryType = absl::flat_hash_map<std::string, User>;

PS: I wanted to put this comment below the std::optional<std::string_view> authed_username; discussion 😬

Comment on lines 54 to 55
std::pair<bool, const std::string_view> AuthUser(std::string_view username,
std::string_view password) const;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please add a comment what the return value means (not obvious without looking at impl how to tell apart non-existing and existing but disabled user)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No needed anymore, we deep copy the username :)

@kostasrim
Copy link
Contributor Author

kostasrim commented Aug 25, 2023

Wait... What operations are permitted? If the string is SSO, then any operations on the map (rehashing, shrinking, etc) will invalidate the string_view 🤨

The SSO is an issue. I am not happy about this, and honestly I did not consider it -- really good catch here. You can only add users to the registry but you can't really update their usernames. But this doesn't matter because as you say, if the hash table resizes the string_view will dangle. That being said, for now I will add std::string on the connection_context. I was about to say that maybe I should introduce a small string class that doesn't have SSO but this is really unnecessary, (estimating 10k connections * 15 characters each is a few kb -- an insignificant amount of memory)

@dranikpg
Copy link
Contributor

I was about to say that maybe I should introduce a small string class that doesn't have SSO but this is really unnecessary,

Thought about this 100500 times, there are cases where SSO is not desired and I just use unique_ptr<char[]>

If we find yet a third use-case lets implement a good wrapper for it 🤣

@kostasrim kostasrim requested a review from dranikpg August 25, 2023 15:07
dranikpg
dranikpg previously approved these changes Aug 25, 2023
@kostasrim kostasrim enabled auto-merge (squash) August 25, 2023 15:09
@kostasrim kostasrim merged commit 50545fc into main Aug 25, 2023
@kostasrim kostasrim deleted the acl_part_5_add_acl_auth branch August 25, 2023 15:56
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants