/* * Copyright 2013--2016 Jan Pazdziora * * 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 * * 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. */ #include #include "apr_hash.h" #include "ap_config.h" #include "ap_provider.h" #include "apr_strings.h" #include "apr_optional.h" #include "httpd.h" #include "http_core.h" #include "http_config.h" #include "http_log.h" #include "http_protocol.h" #include "http_request.h" #include #include #ifndef NO_USER_ATTR #include #define DBUS_SSSD_PATH "/org/freedesktop/sssd/infopipe" #define DBUS_SSSD_PATH_USERS "/org/freedesktop/sssd/infopipe/Users" #define DBUS_SSSD_IFACE "org.freedesktop.sssd.infopipe" #define DBUS_SSSD_IFACE_USERS "org.freedesktop.sssd.infopipe.Users" #define DBUS_SSSD_GET_USER_GROUPS_METHOD "GetUserGroups" #define DBUS_SSSD_GET_USER_ATTR_METHOD "GetUserAttr" #define DBUS_SSSD_FIND_BY_CERTIFICATE "FindByCertificate" #define DBUS_SSSD_DEST "org.freedesktop.sssd.infopipe" #define DBUS_SSSD_TIMEOUT 5000 #define DBUS_PROPERTIES "org.freedesktop.DBus.Properties" #define DBUS_PROPERTIES_GET "Get" #define DBUS_SSSD_USERS_USER "org.freedesktop.sssd.infopipe.Users.User" #define DBUS_SSSD_USERS_ID "name"; #endif static const int LOOKUP_IDENTITY_OUTPUT_DEFAULT = 0; static const int LOOKUP_IDENTITY_OUTPUT_NONE = 128; static const int LOOKUP_IDENTITY_OUTPUT_NOTES = 1; static const int LOOKUP_IDENTITY_OUTPUT_ENV = 2; static const int LOOKUP_IDENTITY_OUTPUT_HEADERS = 4; static const int LOOKUP_IDENTITY_OUTPUT_HEADERS_BASE64 = 8; static char * LOOKUP_IDENTITY_OUTPUT_GECOS = "REMOTE_USER_GECOS"; typedef struct lookup_identity_config { char * context; int output; char * output_gecos; #ifndef NO_USER_ATTR char * output_groups; char * output_groups_sep; char * output_groups_iter; apr_hash_t * output_user_attr; apr_hash_t * output_user_attr_sep; apr_hash_t * output_user_attr_iter; int dbus_timeout; int lookup_by_certificate; #endif } lookup_identity_config; module AP_MODULE_DECLARE_DATA lookup_identity_module; #ifndef NO_USER_ATTR static int lookup_user_by_certificate(request_rec * r) { const lookup_identity_config * cfg = (lookup_identity_config *) ap_get_module_config(r->per_dir_config, &lookup_identity_module); if (cfg->lookup_by_certificate < 1 || ! r->user) { return DECLINED; } ap_log_error(APLOG_MARK, APLOG_INFO, 0, r->server, "lookup_user_by_certificate invoked [%s]", r->user); static char * stage = NULL; DBusError error; dbus_error_init(&error); DBusMessage * message = NULL; DBusMessage * reply = NULL; DBusConnection * connection = dbus_bus_get(DBUS_BUS_SYSTEM, &error); if (! connection) { stage = "dbus_bus_get(DBUS_BUS_SYSTEM)"; goto fail; } dbus_connection_set_exit_on_disconnect(connection, FALSE); message = dbus_message_new_method_call(DBUS_SSSD_DEST, DBUS_SSSD_PATH_USERS, DBUS_SSSD_IFACE_USERS, DBUS_SSSD_FIND_BY_CERTIFICATE); if (! message) { stage = "dbus_message_new_method_call(" DBUS_SSSD_IFACE_USERS "." DBUS_SSSD_FIND_BY_CERTIFICATE ")"; goto fail; } dbus_message_set_auto_start(message, TRUE); if (! dbus_message_append_args(message, DBUS_TYPE_STRING, &(r->user), DBUS_TYPE_INVALID)) { stage = apr_psprintf(r->pool, "dbus_message_append_args(%s)", r->user); goto fail; } int timeout = DBUS_SSSD_TIMEOUT; if (cfg->dbus_timeout > 0) { timeout = cfg->dbus_timeout; } reply = dbus_connection_send_with_reply_and_block(connection, message, timeout, &error); if (! reply || dbus_error_is_set(&error)) { stage = "dbus_connection_send_with_reply_and_block(" DBUS_SSSD_IFACE_USERS "." DBUS_SSSD_FIND_BY_CERTIFICATE ")"; goto fail; } int reply_type = dbus_message_get_type(reply); if (reply_type != DBUS_MESSAGE_TYPE_METHOD_RETURN) { stage = apr_psprintf(r->pool, DBUS_SSSD_IFACE_USERS "." DBUS_SSSD_FIND_BY_CERTIFICATE " returned [%d], not DBUS_MESSAGE_TYPE_METHOD_RETURN", reply_type); goto fail; } const char *path; if (! dbus_message_get_args(reply, NULL, DBUS_TYPE_OBJECT_PATH, &path, DBUS_TYPE_INVALID)) { stage = DBUS_SSSD_IFACE_USERS "." DBUS_SSSD_FIND_BY_CERTIFICATE ": return arg not DBUS_TYPE_OBJECT_PATH"; goto fail; } ap_log_error(APLOG_MARK, APLOG_INFO, 0, r->server, "lookup_user_by_certificate got object [%s]", path); dbus_message_unref(message); message = dbus_message_new_method_call(DBUS_SSSD_DEST, path, DBUS_PROPERTIES, DBUS_PROPERTIES_GET); if (! message) { stage = "dbus_message_new_method_call(" DBUS_PROPERTIES "." DBUS_PROPERTIES_GET ")"; goto fail; } dbus_message_set_auto_start(message, TRUE); const char * users_user = DBUS_SSSD_USERS_USER; const char * attrib_name = DBUS_SSSD_USERS_ID; if (! dbus_message_append_args(message, DBUS_TYPE_STRING, &users_user, DBUS_TYPE_STRING, &attrib_name, DBUS_TYPE_INVALID)) { stage = apr_psprintf(r->pool, "dbus_message_append_args(%s, %s)", users_user, attrib_name); goto fail; } dbus_message_unref(reply); reply = dbus_connection_send_with_reply_and_block(connection, message, timeout, &error); dbus_message_unref(message); if (!reply || dbus_error_is_set(&error)) { stage = "dbus_connection_send_with_reply_and_block(" DBUS_PROPERTIES "." DBUS_PROPERTIES_GET ")"; goto fail; } reply_type = dbus_message_get_type(reply); if (reply_type != DBUS_MESSAGE_TYPE_METHOD_RETURN) { stage = apr_psprintf(r->pool, DBUS_SSSD_IFACE_USERS "." DBUS_SSSD_FIND_BY_CERTIFICATE " returned [%d], not DBUS_MESSAGE_TYPE_METHOD_RETURN", reply_type); goto fail; } DBusMessageIter iter, variter; if (! dbus_message_iter_init(reply, &iter)) { stage = DBUS_PROPERTIES "." DBUS_PROPERTIES_GET ": did not return any arguments"; goto fail; } int type = dbus_message_iter_get_arg_type(&iter); if (type != DBUS_TYPE_VARIANT) { stage = DBUS_PROPERTIES "." DBUS_PROPERTIES_GET ": result is not DBUS_TYPE_VARIANT"; goto fail; } dbus_message_iter_recurse(&iter, &variter); type = dbus_message_iter_get_arg_type(&variter); if (type == DBUS_TYPE_STRING) { char * r_data; dbus_message_iter_get_basic(&variter, &r_data); r->user = apr_pstrdup(r->pool, r_data); ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, r->server, "lookup_user_by_certificate found [%s]", r->user); } if (dbus_message_iter_next(&variter) || dbus_message_iter_next(&iter)) { stage = DBUS_PROPERTIES "." DBUS_PROPERTIES_GET ": result is not unique"; goto fail; } goto pass; fail: if (dbus_error_is_set(&error)) { ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server, "lookup_user_by_certificate failed [%s]: [%s]", stage, error.message); } else if (stage) { ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server, "lookup_user_by_certificate failed [%s]", stage); } r->user = NULL; pass: if (! r->user) { ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server, "lookup_user_by_certificate cleared r->user"); } if (reply) { dbus_message_unref(reply); } if (connection) { dbus_connection_unref(connection); } dbus_error_free(&error); return DECLINED; } static DBusMessage * lookup_identity_dbus_message(request_rec * r, DBusConnection * connection, DBusError * error, int timeout, const char * method, apr_hash_t * hash) { DBusMessage * message = dbus_message_new_method_call(DBUS_SSSD_DEST, DBUS_SSSD_PATH, DBUS_SSSD_IFACE, method); if (! message) { ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server, "Error allocating dbus message"); return NULL; } dbus_message_set_auto_start(message, TRUE); char * user = r->user; int nargs = 0; const char ** args = NULL; if (hash && (nargs = apr_hash_count(hash))) { apr_hash_index_t * hi = apr_hash_first(r->pool, hash); args = apr_pcalloc(r->pool, nargs * sizeof(char *)); for (int i = 0; hi; hi = apr_hash_next(hi), i++) { const void * ptr; apr_hash_this(hi, &ptr, NULL, NULL); args[i] = ptr; } } if (args) { dbus_message_append_args(message, DBUS_TYPE_STRING, &user, DBUS_TYPE_ARRAY, DBUS_TYPE_STRING, &args, nargs, DBUS_TYPE_INVALID); } else { dbus_message_append_args(message, DBUS_TYPE_STRING, &user, DBUS_TYPE_INVALID); } DBusMessage * reply = dbus_connection_send_with_reply_and_block(connection, message, timeout, error); dbus_message_unref(message); int is_error = 0; int reply_type = DBUS_MESSAGE_TYPE_ERROR; if (dbus_error_is_set(error)) { is_error = 1; } else { reply_type = dbus_message_get_type(reply); if (reply_type == DBUS_MESSAGE_TYPE_ERROR) { is_error = 1; } else if (reply_type != DBUS_MESSAGE_TYPE_METHOD_RETURN) { is_error = 1; } } if (is_error) { char * args_string = ""; if (args) { int total_args_length = 0; int i; for (i = 0; i < nargs; i++) { total_args_length += strlen(args[i]) + 2; } args_string = apr_palloc(r->pool, total_args_length + 1); char * p = args_string; for (i = 0; i < nargs; i++) { strcpy(p, ", "); strcpy(p + 2, args[i]); p += strlen(args[i]) + 2; } args_string[total_args_length] = '\0'; } if (dbus_error_is_set(error)) { ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server, "Error dbus calling %s(%s%s): %s: %s", method, user, args_string, error->name, error->message); } else if (reply_type == DBUS_MESSAGE_TYPE_ERROR) { ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server, "Error %s dbus calling %s(%s%s)", dbus_message_get_error_name(reply), method, user, args_string); } else { ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server, "Error unexpected reply type %d dbus calling %s(%s%s)", reply_type, method, user, args_string); } if (reply) { dbus_message_unref(reply); } return NULL; } return reply; } #endif static apr_array_header_t * base64_encode_array(apr_pool_t * p, const apr_array_header_t * values) { if (! values) return NULL; apr_array_header_t * base64_values = apr_array_make(p, values->nelts, sizeof(char *)); for (int i = 0; i < values->nelts; i++) { *(char **)apr_array_push(base64_values) = ap_pbase64encode(p, ((char **)values->elts)[i]); } ap_assert(values->nelts == base64_values->nelts); return base64_values; } static void lookup_identity_output_iter_to(request_rec * r, apr_table_t * t, const char * key, const char * sep, const apr_array_header_t * values) { int append = 0; if (key[0] == '+') { key++; append = 1; } long start = 0; const char * key_n = apr_pstrcat(r->pool, key, sep, "N", NULL); if (append) { const char * start_index = apr_table_get(t, key_n); if (start_index) { start = atol(start_index); } } for (int i = 0; values && i < values->nelts; i++) { apr_table_setn(t, apr_psprintf(r->pool, "%s%s%ld", key, sep, ++start), apr_pstrdup(r->pool, ((char **)values->elts)[i])); } apr_table_setn(t, key_n, apr_psprintf(r->pool, "%ld", start)); } static void lookup_identity_output_iter(request_rec * r, int the_output, const char * key, const apr_array_header_t * values) { if (the_output & LOOKUP_IDENTITY_OUTPUT_NOTES) { lookup_identity_output_iter_to(r, r->notes, key, "_", values); } if (the_output & LOOKUP_IDENTITY_OUTPUT_ENV) { lookup_identity_output_iter_to(r, r->subprocess_env, key, "_", values); } if (the_output & LOOKUP_IDENTITY_OUTPUT_HEADERS_BASE64) { lookup_identity_output_iter_to(r, r->headers_in, key, "-", base64_encode_array(r->pool, values)); } else if (the_output & LOOKUP_IDENTITY_OUTPUT_HEADERS) { lookup_identity_output_iter_to(r, r->headers_in, key, "-", values); } } static void lookup_identity_output_data_to(request_rec * r, apr_table_t * t, const char * key, const apr_array_header_t * values, const char * sep) { int append = 0; if (key[0] == '+') { key++; append = 1; } const char * value = apr_table_get(t, key); char * out_value = NULL; if (value) { if (!(append && sep)) { return; } out_value = apr_pstrdup(r->pool, value); } for (int i = 0; values && i < values->nelts; i++) { if (!out_value) { out_value = apr_pstrdup(r->pool, ((char **)values->elts)[i]); } else { if (!sep) { break; } out_value = apr_pstrcat(r->pool, out_value, sep, NULL); out_value = apr_pstrcat(r->pool, out_value, ((char **)values->elts)[i], NULL); } } apr_table_setn(t, key, out_value); } static void lookup_identity_output_data(request_rec * r, int the_output, const char * key, const apr_array_header_t * values, const char * sep) { if (the_output & LOOKUP_IDENTITY_OUTPUT_NOTES) { lookup_identity_output_data_to(r, r->notes, key, values, sep); } if (the_output & LOOKUP_IDENTITY_OUTPUT_ENV) { lookup_identity_output_data_to(r, r->subprocess_env, key, values, sep); } if (the_output & LOOKUP_IDENTITY_OUTPUT_HEADERS_BASE64) { lookup_identity_output_data_to(r, r->headers_in, key, base64_encode_array(r->pool, values), sep); } else if (the_output & LOOKUP_IDENTITY_OUTPUT_HEADERS) { lookup_identity_output_data_to(r, r->headers_in, key, values, sep); } } static void * merge_dir_conf(apr_pool_t * pool, void * base_void, void * add_void); static lookup_identity_config * create_common_conf(apr_pool_t * pool); static int lookup_identity_hook(request_rec * r) { lookup_identity_config * cfg = (lookup_identity_config *) ap_get_module_config(r->per_dir_config, &lookup_identity_module); lookup_identity_config * srv_cfg = (lookup_identity_config *) ap_get_module_config(r->server->module_config, &lookup_identity_module); if (! r->user) { return DECLINED; } if (!(cfg || srv_cfg)) { return DECLINED; } lookup_identity_config * the_config; if (srv_cfg) { if (cfg) { the_config = merge_dir_conf(r->pool, srv_cfg, cfg); } else { the_config = srv_cfg; } } else { the_config = cfg; } int the_output = the_config->output; if (the_output == LOOKUP_IDENTITY_OUTPUT_DEFAULT) { the_output = LOOKUP_IDENTITY_OUTPUT_NOTES | LOOKUP_IDENTITY_OUTPUT_ENV; } else if (the_output & LOOKUP_IDENTITY_OUTPUT_NONE) { return DECLINED; } ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, "invoked for user %s", r->user); struct passwd * pwd = getpwnam(r->user); if (! pwd) { return DECLINED; } if (the_config->output_gecos) { apr_array_header_t * gecos_array = apr_array_make(r->pool, 1, sizeof(char *)); *(char **)apr_array_push(gecos_array) = pwd->pw_gecos; lookup_identity_output_data(r, the_output, the_config->output_gecos, gecos_array, NULL); } #ifndef NO_USER_ATTR int the_timeout = DBUS_SSSD_TIMEOUT; if (the_config->dbus_timeout > 0) { the_timeout = the_config->dbus_timeout; } if (the_config->output_groups || the_config->output_groups_iter || the_config->output_user_attr) { DBusError error; dbus_error_init(&error); DBusConnection * connection = dbus_bus_get(DBUS_BUS_SYSTEM, &error); if (! connection) { ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server, "Error connecting to system dbus: %s", error.message); } else { dbus_connection_set_exit_on_disconnect(connection, FALSE); if (the_config->output_groups || the_config->output_groups_iter) { DBusMessage * reply = lookup_identity_dbus_message(r, connection, &error, the_timeout, DBUS_SSSD_GET_USER_GROUPS_METHOD, NULL); int num; char ** ptr; if (reply && dbus_message_get_args(reply, &error, DBUS_TYPE_ARRAY, DBUS_TYPE_STRING, &ptr, &num, DBUS_TYPE_INVALID)) { int i; apr_array_header_t * values = NULL; if (num) { values = apr_array_make(r->pool, num, sizeof(char *)); } for (i = 0; i < num; i++) { ap_log_error(APLOG_MARK, APLOG_INFO, 0, r->server, "dbus call %s returned group %s", DBUS_SSSD_GET_USER_GROUPS_METHOD, ptr[i]); *(char **)apr_array_push(values) = ptr[i]; } if (num && the_config->output_groups) { lookup_identity_output_data(r, the_output, the_config->output_groups, values, the_config->output_groups_sep); } if (the_config->output_groups_iter) { lookup_identity_output_iter(r, the_output, the_config->output_groups_iter, values); } dbus_free_string_array(ptr); } if (reply) { dbus_message_unref(reply); } if (dbus_error_is_set(&error)) { dbus_error_free(&error); } } if (the_config->output_user_attr) { apr_hash_t * seen = NULL; if (the_config->output_user_attr_iter) { seen = apr_hash_make(r->pool); } DBusMessage * reply = lookup_identity_dbus_message(r, connection, &error, the_timeout, DBUS_SSSD_GET_USER_ATTR_METHOD, the_config->output_user_attr); if (reply) { DBusMessageIter iter; dbus_message_iter_init(reply, &iter); int type = dbus_message_iter_get_arg_type(&iter); if (type == DBUS_TYPE_ARRAY) { dbus_message_iter_recurse(&iter, &iter); do { type = dbus_message_iter_get_arg_type(&iter); if (type != DBUS_TYPE_DICT_ENTRY) { ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server, "dbus call %s returned value %d instead of DBUS_TYPE_DICT_ENTRY", DBUS_SSSD_GET_USER_ATTR_METHOD, type); continue; } DBusMessageIter dictiter; dbus_message_iter_recurse(&iter, &dictiter); char * attr_name; dbus_message_iter_get_basic(&dictiter, &attr_name); char * out_name = apr_hash_get(the_config->output_user_attr, attr_name, APR_HASH_KEY_STRING); if (!out_name) { ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server, "dbus call %s returned key %s that we did not ask for", DBUS_SSSD_GET_USER_ATTR_METHOD, attr_name); continue; } if (seen) { apr_hash_set(seen, attr_name, APR_HASH_KEY_STRING, ""); } if (! dbus_message_iter_next(&dictiter)) { ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server, "dbus call %s returned key %s with no value", DBUS_SSSD_GET_USER_ATTR_METHOD, attr_name); } type = dbus_message_iter_get_arg_type(&dictiter); if (type != DBUS_TYPE_VARIANT) { ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server, "dbus call %s returned key %s which does not have DBUS_TYPE_VARIANT as value", DBUS_SSSD_GET_USER_ATTR_METHOD, attr_name); continue; } dbus_message_iter_recurse(&dictiter, &dictiter); type = dbus_message_iter_get_arg_type(&dictiter); if (type != DBUS_TYPE_ARRAY) { ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server, "dbus call %s returned key %s which does not have DBUS_TYPE_VARIANT DBUS_TYPE_ARRAY as value", DBUS_SSSD_GET_USER_ATTR_METHOD, attr_name); continue; } dbus_message_iter_recurse(&dictiter, &dictiter); char * out_sep = the_config->output_user_attr_sep ? apr_hash_get(the_config->output_user_attr_sep, attr_name, APR_HASH_KEY_STRING) : NULL; char * out_name_iter = the_config->output_user_attr_iter ? apr_hash_get(the_config->output_user_attr_iter, attr_name, APR_HASH_KEY_STRING) : NULL; apr_array_header_t * values = NULL; do { char * r_data; dbus_message_iter_get_basic(&dictiter, &r_data); ap_log_error(APLOG_MARK, APLOG_INFO, 0, r->server, "dbus call %s returned attr %s=%s", DBUS_SSSD_GET_USER_ATTR_METHOD, attr_name, r_data); if (! values) { values = apr_array_make(r->pool, 1, sizeof(char *)); } *(char **)apr_array_push(values) = r_data; } while (dbus_message_iter_next(&dictiter)); if (values && strlen(out_name)) { lookup_identity_output_data(r, the_output, out_name, values, out_sep); } if (out_name_iter) { lookup_identity_output_iter(r, the_output, out_name_iter, values); } } while (dbus_message_iter_next(&iter)); } dbus_message_unref(reply); } if (the_config->output_user_attr_iter) { apr_hash_index_t * hi = apr_hash_first(r->pool, the_config->output_user_attr_iter); while (hi) { const void * key; void * value; apr_hash_this(hi, &key, NULL, &value); if (! apr_hash_get(seen, key, APR_HASH_KEY_STRING)) { lookup_identity_output_iter(r, the_output, value, NULL); } hi = apr_hash_next(hi); } } } dbus_connection_unref(connection); } dbus_error_free(&error); } #endif return OK; } APR_DECLARE_OPTIONAL_FN(int, lookup_identity_hook, (request_rec * r)); static const char * set_output(cmd_parms * cmd, void * conf_void, const char * arg) { lookup_identity_config * cfg = (lookup_identity_config *) conf_void; if (cfg) { if (!strcasecmp(arg, "none")) { cfg->output = LOOKUP_IDENTITY_OUTPUT_NONE; } else if (!strcasecmp(arg, "all")) { cfg->output |= LOOKUP_IDENTITY_OUTPUT_ENV | LOOKUP_IDENTITY_OUTPUT_NOTES; ap_log_error(APLOG_MARK, APLOG_ERR, 0, cmd->server, "LookupOutput all is deprecated, use none, env, notes, or headers"); } else if (!strcasecmp(arg, "env")) { cfg->output |= LOOKUP_IDENTITY_OUTPUT_ENV; } else if (!strcasecmp(arg, "notes")) { cfg->output |= LOOKUP_IDENTITY_OUTPUT_NOTES; } else if (!strcasecmp(arg, "headers-base64")) { cfg->output |= LOOKUP_IDENTITY_OUTPUT_HEADERS_BASE64; } else if (!strcasecmp(arg, "headers")) { cfg->output |= LOOKUP_IDENTITY_OUTPUT_HEADERS; } } return NULL; } #ifndef NO_USER_ATTR static const char * set_output_groups(cmd_parms * cmd, void * conf_void, const char * arg, const char * sep) { lookup_identity_config * cfg = (lookup_identity_config *) conf_void; if (cfg) { cfg->output_groups = apr_pstrdup(cmd->pool, arg); if (sep) { cfg->output_groups_sep = apr_pstrdup(cmd->pool, sep); } } return NULL; } static const char * set_user_attr(cmd_parms * cmd, void * conf_void, const char * attrib, const char * output, const char * sep) { lookup_identity_config * cfg = (lookup_identity_config *) conf_void; if (cfg) { if (!cfg->output_user_attr) { cfg->output_user_attr = apr_hash_make(cmd->pool); } char * key = apr_pstrdup(cmd->pool, attrib); apr_hash_set(cfg->output_user_attr, key, APR_HASH_KEY_STRING, apr_pstrdup(cmd->pool, output)); if (sep) { if (!cfg->output_user_attr_sep) { cfg->output_user_attr_sep = apr_hash_make(cmd->pool); } apr_hash_set(cfg->output_user_attr_sep, key, APR_HASH_KEY_STRING, apr_pstrdup(cmd->pool, sep)); } } return NULL; } static const char * set_user_attr_iter(cmd_parms * cmd, void * conf_void, const char * attrib, const char * output) { lookup_identity_config * cfg = (lookup_identity_config *) conf_void; if (cfg) { if (!cfg->output_user_attr_iter) { cfg->output_user_attr_iter = apr_hash_make(cmd->pool); } char * key = apr_pstrdup(cmd->pool, attrib); apr_hash_set(cfg->output_user_attr_iter, key, APR_HASH_KEY_STRING, apr_pstrdup(cmd->pool, output)); if (!cfg->output_user_attr) { cfg->output_user_attr = apr_hash_make(cmd->pool); } if (! apr_hash_get(cfg->output_user_attr, key, APR_HASH_KEY_STRING)) { apr_hash_set(cfg->output_user_attr, key, APR_HASH_KEY_STRING, ""); } } return NULL; } #endif static lookup_identity_config * create_common_conf(apr_pool_t * pool) { lookup_identity_config * cfg = apr_pcalloc(pool, sizeof(lookup_identity_config)); if (cfg) { cfg->output = LOOKUP_IDENTITY_OUTPUT_DEFAULT; cfg->output_gecos = LOOKUP_IDENTITY_OUTPUT_GECOS; #ifndef NO_USER_ATTR cfg->lookup_by_certificate = -1; #endif } return cfg; } static void * create_server_conf(apr_pool_t * pool, server_rec * s) { lookup_identity_config * cfg = create_common_conf(pool); if (cfg) cfg->context = apr_psprintf(pool, "(server %s)", s->server_hostname); return cfg; } static void * create_dir_conf(apr_pool_t * pool, char * context) { lookup_identity_config * cfg = create_common_conf(pool); if (cfg) { context = context ? context : "(no directory context)"; cfg->context = apr_pstrdup(pool, context); } return cfg; } static void * merge_dir_conf(apr_pool_t * pool, void * base_void, void * add_void) { lookup_identity_config * base = (lookup_identity_config *) base_void; lookup_identity_config * add = (lookup_identity_config *) add_void; lookup_identity_config * cfg = (lookup_identity_config *) create_dir_conf(pool, add->context); cfg->output = (add->output == LOOKUP_IDENTITY_OUTPUT_DEFAULT) ? base->output : add->output; cfg->output_gecos = add->output_gecos ? add->output_gecos : base->output_gecos; #ifndef NO_USER_ATTR cfg->output_groups = add->output_groups ? add->output_groups : base->output_groups; cfg->output_groups_sep = add->output_groups_sep ? add->output_groups_sep : base->output_groups_sep; cfg->output_groups_iter = add->output_groups_iter ? add->output_groups_iter : base->output_groups_iter; if (base->output_user_attr) { if (add->output_user_attr) { cfg->output_user_attr = apr_hash_overlay(pool, add->output_user_attr, base->output_user_attr); } else { cfg->output_user_attr = base->output_user_attr; } } else if (add->output_user_attr) { cfg->output_user_attr = add->output_user_attr; } if (base->output_user_attr_sep) { if (add->output_user_attr_sep) { cfg->output_user_attr_sep = apr_hash_overlay(pool, add->output_user_attr_sep, base->output_user_attr_sep); } else { cfg->output_user_attr_sep = base->output_user_attr_sep; } } else if (add->output_user_attr_sep) { cfg->output_user_attr_sep = add->output_user_attr_sep; } if (base->output_user_attr_iter) { if (add->output_user_attr_iter) { cfg->output_user_attr_iter = apr_hash_overlay(pool, add->output_user_attr_iter, base->output_user_attr_iter); } else { cfg->output_user_attr_iter = base->output_user_attr_iter; } } else if (add->output_user_attr_iter) { cfg->output_user_attr_iter = add->output_user_attr_iter; } cfg->dbus_timeout = add->dbus_timeout ? add->dbus_timeout : base->dbus_timeout; cfg->lookup_by_certificate = (add->lookup_by_certificate == -1) ? base->lookup_by_certificate : add->lookup_by_certificate; #endif return cfg; } static const command_rec directives[] = { AP_INIT_TAKE1("LookupOutput", set_output, NULL, RSRC_CONF | ACCESS_CONF, "Specify where the lookup results should be stored (notes, variables, headers)"), AP_INIT_TAKE1("LookupUserGECOS", ap_set_string_slot, (void*)APR_OFFSETOF(lookup_identity_config, output_gecos), RSRC_CONF | ACCESS_CONF, "Name of the note/variable for the GECOS information"), #ifndef NO_USER_ATTR AP_INIT_TAKE12("LookupUserGroups", set_output_groups, NULL, RSRC_CONF | ACCESS_CONF, "Name of the note/variable for the group information"), AP_INIT_TAKE1("LookupUserGroupsIter", ap_set_string_slot, (void*)APR_OFFSETOF(lookup_identity_config, output_groups_iter), RSRC_CONF | ACCESS_CONF, "Name of the notes/variables for the group information"), AP_INIT_TAKE23("LookupUserAttr", set_user_attr, NULL, RSRC_CONF | ACCESS_CONF, "Additional user attribute (attr, note/variable name, separator)"), AP_INIT_TAKE2("LookupUserAttrIter", set_user_attr_iter, NULL, RSRC_CONF | ACCESS_CONF, "Additional user attributes (attr, note/variable name)"), AP_INIT_TAKE1("LookupDbusTimeout", ap_set_int_slot, (void*)APR_OFFSETOF(lookup_identity_config, dbus_timeout), RSRC_CONF | ACCESS_CONF, "Timeout for sssd dbus calls (in ms)"), AP_INIT_FLAG("LookupUserByCertificate", ap_set_flag_slot, (void*)APR_OFFSETOF(lookup_identity_config, lookup_by_certificate), RSRC_CONF | ACCESS_CONF, "Use org.freedesktop.sssd.infopipe.Users.FindByCertificate to lookup user identity"), #endif { NULL } }; static void register_hooks(apr_pool_t * pool) { #ifndef NO_USER_ATTR static const char * const access_succ[] = {"mod_authz_core.c", NULL}; static const char * const access_pred[] = {"mod_ssl.c", NULL}; #ifdef AP_AUTH_INTERNAL_PER_CONF ap_hook_check_access(lookup_user_by_certificate, access_pred, access_succ, APR_HOOK_MIDDLE, AP_AUTH_INTERNAL_PER_CONF); #else ap_hook_access_checker(lookup_user_by_certificate, access_pred, access_succ, APR_HOOK_MIDDLE); #endif #endif static const char * const fixup_succ[] = {"mod_headers.c", NULL}; ap_hook_fixups(lookup_identity_hook, NULL, fixup_succ, APR_HOOK_LAST); APR_REGISTER_OPTIONAL_FN(lookup_identity_hook); } module AP_MODULE_DECLARE_DATA lookup_identity_module = { STANDARD20_MODULE_STUFF, create_dir_conf, /* Per-directory configuration handler */ merge_dir_conf, /* Merge handler for per-directory configurations */ create_server_conf, /* Per-server configuration handler */ merge_dir_conf, /* Merge handler for per-server configurations */ directives, /* Any directives we may have for httpd */ register_hooks /* Our hook registering function */ };