From c9316efd00a49b02996b74b0c50737938d36227b Mon Sep 17 00:00:00 2001 From: Packit Service Date: Feb 03 2021 22:22:53 +0000 Subject: mod_lookup_identity-1.0.0 base --- diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..37ec93a --- /dev/null +++ b/LICENSE @@ -0,0 +1,191 @@ +Apache License +Version 2.0, January 2004 +http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + +"License" shall mean the terms and conditions for use, reproduction, and +distribution as defined by Sections 1 through 9 of this document. + +"Licensor" shall mean the copyright owner or entity authorized by the copyright +owner that is granting the License. + +"Legal Entity" shall mean the union of the acting entity and all other entities +that control, are controlled by, or are under common control with that entity. +For the purposes of this definition, "control" means (i) the power, direct or +indirect, to cause the direction or management of such entity, whether by +contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the +outstanding shares, or (iii) beneficial ownership of such entity. + +"You" (or "Your") shall mean an individual or Legal Entity exercising +permissions granted by this License. + +"Source" form shall mean the preferred form for making modifications, including +but not limited to software source code, documentation source, and configuration +files. + +"Object" form shall mean any form resulting from mechanical transformation or +translation of a Source form, including but not limited to compiled object code, +generated documentation, and conversions to other media types. + +"Work" shall mean the work of authorship, whether in Source or Object form, made +available under the License, as indicated by a copyright notice that is included +in or attached to the work (an example is provided in the Appendix below). + +"Derivative Works" shall mean any work, whether in Source or Object form, that +is based on (or derived from) the Work and for which the editorial revisions, +annotations, elaborations, or other modifications represent, as a whole, an +original work of authorship. For the purposes of this License, Derivative Works +shall not include works that remain separable from, or merely link (or bind by +name) to the interfaces of, the Work and Derivative Works thereof. + +"Contribution" shall mean any work of authorship, including the original version +of the Work and any modifications or additions to that Work or Derivative Works +thereof, that is intentionally submitted to Licensor for inclusion in the Work +by the copyright owner or by an individual or Legal Entity authorized to submit +on behalf of the copyright owner. For the purposes of this definition, +"submitted" means any form of electronic, verbal, or written communication sent +to the Licensor or its representatives, including but not limited to +communication on electronic mailing lists, source code control systems, and +issue tracking systems that are managed by, or on behalf of, the Licensor for +the purpose of discussing and improving the Work, but excluding communication +that is conspicuously marked or otherwise designated in writing by the copyright +owner as "Not a Contribution." + +"Contributor" shall mean Licensor and any individual or Legal Entity on behalf +of whom a Contribution has been received by Licensor and subsequently +incorporated within the Work. + +2. Grant of Copyright License. + +Subject to the terms and conditions of this License, each Contributor hereby +grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, +irrevocable copyright license to reproduce, prepare Derivative Works of, +publicly display, publicly perform, sublicense, and distribute the Work and such +Derivative Works in Source or Object form. + +3. Grant of Patent License. + +Subject to the terms and conditions of this License, each Contributor hereby +grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, +irrevocable (except as stated in this section) patent license to make, have +made, use, offer to sell, sell, import, and otherwise transfer the Work, where +such license applies only to those patent claims licensable by such Contributor +that are necessarily infringed by their Contribution(s) alone or by combination +of their Contribution(s) with the Work to which such Contribution(s) was +submitted. If You institute patent litigation against any entity (including a +cross-claim or counterclaim in a lawsuit) alleging that the Work or a +Contribution incorporated within the Work constitutes direct or contributory +patent infringement, then any patent licenses granted to You under this License +for that Work shall terminate as of the date such litigation is filed. + +4. Redistribution. + +You may reproduce and distribute copies of the Work or Derivative Works thereof +in any medium, with or without modifications, and in Source or Object form, +provided that You meet the following conditions: + +You must give any other recipients of the Work or Derivative Works a copy of +this License; and +You must cause any modified files to carry prominent notices stating that You +changed the files; and +You must retain, in the Source form of any Derivative Works that You distribute, +all copyright, patent, trademark, and attribution notices from the Source form +of the Work, excluding those notices that do not pertain to any part of the +Derivative Works; and +If the Work includes a "NOTICE" text file as part of its distribution, then any +Derivative Works that You distribute must include a readable copy of the +attribution notices contained within such NOTICE file, excluding those notices +that do not pertain to any part of the Derivative Works, in at least one of the +following places: within a NOTICE text file distributed as part of the +Derivative Works; within the Source form or documentation, if provided along +with the Derivative Works; or, within a display generated by the Derivative +Works, if and wherever such third-party notices normally appear. The contents of +the NOTICE file are for informational purposes only and do not modify the +License. You may add Your own attribution notices within Derivative Works that +You distribute, alongside or as an addendum to the NOTICE text from the Work, +provided that such additional attribution notices cannot be construed as +modifying the License. +You may add Your own copyright statement to Your modifications and may provide +additional or different license terms and conditions for use, reproduction, or +distribution of Your modifications, or for any such Derivative Works as a whole, +provided Your use, reproduction, and distribution of the Work otherwise complies +with the conditions stated in this License. + +5. Submission of Contributions. + +Unless You explicitly state otherwise, any Contribution intentionally submitted +for inclusion in the Work by You to the Licensor shall be under the terms and +conditions of this License, without any additional terms or conditions. +Notwithstanding the above, nothing herein shall supersede or modify the terms of +any separate license agreement you may have executed with Licensor regarding +such Contributions. + +6. Trademarks. + +This License does not grant permission to use the trade names, trademarks, +service marks, or product names of the Licensor, except as required for +reasonable and customary use in describing the origin of the Work and +reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. + +Unless required by applicable law or agreed to in writing, Licensor provides the +Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, +including, without limitation, any warranties or conditions of TITLE, +NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are +solely responsible for determining the appropriateness of using or +redistributing the Work and assume any risks associated with Your exercise of +permissions under this License. + +8. Limitation of Liability. + +In no event and under no legal theory, whether in tort (including negligence), +contract, or otherwise, unless required by applicable law (such as deliberate +and grossly negligent acts) or agreed to in writing, shall any Contributor be +liable to You for damages, including any direct, indirect, special, incidental, +or consequential damages of any character arising as a result of this License or +out of the use or inability to use the Work (including but not limited to +damages for loss of goodwill, work stoppage, computer failure or malfunction, or +any and all other commercial damages or losses), even if such Contributor has +been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. + +While redistributing the Work or Derivative Works thereof, You may choose to +offer, and charge a fee for, acceptance of support, warranty, indemnity, or +other liability obligations and/or rights consistent with this License. However, +in accepting such obligations, You may act only on Your own behalf and on Your +sole responsibility, not on behalf of any other Contributor, and only if You +agree to indemnify, defend, and hold each Contributor harmless for any liability +incurred by, or claims asserted against, such Contributor by reason of your +accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work + +To apply the Apache License to your work, attach the following boilerplate +notice, with the fields enclosed by brackets "[]" replaced with your own +identifying information. (Don't include the brackets!) The text should be +enclosed in the appropriate comment syntax for the file format. We also +recommend that a file or class name and description of purpose be included on +the same "printed page" as the copyright notice for easier identification within +third-party archives. + + Copyright [yyyy] [name of copyright owner] + + 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. diff --git a/README b/README new file mode 100644 index 0000000..97c6ebc --- /dev/null +++ b/README @@ -0,0 +1,367 @@ + +Apache module mod_lookup_identity +================================= + +Apache module to lookup user identifier and retrieve additional +information about the authenticated user. + +The module supports lookup of the user identifier based on +certificate via SSSD D-Bus call, setting up the r->user request +structure. +Method org.freedesktop.sssd.infopipe.Users.FindByNameAndCertificate +is used by default and value of query string parameter configured +with directive LookupUserByCertificateParamName is passed to SSSD +together with certificate to allow mapping of single certificate to +multiple user accounts. +Setting NO_CERTIFICATE_MAPPING_SUPPORT at build time changes the +module so it does not check the query string and calls SSSD D-Bus +method org.freedesktop.sssd.infopipe.Users.FindByCertificate. + +It also allows retrieval of list of group names the user belongs to +using org.freedesktop.sssd.infopipe.GetUserGroups call and any +custom attributes configured in /etc/sssd/sssd.conf using with +org.freedesktop.sssd.infopipe.GetUserAttr call. It can also retrieve +the GECOS information using the getpwnam call. The retrieved values +get stored in notes/environment variables. + +The sssd-dbus package needs to be installed and the ifp service +enabled in the [sssd] section of /etc/sssd/sssd.conf. + +The module itself needs to be loaded into Apache's configuration file +like /etc/httpd/conf/httpd.conf with directive like + + LoadModule lookup_identity_module modules/mod_lookup_identity.so + +User Lookup +----------- + +By default, no user identifier lookup is enabled. Directive + + LookupUserByCertificate On + +enables the lookup. It uses the original internal r->user value +as input, so the typical setup for example with mod_ssl will be + + SSLVerifyClient require + SSLUserName SSL_CLIENT_CERT + LookupUserByCertificate On + +when mod_ssl puts the certificate to the username field and then +mod_lookup_identity replaces it with user identifier found with +org.freedesktop.sssd.infopipe.Users.FindByCertificate. If no user +is found based on the certificate, the r->user value is cleared. +Make sure the FakeBasicAuth is not enable in SSLOptions or the +r->user will not be set. + +SSSD version 1.13 or higher is needed for LookupUserByCertificate +functionality. + +Retrieving Group Membership and Attributes +------------------------------------------ + +For the custom attributes fetching, caching of non-POSIX attributes +needs to be enabled in the [domain/*] section of /etc/sssd/sssd.conf, +configuration ldap_user_extra_attrs, and the attributes also need to +be enabled in the [ifp] section using the user_attributes +configuration option. + +Example of sssd.conf: + + [domain/example.com] + ... + ldap_user_extra_attrs = mail, telephoneNumber, givenname, sn + + [sssd] + services = nss, pam, ssh, ifp + + [ifp] + allowed_uids = apache, root + user_attributes = +mail, +telephoneNumber, +givenname, +sn + +The default behaviour can be changed with the following directives: + + LookupOutput where_to_store_results + + Specifies if the lookups will be done at all and where the results + of the lookup will be stored. Possible values are: + + None: Disable the lookup altogether + Notes: Sets the Apache notes table only + Env: Sets environment variables only + Headers: Sets HTTP request headers, for use by proxy setups. + Headers-Base64: Sets HTTP request headers with values + Base64-encoded, for use by proxy setups. + + The default is Notes and Env. + + LookupUserGECOS name + + Name of the note and/or environment variable for the GECOS + value. If prefixed with '+' sign, it is set only if the + note/environment variable is not set yet, otherwise the + value is overwritten. + + Setting this option requires for the user identity to be + POSIX identity, retrievable with getpwnam. + + Example: LookupUserGECOS REMOTE_USER_FULLNAME + + Example: LookupUserGECOS +REMOTE_USER_GECOS + + Default is not set. + + LookupUserGroups name [separator] + + Name of the note and/or environment variable for the list of groups + retrieved using the org.freedesktop.sssd.infopipe.GetUserGroups + call, and optionally separator for multiple groups. + + If the separator is specified, it is used to concatenate + multiple groups in single string value of the + note/environment variable. If the separator is not specified, + only one group is set. + + Example: if user alice is member of groups staff and student, + option + + LookupUserGroups REMOTE_USER_GROUPS : + + will set value of REMOTE_USER_GROUPS to staff:student (or + student:staff, depending on the order returned by the sssd + dbus call). If the option is + + LookupUserGroups REMOTE_USER_GROUPS + + the value will be either staff or student (the first in the list + returned by the sssd dbus call; order not to be relied on). + + When + + LookupOutput headers-base64 + + is specified, the values are encoded individually and then + concatenated. For the staff and student values example, + + LookupUserGroups REMOTE-USER-GROUPS : + + will produce c3RhZmY=:c3R1ZGVudA==. + + When prefixed with '+' sign and the note/environment variable + already has some value set, behaviour differs depending on + whether the optional separator is specified or not. If it is, + the string with separator-separated values is appended after + separator to existing value. If separator is not specified, + existing value is preserved. + + Example: when user alice is member of groups staff and student, + and the environment variable REMOTE_USER_GROUPS already has + value "anonymous" set (by Apache configuration or by some + module that was invoked before mod_lookup_identity), directive + + LookupUserGroups +REMOTE_USER_GROUPS : + + will set the value to "anonymous:staff:student" (or + "anonymous:student:staff"). On the other hand, + + LookupUserGroups +REMOTE_USER_GROUPS + + would leave the value unchanged at "anonymous". + + By default, groups are not retrieved. + + LookupUserGroupsIter name + + The number of groups the user is a member of (as returned by + the org.freedesktop.sssd.infopipe.GetUserGroups call) will be + stored in note/environment variable _N and individual + values in _1 .. _<_N>. This allows for + safe retrieval of groups without the separator clashing with + the values. + + Example: if user alice is member of groups staff and student, + option + + LookupUserGroupsIter REMOTE_USER_GROUP + + will set + + REMOTE_USER_GROUP_N=2 + REMOTE_USER_GROUP_1=staff + REMOTE_USER_GROUP_2=student + + (or the values of REMOTE_USER_GROUP_1 and REMOTE_USER_GROUP_2 + will be flipped). + + When + + LookupOutput headers-base64 + + is specified and assuming + + LookupUserGroupsIter REMOTE-USER-GROUP + + the HTTP header values will be + + REMOTE-USER-GROUP-N=2 + REMOTE-USER-GROUP-1=c3RhZmY= + REMOTE-USER-GROUP-2=c3R1ZGVudA== + + Note that the numerical _N is not Base64-encoded. + + If user is not a member of any group, the _N value will + be set to 0. + + When the name is prefixed with '+' sign, existing values are + preserved and new values added to the list. Thus, if alice is + member of groups staff and student and REMOTE_USER_GROUP_N + already has value 2 set and the directive is + + LookupUserGroupsIter +REMOTE_USER_GROUP + + the module will set values of REMOTE_USER_GROUP_3 + and REMOTE_USER_GROUP_4 and will update REMOTE_USER_GROUP_N + to value 4. The module will however not check/fix the consistency + of existing values; if REMOTE_USER_GROUP_N is set to value 2 + prior to invocation of mod_lookup_identity, it will not check + if REMOTE_USER_GROUP_1 and REMOTE_USER_GROUP_2 are set to match + REMOTE_USER_GROUP_N. + + By default, groups are not retrieved. + + LookupUserAttr the_attribute name [separator] + + Name of the attribute to be retrieved using the + org.freedesktop.sssd.infopipe.GetUserAttr call and name of + the note and/or environment variable where the value will + be stored, and optionally separator for multivalued results. + + If the separator is specified, it is used to concatenate + multiple values in single string value of the + note/environment variable. If the separator is not specified, + only one value is set. + + Example: + + LookupUserAttr mail REMOTE_USER_MAIL + + will retrieve one value from the mail attribute (from potentially + multivalued attribute) and store them to note/environment + variable REMOTE_USER_MAIL. + + Directive + + LookupUserAttr mail REMOTE_USER_MAIL ", " + + will retrieve all the values and store them as comma-separated + string. The same way as with LookupUserGroups, headers-base64 + will first Base64 encode and then concatenate. + + When the name is prefixed with '+' sign, similar to LookupUserGroups + it will only set the value if not set yet, or append to existing + value if separator is specified: when + + LookupUserAttr team +REMOTE_USER_TEAMS ", " + + is configured and REMOTE_USER_ORGS is already set to "IT" and + the sssd dbus returned values "Helpdesk" and "Support", the + resulting value will be "IT, Helpdek, Support". + + Multiple LookupUserAttr lines can be used to retrieve multiple + attributes. + + By default, no user attributes are retrieved. + + LookupUserAttrIter the_attribute name + + The number of attribute values for the user (as returned by + the org.freedesktop.sssd.infopipe.GetUserAttr call) will be + stored in note/environment variable _N and individual + values in _1 .. _<_N>. This allows for + safe retrieval of multivalued attributes without the + separator clashing with the values. + + Example: if user alice has multivalued custom attribute + office_no with values M314 and P005, + + LookupUserAttrIter office_no REMOTE_USER_OFFICE + + will cause the following notes/environment variables to be + set: + + REMOTE_USER_OFFICE_N=2 + REMOTE_USER_OFFICE_1=M314 + REMOTE_USER_OFFICE_2=P005 + + When the '+' sign is used, behaviour matches the behaviour of + LookupUserGroupsIter -- the list formed by _N and _# variables + is appended to. + + Multiple LookupUserAttr lines can be used to retrieve multiple + attributes. + + By default, no user attributes are retrieved. + + LookupDbusTimeout miliseconds + + Default: 5000 (== 5 s). + + LookupUserByCertificateParamName + + Name of parameter for HTTP request's query string. The value from + query string (if there is any) is then sent to SSSD together with + the certificate. This is useful when single certificate is + assigned to multiple user accounts. + + By default, no parameter is parsed from query string. + +Please note that LookupUserGroups and LookupUserGroupsIter, as well as +LookupUserAttr and LookupUserAttrIter for single attribute can be +configured with the same note/environment variable name. For example, + + LookupUserGroups REMOTE_USER_GROUPS : + LookupUserGroupsIter REMOTE_USER_GROUP + +can be set at the same time and for user with two groups, all the +following values will be set: + + REMOTE_USER_GROUPS=staff:student + REMOTE_USER_GROUP_N=2 + REMOTE_USER_GROUP_1=staff + REMOTE_USER_GROUP_2=student + +Building from sources +--------------------- + +When building from sources, command + + apxs -i -a -c $(pkg-config --cflags dbus-1) $(pkg-config --libs dbus-1) \ + -Wc,"-Wall -pedantic -std=c99" mod_lookup_identity.c + +should build and install the module. + +If the available version of sssd does not provide or is not configured +to provide the ifp dbus service, compile with + + apxs -DNO_USER_ATTR -i -a -c -Wc,"-Wall -pedantic -std=c99" \ + mod_lookup_identity.c + +In that case, the LookupUserAttr functionality will not be compiled +in and will not be available. + +License +------- + +Copyright 2013--2017 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. + diff --git a/lookup_identity.conf b/lookup_identity.conf new file mode 100644 index 0000000..d7f9740 --- /dev/null +++ b/lookup_identity.conf @@ -0,0 +1,10 @@ + +# +# LookupUserAttr mail REMOTE_USER_EMAIL " " +# LookupUserAttr givenname REMOTE_USER_FIRSTNAME +# LookupUserAttr sn REMOTE_USER_LASTNAME +# LookupUserAttrIter custom REMOTE_USER_CUSTOM +# LookupUserGroups REMOTE_USER_GROUPS ":" +# LookupUserGroupsIter REMOTE_USER_GROUP +# + diff --git a/lookup_identity.module b/lookup_identity.module new file mode 100644 index 0000000..d19aaa8 --- /dev/null +++ b/lookup_identity.module @@ -0,0 +1,3 @@ + +# LoadModule lookup_identity_module modules/mod_lookup_identity.so + diff --git a/mod_lookup_identity.c b/mod_lookup_identity.c new file mode 100644 index 0000000..649e252 --- /dev/null +++ b/mod_lookup_identity.c @@ -0,0 +1,804 @@ + +/* + * Copyright 2013--2017 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 "util_script.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" +#ifndef NO_CERTIFICATE_MAPPING_SUPPORT +#define DBUS_SSSD_FIND_BY_CERTIFICATE "FindByNameAndCertificate" +#else +#define DBUS_SSSD_FIND_BY_CERTIFICATE "FindByCertificate" +#endif +#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; + +typedef struct lookup_identity_config { + char * context; + int output; + char * output_gecos; +#ifndef NO_CERTIFICATE_MAPPING_SUPPORT + char * arg_name; +#endif +#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_rerror(APLOG_MARK, APLOG_INFO, 0, r, "lookup_user_by_certificate invoked [%s]", r->user); + + static char * stage = NULL; + DBusError error; + dbus_error_init(&error); + DBusMessage * message = NULL; + DBusMessage * reply = NULL; +#ifndef NO_CERTIFICATE_MAPPING_SUPPORT + const char * username = ""; + apr_table_t * arg_table = NULL; + + if (cfg->arg_name) { + ap_args_to_table(r, &arg_table); + username = apr_table_get(arg_table, cfg->arg_name); + if (username == NULL) { + username = ""; + } + } +#endif + + 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, +#ifndef NO_CERTIFICATE_MAPPING_SUPPORT + DBUS_TYPE_STRING, &username, +#endif + 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_rerror(APLOG_MARK, APLOG_INFO, 0, r, "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_rerror(APLOG_MARK, APLOG_INFO, 0, r, "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_rerror(APLOG_MARK, APLOG_ERR, 0, r, "lookup_user_by_certificate failed [%s]: [%s]", stage, error.message); + } else if (stage) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "lookup_user_by_certificate failed [%s]", stage); + } + r->user = NULL; + +pass: + if (! r->user) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "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_rerror(APLOG_MARK, APLOG_ERR, 0, r, "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_rerror(APLOG_MARK, APLOG_ERR, 0, r, + "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_rerror(APLOG_MARK, APLOG_ERR, 0, r, + "Error %s dbus calling %s(%s%s)", dbus_message_get_error_name(reply), method, user, args_string); + } else { + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, + "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_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "invoked for user %s", r->user); + + if (the_config->output_gecos) { + struct passwd * pwd = getpwnam(r->user); + if (! pwd) { + return DECLINED; + } + + 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_rerror(APLOG_MARK, APLOG_ERR, 0, r, + "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_rerror(APLOG_MARK, APLOG_INFO, 0, r, + "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_rerror(APLOG_MARK, APLOG_ERR, 0, r, + "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_rerror(APLOG_MARK, APLOG_ERR, 0, r, + "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_rerror(APLOG_MARK, APLOG_ERR, 0, r, + "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_rerror(APLOG_MARK, APLOG_ERR, 0, r, + "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_rerror(APLOG_MARK, APLOG_ERR, 0, r, + "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_rerror(APLOG_MARK, APLOG_INFO, 0, r, + "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 = NULL; +#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_CERTIFICATE_MAPPING_SUPPORT + cfg->arg_name = add->arg_name ? add->arg_name : base->arg_name; +#endif +#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_CERTIFICATE_MAPPING_SUPPORT + AP_INIT_TAKE1("LookupUserByCertificateParamName", ap_set_string_slot, (void*)APR_OFFSETOF(lookup_identity_config, arg_name), RSRC_CONF | ACCESS_CONF, "Name of the argument/variable in query string used to pass username"), +#endif +#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", "mod_nss.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); +} + +#ifdef AP_DECLARE_MODULE +AP_DECLARE_MODULE(lookup_identity) +#else +module AP_MODULE_DECLARE_DATA lookup_identity_module +#endif + = { + 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 */ +}; + diff --git a/mod_lookup_identity.spec b/mod_lookup_identity.spec new file mode 100644 index 0000000..9ccb0b6 --- /dev/null +++ b/mod_lookup_identity.spec @@ -0,0 +1,135 @@ +%{!?_httpd_mmn: %{expand: %%global _httpd_mmn %%(cat %{_includedir}/httpd/.mmn || echo 0-0)}} +%{!?_httpd_apxs: %{expand: %%global _httpd_apxs %%{_sbindir}/apxs}} +%{!?_httpd_confdir: %{expand: %%global _httpd_confdir %%{_sysconfdir}/httpd/conf.d}} +# /etc/httpd/conf.d with httpd < 2.4 and defined as /etc/httpd/conf.modules.d with httpd >= 2.4 +%{!?_httpd_modconfdir: %{expand: %%global _httpd_modconfdir %%{_sysconfdir}/httpd/conf.d}} +%{!?_httpd_moddir: %{expand: %%global _httpd_moddir %%{_libdir}/httpd/modules}} + +Summary: Apache module to retrieve additional information about the authenticated user +Name: mod_lookup_identity +Version: 1.0.0 +Release: 1%{?dist} +License: ASL 2.0 +Group: System Environment/Daemons +URL: http://www.adelton.com/apache/mod_lookup_identity/ +Source0: http://www.adelton.com/apache/mod_lookup_identity/%{name}-%{version}.tar.gz +BuildRequires: httpd-devel +BuildRequires: dbus-devel +BuildRequires: pkgconfig +Requires: httpd-mmn = %{_httpd_mmn} + +# Suppres auto-provides for module DSO per +# https://fedoraproject.org/wiki/Packaging:AutoProvidesAndRequiresFiltering#Summary +%{?filter_provides_in: %filter_provides_in %{_libdir}/httpd/modules/.*\.so$} +%{?filter_setup} + +%description +mod_lookup_identity can retrieve additional pieces of information +about user authenticated in Apache httpd server and store these values +in notes/environment variables to be consumed by web applications. +Use of REMOTE_USER_* environment variables is recommended. + +%prep +%setup -q -n %{name}-%{version} + +%build +%{_httpd_apxs} -c -Wc,"%{optflags} -Wall -pedantic -std=c99 $(pkg-config --cflags dbus-1)" $(pkg-config --libs dbus-1) mod_lookup_identity.c +%if "%{_httpd_modconfdir}" != "%{_httpd_confdir}" +echo > lookup_identity.confx +echo "# Load the module in %{_httpd_modconfdir}/55-lookup_identity.conf" >> lookup_identity.confx +cat lookup_identity.conf >> lookup_identity.confx +%else +cat lookup_identity.module > lookup_identity.confx +cat lookup_identity.conf >> lookup_identity.confx +%endif + +%install +rm -rf $RPM_BUILD_ROOT +install -Dm 755 .libs/mod_lookup_identity.so $RPM_BUILD_ROOT%{_httpd_moddir}/mod_lookup_identity.so + +%if "%{_httpd_modconfdir}" != "%{_httpd_confdir}" +# httpd >= 2.4.x +install -Dp -m 0644 lookup_identity.module $RPM_BUILD_ROOT%{_httpd_modconfdir}/55-lookup_identity.conf +%endif +install -Dp -m 0644 lookup_identity.confx $RPM_BUILD_ROOT%{_httpd_confdir}/lookup_identity.conf + +%files +%doc README LICENSE +%if "%{_httpd_modconfdir}" != "%{_httpd_confdir}" +%config(noreplace) %{_httpd_modconfdir}/55-lookup_identity.conf +%endif +%config(noreplace) %{_httpd_confdir}/lookup_identity.conf +%{_httpd_moddir}/*.so + +%changelog +* Tue Apr 04 2017 Jan Pazdziora - 1.0.0-1 +- Make LookupUserGECOS optional (no default) to support non-POSIX + user identities. + +* Wed Mar 22 2017 Jan Pazdziora - 0.9.9-1 +- Add support for multiple users mapped to single certificate. + +* Wed Nov 23 2016 Jan Pazdziora - 0.9.8-1 +- Logging improvements; lookup_user_by_certificate logging moved from + notice to info level. + +* Thu Jun 16 2016 Jan Pazdziora - 0.9.7-1 +- Ensure lookup_user_by_certificate runs after mod_nss as well. + +* Mon Mar 21 2016 Jan Pazdziora - 0.9.6-1 +- 1319138 - the Requires(pre) httpd does not seem to be needed. + +* Wed Jan 20 2016 Jan Pazdziora - 0.9.5-1 +- Fix LookupUserByCertificate httpd-2.2 compatibility issue. + +* Wed Jan 20 2016 Jan Pazdziora - 0.9.4-1 +- Added LookupUserOutput headers and headers-base64. + +* Mon Aug 03 2015 Jan Pazdziora - 0.9.3-1 +- Added LookupUserByCertificate. + +* Fri Jun 27 2014 Jan Pazdziora - 0.9.2-1 +- Fix error handling and reporting. +- Fix module loading/configuration for Apache 2.4. + +* Tue May 13 2014 Jan Pazdziora - 0.9.1-1 +- Address code issues revealed by coverity scan. +- Minor README fixes. + +* Tue May 13 2014 Jan Pazdziora - 0.9.0-1 +- Add support for '+'-prefixed note/variable names. +- Silence compile warnings by specifying C99. +- Fix format of logs of dbus calls. + +* Sat Feb 01 2014 Jan Pazdziora - 0.8.3-1 +- 1058812 - drop explicit dbus-libs dependency. + +* Thu Jan 30 2014 Jan Pazdziora - 0.8.2-1 +- 1058812 - .spec changes for Fedora package review. + +* Fri Jan 17 2014 Jan Pazdziora - 0.8.1-1 +- Ensure we run before mod_headers so that our result can be put to + request headers for mod_proxy. + +* Thu Jan 09 2014 Jan Pazdziora - 0.8-1 +- Declare all functions static for proper isolation. + +* Thu Nov 21 2013 Jan Pazdziora - 0.7.1-1 +- Address segfault when no LookupUserAttrIter is set. + +* Tue Nov 19 2013 Jan Pazdziora - 0.7-1 +- Define lookup_identity_hook as optional function, callable from + other modules. + +* Mon Nov 18 2013 Jan Pazdziora - 0.6-1 +- Use org.freedesktop.sssd.infopipe.GetUserGroups for group lists. +- Support new org.freedesktop.sssd.infopipe.GetUserAttr parameters / + return values. +- Removed LookupOutputGroupsSeparator. +- LookupOutputGroups and LookupUserAttr now support separator as + optional second parameter. +- Added LookupUserGroupsIter and LookupUserAttrIter. +- Added LookupDbusTimeout. + +* Mon Oct 28 2013 Jan Pazdziora - 0.5-1 +- Initial release.