├── .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 |
--------------------------------------------------------------------------------