Skip to content

Commit

Permalink
MB-47905: Recognize internal user extracted from client certificate
Browse files Browse the repository at this point in the history
Change-Id: Icc2aa3f13c8a231e1fd5a48e78992e49d5747a72
Reviewed-on: https://review.couchbase.org/c/ns_server/+/172625
Well-Formed: Build Bot <build@couchbase.com>
Tested-by: Timofey Barmin <timofey.barmin@couchbase.com>
Reviewed-by: Artem Stemkovski <artem@couchbase.com>
  • Loading branch information
timofey-barmin committed Apr 7, 2022
1 parent fda7175 commit ff64a7e
Show file tree
Hide file tree
Showing 4 changed files with 47 additions and 8 deletions.
2 changes: 2 additions & 0 deletions src/menelaus_auth.erl
Expand Up @@ -275,6 +275,8 @@ authenticate({token, Token} = Param) ->
true ->
rpc:call(ns_node_disco:ns_server_node(), ?MODULE, authenticate, [Param])
end;
authenticate({client_cert_auth, "@" ++ _ = Username}) ->
{ok, {Username, admin}};
authenticate({client_cert_auth, Username} = Param) ->
%% Just returning the username as the request is already authenticated based
%% on the client certificate.
Expand Down
8 changes: 7 additions & 1 deletion src/ns_error_messages.erl
Expand Up @@ -9,6 +9,7 @@
%%
-module(ns_error_messages).

-include("ns_common.hrl").
-include("cut.hrl").

-export([decode_json_response_error/3,
Expand Down Expand Up @@ -340,7 +341,12 @@ reload_node_certificate_error({conflicting_certs, PemFile, P12File}) ->
"PEM(~s) and PKCS12(~s). Please remove one of them",
[PemFile, P12File]));
reload_node_certificate_error(empty_pass) ->
<<"Empty PKCS12 passwords are not supported for security reasons">>.
<<"Empty PKCS12 passwords are not supported for security reasons">>;
reload_node_certificate_error(bad_cert_identity) ->
"@" ++ Name = ?INTERNAL_CERT_USER,
NameBin = list_to_binary(Name),
<<"Internal client certificate must contain "
"SAN.email=", NameBin/binary ,"@"?INTERNAL_CERT_EMAIL_DOMAIN>>.

node_certificate_warning(unused) ->
<<"This certificate is auto-generated and doesn't seem to be used by any "
Expand Down
29 changes: 28 additions & 1 deletion src/ns_server_cert.erl
Expand Up @@ -46,7 +46,8 @@
expiration_warnings/1,
split_certs/1,
cert_props/1,
cert_expiration_warning_days/0]).
cert_expiration_warning_days/0,
extract_internal_client_cert_user/1]).

inbox_ca_path() ->
filename:join(path_config:component_path(data, "inbox"), "CA").
Expand Down Expand Up @@ -929,6 +930,9 @@ set_certificate_chain(Type, Chain, PKey, PassphraseSettings) ->
end,
fun () ->
validate_otp_certs(Type, ChainPem, PKey, PassFun)
end,
fun () ->
validate_cert_identity(Type, LeafCert)
end]);
{error, _} = Error ->
Error
Expand Down Expand Up @@ -1345,3 +1349,26 @@ get_node_cert_info(Node) ->

cert_expiration_warning_days() ->
ns_config:read_key_fast({cert, expiration_warning_days}, 30).

extract_internal_client_cert_user(Cert) ->
case get_sub_alt_names_by_type(Cert, rfc822Name) of
{error, not_found} ->
{error, not_found};
Emails ->
fun FindInternalEmail ([]) -> {error, not_found};
FindInternalEmail ([Email | T]) ->
case string:split(Email, "@") of
[Name, ?INTERNAL_CERT_EMAIL_DOMAIN] ->
{ok, "@" ++ Name};
_ ->
FindInternalEmail(T)
end
end (Emails)
end.

validate_cert_identity(node_cert, _) -> ok;
validate_cert_identity(client_cert, {'Certificate', DerCert, not_encrypted}) ->
case extract_internal_client_cert_user(DerCert) of
{ok, _UserName} -> ok;
{error, not_found} -> {error, bad_cert_identity}
end.
16 changes: 10 additions & 6 deletions src/ns_ssl_services_setup.erl
Expand Up @@ -911,7 +911,7 @@ certs_epoch() ->

-spec get_user_name_from_client_cert(term()) -> string() | undefined | failed.
get_user_name_from_client_cert(Val) ->
ClientAuth = ns_ssl_services_setup:client_cert_auth(),
ClientAuth = client_cert_auth(),
{state, State} = lists:keyfind(state, 1, ClientAuth),
case Val of
{ssl, SSLSock} ->
Expand All @@ -935,11 +935,15 @@ get_user_name_from_client_cert(Val) ->

get_user_name_from_client_cert(Cert, ClientAuth) ->
Triples = proplists:get_value(prefixes, ClientAuth),
case get_user_name_from_client_cert_inner(Cert, Triples) of
{error, _} ->
failed;
Username ->
Username
case ns_server_cert:extract_internal_client_cert_user(Cert) of
{ok, User} -> User;
{error, not_found} ->
case get_user_name_from_client_cert_inner(Cert, Triples) of
{error, _} ->
failed;
Username ->
Username
end
end.

get_user_name_from_client_cert_inner(_Cert, []) ->
Expand Down

0 comments on commit ff64a7e

Please sign in to comment.