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..4a784252b 100644 --- a/src/emqx_protocol.erl +++ b/src/emqx_protocol.erl @@ -33,35 +33,34 @@ -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, + 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 +70,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])). @@ -106,9 +107,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}) -> @@ -603,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. @@ -671,7 +673,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}])).