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: stable/10/contrib/tcp_wrappers/rfc931.c 311814 2017-01-09 20:14:02Z dim $
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>
28311814Sdim#include <unistd.h>
2944743Smarkm
3063152Sdwmalone#ifndef SEEK_SET
3163152Sdwmalone#define SEEK_SET 0
3263152Sdwmalone#endif
3363152Sdwmalone
3444743Smarkm/* Local stuff. */
3544743Smarkm
3644743Smarkm#include "tcpd.h"
3744743Smarkm
3844743Smarkm#define	RFC931_PORT	113		/* Semi-well-known port */
3944743Smarkm#define	ANY_PORT	0		/* Any old port will do */
4044743Smarkm
4144743Smarkmint     rfc931_timeout = RFC931_TIMEOUT;/* Global so it can be changed */
4244743Smarkm
4344743Smarkmstatic jmp_buf timebuf;
4444743Smarkm
4544743Smarkm/* fsocket - open stdio stream on top of socket */
4644743Smarkm
4744743Smarkmstatic FILE *fsocket(domain, type, protocol)
4844743Smarkmint     domain;
4944743Smarkmint     type;
5044743Smarkmint     protocol;
5144743Smarkm{
5244743Smarkm    int     s;
5344743Smarkm    FILE   *fp;
5444743Smarkm
5544743Smarkm    if ((s = socket(domain, type, protocol)) < 0) {
5644743Smarkm	tcpd_warn("socket: %m");
5744743Smarkm	return (0);
5844743Smarkm    } else {
5944743Smarkm	if ((fp = fdopen(s, "r+")) == 0) {
6044743Smarkm	    tcpd_warn("fdopen: %m");
6144743Smarkm	    close(s);
6244743Smarkm	}
6344743Smarkm	return (fp);
6444743Smarkm    }
6544743Smarkm}
6644743Smarkm
6744743Smarkm/* timeout - handle timeouts */
6844743Smarkm
6944743Smarkmstatic void timeout(sig)
7044743Smarkmint     sig;
7144743Smarkm{
7244743Smarkm    longjmp(timebuf, sig);
7344743Smarkm}
7444743Smarkm
7544743Smarkm/* rfc931 - return remote user name, given socket structures */
7644743Smarkm
7744743Smarkmvoid    rfc931(rmt_sin, our_sin, dest)
7856977Sshin#ifdef INET6
7956977Sshinstruct sockaddr *rmt_sin;
8056977Sshinstruct sockaddr *our_sin;
8156977Sshin#else
8244743Smarkmstruct sockaddr_in *rmt_sin;
8344743Smarkmstruct sockaddr_in *our_sin;
8456977Sshin#endif
8544743Smarkmchar   *dest;
8644743Smarkm{
8744743Smarkm    unsigned rmt_port;
8844743Smarkm    unsigned our_port;
8956977Sshin#ifdef INET6
9056977Sshin    struct sockaddr_storage rmt_query_sin;
9156977Sshin    struct sockaddr_storage our_query_sin;
9256977Sshin    int alen;
9356977Sshin#else
9444743Smarkm    struct sockaddr_in rmt_query_sin;
9544743Smarkm    struct sockaddr_in our_query_sin;
9656977Sshin#endif
9744743Smarkm    char    user[256];			/* XXX */
9844743Smarkm    char    buffer[512];		/* XXX */
9944743Smarkm    char   *cp;
10044743Smarkm    char   *result = unknown;
10144743Smarkm    FILE   *fp;
10244743Smarkm
10356977Sshin#ifdef INET6
10456977Sshin    /* address family must be the same */
10556977Sshin    if (rmt_sin->sa_family != our_sin->sa_family) {
10656977Sshin	STRN_CPY(dest, result, STRING_LENGTH);
10756977Sshin	return;
10856977Sshin    }
10956977Sshin    switch (our_sin->sa_family) {
11056977Sshin    case AF_INET:
11156977Sshin	alen = sizeof(struct sockaddr_in);
11256977Sshin	break;
11356977Sshin    case AF_INET6:
11456977Sshin	alen = sizeof(struct sockaddr_in6);
11556977Sshin	break;
11656977Sshin    default:
11756977Sshin	STRN_CPY(dest, result, STRING_LENGTH);
11856977Sshin	return;
11956977Sshin    }
12056977Sshin#endif
12156977Sshin
12244743Smarkm    /*
12363152Sdwmalone     * If we use a single, buffered, bidirectional stdio stream ("r+" or
12463152Sdwmalone     * "w+" mode) we may read our own output. Such behaviour would make sense
12544743Smarkm     * with resources that support random-access operations, but not with
12663152Sdwmalone     * sockets. ANSI C suggests several functions which can be called when
12763152Sdwmalone     * you want to change IO direction, fseek seems the most portable.
12844743Smarkm     */
12944743Smarkm
13056977Sshin#ifdef INET6
13156977Sshin    if ((fp = fsocket(our_sin->sa_family, SOCK_STREAM, 0)) != 0) {
13256977Sshin#else
13344743Smarkm    if ((fp = fsocket(AF_INET, SOCK_STREAM, 0)) != 0) {
13456977Sshin#endif
13544743Smarkm	/*
13644743Smarkm	 * Set up a timer so we won't get stuck while waiting for the server.
13744743Smarkm	 */
13844743Smarkm
13944743Smarkm	if (setjmp(timebuf) == 0) {
14044743Smarkm	    signal(SIGALRM, timeout);
14144743Smarkm	    alarm(rfc931_timeout);
14244743Smarkm
14344743Smarkm	    /*
14444743Smarkm	     * Bind the local and remote ends of the query socket to the same
14544743Smarkm	     * IP addresses as the connection under investigation. We go
14644743Smarkm	     * through all this trouble because the local or remote system
14744743Smarkm	     * might have more than one network address. The RFC931 etc.
14844743Smarkm	     * client sends only port numbers; the server takes the IP
14944743Smarkm	     * addresses from the query socket.
15044743Smarkm	     */
15144743Smarkm
15256977Sshin#ifdef INET6
15356977Sshin	    memcpy(&our_query_sin, our_sin, alen);
15456977Sshin	    memcpy(&rmt_query_sin, rmt_sin, alen);
15556977Sshin	    switch (our_sin->sa_family) {
15656977Sshin	    case AF_INET:
15756977Sshin		((struct sockaddr_in *)&our_query_sin)->sin_port = htons(ANY_PORT);
15856977Sshin		((struct sockaddr_in *)&rmt_query_sin)->sin_port = htons(RFC931_PORT);
15956977Sshin		break;
16056977Sshin	    case AF_INET6:
16156977Sshin		((struct sockaddr_in6 *)&our_query_sin)->sin6_port = htons(ANY_PORT);
16256977Sshin		((struct sockaddr_in6 *)&rmt_query_sin)->sin6_port = htons(RFC931_PORT);
16356977Sshin		break;
16456977Sshin	    }
16556977Sshin
16656977Sshin	    if (bind(fileno(fp), (struct sockaddr *) & our_query_sin,
16756977Sshin		     alen) >= 0 &&
16856977Sshin		connect(fileno(fp), (struct sockaddr *) & rmt_query_sin,
16956977Sshin			alen) >= 0) {
17056977Sshin#else
17144743Smarkm	    our_query_sin = *our_sin;
17244743Smarkm	    our_query_sin.sin_port = htons(ANY_PORT);
17344743Smarkm	    rmt_query_sin = *rmt_sin;
17444743Smarkm	    rmt_query_sin.sin_port = htons(RFC931_PORT);
17544743Smarkm
17644743Smarkm	    if (bind(fileno(fp), (struct sockaddr *) & our_query_sin,
17744743Smarkm		     sizeof(our_query_sin)) >= 0 &&
17844743Smarkm		connect(fileno(fp), (struct sockaddr *) & rmt_query_sin,
17944743Smarkm			sizeof(rmt_query_sin)) >= 0) {
18056977Sshin#endif
18144743Smarkm
18244743Smarkm		/*
18344743Smarkm		 * Send query to server. Neglect the risk that a 13-byte
18444743Smarkm		 * write would have to be fragmented by the local system and
18544743Smarkm		 * cause trouble with buggy System V stdio libraries.
18644743Smarkm		 */
18744743Smarkm
18844743Smarkm		fprintf(fp, "%u,%u\r\n",
18956977Sshin#ifdef INET6
19056977Sshin			ntohs(((struct sockaddr_in *)rmt_sin)->sin_port),
19156977Sshin			ntohs(((struct sockaddr_in *)our_sin)->sin_port));
19256977Sshin#else
19344743Smarkm			ntohs(rmt_sin->sin_port),
19444743Smarkm			ntohs(our_sin->sin_port));
19556977Sshin#endif
19644743Smarkm		fflush(fp);
19763152Sdwmalone		fseek(fp, 0, SEEK_SET);
19844743Smarkm
19944743Smarkm		/*
20044743Smarkm		 * Read response from server. Use fgets()/sscanf() so we can
20144743Smarkm		 * work around System V stdio libraries that incorrectly
20244743Smarkm		 * assume EOF when a read from a socket returns less than
20344743Smarkm		 * requested.
20444743Smarkm		 */
20544743Smarkm
20644743Smarkm		if (fgets(buffer, sizeof(buffer), fp) != 0
20744743Smarkm		    && ferror(fp) == 0 && feof(fp) == 0
20844743Smarkm		    && sscanf(buffer, "%u , %u : USERID :%*[^:]:%255s",
20944743Smarkm			      &rmt_port, &our_port, user) == 3
21056977Sshin#ifdef INET6
21156977Sshin		    && ntohs(((struct sockaddr_in *)rmt_sin)->sin_port) == rmt_port
21256977Sshin		    && ntohs(((struct sockaddr_in *)our_sin)->sin_port) == our_port) {
21356977Sshin#else
21444743Smarkm		    && ntohs(rmt_sin->sin_port) == rmt_port
21544743Smarkm		    && ntohs(our_sin->sin_port) == our_port) {
21656977Sshin#endif
21744743Smarkm
21844743Smarkm		    /*
21944743Smarkm		     * Strip trailing carriage return. It is part of the
22044743Smarkm		     * protocol, not part of the data.
22144743Smarkm		     */
22244743Smarkm
22344743Smarkm		    if (cp = strchr(user, '\r'))
22444743Smarkm			*cp = 0;
22544743Smarkm		    result = user;
22644743Smarkm		}
22744743Smarkm	    }
22844743Smarkm	    alarm(0);
22944743Smarkm	}
23044743Smarkm	fclose(fp);
23144743Smarkm    }
23244743Smarkm    STRN_CPY(dest, result, STRING_LENGTH);
23344743Smarkm}
234