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