forked from cms-sw/cmsdist
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathbigcouch-add-cmsauth-to-chttpd.patch
142 lines (133 loc) · 5.86 KB
/
bigcouch-add-cmsauth-to-chttpd.patch
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
--- src/chttpd.erl.orig 2014-03-18 15:28:42.000000000 +0100
+++ src/chttpd.erl 2014-03-20 11:36:39.000000000 +0100
@@ -136,7 +136,6 @@
handle_request(MochiReq) ->
Begin = now(),
-
case couch_config:get("chttpd", "socket_options") of
undefined ->
ok;
@@ -145,9 +144,13 @@
ok = mochiweb_socket:setopts(MochiReq:get(socket), SocketOpts)
end,
+ % Keep only CMS authentication handlers as they were designed
+ % for all the agreed cms use cases. Besides, we don't want
+ % people trying to do any sort of password based authentication,
+ % nor to delegate it to third-parties.
AuthenticationFuns = [
- fun couch_httpd_auth:cookie_authentication_handler/1,
- fun couch_httpd_auth:default_authentication_handler/1
+ fun couch_cms_auth:cms_host_authentication_handler/1,
+ fun couch_cms_auth:cms_backend_authentication_handler/1
],
% for the path, use the raw path with the query string and fragment
@@ -198,13 +201,13 @@
HttpReq = #httpd{
mochi_req = MochiReq,
+ peer = Peer,
method = Method,
path_parts = [list_to_binary(chttpd:unquote(Part))
|| Part <- string:tokens(Path, "/")],
db_url_handlers = db_url_handlers(),
design_url_handlers = design_url_handlers()
},
-
% put small token on heap to keep requests synced to backend calls
erlang:put(nonce, couch_util:to_hex(crypto:rand_bytes(4))),
@@ -212,6 +215,8 @@
try
case authenticate_request(HttpReq, AuthenticationFuns) of
#httpd{} = Req ->
+ % After authenticating, check for authorization before proceeding
+ check_authorization(HandlerKey, Req),
HandlerFun = url_handler(HandlerKey),
HandlerFun(possibly_hack(Req));
Response ->
@@ -311,27 +316,57 @@
%%% end hack
+% Redefine the _session API handler to allow read requests only. This is
+% required for futon to display information about the authenticated user.
+% Write calls to this api are disabled to avoid people wrongly flying their
+% passwords and other sensitive information even though the CMS security
+% model is passwordless.
+handle_session_req(#httpd{method='GET', user_ctx=UserCtx}=Req) ->
+ Name = UserCtx#user_ctx.name,
+ case Name of
+ null ->
+ throw({unauthorized, <<"Authentication required.">>});
+ Name ->
+ send_json(Req, {[
+ % remove this ok
+ {ok, true},
+ {<<"userCtx">>, {[
+ {name, Name},
+ {roles, UserCtx#user_ctx.roles}
+ ]}},
+ {<<"info">>, {[
+ {authentication_db, none},
+ {authentication_handlers, [cms_host_authentication_handler, cms_backend_authentication_handler]},
+ {authenticated, cms_any}
+ ]}}
+ ]})
+ end;
+handle_session_req(Req) ->
+ send_method_not_allowed(Req, "GET").
+
+
% Try authentication handlers in order until one returns a result
authenticate_request(#httpd{user_ctx=#user_ctx{}} = Req, _AuthFuns) ->
Req;
authenticate_request(#httpd{} = Req, [AuthFun|Rest]) ->
authenticate_request(AuthFun(Req), Rest);
authenticate_request(#httpd{} = Req, []) ->
- case couch_config:get("chttpd", "require_valid_user", "false") of
- "true" ->
- throw({unauthorized, <<"Authentication required.">>});
- "false" ->
- case couch_config:get("admins") of
- [] ->
- Ctx = #user_ctx{roles=[<<"_reader">>, <<"_writer">>, <<"_admin">>]},
- Req#httpd{user_ctx = Ctx};
- _ ->
- Req#httpd{user_ctx=#user_ctx{}}
- end
- end;
+ throw({unauthorized, <<"Authentication required.">>});
authenticate_request(Response, _AuthFuns) ->
Response.
+% We require the admin role for accessing the following APIs.
+% The replicate API requires protection too, otherwise it can
+% be used to create new local databases even though the default
+% couchdb api for that requires admin rights.
+check_authorization("_replicate", #httpd{} = Req) ->
+ couch_httpd:verify_is_server_admin(Req);
+check_authorization("_sleep", #httpd{} = Req) ->
+ couch_httpd:verify_is_server_admin(Req);
+check_authorization("_restart", #httpd{} = Req) ->
+ couch_httpd:verify_is_server_admin(Req);
+check_authorization(_, _) -> ok.
+
increment_method_stats(Method) ->
couch_stats_collector:increment({httpd_request_methods, Method}).
@@ -340,13 +375,18 @@
url_handler("_utils") -> fun chttpd_misc:handle_utils_dir_req/1;
url_handler("_all_dbs") -> fun chttpd_misc:handle_all_dbs_req/1;
url_handler("_active_tasks") -> fun chttpd_misc:handle_task_status_req/1;
-url_handler("_config") -> fun chttpd_misc:handle_config_req/1;
+% Disabling _config as it an obvious security flaw to provide an API
+% that shows all the internal configuration, including any passwords/keys
+% that have been defined ;-).
+%url_handler("_config") -> fun chttpd_misc:handle_config_req/1;
url_handler("_replicate") -> fun chttpd_misc:handle_replicate_req/1;
url_handler("_uuids") -> fun chttpd_misc:handle_uuids_req/1;
url_handler("_log") -> fun chttpd_misc:handle_log_req/1;
url_handler("_sleep") -> fun chttpd_misc:handle_sleep_req/1;
-url_handler("_session") -> fun couch_httpd_auth:handle_session_req/1;
-url_handler("_oauth") -> fun couch_httpd_oauth:handle_oauth_req/1;
+% Redefine session API to use our own handler
+url_handler("_session") -> fun handle_session_req/1;
+% Disabling oauth, we don't trust any third-party for authentication
+%url_handler("_oauth") -> fun couch_httpd_oauth:handle_oauth_req/1;
%% showroom_http module missing in bigcouch
url_handler("_restart") -> fun showroom_http:handle_restart_req/1;
url_handler("_membership") -> fun mem3_httpd:handle_membership_req/1;