1 /* Copyright (c) 2007-2009, UNINETT AS
2  * Copyright (c) 2012, NORDUnet A/S */
3 /* See LICENSE for licensing information. */
4 
5 #include <signal.h>
6 #include <sys/socket.h>
7 #include <netinet/in.h>
8 #include <netdb.h>
9 #include <string.h>
10 #include <unistd.h>
11 #include <limits.h>
12 #ifdef SYS_SOLARIS9
13 #include <fcntl.h>
14 #endif
15 #include <sys/time.h>
16 #include <sys/types.h>
17 #include <poll.h>
18 #include <ctype.h>
19 #include <sys/wait.h>
20 #include <arpa/inet.h>
21 #include <regex.h>
22 #include <pthread.h>
23 #include "radsecproxy.h"
24 #include "hostport.h"
25 
26 #ifdef RADPROT_TCP
27 #include "debug.h"
28 #include "util.h"
29 static void setprotoopts(struct commonprotoopts *opts);
30 static char **getlistenerargs();
31 void *tcplistener(void *arg);
32 int tcpconnect(struct server *server, int timeout, char * text);
33 void *tcpclientrd(void *arg);
34 int clientradputtcp(struct server *server, unsigned char *rad);
35 void tcpsetsrcres();
36 
37 static const struct protodefs protodefs = {
38     "tcp",
39     NULL, /* secretdefault */
40     SOCK_STREAM, /* socktype */
41     "1812", /* portdefault */
42     0, /* retrycountdefault */
43     0, /* retrycountmax */
44     REQUEST_RETRY_INTERVAL * REQUEST_RETRY_COUNT, /* retryintervaldefault */
45     60, /* retryintervalmax */
46     DUPLICATE_INTERVAL, /* duplicateintervaldefault */
47     setprotoopts, /* setprotoopts */
48     getlistenerargs, /* getlistenerargs */
49     tcplistener, /* listener */
50     tcpconnect, /* connecter */
51     tcpclientrd, /* clientconnreader */
52     clientradputtcp, /* clientradput */
53     NULL, /* addclient */
54     NULL, /* addserverextra */
55     tcpsetsrcres, /* setsrcres */
56     NULL /* initextra */
57 };
58 
59 static struct addrinfo *srcres = NULL;
60 static uint8_t handle;
61 static struct commonprotoopts *protoopts = NULL;
tcpinit(uint8_t h)62 const struct protodefs *tcpinit(uint8_t h) {
63     handle = h;
64     return &protodefs;
65 }
66 
setprotoopts(struct commonprotoopts * opts)67 static void setprotoopts(struct commonprotoopts *opts) {
68     protoopts = opts;
69 }
70 
getlistenerargs()71 static char **getlistenerargs() {
72     return protoopts ? protoopts->listenargs : NULL;
73 }
74 
tcpsetsrcres()75 void tcpsetsrcres() {
76     if (!srcres)
77 	srcres =
78             resolvepassiveaddrinfo(protoopts ? protoopts->sourcearg : NULL,
79                                    AF_UNSPEC, NULL, protodefs.socktype);
80 }
81 
tcpconnect(struct server * server,int timeout,char * text)82 int tcpconnect(struct server *server, int timeout, char *text) {
83     struct timeval now, start;
84     int firsttry = 1;
85     time_t wait;
86 
87     debug(DBG_DBG, "tcpconnect: called from %s", text);
88     pthread_mutex_lock(&server->lock);
89 
90     if (server->state == RSP_SERVER_STATE_CONNECTED)
91         server->state = RSP_SERVER_STATE_RECONNECTING;
92 
93     gettimeofday(&start, NULL);
94 
95     for (;;) {
96         if (server->sock >= 0)
97             close(server->sock);
98         server->sock = -1;
99 
100         pthread_mutex_unlock(&server->lock);
101         wait = connect_wait(start, server->connecttime, firsttry);
102         debug(DBG_INFO, "Next connection attempt to %s in %lds", server->conf->name, wait);
103         sleep(wait);
104         firsttry = 0;
105 
106         gettimeofday(&now, NULL);
107         if (timeout && (now.tv_sec - start.tv_sec) > timeout) {
108             debug(DBG_DBG, "tcpconnect: timeout");
109             return 0;
110         }
111         pthread_mutex_lock(&server->lock);
112 
113         debug(DBG_INFO, "tcpconnect: connecting to %s", server->conf->name);
114         if ((server->sock = connecttcphostlist(server->conf->hostports, srcres)) < 0)
115             continue;
116         if (server->conf->keepalive)
117             enable_keepalive(server->sock);
118         break;
119     }
120     server->state = RSP_SERVER_STATE_CONNECTED;
121     gettimeofday(&server->connecttime, NULL);
122     server->lostrqs = 0;
123     pthread_mutex_unlock(&server->lock);
124     pthread_mutex_lock(&server->newrq_mutex);
125     server->conreset = 1;
126     pthread_cond_signal(&server->newrq_cond);
127     pthread_mutex_unlock(&server->newrq_mutex);
128     return 1;
129 }
130 
131 /* timeout in seconds, 0 means no timeout (blocking), returns when num bytes have been read, or timeout */
132 /* returns 0 on timeout, -1 on error and num if ok */
tcpreadtimeout(int s,unsigned char * buf,int num,int timeout)133 int tcpreadtimeout(int s, unsigned char *buf, int num, int timeout) {
134     int ndesc, cnt, len;
135     struct pollfd fds[1];
136 
137     if (s < 0)
138 	return -1;
139     /* make socket non-blocking? */
140     for (len = 0; len < num; len += cnt) {
141         fds[0].fd = s;
142         fds[0].events = POLLIN;
143 	ndesc = poll(fds, 1, timeout? timeout * 1000 : -1);
144 	if (ndesc < 1)
145 	    return ndesc;
146 
147     if (fds[0].revents & (POLLERR | POLLHUP | POLLNVAL) ) {
148         return -1;
149     }
150 	cnt = read(s, buf + len, num - len);
151 	if (cnt <= 0)
152 	    return -1;
153     }
154     return num;
155 }
156 
157 /* timeout in seconds, 0 means no timeout (blocking) */
radtcpget(int s,int timeout)158 unsigned char *radtcpget(int s, int timeout) {
159     int cnt, len;
160     unsigned char buf[4], *rad;
161 
162     for (;;) {
163 	cnt = tcpreadtimeout(s, buf, 4, timeout);
164 	if (cnt < 1) {
165 	    debug(DBG_DBG, cnt ? "radtcpget: connection lost" : "radtcpget: timeout");
166 	    return NULL;
167 	}
168 
169 	len = RADLEN(buf);
170 	if (len < 4) {
171 	    debug(DBG_ERR, "radtcpget: length too small");
172 	    continue;
173 	}
174 	rad = malloc(len);
175 	if (!rad) {
176 	    debug(DBG_ERR, "radtcpget: malloc failed");
177 	    continue;
178 	}
179 	memcpy(rad, buf, 4);
180 
181 	cnt = tcpreadtimeout(s, rad + 4, len - 4, timeout);
182 	if (cnt < 1) {
183 	    debug(DBG_DBG, cnt ? "radtcpget: connection lost" : "radtcpget: timeout");
184 	    free(rad);
185 	    return NULL;
186 	}
187 
188 	if (len >= 20)
189 	    break;
190 
191 	free(rad);
192 	debug(DBG_WARN, "radtcpget: packet smaller than minimum radius size");
193     }
194 
195     debug(DBG_DBG, "radtcpget: got %d bytes", len);
196     return rad;
197 }
198 
clientradputtcp(struct server * server,unsigned char * rad)199 int clientradputtcp(struct server *server, unsigned char *rad) {
200     int cnt;
201     size_t len;
202     struct clsrvconf *conf = server->conf;
203 
204     if (server->state != RSP_SERVER_STATE_CONNECTED)
205 	return 0;
206     len = RADLEN(rad);
207     if ((cnt = write(server->sock, rad, len)) <= 0) {
208 	debug(DBG_ERR, "clientradputtcp: write error");
209 	return 0;
210     }
211     debug(DBG_DBG, "clientradputtcp: Sent %d bytes, Radius packet of length %d to TCP peer %s", cnt, len, conf->name);
212     return 1;
213 }
214 
tcpclientrd(void * arg)215 void *tcpclientrd(void *arg) {
216     struct server *server = (struct server *)arg;
217     unsigned char *buf;
218 
219     for (;;) {
220 	buf = radtcpget(server->sock, server->dynamiclookuparg ? IDLE_TIMEOUT : 0);
221 	if (!buf) {
222         if (server->dynamiclookuparg)
223 		break;
224 	    tcpconnect(server, 0, "tcpclientrd");
225 	    continue;
226 	}
227 
228 	replyh(server, buf);
229     }
230     server->clientrdgone = 1;
231     pthread_mutex_lock(&server->newrq_mutex);
232     pthread_cond_signal(&server->newrq_cond);
233     pthread_mutex_unlock(&server->newrq_mutex);
234     return NULL;
235 }
236 
tcpserverwr(void * arg)237 void *tcpserverwr(void *arg) {
238     int cnt;
239     struct client *client = (struct client *)arg;
240     struct gqueue *replyq;
241     struct request *reply;
242     char tmp[INET6_ADDRSTRLEN];
243 
244     debug(DBG_DBG, "tcpserverwr: starting for %s", addr2string(client->addr, tmp, sizeof(tmp)));
245     replyq = client->replyq;
246     for (;;) {
247 	pthread_mutex_lock(&replyq->mutex);
248 	while (!list_first(replyq->entries)) {
249 	    if (client->sock >= 0) {
250 		debug(DBG_DBG, "tcpserverwr: waiting for signal");
251 		pthread_cond_wait(&replyq->cond, &replyq->mutex);
252 		debug(DBG_DBG, "tcpserverwr: got signal");
253 	    }
254 	    if (client->sock < 0) {
255 		/* s might have changed while waiting */
256 		pthread_mutex_unlock(&replyq->mutex);
257 		debug(DBG_DBG, "tcpserverwr: exiting as requested");
258 		pthread_exit(NULL);
259 	    }
260 	}
261 	reply = (struct request *)list_shift(replyq->entries);
262 	pthread_mutex_unlock(&replyq->mutex);
263 	cnt = write(client->sock, reply->replybuf, RADLEN(reply->replybuf));
264 	if (cnt > 0)
265 	    debug(DBG_DBG, "tcpserverwr: sent %d bytes, Radius packet of length %d to %s",
266 		  cnt, RADLEN(reply->replybuf), addr2string(client->addr, tmp, sizeof(tmp)));
267 	else
268 	    debug(DBG_ERR, "tcpserverwr: write error for %s", addr2string(client->addr, tmp, sizeof(tmp)));
269 	freerq(reply);
270     }
271 }
272 
tcpserverrd(struct client * client)273 void tcpserverrd(struct client *client) {
274     struct request *rq;
275     uint8_t *buf;
276     pthread_t tcpserverwrth;
277     char tmp[INET6_ADDRSTRLEN];
278 
279     debug(DBG_DBG, "tcpserverrd: starting for %s", addr2string(client->addr, tmp, sizeof(tmp)));
280 
281     if (pthread_create(&tcpserverwrth, &pthread_attr, tcpserverwr, (void *)client)) {
282 	debug(DBG_ERR, "tcpserverrd: pthread_create failed");
283 	return;
284     }
285 
286     for (;;) {
287 	buf = radtcpget(client->sock, 0);
288 	if (!buf) {
289 	    debug(DBG_ERR, "tcpserverrd: connection from %s lost", addr2string(client->addr, tmp, sizeof(tmp)));
290 	    break;
291 	}
292 	debug(DBG_DBG, "tcpserverrd: got Radius message from %s", addr2string(client->addr, tmp, sizeof(tmp)));
293 	rq = newrequest();
294 	if (!rq) {
295 	    free(buf);
296 	    continue;
297 	}
298 	rq->buf = buf;
299 	rq->from = client;
300 	if (!radsrv(rq)) {
301 	    debug(DBG_ERR, "tcpserverrd: message authentication/validation failed, closing connection from %s", addr2string(client->addr, tmp, sizeof(tmp)));
302 	    break;
303 	}
304     }
305 
306     /* stop writer by setting s to -1 and give signal in case waiting for data */
307     client->sock = -1;
308     pthread_mutex_lock(&client->replyq->mutex);
309     pthread_cond_signal(&client->replyq->cond);
310     pthread_mutex_unlock(&client->replyq->mutex);
311     debug(DBG_DBG, "tcpserverrd: waiting for writer to end");
312     pthread_join(tcpserverwrth, NULL);
313     debug(DBG_DBG, "tcpserverrd: reader for %s exiting", addr2string(client->addr, tmp, sizeof(tmp)));
314 }
tcpservernew(void * arg)315 void *tcpservernew(void *arg) {
316     int s;
317     struct sockaddr_storage from;
318     socklen_t fromlen = sizeof(from);
319     struct clsrvconf *conf;
320     struct client *client;
321     char tmp[INET6_ADDRSTRLEN];
322 
323     s = *(int *)arg;
324     free(arg);
325     if (getpeername(s, (struct sockaddr *)&from, &fromlen)) {
326 	debug(DBG_DBG, "tcpservernew: getpeername failed, exiting");
327 	goto exit;
328     }
329     debug(DBG_WARN, "tcpservernew: incoming TCP connection from %s", addr2string((struct sockaddr *)&from, tmp, sizeof(tmp)));
330 
331     conf = find_clconf(handle, (struct sockaddr *)&from, NULL);
332     if (conf) {
333         client = addclient(conf, 1);
334         if (client) {
335             if(conf->keepalive)
336                 enable_keepalive(s);
337             client->sock = s;
338             client->addr = addr_copy((struct sockaddr *)&from);
339             tcpserverrd(client);
340             removeclient(client);
341         } else
342             debug(DBG_WARN, "tcpservernew: failed to create new client instance");
343     } else
344         debug(DBG_WARN, "tcpservernew: ignoring request, no matching TCP client");
345 
346 exit:
347     shutdown(s, SHUT_RDWR);
348     close(s);
349     pthread_exit(NULL);
350 }
351 
tcplistener(void * arg)352 void *tcplistener(void *arg) {
353     pthread_t tcpserverth;
354     int s, *sp = (int *)arg, *s_arg = NULL;
355     struct sockaddr_storage from;
356     socklen_t fromlen = sizeof(from);
357 
358     listen(*sp, 128);
359 
360     for (;;) {
361 	s = accept(*sp, (struct sockaddr *)&from, &fromlen);
362 	if (s < 0) {
363 	    debug(DBG_WARN, "accept failed");
364 	    continue;
365 	}
366         s_arg = malloc(sizeof(s));
367         if (!s_arg)
368             debugx(1, DBG_ERR, "malloc failed");
369         *s_arg = s;
370 	if (pthread_create(&tcpserverth, &pthread_attr, tcpservernew, (void *) s_arg)) {
371 	    debug(DBG_ERR, "tcplistener: pthread_create failed");
372             free(s_arg);
373 	    shutdown(s, SHUT_RDWR);
374 	    close(s);
375 	    continue;
376 	}
377 	pthread_detach(tcpserverth);
378     }
379     free(sp);
380     return NULL;
381 }
382 #else
tcpinit(uint8_t h)383 const struct protodefs *tcpinit(uint8_t h) {
384     return NULL;
385 }
386 #endif
387 
388 /* Local Variables: */
389 /* c-file-style: "stroustrup" */
390 /* End: */
391