chiark / gitweb /
test: udp-preload: Proof of concept wrapping (2)
[secnet.git] / test / udp-preload.c
1 /*
2  *  libauthbind.c - bind(2)-redirector library for authbind
3  *
4  *  authbind is Copyright (C) 1998 Ian Jackson
5  * 
6  *  This program is free software; you can redistribute it and/or modify
7  *  it under the terms of the GNU General Public License as published by
8  *  the Free Software Foundation; either version 2, or (at your option)
9  *  any later version.
10  * 
11  *  This program is distributed in the hope that it will be useful,
12  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  *  GNU General Public License for more details.
15  * 
16  *  You should have received a copy of the GNU General Public License
17  *  along with this program; if not, write to the Free Software Foundation,
18  *  Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 
19  * 
20  */
21
22 #define _GNU_SOURCE
23
24 #include <dlfcn.h>
25 #include <stdio.h>
26 #include <sys/types.h>
27 #include <unistd.h>
28 #include <errno.h>
29 #include <stdlib.h>
30 #include <signal.h>
31 #include <string.h>
32 #include <sys/socket.h>
33 #include <sys/wait.h>
34 #include <netinet/in.h>
35
36 #define STDERRSTR_CONST(m) write(2,m,sizeof(m)-1)
37 #define STDERRSTR_STRING(m) write(2,m,strlen(m))
38
39 typedef void anyfn_type(void);
40
41 static anyfn_type *find_any(const char *name) {
42   static const char *dlerr;
43   anyfn_type *kv;
44
45   kv= dlsym(RTLD_NEXT,name); if (kv) return kv;
46   dlerr= dlerror(); if (!dlerr) dlerr= "dlsym() failed for no reason";
47   STDERRSTR_CONST("libauthbind: error finding original version of ");
48   STDERRSTR_STRING(name);
49   STDERRSTR_CONST(": ");
50   STDERRSTR_STRING(dlerr);
51   STDERRSTR_STRING("\n");
52   errno= ENOSYS;
53   return 0;
54 }
55
56 #define socket_args int domain, int type, int protocol
57 #define WRAPS(X) X(socket, (domain,type,protocol))
58
59 #define DEF_OLD(fn,args)                                \
60   typedef int fn##_fn_type(fn##_args);                  \
61   static int find_##fn(fn##_args);                      \
62   static fn##_fn_type find_##fn, *old_##fn=find_##fn;   \
63   static int find_##fn(fn##_args) {                     \
64     anyfn_type *anyfn;                                  \
65     anyfn= find_any(#fn); if (!anyfn) return -1;        \
66     old_##fn= (fn##_fn_type*)anyfn;                     \
67     return old_##fn args;                               \
68   }
69
70 WRAPS(DEF_OLD)
71
72 #define WRAP(fn) int fn(fn##_args)
73
74 WRAP(socket) {
75     return old_socket(domain,type,protocol);
76 }
77
78 #if 0
79 WRAP(bind, (int fd, const struct sockaddr *addr, socklen_t addrlen), {
80     
81 });
82                     
83 static bindfn_type find_bind, *old_bind= find_bind;
84
85 int find_bind(int fd, const struct sockaddr *addr, socklen_t addrlen) {
86   anyfn_type *anyfn;
87   anyfn= find_any("bind"); if (!anyfn) return -1;
88   old_bind= (bindfn_type*)anyfn;
89   return old_bind(fd,addr,addrlen);
90 }
91
92 static int exiterrno(int e) {
93   _exit(e>0 && e<128 ? e : -1);
94 }
95
96 static void removepreload(void) {
97   const char *myself, *found;
98   char *newval, *preload;
99   int lpreload, lmyself, before, after;
100
101   preload= getenv(PRELOAD_VAR);
102   myself= getenv(AUTHBINDLIB_VAR);
103   if (!myself || !preload) return;
104
105   lpreload= strlen(preload);
106   lmyself= strlen(myself);
107
108   if (lmyself < 1 || lpreload<lmyself) return;
109   if (lpreload==lmyself) {
110     if (!strcmp(preload,myself)) unsetenv(PRELOAD_VAR);
111     return;
112   }
113   if (!memcmp(preload,myself,lmyself) && preload[lmyself]==':') {
114     before= 0; after= lpreload-(lmyself+1);
115   } else if (!memcmp(preload+lpreload-lmyself,myself,lmyself) &&
116              preload[lpreload-(lmyself+1)]==':') {
117     before= lpreload-(lmyself+1); after= 0;
118   } else {
119     if (lpreload<lmyself+2) return;
120     found= preload+1;
121     for (;;) {
122       found= strstr(found,myself); if (!found) return;
123       if (found > preload+lpreload-(lmyself+1)) return;
124       if (found[-1]==':' && found[lmyself]==':') break;
125       found++;
126     }
127     before= found-preload;
128     after= lpreload-(before+lmyself+1);
129   }
130   newval= malloc(before+after+1);
131   if (newval) {
132     memcpy(newval,preload,before);
133     strcpy(newval+before,preload+lpreload-after);
134     if (setenv(PRELOAD_VAR,newval,1)) return;
135     free(newval);
136   }
137   strcpy(preload+before,preload+lpreload-after);
138   return;
139 }
140
141 int _init(void);
142 int _init(void) {
143   char *levels;
144   int levelno;
145
146   /* If AUTHBIND_LEVELS is
147    *  unset => always strip from preload
148    *  set and starts with `y' => never strip from preload, keep AUTHBIND_LEVELS
149    *  set to integer > 1 => do not strip now, subtract one from AUTHBIND_LEVELS
150    *  set to integer 1 => do not strip now, unset AUTHBIND_LEVELS
151    *  set to empty string or 0 => strip now, unset AUTHBIND_LEVELS
152    */
153   levels= getenv(AUTHBIND_LEVELS_VAR);
154   if (levels) {
155     if (levels[0]=='y') return 0;
156     levelno= atoi(levels);
157     if (levelno > 0) {
158       levelno--;
159       if (levelno > 0) sprintf(levels,"%d",levelno);
160       else unsetenv(AUTHBIND_LEVELS_VAR);
161       return 0;
162     }
163     unsetenv(AUTHBIND_LEVELS_VAR);
164   }
165   removepreload();
166   return 0;
167 }
168
169 static const int evilsignals[]= { SIGFPE, SIGILL, SIGSEGV, SIGBUS, 0 };
170
171 int bind(int fd, const struct sockaddr *addr, socklen_t addrlen) {
172   pid_t child, rchild;
173   char portarg[5], addrarg[33];
174   const char *afarg;
175   int i, r, status, restore_sigchild;
176   const int *evilsignal;
177   sigset_t block, saved;
178   struct sigaction old_sigchild;
179   unsigned int portval;
180
181   switch (addr->sa_family) {
182   case AF_INET:
183     portval = ((struct sockaddr_in*)addr)->sin_port;
184     if (addrlen != sizeof(struct sockaddr_in)) goto bail;
185     break;
186   case AF_INET6:
187     portval = ((struct sockaddr_in6*)addr)->sin6_port;
188     if (addrlen != sizeof(struct sockaddr_in6)) goto bail;
189     break;
190   default:
191     goto bail;
192   }
193
194   if (!geteuid() || portval == 0 || ntohs(portval) >= IPPORT_RESERVED) {
195   bail:
196     return old_bind(fd,addr,addrlen);
197   }
198
199   sigfillset(&block);
200   for (evilsignal=evilsignals;
201        *evilsignal;
202        evilsignal++)
203     sigdelset(&block,*evilsignal);
204   if (sigprocmask(SIG_BLOCK,&block,&saved)) return -1;
205
206   switch (addr->sa_family) {
207   case AF_INET:
208     afarg = 0;
209     sprintf(addrarg,"%08lx",
210             ((unsigned long)(((struct sockaddr_in*)addr)->sin_addr.s_addr))
211             &0x0ffffffffUL);
212     break;
213   case AF_INET6:
214     afarg = "6";
215     for (i=0; i<16; i++)
216       sprintf(addrarg+i*2,"%02x",
217               ((struct sockaddr_in6*)addr)->sin6_addr.s6_addr[i]);
218     break;
219   default:
220     abort();
221   }
222   sprintf(portarg,"%04x",
223           portval&0x0ffff);
224
225   restore_sigchild= 0;
226   if (sigaction(SIGCHLD,NULL,&old_sigchild)) return -1;
227   if (old_sigchild.sa_handler == SIG_IGN) {
228     struct sigaction new_sigchild;
229
230     new_sigchild.sa_handler= SIG_DFL;
231     sigemptyset(&new_sigchild.sa_mask);
232     new_sigchild.sa_flags= 0;
233     if (sigaction(SIGCHLD,&new_sigchild,&old_sigchild)) return -1;
234     restore_sigchild= 1;
235   }
236
237   child= fork(); if (child==-1) goto x_err;
238
239   if (!child) {
240     if (dup2(fd,0)) exiterrno(errno);
241     removepreload();
242     execl(HELPER,HELPER,addrarg,portarg,afarg,(char*)0);
243     status= errno > 0 && errno < 127 ? errno : 127;
244     STDERRSTR_CONST("libauthbind: possible installation problem - "
245                     "could not invoke " HELPER "\n");
246     exiterrno(status);
247   }
248
249   rchild= waitpid(child,&status,0);
250   if (rchild==-1) goto x_err;
251   if (rchild!=child) { errno= ECHILD; goto x_err; }
252
253   if (WIFEXITED(status)) {
254     if (WEXITSTATUS(status)) {
255       errno= WEXITSTATUS(status);
256       if (errno >= 127) errno= ENXIO;
257       goto x_err;
258     }
259     r= 0;
260     goto x;
261   } else {
262     errno= ENOSYS;
263     goto x_err;
264   }
265
266 x_err:
267   r= -1;
268 x:
269   if (sigprocmask(SIG_SETMASK,&saved,0)) abort();
270   if (restore_sigchild) {
271     if (sigaction(SIGCHLD,&old_sigchild,NULL)) return -1;
272     if (old_sigchild.sa_handler == SIG_IGN) {
273       int discard;
274       while (waitpid(-1, &discard, WNOHANG) > 0)
275         ;
276     }
277   }
278   return r;
279 }
280 #endif