packet.c 12.2 KB
Newer Older
K
Kozlov Dmitry 已提交
1 2 3 4
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
D
Dmitry Kozlov 已提交
5 6
#include <stdio.h>
#include <unistd.h>
7
#include <fcntl.h>
D
Dmitry Kozlov 已提交
8 9

#include "log.h"
K
Kozlov Dmitry 已提交
10

11
#include "radius_p.h"
K
Kozlov Dmitry 已提交
12

D
Dmitry Kozlov 已提交
13 14
#include "memdebug.h"

15 16 17 18
struct rad_packet_t *rad_packet_alloc(int code)
{
	struct rad_packet_t *pack;

D
Dmitry Kozlov 已提交
19
	pack = _malloc(sizeof(*pack));
20
	if (!pack) {
21
		log_emerg("radius:packet: out of memory\n");
22 23 24 25 26 27 28 29 30 31 32
		return NULL;
	}

	memset(pack, 0, sizeof(*pack));
	pack->code = code;
	pack->len = 20;
	pack->id = 1;
	INIT_LIST_HEAD(&pack->attrs);

	return pack;
}
K
Kozlov Dmitry 已提交
33

34 35 36 37 38 39 40 41 42
void print_buf(uint8_t *buf,int size)
{
	int i;
	for(i=0;i<size;i++)
		printf("%x ",buf[i]);
	printf("\n");
}

int rad_packet_build(struct rad_packet_t *pack, uint8_t *RA)
K
Kozlov Dmitry 已提交
43
{
44
	struct rad_attr_t *attr;
K
Kozlov Dmitry 已提交
45 46
	uint8_t *ptr;

47
	if (pack->buf)
D
Dmitry Kozlov 已提交
48
		ptr = _realloc(pack->buf, pack->len);
49
	else
D
Dmitry Kozlov 已提交
50
		ptr = _malloc(pack->len);
51

K
Kozlov Dmitry 已提交
52
	if (!ptr) {
53
		log_emerg("radius:packet: out of memory\n");
K
Kozlov Dmitry 已提交
54 55 56
		return -1;
	}
	
57
	pack->buf = ptr;
K
Kozlov Dmitry 已提交
58 59
	*ptr = pack->code; ptr++;
	*ptr = pack->id; ptr++;
60 61
	*(uint16_t*)ptr = htons(pack->len); ptr+= 2;
	memcpy(ptr, RA, 16);	ptr+=16;
K
Kozlov Dmitry 已提交
62 63

	list_for_each_entry(attr, &pack->attrs, entry) {
64 65 66 67 68
		if (attr->vendor) {
			*ptr = 26; ptr++;
			*ptr = attr->len + 2 + 6; ptr++;
			*(uint32_t *)ptr = htonl(attr->vendor->id); ptr+=4;
		} 
69 70
		*ptr = attr->attr->id; ptr++;
		*ptr = attr->len + 2; ptr++;
D
Dmitry Kozlov 已提交
71
		switch(attr->attr->type) {
K
Kozlov Dmitry 已提交
72
			case ATTR_TYPE_INTEGER:
73
				*(uint32_t*)ptr = htonl(attr->val.integer);
K
Kozlov Dmitry 已提交
74
				break;
75
			case ATTR_TYPE_OCTETS:
K
Kozlov Dmitry 已提交
76
			case ATTR_TYPE_STRING:
D
Dmitry Kozlov 已提交
77
				memcpy(ptr, attr->val.string, attr->len);
K
Kozlov Dmitry 已提交
78 79 80 81 82
				break;
			case ATTR_TYPE_IPADDR:
				*(in_addr_t*)ptr = attr->val.ipaddr;
				break;
			case ATTR_TYPE_DATE:
83
				*(uint32_t*)ptr = htonl(attr->val.date);
K
Kozlov Dmitry 已提交
84 85
				break;
			default:
86
				log_emerg("radius:packet:BUG: unknown attribute type\n");
K
Kozlov Dmitry 已提交
87 88 89 90 91
				abort();
		}
		ptr += attr->len;
	}

92
	//print_buf(pack->buf, pack->len);
93
	return 0;
K
Kozlov Dmitry 已提交
94 95
}

