#include <unistd.h>
#include <errno.h>
#include <stdlib.h>
+#include <signal.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/wait.h>
#include <netinet/in.h>
-static const char *rcsid="$Id$";
-
-#ifndef HELPER
-# define HELPER "/usr/local/lib/authbind/helper"
-#endif
-
-#define AUTHBIND_NESTED_VAR "AUTHBIND_NESTED"
+#include "authbind.h"
typedef void anyfn_type(void);
typedef int bindfn_type(int fd, const struct sockaddr *addr, socklen_t addrlen);
#define STDERRSTR_CONST(m) write(2,m,sizeof(m)-1)
#define STDERRSTR_STRING(m) write(2,m,strlen(m))
-static int find_any(const char *name, anyfn_type **keep) {
+static anyfn_type *find_any(const char *name) {
static const char *dlerr;
anyfn_type *kv;
- if (*keep) return 0;
- kv= dlsym(RTLD_NEXT,name);
- if (kv) { *keep= kv; return 0; }
+ kv= dlsym(RTLD_NEXT,name); if (kv) return kv;
dlerr= dlerror(); if (!dlerr) dlerr= "dlsym() failed for no reason";
STDERRSTR_CONST("libauthbind: error finding original version of ");
STDERRSTR_STRING(name);
STDERRSTR_STRING(dlerr);
STDERRSTR_STRING("\n");
errno= ENOSYS;
- return -1;
+ return 0;
}
static bindfn_type find_bind, *old_bind= find_bind;
int find_bind(int fd, const struct sockaddr *addr, socklen_t addrlen) {
anyfn_type *anyfn;
- if (find_any("bind",&anyfn)) return -1;
+ anyfn= find_any("bind"); if (!anyfn) return -1;
old_bind= (bindfn_type*)anyfn;
return old_bind(fd,addr,addrlen);
}
_exit(e>0 && e<128 ? e : -1);
}
+static void removepreload(void) {
+ const char *myself, *found;
+ char *newval, *preload;
+ int lpreload, lmyself, before, after;
+
+ preload= getenv(PRELOAD_VAR);
+ myself= getenv(AUTHBINDLIB_VAR);
+ if (!myself || !preload) return;
+
+ lpreload= strlen(preload);
+ lmyself= strlen(myself);
+
+ if (lmyself < 1 || lpreload<lmyself) return;
+ if (lpreload==lmyself) {
+ if (!strcmp(preload,myself)) unsetenv(PRELOAD_VAR);
+ return;
+ }
+ if (!memcmp(preload,myself,lmyself) && preload[lmyself]==':') {
+ before= 0; after= lpreload-(lmyself+1);
+ } else if (!memcmp(preload+lpreload-lmyself,myself,lmyself) &&
+ preload[lpreload-(lmyself+1)]==':') {
+ before= lpreload-(lmyself+1); after= 0;
+ } else {
+ if (lpreload<lmyself+2) return;
+ found= preload+1;
+ for (;;) {
+ found= strstr(found,myself); if (!found) return;
+ if (found > preload+lpreload-(lmyself+1)) return;
+ if (found[-1]==':' && found[lmyself]==':') break;
+ found++;
+ }
+ before= found-preload;
+ after= lpreload-(before+lmyself+1);
+ }
+ newval= malloc(before+after+1);
+ if (newval) {
+ memcpy(newval,preload,before);
+ strcpy(newval+before,preload+lpreload-after);
+ if (setenv(PRELOAD_VAR,newval,1)) return;
+ free(newval);
+ }
+ strcpy(preload+before,preload+lpreload-after);
+ return;
+}
+
+int _init(void);
+int _init(void) {
+ char *levels;
+ int levelno;
+
+ /* If AUTHBIND_LEVELS is
+ * unset => always strip from preload
+ * set and starts with `y' => never strip from preload, keep AUTHBIND_LEVELS
+ * set to integer > 1 => do not strip now, subtract one from AUTHBIND_LEVELS
+ * set to integer 1 => do not strip now, unset AUTHBIND_LEVELS
+ * set to empty string or 0 => strip now, unset AUTHBIND_LEVELS
+ */
+ levels= getenv(AUTHBIND_LEVELS_VAR);
+ if (levels) {
+ if (levels[0]=='y') return 0;
+ levelno= atoi(levels);
+ if (levelno > 0) {
+ levelno--;
+ if (levelno > 0) sprintf(levels,"%d",levelno);
+ else unsetenv(AUTHBIND_LEVELS_VAR);
+ return 0;
+ }
+ unsetenv(AUTHBIND_LEVELS_VAR);
+ }
+ removepreload();
+ return 0;
+}
+
+static const int evilsignals[]= { SIGFPE, SIGILL, SIGSEGV, SIGBUS, 0 };
+
int bind(int fd, const struct sockaddr *addr, socklen_t addrlen) {
pid_t child, rchild;
- char portarg[5], addrarg[9];
- int status;
-
- if (addr->sa_family != AF_INET || addrlen != sizeof(struct sockaddr_in) ||
- ntohs(((struct sockaddr_in*)addr)->sin_port) >= IPPORT_RESERVED/2 || !geteuid())
- return old_bind(fd,addr,addrlen);
+ char portarg[5], addrarg[33];
+ const char *afarg;
+ int i, r, status, restore_sigchild;
+ const int *evilsignal;
+ sigset_t block, saved;
+ struct sigaction old_sigchild;
+ unsigned int portval;
+
+ switch (addr->sa_family) {
+ case AF_INET:
+ portval = ((struct sockaddr_in*)addr)->sin_port;
+ if (addrlen != sizeof(struct sockaddr_in)) goto bail;
+ break;
+ case AF_INET6:
+ portval = ((struct sockaddr_in6*)addr)->sin6_port;
+ if (addrlen != sizeof(struct sockaddr_in6)) goto bail;
+ break;
+ default:
+ goto bail;
+ }
- if (getenv(AUTHBIND_NESTED_VAR)) {
- STDERRSTR_CONST("libauthbind: possible installation problem - "
- "nested invocation, perhaps helper is not setuid\n ");
- STDERRSTR_STRING(rcsid);
- STDERRSTR_CONST("\n");
+ if (!geteuid() || portval == 0 || ntohs(portval) >= IPPORT_RESERVED) {
+ bail:
return old_bind(fd,addr,addrlen);
}
- sprintf(addrarg,"%08lx",
- ((unsigned long)(((struct sockaddr_in*)addr)->sin_addr.s_addr))&0x0ffffffffUL);
+ sigfillset(&block);
+ for (evilsignal=evilsignals;
+ *evilsignal;
+ evilsignal++)
+ sigdelset(&block,*evilsignal);
+ if (sigprocmask(SIG_BLOCK,&block,&saved)) return -1;
+
+ switch (addr->sa_family) {
+ case AF_INET:
+ afarg = 0;
+ sprintf(addrarg,"%08lx",
+ ((unsigned long)(((struct sockaddr_in*)addr)->sin_addr.s_addr))
+ &0x0ffffffffUL);
+ break;
+ case AF_INET6:
+ afarg = "6";
+ for (i=0; i<16; i++)
+ sprintf(addrarg+i*2,"%02x",
+ ((struct sockaddr_in6*)addr)->sin6_addr.s6_addr[i]);
+ break;
+ default:
+ abort();
+ }
sprintf(portarg,"%04x",
- ((unsigned int)(((struct sockaddr_in*)addr)->sin_port))&0x0ffff);
+ portval&0x0ffff);
+
+ restore_sigchild= 0;
+ if (sigaction(SIGCHLD,NULL,&old_sigchild)) return -1;
+ if (old_sigchild.sa_handler == SIG_IGN) {
+ struct sigaction new_sigchild;
+
+ new_sigchild.sa_handler= SIG_DFL;
+ sigemptyset(&new_sigchild.sa_mask);
+ new_sigchild.sa_flags= 0;
+ if (sigaction(SIGCHLD,&new_sigchild,&old_sigchild)) return -1;
+ restore_sigchild= 1;
+ }
- child= fork(); if (child==-1) return -1;
+ child= fork(); if (child==-1) goto x_err;
if (!child) {
if (dup2(fd,0)) exiterrno(errno);
- if (setenv(AUTHBIND_NESTED_VAR,"1",1)) exiterrno(errno);
- execl(HELPER,HELPER,addrarg,portarg,(char*)0);
- status= errno;
+ removepreload();
+ execl(HELPER,HELPER,addrarg,portarg,afarg,(char*)0);
+ status= errno > 0 && errno < 127 ? errno : 127;
STDERRSTR_CONST("libauthbind: possible installation problem - "
"could not invoke " HELPER "\n");
exiterrno(status);
}
rchild= waitpid(child,&status,0);
- if (rchild==-1) return -1;
- if (rchild!=child) { errno= ECHILD; return -1; }
+ if (rchild==-1) goto x_err;
+ if (rchild!=child) { errno= ECHILD; goto x_err; }
if (WIFEXITED(status)) {
- if (WEXITSTATUS(status)) { errno= WEXITSTATUS(status); return -1; }
- return 0;
+ if (WEXITSTATUS(status)) {
+ errno= WEXITSTATUS(status);
+ if (errno >= 127) errno= ENXIO;
+ goto x_err;
+ }
+ r= 0;
+ goto x;
} else {
errno= ENOSYS;
- return -1;
+ goto x_err;
+ }
+
+x_err:
+ r= -1;
+x:
+ if (sigprocmask(SIG_SETMASK,&saved,0)) abort();
+ if (restore_sigchild) {
+ if (sigaction(SIGCHLD,&old_sigchild,NULL)) return -1;
+ if (old_sigchild.sa_handler == SIG_IGN) {
+ int discard;
+ while (waitpid(-1, &discard, WNOHANG) > 0)
+ ;
+ }
}
+ return r;
}