Do some more cleanup in libapache2-mod-auth-sslcert of our own
[invirt/packages/libapache2-mod-auth-sslcert.git] / mod_auth_sslcert.c
1 /* mod_auth_sslcert
2  * version 1.1.1, released 2007-10-01
3  * Anders Kaseorg <andersk@mit.edu>
4  *
5  * This module does authentication based on SSL client certificates:
6  *   AuthType SSLCert
7  *   AuthSSLCertVar SSL_CLIENT_S_DN_Email
8  *   AuthSSLCertStripSuffix "@MIT.EDU"
9  */
10
11 #include "apr_strings.h"
12 #define APR_WANT_STRFUNC        /* for strcasecmp */
13 #include "apr_want.h"
14
15 #include "ap_config.h"
16 #include "httpd.h"
17 #include "http_config.h"
18 #include "http_core.h"
19 #include "http_log.h"
20 #include "http_request.h"
21
22 #include "mod_auth.h"
23 #include "mod_ssl.h"
24
25 static APR_OPTIONAL_FN_TYPE(ssl_var_lookup) *ssl_var_lookup;
26
27 typedef struct {
28     int authoritative;
29     char *var;
30     char *strip_suffix;
31     int strip_suffix_required;
32 } auth_sslcert_config_rec;
33
34 static void *create_auth_sslcert_dir_config(apr_pool_t *p, char *dirspec)
35 {
36     auth_sslcert_config_rec *conf = apr_pcalloc(p, sizeof(*conf));
37
38     conf->authoritative = -1;
39     conf->var = NULL;
40     conf->strip_suffix = NULL;
41     conf->strip_suffix_required = -1;
42
43     return conf;
44 }
45
46 static void *merge_auth_sslcert_dir_config(apr_pool_t *p, void *parent_conf, void *newloc_conf)
47 {
48     auth_sslcert_config_rec *pconf = parent_conf, *nconf = newloc_conf,
49         *conf = apr_pcalloc(p, sizeof(*conf));
50
51     conf->authoritative = (nconf->authoritative != -1) ?
52         nconf->authoritative : pconf->authoritative;
53     conf->var = (nconf->var != NULL) ?
54         nconf->var : pconf->var;
55     conf->strip_suffix = (nconf->var != NULL || nconf->strip_suffix != NULL) ?
56         nconf->strip_suffix : pconf->strip_suffix;
57     conf->strip_suffix_required = (nconf->var != NULL || nconf->strip_suffix_required != -1) ?
58         nconf->authoritative : pconf->authoritative;
59
60     return conf;
61 }
62
63 static const command_rec auth_sslcert_cmds[] =
64 {
65     AP_INIT_FLAG("AuthSSLCertAuthoritative", ap_set_flag_slot,
66                  (void *)APR_OFFSETOF(auth_sslcert_config_rec, authoritative),
67                  OR_AUTHCFG,
68                  "Set to 'Off' to allow access control to be passed along to "
69                  "lower modules if the UserID is not known to this module"),
70     AP_INIT_TAKE1("AuthSSLCertVar", ap_set_string_slot,
71                   (void*)APR_OFFSETOF(auth_sslcert_config_rec, var),
72                   OR_AUTHCFG,
73                   "SSL variable to use as the username"),
74     AP_INIT_TAKE1("AuthSSLCertStripSuffix", ap_set_string_slot,
75                   (void*)APR_OFFSETOF(auth_sslcert_config_rec, strip_suffix),
76                   OR_AUTHCFG,
77                   "An optional suffix to strip from the username"),
78     AP_INIT_FLAG("AuthSSLCertStripSuffixRequired", ap_set_flag_slot,
79                  (void *)APR_OFFSETOF(auth_sslcert_config_rec, strip_suffix_required),
80                  OR_AUTHCFG,
81                  "Set to 'Off' to allow certs that don't end with a recognized "
82                  "suffix to still authenticate"),
83     {NULL}
84 };
85
86 module AP_MODULE_DECLARE_DATA auth_sslcert_module;
87
88 static int authenticate_sslcert_user(request_rec *r)
89 {
90     auth_sslcert_config_rec *conf = ap_get_module_config(r->per_dir_config,
91                                                          &auth_sslcert_module);
92     const char *current_auth;
93
94     /* Are we configured to be SSLCert auth? */
95     current_auth = ap_auth_type(r);
96     if (!current_auth || strcasecmp(current_auth, "SSLCert") != 0) {
97         return DECLINED;
98     }
99
100     r->ap_auth_type = "SSLCert";
101
102     if (strcasecmp((char *)ssl_var_lookup(r->pool, r->server, r->connection, r,
103                                           "SSL_CLIENT_VERIFY"),
104                    "SUCCESS") == 0) {
105         if (conf->var == NULL) {
106             ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
107                           "AuthSSLCertVar is not set: \"%s\"", r->uri);
108             return HTTP_INTERNAL_SERVER_ERROR;
109         }
110         char *user = (char *)ssl_var_lookup(r->pool, r->server, r->connection, r,
111                                             conf->var);
112         if (user != NULL && user[0] != '\0') {
113             if (conf->strip_suffix != NULL) {
114                 int i = strlen(user) - strlen(conf->strip_suffix);
115                 if (i >= 0 && strcasecmp(user + i, conf->strip_suffix) == 0) {
116                     r->user = apr_pstrmemdup(r->pool, user, i);
117                     return OK;
118                 } else if (!conf->strip_suffix_required) {
119                     r->user = user;
120                     return OK;
121                 } else {
122                     ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
123                                   "SSL username for \"%s\" has wrong suffix: \"%s\"",
124                                   r->uri, user);
125                 }
126             } else {
127                 r->user = user;
128                 return OK;
129             }
130         } else {
131             ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
132                           "no SSL username for \"%s\"", r->uri);
133         }
134     } else if (conf->authoritative) {
135         ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
136                       "SSL client not verified for \"%s\"", r->uri);
137     }
138
139     /* If we're not authoritative, then any error is ignored. */
140     if (!conf->authoritative) {
141         return DECLINED;
142     }
143
144     ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
145                   "SSLCert authentication failure for \"%s\"",
146                   r->uri);
147     return HTTP_UNAUTHORIZED;
148 }
149
150 static void import_ssl_var_lookup()
151 {
152     ssl_var_lookup = APR_RETRIEVE_OPTIONAL_FN(ssl_var_lookup);
153 }
154
155 static void register_hooks(apr_pool_t *p)
156 {
157     ap_hook_check_user_id(authenticate_sslcert_user, NULL, NULL, APR_HOOK_MIDDLE);
158     ap_hook_optional_fn_retrieve(import_ssl_var_lookup, NULL, NULL, APR_HOOK_MIDDLE);
159 }
160
161 module AP_MODULE_DECLARE_DATA auth_sslcert_module =
162 {
163     STANDARD20_MODULE_STUFF,
164     create_auth_sslcert_dir_config,  /* dir config creater */
165     merge_auth_sslcert_dir_config,   /* dir merger */
166     NULL,                            /* server config */
167     NULL,                            /* merge server config */
168     auth_sslcert_cmds,               /* command apr_table_t */
169     register_hooks                   /* register hooks */
170 };