File: mod_authn_sasl.c

package info (click to toggle)
libapache2-mod-authn-sasl 1.1-1
  • links: PTS
  • area: main
  • in suites: squeeze
  • size: 1,548 kB
  • ctags: 61
  • sloc: sh: 8,923; ansic: 190; makefile: 68
file content (316 lines) | stat: -rw-r--r-- 9,002 bytes parent folder | download
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
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
/*
 * Copyright 2009  Heiko Hund <heikoh@users.sf.net>
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * or see the LICENSE file that should have been distributed with this code.
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/*
 * Apache httpd SASL authentication module.
 *
 * Provides SASL username and password verification for HTTP Basic
 * Authentication. Uses Cyrus' libsasl2 backends for this task.
 * Based on mod_authn_file.c from the httpd-2.2.3 distribution.
 */

#include "ap_config.h"
#include "ap_provider.h"
#include "httpd.h"
#include "http_config.h"
#include "http_core.h"
#include "http_log.h"
#include "http_protocol.h"
#include "http_request.h"
#include "apr_strings.h"

#include "mod_auth.h"

#include <sasl/sasl.h>


/* Forward declaration of the module struct. */
module AP_MODULE_DECLARE_DATA authn_sasl_module;

/* Valid strings for AuthSaslPwcheckMethod */
#define NUM_PWCHECK_METHODS 3
static const char *pwcheck_methods[NUM_PWCHECK_METHODS] = {
	"saslauthd", "auxprop", "sasldb"
};

/* Default pwcheck_method for use with libsasl. */
static const char *default_pwcheck_method = "saslauthd";

/* Default auxprop_plugin for use with libsasl. */
static const char *default_auxprop_plugin = "sasldb";

/* Default service name for use with libsasl. */
static const char *default_service_name = "http";

/* The per-directory module config. */
typedef struct {
	const char *pwcheck_method;
	const char *sasldb_path;
	const char *service;
	const char *realm;
} authn_sasl_cfg_t;


/* authn_sasl_create_dir_config -
 * Create a default per-directory configuration. Set the default
 * service name and pwcheck_method.
 */
static void *
authn_sasl_create_dir_config(apr_pool_t *p, char *dirspec)
{
	authn_sasl_cfg_t *cfg = apr_pcalloc(p, sizeof(*cfg));

	cfg->service = default_service_name;
	cfg->pwcheck_method = default_pwcheck_method;

	return (void *) cfg;
}


/* set_pwcheck_method -
 * Store the user defined pwcheck_method(s) into the config.
 */
static const char *
set_pwcheck_method(cmd_parms *cmd, void *mconfig, const char *arg1, const char *arg2)
{
	authn_sasl_cfg_t *cfg = mconfig;
	const char *method_1 = NULL, *method_2 = NULL;
	int i;

	for (i = 0; i < NUM_PWCHECK_METHODS; ++i) {
		if (apr_strnatcmp(arg1, pwcheck_methods[i]) == 0)
			method_1 = pwcheck_methods[i];
		if (arg2 && apr_strnatcmp(arg2, pwcheck_methods[i]) == 0)
			method_2 = pwcheck_methods[i];
	}

	if (method_1 == NULL)
		return apr_pstrcat(cmd->pool, "Invalid SASL pwcheck method string: ", arg1, NULL);

	/* Convert "sasldb" to "auxprop" as expected by libsasl2 */
	if (method_1 == pwcheck_methods[2])
		method_1 = pwcheck_methods[1];
	if (method_2 == pwcheck_methods[2])
		method_2 = pwcheck_methods[1];

	if (arg2 == NULL) {
		cfg->pwcheck_method = method_1;
	}
	else {
		if (method_2 == NULL || method_1 == method_2) {
			return apr_pstrcat(cmd->pool, "Invalid SASL pwcheck method string: ", arg2, NULL);
		}
		cfg->pwcheck_method = apr_pstrcat(cmd->pool, method_1, " ", method_2, NULL);
	}

	return NULL;
}


/* set_service -
 * Store the user defined service name into the config.
 */
static const char *
set_service(cmd_parms *cmd, void *mconfig, const char *arg1)
{
	authn_sasl_cfg_t *cfg = mconfig;
	cfg->service = apr_pstrdup(cmd->pool, arg1);
	return NULL;
}


/* set_realm -
 * Store the user defined user realm into the config.
 */
static const char *
set_realm(cmd_parms *cmd, void *mconfig, const char *arg1)
{
	authn_sasl_cfg_t *cfg = mconfig;
	cfg->realm = apr_pstrdup(cmd->pool, arg1);
	return NULL;
}


/* set_sasldb_path -
 * Store the path to the sasldb file into the config.
 */
static const char *
set_sasldb_path(cmd_parms *cmd, void *mconfig, const char *arg1)
{
	authn_sasl_cfg_t *cfg = mconfig;
	cfg->sasldb_path = apr_pstrdup(cmd->pool, arg1);
	return NULL;
}


