chiark / gitweb /
a16f8357baf16eccd3af0bfff51ad00845dbc97d
[authbind.git] / libauthbind.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 #include <dlfcn.h>
23 #include <stdio.h>
24 #include <sys/types.h>
25 #include <unistd.h>
26 #include <errno.h>
27 #include <stdlib.h>
28 #include <string.h>
29 #include <sys/socket.h>
30 #include <sys/wait.h>
31 #include <netinet/in.h>
32
33 static const char *rcsid="$Id$";
34
35 #include "authbind.h"
36
37 typedef void anyfn_type(void);
38 typedef int bindfn_type(int fd, const struct sockaddr *addr, socklen_t addrlen);
39
40 #define STDERRSTR_CONST(m) write(2,m,sizeof(m)-1)
41 #define STDERRSTR_STRING(m) write(2,m,strlen(m))
42
43 static anyfn_type *find_any(const char *name) {
44   static const char *dlerr;
45   anyfn_type *kv;
46
47   kv= dlsym(RTLD_NEXT,name); if (kv) return kv;
48   dlerr= dlerror(); if (!dlerr) dlerr= "dlsym() failed for no reason";
49   STDERRSTR_CONST("libauthbind: error finding original version of ");
50   STDERRSTR_STRING(name);
51   STDERRSTR_CONST(": ");
52   STDERRSTR_STRING(dlerr);
53   STDERRSTR_STRING("\n");
54   errno= ENOSYS;
55   return 0;
56 }
57
58 static bindfn_type find_bind, *old_bind= find_bind;
59
60 int find_bind(int fd, const struct sockaddr *addr, socklen_t addrlen) {
61   anyfn_type *anyfn;
62   anyfn= find_any("bind"); if (!anyfn) return -1;
63   old_bind= (bindfn_type*)anyfn;
64   return old_bind(fd,addr,addrlen);
65 }
66
67 static int exiterrno(int e) {
68   _exit(e>0 && e<128 ? e : -1);
69 }
70
71 static void removepreload(void) {
72   const char *myself, *found;
73   char *newval, *preload;
74   int lpreload, lmyself, before, after;
75
76   preload= getenv(PRELOAD_VAR);
77   myself= getenv(AUTHBINDLIB_VAR);
78   if (!myself || !preload) return;
79
80   lpreload= strlen(preload);
81   lmyself= strlen(myself);
82
83   if (lmyself < 1 || lpreload<lmyself) return;
84   if (lpreload==lmyself) {
85     if (!strcmp(preload,myself)) unsetenv(PRELOAD_VAR);
86     return;
87   }
88   if (!memcmp(preload,myself,lmyself) && preload[lmyself]==':') {
89     before= 0; after= lpreload-(lmyself+1);
90   } else if (!memcmp(preload+lpreload-lmyself,myself,lmyself) &&
91              preload[lpreload-(lmyself+1)]==':') {
92     before= lpreload-(lmyself+1); after= 0;
93   } else {
94     if (lpreload<lmyself+2) return;
95     found= preload+1;
96     for (;;) {
97       found= strstr(found,myself); if (!found) return;
98       if (found > preload+lpreload-(lmyself+1)) return;
99       if (found[-1]==':' && found[lmyself]==':') break;
100       found++;
101     }
102     before= found-preload;
103     after= lpreload-(before+lmyself+1);
104   }
105   newval= malloc(before+after+1);
106   if (newval) {
107     memcpy(newval,preload,before);
108     strcpy(newval+before,preload+lpreload-after);
109     if (setenv(PRELOAD_VAR,newval,1)) return;
110     free(newval);
111   }
112   strcpy(preload+before,preload+lpreload-after);
113   return;
114 }
115
116 int _init(void);
117 int _init(void) {
118   char *levels;
119   int levelno;
120
121   /* If AUTHBIND_LEVELS is
122    *  unset => always strip from preload
123    *  set and starts with `y' => never strip from preload, keep AUTHBIND_LEVELS
124    *  set to integer > 1 => do not strip now, subtract one from AUTHBIND_LEVELS
125    *  set to integer 1 => do not strip now, unset AUTHBIND_LEVELS
126    *  set to empty string or 0 => strip now, unset AUTHBIND_LEVELS
127    */
128   levels= getenv(AUTHBIND_LEVELS_VAR);
129   if (levels) {
130     if (levels[0]=='y') return 0;
131     levelno= atoi(levels);
132     if (levelno > 0) {
133       levelno--;
134       if (levelno > 0) sprintf(levels,"%d",levelno);
135       else unsetenv(AUTHBIND_LEVELS_VAR);
136       return 0;
137     }
138     unsetenv(AUTHBIND_LEVELS_VAR);
139   }
140   removepreload();
141   return 0;
142 }
143
144 static const int evilsignals[]= { SIGFPE, SIGILL, SIGSEGV, SIGBUS, 0 };
145
146 int bind(int fd, const struct sockaddr *addr, socklen_t addrlen) {
147   pid_t child, rchild;
148   char portarg[5], addrarg[9];
149   int r, status;
150   const int *evilsignal;
151   sigset_t block, saved;
152
153   if (addr->sa_family != AF_INET || addrlen != sizeof(struct sockaddr_in) ||
154       !geteuid() || ((struct sockaddr_in*)addr)->sin_port == 0 ||
155       ntohs(((struct sockaddr_in*)addr)->sin_port) >= IPPORT_RESERVED/2)
156     return old_bind(fd,addr,addrlen);
157
158   sigfillset(&block);
159   for (evilsignal=evilsignals;
160        *evilsignal;
161        evilsignal++)
162     sigdelset(&block,*evilsignal);
163   if (sigprocmask(SIG_BLOCK,&block,&saved)) return -1;
164   
165   sprintf(addrarg,"%08lx",
166           ((unsigned long)(((struct sockaddr_in*)addr)->sin_addr.s_addr))&0x0ffffffffUL);
167   sprintf(portarg,"%04x",
168           ((unsigned int)(((struct sockaddr_in*)addr)->sin_port))&0x0ffff);
169
170   child= fork(); if (child==-1) goto x_err;
171
172   if (!child) {
173     if (dup2(fd,0)) exiterrno(errno);
174     removepreload();
175     execl(HELPER,HELPER,addrarg,portarg,(char*)0);
176     status= errno > 0 && errno < 127 ? errno : 127;
177     STDERRSTR_CONST("libauthbind: possible installation problem - "
178                     "could not invoke " HELPER " (");
179     STDERRSTR_STRING(rcsid);
180     STDERRSTR_CONST(")\n");
181     exiterrno(status);
182   }
183
184   rchild= waitpid(child,&status,0);
185   if (rchild==-1) goto x_err;
186   if (rchild!=child) { errno= ECHILD; goto x_err; }
187
188   if (WIFEXITED(status)) {
189     if (WEXITSTATUS(status)) {
190       errno= WEXITSTATUS(status);
191       if (errno >= 127) errno= ENXIO;
192       goto x_err;
193     }
194     r= 0;
195     goto x;
196   } else {
197     errno= ENOSYS;
198     goto x_err;
199   }
200
201 x_err:
202   r= -1;
203 x:
204   if (sigprocmask(SIG_SETMASK,&saved,0)) abort();
205   return r;
206 }