96
struct rad_packet_t *rad_packet_recv(int fd, struct sockaddr_in *addr)
K
Kozlov Dmitry 已提交
97 98
{
	struct rad_packet_t *pack;
99
	struct rad_attr_t *attr;
K
Kozlov Dmitry 已提交
100
	struct rad_dict_attr_t *da;
101
	struct rad_dict_vendor_t *vendor;
K
Kozlov Dmitry 已提交
102
	uint8_t *ptr;
103
	int n, id, len, vendor_id;
104
	socklen_t addr_len = sizeof(*addr);
K
Kozlov Dmitry 已提交
105

106 107 108
	pack = rad_packet_alloc(0);
	if (!pack)
		return NULL;
K
Kozlov Dmitry 已提交
109

D
Dmitry Kozlov 已提交
110
	pack->buf = _malloc(REQ_LENGTH_MAX);
K
Kozlov Dmitry 已提交
111
	if (!pack->buf) {
112
		log_emerg("radius:packet: out of memory\n");
113
		goto out_err;
K
Kozlov Dmitry 已提交
114 115 116
	}

	while (1) {
117 118 119 120
		if (addr)
			n = recvfrom(fd, pack->buf, REQ_LENGTH_MAX, 0, addr, &addr_len);
		else
			n = read(fd, pack->buf, REQ_LENGTH_MAX);
K
Kozlov Dmitry 已提交
121 122 123
		if (n < 0) {
			if (errno == EINTR)
				continue;
124
			log_ppp_error("radius:packet:read: %s\n", strerror(errno));
K
Kozlov Dmitry 已提交
125 126 127 128 129 130
			goto out_err;
		}
		break;
	}

	if (n < 20) {
131
		log_ppp_warn("radius:packet: short packed received (%i)\n", n);
K
Kozlov Dmitry 已提交
132 133 134 135 136 137 138
		goto out_err;
	}

	ptr = (uint8_t *)pack->buf;

	pack->code = *ptr; ptr++;
	pack->id = *ptr; ptr++;
139
	pack->len = ntohs(*(uint16_t*)ptr); ptr += 2;
K
Kozlov Dmitry 已提交
140 141

	if (pack->len > n) {
142
		log_ppp_warn("radius:packet: short packet received %i, expected %i\n", pack->len, n);
K
Kozlov Dmitry 已提交
143 144 145 146 147 148 149
		goto out_err;
	}

	ptr += 16;
	n -= 20;

	while (n>0) {
150 151 152
		id = *ptr; ptr++;
		len = *ptr - 2; ptr++;
		if (len < 0) {
153
			log_ppp_warn("radius:packet short attribute len received\n");
154 155
			goto out_err;
		}
K
Kozlov Dmitry 已提交
156
		if (2 + len > n) {
157
			log_ppp_warn("radius:packet: too long attribute received (%i, %i)\n", id, len);
K
Kozlov Dmitry 已提交
158 159
			goto out_err;
		}
160 161 162 163 164 165 166
		if (id == 26) {
			vendor_id = ntohl(*(uint32_t *)ptr);
			vendor = rad_dict_find_vendor_id(vendor_id);
			if (vendor) {
				ptr += 4;
				id = *ptr; ptr++;
				len = *ptr - 2; ptr++;
167
				n -= 2 + 4;
168
			} else
169
				log_ppp_warn("radius:packet: vendor %i not found\n", id);
170 171
		} else
			vendor = NULL;
172
		da = rad_dict_find_attr_id(vendor, id);
K
Kozlov Dmitry 已提交
173
		if (da) {
D
Dmitry Kozlov 已提交
174
			attr = _malloc(sizeof(*attr));
K
Kozlov Dmitry 已提交
175
			if (!attr) {
176
				log_emerg("radius:packet: out of memory\n");
K
Kozlov Dmitry 已提交
177 178
				goto out_err;
			}
179 180
			memset(attr, 0, sizeof(*attr));
			attr->vendor = vendor;
K
Kozlov Dmitry 已提交
181 182
			attr->attr = da;
			attr->len = len;
183 184
			switch (da->type) {
				case ATTR_TYPE_STRING:
D
Dmitry Kozlov 已提交
185
					attr->val.string = _malloc(len+1);
186
					if (!attr->val.string) {
187
						log_emerg("radius:packet: out of memory\n");
D
Dmitry Kozlov 已提交
188
						_free(attr);
189 190 191 192 193
						goto out_err;
					}
					memcpy(attr->val.string, ptr, len);
					attr->val.string[len] = 0;
					break;
194
				case ATTR_TYPE_OCTETS:
D
Dmitry Kozlov 已提交
195
					attr->val.octets = _malloc(len);
196
					if (!attr->val.octets) {
197
						log_emerg("radius:packet: out of memory\n");
D
Dmitry Kozlov 已提交
198
						_free(attr);
199 200 201 202
						goto out_err;
					}
					memcpy(attr->val.octets, ptr, len);
					break;				
203 204 205 206 207 208 209 210
				case ATTR_TYPE_DATE:
				case ATTR_TYPE_INTEGER:
					attr->val.integer = ntohl(*(uint32_t*)ptr);
					break;
				case ATTR_TYPE_IPADDR:
					attr->val.integer = *(uint32_t*)ptr;
					break;
			}
K
Kozlov Dmitry 已提交
211 212
			list_add_tail(&attr->entry, &pack->attrs);
		} else
213
			log_ppp_warn("radius:packet: unknown attribute received (%i)\n", id);
K
Kozlov Dmitry 已提交
214 215 216 217 218 219 220 221 222 223 224 225 226
		ptr += len;
		n -= 2 + len;
	}

	return pack;

out_err:
	rad_packet_free(pack);
	return NULL;
}

