View | Details | Raw Unified | Return to bug 48780
Collapse All | Expand All

(-)modules/aaa/mod_authnz_ldap.c (-1 / +19 lines)
Lines 398-405 Link Here
398
    authn_ldap_build_filter(filtbuf, r, user, NULL, sec);
398
    authn_ldap_build_filter(filtbuf, r, user, NULL, sec);
399
399
400
    /* do the user search */
400
    /* do the user search */
401
    result = util_ldap_cache_checkuserid(r, ldc, sec->url, sec->basedn, sec->scope,
401
    const char* current_auth;
402
    current_auth = ap_auth_type(r);
403
    if (!current_auth || strcasecmp(current_auth, "Basic")) {
404
        result = util_ldap_cache_checkuserid(r, ldc, sec->url, sec->basedn, sec->scope,
402
                                         sec->attributes, filtbuf, password, &dn, &vals);
405
                                         sec->attributes, filtbuf, password, &dn, &vals);
406
    } else if (!current_auth || strcasecmp(current_auth, "Certificate")) {
407
        result = util_ldap_cache_getuserdn(r, ldc, sec->url, sec->basedn, sec->scope,
408
                                         sec->attributes, filtbuf, &dn, &vals);
409
    }
403
    util_ldap_connection_close(ldc);
410
    util_ldap_connection_close(ldc);
404
411
405
    /* sanity check - if server is down, retry it up to 5 times */
412
    /* sanity check - if server is down, retry it up to 5 times */
Lines 487-492 Link Here
487
    return AUTH_GRANTED;
494
    return AUTH_GRANTED;
488
}
495
}
489
496
497
static authn_status authn_ldap_check_certificate(request_rec *r, const char *certificate)
498
{
499
    /* short-circuit this error early so that we don't get a misleading error later */
500
    if (certificate == NULL) {
501
        ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
502
                      "[%" APR_PID_T_FMT "] auth_ldap authenticate: no certificate specified", getpid());
503
        return AUTH_GENERAL_ERROR;
504
    }
505
    /* To borrow check_password we have to supply a placeholder password */
506
    return authn_ldap_check_password(r, certificate, "No password required");
507
}
490
/*
508
/*
491
 * Authorisation Phase
509
 * Authorisation Phase
492
 * -------------------
510
 * -------------------
(-)modules/aaa/config.m4 (+1 lines)
Lines 54-58 Link Here
54
    enable_auth_digest="no"
54
    enable_auth_digest="no"
55
  fi
55
  fi
56
])
56
])
57
APACHE_MODULE(auth_cert, X.509 certificate authentication, , , most)
57
58
58
APACHE_MODPATH_FINISH
59
APACHE_MODPATH_FINISH
(-)modules/aaa/mod_auth.h (+5 lines)
Lines 62-67 Link Here
62
     */
62
     */
63
    authn_status (*get_realm_hash)(request_rec *r, const char *user,
63
    authn_status (*get_realm_hash)(request_rec *r, const char *user,
64
                                   const char *realm, char **rethash);
64
                                   const char *realm, char **rethash);
65
66
    /* Given an X.509 certificate, expected to return AUTH_GRANTED
67
     * if the provider authenticates the certificate */
68
    authn_status (*check_certificate)(request_rec *r, const char *certificate);
69
65
} authn_provider;
70
} authn_provider;
66
71
67
/* A linked-list of authn providers. */
72
/* A linked-list of authn providers. */
(-)modules/aaa/mod_auth_cert.c (+283 lines)
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
};

Return to bug 48780