2 * libauthbind.c - bind(2)-redirector library for authbind
4 * authbind is Copyright (C) 1998 Ian Jackson
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)
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.
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.
24 #include <sys/types.h>
30 #include <sys/socket.h>
32 #include <netinet/in.h>
36 typedef void anyfn_type(void);
37 typedef int bindfn_type(int fd, const struct sockaddr *addr, socklen_t addrlen);
39 #define STDERRSTR_CONST(m) write(2,m,sizeof(m)-1)
40 #define STDERRSTR_STRING(m) write(2,m,strlen(m))
42 static anyfn_type *find_any(const char *name) {
43 static const char *dlerr;
46 kv= dlsym(RTLD_NEXT,name); if (kv) return kv;
47 dlerr= dlerror(); if (!dlerr) dlerr= "dlsym() failed for no reason";
48 STDERRSTR_CONST("libauthbind: error finding original version of ");
49 STDERRSTR_STRING(name);
50 STDERRSTR_CONST(": ");
51 STDERRSTR_STRING(dlerr);
52 STDERRSTR_STRING("\n");
57 static bindfn_type find_bind, *old_bind= find_bind;
59 int find_bind(int fd, const struct sockaddr *addr, socklen_t addrlen) {
61 anyfn= find_any("bind"); if (!anyfn) return -1;
62 old_bind= (bindfn_type*)anyfn;
63 return old_bind(fd,addr,addrlen);
66 static int exiterrno(int e) {
67 _exit(e>0 && e<128 ? e : -1);
70 static void removepreload(void) {
71 const char *myself, *found;
72 char *newval, *preload;
73 int lpreload, lmyself, before, after;
75 preload= getenv(PRELOAD_VAR);
76 myself= getenv(AUTHBINDLIB_VAR);
77 if (!myself || !preload) return;
79 lpreload= strlen(preload);
80 lmyself= strlen(myself);
82 if (lmyself < 1 || lpreload<lmyself) return;
83 if (lpreload==lmyself) {
84 if (!strcmp(preload,myself)) unsetenv(PRELOAD_VAR);
87 if (!memcmp(preload,myself,lmyself) && preload[lmyself]==':') {
88 before= 0; after= lpreload-(lmyself+1);
89 } else if (!memcmp(preload+lpreload-lmyself,myself,lmyself) &&
90 preload[lpreload-(lmyself+1)]==':') {
91 before= lpreload-(lmyself+1); after= 0;
93 if (lpreload<lmyself+2) return;
96 found= strstr(found,myself); if (!found) return;
97 if (found > preload+lpreload-(lmyself+1)) return;
98 if (found[-1]==':' && found[lmyself]==':') break;
101 before= found-preload;
102 after= lpreload-(before+lmyself+1);
104 newval= malloc(before+after+1);
106 memcpy(newval,preload,before);
107 strcpy(newval+before,preload+lpreload-after);
108 if (setenv(PRELOAD_VAR,newval,1)) return;
111 strcpy(preload+before,preload+lpreload-after);
120 /* If AUTHBIND_LEVELS is
121 * unset => always strip from preload
122 * set and starts with `y' => never strip from preload, keep AUTHBIND_LEVELS
123 * set to integer > 1 => do not strip now, subtract one from AUTHBIND_LEVELS
124 * set to integer 1 => do not strip now, unset AUTHBIND_LEVELS
125 * set to empty string or 0 => strip now, unset AUTHBIND_LEVELS
127 levels= getenv(AUTHBIND_LEVELS_VAR);
129 if (levels[0]=='y') return 0;
130 levelno= atoi(levels);
133 if (levelno > 0) sprintf(levels,"%d",levelno);
134 else unsetenv(AUTHBIND_LEVELS_VAR);
137 unsetenv(AUTHBIND_LEVELS_VAR);
143 static const int evilsignals[]= { SIGFPE, SIGILL, SIGSEGV, SIGBUS, 0 };
145 int bind(int fd, const struct sockaddr *addr, socklen_t addrlen) {
147 char portarg[5], addrarg[33];
150 const int *evilsignal;
151 sigset_t block, saved;
152 unsigned int portval;
154 switch (addr->sa_family) {
156 portval = ((struct sockaddr_in*)addr)->sin_port;
157 if (addrlen != sizeof(struct sockaddr_in)) goto bail;
160 portval = ((struct sockaddr_in6*)addr)->sin6_port;
161 if (addrlen != sizeof(struct sockaddr_in6)) goto bail;
167 if (!geteuid() || portval == 0 || ntohs(portval) >= IPPORT_RESERVED) {
169 return old_bind(fd,addr,addrlen);
173 for (evilsignal=evilsignals;
176 sigdelset(&block,*evilsignal);
177 if (sigprocmask(SIG_BLOCK,&block,&saved)) return -1;
179 switch (addr->sa_family) {
182 sprintf(addrarg,"%08lx",
183 ((unsigned long)(((struct sockaddr_in*)addr)->sin_addr.s_addr))
189 sprintf(addrarg+i*2,"%02x",
190 ((struct sockaddr_in6*)addr)->sin6_addr.s6_addr[i]);
195 sprintf(portarg,"%04x",
198 child= fork(); if (child==-1) goto x_err;
201 if (dup2(fd,0)) exiterrno(errno);
203 execl(HELPER,HELPER,addrarg,portarg,afarg,(char*)0);
204 status= errno > 0 && errno < 127 ? errno : 127;
205 STDERRSTR_CONST("libauthbind: possible installation problem - "
206 "could not invoke " HELPER "\n");
210 rchild= waitpid(child,&status,0);
211 if (rchild==-1) goto x_err;
212 if (rchild!=child) { errno= ECHILD; goto x_err; }
214 if (WIFEXITED(status)) {
215 if (WEXITSTATUS(status)) {
216 errno= WEXITSTATUS(status);
217 if (errno >= 127) errno= ENXIO;
230 if (sigprocmask(SIG_SETMASK,&saved,0)) abort();