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
12#ifndef lint
13static char sccsid[] = "@(#) rfc931.c 1.10 95/01/02 16:11:34";
14#endif
15
16/* System libraries. */
17
18#include <stdio.h>
19#include <syslog.h>
20#include <sys/types.h>
21#include <sys/socket.h>
22#include <netinet/in.h>
23#include <setjmp.h>
24#include <signal.h>
25#include <string.h>
26
27/* Local stuff. */
28
29#include "tcpd.h"
30
31#define	RFC931_PORT	113		/* Semi-well-known port */
32#define	ANY_PORT	0		/* Any old port will do */
33
34int     rfc931_timeout = RFC931_TIMEOUT;/* Global so it can be changed */
35
36static jmp_buf timebuf;
37
38/* fsocket - open stdio stream on top of socket */
39
40static FILE *fsocket(domain, type, protocol)
41int     domain;
42int     type;
43int     protocol;
44{
45    int     s;
46    FILE   *fp;
47
48    if ((s = socket(domain, type, protocol)) < 0) {
49	tcpd_warn("socket: %m");
50	return (0);
51    } else {
52	if ((fp = fdopen(s, "r+")) == 0) {
53	    tcpd_warn("fdopen: %m");
54	    close(s);
55	}
56	return (fp);
57    }
58}
59
60/* timeout - handle timeouts */
61
62static void timeout(sig)
63int     sig;
64{
65    longjmp(timebuf, sig);
66}
67
68/* rfc931 - return remote user name, given socket structures */
69
70void    rfc931(rmt_sin, our_sin, dest)
71struct sockaddr_in *rmt_sin;
72struct sockaddr_in *our_sin;
73char   *dest;
74{
75    unsigned rmt_port;
76    unsigned our_port;
77    struct sockaddr_in rmt_query_sin;
78    struct sockaddr_in our_query_sin;
79    char    user[256];			/* XXX */
80    char    buffer[512];		/* XXX */
81    char   *cp;
82    char   *result = unknown;
83    FILE   *fp;
84
85    /*
86     * Use one unbuffered stdio stream for writing to and for reading from
87     * the RFC931 etc. server. This is done because of a bug in the SunOS
88     * 4.1.x stdio library. The bug may live in other stdio implementations,
89     * too. When we use a single, buffered, bidirectional stdio stream ("r+"
90     * or "w+" mode) we read our own output. Such behaviour would make sense
91     * with resources that support random-access operations, but not with
92     * sockets.
93     */
94
95    if ((fp = fsocket(AF_INET, SOCK_STREAM, 0)) != 0) {
96	setbuf(fp, (char *) 0);
97
98	/*
99	 * Set up a timer so we won't get stuck while waiting for the server.
100	 */
101
102	if (setjmp(timebuf) == 0) {
103	    signal(SIGALRM, timeout);
104	    alarm(rfc931_timeout);
105
106	    /*
107	     * Bind the local and remote ends of the query socket to the same
108	     * IP addresses as the connection under investigation. We go
109	     * through all this trouble because the local or remote system
110	     * might have more than one network address. The RFC931 etc.
111	     * client sends only port numbers; the server takes the IP
112	     * addresses from the query socket.
113	     */
114
115	    our_query_sin = *our_sin;
116	    our_query_sin.sin_port = htons(ANY_PORT);
117	    rmt_query_sin = *rmt_sin;
118	    rmt_query_sin.sin_port = htons(RFC931_PORT);
119
120	    if (bind(fileno(fp), (struct sockaddr *) & our_query_sin,
121		     sizeof(our_query_sin)) >= 0 &&
122		connect(fileno(fp), (struct sockaddr *) & rmt_query_sin,
123			sizeof(rmt_query_sin)) >= 0) {
124
125		/*
126		 * Send query to server. Neglect the risk that a 13-byte
127		 * write would have to be fragmented by the local system and
128		 * cause trouble with buggy System V stdio libraries.
129		 */
130
131		fprintf(fp, "%u,%u\r\n",
132			ntohs(rmt_sin->sin_port),
133			ntohs(our_sin->sin_port));
134		fflush(fp);
135
136		/*
137		 * Read response from server. Use fgets()/sscanf() so we can
138		 * work around System V stdio libraries that incorrectly
139		 * assume EOF when a read from a socket returns less than
140		 * requested.
141		 */
142
143		if (fgets(buffer, sizeof(buffer), fp) != 0
144		    && ferror(fp) == 0 && feof(fp) == 0
145		    && sscanf(buffer, "%u , %u : USERID :%*[^:]:%255s",
146			      &rmt_port, &our_port, user) == 3
147		    && ntohs(rmt_sin->sin_port) == rmt_port
148		    && ntohs(our_sin->sin_port) == our_port) {
149
150		    /*
151		     * Strip trailing carriage return. It is part of the
152		     * protocol, not part of the data.
153		     */
154
155		    if (cp = strchr(user, '\r'))
156			*cp = 0;
157		    result = user;
158		}
159	    }
160	    alarm(0);
161	}
162	fclose(fp);
163    }
164    STRN_CPY(dest, result, STRING_LENGTH);
165}
166