Annotation of embedaddon/coova-chilli/src/redir.c, revision 1.1.1.1
1.1 misho 1: /*
2: * HTTP redirection functions.
3: * Copyright (C) 2004, 2005 Mondru AB.
4: * Copyright (c) 2006-2007 David Bird <david@cova.com>
5: *
6: * The contents of this file may be used under the terms of the GNU
7: * General Public License Version 2, provided that the above copyright
8: * notice and this permission notice is included in all copies or
9: * substantial portions of the software.
10: *
11: */
12:
13: #include "system.h"
14: #include "syserr.h"
15: #include "radius.h"
16: #include "radius_wispr.h"
17: #include "radius_chillispot.h"
18: #include "redir.h"
19: #include "md5.h"
20: #include "dhcp.h"
21: #include "chilli.h"
22: #include "options.h"
23:
24: static int optionsdebug = 0; /* TODO: Should be changed to instance */
25:
26: static int keep_going = 1; /* OK as global variable for child process */
27:
28: static int termstate = REDIR_TERM_INIT; /* When we were terminated */
29:
30: char credits[] =
31: "<H1>CoovaChilli(ChilliSpot) " VERSION "</H1>"
32: "<p>Copyright 2002-2005 Mondru AB</p>"
33: "<p>Copyright 2006-2007 Coova.org</p>"
34: "ChilliSpot is an Open Source captive portal or wireless LAN access point "
35: "controller developed by the community at <a href=\"http://coova.org\">coova.org</a>. "
36: "It is licensed under the Gnu Public License (GPL). ";
37:
38: struct redir_socket{int fd[2];};
39: static unsigned char redir_radius_id=0;
40: static int redir_getparam(struct redir_t *redir, char *src, char *param, bstring dst);
41: extern time_t mainclock;
42:
43: /* Termination handler for clean shutdown */
44: static void redir_termination(int signum) {
45: if (optionsdebug) log_dbg("Terminating redir client!\n");
46: keep_going = 0;
47: }
48:
49: /* Alarm handler for ensured shutdown */
50: static void redir_alarm(int signum) {
51: log_warn(0, "Client process timed out: %d", termstate);
52: exit(0);
53: }
54:
55: /* Generate a 16 octet random challenge */
56: static int redir_challenge(unsigned char *dst) {
57: FILE *file;
58:
59: if ((file = fopen("/dev/urandom", "r")) == NULL) {
60: log_err(errno, "fopen(/dev/urandom, r) failed");
61: return -1;
62: }
63:
64: if (fread(dst, 1, REDIR_MD5LEN, file) != REDIR_MD5LEN) {
65: log_err(errno, "fread() failed");
66: return -1;
67: }
68:
69: fclose(file);
70: return 0;
71: }
72:
73: /* Convert 32+1 octet ASCII hex string to 16 octet unsigned char */
74: static int redir_hextochar(unsigned char *src, unsigned char * dst) {
75: char x[3];
76: int n;
77: int y;
78:
79: for (n=0; n< REDIR_MD5LEN; n++) {
80: x[0] = src[n*2+0];
81: x[1] = src[n*2+1];
82: x[2] = 0;
83: if (sscanf (x, "%2x", &y) != 1) {
84: log_err(0, "HEX conversion failed!");
85: return -1;
86: }
87: dst[n] = (unsigned char) y;
88: }
89:
90: return 0;
91: }
92:
93: /* Convert 16 octet unsigned char to 32+1 octet ASCII hex string */
94: static int redir_chartohex(unsigned char *src, char *dst) {
95: char x[3];
96: int n;
97:
98: for (n=0; n<REDIR_MD5LEN; n++) {
99: snprintf(x, 3, "%.2x", src[n]);
100: dst[n*2+0] = x[0];
101: dst[n*2+1] = x[1];
102: }
103:
104: dst[REDIR_MD5LEN*2] = 0;
105: return 0;
106: }
107:
108: static int redir_xmlencode(char *src, int srclen, char *dst, int dstsize) {
109: char *x;
110: int n;
111: int i = 0;
112:
113: for (n=0; n<srclen; n++) {
114: x=0;
115: switch(src[n]) {
116: case '&': x = "&"; break;
117: case '\"': x = """; break;
118: case '<': x = "<"; break;
119: case '>': x = ">"; break;
120: default:
121: if (i < dstsize - 1) dst[i++] = src[n];
122: break;
123: }
124: if (x) {
125: if (i < dstsize - strlen(x)) {
126: strncpy(dst + i, x, strlen(x));
127: i += strlen(x);
128: }
129: }
130: }
131: dst[i] = 0;
132: return 0;
133: }
134:
135: static int bstrtocstr(bstring src, char *dst, unsigned int len) {
136: int l;
137:
138: if (!src || src->slen == 0) {
139: strcpy(dst,"");
140: return 0;
141: }
142:
143: l = src->slen;
144: if (l > len) l = len;
145: strncpy(dst, (char*)src->data, len);
146: return 0;
147: }
148:
149: /* Encode src as urlencoded and place null terminated result in dst */
150: static int redir_urlencode(bstring src, bstring dst) {
151: char x[3];
152: int n;
153:
154: bassigncstr(dst, "");
155: for (n=0; n<src->slen; n++) {
156: if ((('A' <= src->data[n]) && (src->data[n] <= 'Z')) ||
157: (('a' <= src->data[n]) && (src->data[n] <= 'z')) ||
158: (('0' <= src->data[n]) && (src->data[n] <= '9')) ||
159: ('-' == src->data[n]) ||
160: ('_' == src->data[n]) ||
161: ('.' == src->data[n]) ||
162: ('!' == src->data[n]) ||
163: ('~' == src->data[n]) ||
164: ('*' == src->data[n])) {
165: bconchar(dst,src->data[n]);
166: }
167: else {
168: snprintf(x, 3, "%.2x", src->data[n]);
169: bconchar(dst, '%');
170: bconchar(dst, x[0]);
171: bconchar(dst, x[1]);
172: }
173: }
174: return 0;
175: }
176:
177: /* Decode urlencoded src and place null terminated result in dst */
178: static int redir_urldecode(bstring src, bstring dst) {
179: char x[3];
180: int n = 0;
181: unsigned int c;
182:
183: bassigncstr(dst, "");
184: while (n<src->slen) {
185: if (src->data[n] == '%') {
186: if ((n+2) < src->slen) {
187: x[0] = src->data[n+1];
188: x[1] = src->data[n+2];
189: x[2] = 0;
190: c = '_';
191: sscanf(x, "%x", &c);
192: bconchar(dst,c);
193: }
194: n += 3;
195: }
196: else {
197: bconchar(dst,src->data[n]);
198: n++;
199: }
200: }
201: return 0;
202: }
203:
204: /* Make an XML Reply */
205: static int redir_xmlreply(struct redir_t *redir,
206: struct redir_conn_t *conn, int res, long int timeleft, char* hexchal,
207: char* reply, char* redirurl, bstring b) {
208: bstring bt;
209:
210: if (redir->no_uamwispr &&
211: !(redir->chillixml)) return 0;
212:
213: bt = bfromcstr("");
214:
215: bcatcstr(b,
216: "<!--\r\n"
217: "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n");
218:
219: if (!redir->no_uamwispr) {
220: bcatcstr(b,
221: "<WISPAccessGatewayParam\r\n"
222: " xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\r\n"
223: " xsi:noNamespaceSchemaLocation=\"http://www.acmewisp.com/WISPAccessGatewayParam.xsd\""
224: ">\r\n");
225:
226: switch (res) {
227:
228: case REDIR_ALREADY:
229: bcatcstr(b,
230: "<AuthenticationPollReply>\r\n"
231: "<MessageType>140</MessageType>\r\n"
232: "<ResponseCode>102</ResponseCode>\r\n"
233: "<ReplyMessage>Already logged on</ReplyMessage>\r\n"
234: "</AuthenticationPollReply>\r\n");
235: break;
236:
237: case REDIR_FAILED_REJECT:
238: bcatcstr(b,
239: "<AuthenticationPollReply>\r\n"
240: "<MessageType>140</MessageType>\r\n"
241: "<ResponseCode>100</ResponseCode>\r\n");
242:
243: if (reply) {
244: bassignformat(bt, "<ReplyMessage>%s</ReplyMessage>\r\n", reply);
245: bconcat(b, bt);
246: }
247: else {
248: bcatcstr(b, "<ReplyMessage>Invalid Password</ReplyMessage>\r\n");
249: }
250:
251: bcatcstr(b, "</AuthenticationPollReply>\r\n");
252: break;
253:
254: case REDIR_FAILED_OTHER:
255: bcatcstr(b,
256: "<AuthenticationPollReply>\r\n"
257: "<MessageType>140</MessageType>\r\n"
258: "<ResponseCode>102</ResponseCode>\r\n");
259:
260: if (reply) {
261: bassignformat(bt, "<ReplyMessage>%s</ReplyMessage>\r\n", reply);
262: bconcat(b, bt);
263: }
264: else {
265: bcatcstr(b, "<ReplyMessage>Radius error</ReplyMessage>\r\n");
266: }
267:
268: bcatcstr(b, "</AuthenticationPollReply>\r\n");
269: break;
270:
271: case REDIR_SUCCESS:
272: bcatcstr(b,
273: "<AuthenticationPollReply>\r\n"
274: "<MessageType>140</MessageType>\r\n"
275: "<ResponseCode>50</ResponseCode>\r\n");
276:
277: if (reply) {
278: bassignformat(bt, "<ReplyMessage>%s</ReplyMessage>\r\n", reply);
279: bconcat(b, bt);
280: }
281:
282: bassignformat(bt, "<LogoffURL>http://%s:%d/logoff</LogoffURL>\r\n",
283: inet_ntoa(redir->addr), redir->port);
284: bconcat(b, bt);
285:
286: if (redirurl) {
287: bassignformat(bt, "<RedirectionURL>%s</RedirectionURL>\r\n", redirurl);
288: bconcat(b, bt);
289: }
290: bcatcstr(b, "</AuthenticationPollReply>\r\n");
291: break;
292:
293: case REDIR_LOGOFF:
294: bcatcstr(b,
295: "<LogoffReply>\r\n"
296: "<MessageType>130</MessageType>\r\n"
297: "<ResponseCode>150</ResponseCode>\r\n"
298: "</LogoffReply>\r\n");
299: break;
300:
301: case REDIR_SPLASH:
302: case REDIR_NOTYET:
303: bcatcstr(b,
304: "<Redirect>\r\n"
305: "<AccessProcedure>1.0</AccessProcedure>\r\n");
306:
307: if (redir->radiuslocationid) {
308: bassignformat(bt, "<AccessLocation>%s</AccessLocation>\r\n", redir->radiuslocationid);
309: bconcat(b, bt);
310: }
311:
312: if (redir->radiuslocationname) {
313: bassignformat(bt, "<LocationName>%s</LocationName>\r\n", redir->radiuslocationname);
314: bconcat(b, bt);
315: }
316:
317: bassignformat(bt, "<LoginURL>%s%cres=smartclient&uamip=%s&uamport=%d&challenge=%s</LoginURL>\r\n",
318: options.wisprlogin ? options.wisprlogin : redir->url,
319: strchr(options.wisprlogin ? options.wisprlogin : redir->url, '?') ? '&' : '?',
320: inet_ntoa(redir->addr), redir->port, hexchal);
321: bconcat(b, bt);
322:
323: bassignformat(bt, "<AbortLoginURL>http://%s:%d/abort</AbortLoginURL>\r\n",
324: inet_ntoa(redir->addr), redir->port);
325: bconcat(b, bt);
326:
327: bcatcstr(b,
328: "<MessageType>100</MessageType>\r\n"
329: "<ResponseCode>0</ResponseCode>\r\n"
330: "</Redirect>\r\n");
331: break;
332:
333: case REDIR_ABORT_ACK:
334: bcatcstr(b,
335: "<AbortLoginReply>\r\n"
336: "<MessageType>150</MessageType>\r\n"
337: "<ResponseCode>151</ResponseCode>\r\n"
338: "</AbortLoginReply>\r\n");
339: break;
340:
341: case REDIR_ABORT_NAK:
342: bcatcstr(b,
343: "<AbortLoginReply>\r\n"
344: "<MessageType>150</MessageType>\r\n"
345: "<ResponseCode>50</ResponseCode>\r\n");
346: bassignformat(bt, "<LogoffURL>http://%s:%d/logoff</LogoffURL>\r\n",
347: inet_ntoa(redir->addr), redir->port);
348: bconcat(b, bt);
349: bcatcstr(b, "</AbortLoginReply>\r\n");
350: break;
351:
352: case REDIR_STATUS:
353: bcatcstr(b,
354: "<AuthenticationPollReply>\r\n"
355: "<MessageType>140</MessageType>\r\n");
356: if (conn->s_state.authenticated != 1) {
357: bcatcstr(b,
358: "<ResponseCode>150</ResponseCode>\r\n"
359: "<ReplyMessage>Not logged on</ReplyMessage>\r\n");
360: } else {
361: bcatcstr(b,
362: "<ResponseCode>50</ResponseCode>\r\n"
363: "<ReplyMessage>Already logged on</ReplyMessage>\r\n");
364: }
365: bcatcstr(b, "</AuthenticationPollReply>\r\n");
366: break;
367:
368: default:
369: log_err(0, "Unknown res in switch");
370: bdestroy(bt);
371: return -1;
372:
373: }
374: bcatcstr(b, "</WISPAccessGatewayParam>\r\n");
375: }
376:
377: if (redir->chillixml) {
378: bcatcstr(b, "<ChilliSpotSession>\r\n");
379: switch (res) {
380: case REDIR_SPLASH:
381: case REDIR_NOTYET:
382: bassignformat(bt, "<Challenge>%s</Challenge>\r\n", hexchal);
383: bconcat(b, bt);
384: break;
385: case REDIR_STATUS:
386: if (conn->s_state.authenticated == 1) {
387: time_t timenow = time(0);
388: uint32_t sessiontime;
389:
390: sessiontime = timenow - conn->s_state.start_time;
391:
392: bcatcstr(b, "<State>1</State>\r\n");
393:
394: bassignformat(bt, "<StartTime>%d</StartTime>\r\n" , conn->s_state.start_time);
395: bconcat(b, bt);
396:
397: bassignformat(bt, "<SessionTime>%d</SessionTime>\r\n", sessiontime);
398: bconcat(b, bt);
399:
400: if (timeleft) {
401: bassignformat(bt, "<TimeLeft>%d</TimeLeft>\r\n", timeleft);
402: bconcat(b, bt);
403: }
404:
405: bassignformat(bt, "<Timeout>%d</Timeout>\r\n", conn->s_params.sessiontimeout);
406: bconcat(b, bt);
407:
408: bassignformat(bt, "<InputOctets>%d</InputOctets>\r\n", conn->s_state.input_octets);
409: bconcat(b, bt);
410:
411: bassignformat(bt, "<OutputOctets>%d</OutputOctets>\r\n", conn->s_state.output_octets);
412: bconcat(b, bt);
413:
414: bassignformat(bt, "<MaxInputOctets>%d</MaxInputOctets>\r\n", conn->s_params.maxinputoctets);
415: bconcat(b, bt);
416:
417: bassignformat(bt, "<MaxOutputOctets>%d</MaxOutputOctets>\r\n", conn->s_params.maxoutputoctets);
418: bconcat(b, bt);
419:
420: bassignformat(bt, "<MaxTotalOctets>%d</MaxTotalOctets>\r\n", conn->s_params.maxtotaloctets);
421: bconcat(b, bt);
422: }
423: else {
424: bcatcstr(b, "<State>0</State>\r\n");
425: }
426:
427: break;
428:
429: case REDIR_ALREADY:
430: bcatcstr(b, "<Already>1</Already>\r\n");
431: break;
432:
433: case REDIR_FAILED_REJECT:
434: case REDIR_FAILED_OTHER:
435: if (reply) {
436: bassignformat(bt, "<ReplyMessage>%s</ReplyMessage>\r\n", reply);
437: bconcat(b, bt);
438: }
439: bcatcstr(b, "<State>0</State>\r\n");
440:
441: break;
442: case REDIR_SUCCESS:
443: if (reply) {
444: bassignformat(bt, "<ReplyMessage>%s</ReplyMessage>\r\n", reply);
445: bconcat(b, bt);
446: }
447: bcatcstr(b, "<State>1</State>\r\n");
448: break;
449: case REDIR_LOGOFF:
450: bcatcstr(b, "<State>0</State>\r\n");
451: break;
452: case REDIR_ABORT_ACK:
453: bcatcstr(b, "<Abort_ack>1</Abort_ack>\r\n");
454: break;
455: case REDIR_ABORT_NAK:
456: bcatcstr(b, "<Abort_nak>1</Abort_nak>\r\n");
457: break;
458: default:
459: log_err(0, "Unknown res in switch");
460: bdestroy(bt);
461: return -1;
462: }
463: bcatcstr(b, "</ChilliSpotSession>\r\n");
464: }
465:
466: bcatcstr(b, "-->\r\n");
467: bdestroy(bt);
468: return 0;
469: }
470:
471: static int redir_buildurl(struct redir_conn_t *conn, bstring str,
472: struct redir_t *redir, char *resp,
473: long int timeleft, char* hexchal, char* uid,
474: char* userurl, char* reply, char* redirurl,
475: uint8_t *hismac, struct in_addr *hisip) {
476: char *redir_url = redir->url;
477: bstring bt = bfromcstr("");
478: bstring bt2 = bfromcstr("");
479:
480: if ((conn->s_params.flags & REQUIRE_UAM_SPLASH) &&
481: conn->s_params.url[0]) {
482: redir_url = conn->s_params.url;
483: }
484:
485: bassignformat(str, "%s%cres=%s&uamip=%s&uamport=%d",
486: redir_url, strchr(redir_url, '?') ? '&' : '?',
487: resp, inet_ntoa(redir->addr), redir->port);
488:
489: if (hexchal) {
490: bassignformat(bt, "&challenge=%s", hexchal);
491: bconcat(str, bt);
492: bassigncstr(bt,"");
493: }
494:
495: if (conn->type == REDIR_STATUS) {
496: int starttime = conn->s_state.start_time;
497: if (starttime) {
498: int sessiontime;
499: time_t timenow = time(0);
500:
501: sessiontime = timenow - starttime;
502:
503: bassignformat(bt, "&starttime=%ld", starttime);
504: bconcat(str, bt);
505: bassignformat(bt, "&sessiontime=%ld", sessiontime);
506: bconcat(str, bt);
507: }
508:
509: if (conn->s_params.sessiontimeout) {
510: bassignformat(bt, "&sessiontimeout=%ld", conn->s_params.sessiontimeout);
511: bconcat(str, bt);
512: }
513:
514: if (conn->s_params.sessionterminatetime) {
515: bassignformat(bt, "&stoptime=%ld", conn->s_params.sessionterminatetime);
516: bconcat(str, bt);
517: }
518: }
519:
520: if (uid) {
521: bcatcstr(str, "&uid=");
522: bassigncstr(bt, uid);
523: redir_urlencode(bt, bt2);
524: bconcat(str, bt2);
525: }
526:
527: if (timeleft) {
528: bassignformat(bt, "&timeleft=%ld", timeleft);
529: bconcat(str, bt);
530: }
531:
532: if (hismac) {
533: bcatcstr(str, "&mac=");
534: bassignformat(bt, "%.2X-%.2X-%.2X-%.2X-%.2X-%.2X",
535: hismac[0], hismac[1],
536: hismac[2], hismac[3],
537: hismac[4], hismac[5]);
538: redir_urlencode(bt, bt2);
539: bconcat(str, bt2);
540: }
541:
542: if (hisip) {
543: bassignformat(bt, "&ip=%s", inet_ntoa(*hisip));
544: bconcat(str, bt);
545: }
546:
547: if (reply) {
548: bcatcstr(str, "&reply=");
549: bassigncstr(bt, reply);
550: redir_urlencode(bt, bt2);
551: bconcat(str, bt2);
552: }
553:
554: if (redir->ssid) {
555: bcatcstr(str, "&ssid=");
556: bassigncstr(bt, redir->ssid);
557: redir_urlencode(bt, bt2);
558: bconcat(str, bt2);
559: }
560:
561: if (redir->nasmac) {
562: bcatcstr(str, "&called=");
563: bassigncstr(bt, redir->nasmac);
564: redir_urlencode(bt, bt2);
565: bconcat(str, bt2);
566: }
567:
568: if (redir->radiusnasid) {
569: bcatcstr(str, "&nasid=");
570: bassigncstr(bt, redir->radiusnasid);
571: redir_urlencode(bt, bt2);
572: bconcat(str, bt2);
573: }
574:
575: if (conn->lang[0]) {
576: bcatcstr(str, "&lang=");
577: bassigncstr(bt, conn->lang);
578: redir_urlencode(bt, bt2);
579: bconcat(str, bt2);
580: }
581:
582: if (redirurl) {
583: bcatcstr(str, "&redirurl=");
584: bassigncstr(bt, redirurl);
585: redir_urlencode(bt, bt2);
586: bconcat(str, bt2);
587: }
588:
589: if (userurl) {
590: bcatcstr(str, "&userurl=");
591: bassigncstr(bt, userurl);
592: redir_urlencode(bt, bt2);
593: bconcat(str, bt2);
594: }
595:
596: if (redir->secret && *redir->secret) { /* take the md5 of the url+uamsecret as a checksum */
597: MD5_CTX context;
598: unsigned char cksum[16];
599: char hex[32+1];
600: int i;
601:
602: MD5Init(&context);
603: MD5Update(&context, (uint8_t*)str->data, str->slen);
604: MD5Update(&context, (uint8_t*)redir->secret, strlen(redir->secret));
605: MD5Final(cksum, &context);
606:
607: hex[0]=0;
608: for (i=0; i<16; i++)
609: sprintf(hex+strlen(hex), "%.2X", cksum[i]);
610:
611: bcatcstr(str, "&md=");
612: bcatcstr(str, hex);
613: }
614:
615: bdestroy(bt);
616: bdestroy(bt2);
617: return 0;
618: }
619:
620: ssize_t
621: tcp_write_timeout(int timeout, struct redir_socket *sock, char *buf, size_t len) {
622: fd_set fdset;
623: struct timeval tv;
624: int fd = sock->fd[1];
625:
626: FD_ZERO(&fdset);
627: FD_SET(fd,&fdset);
628:
629: tv.tv_sec = timeout;
630: tv.tv_usec = 0;
631:
632: if (select(fd + 1,(fd_set *) 0,&fdset,(fd_set *) 0,&tv) == -1)
633: return -1;
634:
635: if (FD_ISSET(fd, &fdset))
636: #if WIN32
637: return send(fd,buf,len,0);
638: #else
639: return write(fd,buf,len);
640: #endif
641:
642: return -1;
643: }
644:
645: static int timeout = 10;
646:
647: ssize_t
648: tcp_write(struct redir_socket *sock, char *buf, size_t len) {
649: ssize_t c;
650: size_t r = 0;
651: while (r < len) {
652: c = tcp_write_timeout(timeout, sock, buf+r, len-r);
653: if (c <= 0) return (ssize_t)r;
654: r += (size_t)c;
655: }
656: return (ssize_t)r;
657: }
658:
659: static int redir_json_reply(struct redir_t *redir, int res, struct redir_conn_t *conn,
660: char *hexchal, char *userurl, char *redirurl, uint8_t *hismac,
661: char *reply, char *qs, bstring s) {
662: bstring tmp = bfromcstr("");
663: bstring json = bfromcstr("");
664:
665: unsigned char flg = 0;
666: #define FLG_cb 1
667: #define FLG_chlg 2
668: #define FLG_sess 4
669: #define FLG_loc 8
670: #define FLG_redir 16
671:
672: int state = conn->s_state.authenticated;
673: int splash = (conn->s_params.flags & REQUIRE_UAM_SPLASH) == REQUIRE_UAM_SPLASH;
674:
675: redir_getparam(redir, qs, "callback", tmp);
676:
677: if (tmp->slen) {
678: bconcat(json, tmp);
679: bcatcstr(json, "(");
680: flg |= FLG_cb;
681: }
682:
683: switch (res) {
684: case REDIR_ALREADY:
685: flg |= FLG_sess;
686: break;
687:
688: case REDIR_FAILED_REJECT:
689: case REDIR_FAILED_OTHER:
690: flg |= FLG_chlg;
691: flg |= FLG_redir;
692: break;
693:
694: case REDIR_SUCCESS:
695: flg |= FLG_sess;
696: flg |= FLG_redir;
697: state = 1;
698: break;
699:
700: case REDIR_LOGOFF:
701: flg |= FLG_sess | FLG_chlg;
702: break;
703:
704: case REDIR_SPLASH:
705: case REDIR_NOTYET:
706: flg |= FLG_chlg;
707: flg |= FLG_loc;
708: flg |= FLG_redir;
709: break;
710:
711: case REDIR_ABORT_ACK:
712: case REDIR_ABORT_NAK:
713: case REDIR_ABOUT:
714: break;
715:
716: case REDIR_STATUS:
717: if (state && !splash) {
718: flg |= FLG_sess;
719: } else {
720: flg |= FLG_chlg;
721: flg |= FLG_loc;
722: }
723: flg |= FLG_redir;
724: break;
725:
726: default:
727: break;
728: }
729:
730: if (state && splash)
731: state = 3;
732:
733: bcatcstr(json, "{\"version\":\"1.0\",\"clientState\":");
734:
735: bassignformat(tmp, "%d", state);
736: bconcat(json, tmp);
737:
738: if (reply) {
739: bcatcstr(json, ",\"message\":\"");
740: bcatcstr(json, reply);
741: bcatcstr(json, "\"");
742: }
743:
744: if ((flg & FLG_chlg) && hexchal) {
745: bcatcstr(json, ",\"challenge\":\"");
746: bcatcstr(json, hexchal);
747: bcatcstr(json, "\"");
748: }
749:
750: if (flg & FLG_loc) {
751: bcatcstr(json,",\"location\":{\"name\":\"");
752: if (redir->locationname)
753: bcatcstr(json, redir->locationname);
754: else if (redir->radiuslocationname)
755: bcatcstr(json, redir->radiuslocationname);
756: bcatcstr(json,"\"");
757: bcatcstr(json,"}");
758: }
759:
760: if (flg & FLG_redir)
761: session_redir_json_fmt(json, userurl, redirurl, hismac);
762:
763: if (flg & FLG_sess)
764: session_json_fmt(&conn->s_state, &conn->s_params,
765: json, res == REDIR_SUCCESS);
766:
767: bcatcstr(json, "}");
768:
769: if (flg & FLG_cb) {
770: bcatcstr(json, ")");
771: }
772:
773: bassigncstr(s, "HTTP/1.1 200 OK\r\n");
774: bcatcstr(s, "Cache-Control: no-cache, must-revalidate\r\n");
775:
776: bcatcstr(s, "Content-Length: ");
777: bassignformat(tmp , "%ld", blength(json) );
778: bconcat(s, tmp);
779:
780: bcatcstr(s, "\r\nContent-type: ");
781: if (tmp->slen) bcatcstr(s, "text/javascript");
782: else bcatcstr(s, "application/json");
783:
784: bcatcstr(s, "\r\n\r\n");
785: bconcat(s, json);
786:
787: if (options.debug) {
788: log_dbg("sending json: %s\n", json->data);
789: }
790:
791: bdestroy(json);
792: bdestroy(tmp);
793:
794: return 0;
795: }
796:
797: /* Make an HTTP redirection reply and send it to the client */
798: static int redir_reply(struct redir_t *redir, struct redir_socket *sock,
799: struct redir_conn_t *conn, int res, bstring url,
800: long int timeleft, char* hexchal, char* uid,
801: char* userurl, char* reply, char* redirurl,
802: uint8_t *hismac, struct in_addr *hisip, char *qs) {
803:
804: char *resp = NULL;
805: bstring buffer;
806:
807: switch (res) {
808: case REDIR_ALREADY:
809: resp = "already";
810: break;
811: case REDIR_FAILED_REJECT:
812: case REDIR_FAILED_OTHER:
813: resp = "failed";
814: break;
815: case REDIR_SUCCESS:
816: resp = "success";
817: break;
818: case REDIR_LOGOFF:
819: resp = "logoff";
820: break;
821: case REDIR_NOTYET:
822: resp = "notyet";
823: break;
824: case REDIR_SPLASH:
825: resp = "splash";
826: break;
827: case REDIR_ABORT_ACK:
828: resp = "logoff";
829: break;
830: case REDIR_ABORT_NAK:
831: resp = "already";
832: break;
833: case REDIR_ABOUT:
834: case REDIR_ABORT:
835: break;
836: case REDIR_STATUS:
837: resp = conn->s_state.authenticated == 1 ? "already" : "notyet";
838: break;
839: default:
840: log_err(0, "Unknown res in switch");
841: return -1;
842: }
843:
844: buffer = bfromcstralloc(1024, "");
845:
846: if (conn->format == REDIR_FMT_JSON) {
847:
848: redir_json_reply(redir, res, conn, hexchal, userurl, redirurl, hismac, reply, qs, buffer);
849:
850: } else if (resp) {
851: bcatcstr(buffer, "HTTP/1.0 302 Moved Temporarily\r\nLocation: ");
852:
853: if (url) {
854: bconcat(buffer, url);
855: } else {
856: bstring bt = bfromcstralloc(1024,"");
857: if (redir_buildurl(conn, bt, redir, resp, timeleft, hexchal,
858: uid, userurl, reply, redirurl, hismac, hisip) == -1) {
859: bdestroy(bt);
860: bdestroy(buffer);
861: return -1;
862: }
863: log_dbg("here: %s\n", bt->data);
864: bconcat(buffer, bt);
865: bdestroy(bt);
866: }
867:
868: bcatcstr(buffer,
869: "\r\n\r\n<HTML><BODY><H2>Browser error!</H2>"
870: "Browser does not support redirects!</BODY>\r\n");
871:
872: redir_xmlreply(redir, conn, res, timeleft, hexchal, reply, redirurl, buffer);
873:
874: bcatcstr(buffer, "\r\n</HTML>\r\n");
875:
876: } else {
877: bassigncstr(buffer,
878: "HTTP/1.0 200 OK\r\nContent-type: text/html\r\n\r\n"
879: "<HTML><HEAD><TITLE>CoovaChilli</TITLE></HEAD><BODY>");
880: bcatcstr(buffer, credits);
881: bcatcstr(buffer, "</BODY></HTML>\r\n");
882: }
883:
884: if (tcp_write(sock, (char*)buffer->data, buffer->slen) < 0) {
885: log_err(errno, "tcp_write() failed!");
886: bdestroy(buffer);
887: return -1;
888: }
889:
890: bdestroy(buffer);
891: return 0;
892: }
893:
894: /* Allocate new instance of redir */
895: int redir_new(struct redir_t **redir,
896: struct in_addr *addr, int port, int uiport) {
897: struct sockaddr_in address;
898: int optval = 1;
899: int n = 0;
900:
901: if (!(*redir = calloc(1, sizeof(struct redir_t)))) {
902: log_err(errno, "calloc() failed");
903: return EOF;
904: }
905:
906: (*redir)->addr = *addr;
907: (*redir)->port = port;
908: (*redir)->uiport = uiport;
909: (*redir)->starttime = 0;
910:
911: if (((*redir)->fd[0] = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
912: log_err(errno, "socket() failed");
913: return -1;
914: }
915:
916: if (uiport && ((*redir)->fd[1] = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
917: log_err(errno, "socket() failed");
918: return -1;
919: }
920:
921: /* Set up address */
922: address.sin_family = AF_INET;
923: #if defined(__FreeBSD__) || defined (__APPLE__) || defined (__OpenBSD__) || defined (__NetBSD__)
924: address.sin_len = sizeof (struct sockaddr_in);
925: #endif
926:
927: for (n = 0; n < 2 && (*redir)->fd[n]; n++) {
928:
929: switch(n) {
930: case 0:
931: address.sin_addr.s_addr = addr->s_addr;
932: address.sin_port = htons(port);
933: break;
934: case 1:
935: /* XXX: binding to 0.0.0.0:uiport (should be configurable?) */
936: address.sin_addr.s_addr = INADDR_ANY;
937: address.sin_port = htons(uiport);
938: break;
939: }
940:
941: if (setsockopt((*redir)->fd[n], SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval))) {
942: log_err(errno, "setsockopt() failed");
943: close((*redir)->fd[n]);
944: (*redir)->fd[n]=0;
945: break;
946: }
947:
948: /* TODO: FreeBSD?
949: if (setsockopt((*redir)->fd, SOL_SOCKET, SO_REUSEPORT, &optval, sizeof(optval))) {
950: log_err(errno, "setsockopt() failed");
951: close((*redir)->fd);
952: return -1;
953: }
954: */
955:
956: while (bind((*redir)->fd[n], (struct sockaddr *)&address, sizeof(address))) {
957: if ((EADDRINUSE == errno) && (10 > n++)) {
958: log_warn(0, "UAM port already in use. Waiting for retry.");
959: if (sleep(30)) { /* In case we got killed */
960: close((*redir)->fd[n]);
961: (*redir)->fd[n]=0;
962: break;
963: }
964: }
965: else {
966: log_err(errno, "bind() failed");
967: close((*redir)->fd[n]);
968: (*redir)->fd[n]=0;
969: break;
970: }
971: }
972:
973: if (listen((*redir)->fd[n], REDIR_MAXLISTEN)) {
974: log_err(errno, "listen() failed");
975: close((*redir)->fd[n]);
976: (*redir)->fd[n]=0;
977: break;
978: }
979: }
980:
981: if (((*redir)->msgid = msgget(IPC_PRIVATE, 0)) < 0) {
982: log_err(errno, "msgget() failed");
983: log_err(0, "Most likely your computer does not have System V IPC installed");
984: return -1;
985: }
986:
987: return 0;
988: }
989:
990:
991: /* Free instance of redir */
992: int redir_free(struct redir_t *redir) {
993: int n;
994: for (n = 0; n < 2 && redir->fd[n]; n++) {
995: if (close(redir->fd[n])) {
996: log_err(errno, "close() failed");
997: }
998: }
999:
1000: if (msgctl(redir->msgid, IPC_RMID, NULL)) {
1001: log_err(errno, "msgctl() failed");
1002: }
1003:
1004: free(redir);
1005: return 0;
1006: }
1007:
1008: /* Set redir parameters */
1009: void redir_set(struct redir_t *redir, int debug) {
1010: optionsdebug = debug; /* TODO: Do not change static variable from instance */
1011: redir->debug = debug;
1012: redir->no_uamsuccess = options.no_uamsuccess;
1013: redir->no_uamwispr = options.no_uamwispr;
1014: redir->chillixml = options.chillixml;
1015: redir->url = options.uamurl;
1016: redir->homepage = options.uamhomepage;
1017: redir->secret = options.uamsecret;
1018: redir->ssid = options.ssid;
1019: redir->nasmac = options.nasmac;
1020: redir->nasip = options.nasip;
1021: redir->radiusserver0 = options.radiusserver1;
1022: redir->radiusserver1 = options.radiusserver2;
1023: redir->radiusauthport = options.radiusauthport;
1024: redir->radiusacctport = options.radiusacctport;
1025: redir->radiussecret = options.radiussecret;
1026: redir->radiusnasid = options.radiusnasid;
1027: redir->radiuslocationid = options.radiuslocationid;
1028: redir->radiuslocationname = options.radiuslocationname;
1029: redir->locationname = options.locationname;
1030: redir->radiusnasporttype = options.radiusnasporttype;
1031: return;
1032: }
1033:
1034: /* Get a parameter of an HTTP request. Parameter is url decoded */
1035: /* TODO: Should be merged with other parsers */
1036: static int redir_getparam(struct redir_t *redir, char *src, char *param, bstring dst) {
1037: char *p1;
1038: char *p2;
1039: char sstr[255];
1040: int len = 0;
1041:
1042: strncpy(sstr, param, sizeof(sstr));
1043: sstr[sizeof(sstr)-1] = 0;
1044: strncat(sstr, "=", sizeof(sstr));
1045: sstr[sizeof(sstr)-1] = 0;
1046:
1047: if (!(p1 = strcasestr(src, sstr))) return -1;
1048: p1 += strlen(sstr);
1049:
1050: /* The parameter ends with a & or null */
1051: p2 = strstr(p1, "&");
1052:
1053: if (p2) len = p2 - p1;
1054: else len = strlen(p1);
1055:
1056: if (len) {
1057: bstring s = blk2bstr(p1, len);
1058: redir_urldecode(s, dst);
1059: bdestroy(s);
1060: } else
1061: bassigncstr(dst, "");
1062:
1063: log_dbg("The parameter %s is: [%.*s]", param, dst->slen, dst->data);/**/
1064:
1065: return 0;
1066: }
1067:
1068: /* Read the an HTTP request from a client */
1069: /* If POST is allowed, 1 is the input value of ispost */
1070: static int redir_getreq(struct redir_t *redir, struct redir_socket *sock,
1071: struct redir_conn_t *conn, int *ispost, size_t *clen,
1072: char *qs, size_t qslen) {
1073: int fd = sock->fd[0];
1074: fd_set fds;
1075: struct timeval idleTime;
1076: int status;
1077: ssize_t recvlen = 0;
1078: size_t buflen = 0;
1079: char buffer[REDIR_MAXBUFFER];
1080: char host[256];
1081: char path[256];
1082: int i, lines=0, done=0;
1083: char *eol;
1084:
1085: memset(buffer, 0, sizeof(buffer));
1086: memset(host, 0, sizeof(host));
1087: memset(path, 0, sizeof(path));
1088:
1089: /* read whatever the client send to us */
1090: while (!done && (redir->starttime + REDIR_HTTP_MAX_TIME) > time(NULL)) {
1091: FD_ZERO(&fds);
1092: FD_SET(fd, &fds);
1093:
1094: idleTime.tv_sec = 0;
1095: idleTime.tv_usec = REDIR_HTTP_SELECT_TIME;
1096:
1097: switch (status = select(fd + 1, &fds, NULL, NULL, &idleTime)) {
1098: case -1:
1099: log_err(errno,"select() returned -1!");
1100: return -1;
1101: case 0:
1102: log_dbg("HTTP request timeout!");
1103: return -1;
1104: default:
1105: break;
1106: }
1107:
1108: if ((status > 0) && FD_ISSET(fd, &fds)) {
1109: if (buflen + 2 >= sizeof(buffer)) { /* ensure space for a least one more byte + null */
1110: log_err(0, "Too much data in http request!");
1111: return -1;
1112: }
1113:
1114: /* if post is allowed, we do not buffer on the read (to not eat post data) */
1115: if ((recvlen = recv(fd, buffer + buflen, (*ispost) ? 1 : sizeof(buffer) - 1 - buflen, 0)) < 0) {
1116: if (errno != ECONNRESET)
1117: log_err(errno, "recv() failed!");
1118: return -1;
1119: }
1120:
1121: if (recvlen == 0) done=1;
1122: buflen += recvlen;
1123: buffer[buflen] = 0;
1124: }
1125:
1126: if (buflen == 0) {
1127: log_dbg("No data in HTTP request!");
1128: return -1;
1129: }
1130:
1131: while ((eol = strstr(buffer, "\r\n"))) {
1132: size_t linelen = eol - buffer;
1133: *eol = 0;
1134:
1135: if (lines++ == 0) { /* first line */
1136: size_t dstlen = 0;
1137: char *p1 = buffer;
1138: char *p2;
1139:
1140: if (optionsdebug)
1141: log_dbg("http-request: %s", buffer);
1142:
1143: if (!strncmp("GET ", p1, 4)) { p1 += 4; *ispost = 0; }
1144: else if (!strncmp("HEAD ", p1, 5)) { p1 += 5; *ispost = 0; }
1145: else if ((*ispost) &&
1146: !strncmp("POST ", p1, 5)) { p1 += 5; *ispost = 1; }
1147: else {
1148: if (optionsdebug)
1149: log_dbg("Unhandled http request: %s", buffer);
1150: return -1;
1151: }
1152:
1153: while (*p1 == ' ') p1++; /* Advance through additional white space */
1154: if (*p1 == '/') p1++;
1155: else return -1;
1156:
1157: /* The path ends with a ? or a space */
1158: p2 = strchr(p1, '?');
1159: if (!p2) p2 = strchr(p1, ' ');
1160: if (!p2) return -1;
1161: dstlen = p2 - p1;
1162:
1163: if (dstlen >= sizeof(path)-1)
1164: dstlen = sizeof(path)-1;
1165:
1166: strncpy(path, p1, dstlen);
1167:
1168: if (optionsdebug)
1169: log_dbg("The path: %s", path);
1170:
1171: /* TODO: Should also check the Host: to make sure we are talking directly to uamlisten */
1172:
1173: if (!strncmp(path, "json/", 5) && strlen(path) > 6) {
1174: int i, last=strlen(path)-5;
1175:
1176: conn->format = REDIR_FMT_JSON;
1177:
1178: for (i=0; i < last; i++)
1179: path[i] = path[i+5];
1180:
1181: path[last]=0;
1182:
1183: log_dbg("The (json format) path: %s", path);
1184: }
1185:
1186: if ((!strcmp(path, "logon")) || (!strcmp(path, "login")))
1187: conn->type = REDIR_LOGIN;
1188: else if ((!strcmp(path, "logoff")) || (!strcmp(path, "logout")))
1189: conn->type = REDIR_LOGOUT;
1190: else if (!strncmp(path, "www/", 4) && strlen(path) > 4)
1191: conn->type = REDIR_WWW;
1192: else if (!strcmp(path, "status"))
1193: conn->type = REDIR_STATUS;
1194: else if (!strncmp(path, "msdownload", 10))
1195: { conn->type = REDIR_MSDOWNLOAD; return 0; }
1196: else if (!strcmp(path, "prelogin"))
1197: { conn->type = REDIR_PRELOGIN; return 0; }
1198: else if (!strcmp(path, "abort"))
1199: { conn->type = REDIR_ABORT; return 0; }
1200:
1201: if (*p2 == '?') {
1202: p1 = p2 + 1;
1203: p2 = strchr(p1, ' ');
1204:
1205: if (p2) {
1206: dstlen = p2 - p1;
1207:
1208: if (dstlen >= qslen-1)
1209: dstlen = qslen-1;
1210:
1211: strncpy(qs, p1, dstlen);
1212:
1213: if (optionsdebug)
1214: log_dbg("Query string: %s", qs);
1215: }
1216: }
1217: } else if (linelen == 0) {
1218: /* end of headers */
1219: /*log_dbg("end of http-request");*/
1220: done = 1;
1221: break;
1222: } else {
1223: /* headers */
1224: char *p;
1225: size_t len;
1226:
1227: if (!strncasecmp(buffer,"Host:",5)) {
1228: p = buffer + 5;
1229: while (*p && isspace(*p)) p++;
1230: len = strlen(p);
1231: if (len >= sizeof(host)-1)
1232: len = sizeof(host)-1;
1233: strncpy(host, p, len);
1234: host[len]=0;
1235: if (optionsdebug)
1236: log_dbg("Host: %s",host);
1237: }
1238: else if (!strncasecmp(buffer,"Content-Length:",15)) {
1239: p = buffer + 15;
1240: while (*p && isspace(*p)) p++;
1241: len = strlen(p);
1242: if (len > 0) *clen = atoi(p);
1243: if (optionsdebug)
1244: log_dbg("Content-Length: %s",p);
1245: }
1246: else if (!strncasecmp(buffer,"User-Agent:",11)) {
1247: p = buffer + 11;
1248: while (*p && isspace(*p)) p++;
1249: len = strlen(p);
1250: if (len >= sizeof(conn->useragent)-1)
1251: len = sizeof(conn->useragent)-1;
1252: strncpy(conn->useragent, p, len);
1253: conn->useragent[len]=0;
1254: if (optionsdebug)
1255: log_dbg("User-Agent: %s",conn->useragent);
1256: }
1257: }
1258:
1259: /* shift buffer */
1260: linelen += 2;
1261: for (i = 0; i < (int)(buflen - linelen); i++)
1262: buffer[i] = buffer[(int)linelen+i];
1263:
1264: buflen -= linelen;
1265: }
1266: }
1267:
1268: switch(conn->type) {
1269:
1270: case REDIR_STATUS:
1271: return 0;
1272:
1273: case REDIR_LOGIN:
1274: {
1275: bstring bt = bfromcstr("");
1276:
1277: if (!redir_getparam(redir, qs, "lang", bt))
1278: bstrtocstr(bt, conn->lang, sizeof(conn->lang));
1279:
1280: if (!redir_getparam(redir, qs, "ident", bt) && bt->slen)
1281: conn->chap_ident = atoi((char*)bt->data);
1282:
1283: if (redir_getparam(redir, qs, "username", bt)) {
1284: log_err(0, "No username found in login request");
1285: bdestroy(bt);
1286: return -1;
1287: }
1288:
1289: bstrtocstr(bt, conn->s_state.redir.username, sizeof(conn->s_state.redir.username));
1290: log_dbg("-->> Setting username=[%s]",conn->s_state.redir.username);
1291:
1292: if (!redir_getparam(redir, qs, "userurl", bt)) {
1293: bstring bt2 = bfromcstr("");
1294: redir_urldecode(bt, bt2);
1295: bstrtocstr(bt2, conn->s_state.redir.userurl, sizeof(conn->s_state.redir.userurl));
1296: if (optionsdebug)
1297: log_dbg("-->> Setting userurl=[%s]",conn->s_state.redir.userurl);
1298: bdestroy(bt2);
1299: }
1300:
1301: if (!redir_getparam(redir, qs, "response", bt)) {
1302: redir_hextochar(bt->data, conn->chappassword);
1303: conn->chap = 1;
1304: conn->password[0] = 0;
1305: }
1306: else if (!redir_getparam(redir, qs, "password", bt)) {
1307: redir_hextochar(bt->data, conn->password);
1308: conn->chap = 0;
1309: conn->chappassword[0] = 0;
1310: } else {
1311: if (optionsdebug)
1312: log_dbg("No password found!");
1313: bdestroy(bt);
1314: return -1;
1315: }
1316: bdestroy(bt);
1317: }
1318: break;
1319:
1320: case REDIR_LOGOUT:
1321: case REDIR_PRELOGIN:
1322: {
1323: bstring bt = bfromcstr("");
1324: if (!redir_getparam(redir, qs, "userurl", bt)) {
1325: bstring bt2 = bfromcstr("");
1326: redir_urldecode(bt, bt2);
1327: bstrtocstr(bt2, conn->s_state.redir.userurl, sizeof(conn->s_state.redir.userurl));
1328: if (optionsdebug)
1329: log_dbg("-->> Setting userurl=[%s]",conn->s_state.redir.userurl);
1330: bdestroy(bt2);
1331: }
1332: bdestroy(bt);
1333: }
1334: break;
1335:
1336: case REDIR_WWW:
1337: {
1338: bstring bt = bfromcstr(path+4);
1339: bstring bt2 = bfromcstr("");
1340: redir_urldecode(bt, bt2);
1341: bstrtocstr(bt2,conn->wwwfile, sizeof(conn->wwwfile));
1342: if (optionsdebug)
1343: log_dbg("Serving file %s", conn->wwwfile);
1344: bdestroy(bt2);
1345: bdestroy(bt);
1346: }
1347: break;
1348:
1349: default:
1350: {
1351: /* some basic checks for urls we don't care about */
1352:
1353: snprintf(conn->s_state.redir.userurl, sizeof(conn->s_state.redir.userurl), "http://%s/%s%s%s",
1354: host, path, qs[0] ? "?" : "", qs[0] ? qs : "");
1355:
1356: if (optionsdebug)
1357: log_dbg("-->> Setting userurl=[%s]",conn->s_state.redir.userurl);
1358: }
1359: break;
1360:
1361: }
1362:
1363: return 0;
1364: }
1365:
1366: /* Radius callback when access accept/reject/challenge has been received */
1367: static int redir_cb_radius_auth_conf(struct radius_t *radius,
1368: struct radius_packet_t *pack,
1369: struct radius_packet_t *pack_req, void *cbp) {
1370: struct redir_conn_t *conn = (struct redir_conn_t*) cbp;
1371: struct radius_attr_t *stateattr = NULL;
1372: struct radius_attr_t *classattr = NULL;
1373: struct radius_attr_t *attr = NULL;
1374: char attrs[RADIUS_ATTR_VLEN+1];
1375:
1376: if (optionsdebug)
1377: log_dbg("Received access request confirmation from radius server\n");
1378:
1379: if (!conn) {
1380: log_err(0, "No peer protocol defined");
1381: conn->response = REDIR_FAILED_OTHER;
1382: return 0;
1383: }
1384:
1385: if (!pack) { /* Timeout */
1386: log_err(0, "Radius request timed out");
1387: conn->response = REDIR_FAILED_OTHER;
1388: return 0;
1389: }
1390:
1391: /* We expect ACCESS-ACCEPT, ACCESS-REJECT (or ACCESS-CHALLENGE) */
1392: if ((pack->code != RADIUS_CODE_ACCESS_REJECT) &&
1393: (pack->code != RADIUS_CODE_ACCESS_CHALLENGE) &&
1394: (pack->code != RADIUS_CODE_ACCESS_ACCEPT)) {
1395: log_err(0, "Unknown radius access reply code %d", pack->code);
1396: conn->response = REDIR_FAILED_OTHER;
1397: return 0;
1398: }
1399:
1400: /* Reply message (might be present in both ACCESS-ACCEPT and ACCESS-REJECT */
1401: if (!radius_getattr(pack, &attr, RADIUS_ATTR_REPLY_MESSAGE, 0, 0, 0)) {
1402: memcpy(conn->replybuf, attr->v.t, attr->l-2);
1403: conn->replybuf[attr->l-2] = 0;
1404: conn->reply = conn->replybuf;
1405: }
1406: else {
1407: conn->replybuf[0] = 0;
1408: conn->reply = NULL;
1409: }
1410:
1411: config_radius_session(&conn->s_params, pack, 0);
1412:
1413: /* Class */
1414: if (!radius_getattr(pack, &classattr, RADIUS_ATTR_CLASS, 0, 0, 0)) {
1415: conn->s_state.redir.classlen = classattr->l-2;
1416: memcpy(conn->s_state.redir.classbuf, classattr->v.t, classattr->l-2);
1417: log_dbg("!!!! CLASSLEN = %d !!!!", conn->s_state.redir.classlen);
1418: }
1419: /*else {
1420: log_dbg("!!!! RESET CLASSLEN !!!!");
1421: conn->s_state.redir.classlen = 0;
1422: }*/
1423:
1424: if (pack->code != RADIUS_CODE_ACCESS_ACCEPT) {
1425: /* ACCESS-REJECT */
1426: conn->response = REDIR_FAILED_REJECT;
1427: return 0;
1428: }
1429:
1430: /* ACCESS-ACCEPT */
1431:
1432: /* State */
1433: if (!radius_getattr(pack, &stateattr, RADIUS_ATTR_STATE, 0, 0, 0)) {
1434: conn->s_state.redir.statelen = stateattr->l-2;
1435: memcpy(conn->s_state.redir.statebuf, stateattr->v.t, stateattr->l-2);
1436: }
1437: else {
1438: conn->s_state.redir.statelen = 0;
1439: }
1440:
1441: if (conn->s_params.sessionterminatetime) {
1442: time_t timenow = time(0);
1443: if (timenow > conn->s_params.sessionterminatetime) {
1444: conn->response = REDIR_FAILED_OTHER;
1445: log_warn(0, "WISPr-Session-Terminate-Time in the past received: %s", attrs);
1446: return 0;
1447: }
1448: }
1449:
1450: conn->response = REDIR_SUCCESS;
1451: return 0;
1452: }
1453:
1454: /* Send radius Access-Request and wait for answer */
1455: static int redir_radius(struct redir_t *redir, struct in_addr *addr,
1456: struct redir_conn_t *conn, char reauth) {
1457: unsigned char chap_password[REDIR_MD5LEN + 2];
1458: unsigned char chap_challenge[REDIR_MD5LEN];
1459: unsigned char user_password[REDIR_MD5LEN + 1];
1460: struct radius_packet_t radius_pack;
1461: struct radius_t *radius; /* Radius client instance */
1462: struct timeval idleTime; /* How long to select() */
1463: time_t endtime, now; /* for radius wait */
1464: int maxfd = 0; /* For select() */
1465: fd_set fds; /* For select() */
1466: int status;
1467:
1468: MD5_CTX context;
1469:
1470: char mac[REDIR_MACSTRLEN+1];
1471: char url[REDIR_URL_LEN];
1472: int n;
1473:
1474: if (radius_new(&radius,
1475: &redir->radiuslisten, 0, 0,
1476: NULL, 0, NULL, NULL, NULL)) {
1477: log_err(0, "Failed to create radius");
1478: return -1;
1479: }
1480:
1481: radius->next = redir_radius_id;
1482:
1483: if (radius->fd > maxfd)
1484: maxfd = radius->fd;
1485:
1486: radius_set(radius, dhcp ? dhcp->ipif.hwaddr : 0, (options.debug & DEBUG_RADIUS));
1487:
1488: radius_set_cb_auth_conf(radius, redir_cb_radius_auth_conf);
1489:
1490: radius_default_pack(radius, &radius_pack, RADIUS_CODE_ACCESS_REQUEST);
1491:
1492: if (optionsdebug)
1493: log_dbg("created radius packet (code=%d, id=%d, len=%d)\n",
1494: radius_pack.code, radius_pack.id, ntohs(radius_pack.length));
1495:
1496: radius_addattr(radius, &radius_pack, RADIUS_ATTR_USER_NAME, 0, 0, 0,
1497: (uint8_t*) conn->s_state.redir.username, strlen(conn->s_state.redir.username));
1498:
1499: /* If lang on logon url, then send it with attribute ChilliSpot-Lang */
1500: if(conn->lang[0])
1501: radius_addattr(radius, &radius_pack, RADIUS_ATTR_VENDOR_SPECIFIC,
1502: RADIUS_VENDOR_CHILLISPOT, RADIUS_ATTR_CHILLISPOT_LANG,
1503: 0, (uint8_t*) conn->lang, strlen(conn->lang));
1504:
1505: if (options.radiusoriginalurl)
1506: radius_addattr(radius, &radius_pack, RADIUS_ATTR_VENDOR_SPECIFIC,
1507: RADIUS_VENDOR_CHILLISPOT, RADIUS_ATTR_CHILLISPOT_ORIGINALURL,
1508: 0, (uint8_t*) conn->s_state.redir.userurl, strlen(conn->s_state.redir.userurl));
1509:
1510: if (redir->secret && *redir->secret) {
1511: /*fprintf(stderr,"SECRET: [%s]\n",redir->secret);*/
1512: /* Get MD5 hash on challenge and uamsecret */
1513: MD5Init(&context);
1514: MD5Update(&context, conn->s_state.redir.uamchal, REDIR_MD5LEN);
1515: MD5Update(&context, (uint8_t*) redir->secret, strlen(redir->secret));
1516: MD5Final(chap_challenge, &context);
1517: }
1518: else {
1519: memcpy(chap_challenge, conn->s_state.redir.uamchal, REDIR_MD5LEN);
1520: }
1521:
1522: if (conn->chap == 0) {
1523: for (n=0; n < REDIR_MD5LEN; n++)
1524: user_password[n] = conn->password[n] ^ chap_challenge[n];
1525:
1526: radius_addattr(radius, &radius_pack, RADIUS_ATTR_USER_PASSWORD, 0, 0, 0,
1527: (uint8_t*)user_password, REDIR_MD5LEN);
1528: }
1529: else if (conn->chap == 1) {
1530: chap_password[0] = conn->chap_ident; /* Chap ident found on logon url */
1531: memcpy(chap_password+1, conn->chappassword, REDIR_MD5LEN);
1532:
1533: radius_addattr(radius, &radius_pack, RADIUS_ATTR_CHAP_CHALLENGE, 0, 0, 0,
1534: chap_challenge, REDIR_MD5LEN);
1535:
1536: radius_addattr(radius, &radius_pack, RADIUS_ATTR_CHAP_PASSWORD, 0, 0, 0,
1537: chap_password, REDIR_MD5LEN+1);
1538: }
1539:
1540: radius_addnasip(radius, &radius_pack);
1541:
1542: radius_addattr(radius, &radius_pack, RADIUS_ATTR_SERVICE_TYPE, 0, 0,
1543: RADIUS_SERVICE_TYPE_LOGIN, NULL, 0); /* WISPr_V1.0 */
1544:
1545: radius_addattr(radius, &radius_pack, RADIUS_ATTR_FRAMED_IP_ADDRESS, 0, 0,
1546: ntohl(conn->hisip.s_addr), NULL, 0); /* WISPr_V1.0 */
1547:
1548: /* Include his MAC address */
1549: snprintf(mac, REDIR_MACSTRLEN+1, "%.2X-%.2X-%.2X-%.2X-%.2X-%.2X",
1550: conn->hismac[0], conn->hismac[1],
1551: conn->hismac[2], conn->hismac[3],
1552: conn->hismac[4], conn->hismac[5]);
1553:
1554: radius_addattr(radius, &radius_pack, RADIUS_ATTR_CALLING_STATION_ID, 0, 0, 0,
1555: (uint8_t*) mac, REDIR_MACSTRLEN);
1556:
1557: radius_addcalledstation(radius, &radius_pack);
1558:
1559:
1560: if (redir->radiusnasid)
1561: radius_addattr(radius, &radius_pack, RADIUS_ATTR_NAS_IDENTIFIER, 0, 0, 0,
1562: (uint8_t*) redir->radiusnasid,
1563: strlen(redir->radiusnasid)); /* WISPr_V1.0 */
1564:
1565:
1566: radius_addattr(radius, &radius_pack, RADIUS_ATTR_ACCT_SESSION_ID, 0, 0, 0,
1567: (uint8_t*) conn->s_state.sessionid, REDIR_SESSIONID_LEN-1);
1568:
1569: log_dbg("!!!! CLASSLEN = %d !!!!", conn->s_state.redir.classlen);
1570: if (conn->s_state.redir.classlen) {
1571: radius_addattr(radius, &radius_pack, RADIUS_ATTR_CLASS, 0, 0, 0,
1572: conn->s_state.redir.classbuf,
1573: conn->s_state.redir.classlen);
1574: }
1575:
1576: radius_addattr(radius, &radius_pack, RADIUS_ATTR_NAS_PORT_TYPE, 0, 0,
1577: redir->radiusnasporttype, NULL, 0);
1578:
1579: radius_addattr(radius, &radius_pack, RADIUS_ATTR_NAS_PORT, 0, 0,
1580: conn->nasport, NULL, 0);
1581:
1582: if (redir->radiuslocationid)
1583: radius_addattr(radius, &radius_pack, RADIUS_ATTR_VENDOR_SPECIFIC,
1584: RADIUS_VENDOR_WISPR, RADIUS_ATTR_WISPR_LOCATION_ID, 0,
1585: (uint8_t*) redir->radiuslocationid,
1586: strlen(redir->radiuslocationid));
1587:
1588: if (redir->radiuslocationname)
1589: radius_addattr(radius, &radius_pack, RADIUS_ATTR_VENDOR_SPECIFIC,
1590: RADIUS_VENDOR_WISPR, RADIUS_ATTR_WISPR_LOCATION_NAME, 0,
1591: (uint8_t*) redir->radiuslocationname,
1592: strlen(redir->radiuslocationname));
1593:
1594: if (snprintf(url, sizeof(url)-1, "http://%s:%d/logoff",
1595: inet_ntoa(redir->addr), redir->port) > 0)
1596: radius_addattr(radius, &radius_pack, RADIUS_ATTR_VENDOR_SPECIFIC,
1597: RADIUS_VENDOR_WISPR, RADIUS_ATTR_WISPR_LOGOFF_URL, 0,
1598: (uint8_t*)url, strlen(url));
1599:
1600: if (options.openidauth)
1601: radius_addattr(radius, &radius_pack, RADIUS_ATTR_VENDOR_SPECIFIC,
1602: RADIUS_VENDOR_CHILLISPOT, RADIUS_ATTR_CHILLISPOT_CONFIG,
1603: 0, (uint8_t*)"allow-openidauth", 16);
1604:
1605: radius_addattr(radius, &radius_pack, RADIUS_ATTR_MESSAGE_AUTHENTICATOR,
1606: 0, 0, 0, NULL, RADIUS_MD5LEN);
1607:
1608: if (optionsdebug)
1609: log_dbg("sending radius packet (code=%d, id=%d, len=%d)\n",
1610: radius_pack.code, radius_pack.id, ntohs(radius_pack.length));
1611:
1612: radius_req(radius, &radius_pack, conn);
1613:
1614: now = time(NULL);
1615: endtime = now + REDIR_RADIUS_MAX_TIME;
1616:
1617: while (endtime > now) {
1618:
1619: FD_ZERO(&fds);
1620: if (radius->fd != -1) FD_SET(radius->fd, &fds);
1621: if (radius->proxyfd != -1) FD_SET(radius->proxyfd, &fds);
1622:
1623: idleTime.tv_sec = 0;
1624: idleTime.tv_usec = REDIR_RADIUS_SELECT_TIME;
1625: radius_timeleft(radius, &idleTime);
1626:
1627: switch (status = select(maxfd + 1, &fds, NULL, NULL, &idleTime)) {
1628: case -1:
1629: log_err(errno, "select() returned -1!");
1630: break;
1631: case 0:
1632: /*log_dbg("Select returned 0");*/
1633: radius_timeout(radius);
1634: break;
1635: default:
1636: break;
1637: }
1638:
1639: if (status > 0) {
1640: if ((radius->fd != -1) && FD_ISSET(radius->fd, &fds) &&
1641: radius_decaps(radius) < 0) {
1642: log_err(0, "radius_ind() failed!");
1643: }
1644:
1645: if ((radius->proxyfd != -1) && FD_ISSET(radius->proxyfd, &fds) &&
1646: radius_proxy_ind(radius) < 0) {
1647: log_err(0, "radius_proxy_ind() failed!");
1648: }
1649: }
1650:
1651: if (conn->response) {
1652: radius_free(radius);
1653: return 0;
1654: }
1655:
1656: now = time(NULL);
1657: }
1658:
1659: return 0;
1660: }
1661:
1662: int set_nonblocking(int fd) {
1663: int flags = fcntl(fd, F_GETFL);
1664: if (flags < 0) return -1;
1665: fcntl(fd, F_SETFL, flags | O_NONBLOCK);
1666: return 0;
1667: }
1668:
1669: int clear_nonblocking(int fd) {
1670: int flags = fcntl(fd, F_GETFL);
1671: if (flags < 0) return -1;
1672: fcntl(fd, F_SETFL, flags & (~O_NONBLOCK));
1673: return 0;
1674: }
1675:
1676: int is_local_user(struct redir_t *redir, struct redir_conn_t *conn) {
1677: unsigned char user_password[REDIR_MD5LEN+1];
1678: unsigned char chap_challenge[REDIR_MD5LEN];
1679: unsigned char tmp[REDIR_MD5LEN+1];
1680: char u[256]; char p[256];
1681: size_t usernamelen, sz=1024;
1682: ssize_t len;
1683: int match=0;
1684: char *line=0;
1685: MD5_CTX context;
1686: FILE *f;
1687:
1688: if (!options.localusers) return 0;
1689:
1690: log_dbg("checking %s for user %s", options.localusers, conn->s_state.redir.username);
1691:
1692: if (!(f = fopen(options.localusers, "r"))) {
1693: log_err(errno, "fopen() failed opening %s!", options.localusers);
1694: return 0;
1695: }
1696:
1697: if (options.debug) {/*debug*/
1698: char buffer[64];
1699: redir_chartohex(conn->s_state.redir.uamchal, buffer);
1700: log_dbg("challenge: %s", buffer);
1701: }/**/
1702:
1703: if (redir->secret && *redir->secret) {
1704: MD5Init(&context);
1705: MD5Update(&context, (uint8_t*)conn->s_state.redir.uamchal, REDIR_MD5LEN);
1706: MD5Update(&context, (uint8_t*)redir->secret, strlen(redir->secret));
1707: MD5Final(chap_challenge, &context);
1708: }
1709: else {
1710: memcpy(chap_challenge, conn->s_state.redir.uamchal, REDIR_MD5LEN);
1711: }
1712:
1713: if (options.debug) {/*debug*/
1714: char buffer[64];
1715: redir_chartohex(chap_challenge, buffer);
1716: log_dbg("chap challenge: %s", buffer);
1717: }/**/
1718:
1719: if (conn->chap == 0) {
1720: int n;
1721: for (n=0; n < REDIR_MD5LEN; n++)
1722: user_password[n] = conn->password[n] ^ chap_challenge[n];
1723: }
1724: else if (conn->chap == 1) {
1725: memcpy(user_password, conn->chappassword, REDIR_MD5LEN);
1726: }
1727:
1728: user_password[REDIR_MD5LEN] = 0;
1729:
1730: log_dbg("looking for %s", conn->s_state.redir.username);
1731: usernamelen = strlen(conn->s_state.redir.username);
1732:
1733: line=(char*)malloc(sz);
1734: while ((len = getline(&line, &sz, f)) > 0) {
1735: if (len > 3 && len < sizeof(u) && line[0] != '#') {
1736: char *pl=line, /* pointer to current line */
1737: *pu=u, /* pointer to username */
1738: *pp=p; /* pointer to password */
1739:
1740: /* username until the first ':' */
1741: while (*pl && *pl != ':') *pu++ = *pl++;
1742:
1743: /* skip over ':' otherwise error */
1744: if (*pl == ':') pl++;
1745: else {
1746: log_warn(0, "not a valid localusers line: %s", line);
1747: continue;
1748: }
1749:
1750: /* password until the next ':' */
1751: while (*pl && *pl != ':' && *pl != '\n') *pp++ = *pl++;
1752:
1753: *pu = 0; /* null terminate */
1754: *pp = 0;
1755:
1756: if (usernamelen == strlen(u) &&
1757: !strncmp(conn->s_state.redir.username, u, usernamelen)) {
1758:
1759: log_dbg("found %s, checking password", u);
1760:
1761: if (conn->chap == 0) {
1762: int n;
1763: for (n=0; n < REDIR_MD5LEN; n++)
1764: tmp[n] = p[n] ^ chap_challenge[n];
1765: }
1766: else if (conn->chap == 1) {
1767: MD5Init(&context);
1768: MD5Update(&context, (uint8_t*)&conn->chap_ident, 1);
1769: MD5Update(&context, (uint8_t*)p, strlen(p));
1770: MD5Update(&context, chap_challenge, REDIR_MD5LEN);
1771: MD5Final(tmp, &context);
1772: }
1773:
1774: tmp[REDIR_MD5LEN] = 0;
1775:
1776: if (!memcmp(user_password, tmp, REDIR_MD5LEN))
1777: match = 1;
1778:
1779: break;
1780: }
1781: }
1782: }
1783:
1784: log_dbg("user %s %s", conn->s_state.redir.username, match ? "found" : "not found");
1785:
1786: fclose(f);
1787: free(line);
1788: return match;
1789: }
1790:
1791:
1792: /* redir_accept() does the following:
1793: 1) forks a child process
1794: 2) Accepts the tcp connection
1795: 3) Analyses a HTTP get request
1796: 4) GET request can be one of the following:
1797: a) Logon request with username and challenge response
1798: - Does a radius request
1799: - If OK send result to parent and redirect to welcome page
1800: - Else redirect to error login page
1801: b) Logoff request
1802: - Send logoff request to parent
1803: - Redirect to login page?
1804: c) Request for another server
1805: - Redirect to login server.
1806:
1807: Incoming requests are identified only by their IP address. No MAC
1808: address information is obtained. The main security problem is denial
1809: of service attacks by malicious hosts sending logoff requests for
1810: clients. This can be prevented by checking incoming packets for
1811: matching MAC and src IP addresses.
1812: */
1813:
1814: int redir_accept(struct redir_t *redir, int idx) {
1815: int status;
1816: int new_socket;
1817: struct sockaddr_in address;
1818: socklen_t addrlen;
1819:
1820: addrlen = sizeof(struct sockaddr_in);
1821:
1822: if ((new_socket = accept(redir->fd[idx], (struct sockaddr *)&address, &addrlen)) < 0) {
1823: if (errno != ECONNABORTED)
1824: log_err(errno, "accept() failed!");
1825: return 0;
1826: }
1827:
1828: /* This forks a new process. The child really should close all
1829: unused file descriptors and free memory allocated. This however
1830: is performed when the process exits, so currently we don't
1831: care */
1832:
1833: redir_radius_id++;
1834:
1835: if ((status = fork()) < 0) {
1836: log_err(errno, "fork() returned -1!");
1837: close(new_socket);
1838: return 0;
1839: }
1840:
1841: if (status > 0) { /* Parent */
1842: close(new_socket);
1843: return 0;
1844: }
1845:
1846:
1847: #if defined(F_DUPFD)
1848: if (fcntl(new_socket,F_GETFL,0) == -1) return -1;
1849: close(0);
1850: if (fcntl(new_socket,F_DUPFD,0) == -1) return -1;
1851: if (fcntl(new_socket,F_GETFL,1) == -1) return -1;
1852: close(1);
1853: if (fcntl(new_socket,F_DUPFD,1) == -1) return -1;
1854: #else
1855: if (dup2(new_socket,0) == -1) return -1;
1856: if (dup2(new_socket,1) == -1) return -1;
1857: #endif
1858:
1859: if (idx == 1 && options.uamui) {
1860: char *binqqargs[2] = { options.uamui, 0 } ;
1861: char buffer[128];
1862:
1863: snprintf(buffer,sizeof(buffer)-1,"%s",inet_ntoa(address.sin_addr));
1864: setenv("TCPREMOTEIP",buffer,1);
1865: setenv("REMOTE_ADDR",buffer,1);
1866: snprintf(buffer,sizeof(buffer)-1,"%d",ntohs(address.sin_port));
1867: setenv("TCPREMOTEPORT",buffer,1);
1868: setenv("REMOTE_PORT",buffer,1);
1869:
1870: execv(*binqqargs, binqqargs);
1871:
1872: } else {
1873: return redir_main(redir, 0, 1, &address, idx);
1874: }
1875:
1876: return 0;
1877: }
1878:
1879: static void redir_close(int infd, int outfd) {
1880: char b[128];
1881:
1882: /* Close of socket */
1883: if (shutdown(outfd, SHUT_WR) != 0)
1884: log_dbg("shutdown socket for writing");
1885:
1886: if (!set_nonblocking(infd))
1887: while(read(infd, b, sizeof(b)) > 0);
1888:
1889: if (shutdown(infd, SHUT_RD) != 0)
1890: log_dbg("shutdown socket for reading");
1891:
1892: close(outfd);
1893: close(infd);
1894: exit(0);
1895: }
1896:
1897:
1898: int redir_main(struct redir_t *redir, int infd, int outfd, struct sockaddr_in *address, int isui) {
1899: char hexchal[1+(2*REDIR_MD5LEN)];
1900: unsigned char challenge[REDIR_MD5LEN];
1901: size_t bufsize = REDIR_MAXBUFFER;
1902: char buffer[bufsize+1];
1903: char qs[REDIR_USERURLSIZE];
1904: struct redir_msg_t msg;
1905: ssize_t buflen;
1906:
1907: /**
1908: * connection state
1909: * 0 == un-authenticated
1910: * 1 == authenticated
1911: */
1912: int state = 0;
1913:
1914: /**
1915: * require splash or not
1916: */
1917: int splash = 0;
1918:
1919: struct redir_conn_t conn;
1920: struct sigaction act, oldact;
1921: struct itimerval itval;
1922: struct redir_socket socket;
1923: int ispost = isui;
1924: size_t clen = 0;
1925:
1926:
1927: #define redir_memcopy(msgtype) \
1928: redir_challenge(challenge); \
1929: redir_chartohex(challenge, hexchal); \
1930: msg.mtype = msgtype; \
1931: memcpy(conn.s_state.redir.uamchal, challenge, REDIR_MD5LEN); \
1932: if (options.debug) { \
1933: log_dbg("---->>> resetting challenge: %s", hexchal); \
1934: }
1935:
1936:
1937: #define redir_msg_send(msgopt) \
1938: msg.mdata.opt = msgopt; \
1939: msg.mdata.addr = address->sin_addr; \
1940: memcpy(&msg.mdata.params, &conn.s_params, sizeof(msg.mdata.params)); \
1941: memcpy(&msg.mdata.redir, &conn.s_state.redir, sizeof(msg.mdata.redir)); \
1942: if (msgsnd(redir->msgid, (struct msgbuf *)&msg, sizeof(msg.mdata), 0) < 0) { \
1943: log_err(errno, "msgsnd() failed!"); \
1944: redir_close(infd, outfd); \
1945: }
1946:
1947: /*
1948: * Initializations
1949: */
1950: memset(&socket,0,sizeof(socket));
1951: memset(hexchal, 0, sizeof(hexchal));
1952: memset(&conn, 0, sizeof(conn));
1953: memset(&msg, 0, sizeof(msg));
1954: memset(&act, 0, sizeof(act));
1955: memset(qs, 0, sizeof(qs));
1956:
1957: socket.fd[0] = infd;
1958: socket.fd[1] = outfd;
1959:
1960: redir->starttime = time(NULL);
1961:
1962: if (set_nonblocking(socket.fd[0])) {
1963: log_err(errno, "fcntl() failed");
1964: redir_close(infd, outfd);
1965: }
1966:
1967: act.sa_handler = redir_termination;
1968: sigaction(SIGTERM, &act, &oldact);
1969: sigaction(SIGINT, &act, &oldact);
1970: act.sa_handler = redir_alarm;
1971: sigaction(SIGALRM, &act, &oldact);
1972:
1973: memset(&itval, 0, sizeof(itval));
1974: itval.it_interval.tv_sec = REDIR_MAXTIME;
1975: itval.it_interval.tv_usec = 0;
1976: itval.it_value.tv_sec = REDIR_MAXTIME;
1977: itval.it_value.tv_usec = 0;
1978:
1979: if (setitimer(ITIMER_REAL, &itval, NULL)) {
1980: log_err(errno, "setitimer() failed!");
1981: }
1982:
1983: if (optionsdebug)
1984: log_dbg("Calling redir_getstate()");
1985:
1986: /*
1987: * Fetch the state of the client
1988: */
1989:
1990: termstate = REDIR_TERM_GETSTATE;
1991:
1992: if (!redir->cb_getstate) {
1993: log_err(0, "No cb_getstate() defined!");
1994: redir_close(infd, outfd);
1995: }
1996:
1997: /* get_state returns 0 for unauth'ed and 1 for auth'ed */
1998: state = redir->cb_getstate(redir, &address->sin_addr, &conn);
1999: if (state == -1) {
2000: redir_close(infd, outfd);
2001: }
2002:
2003: splash = (conn.s_params.flags & REQUIRE_UAM_SPLASH) == REQUIRE_UAM_SPLASH;
2004:
2005:
2006: /*
2007: * Parse the request, updating the status
2008: */
2009: if (optionsdebug)
2010: log_dbg("Get HTTP Request");
2011:
2012: termstate = REDIR_TERM_GETREQ;
2013: if (redir_getreq(redir, &socket, &conn, &ispost, &clen, qs, sizeof(qs))) {
2014: log_dbg("Error calling get_req. Terminating\n");
2015: redir_close(infd, outfd);
2016: }
2017:
2018: if (optionsdebug)
2019: log_dbg("Process HTTP Request");
2020:
2021: if (conn.type == REDIR_WWW) {
2022: int fd = -1;
2023: if (options.wwwdir && conn.wwwfile && *conn.wwwfile) {
2024: char *ctype = "text/plain";
2025: char *filename = conn.wwwfile;
2026: size_t namelen = strlen(filename);
2027: int parse = 0;
2028:
2029: /* check filename */
2030: { char *p;
2031: for (p=filename; *p; p++) {
2032: if (*p >= 'a' && *p <= 'z') continue;
2033: if (*p >= 'A' && *p <= 'Z') continue;
2034: if (*p >= '0' && *p <= '9') continue;
2035: if (*p == '.' || *p == '_') continue;
2036: /* invalid file name! */
2037: log_err(0, "invalid www request [%s]!", filename);
2038: redir_close(infd, outfd);
2039: }
2040: }
2041:
2042: /* serve the local content */
2043:
2044: if (!strcmp(filename + (namelen - 5), ".html")) ctype = "text/html";
2045: else if (!strcmp(filename + (namelen - 4), ".gif")) ctype = "image/gif";
2046: else if (!strcmp(filename + (namelen - 3), ".js")) ctype = "text/javascript";
2047: else if (!strcmp(filename + (namelen - 4), ".css")) ctype = "text/css";
2048: else if (!strcmp(filename + (namelen - 4), ".jpg")) ctype = "image/jpeg";
2049: else if (!strcmp(filename + (namelen - 4), ".dat")) ctype = "application/x-ns-proxy-autoconfig";
2050: else if (!strcmp(filename + (namelen - 4), ".png")) ctype = "image/png";
2051: else if (!strcmp(filename + (namelen - 4), ".swf")) ctype = "application/x-shockwave-flash";
2052: else if (!strcmp(filename + (namelen - 4), ".chi")){ ctype = "text/html"; parse = 1; }
2053: else {
2054: /* we do not serve it! */
2055: log_err(0, "invalid file extension! [%s]", filename);
2056: redir_close(infd, outfd);
2057: }
2058:
2059: if (parse) {
2060: if (!options.wwwbin) {
2061: log_err(0, "the 'wwwbin' setting must be configured for CGI use");
2062: redir_close(infd, outfd);
2063: }
2064:
2065: if (clear_nonblocking(socket.fd[0])) {
2066: log_err(errno, "fcntl() failed");
2067: }
2068:
2069: /* XXX: Todo: look for malicious content! */
2070:
2071: sprintf(buffer,"%d", clen > 0 ? clen : 0);
2072: setenv("CONTENT_LENGTH", buffer, 1);
2073: setenv("REQUEST_METHOD", ispost ? "POST" : "GET", 1);
2074: setenv("QUERY_STRING", qs, 1);
2075:
2076: log_dbg("Running: %s %s/%s",options.wwwbin, options.wwwdir, filename);
2077: sprintf(buffer, "%s/%s", options.wwwdir, filename);
2078:
2079: {
2080: char *binqqargs[3] = { options.wwwbin, buffer, 0 } ;
2081: int status;
2082:
2083: if ((status = fork()) < 0) {
2084: log_err(errno, "fork() returned -1!");
2085: /* lets just execv and ignore the extra crlf problem */
2086: execv(*binqqargs, binqqargs);
2087: }
2088:
2089: if (status > 0) { /* Parent */
2090: /* now wait for the child (the cgi-prog) to finish
2091: * and let redir_close remove unwanted data
2092: * (for instance) extra crlf from ie7 in POSTs)
2093: * to avoid a tcp-reset.
2094: */
2095: wait(NULL);
2096: }
2097: else {
2098: /* Child */
2099: execv(*binqqargs, binqqargs);
2100: }
2101: }
2102:
2103: redir_close(infd, outfd);
2104: }
2105:
2106: if (!chroot(options.wwwdir) && !chdir("/")) {
2107:
2108: fd = open(filename, O_RDONLY);
2109:
2110: if (fd > 0) {
2111:
2112: if (clear_nonblocking(socket.fd[0])) {
2113: log_err(errno, "fcntl() failed");
2114: }
2115:
2116: buflen = snprintf(buffer, bufsize,
2117: "HTTP/1.0 200 OK\r\nContent-type: %s\r\n\r\n", ctype);
2118:
2119: if (tcp_write(&socket, buffer, (size_t) buflen) < 0) {
2120: log_err(errno, "tcp_write() failed!");
2121: }
2122:
2123: while ((buflen = read(fd, buffer, bufsize)) > 0)
2124: if (tcp_write(&socket, buffer, (size_t) buflen) < 0)
2125: log_err(errno, "tcp_write() failed!");
2126:
2127: close(fd);
2128: redir_close(infd, outfd); /* which exits */
2129: }
2130: else log_err(0, "could not open local content file %s!", filename);
2131: }
2132: else log_err(0, "chroot to %s was not successful\n", options.wwwdir);
2133: }
2134: else log_err(0, "Required: 'wwwdir' (in chilli.conf) and 'file' query-string param\n");
2135:
2136: redir_close(infd, outfd);
2137: }
2138:
2139: termstate = REDIR_TERM_PROCESS;
2140: if (optionsdebug) log_dbg("Processing received request");
2141:
2142: /* default hexchal for use in replies */
2143: redir_chartohex(conn.s_state.redir.uamchal, hexchal);
2144:
2145: switch (conn.type) {
2146:
2147: case REDIR_LOGIN: {
2148: char reauth = 0;
2149:
2150: /* Was client was already logged on? */
2151: if (state == 1) {
2152: if (splash) {
2153: log_dbg("redir_accept: SPLASH reauth");
2154: reauth = 1;
2155: } else {
2156: log_dbg("redir_accept: already logged on");
2157: redir_reply(redir, &socket, &conn, REDIR_ALREADY, NULL, 0,
2158: NULL, NULL, conn.s_state.redir.userurl, NULL,
2159: NULL, conn.hismac, &conn.hisip, qs);
2160: redir_close(infd, outfd);
2161: }
2162: }
2163:
2164: /* Did the challenge expire? */
2165: if ((conn.s_state.uamtime + REDIR_CHALLENGETIMEOUT2) < time(NULL)) {
2166: log_dbg("redir_accept: challenge expired: %d : %d", conn.s_state.uamtime, time(NULL));
2167:
2168: redir_memcopy(REDIR_CHALLENGE);
2169: redir_msg_send(REDIR_MSG_OPT_REDIR);
2170:
2171: redir_reply(redir, &socket, &conn, REDIR_FAILED_OTHER, NULL,
2172: 0, hexchal, NULL, NULL, NULL,
2173: NULL, conn.hismac, &conn.hisip, qs);
2174:
2175: redir_close(infd, outfd);
2176: }
2177:
2178: if (is_local_user(redir, &conn)) {
2179: conn.response = REDIR_SUCCESS;
2180: }
2181: else {
2182: termstate = REDIR_TERM_RADIUS;
2183:
2184: if (optionsdebug)
2185: log_dbg("redir_accept: Sending radius request\n");
2186:
2187: redir_radius(redir, &address->sin_addr, &conn, reauth);
2188: termstate = REDIR_TERM_REPLY;
2189:
2190: if (optionsdebug)
2191: log_dbg("Received radius reply\n");
2192: }
2193:
2194: if (options.defsessiontimeout && !conn.s_params.sessiontimeout)
2195: conn.s_params.sessiontimeout = options.defsessiontimeout;
2196:
2197: if (options.defidletimeout && !conn.s_params.idletimeout)
2198: conn.s_params.idletimeout = options.defidletimeout;
2199:
2200: if (options.defbandwidthmaxdown && !conn.s_params.bandwidthmaxdown)
2201: conn.s_params.bandwidthmaxdown = options.defbandwidthmaxdown;
2202:
2203: if (options.defbandwidthmaxup && !conn.s_params.bandwidthmaxup)
2204: conn.s_params.bandwidthmaxup = options.defbandwidthmaxup;
2205:
2206: if (options.definteriminterval && !conn.s_params.interim_interval)
2207: conn.s_params.interim_interval = options.definteriminterval;
2208:
2209: if (conn.response == REDIR_SUCCESS) { /* Radius-Accept */
2210: bstring besturl = bfromcstr((char*)conn.s_params.url);
2211:
2212: conn.s_params.flags &= ~REQUIRE_UAM_SPLASH;
2213:
2214: if (reauth) {
2215: conn.s_params.flags |= IS_UAM_REAUTH;
2216: }
2217:
2218: msg.mtype = REDIR_LOGIN;
2219:
2220: if (! (besturl && besturl->slen))
2221: bassigncstr(besturl, conn.s_state.redir.userurl);
2222:
2223: if (redir->no_uamsuccess && besturl && besturl->slen)
2224: redir_reply(redir, &socket, &conn, REDIR_SUCCESS, besturl, conn.s_params.sessiontimeout,
2225: NULL, conn.s_state.redir.username, conn.s_state.redir.userurl, conn.reply,
2226: (char *)conn.s_params.url, conn.hismac, &conn.hisip, qs);
2227: else
2228: redir_reply(redir, &socket, &conn, REDIR_SUCCESS, NULL, conn.s_params.sessiontimeout,
2229: NULL, conn.s_state.redir.username, conn.s_state.redir.userurl, conn.reply,
2230: (char *)conn.s_params.url, conn.hismac, &conn.hisip, qs);
2231:
2232: bdestroy(besturl);
2233: }
2234: else {
2235: bstring besturl = bfromcstr((char *)conn.s_params.url);
2236: int hasnexturl = (besturl && besturl->slen > 5);
2237:
2238: if (!hasnexturl) {
2239: redir_memcopy(REDIR_CHALLENGE);
2240: } else {
2241: msg.mtype = REDIR_NOTYET;
2242: }
2243:
2244: redir_reply(redir, &socket, &conn, REDIR_FAILED_REJECT,
2245: hasnexturl ? besturl : NULL,
2246: 0, hexchal, NULL, conn.s_state.redir.userurl, conn.reply,
2247: (char *)conn.s_params.url, conn.hismac, &conn.hisip, qs);
2248:
2249: bdestroy(besturl);
2250: }
2251:
2252: if (optionsdebug) log_dbg("-->> Msg userurl=[%s]\n",conn.s_state.redir.userurl);
2253: redir_msg_send(REDIR_MSG_OPT_REDIR | REDIR_MSG_OPT_PARAMS);
2254:
2255: redir_close(infd, outfd);
2256: }
2257:
2258: case REDIR_LOGOUT:
2259: {
2260: bstring besturl = bfromcstr((char *)conn.s_params.url);
2261:
2262: redir_memcopy(REDIR_LOGOUT);
2263: redir_msg_send(REDIR_MSG_OPT_REDIR);
2264:
2265: conn.s_state.authenticated=0;
2266:
2267: if (! (besturl && besturl->slen))
2268: bassigncstr(besturl, conn.s_state.redir.userurl);
2269:
2270: if (redir->no_uamsuccess && besturl && besturl->slen)
2271: redir_reply(redir, &socket, &conn, REDIR_LOGOFF, besturl, 0,
2272: hexchal, NULL, conn.s_state.redir.userurl, NULL,
2273: NULL, conn.hismac, &conn.hisip, qs);
2274: else
2275: redir_reply(redir, &socket, &conn, REDIR_LOGOFF, NULL, 0,
2276: hexchal, NULL, conn.s_state.redir.userurl, NULL,
2277: NULL, conn.hismac, &conn.hisip, qs);
2278:
2279: bdestroy(besturl);
2280:
2281: redir_close(infd, outfd);
2282: }
2283:
2284: case REDIR_PRELOGIN:
2285:
2286: /* Did the challenge expire? */
2287: if ((conn.s_state.uamtime + REDIR_CHALLENGETIMEOUT1) < time(NULL)) {
2288: redir_memcopy(REDIR_CHALLENGE);
2289: redir_msg_send(REDIR_MSG_OPT_REDIR);
2290: }
2291:
2292: if (state == 1) {
2293: redir_reply(redir, &socket, &conn, REDIR_ALREADY,
2294: NULL, 0, NULL, NULL, conn.s_state.redir.userurl, NULL,
2295: NULL, conn.hismac, &conn.hisip, qs);
2296: }
2297: else {
2298: redir_reply(redir, &socket, &conn, REDIR_NOTYET,
2299: NULL, 0, hexchal, NULL, conn.s_state.redir.userurl, NULL,
2300: NULL, conn.hismac, &conn.hisip, qs);
2301: }
2302: redir_close(infd, outfd);
2303:
2304: case REDIR_ABORT:
2305:
2306: if (state == 1) {
2307: redir_reply(redir, &socket, &conn, REDIR_ABORT_NAK,
2308: NULL, 0, NULL, NULL, conn.s_state.redir.userurl, NULL,
2309: NULL, conn.hismac, &conn.hisip, qs);
2310: }
2311: else {
2312: redir_memcopy(REDIR_ABORT);
2313: redir_msg_send(0);
2314:
2315: redir_reply(redir, &socket, &conn, REDIR_ABORT_ACK,
2316: NULL, 0, hexchal, NULL, conn.s_state.redir.userurl, NULL,
2317: NULL, conn.hismac, &conn.hisip, qs);
2318: }
2319: redir_close(infd, outfd);
2320:
2321: case REDIR_ABOUT:
2322: redir_reply(redir, &socket, &conn, REDIR_ABOUT, NULL,
2323: 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, qs);
2324: redir_close(infd, outfd);
2325:
2326: case REDIR_STATUS:
2327: {
2328: uint32_t sessiontime;
2329: uint32_t timeleft;
2330: time_t timenow = time(0);
2331:
2332: /* Did the challenge expire? */
2333: if ((conn.s_state.uamtime + REDIR_CHALLENGETIMEOUT1) < time(NULL)) {
2334: redir_memcopy(REDIR_CHALLENGE);
2335: redir_msg_send(REDIR_MSG_OPT_REDIR);
2336: }
2337:
2338: sessiontime = timenow - conn.s_state.start_time;
2339:
2340: if (conn.s_params.sessiontimeout)
2341: timeleft = conn.s_params.sessiontimeout - sessiontime;
2342: else
2343: timeleft = 0;
2344:
2345: redir_reply(redir, &socket, &conn, REDIR_STATUS, NULL, timeleft,
2346: hexchal, conn.s_state.redir.username, conn.s_state.redir.userurl, conn.reply,
2347: (char *)conn.s_params.url, conn.hismac, &conn.hisip, qs);
2348:
2349: redir_close(infd, outfd);
2350: }
2351:
2352: case REDIR_MSDOWNLOAD:
2353: buflen = snprintf(buffer, bufsize, "HTTP/1.0 403 Forbidden\r\n\r\n");
2354: tcp_write(&socket, buffer, buflen);
2355: redir_close(infd, outfd);
2356: }
2357:
2358: /* It was not a request for a known path. It must be an original request */
2359: if (optionsdebug)
2360: log_dbg("redir_accept: Original request");
2361:
2362:
2363: /* Did the challenge expire? */
2364: if ((conn.s_state.uamtime + REDIR_CHALLENGETIMEOUT1) < time(NULL)) {
2365: redir_memcopy(REDIR_CHALLENGE);
2366: redir_msg_send(REDIR_MSG_OPT_REDIR);
2367: }
2368: else {
2369: redir_chartohex(conn.s_state.redir.uamchal, hexchal);
2370: /*
2371: redir_memcopy(REDIR_CHALLENGE);
2372: redir_msg_send(REDIR_MSG_OPT_REDIR);
2373: */
2374: }
2375:
2376: if (redir->homepage) {
2377: bstring url = bfromcstralloc(1024,"");
2378: bstring urlenc = bfromcstralloc(1024,"");
2379:
2380: char *resp = splash ? "splash" : "notyet";
2381: if (redir_buildurl(&conn, url, redir, resp, 0, hexchal, NULL,
2382: conn.s_state.redir.userurl, NULL, NULL, conn.hismac, &conn.hisip) == -1) {
2383: log_err(errno, "redir_buildurl failed!");
2384: redir_close(infd, outfd);
2385: }
2386:
2387: redir_urlencode(url, urlenc);
2388:
2389: bassignformat(url, "%s%cloginurl=",
2390: redir->homepage, strchr(redir->homepage, '?') ? '&' : '?');
2391: bconcat(url, urlenc);
2392:
2393: redir_reply(redir, &socket, &conn, splash ? REDIR_SPLASH : REDIR_NOTYET, url,
2394: 0, hexchal, NULL, conn.s_state.redir.userurl, NULL,
2395: NULL, conn.hismac, &conn.hisip, qs);
2396: }
2397: else if (state == 1) {
2398: redir_reply(redir, &socket, &conn, splash ? REDIR_SPLASH : REDIR_ALREADY, NULL, 0,
2399: splash ? hexchal : NULL, NULL, conn.s_state.redir.userurl, NULL,
2400: NULL, conn.hismac, &conn.hisip, qs);
2401: }
2402: else {
2403: redir_reply(redir, &socket, &conn, splash ? REDIR_SPLASH : REDIR_NOTYET, NULL,
2404: 0, hexchal, NULL, conn.s_state.redir.userurl, NULL,
2405: NULL, conn.hismac, &conn.hisip, qs);
2406: }
2407:
2408: redir_close(infd, outfd);
2409: return -1; /* never gets here */
2410: }
2411:
2412:
2413: /* Set callback to determine state information for the connection */
2414: int redir_set_cb_getstate(struct redir_t *redir,
2415: int (*cb_getstate) (struct redir_t *redir, struct in_addr *addr,
2416: struct redir_conn_t *conn)) {
2417: redir->cb_getstate = cb_getstate;
2418: return 0;
2419: }
2420:
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>