Signed / unsigned fixes and function prototypes
[freeradius.git] / src / modules / rlm_jradius / rlm_jradius.c
1 /**
2  * rlm_jradius - The FreeRADIUS JRadius Server Module
3  * Copyright (C) 2004-2006 PicoPoint, B.V.
4  * Copyright (c) 2007-2008 David Bird
5  * 
6  * This program is free software; you can redistribute it and/or modify it
7  * under the terms of the GNU General Public License as published by the
8  * Free Software Foundation; either version 2 of the License, or (at your
9  * option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful, but
12  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
13  * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 
14  * for more details.
15  * 
16  * You should have received a copy of the GNU General Public License along
17  * with this program; if not, write to the Free Software Foundation, Inc.,
18  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19  *
20  *  This module is used to connect FreeRADIUS to the JRadius server. 
21  *  JRadius is a Java RADIUS client and server framework, see doc/rlm_jradius
22  *  and http://jradius.net/ for more information. 
23  *
24  *  Author(s): David Bird <dbird@acm.org>
25  *
26  *  Connection pooling code based on rlm_sql, see rlm_sql/sql.c for copyright and license.
27  */
28
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <string.h>
32 #include <sys/signal.h>
33 #include <sys/types.h>
34 #include <fcntl.h>
35 #include <poll.h>
36
37 #include <freeradius-devel/ident.h>
38 RCSID("$Id$")
39
40 #include <freeradius-devel/autoconf.h>
41 #include <freeradius-devel/libradius.h>
42 #include <freeradius-devel/radiusd.h>
43 #include <freeradius-devel/modules.h>
44 #include <freeradius-devel/conffile.h>
45
46 #ifdef HAVE_PTHREAD_H
47 #include <pthread.h>
48 #endif
49
50 #ifdef HAVE_SYS_SOCKET_H
51 #include <sys/socket.h>
52 #endif
53
54 #ifndef O_NONBLOCK
55 #define O_NONBLOCK O_NDELAY
56 #endif
57
58 static const int JRADIUS_PORT         = 1814;
59 static const int HALF_MESSAGE_LEN     = 16384;
60 static const int MESSAGE_LEN          = 32768;
61
62 static const int JRADIUS_authenticate = 1;
63 static const int JRADIUS_authorize    = 2;
64 static const int JRADIUS_preacct      = 3;
65 static const int JRADIUS_accounting   = 4;
66 static const int JRADIUS_checksimul   = 5;
67 static const int JRADIUS_pre_proxy    = 6;
68 static const int JRADIUS_post_proxy   = 7;
69 static const int JRADIUS_post_auth    = 8;
70 #ifdef WITH_COA
71 static const int JRADIUS_recv_coa     = 9;
72 static const int JRADIUS_send_coa     = 10;
73 #endif
74
75 #define LOG_PREFIX  "rlm_jradius: "
76 #define MAX_HOSTS   4
77
78 typedef struct jradius_socket {
79   int  id;
80 #ifdef HAVE_PTHREAD_H
81   pthread_mutex_t mutex;
82 #endif
83   struct jradius_socket *next;
84   enum { is_connected, not_connected } state;
85   
86   union {
87     int sock;
88   } con;
89 } JRSOCK;
90
91 typedef struct jradius_inst {
92   time_t      connect_after;
93   JRSOCK    * sock_pool;
94   JRSOCK    * last_used;
95
96   char     * name;
97   char     * host   [MAX_HOSTS];
98   uint32_t   ipaddr [MAX_HOSTS];
99   int        port   [MAX_HOSTS];
100   int        timeout;
101   int        read_timeout;
102   int        write_timeout;
103   int        allow_codechange;
104   int        allow_idchange;
105   int        onfail;
106   char     * onfail_s;
107   int        keepalive;
108   int        jrsock_cnt;
109 } JRADIUS;
110
111 typedef struct _byte_array
112 {
113   unsigned int size;
114   unsigned int pos;
115   unsigned int left;
116   unsigned char * b;
117 } byte_array;
118
119 static CONF_PARSER module_config[] = {
120   { "name",         PW_TYPE_STRING_PTR,  offsetof(JRADIUS, name),       NULL,  "localhost"},
121   { "primary",      PW_TYPE_STRING_PTR,  offsetof(JRADIUS, host[0]),    NULL,  "localhost"},
122   { "secondary",    PW_TYPE_STRING_PTR,  offsetof(JRADIUS, host[1]),    NULL,  NULL},
123   { "tertiary",     PW_TYPE_STRING_PTR,  offsetof(JRADIUS, host[2]),    NULL,  NULL},
124   { "timeout",      PW_TYPE_INTEGER,     offsetof(JRADIUS, timeout),    NULL,  "5"},
125   { "read_timeout", PW_TYPE_INTEGER,     offsetof(JRADIUS, read_timeout), NULL,  "90"},
126   { "write_timeout",PW_TYPE_INTEGER,     offsetof(JRADIUS, write_timeout),NULL,  "90"},
127   { "onfail",       PW_TYPE_STRING_PTR,  offsetof(JRADIUS, onfail_s),   NULL,  NULL},
128   { "keepalive",    PW_TYPE_BOOLEAN,     offsetof(JRADIUS, keepalive),  NULL,  "yes"},
129   { "connections",  PW_TYPE_INTEGER,     offsetof(JRADIUS, jrsock_cnt), NULL,  "8"},
130   { "allow_codechange", PW_TYPE_BOOLEAN, offsetof(JRADIUS, allow_codechange),  NULL,  "no"},
131   { "allow_idchange",   PW_TYPE_BOOLEAN, offsetof(JRADIUS, allow_idchange),    NULL,  "no"},
132   { NULL, -1, 0, NULL, NULL }
133 };
134
135 static int
136 sock_read(JRADIUS * inst, JRSOCK *jrsock, uint8_t *b, size_t blen) {
137   int fd = jrsock->con.sock;
138   int timeout = inst->read_timeout;
139   struct timeval tv;
140   ssize_t c;
141   size_t recd = 0;
142   fd_set fds;
143
144   while (recd < blen) {
145
146     tv.tv_sec = timeout;
147     tv.tv_usec = 0;
148     
149     FD_ZERO(&fds);
150     FD_SET(fd, &fds);
151     
152     if (select(fd + 1, &fds, (fd_set *) 0, (fd_set *) 0, &tv) == -1)
153       return -1;
154     
155     if (FD_ISSET(fd, &fds))
156 #ifdef WIN32
157       c = recv(fd, b + recd, blen-recd, 0);
158 #else
159       c = read(fd, b + recd, blen-recd);
160 #endif
161     else
162       return -1;
163
164     if (c <= 0) return -1;
165     recd += c;
166   }
167
168   if (recd < blen) return -1;
169   return recd;
170 }
171
172 static int
173 sock_write(JRADIUS * inst, JRSOCK *jrsock, uint8_t *b, size_t blen) {
174   int fd = jrsock->con.sock;
175   int timeout = inst->write_timeout;
176   struct timeval tv;
177   ssize_t c;
178   size_t sent = 0;
179   fd_set fds;
180
181   while (sent < blen) {
182
183     tv.tv_sec = timeout;
184     tv.tv_usec = 0;
185     
186     FD_ZERO(&fds);
187     FD_SET(fd, &fds);
188     
189     if (select(fd + 1, (fd_set *) 0, &fds, (fd_set *) 0, &tv) == -1)
190       return -1;
191     
192     if (FD_ISSET(fd, &fds)) 
193 #ifdef WIN32
194       c = send(fd, b+sent, blen-sent, 0);
195 #else
196       c = write(fd, b+sent, blen-sent);
197 #endif
198     else
199       return -1;
200
201     if (c <= 0) return -1;
202     sent += c;
203   }
204
205   if (sent != blen) return -1;
206   return sent;
207 }
208
209 static int connect_socket(JRSOCK *jrsock, JRADIUS *inst)
210 {
211   struct sockaddr_in local_addr, serv_addr;
212   int i, connected = 0;
213   char buff[128];
214   int sock;
215
216   /*
217    *     Connect to jradius servers until we succeed or die trying
218    */
219   for (i = 0; !connected && i < MAX_HOSTS && inst->ipaddr[i] > 0; i++) {
220
221     /*
222      *     Allocate a TCP socket
223      */
224     if ((sock = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
225       radlog(L_ERR, LOG_PREFIX "could not allocate TCP socket");
226       goto failed;
227     }
228     
229     /*
230      *     If we have a timeout value set, make the socket non-blocking
231      */
232     if (inst->timeout > 0 &&
233         fcntl(sock, F_SETFL, fcntl(sock, F_GETFL, 0) | O_NONBLOCK) == -1) {
234       radlog(L_ERR, LOG_PREFIX "could not set non-blocking on socket");
235       goto failed;
236     }
237     
238     /*
239      *     Bind to any local port
240      */
241     memset(&local_addr, 0, sizeof(local_addr));
242     local_addr.sin_family = AF_INET;
243     local_addr.sin_addr.s_addr = htonl(INADDR_ANY);
244     local_addr.sin_port = htons(0);
245     
246     if (bind(sock, (struct sockaddr *) &local_addr, sizeof(local_addr)) < 0) {
247       radlog(L_ERR, LOG_PREFIX "could not locally bind TCP socket");
248       goto failed;
249     }
250     
251     /*
252      *     Attempt connection to remote server
253      */
254     memset(&serv_addr, 0, sizeof(serv_addr));
255     serv_addr.sin_family = AF_INET;
256     memcpy((char *) &serv_addr.sin_addr, &(inst->ipaddr[i]), 4);
257     serv_addr.sin_port = htons(inst->port[i]);
258     
259     if (connect(sock, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0) {
260       if (inst->timeout > 0 && (errno == EINPROGRESS || errno == EWOULDBLOCK)) {
261         /*
262          *     Wait to see if non-blocking socket connects or times-out
263          */
264         struct pollfd pfd;
265         memset(&pfd, 0, sizeof(pfd));
266
267         pfd.fd = sock;
268         pfd.events = POLLOUT;
269
270         if (poll(&pfd, 1, inst->timeout * 1000) == 1 && pfd.revents) {
271           /*
272            *     Lets make absolutely sure we are connected
273            */
274           struct sockaddr_in sa;
275           unsigned int salen = sizeof(sa);
276           if (getpeername(sock, (struct sockaddr *) &sa, &salen) != -1) {
277             /*
278              *     CONNECTED! break out of for-loop
279              */
280             connected = 1;
281             break;
282           }
283         }
284       }
285
286       /*
287        *     Timed-out
288        */
289       radlog(L_ERR, LOG_PREFIX "could not connect to %s:%d", 
290              ip_ntoa(buff, inst->ipaddr[i]), inst->port[i]);
291
292     } else {
293       /*
294        *     CONNECTED (instantly)! break out of for-loop
295        */
296       connected = 1;
297       break;
298     }
299
300     /*
301      *     Unable to connect, cleanup and start over
302      */
303     close(sock); sock=0;
304   }
305
306   if (!connected) {
307     radlog(L_ERR, LOG_PREFIX "could not find any jradius server!");
308     goto failed;
309   }
310
311   /*
312    *     If we previously set the socket to non-blocking, restore blocking 
313   if (inst->timeout > 0 &&
314       fcntl(sock, F_SETFL, fcntl(sock, F_GETFL, 0) & ~O_NONBLOCK) == -1) {
315     radlog(L_ERR, LOG_PREFIX "could not set blocking on socket");
316     goto failed;
317   }
318    */
319
320   jrsock->state = is_connected;
321   jrsock->con.sock = sock;
322   return 1;
323
324  failed:
325   if (sock > 0) { shutdown(sock, 2); close(sock); }
326   jrsock->state = not_connected;
327   return 0;
328 }
329
330 static void close_socket(UNUSED JRADIUS * inst, JRSOCK *jrsock)
331 {
332   radlog(L_INFO, "rlm_jradius: Closing JRadius connection %d", jrsock->id);
333   if (jrsock->con.sock > 0) { 
334     shutdown(jrsock->con.sock, 2); 
335     close(jrsock->con.sock); 
336   }
337   jrsock->state = not_connected;
338   jrsock->con.sock = 0;
339 }
340
341 static void free_socket(JRADIUS * inst, JRSOCK *jrsock) {
342   close_socket(inst, jrsock);
343   if (inst->keepalive) {
344 #ifdef HAVE_PTHREAD_H
345     pthread_mutex_destroy(&jrsock->mutex);
346 #endif
347     free(jrsock);
348   }
349 }
350
351 static int init_socketpool(JRADIUS * inst)
352 {
353   int i, rcode;
354   int success = 0;
355   JRSOCK *jrsock;
356   
357   inst->connect_after = 0;
358   inst->sock_pool = NULL;
359   
360   for (i = 0; i < inst->jrsock_cnt; i++) {
361     radlog(L_INFO, "rlm_jradius: starting JRadius connection %d", i);
362     
363     if ((jrsock = rad_malloc(sizeof(*jrsock))) == 0) return -1;
364     
365     memset(jrsock, 0, sizeof(*jrsock));
366     jrsock->id = i;
367     jrsock->state = not_connected;
368
369 #ifdef HAVE_PTHREAD_H
370     rcode = pthread_mutex_init(&jrsock->mutex,NULL);
371     if (rcode != 0) {
372       radlog(L_ERR, "rlm_jradius: Failed to init lock: %s", strerror(errno));
373       return 0;
374     }
375 #endif
376
377     if (time(NULL) > inst->connect_after)
378       if (connect_socket(jrsock, inst))
379         success = 1;
380     
381     jrsock->next = inst->sock_pool;
382     inst->sock_pool = jrsock;
383   }
384   inst->last_used = NULL;
385   
386   if (!success) {
387     radlog(L_DBG, "rlm_jradius: Failed to connect to JRadius server.");
388   }
389   
390   return 1;
391 }
392
393 static void free_socketpool(JRADIUS * inst)
394 {
395   JRSOCK *cur;
396   JRSOCK *next;
397
398   for (cur = inst->sock_pool; cur; cur = next) {
399     next = cur->next;
400     free_socket(inst, cur);
401   }
402   
403   inst->sock_pool = NULL;
404 }
405
406 static JRSOCK * get_socket(JRADIUS * inst)
407 {
408   JRSOCK *cur, *start;
409   int tried_to_connect = 0;
410   int unconnected = 0;
411
412   start = inst->last_used;
413   if (!start) start = inst->sock_pool;
414   
415   cur = start;
416   
417   while (cur) {
418 #ifdef HAVE_PTHREAD_H
419     if (pthread_mutex_trylock(&cur->mutex) != 0) {
420       goto next;
421     } 
422 #endif
423     
424     if ((cur->state == not_connected) && (time(NULL) > inst->connect_after)) {
425       radlog(L_INFO, "rlm_jradius: Trying to (re)connect unconnected handle %d", cur->id);
426       tried_to_connect++;
427       connect_socket(cur, inst);
428     }
429     
430     if (cur->state == not_connected) {
431       radlog(L_DBG, "rlm_jradius: Ignoring unconnected handle %d", cur->id);
432       unconnected++;
433 #ifdef HAVE_PTHREAD_H
434       pthread_mutex_unlock(&cur->mutex);
435 #endif
436       goto next;
437     }
438     
439     radlog(L_DBG, "rlm_jradius: Reserving JRadius socket id: %d", cur->id);
440     
441     if (unconnected != 0 || tried_to_connect != 0) {
442       radlog(L_INFO, "rlm_jradius: got socket %d after skipping %d unconnected handles, tried to reconnect %d though", 
443              cur->id, unconnected, tried_to_connect);
444     }
445
446     inst->last_used = cur->next;
447     return cur;
448     
449   next:
450     cur = cur->next;
451     if (!cur) cur = inst->sock_pool;
452     if (cur == start) break;
453   }
454   
455   radlog(L_INFO, "rlm_jradius: There are no sockets to use! skipped %d, tried to connect %d", 
456          unconnected, tried_to_connect);
457   return NULL;
458 }
459
460 static int release_socket(UNUSED JRADIUS * inst, JRSOCK * jrsock)
461 {
462 #ifdef HAVE_PTHREAD_H
463   pthread_mutex_unlock(&jrsock->mutex);
464 #endif
465   
466   radlog(L_DBG, "rlm_jradius: Released JRadius socket id: %d", jrsock->id);
467   
468   return 0;
469 }
470
471
472 /*
473  *     Initialize the jradius module
474  */
475 static int jradius_instantiate(CONF_SECTION *conf, void **instance)
476 {
477   JRADIUS *inst = (JRADIUS *) instance;
478   char host[128], b[128], *h;
479   int i, p, idx, port;
480
481   inst = rad_malloc(sizeof(JRADIUS));
482   memset(inst, 0, sizeof(JRADIUS));
483
484   if (cf_section_parse(conf, inst, module_config) < 0) {
485     free(inst);
486     return -1;
487   }
488
489   for (i = 0, idx = 0; i < MAX_HOSTS; i++) {
490     if (inst->host[i] && strlen(inst->host[i]) < sizeof(host)) {
491       h = inst->host[i];
492       p = JRADIUS_PORT;
493       
494       strcpy(b, h);
495       if (sscanf(b, "%[^:]:%d", host, &port) == 2) { h = host; p = port; }
496
497       if (h) {
498         fr_ipaddr_t ipaddr;
499         if (ip_hton(h, AF_INET, &ipaddr) < 0) {
500           radlog(L_ERR, "Can't find IP address for host %s", h);
501           continue;
502         }
503         if ((inst->ipaddr[idx] = ipaddr.ipaddr.ip4addr.s_addr) != htonl(INADDR_NONE)) {
504           inst->port[idx] = p;
505           radlog(L_INFO, LOG_PREFIX "configuring jradius server %s:%d", h, p);
506           idx++;
507         } else {
508           radlog(L_ERR, LOG_PREFIX "invalid jradius server %s", h);
509         }
510       }
511     }
512   }
513
514   if (inst->keepalive) init_socketpool(inst);
515
516   inst->onfail = RLM_MODULE_FAIL;
517
518   if (inst->onfail_s) {
519     if      (!strcmp(inst->onfail_s, "NOOP"))    inst->onfail = RLM_MODULE_NOOP;
520     else if (!strcmp(inst->onfail_s, "REJECT"))  inst->onfail = RLM_MODULE_REJECT;
521     else if (!strcmp(inst->onfail_s, "OK"))      inst->onfail = RLM_MODULE_OK;
522     else if (!strcmp(inst->onfail_s, "FAIL"))    inst->onfail = RLM_MODULE_FAIL;
523     else radlog(L_ERR, LOG_PREFIX "invalid jradius 'onfail' state %s", inst->onfail_s);
524   }
525
526   *instance = inst;
527
528   return 0;
529 }
530
531 /*
532  *     Initialize a byte array buffer structure
533  */
534 static void init_byte_array(byte_array * ba, unsigned char *b, int blen)
535 {
536   ba->b = b;
537   ba->size = ba->left = blen;
538   ba->pos = 0;
539 }
540
541 /*
542  *     Pack a single byte into a byte array buffer
543  */
544 static int pack_byte(byte_array * ba, unsigned char c)
545 {
546   if (ba->left < 1) return -1;
547
548   ba->b[ba->pos] = c;
549   ba->pos++;
550   ba->left--;
551
552   return 0;
553 }
554
555 /*
556  *     Pack an array of bytes into a byte array buffer
557  */
558 static int pack_bytes(byte_array * ba, unsigned char *d, unsigned int dlen)
559 {
560   if (ba->left < dlen) return -1;
561
562   memcpy((void *)(ba->b + ba->pos), d, dlen);
563   ba->pos  += dlen;
564   ba->left -= dlen;
565
566   return 0;
567 }
568
569 /*
570  *     Pack an integer into a byte array buffer (adjusting for byte-order)
571  */
572 static int pack_uint32(byte_array * ba, uint32_t i)
573 {
574   if (ba->left < 4) return -1;
575
576   i = htonl(i);
577
578   memcpy((void *)(ba->b + ba->pos), (void *)&i, 4);
579   ba->pos  += 4;
580   ba->left -= 4;
581
582   return 0;
583 }
584
585 /*
586  *     Pack a short into a byte array buffer (adjusting for byte-order)
587  */
588 static int pack_uint16(byte_array * ba, uint16_t i)
589 {
590   if (ba->left < 2) return -1;
591
592   i = htons(i);
593
594   memcpy((void *)(ba->b + ba->pos), (void *)&i, 2);
595   ba->pos  += 2;
596   ba->left -= 2;
597
598   return 0;
599 }
600
601 /*
602  *     Pack a byte into a byte array buffer 
603  */
604 static int pack_uint8(byte_array * ba, uint8_t i)
605 {
606   if (ba->left < 1) return -1;
607
608   memcpy((void *)(ba->b + ba->pos), (void *)&i, 1);
609   ba->pos  += 1;
610   ba->left -= 1;
611
612   return 0;
613 }
614
615 /*
616  *     Pack one byte array buffer into another byte array buffer
617  */
618 static int pack_array(byte_array * ba, byte_array * a)
619 {
620   if (ba->left < a->pos) return -1;
621
622   memcpy((void *)(ba->b + ba->pos), (void *)a->b, a->pos);
623   ba->pos  += a->pos;
624   ba->left -= a->pos;
625
626   return 0;
627 }
628
629 /*
630  *     Pack radius attributes into a byte array buffer
631  */
632 static int pack_vps(byte_array * ba, VALUE_PAIR * vps)
633 {
634   uint32_t i;
635   VALUE_PAIR * vp;
636
637   for (vp = vps; vp != NULL; vp = vp->next) {
638
639     radlog(L_DBG, LOG_PREFIX "packing attribute %s (type: %d; len: %u)",           vp->name, vp->attribute, (unsigned int) vp->length);
640
641     i = vp->attribute;          /* element is int, not uint32_t */
642     if (pack_uint32(ba, i) == -1) return -1;
643     i = vp->length;
644     if (pack_uint32(ba, i) == -1) return -1;
645     i = vp->operator;
646     if (pack_uint32(ba, i) == -1) return -1;
647
648     switch (vp->type) {
649       case PW_TYPE_BYTE:
650         if (pack_uint8(ba, vp->lvalue) == -1) return -1;
651         break;
652       case PW_TYPE_SHORT:
653         if (pack_uint16(ba, vp->lvalue) == -1) return -1;
654         break;
655       case PW_TYPE_INTEGER:
656       case PW_TYPE_DATE:
657         if (pack_uint32(ba, vp->lvalue) == -1) return -1;
658         break;
659       case PW_TYPE_IPADDR:
660         if (pack_bytes(ba, (void *)&vp->vp_ipaddr, vp->length) == -1) return -1;
661         break;
662       default:
663         if (pack_bytes(ba, (void *)vp->vp_octets, vp->length) == -1) return -1;
664         break;
665     }
666   }
667
668   return 0;
669 }
670
671 /*
672  *     Pack a radius packet into a byte array buffer
673  */
674 static int pack_packet(byte_array * ba, RADIUS_PACKET * p)
675 {
676   /*unsigned char code = p->code;*/
677   unsigned char buff[HALF_MESSAGE_LEN];
678   byte_array pba;
679
680   init_byte_array(&pba, buff, sizeof(buff));
681
682   if (pack_vps(&pba, p->vps) == -1) return -1;
683
684   radlog(L_DBG, LOG_PREFIX "packing packet with code: %d (attr length: %d)", p->code, pba.pos);
685
686 #ifdef EXTENDED_FMT
687   if (pack_uint32(ba, p->code) == -1) return -1;
688   if (pack_uint32(ba, p->id) == -1) return -1;
689 #else
690   if (pack_byte(ba, p->code) == -1) return -1;
691   if (pack_byte(ba, p->id) == -1) return -1;
692 #endif
693   if (pack_uint32(ba, pba.pos) == -1) return -1;
694   if (pba.pos == 0) return 0;
695   if (pack_array(ba, &pba) == -1) return -1;
696
697   return 0;
698 }
699
700 static int pack_request(byte_array * ba, REQUEST *r)
701 {
702   unsigned char buff[HALF_MESSAGE_LEN];
703   byte_array pba;
704
705   init_byte_array(&pba, buff, sizeof(buff));
706
707   if (pack_vps(&pba, r->config_items) == -1) return -1;
708   if (pack_uint32(ba, pba.pos) == -1) return -1;
709   if (pba.pos == 0) return 0;
710   if (pack_array(ba, &pba) == -1) return -1;
711       
712   return 0;
713 }
714
715 static uint32_t unpack_uint32(unsigned char *c)
716 {
717   uint32_t ii;
718   memcpy((void *)&ii, c, 4);
719   return ntohl(ii);
720 }
721
722 static uint16_t unpack_uint16(unsigned char *c)
723 {
724   uint16_t ii;
725   memcpy((void *)&ii, c, 2);
726   return ntohs(ii);
727 }
728
729 static uint8_t unpack_uint8(unsigned char *c)
730 {
731   uint8_t ii;
732   memcpy((void *)&ii, c, 1);
733   return ii;
734 }
735
736
737
738 /*
739  *     Read a single byte from socket
740  */
741 static int read_byte(JRADIUS *inst, JRSOCK *jrsock, uint8_t *b)
742 {
743   return (sock_read(inst, jrsock, b, 1) == 1) ? 0 : -1;
744 }
745
746 /*
747  *     Read an integer from the socket (adjusting for byte-order)
748  */
749 static int read_uint32(JRADIUS *inst, JRSOCK *jrsock, uint32_t *i)
750 {
751   uint32_t ii;
752
753   if (sock_read(inst, jrsock, (uint8_t *)&ii, 4) != 4) return -1;
754   *i = ntohl(ii);
755
756   return 0;
757 }
758
759 /*
760  *     Read a value-pair list from the socket
761  */
762 static int read_vps(JRADIUS *inst, JRSOCK *jrsock, VALUE_PAIR **pl, int plen)
763 {
764   VALUE_PAIR *vp;
765   unsigned char buff[MESSAGE_LEN];
766   uint32_t alen, atype, aop;
767   int rlen = 0;
768   
769   while (rlen < plen) {
770     if (read_uint32(inst, jrsock, &atype) == -1) return -1; rlen += 4;
771     if (read_uint32(inst, jrsock, &alen)  == -1) return -1; rlen += 4;
772     if (read_uint32(inst, jrsock, &aop)   == -1) return -1; rlen += 4; 
773
774     radlog(L_DBG, LOG_PREFIX "reading attribute: type=%d; len=%d", atype, alen);
775
776     if (alen >= sizeof(buff)) {
777       radlog(L_ERR, LOG_PREFIX "packet value too large (len: %d)", alen);
778       return -1;
779     }
780
781     if (sock_read(inst, jrsock, buff, alen) != (int)alen) return -1; rlen += alen;
782     buff[alen]=0;
783
784     /*
785      *     Create new attribute
786      */
787     vp = paircreate(atype, 0, -1);
788     vp->operator = aop;
789
790     if (vp->type == -1) {
791       /*
792        *     FreeRADIUS should know about the same attributes that JRadius knows
793        */
794       radlog(L_ERR, LOG_PREFIX "received attribute we do not recognize (type: %d)", atype);
795       pairbasicfree(vp);
796       continue;
797     }
798
799     /*
800      *     WiMAX combo-ip address
801      *     paircreate() cannot recognize the real type of the address.
802      *     ..ugly code...
803      */
804     if (vp->type==PW_TYPE_COMBO_IP) {
805         switch (alen) {
806             case 4:
807                 vp->type = PW_TYPE_IPADDR;
808                 break;
809             case 16:
810                 vp->type = PW_TYPE_IPV6ADDR;
811                 break;
812         }
813     }
814
815     /*
816      *     Fill in the attribute value based on type
817      */
818     switch (vp->type) {
819       case PW_TYPE_BYTE:
820         vp->lvalue = unpack_uint8(buff);
821         vp->length = 1;
822         break;
823
824       case PW_TYPE_SHORT:
825         vp->lvalue = unpack_uint16(buff);
826         vp->length = 2;
827         break;
828
829       case PW_TYPE_INTEGER:
830       case PW_TYPE_DATE:
831         vp->lvalue = unpack_uint32(buff);
832         vp->length = 4;
833         break;
834
835       case PW_TYPE_IPADDR:
836         memcpy((void *)&vp->vp_ipaddr, buff, 4);
837         vp->length = 4;
838         break;
839
840       default:
841         if (alen >= sizeof(vp->vp_octets)) alen = sizeof(vp->vp_octets) - 1;
842         memcpy((void *)vp->vp_octets, buff, alen);
843         vp->length = alen;
844         break;
845     }
846
847     /*
848      *     Add the attribute to the packet
849      */
850     pairadd(pl, vp);
851   } 
852
853   return rlen;
854 }
855
856 /*
857  *     Read a radius packet from the socket
858  */
859 static int read_packet(JRADIUS * inst, JRSOCK *jrsock, RADIUS_PACKET *p)
860 {
861   uint32_t code;
862   uint32_t id;
863   uint32_t plen;
864
865 #ifdef EXTENDED_FMT
866   if (read_uint32(inst, jrsock, &code) == -1) return -1;
867   if (read_uint32(inst, jrsock, &id)   == -1) return -1;
868 #else
869   { uint8_t c = 0;
870   if (read_byte(inst, jrsock, &c) == -1) return -1;
871   code = c;
872   if (read_byte(inst, jrsock, &c) == -1) return -1;
873   id = c; }
874 #endif
875
876   if (read_uint32(inst, jrsock, &plen) == -1) return -1;
877
878   radlog(L_DBG, LOG_PREFIX "reading packet: code=%d len=%d", (int)code, plen);
879
880   if (inst->allow_codechange)
881     if (code != p->code) {
882       radlog(L_INFO, LOG_PREFIX "changing packet code from %d to %d", p->code, code);
883       p->code = code;
884     }
885
886   if (inst->allow_idchange)
887     if ((int)id != p->id) {
888       radlog(L_INFO, LOG_PREFIX "changing packet id from %d to %d", p->id, id);
889       p->id = (int)id;
890     }
891   
892   /*
893    *     Delete previous attribute list
894    */
895   pairfree(&p->vps);
896
897   if (plen == 0) return 0;
898
899   if (read_vps(inst, jrsock, &p->vps, plen) == -1) return -1;
900
901   return 0;
902 }
903
904 static int read_request(JRADIUS *inst, JRSOCK *jrsock, REQUEST *p)
905 {
906   unsigned int plen;
907
908   if (read_uint32(inst, jrsock, &plen) == -1) return -1;
909
910   radlog(L_DBG, LOG_PREFIX "reading request: config_item: len=%d", plen);
911
912   /*
913    *     Delete previous attribute list
914    */
915   pairfree(&p->config_items);
916
917   if (plen == 0) return 0;
918
919   if (read_vps(inst, jrsock, &p->config_items, plen) == -1) return -1;
920
921   return 0;
922 }
923
924 static int rlm_jradius_call(char func, void *instance, REQUEST *req, int isproxy)
925 {
926   JRADIUS        * inst    = instance;
927   RADIUS_PACKET  * request = req->packet;
928   RADIUS_PACKET  * reply   = req->reply;
929   JRSOCK         * jrsock  = 0;
930   JRSOCK           sjrsock;
931
932   int exitstatus = inst->onfail;
933   unsigned char rcode, pcount;
934
935   unsigned char buff[MESSAGE_LEN];
936   byte_array ba;
937
938   char * n = inst->name;
939   unsigned int nlen = strlen(n);
940   const char * err = 0;
941   int rc, attempt2=0;
942
943 #define W_ERR(s) { err=s; goto packerror;  }
944 #define R_ERR(s) { err=s; goto parseerror; }
945
946 #ifdef WITH_PROXY
947   if (isproxy) {
948           request = req->proxy;
949           reply   = req->proxy_reply;
950   }
951 #endif
952
953   if (inst->keepalive) {
954     jrsock = get_socket(inst);
955     if (!jrsock) return exitstatus;
956   } else {
957     jrsock = &sjrsock;
958     memset(jrsock, 0, sizeof(*jrsock));
959     jrsock->state = not_connected;
960   }
961
962   init_byte_array(&ba, buff, sizeof(buff));
963
964   pcount = 0;
965   if (request) pcount++;
966   if (reply) pcount++;
967
968   /*
969    *     Create byte array to send to jradius
970    */
971   if ((rc = pack_uint32 (&ba, nlen))                  == -1)  W_ERR("pack_uint32(nlen)");
972   if ((rc = pack_bytes  (&ba, (void *)n, nlen))       == -1)  W_ERR("pack_bytes(name)");
973   if ((rc = pack_byte   (&ba, func))                  == -1)  W_ERR("pack_byte(fun)");
974   if ((rc = pack_byte   (&ba, pcount))                == -1)  W_ERR("pack_byte(pcnt)");
975   if (pcount > 0 && (rc = pack_packet (&ba, request)) == -1)  W_ERR("pack_packet(req)");
976   if (pcount > 1 && (rc = pack_packet (&ba, reply))   == -1)  W_ERR("pack_packet(rep)");
977   if ((rc = pack_request(&ba, req))                   == -1)  W_ERR("pack_request()");
978
979   /*
980    *     Send data
981    */
982  start_over:
983   if (jrsock->state == not_connected) {
984     if (attempt2) radlog(L_ERR, LOG_PREFIX "reconnecting socket id %d", jrsock->id);
985     if (!connect_socket(jrsock, inst)) {
986       if (attempt2) radlog(L_ERR, LOG_PREFIX "could not reconnect socket %d, giving up", jrsock->id);
987       goto cleanup;
988     }
989   }
990   radlog(L_DBG, LOG_PREFIX "sending %d bytes to socket %d", ba.pos, jrsock->id);
991   if (sock_write(inst, jrsock, ba.b, ba.pos) != (int)ba.pos ||
992       (rc = read_byte(inst, jrsock, &rcode)) == -1) {
993     /*
994      *   With an error on the write or the first read, try closing the socket
995      *   and reconnecting to see if that improves matters any (tries this only once)
996      */
997     radlog(L_ERR, LOG_PREFIX "error sending request with socket %d", jrsock->id);
998     if (!inst->keepalive || attempt2) W_ERR("socket_send/first_read");
999     close_socket(inst, jrsock);
1000     attempt2 = 1;
1001     goto start_over;
1002   }
1003
1004   /*
1005    *     Read result
1006    */
1007   if ((rc = read_byte(inst, jrsock, &pcount)) == -1)  R_ERR("read_byte(pcnt)");
1008
1009   radlog(L_DBG, LOG_PREFIX "return code %d; receiving %d packets", (int)rcode, (int)pcount);
1010
1011   if (pcount > 0 && request) if ((rc = read_packet (inst, jrsock, request)) == -1)  R_ERR("read_packet(req)");
1012   if (pcount > 1 && reply)   if ((rc = read_packet (inst, jrsock, reply))   == -1)  R_ERR("read_packet(rep)");
1013
1014   if ((rc = read_request(inst, jrsock, req)) == -1) R_ERR("read_request()");
1015
1016   /*
1017    *    Since we deleted all the attribute lists in the request,
1018    *    we need to reconfigure a few pointers in the REQUEST object
1019    */
1020   if (req->username) {
1021     req->username = pairfind(request->vps, PW_USER_NAME, 0);
1022   }
1023   if (req->password) {
1024     req->password = pairfind(request->vps, PW_PASSWORD, 0);
1025     if (!req->password) req->password = pairfind(request->vps, PW_CHAP_PASSWORD, 0);
1026   }
1027
1028   /*
1029    *    All done, set return code and cleanup
1030    */
1031   exitstatus = (int)rcode;
1032   goto cleanup;
1033
1034  parseerror:
1035   radlog(L_ERR, LOG_PREFIX "problem parsing the data [%s]",err);
1036   if (inst->keepalive) close_socket(inst, jrsock);
1037   goto cleanup;
1038
1039  packerror:
1040   radlog(L_ERR, LOG_PREFIX "problem packing the data[%s]",err);
1041   if (inst->keepalive) close_socket(inst, jrsock);
1042
1043  cleanup:
1044   if (inst->keepalive) 
1045     release_socket(inst, jrsock);
1046   else  
1047     close_socket(inst, jrsock);
1048
1049   return exitstatus;
1050 }
1051
1052 static int jradius_authenticate(void *instance, REQUEST *request)
1053 {
1054   return rlm_jradius_call(JRADIUS_authenticate, instance, request, 0);
1055 }
1056
1057 static int jradius_authorize(void *instance, REQUEST *request)
1058 {
1059   return rlm_jradius_call(JRADIUS_authorize, instance, request, 0);
1060 }
1061
1062 static int jradius_preacct(void *instance, REQUEST *request)
1063 {
1064   return rlm_jradius_call(JRADIUS_preacct, instance, request, 0);
1065 }
1066
1067 static int jradius_accounting(void *instance, REQUEST *request)
1068 {
1069   return rlm_jradius_call(JRADIUS_accounting, instance, request, 0);
1070 }
1071
1072 static int jradius_checksimul(void *instance, REQUEST *request)
1073 {
1074   return rlm_jradius_call(JRADIUS_checksimul, instance, request, 0);
1075 }
1076
1077 static int jradius_pre_proxy(void *instance, REQUEST *request)
1078 {
1079   return rlm_jradius_call(JRADIUS_pre_proxy, instance, request, 1);
1080 }
1081
1082 static int jradius_post_proxy(void *instance, REQUEST *request)
1083 {
1084   return rlm_jradius_call(JRADIUS_post_proxy, instance, request, 1);
1085 }
1086
1087 static int jradius_post_auth(void *instance, REQUEST *request)
1088 {
1089   return rlm_jradius_call(JRADIUS_post_auth, instance, request, 0);
1090 }
1091
1092 #ifdef WITH_COA
1093 static int jradius_recv_coa(void *instance, REQUEST *request)
1094 {
1095   return rlm_jradius_call(JRADIUS_recv_coa, instance, request, 0);
1096 }
1097 static int jradius_send_coa(void *instance, REQUEST *request)
1098 {
1099   return rlm_jradius_call(JRADIUS_send_coa, instance, request, 0);
1100 }
1101 #endif
1102
1103 static int jradius_detach(void *instance)
1104 {
1105   JRADIUS *inst = (JRADIUS *) instance;
1106   free_socketpool(inst);
1107   free(inst);
1108   return 0;
1109 }
1110
1111 module_t rlm_jradius = {
1112   RLM_MODULE_INIT,
1113   "jradius",
1114   RLM_TYPE_THREAD_SAFE,
1115   jradius_instantiate,
1116   jradius_detach,
1117   {
1118     jradius_authenticate,
1119     jradius_authorize,
1120     jradius_preacct,
1121     jradius_accounting,
1122     jradius_checksimul,
1123     jradius_pre_proxy,
1124     jradius_post_proxy,
1125     jradius_post_auth
1126 #ifdef WITH_COA
1127     , jradius_recv_coa,
1128     jradius_send_coa
1129 #endif
1130   },
1131 };
1132