/* The config directives introduces by this module. */
static const command_rec authn_sasl_cmds[] = {
	AP_INIT_TAKE12("AuthSaslPwcheckMethod", set_pwcheck_method, NULL, OR_AUTHCFG,
	               "Set this to override libsasl's default 'pwcheck_method' used for "
	               "authentication. Valid values are 'sasldb' and 'saslauthd'."),
	AP_INIT_TAKE1("AuthSaslServiceName", set_service, NULL, OR_AUTHCFG,
	              "Set the service name to be used by libsasl during user authentication"),
	AP_INIT_TAKE1("AuthSaslAppname", set_service, NULL, OR_AUTHCFG,
	              "(DEPRECATED) Same semantics as AuthSaslServiceName, use that instead"),
	AP_INIT_TAKE1("AuthSaslRealm", set_realm, NULL, OR_AUTHCFG,
	              "Set the user realm to be used by libsasl during user authentication"),
	AP_INIT_TAKE1("AuthSaslDbPath", set_sasldb_path, NULL, OR_AUTHCFG,
	              "Set the path to the sasldb file to use with pwcheck_method sasldb"),
	{ NULL, { NULL }, NULL, 0, 0, NULL }
};


/* authn_sasl_cb_getopt -
 * Callback function libsasl2 uses to get option values.
 * The value for option "pwcheck_method", "sasldb_path" and
 * "auxprop_plugin" are set here if requested by libsasl
 */
static int
authn_sasl_cb_getopt(void *mconfig, const char *pname, const char *opt, const char **res, unsigned *len)
{
	authn_sasl_cfg_t *cfg = mconfig;

	if (cfg->pwcheck_method && apr_strnatcmp(opt, "pwcheck_method") == 0) {
		*res = cfg->pwcheck_method;
		return SASL_OK;
	}
	else if (cfg->sasldb_path && apr_strnatcmp(opt, "sasldb_path") == 0) {
		*res = cfg->sasldb_path;
		return SASL_OK;
	}
	else if (apr_strnatcmp(opt, "auxprop_plugin") == 0) {
		*res = default_auxprop_plugin;
		return SASL_OK;
	}

	return SASL_CONTINUE;
}


/* authn_sasl_cb_log -
 * Callback function libsasl2 uses to send log lines
 * which are passed to the httpd log function here
 */
static int
authn_sasl_cb_log(void *req, int level, const char *message)
{
	request_rec *r = req;

	switch (level)
	{
	case SASL_LOG_NONE:
	case SASL_LOG_PASS:
		break; /* always ignore */

	case SASL_LOG_ERR:
	case SASL_LOG_FAIL:
		ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "%s", message);
		break;

	case SASL_LOG_WARN:
		ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, "%s", message);
		break;

	case SASL_LOG_NOTE:
		ap_log_rerror(APLOG_MARK, APLOG_NOTICE, 0, r, "%s", message);
		break;

	case SASL_LOG_DEBUG:
	case SASL_LOG_TRACE:
		ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "%s", message);
		break;
	}

	return SASL_OK;
}


/* check_password -
 * Authentication backend provider callback function used by
 * mod_auth_basic to verify credentials. Uses functions from
 * libsasl2 to do the job.
 */
static authn_status
check_password(request_rec *r, const char *user, const char *pass)
{
	sasl_conn_t *sasl_conn;
	authn_status result = AUTH_GRANTED;

	authn_sasl_cfg_t *cfg = ap_get_module_config(r->per_dir_config, &authn_sasl_module);

	sasl_callback_t const cb[] = {
		{ SASL_CB_GETOPT, authn_sasl_cb_getopt, (void *) cfg },
		{ SASL_CB_LOG, authn_sasl_cb_log, (void *) r },
		{ SASL_CB_LIST_END, NULL, NULL }
	};

	if (sasl_server_new(cfg->service, NULL, cfg->realm, NULL, NULL, cb, 0, &sasl_conn) != SASL_OK ||
	    sasl_checkpass(sasl_conn, user, strlen(user), pass, strlen(pass)) != SASL_OK) {
		ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "%s", sasl_errdetail(sasl_conn));
		result = AUTH_DENIED;
	}

	sasl_dispose(&sasl_conn);

	return result;
}

static apr_status_t
authn_sasl_child_exit(void *data)
{
	sasl_done();
	return APR_SUCCESS;
}


/* authn_sasl_child_init -
 * Initialize libsasl2 once in every child process and register
 * a cleanup function to finalize it before the process exits.
 */
static void
authn_sasl_child_init(apr_pool_t *p, server_rec *s)
{
	sasl_server_init(NULL, NULL);
	apr_pool_cleanup_register(p, s, NULL, authn_sasl_child_exit);
}


/* authn_sasl_register_hooks -
 * Registers an auth provider to be used with
 * mod_auth_basic and a iit function to init sasl.
 */
static void
authn_sasl_register_hooks(apr_pool_t *p)
{
	static const authn_provider authn_sasl_provider = {
		check_password, NULL
	};

	ap_register_provider(p, AUTHN_PROVIDER_GROUP, "sasl", "0", &authn_sasl_provider);
	ap_hook_child_init(authn_sasl_child_init, NULL, NULL, APR_HOOK_MIDDLE);
}


module AP_MODULE_DECLARE_DATA authn_sasl_module = {
	STANDARD20_MODULE_STUFF,
	authn_sasl_create_dir_config,    /* per-directory config creator */
	NULL,                            /* dir config merger */
	NULL,                            /* server config creator */
	NULL,                            /* server config merger */
	authn_sasl_cmds,                 /* command table */
	authn_sasl_register_hooks        /* set up other request processing hooks */
};