chiark / gitweb /
Initial checkin.
[authbind.git] / libauthbind.c
1 /*
2  */
3
4 #include <dlfcn.h>
5 #include <stdio.h>
6 #include <sys/types.h>
7 #include <unistd.h>
8 #include <errno.h>
9 #include <stdlib.h>
10 #include <string.h>
11 #include <sys/socket.h>
12 #include <sys/wait.h>
13 #include <netinet/in.h>
14
15 #define AUTHBIND_NESTED "AUTHBIND_NESTED"
16 #define HELPER "/usr/lib/authbind/helper"
17
18 typedef void anyfn_type(void);
19 typedef int bindfn_type(int fd, const struct sockaddr *addr, socklen_t addrlen);
20
21 #define STDERRSTR_CONST(m) write(2,m,sizeof(m)-1)
22 #define STDERRSTR_STRING(m) write(2,m,strlen(m))
23
24 static int find_any(const char *name, anyfn_type **keep) {
25   static const char *dlerr;
26   anyfn_type *kv;
27
28   if (*keep) return 0;
29   kv= dlsym(RTLD_NEXT,name);
30   if (kv) { *keep= kv; return 0; }
31   dlerr= dlerror(); if (!dlerr) dlerr= "dlsym() failed for no reason";
32   STDERRSTR_CONST("libauthbind: error finding original version of ");
33   STDERRSTR_STRING(name);
34   STDERRSTR_CONST(": ");
35   STDERRSTR_STRING(dlerr);
36   STDERRSTR_STRING("\n");
37   errno= ENOSYS;
38   return -1;
39 }
40
41 static bindfn_type find_bind, *old_bind= find_bind;
42
43 int find_bind(int fd, const struct sockaddr *addr, socklen_t addrlen) {
44   anyfn_type *anyfn;
45   if (find_any("bind",&anyfn)) return -1;
46   old_bind= (bindfn_type*)anyfn;
47   return old_bind(fd,addr,addrlen);
48 }
49
50 static int exiterrno(int e) {
51   _exit(e>0 && e<128 ? e : -1);
52 }
53
54 int bind(int fd, const struct sockaddr *addr, socklen_t addrlen) {
55   pid_t child, rchild;
56   char portarg[5], addrarg[9];
57   int status;
58   
59   if (addr->sa_family != AF_INET || addrlen != sizeof(struct sockaddr_in) ||
60       ntohs(((struct sockaddr_in*)addr)->sin_port) >= 1024 || !geteuid())
61     return old_bind(fd,addr,addrlen);
62
63   if (getenv(AUTHBIND_NESTED)) {
64     STDERRSTR_CONST("libauthbind: possible installation problem - "
65                     "nested invocation, perhaps helper is not setuid\n");
66     return old_bind(fd,addr,addrlen);
67   }
68
69   sprintf(addrarg,"%08lx",
70           ((unsigned long)(((struct sockaddr_in*)addr)->sin_addr.s_addr))&0x0ffffffffUL);
71   sprintf(portarg,"%04x",
72           ((unsigned int)(((struct sockaddr_in*)addr)->sin_port))&0x0ffff);
73
74   child= fork(); if (child==-1) return -1;
75
76   if (!child) {
77     if (dup2(fd,0)) exiterrno(errno);
78     if (setenv(AUTHBIND_NESTED,"1",1)) exiterrno(errno);
79     execl(HELPER,HELPER,addrarg,portarg,(char*)0);
80     status= errno;
81     STDERRSTR_CONST("libauthbind: possible installation problem - "
82                     "could not invoke " HELPER "\n");
83     exiterrno(status);
84   }
85
86   rchild= waitpid(child,&status,0);
87   if (rchild==-1) return -1;
88   if (rchild!=child) { errno= ECHILD; return -1; }
89
90   if (WIFEXITED(status)) {
91     if (WEXITSTATUS(status)) { errno= WEXITSTATUS(status); return -1; }
92     return 0;
93   } else {
94     errno= ENOSYS;
95     return -1;
96   }
97 }