From 3f761cbe6aa3979636db3c21e94e32f95e8368ad Mon Sep 17 00:00:00 2001 From: Gilbert Wong Date: Tue, 23 Oct 2018 14:37:05 +0800 Subject: [PATCH 1/3] Support use certifate as username Prior to this change, you can just use CN or EN field from the client certificate as username. This change add a new option to allow user to use Certificate directly as username. --- etc/emqx.conf | 6 +++--- priv/emqx.schema | 6 +++--- src/emqx_protocol.erl | 7 ++++--- 3 files changed, 10 insertions(+), 9 deletions(-) diff --git a/etc/emqx.conf b/etc/emqx.conf index 874a4c560..56fcf5ffc 100644 --- a/etc/emqx.conf +++ b/etc/emqx.conf @@ -1159,10 +1159,10 @@ listener.ssl.external.ciphers = ECDHE-ECDSA-AES256-GCM-SHA384,ECDHE-RSA-AES256-G ## Value: on | off ## listener.ssl.external.honor_cipher_order = on -## Use the CN field from the client certificate as a username. +## Use the CN, EN or CRT field from the client certificate as a username. ## Notice that 'verify' should be set as 'verify_peer'. ## -## Value: cn | en +## Value: cn | en | crt ## listener.ssl.external.peer_cert_as_username = cn ## TCP backlog for the SSL connection. @@ -1522,7 +1522,7 @@ listener.wss.external.certfile = {{ platform_etc_dir }}/certs/cert.pem ## See: listener.ssl.$name.peer_cert_as_username ## -## Value: cn | dn +## Value: cn | dn | crt ## listener.wss.external.peer_cert_as_username = cn ## TCP backlog for the WebSocket/SSL connection. diff --git a/priv/emqx.schema b/priv/emqx.schema index becb4bff4..1424ab240 100644 --- a/priv/emqx.schema +++ b/priv/emqx.schema @@ -949,7 +949,7 @@ end}. ]}. {mapping, "listener.tcp.$name.peer_cert_as_username", "emqx.listeners", [ - {datatype, {enum, [cn, dn]}} + {datatype, {enum, [cn, dn, crt]}} ]}. {mapping, "listener.tcp.$name.backlog", "emqx.listeners", [ @@ -1139,7 +1139,7 @@ end}. ]}. {mapping, "listener.ssl.$name.peer_cert_as_username", "emqx.listeners", [ - {datatype, {enum, [cn, dn]}} + {datatype, {enum, [cn, dn, crt]}} ]}. %%-------------------------------------------------------------------- @@ -1400,7 +1400,7 @@ end}. ]}. {mapping, "listener.wss.$name.peer_cert_as_username", "emqx.listeners", [ - {datatype, {enum, [cn, dn]}} + {datatype, {enum, [cn, dn, crt]}} ]}. {translation, "emqx.listeners", fun(Conf) -> diff --git a/src/emqx_protocol.erl b/src/emqx_protocol.erl index c3f0689fa..db239acef 100644 --- a/src/emqx_protocol.erl +++ b/src/emqx_protocol.erl @@ -106,9 +106,10 @@ init(#{peername := Peername, peercert := Peercert, sendfun := SendFun}, Options) init_username(Peercert, Options) -> case proplists:get_value(peer_cert_as_username, Options) of - cn -> esockd_peercert:common_name(Peercert); - dn -> esockd_peercert:subject(Peercert); - _ -> undefined + cn -> esockd_peercert:common_name(Peercert); + dn -> esockd_peercert:subject(Peercert); + crt -> Peercert; + _ -> undefined end. set_username(Username, PState = #pstate{username = undefined}) -> From a1092a678473e5eccd16a52d5621a37ffff7aefe Mon Sep 17 00:00:00 2001 From: Gilbert Wong Date: Wed, 24 Oct 2018 15:59:22 +0800 Subject: [PATCH 2/3] Add eunit cases and fix typo. --- src/emqx_protocol.erl | 62 +++++++++++++++++++----------------- test/emqx_protocol_tests.erl | 30 +++++++++++++++++ 2 files changed, 62 insertions(+), 30 deletions(-) create mode 100644 test/emqx_protocol_tests.erl diff --git a/src/emqx_protocol.erl b/src/emqx_protocol.erl index db239acef..fdec62d8b 100644 --- a/src/emqx_protocol.erl +++ b/src/emqx_protocol.erl @@ -33,35 +33,35 @@ -export([shutdown/2]). -record(pstate, { - zone, - sendfun, - peername, - peercert, - proto_ver, - proto_name, - ackprops, - client_id, - is_assigned, - conn_pid, - conn_props, - ack_props, - username, - session, - clean_start, - topic_aliases, - packet_size, - will_topic, - will_msg, - keepalive, - mountpoint, - is_super, - is_bridge, - enable_ban, - enable_acl, - recv_stats, - send_stats, - connected, - connected_at + zone, + sendfun, + peername, + peercert, + proto_ver, + proto_name, + ackprops, + client_id, + is_assigned, + conn_pid, + conn_props, + ack_props, + username, + session, + clean_start, + topic_aliases, + packet_size, + will_topic, + will_msg, + keepalive, + mountpoint, + is_super, + is_bridge, + enable_ban, + enable_acl, + recv_stats, + send_stats, + connected, + connected_at }). -type(state() :: #pstate{}). @@ -71,6 +71,8 @@ -compile(export_all). -endif. +-define(NO_PROPS, undefined). + -define(LOG(Level, Format, Args, PState), emqx_logger:Level([{client, PState#pstate.client_id}], "MQTT(~s@~s): " ++ Format, [PState#pstate.client_id, esockd_net:format(PState#pstate.peername) | Args])). @@ -672,7 +674,7 @@ authenticate(Credentials, Password) -> {error, Error} end. -set_property(Name, Value, undefined) -> +set_property(Name, Value, ?NO_PROPS) -> #{Name => Value}; set_property(Name, Value, Props) -> Props#{Name => Value}. diff --git a/test/emqx_protocol_tests.erl b/test/emqx_protocol_tests.erl new file mode 100644 index 000000000..56b65e36a --- /dev/null +++ b/test/emqx_protocol_tests.erl @@ -0,0 +1,30 @@ +%% Copyright (c) 2018 EMQ Technologies Co., Ltd. All Rights Reserved. +%% +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. + +-module(emqx_protocol_tests). + +-include_lib("eunit/include/eunit.hrl"). + +set_property_test() -> + ?assertEqual(#{test => test_property}, emqx_protocol:set_property(test, test_property, undefined)), + TestMap = #{test => test_property}, + ?assertEqual(#{test => test_property, test1 => test_property2}, + emqx_protocol:set_property(test1, test_property2, TestMap)), + ok. + +init_username_test() -> + ?assertEqual(<<"Peercert">>, + emqx_protocol:init_username(<<"Peercert">>, [{peer_cert_as_username, crt}])), + ?assertEqual(undefined, + emqx_protocol:init_username(undefined, [{peer_cert_as_username, undefined}])). From f7285d5a587915fa78037c03bf088d8704cd9060 Mon Sep 17 00:00:00 2001 From: Gilbert Wong Date: Fri, 26 Oct 2018 17:30:39 +0800 Subject: [PATCH 3/3] Delete ackprops in pstate Prior to this change, ackprops is duplicated with ack_props This change delete ackprops. --- src/emqx_protocol.erl | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/emqx_protocol.erl b/src/emqx_protocol.erl index fdec62d8b..4a784252b 100644 --- a/src/emqx_protocol.erl +++ b/src/emqx_protocol.erl @@ -39,7 +39,6 @@ peercert, proto_ver, proto_name, - ackprops, client_id, is_assigned, conn_pid, @@ -606,10 +605,10 @@ send(Packet = ?PACKET(Type), PState = #pstate{proto_ver = Ver, sendfun = SendFun %%------------------------------------------------------------------------------ %% Assign a clientid -maybe_assign_client_id(PState = #pstate{client_id = <<>>, ackprops = AckProps}) -> +maybe_assign_client_id(PState = #pstate{client_id = <<>>, ack_props = AckProps}) -> ClientId = emqx_guid:to_base62(emqx_guid:gen()), AckProps1 = set_property('Assigned-Client-Identifier', ClientId, AckProps), - PState#pstate{client_id = ClientId, is_assigned = true, ackprops = AckProps1}; + PState#pstate{client_id = ClientId, is_assigned = true, ack_props = AckProps1}; maybe_assign_client_id(PState) -> PState.