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