tagging vde-2 version 2.3.2
[vde.git] / 2.3.2 / src / vde_cryptcab / vde_cryptcab_server.c
blob29df01672e26fbce8a3b08460dcc9bb09cff6707
1 /*
2 * VDE Cryptcab
3 * Copyright © 2006-2008 Daniele Lacamera
4 * from an idea by Renzo Davoli
6 * Released under the terms of GNU GPL v.2
7 * (http://www.gnu.org/licenses/old-licenses/gpl-2.0.html)
8 * with the additional exemption that
9 * compiling, linking, and/or using OpenSSL is allowed.
13 #include "cryptcab.h"
15 static struct peer *list = NULL;
16 static char *plugname;
17 static enum e_enc_type enc_type = ENC_SSH;
20 * Add a peer to the main list.
21 * Client will have a list of one peer only,
22 * server will have a peer in the list for each "connection"
23 * it establishes.
26 static void addpeer(struct peer *np)
28 np->next=list;
29 list=np;
34 * Internal, recursive functions:
36 static int _peers(struct peer *iter)
38 if(!iter)
39 return 0;
40 else
41 return 1+_peers(iter->next);
44 static void _populatepoll(struct pollfd *pfd, struct peer *iter,int index, struct peer *peerlist)
46 int datafd;
47 if(!iter)
48 return;
49 memcpy(&(peerlist[index]),iter,sizeof(struct peer));
50 if(iter->plug){
51 datafd=vde_datafd(iter->plug);
52 pfd[index].fd=datafd;
53 pfd[index++].events=POLLIN|POLLHUP|POLLNVAL;
54 } else if(iter->state == ST_AUTH) {
55 vde_plug(iter, plugname);
56 usleep(100000);
57 _populatepoll(pfd,iter->next,index, peerlist);
58 return;
61 _populatepoll(pfd,iter->next,index, peerlist);
65 struct peer *_getpeer(struct sockaddr_in saddr, struct peer *sublist)
67 if(!sublist)
68 return NULL;
69 if(sublist->in_a.sin_addr.s_addr==saddr.sin_addr.s_addr && sublist->in_a.sin_port==saddr.sin_port)
70 return sublist;
71 return _getpeer(saddr,sublist->next);
77 * Returns peer list length.
80 static int numberofpeers(){
81 struct peer *iter=list;
82 return _peers(iter);
85 static void remove_peerlist(struct peer *sublist)
87 char filename[128];
88 if(!sublist)
89 return;
90 vde_close(sublist->plug);
91 sublist->plug=NULL;
92 if (sublist->state == ST_AUTH && enc_type == ENC_SSH){
93 snprintf(filename,127,"/tmp/.%s.key",sublist->id);
94 if (unlink(filename) == 0){
95 vc_printlog(2,"Successfully removed key file %s", filename);
96 }else{
97 vc_printlog(2,"Could not remove key file %s", filename);
100 remove_peerlist(sublist->next);
105 static struct peer *clean_peerlist(struct peer *sublist)
107 struct timeval now;
108 char filename[128];
109 struct peer *nxt;
111 if (sublist == list) {
112 vc_printlog(4, "Cleaning list of peer from expired clients....");
115 if(!sublist)
116 return NULL;
117 nxt=sublist->next;
118 gettimeofday(&now,NULL);
119 if(after(now,sublist->expire)
120 // || (sublist->state == ST_AUTH && sublist->plug)
122 vc_printlog(1,"Client %s : expired.",inet_ntoa(sublist->in_a.sin_addr));
123 vc_printlog(4,"Client %s : expire time: %lu, now= %lu.",inet_ntoa(sublist->in_a.sin_addr),sublist->expire.tv_sec,now.tv_sec);
124 if (sublist->plug){
125 vde_close(sublist->plug);
126 sublist->plug=NULL;
128 if (sublist->state == ST_AUTH && enc_type == ENC_SSH){
129 snprintf(filename,127,"/tmp/.%s.key",sublist->id);
130 if (unlink(filename) == 0){
131 vc_printlog(2,"Successfully removed key file %s", filename);
132 }else{
133 vc_printlog(2,"Could not remove key file %s", filename);
136 free(sublist);
137 return nxt;
139 sublist->next=clean_peerlist(sublist->next);
140 return sublist;
146 * Returns a list of all the peer in the peer list, adding their
147 * network socket to pollfd.
148 * This is called in blowfish_select, to populate the pollfd structure.
150 static struct peer *populate_peerlist(struct pollfd *pfd)
152 static struct peer *iter, *peerlist;
153 iter=list; //=clean_peerlist(list);
154 if(peerlist)
155 free(peerlist);
156 peerlist=(struct peer *) malloc( (numberofpeers()+1)*sizeof(struct peer) );
157 _populatepoll(pfd,iter,1,peerlist);
159 return peerlist;
163 * Get a pointer to the peer in the list which has the given udp address.
165 static struct peer *getpeer(struct sockaddr_in saddr)
167 struct peer *iter=list;
168 return (_getpeer(saddr,iter));
174 * Get a pointer to the peer in the list which key filename is the same of that in the login datagram.
177 static void
178 do_exit(int signo){
179 vc_printlog(1,"Caught signal, exiting.");
180 remove_peerlist(list);
181 exit(0);
185 static void
186 set_expire(struct peer *p, unsigned char cmd)
188 struct timeval now;
189 gettimeofday(&now,NULL);
190 p->expire.tv_usec = 0;
192 switch (cmd){
193 case EXPIRE_NOW:
194 p->expire.tv_sec = now.tv_sec + PRELOGIN_TIMEOUT;
195 break;
197 case CMD_CHALLENGE:
198 p->expire.tv_sec = now.tv_sec + CHALLENGE_TIMEOUT;
199 break;
201 case CMD_LOGIN:
202 p->expire.tv_sec = now.tv_sec + PRELOGIN_TIMEOUT;
203 break;
205 default:
206 p->expire.tv_sec = now.tv_sec + SESSION_TIMEOUT;
207 break;
212 static void
213 deny_access(struct peer *p)
215 send_udp((unsigned char *)"Access Denied.\0",15,p,CMD_DENY);
216 p->state = ST_CLOSED;
217 set_expire(p, EXPIRE_NOW);
221 * Send a "Challenge" 4WHS packet.
223 static void
224 send_challenge(struct peer *p)
226 int fd;
227 if ( ((fd = open ("/dev/urandom", O_RDONLY)) == -1)||
228 ((read (fd, p->challenge, 128)) != -1))
230 send_udp((unsigned char *)p->challenge,128,p,PKT_CTL|CMD_CHALLENGE);
232 p->state=ST_CHALLENGE;
233 close(fd);
237 * Send a "Auth OK" 4WHS packet.
239 static void
240 send_auth_ok(struct peer *p)
242 send_udp(NULL,0,p,CMD_AUTH_OK);
243 p->state=ST_AUTH;
244 if(!p->plug)
245 vde_plug(p, plugname);
246 set_expire(p,CMD_AUTH_OK);
249 * Receive a login request. Send challenge.
251 static void
252 rcv_login(struct datagram *pkt, char *pre_shared)
254 int fd;
255 char filename[128];
256 if(!pre_shared)
257 snprintf(filename,127,"/tmp/.%s.key",pkt->data+1);
258 else
259 snprintf(filename,127,"%s",pre_shared);
260 sync();
261 usleep(10000);
262 if (((fd = open (filename, O_RDONLY)) == -1)||
263 ((read (fd, pkt->orig->key, 16)) == -1) ||
264 ((read (fd, pkt->orig->iv, 8)) == -1) ){
265 perror ("blowfish.key open error");
266 deny_access(pkt->orig);
267 return;
270 close(fd);
271 memcpy(pkt->orig->id,pkt->data+1,FILENAMESIZE);
272 vc_printlog(2,"Sending challenge... ");
273 send_challenge(pkt->orig);
274 set_expire(pkt->orig, CMD_CHALLENGE);
275 vc_printlog(2,"OK.\n");
280 * Receive a response from challenge. Validate encryption and send "ok auth"
281 * or "access denied"
283 static void
284 rcv_response(struct datagram *pkt)
286 unsigned char response[MAXPKT];
287 int rlen;
288 struct peer *p = pkt->orig;
290 rlen = data_decrypt(pkt->data + 1, response, pkt->len - 1, p);
292 if (rlen > 0 && strncmp((char *)response, p->challenge,128)==0){
293 p->state = ST_AUTH;
294 send_auth_ok(p);
295 } else {
296 deny_access(p);
300 * Main select routine.
301 * A poll will wake up whenever a new packet is available to read, either from one
302 * of the vde_plug attached, or from udp socket.
303 * Returns a struct datagram aware of its own source.
305 static int recv_datagram_srv(struct datagram *pkt, int nfd)
307 unsigned peerlen;
308 int pollret;
309 static struct pollfd *pfd = NULL;
310 static struct peer *peerlist = NULL;
311 static int i=1;
313 if (pfd)
314 free(pfd);
316 pfd=malloc((1+numberofpeers())*sizeof(struct pollfd));
318 pfd[0].fd=nfd;
319 pfd[0].events=POLLIN|POLLHUP;
320 peerlist = populate_peerlist(pfd);
322 pollret = poll(pfd,1+numberofpeers(),1000);
323 if(pollret<0){
324 if(errno==EINTR)
325 return 0;
326 perror("poll");
327 exit(1);
329 if (pollret == 0) {
330 list = clean_peerlist(list);
331 return 0;
335 for(;;){
336 if (pfd[0].revents&POLLIN) {
337 struct sockaddr_in ipaddress;
338 peerlen = sizeof(struct sockaddr_in);
339 pkt->len = recvfrom(nfd, pkt->data, MAXPKT, 0,
340 (struct sockaddr *) &ipaddress, &peerlen);
342 pkt->orig=getpeer(ipaddress);
343 if(!pkt->orig){
344 pkt->orig=malloc(sizeof(struct peer));
345 memset(pkt->orig,0,sizeof(struct peer));
346 pkt->orig->in_a.sin_family = AF_INET;
347 pkt->orig->in_a.sin_port = ipaddress.sin_port;
348 pkt->orig->in_a.sin_addr.s_addr= ipaddress.sin_addr.s_addr;
349 pkt->orig->state=ST_CLOSED;
350 addpeer(pkt->orig);
351 set_expire(pkt->orig, CMD_LOGIN);
353 pkt->src = SRC_UDP;
354 return 1;
357 // This increment comes with "static int i" def, to ensure fairness among peers.
358 i++;
359 if(i>numberofpeers()) {
360 i=1;
363 if (pfd[i].revents&POLLNVAL || pfd[i].revents&POLLHUP){
364 usleep(10000);
365 return 0;
368 if (pfd[i].revents&POLLIN && peerlist[i].plug != NULL ) {
369 pkt->len = vde_recv(peerlist[i].plug, pkt->data, MAXPKT,0);
370 if(pkt->len<1)
371 return 0;
372 pkt->src = SRC_VDE;
373 pkt->orig = &(peerlist[i]);
374 return 1;
376 break;
378 return 0;
381 void cryptcab_server(char *_plugname, unsigned short udp_port, enum e_enc_type _enc_type, char *pre_shared)
383 int wire, r;
384 struct sockaddr_in myaddr;
385 struct datagram pkt, pkt_dec;
386 struct sigaction sa_timer;
387 struct sigaction sa_exit;
388 struct peer *p1;
390 enc_type = _enc_type;
391 plugname = _plugname;
394 sigemptyset(&sa_timer.sa_mask);
395 sigemptyset(&sa_exit.sa_mask);
396 sa_exit.sa_handler = do_exit;
397 sigaction(SIGINT, &sa_exit, NULL);
398 sigaction(SIGTERM, &sa_exit, NULL);
400 if(enc_type == ENC_PRESHARED && (!pre_shared || access(pre_shared,R_OK)!=0)){
401 fprintf(stderr,"Error accessing pre-shared key %s\n",pre_shared);
402 perror ("access");
403 exit(1);
406 if (enc_type == ENC_NOENC)
407 disable_encryption();
409 memset ((char *)&myaddr, 0, sizeof(myaddr));
410 myaddr.sin_family = AF_INET;
411 myaddr.sin_addr.s_addr = htonl(INADDR_ANY);
412 myaddr.sin_port = htons(udp_port);
414 wire = socket(PF_INET,SOCK_DGRAM,0);
415 if (bind(wire,(struct sockaddr *) &myaddr, sizeof(myaddr))<0)
416 {perror("bind socket"); exit(3);}
418 set_nfd(wire);
420 for(;;){
421 r = recv_datagram_srv(&pkt, wire);
422 if (r == 0)
423 continue;
425 // fprintf(stderr,".");
426 p1 = pkt.orig;
427 if(pkt.src==SRC_VDE){
428 if(p1->state==ST_AUTH){
429 send_udp(pkt.data, pkt.len, p1, PKT_DATA);
431 continue;
433 else if(pkt.src==SRC_UDP){
434 switch(p1->state + pkt.data[0]) {
435 case (ST_AUTH + PKT_DATA):
436 vc_printlog(4,"Data pkt received (%d Bytes)",pkt.len);
437 pkt_dec.len = data_decrypt(pkt.data+1, pkt_dec.data, pkt.len-1, p1);
438 set_expire(p1, CMD_KEEPALIVE);
439 vde_send(p1->plug,pkt_dec.data,pkt_dec.len,0);
440 break;
441 case (ST_AUTH + CMD_KEEPALIVE):
442 vc_printlog(4,"Keepalive received from %s",inet_ntoa(p1->in_a.sin_addr));
443 set_expire(p1, CMD_KEEPALIVE);
444 break;
446 case ST_AUTH + CMD_LOGIN:
447 set_expire(p1, EXPIRE_NOW);
448 case ST_CLOSED + CMD_LOGIN:
449 vc_printlog(4,"Login pkt received.");
450 p1->state=ST_OPENING;
451 p1->counter=0;
452 rcv_login(&pkt,pre_shared);
453 break;
455 case ST_CHALLENGE + CMD_RESPONSE:
456 vc_printlog(4,"Response pkt received.");
457 //fprintf(stderr, "Receiving response\n");
458 rcv_response(&pkt);
459 break;
461 default:
462 vc_printlog(4,"Unknown/undesired pkt received. (state: 0x%X code: 0x%X )", p1->state, (unsigned char)pkt.data[0]);
463 if (p1->state != ST_AUTH)
464 deny_access(pkt.orig);
465 else
466 send_auth_ok(pkt.orig);
472 exit (0);