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