2 * based on TSOCKS - Wrapper library for transparent SOCKS
3 * TSOCKS Copyright (C) 2000 Shaun Clowes
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
22 /* Global configuration variables */
23 char *progname
= "libtumfw"; /* Name used in err msgs */
26 #include <arpa/inet.h>
30 #include <netinet/in.h>
40 #include <sys/types.h>
41 #include <sys/select.h>
42 #include <sys/socket.h>
53 /* global declarations */
54 static int (*realconnect
) (CONNECT_SIGNATURE
);
55 static int (*realbind
) (BIND_SIGNATURE
);
56 static ssize_t (*realsendto
) (SENDTO_SIGNATURE
);
59 /* exported function prototypes */
61 int connect (CONNECT_SIGNATURE
);
62 int bind (BIND_SIGNATURE
);
63 ssize_t
sendto (SENDTO_SIGNATURE
);
69 /*****************************************************************************/
70 #include "rulecache.c"
72 #ifdef OPT_USE_RULE_PARSER
73 #include "ruleparser.c"
77 /*****************************************************************************/
78 static int getEnvironment (void) {
80 int loglevel
= LOGMSG_ERR
;
85 if ((env
= getenv("TUMFW_DEBUG"))) loglevel
= atoi(env
);
86 if (((env
= getenv("TUMFW_DEBUG_FILE"))) && !suid
) logfile
= env
;
87 setLogOptions(loglevel
, logfile
, 1);
88 #ifdef OPT_USE_RULE_PARSER
96 static int getConfig (void) {
99 #ifdef OPT_USE_RULE_PARSER
114 static int checkFDandAddr (const char *opname
, int __fd
, const struct sockaddr_in
*connaddr
, uint8_t *type
) {
116 int sock_type_len
= sizeof(sock_type
);
118 /* if this isn't an INET socket we can't handle it, just call the real op */
119 if (connaddr
->sin_family
!= AF_INET
) {
120 logMsg(LOGMSG_DEBUG
, "%s: isn't an AF_INET, ignoring\n", opname
);
124 /* get the type of the socket */
125 getsockopt(__fd
, SOL_SOCKET
, SO_TYPE
, (void *)&sock_type
, (void *)&sock_type_len
);
127 if (sock_type
!= SOCK_STREAM
&& sock_type
!= SOCK_DGRAM
) {
128 logMsg(LOGMSG_DEBUG
, "%s: isn't a TCP/UDP, ignoring\n", opname
);
132 *type
= (sock_type
== SOCK_STREAM
)?IPCQ_PROTO_TCP
:IPCQ_PROTO_UDP
;
134 /* if we haven't initialized yet, do it now */
142 static int askAllowConnect (const struct in_addr
*in
, uint16_t port
, uint8_t proto
, uint8_t action
) {
144 uint32_t *ip
= (uint32_t *)in
;
146 if (!realbind
|| !realconnect
|| !realsendto
) return 1; /* no binds -- no checks */
148 if (((*ip
>>24)&0xff) == 127) return 1; /* localhost always allowed */
150 tSessionRule
*rule
= findRule(*ip
, port
, proto
);
151 if (rule
) return rule
->allow
;
153 int ufd
= socket(AF_UNIX
, SOCK_STREAM
, 0);
155 logMsg(LOGMSG_DEBUG
, "can't create umfw socket\n");
157 return 1; /* alas, no fd -- no checks */
160 struct sockaddr_un srv
;
161 srv
.sun_family
= AF_UNIX
;
162 strcpy(srv
.sun_path
, UNIX_SOCKET_PATH
);
164 if (realbind(ufd, (struct sockaddr *)&srv, sizeof(struct sockaddr_un))) {
165 logMsg(LOGMSG_DEBUG, "can't bind umfw socket\n");
169 if (realconnect(ufd
, (struct sockaddr
*)&srv
, sizeof(struct sockaddr_un
))) {
170 logMsg(LOGMSG_DEBUG
, "can't connect to umfw socket\n");
175 sbuf
.version
= UPROTO_VERSION
;
177 sbuf
.ip
= htonl(*ip
);
178 sbuf
.port
= htons(port
);
180 sbuf
.action
= action
;
181 if (send(ufd
, &sbuf
, sizeof(sbuf
), MSG_NOSIGNAL
) != sizeof(sbuf
)) {
182 logMsg(LOGMSG_DEBUG
, "can't send to umfw socket\n");
190 if ((r
= recv(ufd
, &rbuf
, sizeof(rbuf
), MSG_WAITALL
)) != sizeof(rbuf
)) {
191 logMsg(LOGMSG_DEBUG
, "can't read from umfw socket\n");
192 if (errno
== EAGAIN
|| errno
== EINTR
) {
193 logMsg(LOGMSG_DEBUG
, "umfw socket: restarting\n");
199 if (rbuf
.version
!= UPROTO_VERSION
) {
200 logMsg(LOGMSG_DEBUG
, "umfw socket: invalid version read\n");
207 /* new session/permanent rule */
208 addRule(*ip
, port
, rbuf
.allow
, rbuf
.flags
);
209 logMsg(LOGMSG_DEBUG
, "umfw socket: new session rule: %08x %u %u %02x\n", *ip
, port
, rbuf
.allow
, rbuf
.flags
);
213 return rbuf
.allow
== IPCR_ALLOW
;
216 /* alas, server error -- no checks */
223 #define CHECK_SYMBOL(name) \
226 if (real##name == NULL) { \
227 logMsg(LOGMSG_ERR, "unresolved symbol: %s\n", #name); \
232 #define IP_BYTES(n) \
233 (uint32_t)(n) & 0xff, \
234 (((uint32_t)(n))>>8) & 0xff, \
235 (((uint32_t)(n))>>16) & 0xff, \
236 (((uint32_t)(n))>>24) & 0xff
238 static void logAction (const char *action
, const struct in_addr
*ip
, uint16_t port
) {
239 logMsg(LOGMSG_DEBUG
, "*%s: %u.%u.%u.%u:%u\n", action
, IP_BYTES(ip
->s_addr
), (unsigned int)htons(port
));
243 int connect (CONNECT_SIGNATURE
) {
244 struct sockaddr_in
*connaddr
;
247 CHECK_SYMBOL(connect
);
248 connaddr
= (struct sockaddr_in
*)__addr
;
249 if (checkFDandAddr("connect", __fd
, connaddr
, &type
)) goto skipcheck
;
250 logAction("connect", &connaddr
->sin_addr
, connaddr
->sin_port
);
251 if (!askAllowConnect(&connaddr
->sin_addr
, connaddr
->sin_port
, type
, IPCQ_ACTION_CONNECT
)) {
252 errno
= ECONNREFUSED
;
256 return realconnect(__fd
, __addr
, __len
);
260 int bind (BIND_SIGNATURE
) {
261 struct sockaddr_in
*connaddr
;
265 connaddr
= (struct sockaddr_in
*)__addr
;
266 if (checkFDandAddr("bind", __fd
, connaddr
, &type
)) goto skipcheck
;
267 logAction("bind", &connaddr
->sin_addr
, connaddr
->sin_port
);
268 if (!askAllowConnect(&connaddr
->sin_addr
, connaddr
->sin_port
, type
, IPCQ_ACTION_BIND
)) {
269 errno
= EADDRNOTAVAIL
;
273 return realbind(__fd
, __addr
, __len
);
277 ssize_t
sendto (SENDTO_SIGNATURE
) {
278 const struct sockaddr_in
*connaddr
;
281 CHECK_SYMBOL(sendto
);
282 if (!__dest_addr
) goto skipcheck
; /* no dest addr -- nothing to check */
283 connaddr
= (struct sockaddr_in
*)__dest_addr
;
284 if (checkFDandAddr("sendto", __fd
, connaddr
, &type
)) goto skipcheck
;
285 logAction("sendto", &connaddr
->sin_addr
, connaddr
->sin_port
);
286 if (!askAllowConnect(&connaddr
->sin_addr
, connaddr
->sin_port
, type
, IPCQ_ACTION_SENDTO
)) {
291 return realsendto(__fd
, __buf
, __len
, __flags
, __dest_addr
, __addrlen
);
296 * We could do all our initialization here, but to be honest
297 * most programs that are run won't use our services, so
298 * we do our general initialization on first call
301 /* determine the logging level */
302 suid
= (getuid() != geteuid());
303 /* get 'real' pointers */
304 realconnect
= dlsym(RTLD_NEXT
, "connect");
305 realbind
= dlsym(RTLD_NEXT
, "bind");
306 realsendto
= dlsym(RTLD_NEXT
, "sendto");