DTS Application Library  0.2.3
Application library containing referenced objects and interfaces to common libraries
 All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Groups Pages
radius.c
Go to the documentation of this file.
1 /*
2 Copyright (C) 2012 Gregory Nietsky <gregory@distrotetch.co.za>
3  http://www.distrotech.co.za
4 
5 This program is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation, either version 3 of the License, or
8 (at your option) any later version.
9 
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14 
15 You should have received a copy of the GNU General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18 
29 #include <string.h>
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <unistd.h>
33 #include <errno.h>
34 
35 #ifdef __WIN32__
36 #include <winsock2.h>
37 #else
38 #include <arpa/inet.h>
39 #endif
40 #include <uuid/uuid.h>
41 #include <openssl/md5.h>
42 #include "include/dtsapp.h"
43 
45 struct radius_packet {
48  unsigned char code;
50  unsigned char id;
52  unsigned short len;
54  unsigned char token[RAD_AUTH_TOKEN_LEN];
57 };
58 
65  unsigned short id;
67  unsigned char request[RAD_AUTH_TOKEN_LEN];
69  void *cb_data;
73  unsigned int olen;
77  struct timeval sent;
79  const char *passwd;
81  char retries;
83  char minserver;
84 };
85 
91  struct fwsocket *socket;
93  unsigned char id;
98 };
99 
107  const char *name;
109  const char *authport;
111  const char *acctport;
113  const char *secret;
115  unsigned char id;
117  int timeout;
119  struct timeval service;
122 };
123 
124 static struct bucket_list *servers = NULL;
125 
126 static struct radius_connection *radconnect(struct radius_server *server);
127 
128 static unsigned char *addradattr(struct radius_packet *packet, char type, unsigned char *val, char len) {
129  unsigned char *data = packet->attrs + packet->len - RAD_AUTH_HDR_LEN;
130 
131  if (!len) {
132  return NULL;
133  }
134 
135  data[0] = type;
136  data[1] = len + 2;
137  if (val) {
138  memcpy(data + 2, val, len);
139  }
140 
141  packet->len += data[1];
142  return (data);
143 }
144 
149 extern void addradattrint(struct radius_packet *packet, char type, unsigned int val) {
150  unsigned int tval;
151 
152  tval = htonl(val);
153  addradattr(packet, type, (unsigned char *)&tval, sizeof(tval));
154 }
155 
160 extern void addradattrip(struct radius_packet *packet, char type, char *ipaddr) {
161  unsigned int tval;
162 
163  tval = inet_addr(ipaddr);
164  addradattr(packet, type, (unsigned char *)&tval, sizeof(tval));
165 }
166 
171 extern void addradattrstr(struct radius_packet *packet, char type, char *str) {
172  addradattr(packet, type, (unsigned char *)str, strlen(str));
173 }
174 
175 static void addradattrpasswd(struct radius_packet *packet, const char *pw, const char *secret) {
176  unsigned char pwbuff[RAD_MAX_PASS_LEN];
177  unsigned char digest[RAD_AUTH_TOKEN_LEN];
178  MD5_CTX c, old;
179  int len, n, i;
180 
181  len = strlen(pw);
182  if (len > RAD_MAX_PASS_LEN) {
183  len = RAD_MAX_PASS_LEN;
184  }
185 
186  memcpy(pwbuff, pw, len);
187  memset(pwbuff+len, 0, RAD_MAX_PASS_LEN -len);
188 
189  /* pad len to RAD_AUTH_TOKEN_LEN*/
190  if (!len) {
191  len = RAD_AUTH_TOKEN_LEN;
192  } else
193  if (!(len & 0xf)) {
194  len += 0xf;
195  len &= ~0xf;
196  }
197 
198  MD5_Init(&c);
199  MD5_Update(&c, secret, strlen(secret));
200  old = c;
201 
202  MD5_Update(&c, packet->token, RAD_AUTH_TOKEN_LEN);
203  for (n = 0; n < len; n += RAD_AUTH_TOKEN_LEN) {
204  if (n > 0) {
205  c = old;
206  MD5_Update(&c, pwbuff + n - RAD_AUTH_TOKEN_LEN, RAD_AUTH_TOKEN_LEN);
207  }
208  MD5_Final(digest, &c);
209  for (i = 0; i < RAD_AUTH_TOKEN_LEN; i++) {
210  pwbuff[i + n] ^= digest[i];
211  }
212  }
213  addradattr(packet, RAD_ATTR_USER_PASSWORD, pwbuff, len);
214 }
215 
221 extern struct radius_packet *new_radpacket(unsigned char code) {
222  struct radius_packet *packet;
223 
224  if ((packet = malloc(sizeof(*packet)))) {
225  memset(packet, 0, sizeof(*packet));
226  packet->len = RAD_AUTH_HDR_LEN;
227  packet->code = code;
228  genrand(&packet->token, RAD_AUTH_TOKEN_LEN);
229  }
230  return (packet);
231 }
232 
233 static int32_t hash_session(const void *data, int key) {
234  unsigned int ret;
235  const struct radius_session *session = data;
236  const unsigned char *hashkey = (key) ? data : &session->id;
237 
238  ret = *hashkey << 24;
239 
240  return (ret);
241 }
242 
243 static int32_t hash_connex(const void *data, int key) {
244  int ret;
245  const struct radius_connection *connex = data;
246  const int *hashkey = (key) ? data : &connex->socket;
247 
248  ret = *hashkey;
249 
250  return (ret);
251 }
252 
253 static int32_t hash_server(const void *data, int key) {
254  int ret;
255  const struct radius_server *server = data;
256  const unsigned char *hashkey = (key) ? data : &server->id;
257 
258  ret = *hashkey;
259 
260  return(ret);
261 }
262 
263 static void del_radserver(void *data) {
264  struct radius_server *server = data;
265 
266  if (server->name) {
267  free((char *)server->name);
268  }
269  if (server->authport) {
270  free((char *)server->authport);
271  }
272  if (server->acctport) {
273  free((char *)server->acctport);
274  }
275  if (server->secret) {
276  free((char *)server->secret);
277  }
278  if (server->connex) {
279  objunref(server->connex);
280  }
281 }
282 
289 extern void add_radserver(const char *ipaddr, const char *auth, const char *acct, const char *secret, int timeout) {
290  struct radius_server *server;
291 
292  if ((server = objalloc(sizeof(*server), del_radserver))) {
293  ALLOC_CONST(server->name, ipaddr);
294  ALLOC_CONST(server->authport, auth);
295  ALLOC_CONST(server->acctport, acct);
296  ALLOC_CONST(server->secret, secret);
297  if (!servers) {
298  servers = create_bucketlist(0, hash_server);
299  }
300  server->id = bucket_list_cnt(servers);
301  server->timeout = timeout;
302  gettimeofday(&server->service, NULL);
303  addtobucket(servers, server);
304  }
305 
306  objunref(server);
307 }
308 
309 static void del_radsession(void *data) {
310  struct radius_session *session = data;
311 
312  if (session->passwd) {
313  free((void *)session->passwd);
314  }
315  if (session->packet) {
316  free(session->packet);
317  }
318 }
319 
320 static struct radius_session *rad_session(struct radius_packet *packet, struct radius_connection *connex,
321  const char *passwd, radius_cb read_cb, void *cb_data) {
322  struct radius_session *session = NULL;
323 
324  if ((session = objalloc(sizeof(*session), del_radsession))) {
325  if (!connex->sessions) {
326  connex->sessions = create_bucketlist(4, hash_session);
327  }
328  memcpy(session->request, packet->token, RAD_AUTH_TOKEN_LEN);
329  session->id = packet->id;
330  session->packet = packet;
331  session->read_cb = read_cb;
332  session->cb_data = cb_data;
333  session->olen = packet->len;
334  session->retries = 2;
335  ALLOC_CONST(session->passwd, passwd);
336  addtobucket(connex->sessions, session);
337  }
338  return (session);
339 }
340 
341 static int _send_radpacket(struct radius_packet *packet, const char *userpass, struct radius_session *hint,
342  radius_cb read_cb, void *cb_data) {
343  int scnt;
344  unsigned char *vector;
345  unsigned short len;
346  struct radius_server *server;
347  struct radius_session *session;
348  struct radius_connection *connex;
349  struct bucket_loop *sloop, *cloop;
350  struct timeval curtime;
351 
352 
353  gettimeofday(&curtime, NULL);
354  sloop = init_bucket_loop(servers);
355  objref(hint);
356  while (sloop && (server = next_bucket_loop(sloop))) {
357  objlock(server);
358  if ((hint && (server->id <= hint->minserver)) ||
359  (server->service.tv_sec > curtime.tv_sec)) {
360  objunlock(server);
361  objunref(server);
362  continue;
363  }
364  if (!server->connex) {
365  connex = radconnect(server);
366  objunref(connex);
367  objunlock(server);
368  objref(server);
369  } else {
370  objunlock(server);
371  }
372  cloop = init_bucket_loop(server->connex);
373  while (cloop && (connex = next_bucket_loop(cloop))) {
374  objlock(connex);
375  if (connex->sessions && (bucket_list_cnt(connex->sessions) > 254)) {
376  objunlock(connex);
377  objunref(connex);
378  /* if im overflowing get next or add new*/
379  objlock(server);
380  if (!(connex = next_bucket_loop(cloop))) {
381  if ((connex = radconnect(server))) {
382  objunlock(server);
383  objref(server);
384  } else {
385  break;
386  }
387  } else {
388  objunlock(server);
389  }
390  objlock(connex);
391  }
392 
393  connex->id++;
394  if (hint) {
395  packet = hint->packet;
396  session = hint;
397  packet->id = connex->id;
398  session->id = packet->id;
399  session->retries = 2;
400  if (!connex->sessions) {
401  connex->sessions = create_bucketlist(4, hash_session);
402  }
403  addtobucket(connex->sessions, session);
404  } else {
405  packet->id = connex->id;
406  session = rad_session(packet, connex, userpass, read_cb, cb_data);
407  }
408  session->minserver = server->id;
409  objunlock(connex);
410 
411  if (session->passwd) {
412  addradattrpasswd(packet, session->passwd, server->secret);
413  }
414 
415  vector = addradattr(packet, RAD_ATTR_MESSAGE, NULL, RAD_AUTH_TOKEN_LEN);
416  len = packet->len;
417  packet->len = htons(len);
418  md5hmac(vector + 2, packet, len, server->secret, strlen(server->secret));
419 
420  scnt = send(connex->socket->sock, packet, len, 0);
421  memset(packet->attrs + session->olen - RAD_AUTH_HDR_LEN, 0, len - session->olen);
422  packet->len = session->olen;
423 
424  objunref(connex);
425  if (len == scnt) {
426  session->sent = curtime;
427  objunref(session);
428  objunref(server);
429  objunref(hint);
430  objunref(cloop);
431  objunref(sloop);
432  return (0);
433  } else {
434  remove_bucket_item(connex->sessions, session);
435  }
436  }
437  objunref(server);
438  objunref(cloop);
439  }
440  objunref(sloop);
441  objunref(hint);
442 
443  return (-1);
444 }
445 
452 extern int send_radpacket(struct radius_packet *packet, const char *userpass, radius_cb read_cb, void *cb_data) {
453  return (_send_radpacket(packet, userpass, NULL, read_cb, cb_data));
454 }
455 
456 static int resend_radpacket(struct radius_session *session) {
457  return (_send_radpacket(NULL, NULL, session, NULL, NULL));
458 }
459 
460 static void rad_resend(struct radius_connection *connex) {
461  struct radius_session *session;
462  struct bucket_loop *bloop;
463  struct timeval tv;
464  unsigned int tdiff, len, scnt;
465  unsigned char *vector;
466 
467  gettimeofday(&tv, NULL);
468 
469  bloop=init_bucket_loop(connex->sessions);
470  while (bloop && (session = next_bucket_loop(bloop))) {
471  tdiff = tv.tv_sec - session->sent.tv_sec;
472  if (tdiff > 3) {
473  if (!session->retries) {
474  remove_bucket_loop(bloop);
475  resend_radpacket(session);
476  objunref(session);
477  continue;
478  }
479 
480  if (session->passwd) {
481  addradattrpasswd(session->packet, session->passwd, connex->server->secret);
482  }
483 
484  vector = addradattr(session->packet, RAD_ATTR_MESSAGE, NULL, RAD_AUTH_TOKEN_LEN);
485  len = session->packet->len;
486  session->packet->len = htons(len);
487  md5hmac(vector + 2, session->packet, len, connex->server->secret, strlen(connex->server->secret));
488 
489  scnt = send(connex->socket->sock, session->packet, len, 0);
490  memset(session->packet->attrs + session->olen - RAD_AUTH_HDR_LEN, 0, len - session->olen);
491  session->packet->len = session->olen;
492  session->sent = tv;
493  session->retries--;
494  if (scnt != len) {
495  remove_bucket_loop(bloop);
496  resend_radpacket(session);
497  objunref(session);
498  }
499  }
500  objunref(session);
501  }
502  objunref(bloop);
503 }
504 
505 static void radius_recv(void **data) {
506  struct radius_connection *connex = *data;
507  struct radius_packet *packet;
508  unsigned char buff[RAD_AUTH_PACKET_LEN];
509  unsigned char rtok[RAD_AUTH_TOKEN_LEN];
510  unsigned char rtok2[RAD_AUTH_TOKEN_LEN];
511  struct radius_session *session;
512  int chk, plen;
513 
514  chk = recv(connex->socket->sock, buff, 4096, 0);
515 
516  if (chk < 0) {
517  if (errno == ECONNREFUSED) {
518  printf("Connection Bad\n");
519  }
520  } else
521  if (chk == 0) {
522  objlock(connex->server);
523  printf("Taking server off line for %is\n", connex->server->timeout);
524  gettimeofday(&connex->server->service, NULL);
525  connex->server->service.tv_sec += connex->server->timeout;
526  objunlock(connex->server);
527  }
528 
529  packet = (struct radius_packet *)&buff;
530  plen = ntohs(packet->len);
531 
532  if ((chk < plen) || (chk <= RAD_AUTH_HDR_LEN)) {
533  printf("OOps Did not get proper packet\n");
534  return;
535  }
536 
537  memset(buff + plen, 0, RAD_AUTH_PACKET_LEN - plen);
538 
539  if (!(session = bucket_list_find_key(connex->sessions, &packet->id))) {
540  printf("Could not find session\n");
541  return;
542  }
543 
544  memcpy(rtok, packet->token, RAD_AUTH_TOKEN_LEN);
545  memcpy(packet->token, session->request, RAD_AUTH_TOKEN_LEN);
546  md5sum2(rtok2, packet, plen, connex->server->secret, strlen(connex->server->secret));
547 
548  if (md5cmp(rtok, rtok2)) {
549  printf("Invalid Signature");
550  return;
551  }
552 
553  if (session->read_cb) {
554  packet->len = plen;
555  session->read_cb(packet, session->cb_data);
556  }
557 
558  remove_bucket_item(connex->sessions, session);
559  objunref(session);
560 }
561 
562 static void *rad_return(void *data) {
563  struct radius_connection *connex = data;
564  fd_set rd_set, act_set;
565  struct timeval tv;
566  int selfd;
567 
568  FD_ZERO(&rd_set);
569  FD_SET(connex->socket->sock, &rd_set);
570 
571  while (framework_threadok()) {
572  act_set = rd_set;
573  tv.tv_sec = 0;
574  tv.tv_usec = 200000;
575 
576  selfd = select(connex->socket->sock + 1, &act_set, NULL, NULL, &tv);
577 
578  if ((selfd < 0 && errno == EINTR) || (!selfd)) {
579  rad_resend(connex);
580  continue;
581  } else
582  if (selfd < 0) {
583  break;
584  }
585 
586  if (FD_ISSET(connex->socket->sock, &act_set)) {
587  radius_recv(data);
588  }
589  rad_resend(connex);
590  }
591 
592  return NULL;
593 }
594 
595 static void del_radconnect(void *data) {
596  struct radius_connection *connex = data;
597 
598  objunref(connex->server);
599  objunref(connex->sessions);
600  objunref(connex->socket);
601 }
602 
603 static struct radius_connection *radconnect(struct radius_server *server) {
604  struct radius_connection *connex;
605  int val = 1;
606 
607  if ((connex = objalloc(sizeof(*connex), del_radconnect))) {
608  if ((connex->socket = udpconnect(server->name, server->authport, NULL))) {
609  if (!server->connex) {
610  server->connex = create_bucketlist(0, hash_connex);
611  }
612  setsockopt(connex->socket->sock, SOL_IP, IP_RECVERR,(char *)&val, sizeof(val));
613  connex->server = server;
614  genrand(&connex->id, sizeof(connex->id));
615  addtobucket(server->connex, connex);
616  framework_mkthread(rad_return, NULL, NULL, connex, 0);
617  }
618  }
619  return (connex);
620 }
621 
627 extern unsigned char *radius_attr_first(struct radius_packet *packet) {
628  return (packet->attrs);
629 }
630 
635 extern unsigned char *radius_attr_next(struct radius_packet *packet, unsigned char *attr) {
636  int offset = (packet->len - RAD_AUTH_HDR_LEN) - (attr - packet->attrs);
637 
638  if (!(offset - attr[1])) {
639  return NULL;
640  }
641 
642  return (attr + attr[1]);
643 }
644 
int md5cmp(unsigned char *digest1, unsigned char *digest2)
Compare two md5 hashes.
Definition: util.c:223
Bucket iterator.
Definition: refobj.c:97
struct radius_packet * new_radpacket(unsigned char code)
Create a new radius packet.
Definition: radius.c:221
void * create_bucketlist(int bitmask, blisthash hash_function)
Definition: refobj.c:356
struct radius_packet * packet
Radius packet.
Definition: radius.c:75
int timeout
Server timeout.
Definition: radius.c:117
int objref(void *data)
Reference a object.
Definition: refobj.c:153
Radius Packet.
Definition: radius.c:45
unsigned char token[RAD_AUTH_TOKEN_LEN]
Authentification token.
Definition: radius.c:54
void addradattrip(struct radius_packet *packet, char type, char *ipaddr)
Add a integer attribute too the packet.
Definition: radius.c:160
const char * passwd
Password requires special handling.
Definition: radius.c:79
int objlock(void *data)
Lock the reference.
Definition: refobj.c:269
unsigned short len
Packet length.
Definition: radius.c:52
unsigned char id
Connection ID.
Definition: radius.c:93
void addradattrstr(struct radius_packet *packet, char type, char *str)
Add a integer attribute too the packet.
Definition: radius.c:171
#define RAD_AUTH_TOKEN_LEN
Auth token length.
Definition: dtsapp.h:535
Socket data structure.
Definition: dtsapp.h:131
#define RAD_ATTR_MESSAGE
Radius attribute message.
Definition: dtsapp.h:565
Radius session.
Definition: radius.c:63
unsigned short id
Session id.
Definition: radius.c:65
void * objalloc(int size, objdestroy)
Allocate a referenced lockable object.
Definition: refobj.c:129
const char * name
Server name.
Definition: radius.c:107
#define RAD_AUTH_PACKET_LEN
Auth packet length.
Definition: dtsapp.h:532
int bucket_list_cnt(struct bucket_list *blist)
Return number of items in the list.
Definition: refobj.c:552
void md5hmac(unsigned char *buff, const void *data, unsigned long len, const void *key, unsigned long klen)
Hash Message Authentication Codes (HMAC) MD5.
Definition: util.c:290
#define RAD_AUTH_HDR_LEN
Authentification header length.
Definition: dtsapp.h:529
void * next_bucket_loop(struct bucket_loop *bloop)
Return a reference to the next item in the list this could be the first item.
Definition: refobj.c:662
int sock
Socket FD.
Definition: dtsapp.h:133
Radius connection.
Definition: radius.c:89
void add_radserver(const char *ipaddr, const char *auth, const char *acct, const char *secret, int timeout)
Add new radius server to list of servers.
Definition: radius.c:289
void addradattrint(struct radius_packet *packet, char type, unsigned int val)
Add a integer attribute too the packet.
Definition: radius.c:149
DTS Application library API Include file.
unsigned char code
Radius packet code.
Definition: radius.c:48
#define RAD_ATTR_USER_PASSWORD
Radius attribute password.
Definition: dtsapp.h:544
void md5sum2(unsigned char *buff, const void *data, unsigned long len, const void *data2, unsigned long len2)
Calculate the MD5 hash accross 2 data chunks.
Definition: util.c:185
unsigned int olen
Original length of packet.
Definition: radius.c:73
struct thread_pvt * framework_mkthread(threadfunc, threadcleanup, threadsighandler, void *data, int flags)
create a thread result must be unreferenced
Definition: thread.c:387
struct bucket_list * connex
Bucket list of connextions.
Definition: radius.c:121
int genrand(void *buf, int len)
Generate random sequence.
Definition: util.c:82
#define RAD_MAX_PASS_LEN
Auth max password length.
Definition: dtsapp.h:538
struct fwsocket * udpconnect(const char *ipaddr, const char *port, void *ssl)
UDP Socket client.
Definition: socket.c:262
unsigned char id
Packet ID.
Definition: radius.c:50
const char * secret
Server secret.
Definition: radius.c:113
char retries
Retries available.
Definition: radius.c:81
const char * authport
Server authport.
Definition: radius.c:109
struct timeval service
Server out of service time.
Definition: radius.c:119
unsigned char id
Server hash based on server count.
Definition: radius.c:115
int objunlock(void *data)
Unlock a reference.
Definition: refobj.c:301
const char * acctport
Server accounting port.
Definition: radius.c:111
int framework_threadok(void)
let threads check there status.
Definition: thread.c:143
struct bucket_list * sessions
Bucket list of sessions.
Definition: radius.c:97
unsigned char * radius_attr_first(struct radius_packet *packet)
Return first packet attribute.
Definition: radius.c:627
void * cb_data
Callback data passed to callback.
Definition: radius.c:69
unsigned char request[RAD_AUTH_TOKEN_LEN]
Radius request auth token.
Definition: radius.c:67
struct timeval sent
Time packet was sent.
Definition: radius.c:77
void(* radius_cb)(struct radius_packet *, void *)
Callback to call when response arrives.
Definition: dtsapp.h:306
struct radius_server * server
Reference to radius server.
Definition: radius.c:95
void remove_bucket_loop(struct bucket_loop *bloop)
Safely remove a item from a list while iterating in a loop.
Definition: refobj.c:710
Radius Server.
Definition: radius.c:105
char minserver
Minimum id of server to use.
Definition: radius.c:83
#define ALLOC_CONST(const_var, val)
Macro to assign values to char const.
Definition: dtsapp.h:959
radius_cb read_cb
Radius callback.
Definition: radius.c:71
int addtobucket(struct bucket_list *blist, void *data)
Add a reference to the bucketlist.
Definition: refobj.c:428
void * bucket_list_find_key(struct bucket_list *list, const void *key)
Find and return a reference to a item matching supplied key.
Definition: refobj.c:572
void remove_bucket_item(struct bucket_list *blist, void *data)
Remove and unreference a item from the list.
Definition: refobj.c:517
struct fwsocket * socket
Reference to socket.
Definition: radius.c:91
int send_radpacket(struct radius_packet *packet, const char *userpass, radius_cb read_cb, void *cb_data)
Send radius packet.
Definition: radius.c:452
int objunref(void *data)
Drop reference held.
Definition: refobj.c:184
unsigned char attrs[RAD_AUTH_PACKET_LEN-RAD_AUTH_HDR_LEN]
Radius Attributes.
Definition: radius.c:56
unsigned char * radius_attr_next(struct radius_packet *packet, unsigned char *attr)
Return next packet attribute.
Definition: radius.c:635
struct bucket_loop * init_bucket_loop(struct bucket_list *blist)
Create a bucket list iterator to safely iterate the list.
Definition: refobj.c:640
Bucket list, hold hashed objects in buckets.
Definition: refobj.c:75