xref: /freebsd/contrib/tcp_wrappers/rfc931.c (revision 14f102eacc8434a5a1f96466752578a4167140c9)
1   /*
2    * rfc931() speaks a common subset of the RFC 931, AUTH, TAP, IDENT and RFC
3    * 1413 protocols. It queries an RFC 931 etc. compatible daemon on a remote
4    * host to look up the owner of a connection. The information should not be
5    * used for authentication purposes. This routine intercepts alarm signals.
6    *
7    * Diagnostics are reported through syslog(3).
8    *
9    * Author: Wietse Venema, Eindhoven University of Technology, The Netherlands.
10    *
11    * $FreeBSD$
12    */
13  
14  #ifndef lint
15  static char sccsid[] = "@(#) rfc931.c 1.10 95/01/02 16:11:34";
16  #endif
17  
18  /* System libraries. */
19  
20  #include <stdio.h>
21  #include <syslog.h>
22  #include <sys/types.h>
23  #include <sys/socket.h>
24  #include <netinet/in.h>
25  #include <setjmp.h>
26  #include <signal.h>
27  #include <string.h>
28  #include <unistd.h>
29  
30  #ifndef SEEK_SET
31  #define SEEK_SET 0
32  #endif
33  
34  /* Local stuff. */
35  
36  #include "tcpd.h"
37  
38  #define	RFC931_PORT	113		/* Semi-well-known port */
39  #define	ANY_PORT	0		/* Any old port will do */
40  
41  int     rfc931_timeout = RFC931_TIMEOUT;/* Global so it can be changed */
42  
43  static jmp_buf timebuf;
44  
45  /* fsocket - open stdio stream on top of socket */
46  
fsocket(int domain,int type,int protocol)47  static FILE *fsocket(int domain, int type, int protocol)
48  {
49      int     s;
50      FILE   *fp;
51  
52      if ((s = socket(domain, type, protocol)) < 0) {
53  	tcpd_warn("socket: %m");
54  	return (0);
55      } else {
56  	if ((fp = fdopen(s, "r+")) == 0) {
57  	    tcpd_warn("fdopen: %m");
58  	    close(s);
59  	}
60  	return (fp);
61      }
62  }
63  
64  /* timeout - handle timeouts */
65  
timeout(int sig)66  static void timeout(int sig)
67  {
68      longjmp(timebuf, sig);
69  }
70  
71  /* rfc931 - return remote user name, given socket structures */
72  
73  #ifdef INET6
rfc931(struct sockaddr * rmt_sin,struct sockaddr * our_sin,char * dest)74  void    rfc931(struct sockaddr *rmt_sin, struct sockaddr *our_sin, char *dest)
75  #else
76  void    rfc931(struct sockaddr_in *rmt_sin, struct sockaddr_in *our_sin,
77  	char *dest)
78  #endif
79  {
80      unsigned rmt_port;
81      unsigned our_port;
82  #ifdef INET6
83      struct sockaddr_storage rmt_query_sin;
84      struct sockaddr_storage our_query_sin;
85      int alen;
86  #else
87      struct sockaddr_in rmt_query_sin;
88      struct sockaddr_in our_query_sin;
89  #endif
90      char    user[256];			/* XXX */
91      char    buffer[512];		/* XXX */
92      char   *cp;
93      char   *result = unknown;
94      FILE   *fp;
95  
96  #ifdef INET6
97      /* address family must be the same */
98      if (rmt_sin->sa_family != our_sin->sa_family) {
99  	STRN_CPY(dest, result, STRING_LENGTH);
100  	return;
101      }
102      switch (our_sin->sa_family) {
103      case AF_INET:
104  	alen = sizeof(struct sockaddr_in);
105  	break;
106      case AF_INET6:
107  	alen = sizeof(struct sockaddr_in6);
108  	break;
109      default:
110  	STRN_CPY(dest, result, STRING_LENGTH);
111  	return;
112      }
113  #endif
114  
115      /*
116       * If we use a single, buffered, bidirectional stdio stream ("r+" or
117       * "w+" mode) we may read our own output. Such behaviour would make sense
118       * with resources that support random-access operations, but not with
119       * sockets. ANSI C suggests several functions which can be called when
120       * you want to change IO direction, fseek seems the most portable.
121       */
122  
123  #ifdef INET6
124      if ((fp = fsocket(our_sin->sa_family, SOCK_STREAM, 0)) != 0) {
125  #else
126      if ((fp = fsocket(AF_INET, SOCK_STREAM, 0)) != 0) {
127  #endif
128  	/*
129  	 * Set up a timer so we won't get stuck while waiting for the server.
130  	 */
131  
132  	if (setjmp(timebuf) == 0) {
133  	    signal(SIGALRM, timeout);
134  	    alarm(rfc931_timeout);
135  
136  	    /*
137  	     * Bind the local and remote ends of the query socket to the same
138  	     * IP addresses as the connection under investigation. We go
139  	     * through all this trouble because the local or remote system
140  	     * might have more than one network address. The RFC931 etc.
141  	     * client sends only port numbers; the server takes the IP
142  	     * addresses from the query socket.
143  	     */
144  
145  #ifdef INET6
146  	    memcpy(&our_query_sin, our_sin, alen);
147  	    memcpy(&rmt_query_sin, rmt_sin, alen);
148  	    switch (our_sin->sa_family) {
149  	    case AF_INET:
150  		((struct sockaddr_in *)&our_query_sin)->sin_port = htons(ANY_PORT);
151  		((struct sockaddr_in *)&rmt_query_sin)->sin_port = htons(RFC931_PORT);
152  		break;
153  	    case AF_INET6:
154  		((struct sockaddr_in6 *)&our_query_sin)->sin6_port = htons(ANY_PORT);
155  		((struct sockaddr_in6 *)&rmt_query_sin)->sin6_port = htons(RFC931_PORT);
156  		break;
157  	    }
158  
159  	    if (bind(fileno(fp), (struct sockaddr *) & our_query_sin,
160  		     alen) >= 0 &&
161  		connect(fileno(fp), (struct sockaddr *) & rmt_query_sin,
162  			alen) >= 0) {
163  #else
164  	    our_query_sin = *our_sin;
165  	    our_query_sin.sin_port = htons(ANY_PORT);
166  	    rmt_query_sin = *rmt_sin;
167  	    rmt_query_sin.sin_port = htons(RFC931_PORT);
168  
169  	    if (bind(fileno(fp), (struct sockaddr *) & our_query_sin,
170  		     sizeof(our_query_sin)) >= 0 &&
171  		connect(fileno(fp), (struct sockaddr *) & rmt_query_sin,
172  			sizeof(rmt_query_sin)) >= 0) {
173  #endif
174  
175  		/*
176  		 * Send query to server. Neglect the risk that a 13-byte
177  		 * write would have to be fragmented by the local system and
178  		 * cause trouble with buggy System V stdio libraries.
179  		 */
180  
181  		fprintf(fp, "%u,%u\r\n",
182  #ifdef INET6
183  			ntohs(((struct sockaddr_in *)rmt_sin)->sin_port),
184  			ntohs(((struct sockaddr_in *)our_sin)->sin_port));
185  #else
186  			ntohs(rmt_sin->sin_port),
187  			ntohs(our_sin->sin_port));
188  #endif
189  		fflush(fp);
190  		fseek(fp, 0, SEEK_SET);
191  
192  		/*
193  		 * Read response from server. Use fgets()/sscanf() so we can
194  		 * work around System V stdio libraries that incorrectly
195  		 * assume EOF when a read from a socket returns less than
196  		 * requested.
197  		 */
198  
199  		if (fgets(buffer, sizeof(buffer), fp) != 0
200  		    && ferror(fp) == 0 && feof(fp) == 0
201  		    && sscanf(buffer, "%u , %u : USERID :%*[^:]:%255s",
202  			      &rmt_port, &our_port, user) == 3
203  #ifdef INET6
204  		    && ntohs(((struct sockaddr_in *)rmt_sin)->sin_port) == rmt_port
205  		    && ntohs(((struct sockaddr_in *)our_sin)->sin_port) == our_port) {
206  #else
207  		    && ntohs(rmt_sin->sin_port) == rmt_port
208  		    && ntohs(our_sin->sin_port) == our_port) {
209  #endif
210  
211  		    /*
212  		     * Strip trailing carriage return. It is part of the
213  		     * protocol, not part of the data.
214  		     */
215  
216  		    if (cp = strchr(user, '\r'))
217  			*cp = 0;
218  		    result = user;
219  		}
220  	    }
221  	    alarm(0);
222  	}
223  	fclose(fp);
224      }
225      STRN_CPY(dest, result, STRING_LENGTH);
226  }
227