0
|
1 /* Copyright 2014 Ivo Smits <Ivo@UCIS.nl>. All rights reserved. |
|
2 Redistribution and use in source and binary forms, with or without modification, are |
|
3 permitted provided that the following conditions are met: |
|
4 |
|
5 1. Redistributions of source code must retain the above copyright notice, this list of |
|
6 conditions and the following disclaimer. |
|
7 |
|
8 2. Redistributions in binary form must reproduce the above copyright notice, this list |
|
9 of conditions and the following disclaimer in the documentation and/or other materials |
|
10 provided with the distribution. |
|
11 |
|
12 THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED |
|
13 WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND |
|
14 FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR |
|
15 CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
|
16 CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR |
|
17 SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON |
|
18 ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING |
|
19 NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF |
|
20 ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
|
21 |
|
22 The views and conclusions contained in the software and documentation are those of the |
|
23 authors and should not be interpreted as representing official policies, either expressed |
|
24 or implied, of Ivo Smits.*/ |
|
25 |
|
26 #include <stdlib.h> |
|
27 #include <stdio.h> |
|
28 #include <stdbool.h> |
|
29 #include <fcntl.h> |
|
30 #include <string.h> |
|
31 #include <sys/types.h> |
|
32 #include <poll.h> |
|
33 #include <sys/socket.h> |
|
34 #ifndef HAVE_NETINET_IN_H |
|
35 #include <netinet/in.h> |
|
36 #endif |
|
37 #include <arpa/inet.h> |
|
38 #include <netdb.h> |
|
39 #include "include.h" |
|
40 |
|
41 char* (*getconf)(const char*) = getenv; |
|
42 |
|
43 static void hex2bin(unsigned char* dest, const char* src, const int count) { |
|
44 int i; |
|
45 for (i = 0; i < count; i++) { |
|
46 if (*src >= '0' && *src <= '9') *dest = *src - '0'; |
|
47 else if (*src >= 'a' && * src <='f') *dest = *src - 'a' + 10; |
|
48 else if (*src >= 'A' && * src <='F') *dest = *src - 'A' + 10; |
|
49 src++; *dest = *dest << 4; |
|
50 if (*src >= '0' && *src <= '9') *dest += *src - '0'; |
|
51 else if (*src >= 'a' && *src <= 'f') *dest += *src - 'a' + 10; |
|
52 else if (*src >= 'A' && *src <= 'F') *dest += *src - 'A' + 10; |
|
53 src++; dest++; |
|
54 } |
|
55 } |
|
56 |
|
57 static bool crypto_init(connection_context* context, bool* keyupdate) { |
|
58 unsigned char cpublickey[32], csecretkey[32]; |
|
59 bool hpublickey = false, hsecretkey = false; |
|
60 char* envval; |
|
61 *keyupdate = false; |
|
62 if ((envval = getconf("PUBLIC_KEY"))) { |
|
63 if (strlen(envval) != 64) return errorexit("PUBLIC_KEY length"); |
|
64 hex2bin(cpublickey, envval, 32); |
|
65 hpublickey = true; |
|
66 } |
|
67 if ((envval = getconf("PRIVATE_KEY"))) { |
|
68 if (strlen(envval) != 64) return errorexit("PRIVATE_KEY length"); |
|
69 hex2bin(csecretkey, envval, 32); |
|
70 hsecretkey = true; |
|
71 } else if ((envval = getconf("PRIVATE_KEY_FILE"))) { |
|
72 FILE* pkfile = fopen(envval, "rb"); |
|
73 if (!pkfile) return errorexitp("Could not open PRIVATE_KEY_FILE"); |
|
74 char pktextbuf[64]; |
|
75 const size_t pktextsize = fread(pktextbuf, 1, sizeof(pktextbuf), pkfile); |
|
76 if (pktextsize == 32) { |
|
77 memcpy(csecretkey, pktextbuf, 32); |
|
78 } else if (pktextsize == 64) { |
|
79 hex2bin(csecretkey, pktextbuf, 32); |
|
80 } else { |
|
81 return errorexit("PRIVATE_KEY length"); |
|
82 } |
|
83 fclose(pkfile); |
|
84 hsecretkey = true; |
|
85 } |
|
86 if (hpublickey || hsecretkey || ((envval = getconf("ENCRYPT")) && atoi(envval))) { |
|
87 if (!hpublickey) fprintf(stderr, "Warning: encryption enabled but remote key not set, cryptographic authentication disabled.\n"); |
|
88 if (!connection_init_encryption(context, hsecretkey ? csecretkey : NULL, hpublickey ? cpublickey : NULL)) return false; |
|
89 *keyupdate = true; |
|
90 } else { |
|
91 fprintf(stderr, "Warning: encryption disabled.\n"); |
|
92 } |
|
93 if ((envval = getconf("PASSWORD"))) { |
|
94 if (!connection_init_passwordauth(context, strdup(envval))) return false; |
|
95 } |
|
96 return true; |
|
97 } |
|
98 |
|
99 typedef union { |
|
100 struct sockaddr any; |
|
101 struct sockaddr_in ip4; |
|
102 struct sockaddr_in6 ip6; |
|
103 } sockaddr_any; |
|
104 |
|
105 static int sockaddr_set_port(sockaddr_any* sa, int port) { |
|
106 port = htons(port); |
|
107 int af = sa->any.sa_family; |
|
108 if (af == AF_INET) sa->ip4.sin_port = port; |
|
109 else if (af == AF_INET6) sa->ip6.sin6_port = port; |
|
110 else return errorexit("Unknown address family"); |
|
111 return 0; |
|
112 } |
|
113 |
|
114 static bool socket_init(connection_context* context) { |
|
115 char* envval; |
|
116 fprintf(stderr, "Initializing socket...\n"); |
|
117 struct addrinfo *ai_local = NULL, *ai_remote = NULL; |
|
118 unsigned short af = 0; |
|
119 int ret; |
|
120 if ((envval = getconf("LOCAL_ADDRESS"))) { |
|
121 if ((ret = getaddrinfo(envval, NULL, NULL, &ai_local))) return errorexitf("getaddrinfo(LOCAL_ADDRESS)", gai_strerror(ret)); |
|
122 if (!ai_local) return errorexit("LOCAL_ADDRESS lookup failed"); |
|
123 if (ai_local->ai_addrlen > sizeof(sockaddr_any)) return errorexit("Resolved LOCAL_ADDRESS is too big"); |
|
124 af = ai_local->ai_family; |
|
125 } |
|
126 if ((envval = getconf("REMOTE_ADDRESS"))) { |
|
127 if ((ret = getaddrinfo(envval, NULL, NULL, &ai_remote))) return errorexitf("getaddrinfo(REMOTE_ADDRESS)", gai_strerror(ret)); |
|
128 if (!ai_remote) return errorexit("REMOTE_ADDRESS lookup failed"); |
|
129 if (ai_remote->ai_addrlen > sizeof(sockaddr_any)) return errorexit("Resolved REMOTE_ADDRESS is too big"); |
|
130 if (af && af != ai_remote->ai_family) return errorexit("Address families do not match"); |
|
131 af = ai_remote->ai_family; |
|
132 } |
|
133 if (!af) return connection_init_socket(context, 0, 1); |
|
134 int sa_size = sizeof(sockaddr_any); |
|
135 if (af == AF_INET) sa_size = sizeof(struct sockaddr_in); |
|
136 else if (af == AF_INET6) sa_size = sizeof(struct sockaddr_in6); |
|
137 int sfd = socket(af, SOCK_STREAM, IPPROTO_TCP); |
|
138 if (sfd < 0) return errorexitp("Could not create socket"); |
|
139 sockaddr_any udpaddr; |
|
140 if (ai_local) { |
|
141 memset(&udpaddr, 0, sizeof(udpaddr)); |
|
142 udpaddr.any.sa_family = af; |
|
143 memcpy(&udpaddr, ai_local->ai_addr, ai_local->ai_addrlen); |
|
144 int port = 2998; |
|
145 if ((envval = getconf("LOCAL_PORT"))) port = atoi(envval); |
|
146 if (sockaddr_set_port(&udpaddr, port)) return -1; |
|
147 if (bind(sfd, &udpaddr.any, sa_size)) return errorexitp("Could not bind socket"); |
|
148 } |
|
149 if (ai_remote) { |
|
150 memset(&udpaddr, 0, sizeof(udpaddr)); |
|
151 udpaddr.any.sa_family = af; |
|
152 memcpy(&udpaddr, ai_remote->ai_addr, ai_remote->ai_addrlen); |
|
153 int port = 2998; |
|
154 if ((envval = getconf("REMOTE_PORT"))) port = atoi(envval); |
|
155 if (sockaddr_set_port(&udpaddr, port)) return -1; |
|
156 if (connect(sfd, &udpaddr.any, sa_size)) return errorexitp("Could not connect socket"); |
|
157 } else { |
|
158 return errorexit("REMOTE_ADDRESS not specified and server mode is currently not supported :-(. Please use (x)inetd or similar."); |
|
159 } |
|
160 if (ai_local) freeaddrinfo(ai_local); |
|
161 if (ai_remote) freeaddrinfo(ai_remote); |
|
162 return connection_init_socket(context, sfd, sfd); |
|
163 } |
|
164 |
|
165 static bool mainA() { |
|
166 connection_context context; |
|
167 tunnel_context tunnel; |
|
168 bool keyupdate = false; |
|
169 if (!connection_init(&context)) return false; |
|
170 if (!socket_init(&context)) return false; |
|
171 if (!crypto_init(&context, &keyupdate)) return false; |
|
172 if (!connection_init_done(&context)) return false; |
|
173 while (!context.local_tunnelready) if (!connection_read(&context)) return false; |
|
174 if (keyupdate) connection_update_key(&context); |
|
175 |
|
176 if (!tunnel_init(&tunnel)) return false; |
|
177 context.tunnel = &tunnel; |
|
178 tunnel.connection = &context; |
|
179 |
|
180 struct pollfd fds[2]; |
|
181 fds[0].fd = context.recv_socket; |
|
182 fds[0].events = POLLIN; |
|
183 fds[1].fd = tunnel.fd; |
|
184 fds[1].events = POLLIN; |
|
185 |
|
186 while (true) { |
|
187 int len = poll(fds, 2, 10000); |
|
188 if (len < 0) return errorexitp("poll failed"); |
|
189 else if (fds[0].revents & (POLLERR | POLLHUP | POLLNVAL)) return errorexit("poll error on socket"); |
|
190 else if (fds[1].revents & (POLLHUP | POLLNVAL)) return errorexit("poll error on tap device"); |
|
191 if (len == 0) { |
|
192 if (keyupdate) { |
|
193 if (!context.key_updated) return errorexit("key update timed out"); |
|
194 if (!connection_update_key(&context)) return false; |
|
195 } else { |
|
196 if (!connection_ping(&context)) return errorexit("ping timed out"); |
|
197 } |
|
198 } |
|
199 if (fds[0].revents & POLLERR) return errorexitp("poll error on socket"); |
|
200 if (fds[0].revents & POLLIN) if (!connection_read(&context)) return false; |
|
201 if (fds[1].revents & POLLIN) if (!tunnel_read(&tunnel)) return false; |
|
202 } |
|
203 return -1; |
|
204 } |
|
205 |
|
206 int main() { |
|
207 return mainA() ? 0 : -1; |
|
208 } |
|
209 |
|
210 int errorexit(const char* text) { |
|
211 fprintf(stderr, "%s\n", text); |
|
212 return false; |
|
213 } |
|
214 int errorexitf(const char* text, const char* error) { |
|
215 fprintf(stderr, "%s: %s\n", text, error); |
|
216 return false; |
|
217 } |
|
218 bool errorexitp(const char* text) { |
|
219 perror(text); |
|
220 return false; |
|
221 } |
|
222 |