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