xref: /illumos-gate/usr/src/lib/libwrap/rfc931.c (revision 55fea89dcaa64928bed4327112404dcb3e07b79f)
1 /*
2  * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
3  * Use is subject to license terms.
4  */
5 
6  /*
7   * rfc931() speaks a common subset of the RFC 931, AUTH, TAP, IDENT and RFC
8   * 1413 protocols. It queries an RFC 931 etc. compatible daemon on a remote
9   * host to look up the owner of a connection. The information should not be
10   * used for authentication purposes. This routine intercepts alarm signals.
11   *
12   * Diagnostics are reported through syslog(3).
13   *
14   * Author: Wietse Venema, Eindhoven University of Technology, The Netherlands.
15   */
16 
17 /* System libraries. */
18 
19 #include <stdio.h>
20 #include <stdlib.h>
21 #include <unistd.h>
22 #include <syslog.h>
23 #include <sys/types.h>
24 #include <sys/socket.h>
25 #include <netinet/in.h>
26 #include <setjmp.h>
27 #include <signal.h>
28 #include <string.h>
29 
30 /* Local stuff. */
31 
32 #include "tcpd.h"
33 
34 #define	RFC931_PORT	113		/* Semi-well-known port */
35 #define	ANY_PORT	0		/* Any old port will do */
36 
37 int     rfc931_timeout = RFC931_TIMEOUT;/* Global so it can be changed */
38 
39 static jmp_buf timebuf;
40 
41 /* fsocket - open stdio stream on top of socket */
42 
fsocket(domain,type,protocol)43 static FILE *fsocket(domain, type, protocol)
44 int     domain;
45 int     type;
46 int     protocol;
47 {
48     int     s;
49     FILE   *fp;
50 
51     if ((s = socket(domain, type, protocol)) < 0) {
52 	tcpd_warn("socket: %m");
53 	return (0);
54     } else {
55 	if ((fp = fdopen(s, "r+")) == 0) {
56 	    tcpd_warn("fdopen: %m");
57 	    close(s);
58 	}
59 	return (fp);
60     }
61 }
62 
63 /* timeout - handle timeouts */
64 
timeout(sig)65 static void timeout(sig)
66 int     sig;
67 {
68     longjmp(timebuf, sig);
69 }
70 
71 /* rfc931 - return remote user name, given socket structures */
72 
rfc931(rmt_sin,our_sin,dest)73 void    rfc931(rmt_sin, our_sin, dest)
74 struct sockaddr_gen *rmt_sin;
75 struct sockaddr_gen *our_sin;
76 char   *dest;
77 {
78     unsigned rmt_port;
79     unsigned our_port;
80     struct sockaddr_gen rmt_query_sin;
81     struct sockaddr_gen our_query_sin;
82     char    user[256];			/* XXX */
83     char    buffer[512];		/* XXX */
84     char   *cp;
85     char   *volatile result = unknown;
86     FILE   *fp;
87     volatile unsigned saved_timeout = 0;
88     struct sigaction nact, oact;
89 
90     /*
91      * Use one unbuffered stdio stream for writing to and for reading from
92      * the RFC931 etc. server. This is done because of a bug in the SunOS
93      * 4.1.x stdio library. The bug may live in other stdio implementations,
94      * too. When we use a single, buffered, bidirectional stdio stream ("r+"
95      * or "w+" mode) we read our own output. Such behaviour would make sense
96      * with resources that support random-access operations, but not with
97      * sockets.
98      */
99 
100     if ((fp = fsocket(SGFAM(rmt_sin), SOCK_STREAM, 0)) != 0) {
101 	setbuf(fp, NULL);
102 
103 	/*
104 	 * Set up a timer so we won't get stuck while waiting for the server.
105 	 */
106 
107 	if (setjmp(timebuf) == 0) {
108 	    /*
109 	     * save the pending time in case the caller has armed an alarm.
110 	     */
111 
112 	    saved_timeout = alarm(0);
113 
114 	    /*
115 	     * It's guaranteed to enter this 'if' condition on the direct
116 	     * invocation of setjmp and hence no additional checks while
117 	     * restoring the signal handler.
118 	     * Now, get the old handler and set the new one
119 	     */
120 	    nact.sa_handler = timeout;
121 	    nact.sa_flags = 0;
122 	    (void) sigemptyset(&nact.sa_mask);
123 	    (void) sigaction(SIGALRM, &nact, &oact);
124 	    alarm(rfc931_timeout);
125 
126 	    /*
127 	     * Bind the local and remote ends of the query socket to the same
128 	     * IP addresses as the connection under investigation. We go
129 	     * through all this trouble because the local or remote system
130 	     * might have more than one network address. The RFC931 etc.
131 	     * client sends only port numbers; the server takes the IP
132 	     * addresses from the query socket.
133 	     */
134 
135 	    our_query_sin = *our_sin;
136 	    SGPORT(&our_query_sin) = htons(ANY_PORT);
137 	    rmt_query_sin = *rmt_sin;
138 	    SGPORT(&rmt_query_sin) = htons(RFC931_PORT);
139 
140 	    if (bind(fileno(fp), (struct sockaddr *) &our_query_sin,
141 		     SGSOCKADDRSZ(&our_query_sin)) >= 0 &&
142 		connect(fileno(fp), (struct sockaddr *) &rmt_query_sin,
143 			SGSOCKADDRSZ(&rmt_query_sin)) >= 0) {
144 
145 		/*
146 		 * Send query to server. Neglect the risk that a 13-byte
147 		 * write would have to be fragmented by the local system and
148 		 * cause trouble with buggy System V stdio libraries.
149 		 */
150 
151 		fprintf(fp, "%u,%u\r\n",
152 			ntohs(SGPORT(rmt_sin)),
153 			ntohs(SGPORT(our_sin)));
154 		fflush(fp);
155 
156 		/*
157 		 * Read response from server. Use fgets()/sscanf() so we can
158 		 * work around System V stdio libraries that incorrectly
159 		 * assume EOF when a read from a socket returns less than
160 		 * requested.
161 		 */
162 
163 		if (fgets(buffer, sizeof(buffer), fp) != 0
164 		    && ferror(fp) == 0 && feof(fp) == 0
165 		    && sscanf(buffer, "%u , %u : USERID :%*[^:]:%255s",
166 			      &rmt_port, &our_port, user) == 3
167 		    && ntohs(SGPORT(rmt_sin)) == rmt_port
168 		    && ntohs(SGPORT(our_sin)) == our_port) {
169 
170 		    /*
171 		     * Strip trailing carriage return. It is part of the
172 		     * protocol, not part of the data.
173 		     */
174 
175 		    if (cp = strchr(user, '\r'))
176 			*cp = 0;
177 		    result = user;
178 		}
179 	    }
180 	    alarm(0);
181 	}
182 	/* Restore the old handler */
183 	(void) sigaction(SIGALRM, &oact, NULL);
184 	if (saved_timeout > 0)
185 		alarm(saved_timeout);
186 	fclose(fp);
187     }
188     STRN_CPY(dest, result, STRING_LENGTH);
189 }
190