Skip to content

Commit 586d96c

Browse files
committed
Merge pull request #167 from esl/zlib_cwe400_fix
Add zlib inflated data size limit
2 parents 3ed283f + 8ee494b commit 586d96c

11 files changed

+141
-111
lines changed

apps/ejabberd/c_src/ejabberd_zlib_drv.c

+79-72
Original file line numberDiff line numberDiff line change
@@ -109,79 +109,86 @@ static void ejabberd_zlib_drv_stop(ErlDrvData handle)
109109

110110

111111
static ErlDrvSSizeT ejabberd_zlib_drv_control(ErlDrvData handle,
112-
unsigned int command,
113-
char *buf, ErlDrvSizeT len,
114-
char **rbuf, ErlDrvSizeT rlen)
112+
unsigned int command,
113+
char *buf, ErlDrvSizeT len,
114+
char **rbuf, ErlDrvSizeT rlen)
115115
{
116-
ejabberd_zlib_data *d = (ejabberd_zlib_data *)handle;
117-
int err;
118-
int size;
119-
ErlDrvBinary *b;
120-
121-
switch (command)
122-
{
123-
case DEFLATE:
124-
size = BUF_SIZE + 1;
125-
rlen = 1;
126-
b = driver_alloc_binary(size);
127-
b->orig_bytes[0] = 0;
128-
129-
d->d_stream->next_in = (unsigned char *)buf;
130-
d->d_stream->avail_in = len;
131-
d->d_stream->avail_out = 0;
132-
err = Z_OK;
133-
134-
while (err == Z_OK && d->d_stream->avail_out == 0)
135-
{
136-
d->d_stream->next_out = (unsigned char *)b->orig_bytes + rlen;
137-
d->d_stream->avail_out = BUF_SIZE;
138-
139-
err = deflate(d->d_stream, Z_SYNC_FLUSH);
140-
die_unless((err == Z_OK) || (err == Z_STREAM_END),
141-
"Deflate error");
142-
143-
rlen += (BUF_SIZE - d->d_stream->avail_out);
144-
size += (BUF_SIZE - d->d_stream->avail_out);
145-
b = driver_realloc_binary(b, size);
146-
}
147-
b = driver_realloc_binary(b, rlen);
148-
*rbuf = (char *)b;
149-
return rlen;
150-
case INFLATE:
151-
size = BUF_SIZE + 1;
152-
rlen = 1;
153-
b = driver_alloc_binary(size);
154-
b->orig_bytes[0] = 0;
155-
156-
if (len > 0) {
157-
d->i_stream->next_in = (unsigned char *)buf;
158-
d->i_stream->avail_in = len;
159-
d->i_stream->avail_out = 0;
160-
err = Z_OK;
161-
162-
while (err == Z_OK && d->i_stream->avail_out == 0)
163-
{
164-
d->i_stream->next_out = (unsigned char *)b->orig_bytes + rlen;
165-
d->i_stream->avail_out = BUF_SIZE;
166-
167-
err = inflate(d->i_stream, Z_SYNC_FLUSH);
168-
die_unless((err == Z_OK) || (err == Z_STREAM_END),
169-
"Inflate error");
170-
171-
rlen += (BUF_SIZE - d->i_stream->avail_out);
172-
size += (BUF_SIZE - d->i_stream->avail_out);
173-
b = driver_realloc_binary(b, size);
174-
}
175-
}
176-
b = driver_realloc_binary(b, rlen);
177-
*rbuf = (char *)b;
178-
return rlen;
179-
}
180-
181-
b = driver_alloc_binary(1);
182-
b->orig_bytes[0] = 0;
183-
*rbuf = (char *)b;
184-
return 1;
116+
ejabberd_zlib_data *d = (ejabberd_zlib_data *)handle;
117+
int err;
118+
int size;
119+
int size_limit;
120+
ErlDrvBinary *b;
121+
122+
// operation is in command's 2 lower bits and size_limit is in bits higher than 1
123+
size_limit = command >> 2; // applies only to inflation
124+
command = command & 3;
125+
switch (command)
126+
{
127+
case DEFLATE:
128+
size = BUF_SIZE + 1;
129+
rlen = 1;
130+
b = driver_alloc_binary(size);
131+
b->orig_bytes[0] = 0;
132+
133+
d->d_stream->next_in = (unsigned char *)buf;
134+
d->d_stream->avail_in = len;
135+
d->d_stream->avail_out = 0;
136+
err = Z_OK;
137+
138+
while (err == Z_OK && d->d_stream->avail_out == 0)
139+
{
140+
d->d_stream->next_out = (unsigned char *)b->orig_bytes + rlen;
141+
d->d_stream->avail_out = BUF_SIZE;
142+
143+
err = deflate(d->d_stream, Z_SYNC_FLUSH);
144+
die_unless((err == Z_OK) || (err == Z_STREAM_END),
145+
"deflate_error");
146+
147+
rlen += (BUF_SIZE - d->d_stream->avail_out);
148+
size += (BUF_SIZE - d->d_stream->avail_out);
149+
b = driver_realloc_binary(b, size);
150+
}
151+
b = driver_realloc_binary(b, rlen);
152+
*rbuf = (char *)b;
153+
return rlen;
154+
case INFLATE:
155+
size = BUF_SIZE + 1;
156+
rlen = 1;
157+
b = driver_alloc_binary(size);
158+
b->orig_bytes[0] = 0;
159+
160+
if (len > 0) {
161+
d->i_stream->next_in = (unsigned char *)buf;
162+
d->i_stream->avail_in = len;
163+
d->i_stream->avail_out = 0;
164+
err = Z_OK;
165+
166+
while (err == Z_OK && d->i_stream->avail_out == 0)
167+
{
168+
d->i_stream->next_out = (unsigned char *)b->orig_bytes + rlen;
169+
d->i_stream->avail_out = BUF_SIZE;
170+
171+
err = inflate(d->i_stream, Z_SYNC_FLUSH);
172+
die_unless((err == Z_OK) || (err == Z_STREAM_END),
173+
"inflate_error");
174+
175+
rlen += (BUF_SIZE - d->i_stream->avail_out);
176+
die_unless((rlen < size_limit) || (size_limit == 0),
177+
"inflate_size_exceeded");
178+
179+
size += (BUF_SIZE - d->i_stream->avail_out);
180+
b = driver_realloc_binary(b, size);
181+
}
182+
}
183+
b = driver_realloc_binary(b, rlen);
184+
*rbuf = (char *)b;
185+
return rlen;
186+
}
187+
188+
b = driver_alloc_binary(1);
189+
b->orig_bytes[0] = 0;
190+
*rbuf = (char *)b;
191+
return 1;
185192
}
186193