void rad_packet_free(struct rad_packet_t *pack)
{
227
	struct rad_attr_t *attr;
K
Kozlov Dmitry 已提交
228 229
	
	if (pack->buf)
D
Dmitry Kozlov 已提交
230
		_free(pack->buf);
K
Kozlov Dmitry 已提交
231 232 233 234

	while(!list_empty(&pack->attrs)) {
		attr = list_entry(pack->attrs.next, typeof(*attr), entry);
		list_del(&attr->entry);
D
Dmitry Kozlov 已提交
235 236 237
		if (attr->attr->type == ATTR_TYPE_STRING || attr->attr->type == ATTR_TYPE_OCTETS)
			_free(attr->val.string);
		_free(attr);
K
Kozlov Dmitry 已提交
238 239
	}

D
Dmitry Kozlov 已提交
240
	_free(pack);
K
Kozlov Dmitry 已提交
241
}
242 243 244

void rad_packet_print(struct rad_packet_t *pack, void (*print)(const char *fmt, ...))
{
245
	struct rad_attr_t *attr;
246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261
	struct rad_dict_value_t *val;
	
	print("[RADIUS ");
	switch(pack->code) {
		case CODE_ACCESS_REQUEST:
			print("Access-Request");
			break;
		case CODE_ACCESS_CHALLENGE:
			print("Access-Challenge");
			break;
		case CODE_ACCESS_ACCEPT:
			print("Access-Accept");
			break;
		case CODE_ACCESS_REJECT:
			print("Access-Reject");
			break;
262
		case CODE_ACCOUNTING_REQUEST:
263
			print("Accounting-Request");
264 265
			break;
		case CODE_ACCOUNTING_RESPONSE:
266
			print("Accounting-Response");
267
			break;
268
		case CODE_DISCONNECT_REQUEST:
269
			print("Disconnect-Request");
270 271
			break;
		case CODE_DISCONNECT_ACK:
272
			print("Disconnect-ACK");
273 274
			break;
		case CODE_DISCONNECT_NAK:
275
			print("Disconnect-NAK");
276 277
			break;
		case CODE_COA_REQUEST:
278
			print("CoA-Request");
279 280
			break;
		case CODE_COA_ACK:
281
			print("CoA-ACK");
282 283
			break;
		case CODE_COA_NAK:
284
			print("CoA-NAK");
285
			break;
286 287 288 289 290 291
		default:
			print("Unknown (%i)", pack->code);
	}
	print(" id=%x", pack->id);

	list_for_each_entry(attr, &pack->attrs, entry) {
292 293 294 295
		if (attr->vendor)
			print("<%s %s ", attr->vendor->name, attr->attr->name);
		else
			print(" <%s ", attr->attr->name);
296 297 298 299 300 301 302 303 304 305 306 307 308 309
		switch (attr->attr->type) {
			case ATTR_TYPE_INTEGER:
				val = rad_dict_find_val(attr->attr, attr->val);
				if (val)
					print("%s", val->name);
				else
					print("%i", attr->val.integer);
				break;
			case ATTR_TYPE_STRING:
				print("\"%s\"", attr->val.string);
				break;
			case ATTR_TYPE_IPADDR:
				print("%i.%i.%i.%i", attr->val.ipaddr & 0xff, (attr->val.ipaddr >> 8) & 0xff, (attr->val.ipaddr >> 16) & 0xff, (attr->val.ipaddr >> 24) & 0xff);
				break;
310 311 312 313 314 315
		}
		print(">");
	}
	print("]\n");
}

