1 /*
2 *
3 * jk_procmailwrapper
4 * this program will simply execute procmail for users that are not in a jail
5 * and it will exit() for users that are in a jail (mail will *not* be delivered)
6 *
7 * this will probably extended in the near future
8 *
9 Copyright (c) 2003, 2004, 2005, 2006 Olivier Sessink
10 All rights reserved.
11
12 Redistribution and use in source and binary forms, with or without
13 modification, are permitted provided that the following conditions
14 are met:
15 * Redistributions of source code must retain the above copyright
16 notice, this list of conditions and the following disclaimer.
17 * Redistributions in binary form must reproduce the above
18 copyright notice, this list of conditions and the following
19 disclaimer in the documentation and/or other materials provided
20 with the distribution.
21 * The names of its contributors may not be used to endorse or
22 promote products derived from this software without specific
23 prior written permission.
24
25 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
26 "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
27 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
28 FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
29 COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
30 INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
31 BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
32 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
33 CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
34 LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
35 ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
36 POSSIBILITY OF SUCH DAMAGE.
37 */
38 /* #define DEBUG */
39
40 #include "config.h"
41
42 #include <string.h>
43 #include <stdio.h>
44 #include <pwd.h>
45 #include <sys/types.h>
46 #include <unistd.h>
47 #include <stdlib.h>
48 #include <syslog.h>
49 #include <errno.h>
50 #include <grp.h>
51
52 #include "jk_lib.h"
53
54 #define PROGRAMNAME "jk_procmailwrapper"
55
user_is_chrooted(const char * homedir)56 int user_is_chrooted(const char *homedir) {
57 char *tmp;
58 tmp = strstr(homedir, "/./");
59 if (tmp != NULL) {
60 return 1;
61 }
62 return 0;
63 }
64
clean_exit(char * name,int error)65 void clean_exit(char * name, int error) {
66 printf("%s, exiting with error %d\n", name, error);
67 exit(error);
68 }
69
main(int argc,char ** argv,char ** envp)70 int main (int argc, char **argv, char **envp) {
71 int i;
72 struct passwd *pw=NULL;
73 struct group *gr=NULL;
74 char *jaildir=NULL, *newhome=NULL;
75
76 DEBUG_MSG(PROGRAMNAME", started\n");
77 pw = getpwuid(getuid());
78 if (!user_is_chrooted(pw->pw_dir)) {
79 /* if the user does not have a chroot homedir, we start the normal procmail now,
80 but first we drop all privileges */
81 if (setgid(getgid())) {
82 syslog(LOG_ERR, "abort, failed to become gid %d", getgid());
83 exit(34);
84 }
85 if (initgroups(pw->pw_name, getgid())) {
86 syslog(LOG_ERR, "abort, failed to initgroups for user %s and group %d", pw->pw_name, getgid());
87 exit(35);
88 }
89 if (setuid(getuid())) {
90 syslog(LOG_ERR, "abort, failed to become uid %d", getuid());
91 exit(36);
92 }
93 execve(PROCMAILPATH, argv, envp);
94 /* if we get here, there is something wrong */
95 exit(1);
96 }
97 /* OK, so the user is a jailed user, now we start checking things!! */
98
99 /* open the log facility */
100 openlog(PROGRAMNAME, LOG_PID, LOG_AUTH);
101
102 /* check if it us that the user wants */
103 {
104 char *tmp = strrchr(argv[0], '/');
105 if (!tmp) {
106 tmp = argv[0];
107 } else {
108 tmp++;
109 }
110 if (strcmp(tmp, PROGRAMNAME) && (tmp[0] != '-' || strcmp(&tmp[1], PROGRAMNAME))) {
111 DEBUG_MSG("wrong name, tmp=%s, &tmp[1]=%s\n", tmp, &tmp[1]);
112 syslog(LOG_ERR, "abort, "PROGRAMNAME" is called as %s", argv[0]);
113 exit(1);
114 }
115 }
116 DEBUG_MSG("close filedescriptors\n");
117 /* open file descriptors can be used to break out of a chroot, so we close all of them, except for stdin,stdout and stderr */
118 for (i=getdtablesize();i>3;i--) {
119 while (close(i) != 0 && errno == EINTR);
120 }
121
122 /* now test if we are setuid root (the effective user id must be 0, and the real user id > 0 */
123 if (geteuid() != 0 || getuid() == 0) {
124 syslog(LOG_ERR, "abort, "PROGRAMNAME" is not setuid root, or is run by root");
125 exit(11);
126 }
127
128 DEBUG_MSG("get user info\n");
129 gr = getgrgid(getgid());
130 if (!pw || !gr) {
131 syslog(LOG_ERR, "abort, failed to get user or group information for %d:%d", getuid(), getgid());
132 exit(13);
133 }
134
135 /* now we clear the environment */
136 clearenv();
137
138 if (pw->pw_gid != getgid()) {
139 syslog(LOG_ERR, "abort, the group ID from /etc/passwd (%d) does not match the group ID we run with (%d)", pw->pw_gid, getgid());
140 exit(15);
141 }
142 if (!pw->pw_dir || strlen(pw->pw_dir) ==0 || strstr(pw->pw_dir, "/./") == NULL) {
143 syslog(LOG_ERR, "abort, the homedir %s does not contain the jail pattern path/./path", pw->pw_dir?pw->pw_dir:"NULL");
144 exit(17);
145 }
146 DEBUG_MSG("get jaildir\n");
147 if (!getjaildir(pw->pw_dir, &jaildir, &newhome)) {
148 syslog(LOG_ERR, "abort, failed to read the jail and the home from %s",pw->pw_dir);
149 exit(17);
150 }
151 DEBUG_MSG("get chdir()\n");
152 if (chdir(jaildir) != 0) {
153 syslog(LOG_ERR, "abort, failed to chdir() to %s",jaildir);
154 exit(19);
155 } else {
156 /* test if it really succeeded */
157 char *test = get_current_dir_name();
158 if (strcmp(jaildir, test) != 0) {
159 syslog(LOG_ERR, "abort, current dir != %s after chdir()",jaildir);
160 exit(21);
161 }
162 free(test);
163 }
164
165 /* here do test the ownership of the jail and the homedir and such
166 the function testsafepath doe exit itself on any failure */
167 {
168 int ret;
169 DEBUG_MSG("test paths\n");
170 ret = testsafepath(jaildir,0,0);
171 if (ret != 0) {
172 syslog(LOG_ERR, "abort, path %s is not a safe jail, check ownership and permissions", jaildir);
173 exit(53);
174 }
175 ret = testsafepath(pw->pw_dir, getuid(), getgid());
176 if ((ret & TESTPATH_NOREGPATH) ) {
177 syslog(LOG_ERR, "abort, path %s is not a directory", pw->pw_dir);
178 exit(53);
179 }
180 if ((ret & TESTPATH_OWNER) ) {
181 syslog(LOG_ERR, "abort, path %s is not owned by %d", pw->pw_dir,getuid());
182 exit(53);
183 }
184 }
185 /* do a final log message */
186 syslog(LOG_INFO, "now entering jail %s for user %d", jaildir, getuid());
187
188 DEBUG_MSG("chroot()\n");
189 /* do the chroot() call */
190 if (chroot(jaildir)) {
191 syslog(LOG_ERR, "abort, failed to chroot() to %s", jaildir);
192 exit(33);
193 }
194
195 /* drop all privileges, it seems that we first have to setgid(),
196 then we have to call initgroups(),
197 then we call setuid() */
198 if (setgid(getgid())) {
199 syslog(LOG_ERR, "abort, failed to become gid %d", getgid());
200 exit(34);
201 }
202 if (initgroups(pw->pw_name, getgid())) {
203 syslog(LOG_ERR, "abort, failed to initgroups for user %s and group %d", pw->pw_name, getgid());
204 exit(35);
205 }
206 if (setuid(getuid())) {
207 syslog(LOG_ERR, "abort, failed to become uid %d", getuid());
208 exit(36);
209 }
210
211 /* test for user and group info, is it the same? checks username, groupname and home */
212 {
213 char *oldpw_name,*oldgr_name;
214 oldpw_name = strdup(pw->pw_name);
215 oldgr_name = strdup(gr->gr_name);
216
217 pw = getpwuid(getuid());
218 gr = getgrgid(getgid());
219 if (!pw || !gr) {
220 syslog(LOG_ERR, "abort, failed to get user and group information in the jail for %d:%d", getuid(), getgid());
221 exit(35);
222 }
223 if (strcmp(pw->pw_name, oldpw_name)!=0 || strcmp(gr->gr_name, oldgr_name)!=0) {
224 syslog(LOG_ERR, "abort, user or group names differ inside the jail for %d:%d", getuid(), getgid());
225 exit(37);
226 }
227 if (strcmp(pw->pw_dir, newhome)!=0) {
228 syslog(LOG_ERR, "abort, home directory is incorrect inside the jail for %d:%d", getuid(), getgid());
229 exit(39);
230 }
231 free(oldpw_name);
232 free(oldgr_name);
233 }
234
235 /* test procmail in the jail, it is not allowed to be setuid() or setgid()
236 it is common to have procmail setuid() root and setgid() mail in the regular
237 system, but it is for most situations not required, and therefore very much
238 not recommended inside a jail. So we will simply exit because it is a
239 security risk */
240 testsafepath(PROCMAILPATH,0,0);
241
242 /* prepare the new environment */
243 setenv("HOME",newhome,1);
244 setenv("USER",pw->pw_name,1);
245 if (chdir(newhome) != 0) {
246 syslog(LOG_ERR, "abort, failed to chdir() inside the jail to %s",newhome);
247 exit(41);
248 }
249
250 /* cleanup before execution */
251 free(newhome);
252 free(jaildir);
253
254 /* now execute the jailed shell */
255 /*execl(pw->pw_shell, pw->pw_shell, NULL);*/
256 {
257 char **newargv;
258 int i;
259 newargv = malloc0((argc+1)*sizeof(char *));
260 newargv[0] = PROCMAILPATH;
261 for (i=1;i<argc;i++) {
262 newargv[i] = argv[i];
263 }
264 execv(PROCMAILPATH, newargv);
265 }
266 DEBUG_MSG(strerror(errno));
267 syslog(LOG_ERR, "WARNING: could not execute %s for user %d:%d",PROCMAILPATH,getuid(),getgid());
268
269 exit(111);
270 }
271