├── .gitignore ├── ruid2.conf ├── README ├── contrib └── mod_ruid2-redhat.spec ├── LICENSE └── mod_ruid2.c /.gitignore: -------------------------------------------------------------------------------- 1 | .libs/ 2 | *.la 3 | *.lo 4 | *.o 5 | *.slo 6 | -------------------------------------------------------------------------------- /ruid2.conf: -------------------------------------------------------------------------------- 1 | # EXAMPLE: 2 | # 3 | # LoadModule ruid2_module modules/mod_ruid2.so 4 | # User apache 5 | # Group apache 6 | # RMode stat 7 | # RGroups apachetmp 8 | # RDocumentChRoot /home /example.com/public_html 9 | # 10 | # NameVirtualHost 192.168.0.1 11 | # 12 | # ServerAdmin webmaster@example.com 13 | # RDocumentChRoot /home /example.com/public_html 14 | # ServerName example.com 15 | # ServerAlias www.example.com 16 | # RMode config # unnecessary since config is the default 17 | # RUidGid user1 group1 18 | # RGroups apachetmp 19 | # 20 | # 21 | # RMode stat 22 | # 23 | # 24 | # 25 | # RMode config 26 | # RUidGid user2 group2 27 | # RGroups groups1 28 | # 29 | # 30 | # 31 | # RUidGid user3 group3 32 | # 33 | # 34 | # 35 | # RMode config 36 | # RUidGid user4 user4 37 | # RGroups groups4 38 | # 39 | # 40 | # 41 | # 42 | # 43 | # ServerAdmin webmaster@example.net 44 | # DocumentRoot /home/example.net/public_html 45 | # ServerName example.net 46 | # ServerAlias www.example.net 47 | # 48 | 49 | LoadModule ruid2_module modules/mod_ruid2.so 50 | 51 | # 52 | # RMode stat 53 | # RGroups @none 54 | # 55 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | ABOUT 2 | mod_ruid2 is a suexec module for apache 2.0, 2.2 and 2.4, based on mod_ruid and mod_suid2 3 | 4 | -it runs only on Linux because only the Linux kernel has implemented the required process capabilities. 5 | -it has better performance than mod_suid2 because it doesn`t need to kill httpd children 6 | after one request. it makes use of kernel capabilites and after receiving a new request suids again. 7 | -there are some security issues, for instance if attacker successfully exploits the httpd process, 8 | he can set effective capabilities and setuid to root. i recommend to use some security patch in kernel (grsec), 9 | or something.. 10 | 11 | -there are two main operation modes: stat and config 12 | 1. config 13 | is default, you must define uid and gid. If no [ug]id is defined the default user and group are used. 14 | 15 | 2. stat 16 | httpd setuid and setgid to uid and gid of requested filename(script)/directory 17 | this is good if you use mod_vhost_alias for virtual hosting 18 | 19 | INSTALL 20 | 1. download and install latest libcap from here 21 | 2. run /apachedir/bin/apxs -a -i -l cap -c mod_ruid2.c 22 | 3. configure httpd.conf 23 | 4. restart apache 24 | 25 | CONFIGURE OPTIONS: 26 | RMode config|stat (default is config) 27 | RUidGid user|#uid group|#gid - when RMode is config, set to this uid and gid 28 | 29 | RMinUidGid user|#uid group|#gid - when uid/gid is < than min uid/gid set to default uid/gid 30 | RDefaultUidGid user|#uid group|#gid 31 | 32 | RGroups group1 group2 - additional groups set via setgroups 33 | @none - clear all previous defined groups. 34 | 35 | RDocumentChrRoot - Set chroot directory and the document root inside 36 | 37 | 38 | EXAMPLE: 39 | 40 | LoadModule ruid2_module modules/mod_ruid2.so 41 | User apache 42 | Group apache 43 | RMode stat 44 | RGroups apachetmp 45 | RDocumentChRoot /home /example.com/public_html 46 | 47 | NameVirtualHost 192.168.0.1 48 | 49 | ServerAdmin webmaster@example.com 50 | RDocumentChRoot /home /example.com/public_html 51 | ServerName example.com 52 | ServerAlias www.example.com 53 | RMode config # unnecessary since config is the default 54 | RUidGid user1 group1 55 | RGroups apachetmp 56 | 57 | 58 | RMode stat 59 | 60 | 61 | 62 | RMode config 63 | RUidGid user2 group2 64 | RGroups groups1 65 | 66 | 67 | 68 | RUidGid user3 group3 69 | 70 | 71 | 72 | RMode config 73 | RUidGid user4 user4 74 | RGroups groups4 75 | 76 | 77 | 78 | 79 | 80 | ServerAdmin webmaster@example.net 81 | DocumentRoot /home/example.net/public_html 82 | ServerName example.net 83 | ServerAlias www.example.net 84 | 85 | -------------------------------------------------------------------------------- /contrib/mod_ruid2-redhat.spec: -------------------------------------------------------------------------------- 1 | Summary: Run all httpd process under user's access right. 2 | Name: mod_ruid2 3 | Version: 0.9.8 4 | Release: 1%{dist} 5 | Group: System Environment/Daemons 6 | URL: http://sourceforge.net/projects/mod-ruid/ 7 | Source0: http://sourceforge.net/projects/mod-ruid/files/mod_ruid2/mod_ruid2-%{version}.tar.bz2 8 | License: Apache Software License version 2 9 | BuildRoot: %{_tmppath}/%{name}-%{version}-root 10 | BuildRequires: httpd-devel >= 2.0.40 libcap-devel 11 | Requires: httpd >= 2.0.40 libcap 12 | Obsoletes: mod_ruid 13 | 14 | %description 15 | With this module, all httpd process run under user's access right, not nobody or apache. 16 | mod_ruid2 is similar to mod_suid2, but has better performance than mod_suid2 because it 17 | doesn`t need to kill httpd children after one request. It makes use of kernel capabilites 18 | and after receiving a new request suids again. If you want to run apache modules, i.e. 19 | WebDAV, PHP, and so on under user's right, this module is useful. 20 | 21 | %prep 22 | %setup -q 23 | 24 | %build 25 | %{_sbindir}/apxs -l cap -c %{name}.c 26 | mv .libs/%{name}.so . 27 | %{__strip} -g %{name}.so 28 | 29 | %install 30 | [ "$RPM_BUILD_ROOT" != "/" ] && rm -rf $RPM_BUILD_ROOT 31 | mkdir -p $RPM_BUILD_ROOT%{_libdir}/httpd/modules 32 | install -m755 %{name}.so $RPM_BUILD_ROOT%{_libdir}/httpd/modules 33 | 34 | # Install the config file 35 | mkdir -p $RPM_BUILD_ROOT%{_sysconfdir}/httpd/conf.d 36 | install -m 644 ruid2.conf \ 37 | $RPM_BUILD_ROOT%{_sysconfdir}/httpd/conf.d/ 38 | 39 | %clean 40 | [ "$RPM_BUILD_ROOT" != "/" ] && rm -rf $RPM_BUILD_ROOT 41 | 42 | %files 43 | %defattr(644,root,root,755) 44 | %doc README LICENSE 45 | %attr(755,root,root)%{_libdir}/httpd/modules/*.so 46 | %config(noreplace) %{_sysconfdir}/httpd/conf.d/*.conf 47 | 48 | 49 | %changelog 50 | * Fri Mar 22 2013 Kees Monshouwer 0.9.8-1 51 | - Address reported security bug in chroot mode. Thanks to the 52 | "cPanel Security Team" for the discovery of this bug. 53 | - Improve chroot behavior in drop capability mode. 54 | 55 | * Wed Apr 11 2012 Kees Monshouwer 0.9.7-1 56 | - Update to 0.9.7 57 | - Reduction of memory usage, especially in large deployments 58 | 59 | * Wed Apr 11 2012 Kees Monshouwer 0.9.6-1 60 | - Update to 0.9.6 61 | - Fixed: user group exchange in default config 62 | 63 | * Wed Mar 07 2012 Kees Monshouwer 0.9.5-1 64 | - Update to 0.9.5 65 | - Switch default mode to 'config' !!! 66 | - Apache 2.4 compatibility 67 | 68 | * Wed Feb 23 2011 Kees Monshouwer 0.9.4-1 69 | - Update to 0.9.4 70 | - Fixed: mod_security incompatibility issue 71 | 72 | * Tue Jan 04 2011 Kees Monshouwer 0.9.3-1 73 | - Update to 0.9.3 74 | - Fixed: chroot issue with sub-requests caused by mod_rewrite 75 | 76 | * Tue Dec 20 2010 Kees Monshouwer 0.9.2-1 77 | - Update to 0.9.2 78 | - Fixed: array subscript was above array bounds in ruid_set_perm 79 | 80 | * Mon Oct 18 2010 Kees Monshouwer 0.9.1-1 81 | - Update to 0.9.1 82 | 83 | * Wed Jun 23 2010 Kees Monshouwer 0.9-1 84 | - Added chroot functionality 85 | - Update to 0.9 86 | 87 | * Mon Jun 21 2010 Kees Monshouwer 0.8.2-1 88 | - Added drop capability mode to drop capabilities permanent after set[ug]id 89 | - Update to 0.8.2 90 | 91 | * Thu May 27 2010 Kees Monshouwer 0.8.1-1 92 | - Changed module name to mod_ruid2 93 | - Update to 0.8.1 94 | 95 | * Mon Apr 12 2010 Kees Monshouwer 0.8-1 96 | - Update to 0.8 97 | 98 | * Wed Oct 21 2009 Kees Monshouwer 0.7.1-1 99 | - Fixed security problem in config 100 | 101 | * Sun Sep 27 2009 Kees Monshouwer 0.7-1 102 | - Added per directory config option 103 | 104 | * Wed Aug 29 2007 Kees Monshouwer 0.6-3.1 105 | - Build for CentOS 5 106 | 107 | * Fri Sep 07 2006 Kees Monshouwer 0.6-3 108 | - Fixed first child request groups bug 109 | 110 | * Fri Sep 07 2006 Kees Monshouwer 0.6-2 111 | - Fixed some uninitalized vars and a typo 112 | - Changed the default user and group to apache 113 | 114 | * Wed Mar 08 2006 Kees Monshouwer 0.6-1 115 | - Inital build for CentOS 4.2 116 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright [yyyy] [name of copyright owner] 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | -------------------------------------------------------------------------------- /mod_ruid2.c: -------------------------------------------------------------------------------- 1 | /* 2 | mod_ruid2 0.9.8 3 | Copyright (C) 2009-2013 Monshouwer Internet Diensten 4 | 5 | Author: Kees Monshouwer 6 | 7 | Licensed under the Apache License, Version 2.0 (the "License"); 8 | you may not use this file except in compliance with the License. 9 | You may obtain a copy of the License at 10 | 11 | http://www.apache.org/licenses/LICENSE-2.0 12 | 13 | Unless required by applicable law or agreed to in writing, software 14 | distributed under the License is distributed on an "AS IS" BASIS, 15 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | See the License for the specific language governing permissions and 17 | limitations under the License. 18 | 19 | Based on: 20 | - mod_suid - http://bluecoara.net/servers/apache/mod_suid2_en.phtml 21 | Copyright 2004 by Hideo NAKAMITSU. All rights reserved 22 | - mod_ruid - http://websupport.sk/~stanojr/projects/mod_ruid/ 23 | Copyright 2004 by Pavel Stano. All rights reserved 24 | 25 | Instalation: 26 | - /usr/apache/bin/apxs -a -i -l cap -c mod_ruid2.c 27 | 28 | Issues: 29 | - https://github.com/mind04/mod-ruid2/issues 30 | */ 31 | 32 | #include 33 | 34 | /* define CORE_PRIVATE for apache < 2.4 */ 35 | #if AP_SERVER_MAJORVERSION_NUMBER == 2 && AP_SERVER_MINORVERSION_NUMBER < 4 36 | #define CORE_PRIVATE 37 | #endif 38 | 39 | #include 40 | #include 41 | #include 42 | #include 43 | #include 44 | #include 45 | #include 46 | #include 47 | #include 48 | 49 | #include 50 | #include 51 | #include 52 | 53 | #define MODULE_NAME "mod_ruid2" 54 | #define MODULE_VERSION "0.9.8" 55 | 56 | #define RUID_MIN_UID 100 57 | #define RUID_MIN_GID 100 58 | 59 | #define RUID_MAXGROUPS 8 60 | 61 | #define RUID_MODE_CONF 0 62 | #define RUID_MODE_STAT 1 63 | #define RUID_MODE_UNDEFINED 2 64 | 65 | #define RUID_MODE_STAT_NOT_USED 0 66 | #define RUID_MODE_STAT_USED 1 67 | #define RUID_CHROOT_NOT_USED 0 68 | #define RUID_CHROOT_USED 1 69 | 70 | #define RUID_CAP_MODE_DROP 0 71 | #define RUID_CAP_MODE_KEEP 1 72 | 73 | #define NONE -2 74 | #define UNSET -1 75 | #define SET 1 76 | 77 | #define UNUSED(x) (void)(x) 78 | 79 | /* added for apache 2.0 and 2.2 compatibility */ 80 | #if !AP_MODULE_MAGIC_AT_LEAST(20081201,0) 81 | #define ap_unixd_config unixd_config 82 | #endif 83 | 84 | typedef struct 85 | { 86 | int8_t ruid_mode; 87 | uid_t ruid_uid; 88 | gid_t ruid_gid; 89 | gid_t groups[RUID_MAXGROUPS]; 90 | int groupsnr; 91 | } ruid_dir_config_t; 92 | 93 | 94 | typedef struct 95 | { 96 | uid_t default_uid; 97 | gid_t default_gid; 98 | uid_t min_uid; 99 | gid_t min_gid; 100 | const char *chroot_dir; 101 | const char *document_root; 102 | } ruid_config_t; 103 | 104 | 105 | module AP_MODULE_DECLARE_DATA ruid2_module; 106 | 107 | 108 | static int mode_stat_used = RUID_MODE_STAT_NOT_USED; 109 | static int chroot_used = RUID_CHROOT_NOT_USED; 110 | static int cap_mode = RUID_CAP_MODE_KEEP; 111 | 112 | static int coredump, root_handle; 113 | static const char *old_root; 114 | 115 | static gid_t startup_groups[RUID_MAXGROUPS]; 116 | static int startup_groupsnr; 117 | 118 | 119 | static void *create_dir_config(apr_pool_t *p, char *d) 120 | { 121 | char *dname = d; 122 | ruid_dir_config_t *dconf = apr_pcalloc (p, sizeof(*dconf)); 123 | 124 | if (dname == NULL) { 125 | // Server config 126 | dconf->ruid_mode=RUID_MODE_CONF; 127 | } else { 128 | // Directory config 129 | dconf->ruid_mode=RUID_MODE_UNDEFINED; 130 | } 131 | dconf->ruid_uid=UNSET; 132 | dconf->ruid_gid=UNSET; 133 | dconf->groupsnr=UNSET; 134 | 135 | return dconf; 136 | } 137 | 138 | 139 | static void *merge_dir_config(apr_pool_t *p, void *base, void *overrides) 140 | { 141 | ruid_dir_config_t *parent = base; 142 | ruid_dir_config_t *child = overrides; 143 | ruid_dir_config_t *conf = apr_pcalloc(p, sizeof(ruid_dir_config_t)); 144 | 145 | if (child->ruid_mode == RUID_MODE_UNDEFINED) { 146 | conf->ruid_mode = parent->ruid_mode; 147 | } else { 148 | conf->ruid_mode = child->ruid_mode; 149 | } 150 | if (conf->ruid_mode == RUID_MODE_STAT) { 151 | conf->ruid_uid=UNSET; 152 | conf->ruid_gid=UNSET; 153 | conf->groupsnr = (child->groupsnr != NONE) ? UNSET : NONE; 154 | } else { 155 | conf->ruid_uid = (child->ruid_uid == UNSET) ? parent->ruid_uid : child->ruid_uid; 156 | conf->ruid_gid = (child->ruid_gid == UNSET) ? parent->ruid_gid : child->ruid_gid; 157 | if (child->groupsnr == NONE) { 158 | conf->groupsnr = NONE; 159 | } else if (child->groupsnr > 0) { 160 | memcpy(conf->groups, child->groups, sizeof(child->groups)); 161 | conf->groupsnr = child->groupsnr; 162 | } else if (parent->groupsnr > 0) { 163 | memcpy(conf->groups, parent->groups, sizeof(parent->groups)); 164 | conf->groupsnr = parent->groupsnr; 165 | } else { 166 | conf->groupsnr = (child->groupsnr == UNSET) ? parent->groupsnr : child->groupsnr; 167 | } 168 | } 169 | 170 | return conf; 171 | } 172 | 173 | 174 | static void *create_config (apr_pool_t *p, server_rec *s) 175 | { 176 | UNUSED(s); 177 | 178 | ruid_config_t *conf = apr_palloc (p, sizeof (*conf)); 179 | 180 | conf->default_uid=ap_unixd_config.user_id; 181 | conf->default_gid=ap_unixd_config.group_id; 182 | conf->min_uid=RUID_MIN_UID; 183 | conf->min_gid=RUID_MIN_GID; 184 | conf->chroot_dir=NULL; 185 | conf->document_root=NULL; 186 | 187 | return conf; 188 | } 189 | 190 | 191 | /* configure option functions */ 192 | static const char *set_mode (cmd_parms *cmd, void *mconfig, const char *arg) 193 | { 194 | ruid_dir_config_t *dconf = (ruid_dir_config_t *) mconfig; 195 | const char *err = ap_check_cmd_context (cmd, NOT_IN_FILES | NOT_IN_LIMIT); 196 | 197 | if (err != NULL) { 198 | return err; 199 | } 200 | 201 | if (strcasecmp(arg,"stat")==0) { 202 | dconf->ruid_mode=RUID_MODE_STAT; 203 | mode_stat_used |= RUID_MODE_STAT_USED; 204 | } else { 205 | dconf->ruid_mode=RUID_MODE_CONF; 206 | } 207 | 208 | return NULL; 209 | } 210 | 211 | 212 | static const char *set_uidgid (cmd_parms *cmd, void *mconfig, const char *uid, const char *gid) 213 | { 214 | ruid_dir_config_t *dconf = (ruid_dir_config_t *) mconfig; 215 | const char *err = ap_check_cmd_context (cmd, NOT_IN_FILES | NOT_IN_LIMIT); 216 | 217 | if (err != NULL) { 218 | return err; 219 | } 220 | 221 | dconf->ruid_uid = ap_uname2id(uid); 222 | dconf->ruid_gid = ap_gname2id(gid); 223 | 224 | return NULL; 225 | } 226 | 227 | 228 | static const char *set_groups (cmd_parms *cmd, void *mconfig, const char *arg) 229 | { 230 | ruid_dir_config_t *dconf = (ruid_dir_config_t *) mconfig; 231 | const char *err = ap_check_cmd_context (cmd, NOT_IN_FILES | NOT_IN_LIMIT); 232 | 233 | if (err != NULL) { 234 | return err; 235 | } 236 | 237 | if (strcasecmp(arg,"@none") == 0) { 238 | dconf->groupsnr=NONE; 239 | } 240 | 241 | if (dconf->groupsnr == UNSET) { 242 | dconf->groupsnr = 0; 243 | } 244 | if ((dconf->groupsnr < RUID_MAXGROUPS) && (dconf->groupsnr >= 0)) { 245 | dconf->groups[dconf->groupsnr++] = ap_gname2id (arg); 246 | } 247 | 248 | return NULL; 249 | } 250 | 251 | 252 | static const char *set_defuidgid (cmd_parms *cmd, void *mconfig, const char *uid, const char *gid) 253 | { 254 | UNUSED(mconfig); 255 | 256 | ruid_config_t *conf = ap_get_module_config (cmd->server->module_config, &ruid2_module); 257 | const char *err = ap_check_cmd_context (cmd, NOT_IN_DIR_LOC_FILE | NOT_IN_LIMIT); 258 | 259 | if (err != NULL) { 260 | return err; 261 | } 262 | 263 | conf->default_uid = ap_uname2id(uid); 264 | conf->default_gid = ap_gname2id(gid); 265 | 266 | return NULL; 267 | } 268 | 269 | 270 | static const char *set_minuidgid (cmd_parms *cmd, void *mconfig, const char *uid, const char *gid) 271 | { 272 | UNUSED(mconfig); 273 | 274 | ruid_config_t *conf = ap_get_module_config (cmd->server->module_config, &ruid2_module); 275 | const char *err = ap_check_cmd_context (cmd, NOT_IN_DIR_LOC_FILE | NOT_IN_LIMIT); 276 | 277 | if (err != NULL) { 278 | return err; 279 | } 280 | 281 | conf->min_uid = ap_uname2id(uid); 282 | conf->min_gid = ap_gname2id(gid); 283 | 284 | return NULL; 285 | } 286 | 287 | 288 | static const char *set_documentchroot (cmd_parms *cmd, void *mconfig, const char *chroot_dir, const char *document_root) 289 | { 290 | UNUSED(mconfig); 291 | 292 | ruid_config_t *conf = ap_get_module_config (cmd->server->module_config, &ruid2_module); 293 | const char *err = ap_check_cmd_context (cmd, NOT_IN_DIR_LOC_FILE | NOT_IN_LIMIT); 294 | 295 | if (err != NULL) { 296 | return err; 297 | } 298 | 299 | conf->chroot_dir = chroot_dir; 300 | conf->document_root = document_root; 301 | chroot_used |= RUID_CHROOT_USED; 302 | 303 | return NULL; 304 | } 305 | 306 | 307 | /* configure options in httpd.conf */ 308 | static const command_rec ruid_cmds[] = { 309 | 310 | AP_INIT_TAKE1 ("RMode", set_mode, NULL, RSRC_CONF | ACCESS_CONF, "Set mode to config or stat (default: config)"), 311 | AP_INIT_TAKE2 ("RUidGid", set_uidgid, NULL, RSRC_CONF | ACCESS_CONF, "Minimal uid or gid file/dir, else set[ug]id to default (User,Group)"), 312 | AP_INIT_ITERATE ("RGroups", set_groups, NULL, RSRC_CONF | ACCESS_CONF, "Set additional groups"), 313 | AP_INIT_TAKE2 ("RDefaultUidGid", set_defuidgid, NULL, RSRC_CONF, "If uid or gid is < than RMinUidGid set[ug]id to this uid gid"), 314 | AP_INIT_TAKE2 ("RMinUidGid", set_minuidgid, NULL, RSRC_CONF, "Minimal uid or gid file/dir, else set[ug]id to default (RDefaultUidGid)"), 315 | AP_INIT_TAKE2 ("RDocumentChRoot", set_documentchroot, NULL, RSRC_CONF, "Set chroot directory and the document root inside"), 316 | {NULL, {NULL}, NULL, 0, NO_ARGS, NULL} 317 | }; 318 | 319 | 320 | /* run in post config hook ( we are parent process and we are uid 0) */ 321 | static int ruid_init (apr_pool_t *p, apr_pool_t *plog, apr_pool_t *ptemp, server_rec *s) 322 | { 323 | UNUSED(p); 324 | UNUSED(plog); 325 | UNUSED(ptemp); 326 | 327 | void *data; 328 | const char *userdata_key = "ruid2_init"; 329 | 330 | /* keep capabilities after setuid */ 331 | prctl(PR_SET_KEEPCAPS,1); 332 | 333 | /* initialize_module() will be called twice, and if it's a DSO 334 | * then all static data from the first call will be lost. Only 335 | * set up our static data on the second call. */ 336 | apr_pool_userdata_get(&data, userdata_key, s->process->pool); 337 | if (!data) { 338 | apr_pool_userdata_set((const void *)1, userdata_key, apr_pool_cleanup_null, s->process->pool); 339 | } else { 340 | ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, NULL, MODULE_NAME "/" MODULE_VERSION " enabled"); 341 | 342 | /* MaxRequestsPerChild MUST be 1 to enable drop capability mode */ 343 | if (ap_max_requests_per_child == 1) { 344 | ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, NULL, MODULE_NAME " is in drop capability mode"); 345 | cap_mode = RUID_CAP_MODE_DROP; 346 | } 347 | } 348 | 349 | return OK; 350 | } 351 | 352 | 353 | /* child cleanup function */ 354 | static apr_status_t ruid_child_exit(void *data) 355 | { 356 | int fd = (int)((long)data); 357 | 358 | if (close(fd) < 0) { 359 | ap_log_error (APLOG_MARK, APLOG_ERR, 0, NULL, "%s CRITICAL ERROR closing root file descriptor (%d) failed", MODULE_NAME, fd); 360 | return APR_EGENERAL; 361 | } 362 | 363 | return APR_SUCCESS; 364 | } 365 | 366 | 367 | /* run after child init we are uid User and gid Group */ 368 | static void ruid_child_init (apr_pool_t *p, server_rec *s) 369 | { 370 | UNUSED(s); 371 | 372 | int ncap; 373 | cap_t cap; 374 | cap_value_t capval[4]; 375 | 376 | /* detect default supplementary group IDs */ 377 | if ((startup_groupsnr = getgroups(RUID_MAXGROUPS, startup_groups)) == -1) { 378 | startup_groupsnr = 0; 379 | ap_log_error (APLOG_MARK, APLOG_ERR, 0, NULL, "%s ERROR getgroups() failed on child init, ignoring supplementary group IDs", MODULE_NAME); 380 | } 381 | 382 | /* setup chroot jailbreak */ 383 | if (chroot_used == RUID_CHROOT_USED && cap_mode == RUID_CAP_MODE_KEEP) { 384 | if ((root_handle = open("/.", O_RDONLY)) < 0) { 385 | root_handle = UNSET; 386 | ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "%s CRITICAL ERROR opening root file descriptor failed (%s)", MODULE_NAME, strerror(errno)); 387 | } else if (fcntl(root_handle, F_SETFD, FD_CLOEXEC) < 0) { 388 | ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "%s CRITICAL ERROR unable to set close-on-exec flag on root file descriptor (%s)", MODULE_NAME, strerror(errno)); 389 | if (close(root_handle) < 0) 390 | ap_log_error (APLOG_MARK, APLOG_ERR, 0, NULL, "%s CRITICAL ERROR closing root file descriptor (%d) failed", MODULE_NAME, root_handle); 391 | root_handle = UNSET; 392 | } else { 393 | /* register cleanup function */ 394 | apr_pool_cleanup_register(p, (void*)((long)root_handle), ruid_child_exit, apr_pool_cleanup_null); 395 | } 396 | } else { 397 | root_handle = (chroot_used == RUID_CHROOT_USED ? NONE : UNSET); 398 | } 399 | 400 | /* init cap with all zeros */ 401 | cap = cap_init(); 402 | 403 | capval[0] = CAP_SETUID; 404 | capval[1] = CAP_SETGID; 405 | ncap = 2; 406 | if (mode_stat_used == RUID_MODE_STAT_USED) { 407 | capval[ncap++] = CAP_DAC_READ_SEARCH; 408 | } 409 | if (root_handle != UNSET) { 410 | capval[ncap++] = CAP_SYS_CHROOT; 411 | } 412 | cap_set_flag(cap, CAP_PERMITTED, ncap, capval, CAP_SET); 413 | if (cap_set_proc(cap) != 0) { 414 | ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "%s CRITICAL ERROR %s:cap_set_proc failed", MODULE_NAME, __func__); 415 | } 416 | cap_free(cap); 417 | 418 | /* check if process is dumpable */ 419 | coredump = prctl(PR_GET_DUMPABLE); 420 | } 421 | 422 | 423 | /* run during request cleanup */ 424 | static apr_status_t ruid_suidback (void *data) 425 | { 426 | request_rec *r = data; 427 | 428 | ruid_config_t *conf = ap_get_module_config (r->server->module_config, &ruid2_module); 429 | core_server_config *core = (core_server_config *) ap_get_module_config(r->server->module_config, &core_module); 430 | 431 | cap_t cap; 432 | cap_value_t capval[3]; 433 | 434 | if (cap_mode == RUID_CAP_MODE_KEEP) { 435 | 436 | cap=cap_get_proc(); 437 | capval[0]=CAP_SETUID; 438 | capval[1]=CAP_SETGID; 439 | capval[2]=CAP_SYS_CHROOT; 440 | cap_set_flag(cap, CAP_EFFECTIVE, (conf->chroot_dir ? 3 : 2), capval, CAP_SET); 441 | if (cap_set_proc(cap)!=0) { 442 | ap_log_error (APLOG_MARK, APLOG_ERR, 0, NULL, "%s CRITICAL ERROR %s:cap_set_proc failed before setuid", MODULE_NAME, __func__); 443 | } 444 | cap_free(cap); 445 | 446 | setgroups(startup_groupsnr, startup_groups); 447 | setgid(ap_unixd_config.group_id); 448 | setuid(ap_unixd_config.user_id); 449 | 450 | /* set httpd process dumpable after setuid */ 451 | if (coredump) { 452 | prctl(PR_SET_DUMPABLE,1); 453 | } 454 | 455 | /* jail break */ 456 | if (conf->chroot_dir) { 457 | if (fchdir(root_handle) < 0) { 458 | ap_log_error (APLOG_MARK, APLOG_ERR, 0, NULL, "%s failed to fchdir to root dir (%d) (%s)", MODULE_NAME, root_handle, strerror(errno)); 459 | } else { 460 | if (chroot(".") != 0) { 461 | ap_log_error (APLOG_MARK, APLOG_ERR, 0, NULL, "%s jail break failed", MODULE_NAME); 462 | } 463 | } 464 | core->ap_document_root = old_root; 465 | } 466 | 467 | cap=cap_get_proc(); 468 | capval[0]=CAP_SETUID; 469 | capval[1]=CAP_SETGID; 470 | capval[2]=CAP_SYS_CHROOT; 471 | cap_set_flag(cap, CAP_EFFECTIVE, 3, capval, CAP_CLEAR); 472 | if (cap_set_proc(cap)!=0) { 473 | ap_log_error (APLOG_MARK, APLOG_ERR, 0, NULL, "%s CRITICAL ERROR %s:cap_set_proc failed after setuid", MODULE_NAME, __func__); 474 | } 475 | cap_free(cap); 476 | } 477 | 478 | return DECLINED; 479 | } 480 | 481 | 482 | static int ruid_set_perm (request_rec *r, const char *from_func) 483 | { 484 | ruid_config_t *conf = ap_get_module_config(r->server->module_config, &ruid2_module); 485 | ruid_dir_config_t *dconf = ap_get_module_config(r->per_dir_config, &ruid2_module); 486 | 487 | int retval = DECLINED; 488 | gid_t gid; 489 | uid_t uid; 490 | gid_t groups[RUID_MAXGROUPS]; 491 | int groupsnr; 492 | 493 | cap_t cap; 494 | cap_value_t capval[3]; 495 | 496 | cap=cap_get_proc(); 497 | capval[0]=CAP_SETUID; 498 | capval[1]=CAP_SETGID; 499 | cap_set_flag(cap,CAP_EFFECTIVE,2,capval,CAP_SET); 500 | if (cap_set_proc(cap)!=0) { 501 | ap_log_error (APLOG_MARK, APLOG_ERR, 0, NULL, "%s CRITICAL ERROR %s>%s:cap_set_proc failed before setuid", MODULE_NAME, from_func, __func__); 502 | } 503 | cap_free(cap); 504 | 505 | if (dconf->ruid_mode==RUID_MODE_STAT) { 506 | /* set uid,gid to uid,gid of file 507 | * if file does not exist, finfo.user and finfo.group is set to uid,gid of parent directory 508 | */ 509 | gid=r->finfo.group; 510 | uid=r->finfo.user; 511 | } else { 512 | gid=(dconf->ruid_gid == UNSET) ? ap_unixd_config.group_id : dconf->ruid_gid; 513 | uid=(dconf->ruid_uid == UNSET) ? ap_unixd_config.user_id : dconf->ruid_uid; 514 | } 515 | 516 | /* if uid of filename is less than conf->min_uid then set to conf->default_uid */ 517 | if (uid < conf->min_uid) { 518 | uid=conf->default_uid; 519 | } 520 | if (gid < conf->min_gid) { 521 | gid=conf->default_gid; 522 | } 523 | 524 | /* set supplementary groups */ 525 | if ((dconf->groupsnr == UNSET) && (startup_groupsnr > 0)) { 526 | memcpy(groups, startup_groups, sizeof(groups)); 527 | groupsnr = startup_groupsnr; 528 | } else if (dconf->groupsnr > 0) { 529 | for (groupsnr = 0; groupsnr < dconf->groupsnr; groupsnr++) { 530 | if (dconf->groups[groupsnr] >= conf->min_gid) { 531 | groups[groupsnr] = dconf->groups[groupsnr]; 532 | } else { 533 | groups[groupsnr] = conf->default_gid; 534 | } 535 | } 536 | } else { 537 | groupsnr = 0; 538 | } 539 | setgroups(groupsnr, groups); 540 | 541 | /* final set[ug]id */ 542 | if (setgid(gid) != 0) 543 | { 544 | ap_log_error (APLOG_MARK, APLOG_ERR, 0, NULL, "%s %s %s %s>%s:setgid(%d) failed. getgid=%d getuid=%d", MODULE_NAME, ap_get_server_name(r), r->the_request, from_func, __func__, dconf->ruid_gid, getgid(), getuid()); 545 | retval = HTTP_FORBIDDEN; 546 | } else { 547 | if (setuid(uid) != 0) 548 | { 549 | ap_log_error (APLOG_MARK, APLOG_ERR, 0, NULL, "%s %s %s %s>%s:setuid(%d) failed. getuid=%d", MODULE_NAME, ap_get_server_name(r), r->the_request, from_func, __func__, dconf->ruid_uid, getuid()); 550 | retval = HTTP_FORBIDDEN; 551 | } 552 | } 553 | 554 | /* set httpd process dumpable after setuid */ 555 | if (coredump) { 556 | prctl(PR_SET_DUMPABLE,1); 557 | } 558 | 559 | /* clear capabilties from effective set */ 560 | cap=cap_get_proc(); 561 | capval[0]=CAP_SETUID; 562 | capval[1]=CAP_SETGID; 563 | capval[2]=CAP_DAC_READ_SEARCH; 564 | cap_set_flag(cap,CAP_EFFECTIVE,3,capval,CAP_CLEAR); 565 | 566 | if (cap_set_proc(cap)!=0) { 567 | ap_log_error (APLOG_MARK, APLOG_ERR, 0, NULL, "%s CRITICAL ERROR %s>%s:cap_set_proc failed after setuid", MODULE_NAME, from_func, __func__); 568 | retval = HTTP_FORBIDDEN; 569 | } 570 | cap_free(cap); 571 | 572 | return retval; 573 | } 574 | 575 | 576 | /* run in post_read_request hook */ 577 | static int ruid_setup (request_rec *r) 578 | { 579 | /* We decline when we are in a subrequest. The ruid_setup function was 580 | * already executed in the main request. */ 581 | if (!ap_is_initial_req(r)) { 582 | return DECLINED; 583 | } 584 | 585 | ruid_config_t *conf = ap_get_module_config (r->server->module_config, &ruid2_module); 586 | ruid_dir_config_t *dconf = ap_get_module_config(r->per_dir_config, &ruid2_module); 587 | core_server_config *core = (core_server_config *) ap_get_module_config(r->server->module_config, &core_module); 588 | 589 | int ncap=0; 590 | cap_t cap; 591 | cap_value_t capval[2]; 592 | 593 | if (dconf->ruid_mode==RUID_MODE_STAT) capval[ncap++] = CAP_DAC_READ_SEARCH; 594 | if (root_handle != UNSET) capval[ncap++] = CAP_SYS_CHROOT; 595 | if (ncap) { 596 | cap=cap_get_proc(); 597 | cap_set_flag(cap, CAP_EFFECTIVE, ncap, capval, CAP_SET); 598 | if (cap_set_proc(cap)!=0) { 599 | ap_log_error (APLOG_MARK, APLOG_ERR, 0, NULL, "%s CRITICAL ERROR %s:cap_set_proc failed", MODULE_NAME, __func__); 600 | } 601 | cap_free(cap); 602 | } 603 | 604 | /* do chroot trick only if chrootdir is defined */ 605 | if (conf->chroot_dir) 606 | { 607 | old_root = ap_document_root(r); 608 | core->ap_document_root = conf->document_root; 609 | if (chdir(conf->chroot_dir) != 0) 610 | { 611 | ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL,"%s %s %s chdir to %s failed", MODULE_NAME, ap_get_server_name (r), r->the_request, conf->chroot_dir); 612 | return HTTP_FORBIDDEN; 613 | } 614 | if (chroot(conf->chroot_dir) != 0) 615 | { 616 | ap_log_error (APLOG_MARK, APLOG_ERR, 0, NULL,"%s %s %s chroot to %s failed", MODULE_NAME, ap_get_server_name (r), r->the_request, conf->chroot_dir); 617 | return HTTP_FORBIDDEN; 618 | } 619 | 620 | cap = cap_get_proc(); 621 | capval[0] = CAP_SYS_CHROOT; 622 | cap_set_flag(cap, CAP_EFFECTIVE, 1, capval, CAP_CLEAR); 623 | if (cap_set_proc(cap) != 0 ) 624 | { 625 | ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "%s CRITICAL ERROR %s:cap_set_proc failed", MODULE_NAME, __func__); 626 | } 627 | cap_free(cap); 628 | } 629 | 630 | /* register suidback function */ 631 | apr_pool_cleanup_register(r->pool, r, ruid_suidback, apr_pool_cleanup_null); 632 | 633 | if (dconf->ruid_mode==RUID_MODE_CONF) 634 | { 635 | return ruid_set_perm(r, __func__); 636 | } else { 637 | return DECLINED; 638 | } 639 | } 640 | 641 | 642 | /* run in map_to_storage hook */ 643 | static int ruid_uiiii (request_rec *r) 644 | { 645 | if (!ap_is_initial_req(r)) { 646 | return DECLINED; 647 | } 648 | 649 | int retval = ruid_set_perm(r, __func__); 650 | 651 | int ncap; 652 | cap_t cap; 653 | cap_value_t capval[4]; 654 | 655 | /* clear capabilities from permitted set (permanent) */ 656 | if (cap_mode == RUID_CAP_MODE_DROP) { 657 | cap=cap_get_proc(); 658 | capval[0]=CAP_SETUID; 659 | capval[1]=CAP_SETGID; 660 | capval[2]=CAP_DAC_READ_SEARCH; 661 | ncap = 2; 662 | if (root_handle == UNSET) capval[ncap++] = CAP_SYS_CHROOT; 663 | cap_set_flag(cap,CAP_PERMITTED,ncap,capval,CAP_CLEAR); 664 | 665 | if (cap_set_proc(cap)!=0) { 666 | ap_log_error (APLOG_MARK, APLOG_ERR, 0, NULL, "%s CRITICAL ERROR %s:cap_set_proc failed after setuid", MODULE_NAME, __func__); 667 | retval = HTTP_FORBIDDEN; 668 | } 669 | cap_free(cap); 670 | } 671 | 672 | return retval; 673 | } 674 | 675 | 676 | static void register_hooks (apr_pool_t *p) 677 | { 678 | UNUSED(p); 679 | 680 | ap_hook_post_config (ruid_init, NULL, NULL, APR_HOOK_MIDDLE); 681 | ap_hook_child_init (ruid_child_init, NULL, NULL, APR_HOOK_MIDDLE); 682 | ap_hook_post_read_request(ruid_setup, NULL, NULL, APR_HOOK_MIDDLE); 683 | ap_hook_header_parser(ruid_uiiii, NULL, NULL, APR_HOOK_FIRST); 684 | } 685 | 686 | 687 | module AP_MODULE_DECLARE_DATA ruid2_module = { 688 | STANDARD20_MODULE_STUFF, 689 | create_dir_config, /* dir config creater */ 690 | merge_dir_config, /* dir merger --- default is to override */ 691 | create_config, /* server config */ 692 | NULL, /* merge server config */ 693 | ruid_cmds, /* command apr_table_t */ 694 | register_hooks /* register hooks */ 695 | }; 696 | --------------------------------------------------------------------------------