144743Smarkm /* 244743Smarkm * rfc931() speaks a common subset of the RFC 931, AUTH, TAP, IDENT and RFC 344743Smarkm * 1413 protocols. It queries an RFC 931 etc. compatible daemon on a remote 444743Smarkm * host to look up the owner of a connection. The information should not be 544743Smarkm * used for authentication purposes. This routine intercepts alarm signals. 644743Smarkm * 744743Smarkm * Diagnostics are reported through syslog(3). 844743Smarkm * 944743Smarkm * Author: Wietse Venema, Eindhoven University of Technology, The Netherlands. 1056977Sshin * 1156977Sshin * $FreeBSD$ 1244743Smarkm */ 1344743Smarkm 1444743Smarkm#ifndef lint 1544743Smarkmstatic char sccsid[] = "@(#) rfc931.c 1.10 95/01/02 16:11:34"; 1644743Smarkm#endif 1744743Smarkm 1844743Smarkm/* System libraries. */ 1944743Smarkm 2044743Smarkm#include <stdio.h> 2144743Smarkm#include <syslog.h> 2244743Smarkm#include <sys/types.h> 2344743Smarkm#include <sys/socket.h> 2444743Smarkm#include <netinet/in.h> 2544743Smarkm#include <setjmp.h> 2644743Smarkm#include <signal.h> 2744743Smarkm#include <string.h> 2844743Smarkm 2963152Sdwmalone#ifndef SEEK_SET 3063152Sdwmalone#define SEEK_SET 0 3163152Sdwmalone#endif 3263152Sdwmalone 3344743Smarkm/* Local stuff. */ 3444743Smarkm 3544743Smarkm#include "tcpd.h" 3644743Smarkm 3744743Smarkm#define RFC931_PORT 113 /* Semi-well-known port */ 3844743Smarkm#define ANY_PORT 0 /* Any old port will do */ 3944743Smarkm 4044743Smarkmint rfc931_timeout = RFC931_TIMEOUT;/* Global so it can be changed */ 4144743Smarkm 4244743Smarkmstatic jmp_buf timebuf; 4344743Smarkm 4444743Smarkm/* fsocket - open stdio stream on top of socket */ 4544743Smarkm 4644743Smarkmstatic FILE *fsocket(domain, type, protocol) 4744743Smarkmint domain; 4844743Smarkmint type; 4944743Smarkmint protocol; 5044743Smarkm{ 5144743Smarkm int s; 5244743Smarkm FILE *fp; 5344743Smarkm 5444743Smarkm if ((s = socket(domain, type, protocol)) < 0) { 5544743Smarkm tcpd_warn("socket: %m"); 5644743Smarkm return (0); 5744743Smarkm } else { 5844743Smarkm if ((fp = fdopen(s, "r+")) == 0) { 5944743Smarkm tcpd_warn("fdopen: %m"); 6044743Smarkm close(s); 6144743Smarkm } 6244743Smarkm return (fp); 6344743Smarkm } 6444743Smarkm} 6544743Smarkm 6644743Smarkm/* timeout - handle timeouts */ 6744743Smarkm 6844743Smarkmstatic void timeout(sig) 6944743Smarkmint sig; 7044743Smarkm{ 7144743Smarkm longjmp(timebuf, sig); 7244743Smarkm} 7344743Smarkm 7444743Smarkm/* rfc931 - return remote user name, given socket structures */ 7544743Smarkm 7644743Smarkmvoid rfc931(rmt_sin, our_sin, dest) 7756977Sshin#ifdef INET6 7856977Sshinstruct sockaddr *rmt_sin; 7956977Sshinstruct sockaddr *our_sin; 8056977Sshin#else 8144743Smarkmstruct sockaddr_in *rmt_sin; 8244743Smarkmstruct sockaddr_in *our_sin; 8356977Sshin#endif 8444743Smarkmchar *dest; 8544743Smarkm{ 8644743Smarkm unsigned rmt_port; 8744743Smarkm unsigned our_port; 8856977Sshin#ifdef INET6 8956977Sshin struct sockaddr_storage rmt_query_sin; 9056977Sshin struct sockaddr_storage our_query_sin; 9156977Sshin int alen; 9256977Sshin#else 9344743Smarkm struct sockaddr_in rmt_query_sin; 9444743Smarkm struct sockaddr_in our_query_sin; 9556977Sshin#endif 9644743Smarkm char user[256]; /* XXX */ 9744743Smarkm char buffer[512]; /* XXX */ 9844743Smarkm char *cp; 9944743Smarkm char *result = unknown; 10044743Smarkm FILE *fp; 10144743Smarkm 10256977Sshin#ifdef INET6 10356977Sshin /* address family must be the same */ 10456977Sshin if (rmt_sin->sa_family != our_sin->sa_family) { 10556977Sshin STRN_CPY(dest, result, STRING_LENGTH); 10656977Sshin return; 10756977Sshin } 10856977Sshin switch (our_sin->sa_family) { 10956977Sshin case AF_INET: 11056977Sshin alen = sizeof(struct sockaddr_in); 11156977Sshin break; 11256977Sshin case AF_INET6: 11356977Sshin alen = sizeof(struct sockaddr_in6); 11456977Sshin break; 11556977Sshin default: 11656977Sshin STRN_CPY(dest, result, STRING_LENGTH); 11756977Sshin return; 11856977Sshin } 11956977Sshin#endif 12056977Sshin 12144743Smarkm /* 12263152Sdwmalone * If we use a single, buffered, bidirectional stdio stream ("r+" or 12363152Sdwmalone * "w+" mode) we may read our own output. Such behaviour would make sense 12444743Smarkm * with resources that support random-access operations, but not with 12563152Sdwmalone * sockets. ANSI C suggests several functions which can be called when 12663152Sdwmalone * you want to change IO direction, fseek seems the most portable. 12744743Smarkm */ 12844743Smarkm 12956977Sshin#ifdef INET6 13056977Sshin if ((fp = fsocket(our_sin->sa_family, SOCK_STREAM, 0)) != 0) { 13156977Sshin#else 13244743Smarkm if ((fp = fsocket(AF_INET, SOCK_STREAM, 0)) != 0) { 13356977Sshin#endif 13444743Smarkm /* 13544743Smarkm * Set up a timer so we won't get stuck while waiting for the server. 13644743Smarkm */ 13744743Smarkm 13844743Smarkm if (setjmp(timebuf) == 0) { 13944743Smarkm signal(SIGALRM, timeout); 14044743Smarkm alarm(rfc931_timeout); 14144743Smarkm 14244743Smarkm /* 14344743Smarkm * Bind the local and remote ends of the query socket to the same 14444743Smarkm * IP addresses as the connection under investigation. We go 14544743Smarkm * through all this trouble because the local or remote system 14644743Smarkm * might have more than one network address. The RFC931 etc. 14744743Smarkm * client sends only port numbers; the server takes the IP 14844743Smarkm * addresses from the query socket. 14944743Smarkm */ 15044743Smarkm 15156977Sshin#ifdef INET6 15256977Sshin memcpy(&our_query_sin, our_sin, alen); 15356977Sshin memcpy(&rmt_query_sin, rmt_sin, alen); 15456977Sshin switch (our_sin->sa_family) { 15556977Sshin case AF_INET: 15656977Sshin ((struct sockaddr_in *)&our_query_sin)->sin_port = htons(ANY_PORT); 15756977Sshin ((struct sockaddr_in *)&rmt_query_sin)->sin_port = htons(RFC931_PORT); 15856977Sshin break; 15956977Sshin case AF_INET6: 16056977Sshin ((struct sockaddr_in6 *)&our_query_sin)->sin6_port = htons(ANY_PORT); 16156977Sshin ((struct sockaddr_in6 *)&rmt_query_sin)->sin6_port = htons(RFC931_PORT); 16256977Sshin break; 16356977Sshin } 16456977Sshin 16556977Sshin if (bind(fileno(fp), (struct sockaddr *) & our_query_sin, 16656977Sshin alen) >= 0 && 16756977Sshin connect(fileno(fp), (struct sockaddr *) & rmt_query_sin, 16856977Sshin alen) >= 0) { 16956977Sshin#else 17044743Smarkm our_query_sin = *our_sin; 17144743Smarkm our_query_sin.sin_port = htons(ANY_PORT); 17244743Smarkm rmt_query_sin = *rmt_sin; 17344743Smarkm rmt_query_sin.sin_port = htons(RFC931_PORT); 17444743Smarkm 17544743Smarkm if (bind(fileno(fp), (struct sockaddr *) & our_query_sin, 17644743Smarkm sizeof(our_query_sin)) >= 0 && 17744743Smarkm connect(fileno(fp), (struct sockaddr *) & rmt_query_sin, 17844743Smarkm sizeof(rmt_query_sin)) >= 0) { 17956977Sshin#endif 18044743Smarkm 18144743Smarkm /* 18244743Smarkm * Send query to server. Neglect the risk that a 13-byte 18344743Smarkm * write would have to be fragmented by the local system and 18444743Smarkm * cause trouble with buggy System V stdio libraries. 18544743Smarkm */ 18644743Smarkm 18744743Smarkm fprintf(fp, "%u,%u\r\n", 18856977Sshin#ifdef INET6 18956977Sshin ntohs(((struct sockaddr_in *)rmt_sin)->sin_port), 19056977Sshin ntohs(((struct sockaddr_in *)our_sin)->sin_port)); 19156977Sshin#else 19244743Smarkm ntohs(rmt_sin->sin_port), 19344743Smarkm ntohs(our_sin->sin_port)); 19456977Sshin#endif 19544743Smarkm fflush(fp); 19663152Sdwmalone fseek(fp, 0, SEEK_SET); 19744743Smarkm 19844743Smarkm /* 19944743Smarkm * Read response from server. Use fgets()/sscanf() so we can 20044743Smarkm * work around System V stdio libraries that incorrectly 20144743Smarkm * assume EOF when a read from a socket returns less than 20244743Smarkm * requested. 20344743Smarkm */ 20444743Smarkm 20544743Smarkm if (fgets(buffer, sizeof(buffer), fp) != 0 20644743Smarkm && ferror(fp) == 0 && feof(fp) == 0 20744743Smarkm && sscanf(buffer, "%u , %u : USERID :%*[^:]:%255s", 20844743Smarkm &rmt_port, &our_port, user) == 3 20956977Sshin#ifdef INET6 21056977Sshin && ntohs(((struct sockaddr_in *)rmt_sin)->sin_port) == rmt_port 21156977Sshin && ntohs(((struct sockaddr_in *)our_sin)->sin_port) == our_port) { 21256977Sshin#else 21344743Smarkm && ntohs(rmt_sin->sin_port) == rmt_port 21444743Smarkm && ntohs(our_sin->sin_port) == our_port) { 21556977Sshin#endif 21644743Smarkm 21744743Smarkm /* 21844743Smarkm * Strip trailing carriage return. It is part of the 21944743Smarkm * protocol, not part of the data. 22044743Smarkm */ 22144743Smarkm 22244743Smarkm if (cp = strchr(user, '\r')) 22344743Smarkm *cp = 0; 22444743Smarkm result = user; 22544743Smarkm } 22644743Smarkm } 22744743Smarkm alarm(0); 22844743Smarkm } 22944743Smarkm fclose(fp); 23044743Smarkm } 23144743Smarkm STRN_CPY(dest, result, STRING_LENGTH); 23244743Smarkm} 233