File:  [ELWIX - Embedded LightWeight unIX -] / embedaddon / lighttpd / src / mod_auth.c
Revision 1.1.1.3 (vendor branch): download - view: text, annotated - select for diffs - revision graph
Wed Nov 2 10:35:00 2016 UTC (7 years, 8 months ago) by misho
Branches: lighttpd, MAIN
CVS tags: v1_4_41p8, HEAD
lighttpd 1.4.41

    1: #include "first.h"
    2: 
    3: #include "plugin.h"
    4: #include "http_auth.h"
    5: #include "log.h"
    6: #include "response.h"
    7: 
    8: #include <sys/types.h>
    9: #include <sys/stat.h>
   10: 
   11: #include <stdlib.h>
   12: #include <string.h>
   13: #include <errno.h>
   14: #include <fcntl.h>
   15: #include <unistd.h>
   16: #include <mysql/mysql.h>
   17: 
   18: handler_t auth_ldap_init(server *srv, mod_auth_plugin_config *s);
   19: 
   20: 
   21: /**
   22:  * the basic and digest auth framework
   23:  *
   24:  * - config handling
   25:  * - protocol handling
   26:  *
   27:  * http_auth.c
   28:  * http_auth_digest.c
   29:  *
   30:  * do the real work
   31:  */
   32: 
   33: INIT_FUNC(mod_auth_init) {
   34: 	mod_auth_plugin_data *p;
   35: 
   36: 	p = calloc(1, sizeof(*p));
   37: 
   38: 	p->tmp_buf = buffer_init();
   39: 
   40: 	p->auth_user = buffer_init();
   41: #ifdef USE_LDAP
   42: 	p->ldap_filter = buffer_init();
   43: #endif
   44: 
   45: 	return p;
   46: }
   47: 
   48: FREE_FUNC(mod_auth_free) {
   49: 	mod_auth_plugin_data *p = p_d;
   50: 
   51: 	UNUSED(srv);
   52: 
   53: 	if (!p) return HANDLER_GO_ON;
   54: 
   55: 	buffer_free(p->tmp_buf);
   56: 	buffer_free(p->auth_user);
   57: #ifdef USE_LDAP
   58: 	buffer_free(p->ldap_filter);
   59: #endif
   60: 
   61: 	if (p->config_storage) {
   62: 		size_t i;
   63: 		for (i = 0; i < srv->config_context->used; i++) {
   64: 			mod_auth_plugin_config *s = p->config_storage[i];
   65: 
   66: 			if (NULL == s) continue;
   67: 
   68: 			array_free(s->auth_require);
   69: 			buffer_free(s->auth_plain_groupfile);
   70: 			buffer_free(s->auth_plain_userfile);
   71: 			buffer_free(s->auth_htdigest_userfile);
   72: 			buffer_free(s->auth_htpasswd_userfile);
   73: 			buffer_free(s->auth_backend_conf);
   74: 
   75: 			buffer_free(s->auth_ldap_hostname);
   76: 			buffer_free(s->auth_ldap_basedn);
   77: 			buffer_free(s->auth_ldap_binddn);
   78: 			buffer_free(s->auth_ldap_bindpw);
   79: 			buffer_free(s->auth_ldap_filter);
   80: 			buffer_free(s->auth_ldap_cafile);
   81: 
   82: #ifdef USE_LDAP
   83: 			buffer_free(s->ldap_filter_pre);
   84: 			buffer_free(s->ldap_filter_post);
   85: 
   86: 			if (s->ldap) ldap_unbind_s(s->ldap);
   87: #endif
   88: 			buffer_free(s->auth_mysql_host);
   89: 			buffer_free(s->auth_mysql_user);
   90: 			buffer_free(s->auth_mysql_pass);
   91: 			buffer_free(s->auth_mysql_db);
   92: 			buffer_free(s->auth_mysql_socket);
   93: 			buffer_free(s->auth_mysql_users_table);
   94: 			buffer_free(s->auth_mysql_col_user);
   95: 			buffer_free(s->auth_mysql_col_pass);
   96: 			buffer_free(s->auth_mysql_col_realm);
   97: 			buffer_free(s->auth_mysql_domains_table);
   98: 			buffer_free(s->auth_mysql_col_domain);
   99: 			buffer_free(s->auth_mysql_domains_table_col_domain_id);
  100: 			buffer_free(s->auth_mysql_users_table_col_domain_id);
  101: 
  102: 			free(s);
  103: 		}
  104: 		free(p->config_storage);
  105: 	}
  106: 
  107: 	free(p);
  108: 
  109: 	return HANDLER_GO_ON;
  110: }
  111: 
  112: #define PATCH(x) \
  113: 	p->conf.x = s->x;
  114: static int mod_auth_patch_connection(server *srv, connection *con, mod_auth_plugin_data *p) {
  115: 	size_t i, j;
  116: 	mod_auth_plugin_config *s = p->config_storage[0];
  117: 
  118: 	PATCH(auth_backend);
  119: 	PATCH(auth_plain_groupfile);
  120: 	PATCH(auth_plain_userfile);
  121: 	PATCH(auth_htdigest_userfile);
  122: 	PATCH(auth_htpasswd_userfile);
  123: 	PATCH(auth_require);
  124: 	PATCH(auth_debug);
  125: 	PATCH(auth_ldap_hostname);
  126: 	PATCH(auth_ldap_basedn);
  127: 	PATCH(auth_ldap_binddn);
  128: 	PATCH(auth_ldap_bindpw);
  129: 	PATCH(auth_ldap_filter);
  130: 	PATCH(auth_ldap_cafile);
  131: 	PATCH(auth_ldap_starttls);
  132: 	PATCH(auth_ldap_allow_empty_pw);
  133: #ifdef USE_LDAP
  134: 	p->anon_conf = s;
  135: 	PATCH(ldap_filter_pre);
  136: 	PATCH(ldap_filter_post);
  137: #endif
  138: 
  139: 	PATCH(auth_mysql_host);
  140: 	PATCH(auth_mysql_user);
  141: 	PATCH(auth_mysql_pass);
  142: 	PATCH(auth_mysql_db);
  143: 	PATCH(auth_mysql_port);
  144: 	PATCH(auth_mysql_socket);
  145: 	PATCH(auth_mysql_users_table);
  146: 	PATCH(auth_mysql_col_user);
  147: 	PATCH(auth_mysql_col_pass);
  148: 	PATCH(auth_mysql_col_realm);
  149: 	PATCH(auth_mysql_domains_table);
  150: 	PATCH(auth_mysql_col_domain);
  151: 	PATCH(auth_mysql_domains_table_col_domain_id);
  152: 	PATCH(auth_mysql_users_table_col_domain_id);
  153: 
  154: 	/* skip the first, the global context */
  155: 	for (i = 1; i < srv->config_context->used; i++) {
  156: 		data_config *dc = (data_config *)srv->config_context->data[i];
  157: 		s = p->config_storage[i];
  158: 
  159: 		/* condition didn't match */
  160: 		if (!config_check_cond(srv, con, dc)) continue;
  161: 
  162: 		/* merge config */
  163: 		for (j = 0; j < dc->value->used; j++) {
  164: 			data_unset *du = dc->value->data[j];
  165: 
  166: 			if (buffer_is_equal_string(du->key, CONST_STR_LEN("auth.backend"))) {
  167: 				PATCH(auth_backend);
  168: 			} else if (buffer_is_equal_string(du->key, CONST_STR_LEN("auth.backend.plain.groupfile"))) {
  169: 				PATCH(auth_plain_groupfile);
  170: 			} else if (buffer_is_equal_string(du->key, CONST_STR_LEN("auth.backend.plain.userfile"))) {
  171: 				PATCH(auth_plain_userfile);
  172: 			} else if (buffer_is_equal_string(du->key, CONST_STR_LEN("auth.backend.htdigest.userfile"))) {
  173: 				PATCH(auth_htdigest_userfile);
  174: 			} else if (buffer_is_equal_string(du->key, CONST_STR_LEN("auth.backend.htpasswd.userfile"))) {
  175: 				PATCH(auth_htpasswd_userfile);
  176: 			} else if (buffer_is_equal_string(du->key, CONST_STR_LEN("auth.require"))) {
  177: 				PATCH(auth_require);
  178: 			} else if (buffer_is_equal_string(du->key, CONST_STR_LEN("auth.debug"))) {
  179: 				PATCH(auth_debug);
  180: 			} else if (buffer_is_equal_string(du->key, CONST_STR_LEN("auth.backend.ldap.hostname"))) {
  181: 				PATCH(auth_ldap_hostname);
  182: #ifdef USE_LDAP
  183: 				p->anon_conf = s;
  184: #endif
  185: 			} else if (buffer_is_equal_string(du->key, CONST_STR_LEN("auth.backend.ldap.base-dn"))) {
  186: 				PATCH(auth_ldap_basedn);
  187: 			} else if (buffer_is_equal_string(du->key, CONST_STR_LEN("auth.backend.ldap.filter"))) {
  188: 				PATCH(auth_ldap_filter);
  189: #ifdef USE_LDAP
  190: 				PATCH(ldap_filter_pre);
  191: 				PATCH(ldap_filter_post);
  192: #endif
  193: 			} else if (buffer_is_equal_string(du->key, CONST_STR_LEN("auth.backend.ldap.ca-file"))) {
  194: 				PATCH(auth_ldap_cafile);
  195: 			} else if (buffer_is_equal_string(du->key, CONST_STR_LEN("auth.backend.ldap.starttls"))) {
  196: 				PATCH(auth_ldap_starttls);
  197: 			} else if (buffer_is_equal_string(du->key, CONST_STR_LEN("auth.backend.ldap.bind-dn"))) {
  198: 				PATCH(auth_ldap_binddn);
  199: 			} else if (buffer_is_equal_string(du->key, CONST_STR_LEN("auth.backend.ldap.bind-pw"))) {
  200: 				PATCH(auth_ldap_bindpw);
  201: 			} else if (buffer_is_equal_string(du->key, CONST_STR_LEN("auth.backend.ldap.allow-empty-pw"))) {
  202: 				PATCH(auth_ldap_allow_empty_pw);
  203: 			} else if (buffer_is_equal_string(du->key, CONST_STR_LEN("auth.backend.mysql.host"))) {
  204: 				PATCH(auth_mysql_host);
  205: 			} else if (buffer_is_equal_string(du->key, CONST_STR_LEN("auth.backend.mysql.user"))) {
  206: 				PATCH(auth_mysql_user);
  207: 			} else if (buffer_is_equal_string(du->key, CONST_STR_LEN("auth.backend.mysql.pass"))) {
  208: 				PATCH(auth_mysql_pass);
  209: 			} else if (buffer_is_equal_string(du->key, CONST_STR_LEN("auth.backend.mysql.db"))) {
  210: 				PATCH(auth_mysql_db);
  211: 			} else if (buffer_is_equal_string(du->key, CONST_STR_LEN("auth.backend.mysql.port"))) {
  212: 				PATCH(auth_mysql_port);
  213: 			} else if (buffer_is_equal_string(du->key, CONST_STR_LEN("auth.backend.mysql.socket"))) {
  214: 				PATCH(auth_mysql_user);
  215: 			} else if (buffer_is_equal_string(du->key, CONST_STR_LEN("auth.backend.mysql.users_table"))) {
  216: 				PATCH(auth_mysql_users_table);
  217: 			} else if (buffer_is_equal_string(du->key, CONST_STR_LEN("auth.backend.mysql.col_user"))) {
  218: 				PATCH(auth_mysql_col_user);
  219: 			} else if (buffer_is_equal_string(du->key, CONST_STR_LEN("auth.backend.mysql.col_pass"))) {
  220: 				PATCH(auth_mysql_col_pass);
  221: 			} else if (buffer_is_equal_string(du->key, CONST_STR_LEN("auth.backend.mysql.col_realm"))) {
  222: 				PATCH(auth_mysql_col_realm);
  223: 			} else if (buffer_is_equal_string(du->key, CONST_STR_LEN("auth.backend.mysql.domains_table"))) {
  224: 				PATCH(auth_mysql_domains_table);
  225: 			} else if (buffer_is_equal_string(du->key, CONST_STR_LEN("auth.backend.mysql.col_domain"))) {
  226: 				PATCH(auth_mysql_col_domain);
  227: 			} else if (buffer_is_equal_string(du->key, CONST_STR_LEN("auth.backend.mysql.domains_table_col_domain_id"))) {
  228: 				PATCH(auth_mysql_domains_table_col_domain_id);
  229: 			} else if (buffer_is_equal_string(du->key, CONST_STR_LEN("auth.backend.mysql.users_table_col_domain_id"))) {
  230: 				PATCH(auth_mysql_users_table_col_domain_id);
  231: 			}
  232: 		}
  233: 	}
  234: 
  235: 	return 0;
  236: }
  237: #undef PATCH
  238: 
  239: static handler_t mod_auth_uri_handler(server *srv, connection *con, void *p_d) {
  240: 	size_t k;
  241: 	int auth_required = 0, auth_satisfied = 0;
  242: 	char *http_authorization = NULL;
  243: 	const char *auth_type = NULL;
  244: 	data_string *ds;
  245: 	mod_auth_plugin_data *p = p_d;
  246: 	array *req;
  247: 	data_string *req_method;
  248: 
  249: 	/* select the right config */
  250: 	mod_auth_patch_connection(srv, con, p);
  251: 
  252: 	if (p->conf.auth_require == NULL) return HANDLER_GO_ON;
  253: 
  254: 	/*
  255: 	 * AUTH
  256: 	 *
  257: 	 */
  258: 
  259: 	/* do we have to ask for auth ? */
  260: 
  261: 	auth_required = 0;
  262: 	auth_satisfied = 0;
  263: 
  264: 	/* search auth-directives for path */
  265: 	for (k = 0; k < p->conf.auth_require->used; k++) {
  266: 		buffer *require = p->conf.auth_require->data[k]->key;
  267: 
  268: 		if (buffer_is_empty(require)) continue;
  269: 		if (buffer_string_length(con->uri.path) < buffer_string_length(require)) continue;
  270: 
  271: 		/* if we have a case-insensitive FS we have to lower-case the URI here too */
  272: 
  273: 		if (con->conf.force_lowercase_filenames) {
  274: 			if (0 == strncasecmp(con->uri.path->ptr, require->ptr, buffer_string_length(require))) {
  275: 				auth_required = 1;
  276: 				break;
  277: 			}
  278: 		} else {
  279: 			if (0 == strncmp(con->uri.path->ptr, require->ptr, buffer_string_length(require))) {
  280: 				auth_required = 1;
  281: 				break;
  282: 			}
  283: 		}
  284: 	}
  285: 
  286: 	/* nothing to do for us */
  287: 	if (auth_required == 0) return HANDLER_GO_ON;
  288: 
  289: 	req = ((data_array *)(p->conf.auth_require->data[k]))->value;
  290: 	req_method = (data_string *)array_get_element(req, "method");
  291: 
  292: 	if (0 == strcmp(req_method->value->ptr, "extern")) {
  293: 		/* require REMOTE_USER to be already set */
  294: 		if (NULL == (ds = (data_string *)array_get_element(con->environment, "REMOTE_USER"))) {
  295: 			con->http_status = 401;
  296: 			con->mode = DIRECT;
  297: 			return HANDLER_FINISHED;
  298: 		} else if (http_auth_match_rules(srv, req, ds->value->ptr, NULL, NULL)) {
  299: 			log_error_write(srv, __FILE__, __LINE__, "s", "rules didn't match");
  300: 			con->http_status = 401;
  301: 			con->mode = DIRECT;
  302: 			return HANDLER_FINISHED;
  303: 		} else {
  304: 			return HANDLER_GO_ON;
  305: 		}
  306: 	}
  307: 
  308: 	/* try to get Authorization-header */
  309: 
  310: 	if (NULL != (ds = (data_string *)array_get_element(con->request.headers, "Authorization")) && !buffer_is_empty(ds->value)) {
  311: 		char *auth_realm;
  312: 
  313: 		http_authorization = ds->value->ptr;
  314: 
  315: 		/* parse auth-header */
  316: 		if (NULL != (auth_realm = strchr(http_authorization, ' '))) {
  317: 			int auth_type_len = auth_realm - http_authorization;
  318: 
  319: 			if ((auth_type_len == 5) &&
  320: 			    (0 == strncasecmp(http_authorization, "Basic", auth_type_len))) {
  321: 				auth_type = "Basic";
  322: 
  323: 				if (0 == strcmp(req_method->value->ptr, "basic")) {
  324: 					auth_satisfied = http_auth_basic_check(srv, con, p, req, auth_realm+1);
  325: 				}
  326: 			} else if ((auth_type_len == 6) &&
  327: 				   (0 == strncasecmp(http_authorization, "Digest", auth_type_len))) {
  328: 				auth_type = "Digest";
  329: 				if (0 == strcmp(req_method->value->ptr, "digest")) {
  330: 					if (-1 == (auth_satisfied = http_auth_digest_check(srv, con, p, req, auth_realm+1))) {
  331: 						con->http_status = 400;
  332: 						con->mode = DIRECT;
  333: 
  334: 						/* a field was missing */
  335: 
  336: 						return HANDLER_FINISHED;
  337: 					}
  338: 				}
  339: 			} else {
  340: 				log_error_write(srv, __FILE__, __LINE__, "ss",
  341: 						"unknown authentication type:",
  342: 						http_authorization);
  343: 			}
  344: 		}
  345: 	}
  346: 
  347: 	if (1 != auth_satisfied) { /*(0 or -2)*/
  348: 		data_string *method, *realm;
  349: 		method = (data_string *)array_get_element(req, "method");
  350: 		realm = (data_string *)array_get_element(req, "realm");
  351: 
  352: 		con->http_status = 401;
  353: 		con->mode = DIRECT;
  354: 
  355: 		if (!method) return HANDLER_FINISHED;/*(should not happen; config is validated at startup)*/
  356: 		if (!realm) return HANDLER_FINISHED; /*(should not happen; config is validated at startup)*/
  357: 
  358: 		if (0 == strcmp(method->value->ptr, "basic")) {
  359: 			buffer_copy_string_len(p->tmp_buf, CONST_STR_LEN("Basic realm=\""));
  360: 			buffer_append_string_buffer(p->tmp_buf, realm->value);
  361: 			buffer_append_string_len(p->tmp_buf, CONST_STR_LEN("\", charset=\"UTF-8\""));
  362: 
  363: 			response_header_insert(srv, con, CONST_STR_LEN("WWW-Authenticate"), CONST_BUF_LEN(p->tmp_buf));
  364: 		} else if (0 == strcmp(method->value->ptr, "digest")) {
  365: 			char hh[33];
  366: 			http_auth_digest_generate_nonce(srv, p, srv->tmp_buf, &hh);
  367: 
  368: 			buffer_copy_string_len(p->tmp_buf, CONST_STR_LEN("Digest realm=\""));
  369: 			buffer_append_string_buffer(p->tmp_buf, realm->value);
  370: 			buffer_append_string_len(p->tmp_buf, CONST_STR_LEN("\", charset=\"UTF-8\", nonce=\""));
  371: 			buffer_append_uint_hex(p->tmp_buf, (uintmax_t)srv->cur_ts);
  372: 			buffer_append_string_len(p->tmp_buf, CONST_STR_LEN(":"));
  373: 			buffer_append_string(p->tmp_buf, hh);
  374: 			buffer_append_string_len(p->tmp_buf, CONST_STR_LEN("\", qop=\"auth\""));
  375: 			if (-2 == auth_satisfied) {
  376: 				buffer_append_string_len(p->tmp_buf, CONST_STR_LEN(", stale=true"));
  377: 			}
  378: 
  379: 			response_header_insert(srv, con, CONST_STR_LEN("WWW-Authenticate"), CONST_BUF_LEN(p->tmp_buf));
  380: 		} else {
  381: 			/* evil */
  382: 		}
  383: 		return HANDLER_FINISHED;
  384: 	} else {
  385: 		/* the REMOTE_USER header */
  386: 
  387: 		if (NULL == (ds = (data_string *)array_get_element(con->environment, "REMOTE_USER"))) {
  388: 			if (NULL == (ds = (data_string *)array_get_unused_element(con->environment, TYPE_STRING))) {
  389: 				ds = data_string_init();
  390: 			}
  391: 			buffer_copy_string(ds->key, "REMOTE_USER");
  392: 			array_insert_unique(con->environment, (data_unset *)ds);
  393: 		}
  394: 		buffer_copy_buffer(ds->value, p->auth_user);
  395: 
  396: 		/* AUTH_TYPE environment */
  397: 
  398: 		if (NULL == (ds = (data_string *)array_get_element(con->environment, "AUTH_TYPE"))) {
  399: 			if (NULL == (ds = (data_string *)array_get_unused_element(con->environment, TYPE_STRING))) {
  400: 				ds = data_string_init();
  401: 			}
  402: 			buffer_copy_string(ds->key, "AUTH_TYPE");
  403: 			array_insert_unique(con->environment, (data_unset *)ds);
  404: 		}
  405: 		buffer_copy_string(ds->value, auth_type);
  406: 	}
  407: 
  408: 	return HANDLER_GO_ON;
  409: }
  410: 
  411: SETDEFAULTS_FUNC(mod_auth_set_defaults) {
  412: 	mod_auth_plugin_data *p = p_d;
  413: 	size_t i;
  414: 
  415: 	config_values_t cv[] = {
  416: 		{ "auth.backend",                   NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 0 */
  417: 		{ "auth.backend.plain.groupfile",   NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 1 */
  418: 		{ "auth.backend.plain.userfile",    NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 2 */
  419: 		{ "auth.require",                   NULL, T_CONFIG_LOCAL, T_CONFIG_SCOPE_CONNECTION },  /* 3 */
  420: 		{ "auth.backend.ldap.hostname",     NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 4 */
  421: 		{ "auth.backend.ldap.base-dn",      NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 5 */
  422: 		{ "auth.backend.ldap.filter",       NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 6 */
  423: 		{ "auth.backend.ldap.ca-file",      NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 7 */
  424: 		{ "auth.backend.ldap.starttls",     NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 8 */
  425:  		{ "auth.backend.ldap.bind-dn",      NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 9 */
  426:  		{ "auth.backend.ldap.bind-pw",      NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 10 */
  427: 		{ "auth.backend.ldap.allow-empty-pw",     NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION },
  428: 		{ "auth.backend.htdigest.userfile", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 12 */
  429: 		{ "auth.backend.htpasswd.userfile", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 13 */
  430: 		{ "auth.debug",                     NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_CONNECTION },  /* 14 */
  431: 		{ "auth.backend.mysql.host",        NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION },
  432: 		{ "auth.backend.mysql.user",        NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION },
  433: 		{ "auth.backend.mysql.pass",        NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION },
  434: 		{ "auth.backend.mysql.db",          NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION },
  435: 		{ "auth.backend.mysql.port",        NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION },
  436: 		{ "auth.backend.mysql.socket",      NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION },
  437: 		{ "auth.backend.mysql.users_table", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION },
  438: 		{ "auth.backend.mysql.col_user",    NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION },
  439: 		{ "auth.backend.mysql.col_pass",    NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION },
  440: 		{ "auth.backend.mysql.col_realm",   NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 23 */
  441: 		{ "auth.backend.mysql.domains_table",               NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION },
  442: 		{ "auth.backend.mysql.col_domain",                  NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION },
  443: 		{ "auth.backend.mysql.domains_table_col_domain_id", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION },
  444: 		{ "auth.backend.mysql.users_table_col_domain_id",   NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 27 */
  445: 
  446: 		{ NULL,                             NULL, T_CONFIG_UNSET, T_CONFIG_SCOPE_UNSET }
  447: 	};
  448: 
  449: 	p->config_storage = calloc(1, srv->config_context->used * sizeof(mod_auth_plugin_config *));
  450: 
  451: 	for (i = 0; i < srv->config_context->used; i++) {
  452: 		data_config const* config = (data_config const*)srv->config_context->data[i];
  453: 		mod_auth_plugin_config *s;
  454: 		size_t n;
  455: 		data_array *da;
  456: 
  457: 		s = calloc(1, sizeof(mod_auth_plugin_config));
  458: 		s->auth_plain_groupfile = buffer_init();
  459: 		s->auth_plain_userfile = buffer_init();
  460: 		s->auth_htdigest_userfile = buffer_init();
  461: 		s->auth_htpasswd_userfile = buffer_init();
  462: 		s->auth_backend_conf = buffer_init();
  463: 
  464: 		s->auth_ldap_hostname = buffer_init();
  465: 		s->auth_ldap_basedn = buffer_init();
  466: 		s->auth_ldap_binddn = buffer_init();
  467: 		s->auth_ldap_bindpw = buffer_init();
  468: 		s->auth_ldap_filter = buffer_init();
  469: 		s->auth_ldap_cafile = buffer_init();
  470: 		s->auth_ldap_starttls = 0;
  471: 		s->auth_debug = 0;
  472: 
  473: 		s->auth_require = array_init();
  474: 		s->mysql_conn                             = NULL;
  475: 		s->auth_mysql_host                        = buffer_init();
  476: 		s->auth_mysql_user                        = buffer_init();
  477: 		s->auth_mysql_pass                        = buffer_init();
  478: 		s->auth_mysql_db                          = buffer_init();
  479: 		s->auth_mysql_port                        = buffer_init();
  480: 		s->auth_mysql_socket                      = buffer_init();
  481: 		s->auth_mysql_users_table                 = buffer_init();
  482: 		s->auth_mysql_col_user                    = buffer_init();
  483: 		s->auth_mysql_col_pass                    = buffer_init();
  484: 		s->auth_mysql_col_realm                   = buffer_init();
  485: 		s->auth_mysql_domains_table               = buffer_init();
  486: 		s->auth_mysql_col_domain                  = buffer_init();
  487: 		s->auth_mysql_domains_table_col_domain_id = buffer_init();
  488: 		s->auth_mysql_users_table_col_domain_id   = buffer_init();
  489: 
  490: 
  491: #ifdef USE_LDAP
  492: 		s->ldap_filter_pre = buffer_init();
  493: 		s->ldap_filter_post = buffer_init();
  494: 		s->ldap = NULL;
  495: #endif
  496: 
  497: 		cv[0].destination = s->auth_backend_conf;
  498: 		cv[1].destination = s->auth_plain_groupfile;
  499: 		cv[2].destination = s->auth_plain_userfile;
  500: 		cv[3].destination = s->auth_require;
  501: 		cv[4].destination = s->auth_ldap_hostname;
  502: 		cv[5].destination = s->auth_ldap_basedn;
  503: 		cv[6].destination = s->auth_ldap_filter;
  504: 		cv[7].destination = s->auth_ldap_cafile;
  505: 		cv[8].destination = &(s->auth_ldap_starttls);
  506: 		cv[9].destination = s->auth_ldap_binddn;
  507: 		cv[10].destination = s->auth_ldap_bindpw;
  508: 		cv[11].destination = &(s->auth_ldap_allow_empty_pw);
  509: 		cv[12].destination = s->auth_htdigest_userfile;
  510: 		cv[13].destination = s->auth_htpasswd_userfile;
  511: 		cv[14].destination = &(s->auth_debug);
  512: 		cv[15].destination = s->auth_mysql_host;
  513: 		cv[16].destination = s->auth_mysql_user;
  514: 		cv[17].destination = s->auth_mysql_pass;
  515: 		cv[18].destination = s->auth_mysql_db;
  516: 		cv[19].destination = s->auth_mysql_port;
  517: 		cv[20].destination = s->auth_mysql_socket;
  518: 		cv[21].destination = s->auth_mysql_users_table;
  519: 		cv[22].destination = s->auth_mysql_col_user;
  520: 		cv[23].destination = s->auth_mysql_col_pass;
  521: 		cv[24].destination = s->auth_mysql_col_realm;
  522: 		cv[25].destination = s->auth_mysql_domains_table;
  523: 		cv[26].destination = s->auth_mysql_col_domain;
  524: 		cv[27].destination = s->auth_mysql_domains_table_col_domain_id;
  525: 		cv[28].destination = s->auth_mysql_users_table_col_domain_id;
  526: 		p->config_storage[i] = s;
  527: 
  528: 		if (0 != config_insert_values_global(srv, config->value, cv, i == 0 ? T_CONFIG_SCOPE_SERVER : T_CONFIG_SCOPE_CONNECTION)) {
  529: 			return HANDLER_ERROR;
  530: 		}
  531: 
  532: 		if (!buffer_string_is_empty(s->auth_backend_conf)) {
  533: 			if (0 == strcmp(s->auth_backend_conf->ptr, "htpasswd")) {
  534: 				s->auth_backend = AUTH_BACKEND_HTPASSWD;
  535: 			} else if (0 == strcmp(s->auth_backend_conf->ptr, "htdigest")) {
  536: 				s->auth_backend = AUTH_BACKEND_HTDIGEST;
  537: 			} else if (0 == strcmp(s->auth_backend_conf->ptr, "plain")) {
  538: 				s->auth_backend = AUTH_BACKEND_PLAIN;
  539: 			} else if (0 == strcmp(s->auth_backend_conf->ptr, "ldap")) {
  540: 				s->auth_backend = AUTH_BACKEND_LDAP;
  541: 			} else if (0 == strcmp(s->auth_backend_conf->ptr, "mysql")) {
  542: 				s->auth_backend = AUTH_BACKEND_MYSQL;
  543: 			} else {
  544: 				log_error_write(srv, __FILE__, __LINE__, "sb", "auth.backend not supported:", s->auth_backend_conf);
  545: 
  546: 				return HANDLER_ERROR;
  547: 			}
  548: 		}
  549: 
  550: #ifdef USE_LDAP
  551: 		if (!buffer_string_is_empty(s->auth_ldap_filter)) {
  552: 			char *dollar;
  553: 
  554: 			/* parse filter */
  555: 
  556: 			if (NULL == (dollar = strchr(s->auth_ldap_filter->ptr, '$'))) {
  557: 				log_error_write(srv, __FILE__, __LINE__, "s", "ldap: auth.backend.ldap.filter is missing a replace-operator '$'");
  558: 
  559: 				return HANDLER_ERROR;
  560: 			}
  561: 
  562: 			buffer_copy_string_len(s->ldap_filter_pre, s->auth_ldap_filter->ptr, dollar - s->auth_ldap_filter->ptr);
  563: 			buffer_copy_string(s->ldap_filter_post, dollar+1);
  564: 		}
  565: #endif
  566: 
  567: 		/* no auth.require for this section */
  568: 		if (NULL == (da = (data_array *)array_get_element(config->value, "auth.require"))) continue;
  569: 
  570: 		if (da->type != TYPE_ARRAY) continue;
  571: 
  572: 		for (n = 0; n < da->value->used; n++) {
  573: 			size_t m;
  574: 			data_array *da_file = (data_array *)da->value->data[n];
  575: 			const char *method, *realm, *require;
  576: 
  577: 			if (da->value->data[n]->type != TYPE_ARRAY) {
  578: 				log_error_write(srv, __FILE__, __LINE__, "ss",
  579: 						"auth.require should contain an array as in:",
  580: 						"auth.require = ( \"...\" => ( ..., ...) )");
  581: 
  582: 				return HANDLER_ERROR;
  583: 			}
  584: 
  585: 			method = realm = require = NULL;
  586: 
  587: 			for (m = 0; m < da_file->value->used; m++) {
  588: 				if (da_file->value->data[m]->type == TYPE_STRING) {
  589: 					if (0 == strcmp(da_file->value->data[m]->key->ptr, "method")) {
  590: 						method = ((data_string *)(da_file->value->data[m]))->value->ptr;
  591: 					} else if (0 == strcmp(da_file->value->data[m]->key->ptr, "realm")) {
  592: 						realm = ((data_string *)(da_file->value->data[m]))->value->ptr;
  593: 					} else if (0 == strcmp(da_file->value->data[m]->key->ptr, "require")) {
  594: 						require = ((data_string *)(da_file->value->data[m]))->value->ptr;
  595: 					} else {
  596: 						log_error_write(srv, __FILE__, __LINE__, "ssbs",
  597: 							"the field is unknown in:",
  598: 							"auth.require = ( \"...\" => ( ..., -> \"",
  599: 							da_file->value->data[m]->key,
  600: 							"\" <- => \"...\" ) )");
  601: 
  602: 						return HANDLER_ERROR;
  603: 					}
  604: 				} else {
  605: 					log_error_write(srv, __FILE__, __LINE__, "ssbs",
  606: 						"a string was expected for:",
  607: 						"auth.require = ( \"...\" => ( ..., -> \"",
  608: 						da_file->value->data[m]->key,
  609: 						"\" <- => \"...\" ) )");
  610: 
  611: 					return HANDLER_ERROR;
  612: 				}
  613: 			}
  614: 
  615: 			if (method == NULL) {
  616: 				log_error_write(srv, __FILE__, __LINE__, "ss",
  617: 						"the method field is missing in:",
  618: 						"auth.require = ( \"...\" => ( ..., \"method\" => \"...\" ) )");
  619: 				return HANDLER_ERROR;
  620: 			} else {
  621: 				if (0 != strcmp(method, "basic") &&
  622: 				    0 != strcmp(method, "digest") &&
  623: 				    0 != strcmp(method, "extern")) {
  624: 					log_error_write(srv, __FILE__, __LINE__, "ss",
  625: 							"method has to be either \"basic\", \"digest\" or \"extern\" in",
  626: 							"auth.require = ( \"...\" => ( ..., \"method\" => \"...\") )");
  627: 					return HANDLER_ERROR;
  628: 				}
  629: 			}
  630: 
  631: 			if (realm == NULL) {
  632: 				log_error_write(srv, __FILE__, __LINE__, "ss",
  633: 						"the realm field is missing in:",
  634: 						"auth.require = ( \"...\" => ( ..., \"realm\" => \"...\" ) )");
  635: 				return HANDLER_ERROR;
  636: 			}
  637: 
  638: 			if (require == NULL) {
  639: 				log_error_write(srv, __FILE__, __LINE__, "ss",
  640: 						"the require field is missing in:",
  641: 						"auth.require = ( \"...\" => ( ..., \"require\" => \"...\" ) )");
  642: 				return HANDLER_ERROR;
  643: 			}
  644: 
  645: 			if (method && realm && require) {
  646: 				data_string *ds;
  647: 				data_array *a;
  648: 
  649: 				a = data_array_init();
  650: 				buffer_copy_buffer(a->key, da_file->key);
  651: 
  652: 				ds = data_string_init();
  653: 
  654: 				buffer_copy_string_len(ds->key, CONST_STR_LEN("method"));
  655: 				buffer_copy_string(ds->value, method);
  656: 
  657: 				array_insert_unique(a->value, (data_unset *)ds);
  658: 
  659: 				ds = data_string_init();
  660: 
  661: 				buffer_copy_string_len(ds->key, CONST_STR_LEN("realm"));
  662: 				buffer_copy_string(ds->value, realm);
  663: 
  664: 				array_insert_unique(a->value, (data_unset *)ds);
  665: 
  666: 				ds = data_string_init();
  667: 
  668: 				buffer_copy_string_len(ds->key, CONST_STR_LEN("require"));
  669: 				buffer_copy_string(ds->value, require);
  670: 
  671: 				array_insert_unique(a->value, (data_unset *)ds);
  672: 
  673: 				array_insert_unique(s->auth_require, (data_unset *)a);
  674: 			}
  675: 		}
  676: 
  677: 		switch(s->auth_backend) {
  678: 		case AUTH_BACKEND_LDAP: {
  679: 			handler_t ret = auth_ldap_init(srv, s);
  680: 			if (ret == HANDLER_ERROR)
  681: 				return (ret);
  682: 			break;
  683: 		}
  684: 		case AUTH_BACKEND_MYSQL: {
  685: 			int port = atoi(s->auth_mysql_port->ptr);
  686: 
  687: 			/* ignore if auth_mysql_socket is invalid */
  688: 			if (p->conf.auth_mysql_socket == NULL)
  689: 				return HANDLER_GO_ON;
  690: 			if (p->conf.auth_mysql_socket->ptr != NULL)
  691: 				if (0 == strcmp(s->auth_mysql_socket->ptr, "")) s->auth_mysql_socket->ptr = NULL;
  692: 
  693: 			s->mysql_conn = mysql_init(NULL);
  694: 			if (!mysql_real_connect(s->mysql_conn, s->auth_mysql_host->ptr, s->auth_mysql_user->ptr, s->auth_mysql_pass->ptr, s->auth_mysql_db->ptr, port, NULL, 0))
  695: 			{
  696: 				log_error_write(srv, __FILE__, __LINE__, "sbsbsbsbss",
  697: 						"opening connection to mysql:", s->auth_mysql_host,
  698: 						"user:", s->auth_mysql_user,
  699: 						"pass:", s->auth_mysql_pass,
  700: 						"db:", s->auth_mysql_db,
  701: 						"failed:", strerror(errno));
  702: 
  703: 				return HANDLER_ERROR;
  704: 			}
  705: 			mysql_close(s->mysql_conn);
  706: 
  707: 			break;
  708: 		}
  709: 		default:
  710: 			break;
  711: 		}
  712: 	}
  713: 
  714: 	return HANDLER_GO_ON;
  715: }
  716: 
  717: handler_t auth_ldap_init(server *srv, mod_auth_plugin_config *s) {
  718: #ifdef USE_LDAP
  719: 	int ret;
  720: #if 0
  721: 	if (s->auth_ldap_basedn->used == 0) {
  722: 		log_error_write(srv, __FILE__, __LINE__, "s", "ldap: auth.backend.ldap.base-dn has to be set");
  723: 
  724: 		return HANDLER_ERROR;
  725: 	}
  726: #endif
  727: 
  728: 	if (!buffer_string_is_empty(s->auth_ldap_hostname)) {
  729: 		/* free old context */
  730: 		if (NULL != s->ldap) ldap_unbind_s(s->ldap);
  731: 
  732: 		if (NULL == (s->ldap = ldap_init(s->auth_ldap_hostname->ptr, LDAP_PORT))) {
  733: 			log_error_write(srv, __FILE__, __LINE__, "ss", "ldap ...", strerror(errno));
  734: 
  735: 			return HANDLER_ERROR;
  736: 		}
  737: 
  738: 		ret = LDAP_VERSION3;
  739: 		if (LDAP_OPT_SUCCESS != (ret = ldap_set_option(s->ldap, LDAP_OPT_PROTOCOL_VERSION, &ret))) {
  740: 			log_error_write(srv, __FILE__, __LINE__, "ss", "ldap:", ldap_err2string(ret));
  741: 
  742: 			return HANDLER_ERROR;
  743: 		}
  744: 
  745: 		if (s->auth_ldap_starttls) {
  746: 			/* if no CA file is given, it is ok, as we will use encryption
  747: 				* if the server requires a CAfile it will tell us */
  748: 			if (!buffer_string_is_empty(s->auth_ldap_cafile)) {
  749: 				if (LDAP_OPT_SUCCESS != (ret = ldap_set_option(NULL, LDAP_OPT_X_TLS_CACERTFILE,
  750: 								s->auth_ldap_cafile->ptr))) {
  751: 					log_error_write(srv, __FILE__, __LINE__, "ss",
  752: 							"Loading CA certificate failed:", ldap_err2string(ret));
  753: 
  754: 					return HANDLER_ERROR;
  755: 				}
  756: 			}
  757: 
  758: 			if (LDAP_OPT_SUCCESS != (ret = ldap_start_tls_s(s->ldap, NULL,  NULL))) {
  759: 				log_error_write(srv, __FILE__, __LINE__, "ss", "ldap startTLS failed:", ldap_err2string(ret));
  760: 
  761: 				return HANDLER_ERROR;
  762: 			}
  763: 		}
  764: 
  765: 
  766: 		/* 1. */
  767: 		if (!buffer_string_is_empty(s->auth_ldap_binddn)) {
  768: 			if (LDAP_SUCCESS != (ret = ldap_simple_bind_s(s->ldap, s->auth_ldap_binddn->ptr, s->auth_ldap_bindpw->ptr))) {
  769: 				log_error_write(srv, __FILE__, __LINE__, "ss", "ldap:", ldap_err2string(ret));
  770: 
  771: 				return HANDLER_ERROR;
  772: 			}
  773: 		} else {
  774: 			if (LDAP_SUCCESS != (ret = ldap_simple_bind_s(s->ldap, NULL, NULL))) {
  775: 				log_error_write(srv, __FILE__, __LINE__, "ss", "ldap:", ldap_err2string(ret));
  776: 
  777: 				return HANDLER_ERROR;
  778: 			}
  779: 		}
  780: 	}
  781: 	return HANDLER_GO_ON;
  782: #else
  783: 	UNUSED(s);
  784: 	log_error_write(srv, __FILE__, __LINE__, "s", "no ldap support available");
  785: 	return HANDLER_ERROR;
  786: #endif
  787: }
  788: 
  789: int mod_auth_plugin_init(plugin *p);
  790: int mod_auth_plugin_init(plugin *p) {
  791: 	p->version     = LIGHTTPD_VERSION_ID;
  792: 	p->name        = buffer_init_string("auth");
  793: 	p->init        = mod_auth_init;
  794: 	p->set_defaults = mod_auth_set_defaults;
  795: 	p->handle_uri_clean = mod_auth_uri_handler;
  796: 	p->cleanup     = mod_auth_free;
  797: 
  798: 	p->data        = NULL;
  799: 
  800: 	return 0;
  801: }

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>