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>
34 static const char *rcsid="$Id$";
38 typedef void anyfn_type(void);
39 typedef int bindfn_type(int fd, const struct sockaddr *addr, socklen_t addrlen);
41 #define STDERRSTR_CONST(m) write(2,m,sizeof(m)-1)
42 #define STDERRSTR_STRING(m) write(2,m,strlen(m))
44 static anyfn_type *find_any(const char *name) {
45 static const char *dlerr;
48 kv= dlsym(RTLD_NEXT,name); if (kv) return kv;
49 dlerr= dlerror(); if (!dlerr) dlerr= "dlsym() failed for no reason";
50 STDERRSTR_CONST("libauthbind: error finding original version of ");
51 STDERRSTR_STRING(name);
52 STDERRSTR_CONST(": ");
53 STDERRSTR_STRING(dlerr);
54 STDERRSTR_STRING("\n");
59 static bindfn_type find_bind, *old_bind= find_bind;
61 int find_bind(int fd, const struct sockaddr *addr, socklen_t addrlen) {
63 anyfn= find_any("bind"); if (!anyfn) return -1;
64 old_bind= (bindfn_type*)anyfn;
65 return old_bind(fd,addr,addrlen);
68 static int exiterrno(int e) {
69 _exit(e>0 && e<128 ? e : -1);
72 static void removepreload(void) {
73 const char *myself, *found;
74 char *newval, *preload;
75 int lpreload, lmyself, before, after;
77 preload= getenv(PRELOAD_VAR);
78 myself= getenv(AUTHBINDLIB_VAR);
79 if (!myself || !preload) return;
81 lpreload= strlen(preload);
82 lmyself= strlen(myself);
84 if (lmyself < 1 || lpreload<lmyself) return;
85 if (lpreload==lmyself) {
86 if (!strcmp(preload,myself)) unsetenv(PRELOAD_VAR);
89 if (!memcmp(preload,myself,lmyself) && preload[lmyself]==':') {
90 before= 0; after= lpreload-(lmyself+1);
91 } else if (!memcmp(preload+lpreload-lmyself,myself,lmyself) &&
92 preload[lpreload-(lmyself+1)]==':') {
93 before= lpreload-(lmyself+1); after= 0;
95 if (lpreload<lmyself+2) return;
98 found= strstr(found,myself); if (!found) return;
99 if (found > preload+lpreload-(lmyself+1)) return;
100 if (found[-1]==':' && found[lmyself]==':') break;
103 before= found-preload;
104 after= lpreload-(before+lmyself+1);
106 newval= malloc(before+after+1);
108 memcpy(newval,preload,before);
109 strcpy(newval+before,preload+lpreload-after);
110 if (setenv(PRELOAD_VAR,newval,1)) return;
113 strcpy(preload+before,preload+lpreload-after);
122 /* If AUTHBIND_LEVELS is
123 * unset => always strip from preload
124 * set and starts with `y' => never strip from preload, keep AUTHBIND_LEVELS
125 * set to integer > 1 => do not strip now, subtract one from AUTHBIND_LEVELS
126 * set to integer 1 => do not strip now, unset AUTHBIND_LEVELS
127 * set to empty string or 0 => strip now, unset AUTHBIND_LEVELS
129 levels= getenv(AUTHBIND_LEVELS_VAR);
131 if (levels[0]=='y') return 0;
132 levelno= atoi(levels);
135 if (levelno > 0) sprintf(levels,"%d",levelno);
136 else unsetenv(AUTHBIND_LEVELS_VAR);
139 unsetenv(AUTHBIND_LEVELS_VAR);
145 static const int evilsignals[]= { SIGFPE, SIGILL, SIGSEGV, SIGBUS, 0 };
147 int bind(int fd, const struct sockaddr *addr, socklen_t addrlen) {
149 char portarg[5], addrarg[9];
151 const int *evilsignal;
152 sigset_t block, saved;
154 if (addr->sa_family != AF_INET || addrlen != sizeof(struct sockaddr_in) ||
155 !geteuid() || ((struct sockaddr_in*)addr)->sin_port == 0 ||
156 ntohs(((struct sockaddr_in*)addr)->sin_port) >= IPPORT_RESERVED/2)
157 return old_bind(fd,addr,addrlen);
160 for (evilsignal=evilsignals;
163 sigdelset(&block,*evilsignal);
164 if (sigprocmask(SIG_BLOCK,&block,&saved)) return -1;
166 sprintf(addrarg,"%08lx",
167 ((unsigned long)(((struct sockaddr_in*)addr)->sin_addr.s_addr))&0x0ffffffffUL);
168 sprintf(portarg,"%04x",
169 ((unsigned int)(((struct sockaddr_in*)addr)->sin_port))&0x0ffff);
171 child= fork(); if (child==-1) goto x_err;
174 if (dup2(fd,0)) exiterrno(errno);
176 execl(HELPER,HELPER,addrarg,portarg,(char*)0);
177 status= errno > 0 && errno < 127 ? errno : 127;
178 STDERRSTR_CONST("libauthbind: possible installation problem - "
179 "could not invoke " HELPER " (");
180 STDERRSTR_STRING(rcsid);
181 STDERRSTR_CONST(")\n");
185 rchild= waitpid(child,&status,0);
186 if (rchild==-1) goto x_err;
187 if (rchild!=child) { errno= ECHILD; goto x_err; }
189 if (WIFEXITED(status)) {
190 if (WEXITSTATUS(status)) {
191 errno= WEXITSTATUS(status);
192 if (errno >= 127) errno= ENXIO;
205 if (sigprocmask(SIG_SETMASK,&saved,0)) abort();