X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?a=blobdiff_plain;f=test%2Fudp-preload.c;h=49457ad42af888c8018d9c342f2f139fc9ef96f8;hb=539f5d7682b0afcf4f79c048666ade37da387272;hp=8c7d18cc175b284e4e905fb6e95e3fe73a8ee2d3;hpb=03d4c598998e9373fd538ce1fb920c62dbde1cdd;p=secnet.git diff --git a/test/udp-preload.c b/test/udp-preload.c index 8c7d18c..49457ad 100644 --- a/test/udp-preload.c +++ b/test/udp-preload.c @@ -33,6 +33,10 @@ #include #include +#include +#include +#include + #define STDERRSTR_CONST(m) write(2,m,sizeof(m)-1) #define STDERRSTR_STRING(m) write(2,m,strlen(m)) @@ -54,13 +58,26 @@ static anyfn_type *find_any(const char *name) { } #define socket_args int domain, int type, int protocol -#define WRAPS(X) X(socket, (domain,type,protocol)) - -#define DEF_OLD(fn,args) \ - typedef int fn##_fn_type(fn##_args); \ - static int find_##fn(fn##_args); \ +#define close_args int fd +#define bind_args int fd, const struct sockaddr *addr, socklen_t addrlen +#define sendto_args int fd, const void *buf, size_t len, int flags, \ + const struct sockaddr *addr, socklen_t addrlen +#define setsockopt_args int fd, int level, int optname, \ + const void *optval, socklen_t optlen +#define getsockname_args int fd, struct sockaddr *addr, socklen_t *addrlen +#define WRAPS(X) \ + X(socket, int, (domain,type,protocol)) \ + X(close, int, (fd)) \ + X(bind, int, (fd,addr,addrlen)) \ + X(sendto, ssize_t, (fd,buf,len,flags,addr,addrlen)) \ + X(setsockopt, int, (fd,level,optname,optval,optlen)) \ + X(getsockname,int, (fd,addr,addrlen)) + +#define DEF_OLD(fn,rt,args) \ + typedef rt fn##_fn_type(fn##_args); \ + static rt find_##fn(fn##_args); \ static fn##_fn_type find_##fn, *old_##fn=find_##fn; \ - static int find_##fn(fn##_args) { \ + static rt find_##fn(fn##_args) { \ anyfn_type *anyfn; \ anyfn= find_any(#fn); if (!anyfn) return -1; \ old_##fn= (fn##_fn_type*)anyfn; \ @@ -70,211 +87,201 @@ static anyfn_type *find_any(const char *name) { WRAPS(DEF_OLD) #define WRAP(fn) int fn(fn##_args) +#define TWRAP(fn) fn(fn##_args) -WRAP(socket) { - errno=EMSGSIZE; return -1; -} +typedef struct{ + int af; +} fdinfo; +static fdinfo **table; +static int tablesz; -#if 0 -WRAP(bind, (int fd, const struct sockaddr *addr, socklen_t addrlen), { - -}); - -static bindfn_type find_bind, *old_bind= find_bind; - -int find_bind(int fd, const struct sockaddr *addr, socklen_t addrlen) { - anyfn_type *anyfn; - anyfn= find_any("bind"); if (!anyfn) return -1; - old_bind= (bindfn_type*)anyfn; - return old_bind(fd,addr,addrlen); +static fdinfo *lookup(int fd) { + if (fd<0 || fd>=tablesz) return 0; + return table[fd]; } -static int exiterrno(int e) { - _exit(e>0 && e<128 ? e : -1); +#define ADDRPORTSTRLEN (INET6_ADDRSTRLEN+1+5) /* not including nul */ + +static int addrport2str(char buf[ADDRPORTSTRLEN+1], + const struct sockaddr *addr, socklen_t addrlen) { + const void *addrv=addr; + const void *iav; + const struct sockaddr_in *sin; + const struct sockaddr_in6 *sin6; + uint16_t port; + socklen_t el; + switch (addr->sa_family) { + case AF_INET: sin =addrv; el=sizeof(*sin ); iav=&sin ->sin_addr ; port=sin ->sin_port ; break; + case AF_INET6: sin6=addrv; el=sizeof(*sin6); iav=&sin6->sin6_addr; port=sin6->sin6_port; break; + default: errno=ESRCH; return -1; + } +//fprintf(stderr,"af=%lu el=%lu addrlen=%lu\n", +// (unsigned long)addr->sa_family, +// (unsigned long)el, +// (unsigned long)addrlen); + if (addrlen!=el) { errno=EINVAL; return -1; } + char *p=buf; + if (!inet_ntop(addr->sa_family,iav,p,INET6_ADDRSTRLEN)) return -1; + p+=strlen(p); + sprintf(p,",%u",(unsigned)ntohs(port)); + return 0; } -static void removepreload(void) { - const char *myself, *found; - char *newval, *preload; - int lpreload, lmyself, before, after; +static int str2addrport(char *str, + struct sockaddr *addr, socklen_t *addrlen) { + union { + struct sockaddr_in sin; + struct sockaddr_in6 sin6; + } si; + + memset(&si,0,sizeof(si)); + + int af; + void *iav; + uint16_t *portp; + socklen_t al; + switch (str[strcspn(str,".:")]) { + case '.': af=AF_INET ; iav=&si.sin .sin_addr ; al=sizeof(si.sin ); portp=&si.sin .sin_port ; break; + case ':': af=AF_INET6; iav=&si.sin6.sin6_addr; al=sizeof(si.sin6); portp=&si.sin6.sin6_port; break; + default: errno=ESRCH; return -1; + } + si.sin.sin_family=af; + + char *comma=strchr(str,','); + if (!comma) { errno=ESRCH; return -1; } + *comma++=0; + int r=inet_pton(af,str,iav); +//fprintf(stderr,"inet_pton(%d,\"%s\",)=%d\n",af,str,r); + if (r<0) return -1; + if (r==0) { errno=ENOTTY; return -1; } + + char *ep; + errno=0; + unsigned long port=strtoul(comma,&ep,10); + if (ep==comma || *ep || errno || port>65536) { errno=ESRCH; return -1; } + *portp= htons(port); + + if (addr) memcpy(addr,&si, *addrlensun_family=AF_UNIX; + int dl = strlen(dir); + if (dl + 1 + ADDRPORTSTRLEN + 1 > sizeof(sun->sun_path)) { + errno=ENAMETOOLONG; return 0; + } + strcpy(sun->sun_path,dir); + char *p=sun->sun_path+dl; + *p++='/'; + return p; +} - if (lmyself < 1 || lpreload preload+lpreload-(lmyself+1)) return; - if (found[-1]==':' && found[lmyself]==':') break; - found++; +WRAP(socket) { + if (!((domain==AF_INET || domain==AF_INET6) && + type==SOCK_DGRAM)) + return old_socket(domain,type,protocol); + int fd=socket(AF_UNIX,SOCK_DGRAM,0); + if (fd<0) return fd; + if (fd>=tablesz) { + int newsz=(fd+1)*2; + table=realloc(table,newsz*sizeof(*table)); + if (!table) goto fail; + while (tableszaf=domain; + return fd; + + fail: + close(fd); + return -1; } -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; +WRAP(close) { + if (fd>=0 && fdsa_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; - } +WRAP(bind) { + fdinfo *ent=lookup(fd); + if (!ent) return old_bind(fd,addr,addrlen); + struct sockaddr_un sun; + char *p=sun_prep(&sun); + if (addrport2str(p,addr,addrlen)) return -1; +//fprintf(stderr,"binding %s\n",sun.sun_path); + if (unlink(sun.sun_path) && errno!=ENOENT) return -1; + return old_bind(fd,(const void*)&sun,sizeof(sun)); +} - if (!geteuid() || portval == 0 || ntohs(portval) >= IPPORT_RESERVED) { - bail: - return old_bind(fd,addr,addrlen); - } +WRAP(setsockopt) { + fdinfo *ent=lookup(fd); + if (!ent) return old_setsockopt(fd,level,optname,optval,optlen); + if (ent->af==AF_INET6 && level==IPPROTO_IPV6 && optname==IPV6_V6ONLY + && optlen==sizeof(int) && *(int*)optval==1) { + return 0; + } + errno=ENOTTY; + return -1; +} - 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", - 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; - } +WRAP(getsockname) { + fdinfo *ent=lookup(fd); + if (!ent) return old_getsockname(fd,addr,addrlen); + struct sockaddr_un sun; + socklen_t sunlen=sizeof(sun); + if (old_getsockname(fd,(void*)&sun,&sunlen)) return -1; + if (sun.sun_family!=AF_UNIX || sunlen>sizeof(sun)) { +//fprintf(stderr,"old_getsockname af=%lu sunlen=%lu\n", +// (unsigned long)sun.sun_family, +// (unsigned long)sunlen); + errno=EDOM; return -1; + } + char *slash=strrchr(sun.sun_path,'/'); + if (str2addrport(slash ? slash+1 : sun.sun_path, + addr,addrlen)) return -1; + return 0; +} - child= fork(); if (child==-1) goto x_err; +ssize_t TWRAP(sendto) { + fdinfo *ent=lookup(fd); + if (!ent) return old_sendto(fd,buf,len,flags,addr,addrlen); - if (!child) { - if (dup2(fd,0)) exiterrno(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); - } + if (flags) { errno=ENOEXEC; return -1; } - rchild= waitpid(child,&status,0); - if (rchild==-1) goto x_err; - if (rchild!=child) { errno= ECHILD; goto x_err; } + const char *leaf=getenv("UDP_PRELOAD_SERVER"); + if (!leaf) leaf="udp"; + if (strlen(leaf) > ADDRPORTSTRLEN) { errno=ENAMETOOLONG; return -1; } + struct sockaddr_un sun; + char *p=sun_prep(&sun); + strcpy(p,leaf); - if (WIFEXITED(status)) { - if (WEXITSTATUS(status)) { - errno= WEXITSTATUS(status); - if (errno >= 127) errno= ENXIO; - goto x_err; - } - r= 0; - goto x; - } else { - errno= ENOSYS; - goto x_err; - } + char tbuf[ADDRPORTSTRLEN+1]; + memset(tbuf,0,sizeof(tbuf)); + if (addrport2str(tbuf,addr,addrlen)) return -1; -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; + struct iovec iov[2]; + iov[0].iov_base=tbuf; + iov[0].iov_len=sizeof(tbuf); + iov[1].iov_base=(void*)buf; + iov[1].iov_len=len; + + struct msghdr m; + memset(&m,0,sizeof(m)); + m.msg_name=&sun; + m.msg_namelen=sizeof(sun); + m.msg_iov=iov; + m.msg_iovlen=2; + + return sendmsg(fd,&m,0); } -#endif