1 /* This is mod_radius.c -- a GNU Radius interface module for PHP
2 Copyright (C) 2004, 2007 Sergey Sinkovsky
3
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 3 of the License, or
7 (at your option) any later version.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
13
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software Foundation,
16 Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
17
18 #include "php.h"
19 #include "zend_ini.h"
20 #include <radius/radius.h>
21
22
23 ZEND_BEGIN_MODULE_GLOBALS(radius)
24 grad_list_t *resources;
25 ZEND_END_MODULE_GLOBALS(radius)
26
27 #ifdef ZTS
28 # define MRADG(v) TSRMG(radius_globals_id, zend_radius_globals *, v)
29 #else
30 # define MRADG(v) (radius_globals.v)
31 #endif
32
33 ZEND_DECLARE_MODULE_GLOBALS(radius);
34
35 typedef void (*mod_destroy_resource_t)(void *ptr);
36
37 struct mod_resource {
38 void *ptr;
39 mod_destroy_resource_t destr;
40 };
41
42 static void
mod_register_resource(void * ptr,mod_destroy_resource_t destr)43 mod_register_resource(void *ptr, mod_destroy_resource_t destr)
44 {
45 struct mod_resource *mr;
46 if (!MRADG(resources))
47 MRADG(resources) = grad_list_create();
48 mr = malloc(sizeof(*mr));
49 if (!mr)
50 zend_error(E_ERROR, "Not enough memory");
51 mr->ptr = ptr;
52 mr->destr = destr;
53 grad_list_append(MRADG(resources), mr);
54 }
55
56 static int
mod_res_cmp(const void * a,const void * b)57 mod_res_cmp(const void *a, const void *b)
58 {
59 const struct mod_resource *ma = a;
60 const struct mod_resource *mb = b;
61 return ma->ptr != mb->ptr;
62 }
63
64 static void
mod_destroy_resource(void * ptr)65 mod_destroy_resource(void *ptr)
66 {
67 struct mod_resource *res = grad_list_remove(MRADG(resources), ptr,
68 mod_res_cmp);
69 if (res) {
70 res->destr(res->ptr);
71 free(res);
72 }
73 }
74
75 static int
mod_free_res(void * item,void * data)76 mod_free_res(void *item, void *data)
77 {
78 struct mod_resource *res = item;
79 if (res) {
80 res->destr(res->ptr);
81 free(res);
82 }
83 return 0;
84 }
85
86 static void
mod_destroy_all_resources()87 mod_destroy_all_resources()
88 {
89 grad_list_destroy(&MRADG(resources), mod_free_res, NULL);
90 }
91
92
93
94 static void
mod_destroy_queue(void * ptr)95 mod_destroy_queue(void *ptr)
96 {
97 grad_client_destroy_queue((grad_server_queue_t *)ptr);
98 }
99
100
101
102 ZEND_MINFO_FUNCTION(radauthmod);
103 ZEND_MINIT_FUNCTION(radauthmod);
104 ZEND_MSHUTDOWN_FUNCTION(radauthmod);
105 ZEND_RSHUTDOWN_FUNCTION(radauthmod);
106
107 ZEND_FUNCTION(radius_auth_user);
108 ZEND_FUNCTION(radius_query);
109 ZEND_FUNCTION(radius_set_debug);
110 ZEND_FUNCTION(radius_clear_debug);
111 ZEND_FUNCTION(radius_lookup_value);
112 ZEND_FUNCTION(radius_attribute_type);
113
114 static unsigned char radius_query_args_force_ref[] = { 5, BYREF_NONE, BYREF_NONE, BYREF_NONE, BYREF_FORCE, BYREF_FORCE };
115
116 /* compiled function list so Zend knows what's in this module */
117 zend_function_entry radauthmod_functions[] = {
118 ZEND_FE(radius_auth_user, NULL)
119 ZEND_FE(radius_query, radius_query_args_force_ref)
120 ZEND_FE(radius_set_debug, NULL)
121 ZEND_FE(radius_clear_debug, NULL)
122 ZEND_FE(radius_lookup_value, NULL)
123 ZEND_FE(radius_attribute_type, NULL)
124 {NULL, NULL, NULL}
125 };
126
127 zend_module_entry radauthmod_module_entry = {
128 STANDARD_MODULE_HEADER,
129 "RADIUS auth module",
130 radauthmod_functions,
131 ZEND_MINIT(radauthmod),
132 ZEND_MSHUTDOWN(radauthmod),
133 NULL,
134 ZEND_RSHUTDOWN(radauthmod),
135 ZEND_MINFO(radauthmod),
136 NO_VERSION_YET,
137 STANDARD_MODULE_PROPERTIES
138 };
139
ZEND_MINIT_FUNCTION(radauthmod)140 ZEND_MINIT_FUNCTION(radauthmod)
141 {
142 REGISTER_LONG_CONSTANT("RT_ACCESS_REQUEST", RT_ACCESS_REQUEST,
143 CONST_CS | CONST_PERSISTENT);
144 REGISTER_LONG_CONSTANT("RT_ACCESS_ACCEPT", RT_ACCESS_ACCEPT,
145 CONST_CS | CONST_PERSISTENT);
146 REGISTER_LONG_CONSTANT("RT_ACCESS_REJECT", RT_ACCESS_REJECT,
147 CONST_CS | CONST_PERSISTENT);
148 REGISTER_LONG_CONSTANT("RT_ACCOUNTING_REQUEST",
149 RT_ACCOUNTING_REQUEST,
150 CONST_CS | CONST_PERSISTENT);
151 REGISTER_LONG_CONSTANT("RT_ACCOUNTING_RESPONSE",
152 RT_ACCOUNTING_RESPONSE,
153 CONST_CS | CONST_PERSISTENT);
154 REGISTER_LONG_CONSTANT("RT_ACCOUNTING_STATUS",
155 RT_ACCOUNTING_STATUS,
156 CONST_CS | CONST_PERSISTENT);
157 REGISTER_LONG_CONSTANT("RT_PASSWORD_REQUEST", RT_PASSWORD_REQUEST,
158 CONST_CS | CONST_PERSISTENT);
159 REGISTER_LONG_CONSTANT("RT_PASSWORD_ACK", RT_PASSWORD_ACK,
160 CONST_CS | CONST_PERSISTENT);
161 REGISTER_LONG_CONSTANT("RT_PASSWORD_REJECT", RT_PASSWORD_REJECT,
162 CONST_CS | CONST_PERSISTENT);
163 REGISTER_LONG_CONSTANT("RT_ACCOUNTING_MESSAGE",
164 RT_ACCOUNTING_MESSAGE,
165 CONST_CS | CONST_PERSISTENT);
166 REGISTER_LONG_CONSTANT("RT_ACCESS_CHALLENGE", RT_ACCESS_CHALLENGE,
167 CONST_CS | CONST_PERSISTENT);
168 REGISTER_LONG_CONSTANT("RT_STATUS_SERVER", RT_STATUS_SERVER,
169 CONST_CS | CONST_PERSISTENT);
170 REGISTER_LONG_CONSTANT("RT_STATUS_CLIENT", RT_STATUS_CLIENT,
171 CONST_CS | CONST_PERSISTENT);
172
173 /* Not implemented in GNU Radius, but have place in radius.h */
174 REGISTER_LONG_CONSTANT("RT_ASCEND_TERMINATE_SESSION",
175 RT_ASCEND_TERMINATE_SESSION,
176 CONST_CS | CONST_PERSISTENT);
177 REGISTER_LONG_CONSTANT("RT_ASCEND_EVENT_REQUEST",
178 RT_ASCEND_EVENT_REQUEST,
179 CONST_CS | CONST_PERSISTENT);
180 REGISTER_LONG_CONSTANT("RT_ASCEND_EVENT_RESPONSE",
181 RT_ASCEND_EVENT_RESPONSE,
182 CONST_CS | CONST_PERSISTENT);
183 REGISTER_LONG_CONSTANT("RT_ASCEND_ALLOCATE_IP",
184 RT_ASCEND_ALLOCATE_IP,
185 CONST_CS | CONST_PERSISTENT);
186 REGISTER_LONG_CONSTANT("RT_ASCEND_RELEASE_IP",
187 RT_ASCEND_RELEASE_IP,
188 CONST_CS | CONST_PERSISTENT);
189
190 REGISTER_LONG_CONSTANT("PORT_AUTH", GRAD_PORT_AUTH,
191 CONST_CS | CONST_PERSISTENT);
192 REGISTER_LONG_CONSTANT("PORT_ACCT", GRAD_PORT_ACCT,
193 CONST_CS | CONST_PERSISTENT);
194
195 REGISTER_LONG_CONSTANT("TYPE_INVALID", GRAD_TYPE_INVALID,
196 CONST_CS | CONST_PERSISTENT);
197 REGISTER_LONG_CONSTANT("TYPE_INTEGER", GRAD_TYPE_INTEGER,
198 CONST_CS | CONST_PERSISTENT);
199 REGISTER_LONG_CONSTANT("TYPE_IPADDR", GRAD_TYPE_IPADDR,
200 CONST_CS | CONST_PERSISTENT);
201 REGISTER_LONG_CONSTANT("TYPE_STRING", GRAD_TYPE_STRING,
202 CONST_CS | CONST_PERSISTENT);
203 REGISTER_LONG_CONSTANT("TYPE_DATE", GRAD_TYPE_DATE,
204 CONST_CS | CONST_PERSISTENT);
205
206 grad_path_init();
207 srand(time(NULL));
208
209 if (grad_dict_init())
210 zend_error(E_ERROR, "Can't read RADIUS dictionaries");
211
212 return SUCCESS;
213 }
214
ZEND_MSHUTDOWN_FUNCTION(radauthmod)215 ZEND_MSHUTDOWN_FUNCTION(radauthmod)
216 {
217 UNREGISTER_INI_ENTRIES();
218 /* deallocate the dictionary entries */
219 grad_dict_free();
220 grad_path_free();
221 return SUCCESS;
222 }
223
ZEND_RSHUTDOWN_FUNCTION(radauthmod)224 ZEND_RSHUTDOWN_FUNCTION(radauthmod)
225 {
226 mod_destroy_all_resources();
227 }
228
ZEND_MINFO_FUNCTION(radauthmod)229 ZEND_MINFO_FUNCTION(radauthmod)
230 {
231 php_info_print_table_start();
232 php_info_print_table_header(2, "Function", "Support");
233 php_info_print_table_row(2, "Checking user", "Enabled");
234 php_info_print_table_row(2, "Passing additional parameters",
235 "Enabled");
236 php_info_print_table_row(2, "Build ", __DATE__ " " __TIME__);
237
238 php_info_print_table_end();
239 }
240
241 #if COMPILE_DL_FIRST_MODULE
ZEND_GET_MODULE(radauthmod)242 ZEND_GET_MODULE(radauthmod)
243 #endif
244
245 static void
246 put_string(grad_avp_t ** plist, int attrnum, zval * data)
247 {
248 zval dcopy = *data;
249
250 zval_copy_ctor(&dcopy);
251 convert_to_string(&dcopy);
252 grad_avl_add_pair(plist,
253 grad_avp_create_string(attrnum,
254 Z_STRVAL(dcopy)));
255 zval_dtor(&dcopy);
256 }
257
258 static void
put_long(grad_avp_t ** plist,int attrnum,zval * data)259 put_long(grad_avp_t ** plist, int attrnum, zval * data)
260 {
261 long longval;
262 grad_dict_value_t *val = grad_value_name_to_value(Z_STRVAL(*data),
263 attrnum);
264
265 if (val)
266 longval = val->value;
267 else {
268 zval dcopy = *data;
269
270 zval_copy_ctor(&dcopy);
271 convert_to_long(&dcopy);
272 longval = Z_LVAL(dcopy);
273 zval_dtor(&dcopy);
274 }
275 grad_avl_add_pair(plist,
276 grad_avp_create_integer(attrnum, longval));
277 }
278
279 static void
put_ipaddr(grad_avp_t ** plist,int attrnum,zval * data)280 put_ipaddr(grad_avp_t ** plist, int attrnum, zval * data)
281 {
282 zval dcopy = *data;
283
284 zval_copy_ctor(&dcopy);
285 convert_to_string(&dcopy);
286 grad_avl_add_pair(plist,
287 grad_avp_create_integer(attrnum,
288 grad_ip_strtoip(Z_STRVAL
289 (dcopy))));
290 zval_dtor(&dcopy);
291 }
292
293 static void
put_data(grad_avp_t ** plist,grad_dict_attr_t * attr,zval * data)294 put_data(grad_avp_t ** plist, grad_dict_attr_t * attr, zval * data)
295 {
296 switch (attr->type) {
297 case GRAD_TYPE_STRING:
298 put_string(plist, attr->value, data);
299 break;
300
301 case GRAD_TYPE_INTEGER:
302 put_long(plist, attr->value, data);
303 break;
304
305 case GRAD_TYPE_IPADDR:
306 put_ipaddr(plist, attr->value, data);
307 break;
308
309 default:
310 zend_error(E_ERROR,
311 "Unhadled data type for 3rd argument");
312 }
313 }
314
315 int
convert_pair_list(grad_avp_t ** plist,zval * obj)316 convert_pair_list(grad_avp_t ** plist, zval * obj)
317 {
318 zval **data;
319 char *key_str;
320 ulong key_num;
321
322 int i = 0;
323
324 if (obj != NULL) {
325 while (zend_hash_get_current_data(Z_ARRVAL(*obj),
326 (void **) &data)
327 == SUCCESS) {
328 grad_dict_attr_t *attr;
329 grad_avp_t *pair;
330
331 key_str = NULL;
332 zend_hash_get_current_key(Z_ARRVAL(*obj),
333 &key_str, &key_num,
334 0);
335 if (key_str)
336 attr = grad_attr_name_to_dict(key_str);
337 else
338 attr = grad_attr_number_to_dict(key_num);
339 if (!attr) {
340 zend_error(E_ERROR, "Wrong 3rd parameter");
341 }
342
343 if (Z_TYPE_PP(data) == IS_ARRAY) {
344 zval **data2;
345
346 while (zend_hash_get_current_data
347 (Z_ARRVAL(**data),
348 (void **) &data2) == SUCCESS) {
349
350 put_data(plist, attr, *data2);
351 zend_hash_move_forward(Z_ARRVAL(**data));
352 }
353 } else
354 put_data(plist, attr, *data);
355 zend_hash_move_forward(Z_ARRVAL(*obj));
356 }
357 }
358 }
359
360 int
convert_from_pair_list(zval * obj,grad_avp_t * plist)361 convert_from_pair_list(zval * obj, grad_avp_t * plist)
362 {
363 grad_avp_t *p;
364 char ipaddr[GRAD_IPV4_STRING_LENGTH];
365
366 if (array_init(obj) == FAILURE) {
367 zend_error(E_ERROR, "Can't initialize array");
368 }
369
370 for (p = plist; p; p = p->next) {
371 zval **found;
372
373 if (zend_hash_find(Z_ARRVAL(*obj), p->name,
374 strlen(p->name) + 1,
375 (void **) &found) == SUCCESS) {
376 char *str;
377 long lval;
378
379 switch ((*found)->type) {
380 case IS_STRING:
381 str = Z_STRVAL(**found);
382 array_init(*found);
383 add_next_index_string(*found, str, 0);
384 break;
385
386 case IS_LONG:
387 lval = Z_LVAL(**found);
388 array_init(*found);
389 add_next_index_long(*found, lval);
390 break;
391 }
392
393 switch (p->type) {
394 case GRAD_TYPE_STRING:
395 case GRAD_TYPE_DATE:
396 add_next_index_string(*found, p->avp_strvalue,
397 1);
398 break;
399
400 case GRAD_TYPE_INTEGER:
401 case GRAD_TYPE_IPADDR:
402 add_next_index_long(*found, p->avp_lvalue);
403 break;
404 }
405 } else
406 switch (p->type) {
407 case GRAD_TYPE_STRING:
408 case GRAD_TYPE_DATE:
409 add_assoc_string(obj, p->name, p->avp_strvalue,
410 1);
411 break;
412
413 case GRAD_TYPE_INTEGER:
414 case GRAD_TYPE_IPADDR:
415 add_assoc_long(obj, p->name, p->avp_lvalue);
416 break;
417 }
418 }
419
420 }
421
422 char *
get_reply_message(grad_avp_t * plist)423 get_reply_message(grad_avp_t * plist)
424 {
425 grad_avp_t *p;
426 size_t len;
427 char *str, *s;
428
429 for (len = 0, p = plist; p; p = p->next)
430 if (p->attribute == DA_REPLY_MESSAGE)
431 len += p->avp_strlength;
432
433 str = emalloc(len + 1);
434
435 for (s = str, p = plist; p; p = p->next)
436 if (p->attribute == DA_REPLY_MESSAGE) {
437 strcpy(s, p->avp_strvalue);
438 s += strlen(s);
439 }
440
441 *s = 0;
442 return str;
443 }
444
ZEND_FUNCTION(radius_query)445 ZEND_FUNCTION(radius_query)
446 {
447 int port_type;
448 int request_type;
449 zval *pair_list = NULL;
450 zval *reply_pairs = NULL;
451 zval *reply_string = NULL;
452 int reply_code;
453
454 grad_server_queue_t *queue;
455 grad_avp_t *plist = NULL, *p;
456 grad_request_t *reply = NULL;
457
458 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC,
459 "lla|zz",
460 &port_type,
461 &request_type,
462 &pair_list,
463 &reply_pairs,
464 &reply_string) == FAILURE) {
465 return;
466 }
467
468 if (reply_pairs && !PZVAL_IS_REF(reply_pairs)) {
469 zend_error(E_ERROR, "Argument 4 is not passed by reference");
470 }
471
472 if (reply_string && !PZVAL_IS_REF(reply_string)) {
473 zend_error(E_ERROR, "Argument 5 is not passed by reference");
474 }
475
476 queue = grad_client_create_queue(1, 0, 0);
477 mod_register_resource(queue, mod_destroy_queue);
478
479 convert_pair_list(&plist, pair_list);
480 reply = grad_client_send(queue, port_type, request_type, plist);
481
482 if (!reply) {
483 zend_error(E_ERROR, "No reply from servers");
484 }
485
486 reply_code = reply->code;
487 if (reply_string) {
488 ZVAL_STRING(reply_string, get_reply_message(reply->avlist),
489 0);
490 }
491 if (reply_pairs) {
492 convert_from_pair_list(reply_pairs, reply->avlist);
493 }
494
495 grad_avl_free(plist);
496 grad_request_free(reply);
497 mod_destroy_resource(queue);
498
499 RETURN_LONG(reply_code);
500 }
501
ZEND_FUNCTION(radius_set_debug)502 ZEND_FUNCTION(radius_set_debug)
503 {
504 char *levels;
505 int levels_len;
506
507 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC,
508 "s",
509 &levels,
510 &levels_len) == FAILURE)
511 return;
512
513 grad_set_debug_levels(levels);
514 }
515
ZEND_FUNCTION(radius_clear_debug)516 ZEND_FUNCTION(radius_clear_debug)
517 {
518 grad_clear_debug();
519 }
520
ZEND_FUNCTION(radius_lookup_value)521 ZEND_FUNCTION(radius_lookup_value)
522 {
523 char *attribute;
524 int attribute_len;
525
526 char *return_name;
527 grad_dict_value_t *vp;
528 int value;
529
530 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC,
531 "sl",
532 &attribute,
533 &attribute_len,
534 &value) == FAILURE)
535 return;
536
537 vp = grad_value_lookup(value, attribute);
538 if (!vp) {
539 RETURN_NULL();
540 }
541 RETURN_STRING(vp->name, 1);
542 }
543
ZEND_FUNCTION(radius_auth_user)544 ZEND_FUNCTION(radius_auth_user)
545 {
546 long return_code;
547 char *username;
548 int username_len;
549 char *password;
550 int passwd_len;
551 zval *pair_list = NULL;
552
553 grad_server_queue_t *queue;
554 grad_avp_t *plist = NULL, *p;
555 grad_request_t *reply;
556
557 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC,
558 "ss|a",
559 &username,
560 &username_len,
561 &password,
562 &passwd_len,
563 &pair_list) == FAILURE) {
564 return;
565 }
566
567 /* main auth proc */
568
569 queue = grad_client_create_queue(1, 0, 0);
570 mod_register_resource(queue, mod_destroy_queue);
571
572 /* �������� ������ ��� ��� ����������� */
573 convert_pair_list(&plist, pair_list);
574 /* 1. �����: */
575 grad_avl_add_pair(&plist,
576 grad_avp_create_string(DA_USER_NAME, username));
577 /* 2. ������: */
578 grad_avl_add_pair(&plist,
579 grad_avp_create_string(DA_USER_PASSWORD,
580 password));
581
582 /* ������ ��������: */
583 reply = grad_client_send(queue, GRAD_PORT_AUTH, RT_ACCESS_REQUEST, plist);
584
585 /* ������ ������: */
586 if (!reply) {
587 zend_printf("No reply from servers");
588 }
589
590 switch (reply->code) {
591 case RT_ACCESS_ACCEPT:
592 return_code = 0;
593 break;
594
595 case RT_ACCESS_REJECT:
596 return_code = 1;
597 break;
598
599 default:
600 return_code = 1;
601 zend_printf("Authentication filed with code %s\n",
602 grad_request_code_to_name(reply->code));
603 }
604
605 grad_avl_free(plist);
606 grad_request_free(reply);
607 mod_destroy_resource(queue);
608 RETURN_BOOL(return_code == 0);
609 }
610
ZEND_FUNCTION(radius_attribute_type)611 ZEND_FUNCTION(radius_attribute_type)
612 {
613 char *attrname;
614 int attrname_len;
615 grad_dict_attr_t *attr;
616 int type;
617
618 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC,
619 "s",
620 &attrname,
621 &attrname_len) == FAILURE)
622 return;
623
624 attr = grad_attr_name_to_dict(attrname);
625 if (!attr)
626 type = GRAD_TYPE_INVALID;
627 else
628 type = attr->type;
629 RETURN_LONG(type);
630 }
631
632 #ifdef __DEBUG
633 /* Guess what we're using it for */
wd()634 wd()
635 {
636 int volatile _st = 0;
637
638 printf("PID %lu\n", getpid());
639 while (!_st)
640 sleep(1);
641 }
642 #endif
643