187194

apps/ejabberd/src/ejabberd_c2s.erl

+20-18
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@
8080
sasl_state,
8181
access,
8282
shaper,
83-
zlib = false,
83+
zlib = {false, 0},
8484
tls = false,
8585
tls_required = false,
8686
tls_enabled = false,
@@ -161,8 +161,8 @@ get_presence(FsmRef) ->
161161
?GEN_FSM:sync_send_all_state_event(FsmRef, get_presence, 1000).
162162

163163
get_aux_field(Key, #state{aux_fields = Opts}) ->
164-
case lists:keysearch(Key, 1, Opts) of
165-
{value, {_, Val}} ->
164+
case lists:keyfind(Key, 1, Opts) of
165+
{_, Val} ->
166166
{ok, Val};
167167
_ ->
168168
error
@@ -208,20 +208,23 @@ stop(FsmRef) ->
208208
%% {stop, StopReason}
209209
%%----------------------------------------------------------------------
210210
init([{SockMod, Socket}, Opts]) ->
211-
Access = case lists:keysearch(access, 1, Opts) of
212-
{value, {_, A}} -> A;
211+
Access = case lists:keyfind(access, 1, Opts) of
212+
{_, A} -> A;
213213
_ -> all
214214
end,
215-
Shaper = case lists:keysearch(shaper, 1, Opts) of
216-
{value, {_, S}} -> S;
215+
Shaper = case lists:keyfind(shaper, 1, Opts) of
216+
{_, S} -> S;
217217
_ -> none
218218
end,
219219
XMLSocket =
220-
case lists:keysearch(xml_socket, 1, Opts) of
221-
{value, {_, XS}} -> XS;
220+
case lists:keyfind(xml_socket, 1, Opts) of
221+
{_, XS} -> XS;
222222
_ -> false
223223
end,
224-
Zlib = lists:member(zlib, Opts),
224+
Zlib = case lists:keyfind(zlib, 1, Opts) of
225+
{_, ZlibLimit} -> {true, ZlibLimit};
226+
_ -> {false, 0}
227+
end,
225228
StartTLS = lists:member(starttls, Opts),
226229
StartTLSRequired = lists:member(starttls_required, Opts),
227230
TLSEnabled = lists:member(tls, Opts),
@@ -328,7 +331,7 @@ wait_for_stream({xmlstreamstart, _Name, Attrs}, StateData) ->
328331
SockMod =
329332
(StateData#state.sockmod):get_sockmod(
330333
StateData#state.socket),
331-
Zlib = StateData#state.zlib,
334+
{Zlib, _} = StateData#state.zlib,
332335
CompressFeature =
333336
case Zlib andalso
334337
((SockMod == gen_tcp) orelse
@@ -469,8 +472,7 @@ wait_for_stream(closed, StateData) ->
469472
wait_for_auth({xmlstreamelement, El}, StateData) ->
470473
case is_auth_packet(El) of
471474
{auth, _ID, get, {U, _, _, _}} ->
472-
XE = #xmlel{name = Name,
473-
attrs = Attrs} = jlib:make_result_iq_reply(El),
475+
XE = jlib:make_result_iq_reply(El),
474476
case U of
475477
<<>> ->
476478
UCdata = [];
@@ -613,7 +615,7 @@ wait_for_auth(closed, StateData) ->
613615

614616
wait_for_feature_request({xmlstreamelement, El}, StateData) ->
615617
#xmlel{name = Name, attrs = Attrs, children = Els} = El,
616-
Zlib = StateData#state.zlib,
618+
{Zlib, ZlibLimit} = StateData#state.zlib,
617619
TLS = StateData#state.tls,
618620
TLSEnabled = StateData#state.tls_enabled,
619621
TLSRequired = StateData#state.tls_required,
@@ -705,7 +707,7 @@ wait_for_feature_request({xmlstreamelement, El}, StateData) ->
705707
<<"zlib">> ->
706708
Socket = StateData#state.socket,
707709
ZlibSocket = (StateData#state.sockmod):compress(
708-
Socket,
710+
Socket, ZlibLimit,
709711
xml:element_to_binary(
710712
#xmlel{name = <<"compressed">>,
711713
attrs = [{<<"xmlns">>, ?NS_COMPRESS}]})),
@@ -979,7 +981,7 @@ session_established({xmlstreamend, _Name}, StateData) ->
979981
send_trailer(StateData),
980982
{stop, normal, StateData};
981983

982-
session_established({xmlstreamerror, "XML stanza is too big" = E}, StateData) ->
984+
session_established({xmlstreamerror, <<"XML stanza is too big">> = E}, StateData) ->
983985
send_element(StateData, ?POLICY_VIOLATION_ERR(StateData#state.lang, E)),
984986
send_trailer(StateData),
985987
{stop, normal, StateData};
@@ -2217,8 +2219,8 @@ check_from(El, FromJID) ->
22172219
end.
22182220

22192221
fsm_limit_opts(Opts) ->
2220-
case lists:keysearch(max_fsm_queue, 1, Opts) of
2221-
{value, {_, N}} when is_integer(N) ->
2222+
case lists:keyfind(max_fsm_queue, 1, Opts) of
2223+
{_, N} when is_integer(N) ->
22222224
[{max_queue, N}];
22232225
_ ->
22242226
case ejabberd_config:get_local_option(max_fsm_queue) of

apps/ejabberd/src/ejabberd_receiver.erl

+10-2
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@
5656
timeout}).
5757

5858
-define(HIBERNATE_TIMEOUT, 90000).
59+
-define(GEN_FSM, p1_fsm).
5960

6061
%%====================================================================
6162
%% API
@@ -160,7 +161,10 @@ handle_call({compress, ZlibSocket}, _From,
160161
case ejabberd_zlib:recv_data(ZlibSocket, "") of
161162
{ok, ZlibData} ->
162163
{reply, ok, process_data(ZlibData, NewState), ?HIBERNATE_TIMEOUT};
163-
{error, _Reason} ->
164+
{error, inflate_size_exceeded} ->
165+
?GEN_FSM:send_event(C2SPid, {xmlstreamerror, <<"XML stanza is too big">>}),
166+
{reply, ok, NewState, ?HIBERNATE_TIMEOUT};
167+
{error, inflate_error} ->
164168
{stop, normal, ok, NewState}
165169
end;
166170
handle_call(reset_stream, _From,
@@ -205,6 +209,7 @@ handle_cast(_Msg, State) ->
205209
%%--------------------------------------------------------------------
206210
handle_info({Tag, _TCPSocket, Data},
207211
#state{socket = Socket,
212+
c2s_pid = C2SPid,
208213
sock_mod = SockMod} = State)
209214
when (Tag == tcp) or (Tag == ssl) or (Tag == ejabberd_xml) ->
210215
case SockMod of
@@ -221,7 +226,10 @@ handle_info({Tag, _TCPSocket, Data},
221226
{ok, ZlibData} ->
222227
{noreply, process_data(ZlibData, State),
223228
?HIBERNATE_TIMEOUT};
224-
{error, _Reason} ->
229+
{error, inflate_size_exceeded} ->
230+
?GEN_FSM:send_event(C2SPid, {xmlstreamerror, <<"XML stanza is too big">>}),
231+
{noreply, State, ?HIBERNATE_TIMEOUT};
232+
{error, inflate_error} ->
225233
{stop, normal, State}
226234
end;
227235
_ ->

apps/ejabberd/src/ejabberd_socket.erl

+4-3
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@
3434
starttls/2,
3535
starttls/3,
3636
compress/1,
37-
compress/2,
37+
compress/3,
3838
reset_stream/1,
3939
send/2,
4040
send_xml/2,
@@ -152,10 +152,11 @@ compress(SocketData) ->
152152
ejabberd_receiver:compress(SocketData#socket_state.receiver, ZlibSocket),
153153
SocketData#socket_state{socket = ZlibSocket, sockmod = ejabberd_zlib}.
154154

155-
compress(SocketData, Data) ->
155+
compress(SocketData, InflateSizeLimit, Data) ->
156156
{ok, ZlibSocket} = ejabberd_zlib:enable_zlib(
157157
SocketData#socket_state.sockmod,
158-
SocketData#socket_state.socket),
158+
SocketData#socket_state.socket,
159+
InflateSizeLimit),
159160
ejabberd_receiver:compress(SocketData#socket_state.receiver, ZlibSocket),
160161
send(SocketData, Data),
161162
SocketData#socket_state{socket = ZlibSocket, sockmod = ejabberd_zlib}.

apps/ejabberd/src/ejabberd_zlib.erl

+11-8
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@
3030
-behaviour(gen_server).
3131

3232
-export([start/0, start_link/0,
33-
enable_zlib/2, disable_zlib/1,
33+
enable_zlib/3, disable_zlib/1,
3434
send/2,
3535
recv/2, recv/3, recv_data/2,
3636
setopts/2,
@@ -50,7 +50,7 @@
5050
-define(DEFLATE, 1).
5151
-define(INFLATE, 2).
5252

53-
-record(zlibsock, {sockmod, socket, zlibport}).
53+
-record(zlibsock, {sockmod, socket, zlibport, inflate_size_limit = 0}).
5454

5555
start() ->
5656
gen_server:start({local, ?MODULE}, ?MODULE, [], []).
@@ -94,13 +94,14 @@ terminate(_Reason, Port) ->
9494
ok.
9595

9696

97-
enable_zlib(SockMod, Socket) ->
97+
enable_zlib(SockMod, Socket, InflateSizeLimit) ->
9898
case erl_ddll:load_driver(ejabberd:get_so_path(), ejabberd_zlib_drv) of
9999
ok -> ok;
100100
{error, already_loaded} -> ok
101101
end,
102102
Port = open_port({spawn, ejabberd_zlib_drv}, [binary]),
103-
{ok, #zlibsock{sockmod = SockMod, socket = Socket, zlibport = Port}}.
103+
{ok, #zlibsock{sockmod = SockMod, socket = Socket, zlibport = Port,
104+
inflate_size_limit = InflateSizeLimit}}.
104105

105106
disable_zlib(#zlibsock{sockmod = SockMod, socket = Socket, zlibport = Port}) ->
106107
port_close(Port),
@@ -138,12 +139,12 @@ recv_data2(ZlibSock, Packet) ->
138139
Res
139140
end.
140141

141-
recv_data1(#zlibsock{zlibport = Port} = _ZlibSock, Packet) ->
142-
case port_control(Port, ?INFLATE, Packet) of
142+
recv_data1(#zlibsock{zlibport = Port, inflate_size_limit = SizeLimit} = _ZlibSock, Packet) ->
143+
case port_control(Port, SizeLimit bsl 2 + ?INFLATE, Packet) of
143144
<<0, In/binary>> ->
144145
{ok, In};
145146
<<1, Error/binary>> ->
146-
{error, binary_to_list(Error)}
147+
{error, erlang:binary_to_existing_atom(Error, utf8)}
147148
end.
148149

149150
send(#zlibsock{sockmod = SockMod, socket = Socket, zlibport = Port},
@@ -152,7 +153,9 @@ send(#zlibsock{sockmod = SockMod, socket = Socket, zlibport = Port},
152153
<<0, Out/binary>> ->
153154
SockMod:send(Socket, Out);
154155
<<1, Error/binary>> ->
155-
{error, binary_to_list(Error)}
156+
{error, erlang:binary_to_existing_atom(Error, utf8)};
157+
_ ->
158+
{error, deflate_error}
156159
end.
157160

158161

0 commit comments

Comments
 (0)