/* 950123: Fixed things to be more GENERIC -- now it compiles also on OSF/1 Alpha... 21/7/92 Fixed SIGPIPE bug in ident_tcpuser3(). 2/9/92: identuser 4.0. Public domain. 2/9/92: added bunches of zeroing just in case. 2/9/92: added ident_tcpuser3. uses bsd 4.3 select interface. 2/9/92: added ident_tcpsock, ident_sockuser. 2/9/92: added ident_fd2, ident_tcpuser2, simplified some of the code. 12/27/91: fixed up usercmp to deal with restricted tolower XXX 5/6/91 DJB baseline identuser 3.1. Public domain. */ /* Tuned to be part of ZMailer -- #include "zmsignal.h" .. */ #define USENONBLOCK 1 #include "hostenv.h" #include #include "zmsignal.h" #include #include #include #include /*XXX*/ #ifdef HAVE_SYS_SOCKET_H #include #endif #include #ifdef HAVE_NETINET_IN6_H # include #endif #ifdef HAVE_NETINET6_IN6_H # include #endif #ifdef HAVE_LINUX_IN6_H # include #endif #include #include #ifdef ISC #include #endif extern int errno; #include "identuser.h" #ifdef _AIX #include #endif #ifdef HAVE_SYS_SELECT_H #include #endif #include #ifndef FNDELAY #define FNDELAY O_NDELAY #endif unsigned int ident_tcpport = 113; #define SIZ 500 /* various buffers */ #define CLORETS(e) { saveerrno = errno; close(s); errno = saveerrno; return e; } static jmp_buf jmpalarm; static int ident_tcpsock5 __((const int, const int, const void *, const void *, int *)); static int ident_tcpsock5(af, len, inlocal, inremote, sockp) const int af, len; const void *inlocal; const void *inremote; int *sockp; { union { struct sockaddr_in v4; #if defined(AF_INET6) && defined(INET6) struct sockaddr_in6 v6; #endif } sa; register int s; register int fl; register int saveerrno; *sockp = -1; if ((s = socket(af, SOCK_STREAM, 0)) == -1) return -1; memset(&sa,0,sizeof(sa)); if (af == AF_INET) { sa.v4.sin_family = AF_INET; sa.v4.sin_port = 0; memcpy(&sa.v4.sin_addr, inlocal, len); if (sa.v4.sin_addr.s_addr != 0) if (bind(s,(struct sockaddr*)&sa,sizeof(sa.v4)) < 0) CLORETS(-1); } #if defined(AF_INET6) && defined(INET6) else if (af == AF_INET6) { sa.v6.sin6_family = AF_INET6; sa.v6.sin6_flowinfo = 0; sa.v6.sin6_port = 0; memcpy(&sa.v6.sin6_addr, inlocal, len); if (bind(s, (struct sockaddr*)&sa, sizeof(sa.v6)) < 0) CLORETS(-1); } #endif else { CLORETS(-1); } #if USENONBLOCK if ((fl = fcntl(s, F_GETFL, 0)) == -1) CLORETS(-1); if (fcntl(s, F_SETFL, FNDELAY | fl) == -1) CLORETS(-1); #endif *sockp = s; /* Following connect may take non-trivial time, and we may be jumping around with SIGALRM, so for now do save the socket number.. */ memset(&sa,0,sizeof(sa)); if (af == AF_INET) { sa.v4.sin_family = AF_INET; sa.v4.sin_port = htons(ident_tcpport); memcpy(&sa.v4.sin_addr, inremote, len); if (connect(s,(struct sockaddr*)&sa,sizeof(sa.v4)) < 0) if (errno != EINPROGRESS) CLORETS(-1); } #if defined(AF_INET6) && defined(INET6) else if (af == AF_INET6) { sa.v6.sin6_family = AF_INET6; sa.v6.sin6_flowinfo = 0; sa.v6.sin6_port = htons(ident_tcpport); memcpy(&sa.v6.sin6_addr, inremote, len); if (connect(s,(struct sockaddr*)&sa,sizeof(sa.v6)) < 0) if (errno != EINPROGRESS) CLORETS(-1); } #endif else { CLORETS(-1); } return s; } static volatile const char *ident_sockuser2 __((const int, const int, const int, char *, const int)); static volatile const char *ident_sockuser2(s,local,remote,realbuf,realbuflen) const int s; const int local; const int remote; char *realbuf; const int realbuflen; { register int buflen; register int w; register int saveerrno; int rlocal; int rremote; register int fl; fd_set wfds; void (*old_sig)__((int)); char *buf, *ebuf; SIGNAL_HANDLESAVE(SIGPIPE, SIG_IGN, old_sig); FD_ZERO(&wfds); FD_SET(s,&wfds); select(s + 1,(fd_set *) 0,&wfds,(fd_set *) 0,(struct timeval *) 0); /* now s is writable */ #if USENONBLOCK if ((fl = fcntl(s,F_GETFL,0)) == -1) { SIGNAL_HANDLE(SIGPIPE, old_sig); CLORETS("SOCKFCNTL1"); } if (fcntl(s,F_SETFL,~FNDELAY & fl) == -1) { SIGNAL_HANDLE(SIGPIPE, old_sig); CLORETS("SOCKFCNTL2"); } #endif buf = realbuf; sprintf(buf,"%u , %u\r\n",(unsigned int) remote,(unsigned int) local); /* note the reversed order---the example in RFC 931 is misleading */ buflen = strlen(buf); while ((w = write(s,buf,buflen)) < buflen) if (w == -1) /* should we worry about 0 as well? */ { SIGNAL_HANDLE(SIGPIPE, old_sig); saveerrno = errno; close(s); if (errno == ECONNREFUSED || errno == EPIPE) return "NO-IDENT-SERVICE[2]"; else { sprintf(realbuf,"SOCKWRITE-%d", errno); return realbuf; } } else { buf += w; buflen -= w; } buf = realbuf; ebuf = realbuf + realbuflen; while (1) { int spcleft = (ebuf - buf); int c; char *pp = buf; w = read(s, buf, spcleft); if (w < 0 && (errno == EINTR)) continue; if (w == 0) break; /* EOF */ while (w >= 0 && buf < ebuf) { c = *pp; if (!(c == ' ' || c == '\t' || c == '\r')) { *buf = c; ++buf; } ++pp; --w; if (c == '\n') goto bail_out; } } bail_out:; SIGNAL_HANDLE(SIGPIPE, old_sig); if (w == -1) CLORETS("SOCKREAD"); *buf = 0; if (sscanf(realbuf,"%d,%d:USERID:%*[^:]:%s", &rremote,&rlocal,realbuf) < 3) { close(s); errno = EIO; /* makes sense, right? well, not when USERID failed to match ERROR */ /* but there's no good error to return in that case */ return "IDENT-NONSENSE"; } if ((remote != rremote) || (local != rlocal)) { close(s); errno = EIO; return "IDENT-NONSENSE2"; } /* we're not going to do any backslash processing */ close(s); return realbuf; } static void sig_alrm __((int)); static void sig_alrm (sig) int sig; { SIGNAL_RELEASE(sig); SIGNAL_HANDLE(sig, sig_alrm); longjmp(jmpalarm,1); } volatile const char *ident_tcpuser9(af,len,inlocal,inremote,local,remote,timeout,buf,buflen) const int af, len; /* Address family, and address size */ const void *inlocal; /* Addresses */ const void *inremote; const int local; /* Ports */ const int remote; const int timeout; char *buf; const int buflen; { int s, r; struct timeval tv; fd_set wfds; int saveerrno; void (*old_sig)__((int)); void (*old_alrm)__((int)); volatile unsigned int oldival = 0; volatile const char *retval; SIGNAL_HANDLESAVE(SIGPIPE, SIG_IGN, old_sig); SIGNAL_HANDLESAVE(SIGALRM, sig_alrm, old_alrm); s = -1; if (setjmp(jmpalarm) == 0) { oldival = alarm(timeout+5); r = ident_tcpsock5(af, len, inlocal, inremote, &s); if (r < 0) { SIGNAL_HANDLE(SIGPIPE, old_sig); SIGNAL_HANDLE(SIGALRM, old_alrm); alarm(oldival); if (errno == ECONNREFUSED) return "NO-IDENT-SERVICE"; return "SOCKFAULT1"; } tv.tv_sec = timeout; tv.tv_usec = 0; FD_ZERO(&wfds); FD_SET(s,&wfds); r = select(s + 1, NULL, &wfds, NULL,&tv); /* XXX: how to handle EINTR? */ if (r == -1) { SIGNAL_HANDLE(SIGPIPE, old_sig); SIGNAL_HANDLE(SIGALRM, old_alrm); alarm(oldival); CLORETS("SOCKSELERR"); } if (!FD_ISSET(s,&wfds)) { close(s); SIGNAL_HANDLE(SIGALRM, old_alrm); SIGNAL_HANDLE(SIGPIPE, old_sig); alarm(oldival); errno = ETIMEDOUT; return "TIMEDOUT"; } retval = ident_sockuser2(s,local,remote,buf,buflen); } else { /* We reach here in case of alarm timer chime... */ retval = "TIMEDOUT2"; } if (s >= 0) close(s); SIGNAL_HANDLE(SIGALRM, old_alrm); SIGNAL_HANDLE(SIGPIPE, old_sig); alarm(oldival); return retval; } #if 0 /* ------- various unused things! ---------------*/ int ident_fd2(fd,inlocal,inremote,local,remote) register int fd; register struct in_addr *inlocal; register struct in_addr *inremote; register unsigned short *local; register unsigned short *remote; { struct sockaddr_in sa; int dummy; dummy = sizeof(sa); if (getsockname(fd,(struct sockaddr*)&sa,&dummy) == -1) return 1; if (sa.sin_family != AF_INET) { errno = EAFNOSUPPORT; return 2; } *local = ntohs(sa.sin_port); *inlocal = sa.sin_addr; dummy = sizeof(sa); if (getpeername(fd,(struct sockaddr*)&sa,&dummy) == -1) return 3; *remote = ntohs(sa.sin_port); *inremote = sa.sin_addr; return 0; } static int usercmp(u,v) register char *u; register char *v; { register char uc; register char vc; register char ucvc; /* is it correct to consider Foo and fOo the same user? yes */ /* but the function of this routine may change later */ while ((uc = *u) && (vc = *v)) { ucvc = (isupper(uc) ? tolower(uc) : uc) - (isupper(vc) ? tolower(vc) : vc); if (ucvc) return ucvc; else ++u,++v; } return uc || vc; } static char identline[SIZ]; char *ident_infoline(user,fd,in) register char *user; /* the supposed name of the user, NULL if unknown */ register int fd; /* the file descriptor of the connection */ register struct in_addr *in; { unsigned short local; unsigned short remote; register char *ruser; if (ident_fd(fd,in,&local,&remote) == -1) return 0; ruser = ident_tcpuser(*in,local,remote); if (!ruser) return 0; if (!user) user = ruser; /* forces X-Ident-User */ sprintf(identline, (usercmp(ruser,user) ? "forgery %s" : "identuser %s"), ruser); return identline; } int ident_fd(fd,in,local,remote) register int fd; register struct in_addr *in; register unsigned short *local; register unsigned short *remote; { struct in_addr inlocal; return ident_fd2(fd,&inlocal,in,local,remote); } static char ruser[SIZ]; static char realbuf[SIZ]; static char *buf; char *ident_tcpuser(in,local,remote) register struct in_addr in; register unsigned short local; register unsigned short remote; { return ident_tcpuser2(0,in,local,remote); } char *ident_tcpuser2(inlocal,inremote,local,remote) struct in_addr *inlocal; struct in_addr *inremote; unsigned short local; unsigned short remote; { register int s; s = ident_tcpsock(inlocal,inremote); if (s == -1) return 0; return ident_sockuser(s,local,remote); } char *ident_tcpuser3(inlocal,inremote,local,remote,timeout) register struct in_addr *inlocal; register struct in_addr *inremote; register unsigned short local; register unsigned short remote; register int timeout; { register int s; struct timeval tv; fd_set wfds; register int r; register int saveerrno; void (*old_sig)__((int)); char *retval; SIGNAL_HANDLESAVE(SIGPIPE, SIG_IGN, old_sig); s = ident_tcpsock(inlocal,inremote); if (s == -1) { SIGNAL_HANDLE(SIGPIPE, old_sig); return "SOCKFAULT1"; } tv.tv_sec = timeout; tv.tv_usec = 0; FD_ZERO(&wfds); FD_SET(s,&wfds); r = select(s + 1,(fd_set *) 0,&wfds,(fd_set *) 0,&tv); /* XXX: how to handle EINTR? */ if (r == -1) { SIGNAL_HANDLE(SIGPIPE, old_sig); CLORETS("SOCKSELERR"); } if (!FD_ISSET(s,&wfds)) { close(s); errno = ETIMEDOUT; SIGNAL_HANDLE(SIGPIPE, old_sig); return "TIMEDOUT"; } retval = ident_sockuser(s,local,remote); SIGNAL_HANDLE(SIGPIPE, old_sig); return retval; } char *ident_sockuser(s,local,remote) register int s; register unsigned short local; register unsigned short remote; { register int buflen; register int w; register int saveerrno; char ch; unsigned short rlocal; unsigned short rremote; register int fl; fd_set wfds; void (*old_sig)__((int)); char userid[24]; SIGNAL_HANDLESAVE(SIGPIPE, SIG_IGN, old_sig); FD_ZERO(&wfds); FD_SET(s,&wfds); select(s + 1,(fd_set *) 0,&wfds,(fd_set *) 0,(struct timeval *) 0); /* now s is writable */ #if USENONBLOCK if ((fl = fcntl(s,F_GETFL,0)) == -1) { SIGNAL_HANDLE(SIGPIPE, old_sig); CLORETS("SOCKFCNTL1"); } if (fcntl(s,F_SETFL,~FNDELAY & fl) == -1) { SIGNAL_HANDLE(SIGPIPE, old_sig); CLORETS("SOCKFCNTL2"); } #endif buf = realbuf; sprintf(buf,"%u , %u\r\n",(unsigned int) remote,(unsigned int) local); /* note the reversed order---the example in RFC 931 is misleading */ buflen = strlen(buf); while ((w = write(s,buf,buflen)) < buflen) if (w == -1) /* should we worry about 0 as well? */ { SIGNAL_HANDLE(SIGPIPE, old_sig); saveerrno = errno; close(s); if (errno = ECONNREFUSED) return "NO-IDENT-SERVICE"; else return "SOCKWRITE"; } else { buf += w; buflen -= w; } buf = realbuf; while ((w = read(s,&ch,1)) == 1) { *buf = ch; if ((ch != ' ') && (ch != '\t') && (ch != '\r')) ++buf; if ((buf - realbuf == sizeof(realbuf) - 1) || (ch == '\n')) break; } SIGNAL_HANDLE(SIGPIPE, old_sig); if (w == -1) CLORETS("SOCKREAD"); *buf = 0; if ((sscanf(realbuf,"%hd,%hd:%20s:%*[^:]:%s", &rremote,&rlocal,userid,ruser) < 3) || cistrcmp(userid,"userid") != 0 ) { close(s); errno = EIO; /* makes sense, right? well, not when USERID failed to match ERROR */ /* but there's no good error to return in that case */ return "IDENT-NONSENSE"; } if ((remote != rremote) || (local != rlocal)) { close(s); errno = EIO; return "IDENT-NONSENSE2"; } /* we're not going to do any backslash processing */ close(s); return ruser; } int ident_tcpsock(inlocal,inremote) register struct in_addr *inlocal; register struct in_addr *inremote; { struct sockaddr_in sa; register int s; register int fl; register int saveerrno; if ((s = socket(PF_INET,SOCK_STREAM,0)) == -1) return -1; if (inlocal->s_addr) { memset(&sa,0,sizeof(sa)); sa.sin_family = AF_INET; sa.sin_port = 0; sa.sin_addr = *inlocal; if (bind(s,(struct sockaddr*)&sa,sizeof(sa)) == -1) CLORETS(-1); } if ((fl = fcntl(s,F_GETFL,0)) == -1) CLORETS(-1); if (fcntl(s,F_SETFL,FNDELAY | fl) == -1) CLORETS(-1); memset(&sa,0,sizeof(sa)); sa.sin_family = AF_INET; sa.sin_port = htons(ident_tcpport); sa.sin_addr = *inremote; if (connect(s,(struct sockaddr*)&sa,sizeof(sa)) == -1) if (errno != EINPROGRESS) CLORETS(-1); return s; } #endif