316 317 318 319 320 321 322 323 324 325 326 327
int rad_packet_add_int(struct rad_packet_t *pack, const char *name, int val)
{
	struct rad_attr_t *ra;
	struct rad_dict_attr_t *attr;

	if (pack->len + 2 + 4 >= REQ_LENGTH_MAX)
		return -1;

	attr = rad_dict_find_attr(name);
	if (!attr)
		return -1;
	
D
Dmitry Kozlov 已提交
328
	ra = _malloc(sizeof(*ra));
329 330 331
	if (!ra)
		return -1;

332
	memset(ra, 0, sizeof(*ra));
333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366
	ra->attr = attr;
	ra->len = 4;
	ra->val.integer = val;
	list_add_tail(&ra->entry, &pack->attrs);
	pack->len += 2 + 4;

	return 0;
}

int rad_packet_change_int(struct rad_packet_t *pack, const char *name, int val)
{
	struct rad_attr_t *ra;
	
	ra = rad_packet_find_attr(pack, name);
	if (!ra)
		return -1;

	ra->val.integer = val;

	return 0;
}

int rad_packet_add_octets(struct rad_packet_t *pack, const char *name, uint8_t *val, int len)
{
	struct rad_attr_t *ra;
	struct rad_dict_attr_t *attr;

	if (pack->len + 2 + len >= REQ_LENGTH_MAX)
		return -1;

	attr = rad_dict_find_attr(name);
	if (!attr)
		return -1;
	
D
Dmitry Kozlov 已提交
367
	ra = _malloc(sizeof(*ra));
368
	if (!ra) {
369
		log_emerg("radius: out of memory\n");
370 371 372
		return -1;
	}

373
	memset(ra, 0, sizeof(*ra));
374 375
	ra->attr = attr;
	ra->len = len;
D
Dmitry Kozlov 已提交
376
	ra->val.octets = _malloc(len);
377
	if (!ra->val.octets) {
378
		log_emerg("radius: out of memory\n");
D
Dmitry Kozlov 已提交
379
		_free(ra);
380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399
		return -1;
	}
	memcpy(ra->val.octets, val, len);
	list_add_tail(&ra->entry, &pack->attrs);
	pack->len += 2 + len;

	return 0;
}
int rad_packet_add_str(struct rad_packet_t *pack, const char *name, const char *val, int len)
{
	struct rad_attr_t *ra;
	struct rad_dict_attr_t *attr;

	if (pack->len + 2 + len >= REQ_LENGTH_MAX)
		return -1;

	attr = rad_dict_find_attr(name);
	if (!attr)
		return -1;
	
D
Dmitry Kozlov 已提交
400
	ra = _malloc(sizeof(*ra));
401
	if (!ra) {
402
		log_emerg("radius: out of memory\n");
403 404 405
		return -1;
	}

406
	memset(ra, 0, sizeof(*ra));
407 408
	ra->attr = attr;
	ra->len = len;
D
Dmitry Kozlov 已提交
409
	ra->val.string = _malloc(len+1);
410
	if (!ra->val.string) {
411
		log_emerg("radius: out of memory\n");
D
Dmitry Kozlov 已提交
412
		_free(ra);
413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434
		return -1;
	}
	memcpy(ra->val.string, val, len);
	ra->val.string[len] = 0;
	list_add_tail(&ra->entry, &pack->attrs);
	pack->len += 2 + len;

	return 0;
}

int rad_packet_change_str(struct rad_packet_t *pack, const char *name, const char *val, int len)
{
	struct rad_attr_t *ra;
	
	ra = rad_packet_find_attr(pack, name);
	if (!ra)
		return -1;

	if (ra->len != len) {
		if (pack->len - ra->len + len >= REQ_LENGTH_MAX)
			return -1;

D
Dmitry Kozlov 已提交
435
		ra->val.string = _realloc(ra->val.string, len + 1);
436
		if (!ra->val.string) {
437
			log_emerg("radius: out of memory\n");
438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467
			return -1;
		}
	
		pack->len += len - ra->len;
		ra->len = len;
	}

	memcpy(ra->val.string, val, len);
	ra->val.string[len] = 0;

	return 0;
}

