Line 0
Link Here
|
|
|
1 |
/* Licensed to the Apache Software Foundation (ASF) under one or more |
2 |
* contributor license agreements. See the NOTICE file distributed with |
3 |
* this work for additional information regarding copyright ownership. |
4 |
* The ASF licenses this file to You under the Apache License, Version 2.0 |
5 |
* (the "License"); you may not use this file except in compliance with |
6 |
* the License. You may obtain a copy of the License at |
7 |
* |
8 |
* http://www.apache.org/licenses/LICENSE-2.0 |
9 |
* |
10 |
* Unless required by applicable law or agreed to in writing, software |
11 |
* distributed under the License is distributed on an "AS IS" BASIS, |
12 |
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
13 |
* See the License for the specific language governing permissions and |
14 |
* limitations under the License. |
15 |
*/ |
16 |
|
17 |
#include "apr_strings.h" |
18 |
#define APR_WANT_STRFUNC /* for strcasecmp */ |
19 |
#include "apr_want.h" |
20 |
|
21 |
#include "ap_config.h" |
22 |
#include "httpd.h" |
23 |
#include "http_config.h" |
24 |
#include "http_core.h" |
25 |
#include "http_log.h" |
26 |
#include "http_protocol.h" |
27 |
#include "http_request.h" |
28 |
#include "ap_provider.h" |
29 |
|
30 |
#include "mod_auth.h" |
31 |
#include "mod_ssl.h" |
32 |
|
33 |
typedef struct { |
34 |
authn_provider_list *providers; |
35 |
char *dir; |
36 |
int authoritative; |
37 |
} auth_cert_config_rec; |
38 |
|
39 |
static void *create_auth_cert_dir_config(apr_pool_t *p, char *d) |
40 |
{ |
41 |
auth_cert_config_rec *conf = apr_pcalloc(p, sizeof(*conf)); |
42 |
|
43 |
conf->dir = d; |
44 |
/* Any failures are fatal. */ |
45 |
conf->authoritative = 1; |
46 |
|
47 |
return conf; |
48 |
} |
49 |
|
50 |
static const char *add_authn_provider(cmd_parms *cmd, void *config, |
51 |
const char *arg) |
52 |
{ |
53 |
auth_cert_config_rec *conf = (auth_cert_config_rec*)config; |
54 |
authn_provider_list *newp; |
55 |
|
56 |
newp = apr_pcalloc(cmd->pool, sizeof(authn_provider_list)); |
57 |
newp->provider_name = apr_pstrdup(cmd->pool, arg); |
58 |
|
59 |
/* lookup and cache the actual provider now */ |
60 |
newp->provider = ap_lookup_provider(AUTHN_PROVIDER_GROUP, |
61 |
newp->provider_name, "0"); |
62 |
|
63 |
if (newp->provider == NULL) { |
64 |
/* by the time they use it, the provider should be loaded and |
65 |
registered with us. */ |
66 |
return apr_psprintf(cmd->pool, |
67 |
"Unknown Authn provider: %s", |
68 |
newp->provider_name); |
69 |
} |
70 |
|
71 |
if (!newp->provider->check_certificate) { |
72 |
/* if it doesn't provide the appropriate function, reject it */ |
73 |
return apr_psprintf(cmd->pool, |
74 |
"The '%s' Authn provider doesn't support " |
75 |
"Certificate Authentication", newp->provider_name); |
76 |
} |
77 |
|
78 |
/* Add it to the list now. */ |
79 |
if (!conf->providers) { |
80 |
conf->providers = newp; |
81 |
} |
82 |
else { |
83 |
authn_provider_list *last = conf->providers; |
84 |
|
85 |
while (last->next) { |
86 |
last = last->next; |
87 |
} |
88 |
last->next = newp; |
89 |
} |
90 |
|
91 |
return NULL; |
92 |
} |
93 |
|
94 |
static const command_rec auth_cert_cmds[] = |
95 |
{ |
96 |
AP_INIT_ITERATE("AuthCertificateProvider", add_authn_provider, NULL, OR_AUTHCFG, |
97 |
"specify the auth providers for a directory or location"), |
98 |
AP_INIT_FLAG("AuthCertificateAuthoritative", ap_set_flag_slot, |
99 |
(void *)APR_OFFSETOF(auth_cert_config_rec, authoritative), |
100 |
OR_AUTHCFG, |
101 |
"Set to 'Off' to allow access control to be passed along to " |
102 |
"lower modules if the UserID is not known to this module"), |
103 |
{NULL} |
104 |
}; |
105 |
|
106 |
static APR_OPTIONAL_FN_TYPE(ssl_var_lookup) *ssl_svl_func; |
107 |
|
108 |
module AP_MODULE_DECLARE_DATA auth_cert_module; |
109 |
|
110 |
/* These functions return 0 if client is OK, and proper error status |
111 |
* if not... either HTTP_UNAUTHORIZED, if we made a check, and it failed, or |
112 |
* HTTP_INTERNAL_SERVER_ERROR, if things are so totally confused that we |
113 |
* couldn't figure out how to tell if the client is authorized or not. |
114 |
* |
115 |
* If they return DECLINED, and all other modules also decline, that's |
116 |
* treated by the server core as a configuration error, logged and |
117 |
* reported as such. |
118 |
*/ |
119 |
|
120 |
/* Determine user ID, and check if it really is that user, for HTTP |
121 |
*/ |
122 |
static int authenticate_cert_user(request_rec *r) |
123 |
{ |
124 |
auth_cert_config_rec *conf = ap_get_module_config(r->per_dir_config, |
125 |
&auth_cert_module); |
126 |
const char *client_cert, *current_auth; |
127 |
const char *verstatus; |
128 |
authn_status auth_result; |
129 |
authn_provider_list *current_provider; |
130 |
|
131 |
/* Are we configured to be Certificate auth? */ |
132 |
current_auth = ap_auth_type(r); |
133 |
if (!current_auth || strcasecmp(current_auth, "Certificate")) { |
134 |
return DECLINED; |
135 |
} |
136 |
|
137 |
/* We need an authentication realm. */ |
138 |
if (!ap_auth_name(r)) { |
139 |
ap_log_rerror(APLOG_MARK, APLOG_ERR, |
140 |
0, r, "need AuthName: %s", r->uri); |
141 |
return HTTP_INTERNAL_SERVER_ERROR; |
142 |
} |
143 |
|
144 |
r->ap_auth_type = (char*)current_auth; |
145 |
|
146 |
verstatus = ssl_svl_func(r->pool,r->server,r->connection,r,"CLIENT_VERIFY"); |
147 |
|
148 |
switch (*verstatus) { |
149 |
case 'G': // ENEROUS |
150 |
ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, |
151 |
"Client certificate is not from a trusted CA"); |
152 |
case 'S': // UCCESS |
153 |
break; |
154 |
case 'N': // ONE |
155 |
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, |
156 |
"Client certificate not provided"); |
157 |
return HTTP_INTERNAL_SERVER_ERROR; |
158 |
case 'F': // AILED |
159 |
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "Authentication " |
160 |
"aborted; certificate verification %s", |
161 |
verstatus); |
162 |
return HTTP_UNAUTHORIZED; |
163 |
default: // Only possible if another return is defined by mod_ssl.c |
164 |
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "Unexpected return " |
165 |
"code from certificate verification: %s", verstatus); |
166 |
return HTTP_INTERNAL_SERVER_ERROR; |
167 |
} |
168 |
|
169 |
/* |
170 |
* At this point we know we have a certificate. |
171 |
*/ |
172 |
|
173 |
client_cert = ssl_svl_func(r->pool,r->server,r->connection,r,"CLIENT_CERT"); |
174 |
|
175 |
current_provider = conf->providers; |
176 |
do { |
177 |
const authn_provider *provider; |
178 |
|
179 |
/* For now, if a provider isn't set, we'll be nice and use the file |
180 |
* provider. |
181 |
*/ |
182 |
if (!current_provider) { |
183 |
provider = ap_lookup_provider(AUTHN_PROVIDER_GROUP, |
184 |
AUTHN_DEFAULT_PROVIDER, "0"); |
185 |
|
186 |
if (!provider || !provider->check_certificate) { |
187 |
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, |
188 |
"No Authn provider configured"); |
189 |
auth_result = AUTH_GENERAL_ERROR; |
190 |
break; |
191 |
} |
192 |
apr_table_setn(r->notes, AUTHN_PROVIDER_NAME_NOTE, AUTHN_DEFAULT_PROVIDER); |
193 |
} |
194 |
else { |
195 |
provider = current_provider->provider; |
196 |
apr_table_setn(r->notes, AUTHN_PROVIDER_NAME_NOTE, current_provider->provider_name); |
197 |
} |
198 |
|
199 |
|
200 |
auth_result = provider->check_certificate(r, client_cert); |
201 |
|
202 |
apr_table_unset(r->notes, AUTHN_PROVIDER_NAME_NOTE); |
203 |
|
204 |
/* Something occured. Stop checking. */ |
205 |
if (auth_result != AUTH_USER_NOT_FOUND) { |
206 |
break; |
207 |
} |
208 |
|
209 |
/* If we're not really configured for providers, stop now. */ |
210 |
if (!conf->providers) { |
211 |
break; |
212 |
} |
213 |
|
214 |
current_provider = current_provider->next; |
215 |
} while (current_provider); |
216 |
|
217 |
if (auth_result != AUTH_GRANTED) { |
218 |
int return_code; |
219 |
|
220 |
/* If we're not authoritative, then any error is ignored. */ |
221 |
if (!(conf->authoritative) && auth_result != AUTH_DENIED) { |
222 |
return DECLINED; |
223 |
} |
224 |
|
225 |
switch (auth_result) { |
226 |
case AUTH_DENIED: |
227 |
/* Currently we are not passing a user name with the certificate. |
228 |
* This may change eventually, in which case we could see a denial |
229 |
* where the requested user is found, but there is no corresponding |
230 |
* certificate in its attributes. Until then AUTH_DENIED |
231 |
* and AUTH_USER_NOT_FOUND are really identical results. |
232 |
*/ |
233 |
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, |
234 |
"user %s: authentication failure for \"%s\": " |
235 |
"Certificate Mismatch", |
236 |
r->user, r->uri); |
237 |
return_code = HTTP_UNAUTHORIZED; |
238 |
break; |
239 |
case AUTH_USER_NOT_FOUND: |
240 |
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, |
241 |
"user %s not found: %s", r->user, r->uri); |
242 |
return_code = HTTP_UNAUTHORIZED; |
243 |
break; |
244 |
case AUTH_GENERAL_ERROR: |
245 |
default: |
246 |
/* We'll assume that the module has already said what its error |
247 |
* was in the logs. |
248 |
*/ |
249 |
return_code = HTTP_INTERNAL_SERVER_ERROR; |
250 |
break; |
251 |
} |
252 |
|
253 |
return return_code; |
254 |
} |
255 |
|
256 |
return OK; |
257 |
} |
258 |
|
259 |
static int auth_cert_post_config(apr_pool_t *pconf, apr_pool_t *plog, |
260 |
apr_pool_t *ptemp, server_rec *s) |
261 |
{ |
262 |
ssl_svl_func = APR_RETRIEVE_OPTIONAL_FN(ssl_var_lookup); |
263 |
return OK; |
264 |
} |
265 |
|
266 |
|
267 |
static void register_hooks(apr_pool_t *p) |
268 |
{ |
269 |
static const char * const aszPre[]={ "mod_ssl.c",NULL }; |
270 |
ap_hook_check_user_id(authenticate_cert_user, aszPre, NULL, APR_HOOK_MIDDLE); |
271 |
ap_hook_post_config(auth_cert_post_config, NULL, NULL, APR_HOOK_MIDDLE); |
272 |
} |
273 |
|
274 |
module AP_MODULE_DECLARE_DATA auth_cert_module = |
275 |
{ |
276 |
STANDARD20_MODULE_STUFF, |
277 |
create_auth_cert_dir_config, /* dir config creater */ |
278 |
NULL, /* dir merger --- default is to override */ |
279 |
NULL, /* server config */ |
280 |
NULL, /* merge server config */ |
281 |
auth_cert_cmds, /* command apr_table_t */ |
282 |
register_hooks /* register hooks */ |
283 |
}; |