5 * FUTURE POTENTIAL PERFORMANCE ENHANCEMENTS:
6 * - database response is not cached
7 * TODO: db response caching (for limited time) to reduce load on db
8 * (only cache successful logins to prevent cache bloat?)
9 * (or limit number of entries (size) of cache)
10 * (maybe have negative cache (limited size) of names not found in database)
11 * - database query is synchronous and blocks waiting for response
14 #include <sasl/sasl.h>
17 #include "http_auth.h"
21 #include <sys/utsname.h>
30 const buffer
*pwcheck_method
;
31 const buffer
*sasldb_path
;
36 plugin_config
**config_storage
;
42 static handler_t
mod_authn_sasl_basic(server
*srv
, connection
*con
, void *p_d
, const http_auth_require_t
*require
, const buffer
*username
, const char *pw
);
44 INIT_FUNC(mod_authn_sasl_init
) {
45 static http_auth_backend_t http_auth_backend_sasl
=
46 { "sasl", mod_authn_sasl_basic
, NULL
, NULL
};
47 plugin_data
*p
= calloc(1, sizeof(*p
));
49 /* register http_auth_backend_sasl */
50 http_auth_backend_sasl
.p_d
= p
;
51 http_auth_backend_set(&http_auth_backend_sasl
);
56 FREE_FUNC(mod_authn_sasl_free
) {
58 if (!p
) return HANDLER_GO_ON
;
60 if (p
->initonce
) sasl_done();
62 if (p
->config_storage
) {
63 for (size_t i
= 0; i
< srv
->config_context
->used
; ++i
) {
64 plugin_config
*s
= p
->config_storage
[i
];
65 if (NULL
== s
) continue;
69 free(p
->config_storage
);
77 SETDEFAULTS_FUNC(mod_authn_sasl_set_defaults
) {
80 config_values_t cv
[] = {
81 { "auth.backend.sasl.opts", NULL
, T_CONFIG_ARRAY
, T_CONFIG_SCOPE_CONNECTION
},
82 { NULL
, NULL
, T_CONFIG_UNSET
, T_CONFIG_SCOPE_UNSET
}
85 p
->config_storage
= calloc(srv
->config_context
->used
, sizeof(plugin_config
*));
87 for (i
= 0; i
< srv
->config_context
->used
; i
++) {
88 data_config
const *config
= (data_config
const*)srv
->config_context
->data
[i
];
90 plugin_config
*s
= calloc(1, sizeof(plugin_config
));
91 s
->opts
= array_init();
93 cv
[0].destination
= s
->opts
;
95 p
->config_storage
[i
] = s
;
97 if (0 != config_insert_values_global(srv
, config
->value
, cv
, i
== 0 ? T_CONFIG_SCOPE_SERVER
: T_CONFIG_SCOPE_CONNECTION
)) {
101 if (0 == s
->opts
->used
) continue;
104 array_get_element_klen(s
->opts
, CONST_STR_LEN("service"));
105 s
->service
= (NULL
!= ds
) ? ds
->value
->ptr
: "http";
108 array_get_element_klen(s
->opts
, CONST_STR_LEN("fqdn"));
109 if (NULL
!= ds
) s
->fqdn
= ds
->value
->ptr
;
110 if (NULL
== s
->fqdn
) {
111 if (NULL
== p
->fqdn
) {
113 if (0 != uname(&uts
)) {
114 log_error_write(srv
, __FILE__
, __LINE__
, "ss",
115 "uname():", strerror(errno
));
116 return HANDLER_ERROR
;
118 p
->fqdn
= buffer_init_string(uts
.nodename
);
120 s
->fqdn
= p
->fqdn
->ptr
;
124 array_get_element_klen(s
->opts
, CONST_STR_LEN("pwcheck_method"));
126 s
->pwcheck_method
= ds
->value
;
127 if (!buffer_is_equal_string(ds
->value
, CONST_STR_LEN("saslauthd"))
128 && !buffer_is_equal_string(ds
->value
, CONST_STR_LEN("auxprop"))
129 && !buffer_is_equal_string(ds
->value
, CONST_STR_LEN("sasldb"))){
130 log_error_write(srv
, __FILE__
, __LINE__
, "sb",
131 "sasl pwcheck_method must be one of saslauthd, "
132 "sasldb, or auxprop, not:", ds
->value
);
133 return HANDLER_ERROR
;
135 if (buffer_is_equal_string(ds
->value
, CONST_STR_LEN("sasldb"))) {
136 /* Cyrus libsasl2 expects "auxprop" instead of "sasldb"
137 * (mod_authn_sasl_cb_getopt auxprop_plugin returns "sasldb") */
138 buffer_copy_string_len(ds
->value
, CONST_STR_LEN("auxprop"));
143 array_get_element_klen(s
->opts
, CONST_STR_LEN("sasldb_path"));
144 if (NULL
!= ds
) s
->sasldb_path
= ds
->value
;
147 return HANDLER_GO_ON
;
152 static int mod_authn_sasl_patch_connection(server
*srv
, connection
*con
, plugin_data
*p
) {
153 plugin_config
*s
= p
->config_storage
[0];
156 PATCH(pwcheck_method
);
159 /* skip the first, the global context */
160 for (size_t i
= 1; i
< srv
->config_context
->used
; ++i
) {
161 data_config
*dc
= (data_config
*)srv
->config_context
->data
[i
];
163 /* condition didn't match */
164 if (!config_check_cond(srv
, con
, dc
)) continue;
167 s
= p
->config_storage
[i
];
168 for (size_t j
= 0; j
< dc
->value
->used
; ++j
) {
169 data_unset
*du
= dc
->value
->data
[j
];
170 if (buffer_is_equal_string(du
->key
, CONST_STR_LEN("auth.backend.sasl.opts"))) {
173 PATCH(pwcheck_method
);
183 static int mod_authn_sasl_cb_getopt(void *p_d
, const char *plugin_name
, const char *opt
, const char **res
, unsigned *len
) {
184 plugin_data
*p
= (plugin_data
*)p_d
;
187 if (0 == strcmp(opt
, "pwcheck_method")) {
188 if (!buffer_string_is_empty(p
->conf
.pwcheck_method
)) {
189 *res
= p
->conf
.pwcheck_method
->ptr
;
190 sz
= buffer_string_length(p
->conf
.pwcheck_method
);
194 sz
= sizeof("saslauthd")-1;
197 else if (0 == strcmp(opt
, "sasldb_path")
198 && !buffer_string_is_empty(p
->conf
.sasldb_path
)) {
199 *res
= p
->conf
.sasldb_path
->ptr
;
200 sz
= buffer_string_length(p
->conf
.sasldb_path
);
202 else if (0 == strcmp(opt
, "auxprop_plugin")) {
204 sz
= sizeof("sasldb")-1;
211 if (len
) *len
= (unsigned int)sz
;
215 static int mod_authn_sasl_cb_log(void *vsrv
, int level
, const char *message
) {
228 case SASL_LOG_WARN
: /* (might omit SASL_LOG_WARN if too noisy in logs) */
229 log_error_write((server
*)vsrv
, __FILE__
, __LINE__
, "s", message
);
235 static handler_t
mod_authn_sasl_query(server
*srv
, connection
*con
, void *p_d
, const buffer
*username
, const char *realm
, const char *pw
) {
236 plugin_data
*p
= (plugin_data
*)p_d
;
238 sasl_callback_t
const cb
[] = {
239 { SASL_CB_GETOPT
, (int(*)())mod_authn_sasl_cb_getopt
, (void *) p
},
240 { SASL_CB_LOG
, (int(*)())mod_authn_sasl_cb_log
, (void *) srv
},
241 { SASL_CB_LIST_END
, NULL
, NULL
}
245 mod_authn_sasl_patch_connection(srv
, con
, p
);
248 /* must be done once, but after fork() if multiple lighttpd workers */
249 rc
= sasl_server_init(cb
, NULL
);
250 if (SASL_OK
!= rc
) return HANDLER_ERROR
;
254 rc
= sasl_server_new(p
->conf
.service
, p
->conf
.fqdn
,
255 realm
, NULL
, NULL
, cb
, 0, &sc
);
257 rc
= sasl_checkpass(sc
, CONST_BUF_LEN(username
), pw
, strlen(pw
));
261 return (SASL_OK
== rc
) ? HANDLER_GO_ON
: HANDLER_ERROR
;
264 static handler_t
mod_authn_sasl_basic(server
*srv
, connection
*con
, void *p_d
, const http_auth_require_t
*require
, const buffer
*username
, const char *pw
) {
265 char *realm
= require
->realm
->ptr
;
266 handler_t rc
= mod_authn_sasl_query(srv
, con
, p_d
, username
, realm
, pw
);
267 if (HANDLER_GO_ON
!= rc
) return rc
;
268 return http_auth_match_rules(require
, username
->ptr
, NULL
, NULL
)
269 ? HANDLER_GO_ON
/* access granted */
273 int mod_authn_sasl_plugin_init(plugin
*p
);
274 int mod_authn_sasl_plugin_init(plugin
*p
) {
275 p
->version
= LIGHTTPD_VERSION_ID
;
276 p
->name
= buffer_init_string("authn_sasl");
277 p
->init
= mod_authn_sasl_init
;
278 p
->set_defaults
= mod_authn_sasl_set_defaults
;
279 p
->cleanup
= mod_authn_sasl_free
;