int rad_packet_add_val(struct rad_packet_t *pack, const char *name, const char *val)
{
	struct rad_attr_t *ra;
	struct rad_dict_attr_t *attr;
	struct rad_dict_value_t *v;

	if (pack->len + 2 + 4 >= REQ_LENGTH_MAX)
		return -1;

	attr = rad_dict_find_attr(name);
	if (!attr)
		return -1;
	
	v = rad_dict_find_val_name(attr, val);
	if (!v)
		return -1;
	
D
Dmitry Kozlov 已提交
468
	ra = _malloc(sizeof(*ra));
469 470 471
	if (!ra)
		return -1;

472
	memset(ra, 0, sizeof(*ra));
473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499
	ra->attr = attr;
	ra->len = 4;
	ra->val = v->val;
	list_add_tail(&ra->entry, &pack->attrs);
	pack->len += 2 + 4;

	return 0;
}

int rad_packet_change_val(struct rad_packet_t *pack, const char *name, const char *val)
{
	struct rad_attr_t *ra;
	struct rad_dict_value_t *v;
	
	ra = rad_packet_find_attr(pack, name);
	if (!ra)
		return -1;

	v = rad_dict_find_val_name(ra->attr, val);
	if (!v)
		return -1;

	ra->val = v->val;
	
	return 0;
}

500 501 502 503 504 505
int rad_packet_add_ipaddr(struct rad_packet_t *pack, const char *name, in_addr_t ipaddr)
{
	return rad_packet_add_int(pack, name, ipaddr);
}


506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528
struct rad_attr_t *rad_packet_find_attr(struct rad_packet_t *pack, const char *name)
{
	struct rad_attr_t *ra;

	list_for_each_entry(ra, &pack->attrs, entry)
		if (!strcmp(ra->attr->name, name))
			return ra;

	return NULL;
}

int rad_packet_send(struct rad_packet_t *pack, int fd, struct sockaddr_in *addr)
{
	int n;

	while (1) {
		if (addr)
			n = sendto(fd, pack->buf, pack->len, 0, addr, sizeof(*addr));
		else
			n = write(fd, pack->buf, pack->len);
		if (n < 0) {
			if (errno == EINTR)
				continue;
529
			log_ppp_error("radius:write: %s\n", strerror(errno));
530 531
			return -1;
		} else if (n != pack->len) {
532
			log_ppp_error("radius:write: short write %i, excpected %i\n", n, pack->len);
533 534 535 536 537 538 539 540
			return -1;
		}
		break;
	}

	return 0;
}

541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557
int rad_packet_add_vendor_octets(struct rad_packet_t *pack, const char *vendor_name, const char *name, const uint8_t *val, int len)
{
	struct rad_attr_t *ra;
	struct rad_dict_attr_t *attr;
	struct rad_dict_vendor_t *vendor;

	if (pack->len + 6 + 2 + len >= REQ_LENGTH_MAX)
		return -1;

	vendor = rad_dict_find_vendor_name(vendor_name);
	if (!vendor)
		return -1;

	attr = rad_dict_find_vendor_attr(vendor, name);
	if (!attr)
		return -1;
	
D
Dmitry Kozlov 已提交
558
	ra = _malloc(sizeof(*ra));
559 560 561 562 563 564 565 566 567
	if (!ra) {
		log_emerg("radius: out of memory\n");
		return -1;
	}
	
	memset(ra, 0, sizeof(*ra));
	ra->vendor = vendor;
	ra->attr = attr;
	ra->len = len;
D
Dmitry Kozlov 已提交
568
	ra->val.octets = _malloc(len);
569 570
	if (!ra->val.octets) {
		log_emerg("radius: out of memory\n");
D
Dmitry Kozlov 已提交
571
		_free(ra);
572 573 574 575 576 577 578 579 580
		return -1;
	}
	memcpy(ra->val.octets, val, len);
	list_add_tail(&ra->entry, &pack->attrs);
	pack->len += 6 + 2 + len;

	return 0;
}

581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597
struct rad_attr_t *rad_packet_find_vendor_attr(struct rad_packet_t *pack, const char *vendor_name, const char *name)
{
	struct rad_attr_t *ra;

	list_for_each_entry(ra, &pack->attrs, entry) {
		if (!ra->vendor)
			continue;
		if (strcmp(ra->vendor->name, vendor_name))
			continue;
		if (strcmp(ra->attr->name, name))
			continue;

		return ra;
	}

	return NULL;
}