Skip to content

Commit 5689b6c

Browse files
author
Claes Wikstrom
committed
Merge branch 'master' of git@github.com:klacke/yaws
2 parents 5c3c546 + 12c9f01 commit 5689b6c

File tree

9 files changed

+212
-118
lines changed

9 files changed

+212
-118
lines changed

.gitignore

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
*.beam
2+
*.o
3+
*.so
4+
yaws.pc
5+
bin/yaws
6+
config.log
7+
config.status
8+
ebin/yaws.app
9+
include.mk
10+
priv/epam
11+
scripts/yaws.conf
12+
src/charset.def
13+
src/mime_types.erl
14+
src/yaws_configure.hrl
15+
test/support/include.mk
16+
test/support/include.sh

include/yaws.hrl

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,6 @@
3232
((GC#gconf.flags band ?GC_AUTH_LOG) /= 0)).
3333
-define(gc_has_copy_errlog(GC),
3434
((GC#gconf.flags band ?GC_COPY_ERRLOG) /= 0)).
35-
-define(gc_has_backwards_compat_parse(GC),
36-
((GC#gconf.flags band ?GC_BACKWARDS_COMPAT_PARSE) /= 0)).
3735
-define(gc_log_has_resolve_hostname(GC),
3836
((GC#gconf.flags band ?GC_LOG_RESOLVE_HOSTNAME) /= 0)).
3937
-define(gc_fail_on_bind_err(GC),
@@ -53,9 +51,6 @@
5351
GC#gconf{flags = yaws:flag(GC#gconf.flags, ?GC_AUTH_LOG, Bool)}).
5452
-define(gc_set_copy_errlog(GC, Bool),
5553
GC#gconf{flags = yaws:flag(GC#gconf.flags, ?GC_COPY_ERRLOG, Bool)}).
56-
-define(gc_set_backwards_compat_parse(GC, Bool),
57-
GC#gconf{flags = yaws:flag(GC#gconf.flags,
58-
?GC_BACKWARDS_COMPAT_PARSE, Bool)}).
5954
-define(gc_log_set_resolve_hostname(GC, Bool),
6055
GC#gconf{flags = yaws:flag(GC#gconf.flags,
6156
?GC_LOG_RESOLVE_HOSTNAME, Bool)}).

man/yaws.conf.5

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -137,15 +137,6 @@ erlang errorlog written to a report.log file.
137137
Default value is true.
138138

139139

140-
.TP
141-
\fBbackwards_compat_parse = true | false\fR
142-
Versions of Yaws > than 1.41 changes the return value
143-
of the parse_query and parse_post functions. Earlier versions
144-
used {Key, Val} where Key was an atom. This made Yaws vulnerable
145-
for DOS attacks. Set this flag to keep the old deprecated
146-
and vulnerable behaviour. In versions > 1.41 the Key is a list/string.
147-
148-
149140
.TP
150141
\fBrunmod = ModuleName \fR
151142
At startup yaws will invoke \fIModuleName:start()\fR in a separate

src/yaws.erl

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -140,8 +140,7 @@ start_embedded(DocRoot, SL, GL, Id) when is_list(DocRoot),is_list(SL),is_list(GL
140140
GC = setup_gconf(GL, yaws_config:make_default_gconf(false, Id)),
141141
SC = setup_sconf(DocRoot, #sconf{}, SL),
142142
yaws_config:add_yaws_soap_srv(GC),
143-
SCs = yaws_config:add_yaws_auth([SC]),
144-
yaws_api:setconf(GC, [SCs]).
143+
yaws_api:setconf(GC, [SC]).
145144

146145
add_server(DocRoot, SL) when is_list(DocRoot),is_list(SL) ->
147146
SC = setup_sconf(DocRoot, #sconf{}, SL),
@@ -1785,11 +1784,17 @@ http_get_headers(CliSock, SSL) ->
17851784
GC#gconf.trace == false ->
17861785
Res;
17871786
is_tuple(Res) ->
1788-
{Request, Headers} = Res,
1789-
ReqStr = yaws_api:reformat_request(Request),
1787+
{RoR, Headers} = Res,
1788+
{RoRStr, From} = case element(1, RoR) of
1789+
http_request ->
1790+
{yaws_api:reformat_request(RoR),
1791+
from_client};
1792+
http_response ->
1793+
{yaws_api:reformat_response(RoR),
1794+
from_server}
1795+
end,
17901796
HStr = headers_to_str(Headers),
1791-
yaws_log:trace_traffic(from_client,
1792-
?F("~n~s~n~s~n",[ReqStr, HStr])),
1797+
yaws_log:trace_traffic(From, ?F("~n~s~n~s~n",[RoRStr, HStr])),
17931798
Res;
17941799
Res == closed ->
17951800
yaws_log:trace_traffic(from_client, "closed\n"),

src/yaws_api.erl

Lines changed: 18 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -351,7 +351,7 @@ do_header(Head) ->
351351
{value, {_,"form-data"++Line}} ->
352352
Parameters = parse_arg_line(Line),
353353
{value, {_,Name}} = lists:keysearch(name, 1, Parameters),
354-
{mkkey(Name), Parameters};
354+
{lists:reverse(Name), Parameters};
355355
_ ->
356356
{Header}
357357
end.
@@ -408,7 +408,7 @@ do_parse_spec(<<$%, Hi:8, Lo:8, Tail/binary>>, Spec, Last, Cur, State)
408408
do_parse_spec(Tail, Spec, Last, [ Hex | Cur], State);
409409

410410
do_parse_spec(<<$&, Tail/binary>>, Spec, _Last , Cur, key) ->
411-
[{mkkey_reverse(Cur), undefined} |
411+
[{lists:reverse(Cur), undefined} |
412412
do_parse_spec(Tail, Spec, nokey, [], key)]; %% cont keymode
413413

414414
do_parse_spec(<<$&, Tail/binary>>, Spec, Last, Cur, value) ->
@@ -420,7 +420,7 @@ do_parse_spec(<<$+, Tail/binary>>, Spec, Last, Cur, State) ->
420420
do_parse_spec(Tail, Spec, Last, [$\s|Cur], State);
421421

422422
do_parse_spec(<<$=, Tail/binary>>, Spec, _Last, Cur, key) ->
423-
do_parse_spec(Tail, Spec, mkkey_reverse(Cur), [], value); %% change mode
423+
do_parse_spec(Tail, Spec, lists:reverse(Cur), [], value); %% change mode
424424

425425
do_parse_spec(<<$%, $u, A:8, B:8,C:8,D:8, Tail/binary>>,
426426
Spec, Last, Cur, State) ->
@@ -431,7 +431,7 @@ do_parse_spec(<<$%, $u, A:8, B:8,C:8,D:8, Tail/binary>>,
431431
do_parse_spec(<<H:8, Tail/binary>>, Spec, Last, Cur, State) ->
432432
do_parse_spec(Tail, Spec, Last, [H|Cur], State);
433433
do_parse_spec(<<>>, _Spec, nokey, Cur, _State) ->
434-
[{mkkey_reverse(Cur), undefined}];
434+
[{lists:reverse(Cur), undefined}];
435435
do_parse_spec(<<>>, Spec, Last, Cur, _State) ->
436436
[S|_Ss] = tail_spec(Spec),
437437
[{Last, coerce_type(S, Cur)}];
@@ -463,19 +463,6 @@ coerce_type(ip, _Str) ->
463463
coerce_type(binary, Str) ->
464464
list_to_binary(lists:reverse(Str)).
465465

466-
mkkey_reverse(S) ->
467-
mkkey(lists:reverse(S)).
468-
mkkey(S) ->
469-
GC=get(gc),
470-
if
471-
?gc_has_backwards_compat_parse(GC) ->
472-
list_to_atom(S);
473-
true ->
474-
S
475-
end.
476-
477-
478-
479466

480467
code_to_phrase(100) -> "Continue";
481468
code_to_phrase(101) -> "Switching Protocols ";
@@ -1022,14 +1009,16 @@ reformat_header(H) ->
10221009

10231010

10241011

1012+
reformat_request(#http_request{method = bad_request}) ->
1013+
["Bad request"];
10251014
reformat_request(Req) ->
10261015
Path = case Req#http_request.path of
1027-
{abs_path, AbsPath} ->
1028-
AbsPath;
1029-
{absoluteURI, _Scheme, _Host0, _Port, RawPath} ->
1030-
RawPath
1031-
end,
1032-
{Maj,Min} = Req#http_request.version,
1016+
{abs_path, AbsPath} ->
1017+
AbsPath;
1018+
{absoluteURI, _Scheme, _Host0, _Port, RawPath} ->
1019+
RawPath
1020+
end,
1021+
{Maj, Min} = Req#http_request.version,
10331022
[yaws:to_list(Req#http_request.method), " ", Path," HTTP/",
10341023
integer_to_list(Maj),".", integer_to_list(Min)].
10351024

@@ -1780,14 +1769,16 @@ setconf(GC0, Groups0, CheckCertsChanged) ->
17801769
true ->
17811770
ok
17821771
end,
1783-
{GC, Groups} = yaws_config:verify_upgrade_args(GC0, Groups0),
1772+
1773+
{GC, Groups1} = yaws_config:verify_upgrade_args(GC0, Groups0),
1774+
Groups2 = lists:map(fun(X) -> yaws_config:add_yaws_auth(X) end, Groups1),
17841775
{ok, OLDGC, OldGroups} = yaws_api:getconf(),
17851776
case {yaws_config:can_hard_gc(GC, OLDGC),
1786-
yaws_config:can_soft_setconf(GC, Groups, OLDGC, OldGroups)} of
1777+
yaws_config:can_soft_setconf(GC, Groups2, OLDGC, OldGroups)} of
17871778
{true, true} ->
1788-
yaws_config:soft_setconf(GC, Groups, OLDGC, OldGroups);
1779+
yaws_config:soft_setconf(GC, Groups2, OLDGC, OldGroups);
17891780
{true, false} when OLDGC == undefined ->
1790-
yaws_config:hard_setconf(GC, Groups);
1781+
yaws_config:hard_setconf(GC, Groups2);
17911782
_ ->
17921783
{error, need_restart}
17931784
end.

src/yaws_config.erl

Lines changed: 124 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -100,16 +100,63 @@ add_yaws_auth(SCs) ->
100100
end, SCs).
101101

102102

103-
%% we search and setup www authenticate for each directory
104-
%% specified as an auth directory. These are merged with server conf.
103+
%% We search and setup www authenticate for each directory
104+
%% specified as an auth directory or containing a .yaws_auth file.
105+
%% These are merged with server conf.
105106

106107
setup_auth(SC) ->
107-
lists:flatten(
108-
lists:map(fun(Auth) ->
109-
add_yaws_auth(SC, Auth#auth.dir, Auth)
110-
end, SC#sconf.authdirs)).
108+
Auth_dirs0 = get_yaws_auth_dirs(SC#sconf.docroot),
109+
110+
%% create new auth records
111+
Auth_dirs1 = [#auth{dir = [X]} || X <- Auth_dirs0],
112+
Auth_dirs2 = Auth_dirs1 ++ SC#sconf.authdirs,
113+
114+
%% load the .yaws_auth files and parse them
115+
Auth_dirs3 = load_yaws_auth_file(SC, Auth_dirs2, []),
116+
117+
start_pam(Auth_dirs3),
118+
Auth_dirs3.
119+
120+
121+
122+
%% Call get_yaws_auth_dirs/3 with default values and then
123+
%% strip leading docroot from dir
124+
get_yaws_auth_dirs(undefined) ->
125+
[];
126+
get_yaws_auth_dirs(Docroot) ->
127+
{ok, FileList} = file:list_dir(Docroot),
128+
Auth_dirs = get_yaws_auth_dirs(Docroot ++ "/", FileList, []),
129+
Len = string:len(Docroot),
130+
[string:sub_string(X, Len+1) || X <- Auth_dirs].
131+
132+
133+
134+
%% Bottom of recursion, we have searched all the entries in this directory
135+
get_yaws_auth_dirs(_Docroot, [], AuthDirs) ->
136+
AuthDirs;
137+
138+
%% Recursivly search the docroot for any dir that contains .yaws_auth
139+
%% and return it
140+
get_yaws_auth_dirs(Docroot, [File|T], AuthDirs0) ->
141+
Path = string:concat(Docroot, File),
142+
case filelib:is_dir(Path) of
143+
true ->
144+
{ok, FileList} = file:list_dir(Path),
145+
AuthDirs1 = get_yaws_auth_dirs(Path ++ "/", FileList, AuthDirs0),
146+
get_yaws_auth_dirs(Docroot, T, AuthDirs1);
147+
false ->
148+
case File of
149+
".yaws_auth" ->
150+
get_yaws_auth_dirs(Docroot, T, [Docroot|AuthDirs0]);
151+
_ ->
152+
get_yaws_auth_dirs(Docroot, T, AuthDirs0)
153+
end
154+
end.
111155

112-
add_yaws_auth(SC, Dirs, A) ->
156+
start_pam([]) ->
157+
ok;
158+
159+
start_pam([{_Dir, A}|T]) ->
113160
PamStarted = whereis(yaws_pam) /= undefined,
114161
if
115162
A#auth.pam == false ->
@@ -124,28 +171,75 @@ add_yaws_auth(SC, Dirs, A) ->
124171
true ->
125172
ok
126173
end,
127-
128-
lists:map(
129-
fun(Dir) ->
130-
FN=[SC#sconf.docroot , [$/|Dir], [$/|".yaws_auth"]],
131-
case file:consult(FN) of
132-
{ok, [{realm, Realm} |TermList]} ->
133-
error_logger:info_msg("Reading .yaws_auth ~s~n",[FN]),
134-
{Dir, A#auth{realm = Realm,
135-
users = TermList++A#auth.users}};
136-
{ok, TermList} ->
137-
error_logger:info_msg("Reading .yaws_auth ~s~n",[FN]),
138-
{Dir, A#auth{users = TermList++A#auth.users}};
139-
{error, enoent} ->
140-
{Dir, A};
141-
_Err ->
142-
error_logger:format("Bad .yaws_auth file in dir ~p~n",
143-
[Dir]),
144-
{Dir, A}
145-
end
146-
end, Dirs).
147-
148-
174+
start_pam(T).
175+
176+
177+
178+
load_yaws_auth_file(_SC, [], Acc) ->
179+
Acc;
180+
181+
%% Load the .yaws_auth file if it exists and parse it,
182+
%% adding the result to the record
183+
load_yaws_auth_file(SC, [A|T], Acc) ->
184+
[Dir] = A#auth.dir,
185+
FN0=[SC#sconf.docroot, [$/|Dir], [$/|".yaws_auth"]],
186+
FN1 = remove_multiple_slash(lists:flatten(FN0)),
187+
A2 = case file:consult(FN1) of
188+
{ok, TermList} ->
189+
error_logger:info_msg("Reading .yaws_auth ~s~n",[FN1]),
190+
Auth = parse_yaws_auth_file(TermList, A),
191+
[Dir1] = Auth#auth.dir,
192+
{Dir1, Auth};
193+
{error, enoent} ->
194+
{Dir, A};
195+
_Err ->
196+
error_logger:format("Bad .yaws_auth file in dir ~p~n",
197+
[Dir]),
198+
{Dir, A}
199+
end,
200+
load_yaws_auth_file(SC, T, [A2|Acc]).
201+
202+
parse_yaws_auth_file([], Auth0) ->
203+
Realm = Auth0#auth.realm,
204+
Headers = Auth0#auth.headers ++ yaws:make_www_authenticate_header({realm, Realm}),
205+
Auth0#auth{headers = Headers};
206+
207+
parse_yaws_auth_file([{realm, Realm}|T], Auth0) ->
208+
parse_yaws_auth_file(T, Auth0#auth{realm = Realm});
209+
210+
parse_yaws_auth_file([{pam, Pam}|T], Auth0)
211+
when is_atom(Pam) ->
212+
parse_yaws_auth_file(T, Auth0#auth{pam = Pam});
213+
214+
parse_yaws_auth_file([{authmod, Authmod0}|T], Auth0)
215+
when is_atom(Authmod0)->
216+
code:ensure_loaded(Authmod0),
217+
%% Add the auth header for the mod
218+
Headers = Authmod0:get_header() ++ Auth0#auth.headers,
219+
parse_yaws_auth_file(T, Auth0#auth{mod = Authmod0, headers = Headers});
220+
221+
parse_yaws_auth_file([{file, File}|T], Auth0) ->
222+
[Dir] = Auth0#auth.dir,
223+
New_dir = [Dir ++ File],
224+
parse_yaws_auth_file(T, Auth0#auth{dir = New_dir});
225+
226+
parse_yaws_auth_file([{User, Password}|T], Auth0)
227+
when is_list(User), is_list(Password) ->
228+
Users = [{User, Password}|Auth0#auth.users],
229+
parse_yaws_auth_file(T, Auth0#auth{users = Users}).
230+
231+
232+
%% Replace all "//" and "///" with "/"
233+
%% Might be a better way to do this
234+
remove_multiple_slash(L) ->
235+
remove_multiple_slash(L, []).
236+
237+
remove_multiple_slash([], Acc)->
238+
lists:reverse(Acc);
239+
remove_multiple_slash("//" ++ T, Acc) ->
240+
remove_multiple_slash([$/|T], Acc);
241+
remove_multiple_slash([H|T], Acc) ->
242+
remove_multiple_slash(T, [H|Acc]).
149243

150244
%% This is the function that arranges sconfs into
151245
%% different server groups
@@ -584,14 +678,6 @@ fload(FD, globals, GC, C, Cs, Lno, Chars) ->
584678
{error, ?F("Expect true|false at line ~w", [Lno])}
585679
end;
586680

587-
["backwards_compat_parse", '=', Bool] ->
588-
case is_bool(Bool) of
589-
{true, Val} ->
590-
fload(FD, globals, ?gc_set_backwards_compat_parse(GC, Val),
591-
C, Cs, Lno+1, Next);
592-
false ->
593-
{error, ?F("Expect true|false at line ~w", [Lno])}
594-
end;
595681

596682
["auth_log", '=', Bool] ->
597683
case is_bool(Bool) of
@@ -1060,9 +1146,9 @@ fload(FD, server_auth, GC, C, Cs, Lno, Chars, Auth) ->
10601146
A2 = Auth#auth{realm = Realm},
10611147
fload(FD, server_auth, GC, C, Cs, Lno+1, Next, A2);
10621148
["authmod", '=', Mod] ->
1063-
%% Add the auth header for the mod
10641149
Mod2 = list_to_atom(Mod),
10651150
code:ensure_loaded(Mod2),
1151+
%% Add the auth header for the mod
10661152
H = Mod2:get_header() ++ Auth#auth.headers,
10671153
A2 = Auth#auth{mod = Mod2, headers = H},
10681154
fload(FD, server_auth, GC, C, Cs, Lno+1, Next, A2);
@@ -1560,9 +1646,6 @@ find_sc(_SC,[]) ->
15601646
false.
15611647

15621648

1563-
1564-
1565-
15661649
verify_upgrade_args(GC, Groups0) when is_record(GC, gconf) ->
15671650
case is_groups(Groups0) of
15681651
true ->

0 commit comments

Comments
 (0)