PageRenderTime 51ms CodeModel.GetById 14ms RepoModel.GetById 0ms app.codeStats 1ms

/src/modules/frs_vmps/vqp.c

https://github.com/mikeross/freeradius-server
C | 698 lines | 373 code | 104 blank | 221 comment | 89 complexity | d83eec08fa121bfdab8e710c94068590 MD5 | raw file
  1. /*
  2. * vqp.c Functions to send/receive VQP packets.
  3. *
  4. * Version: $Id$
  5. *
  6. * This library is free software; you can redistribute it and/or
  7. * modify it under the terms of the GNU Lesser General Public
  8. * License as published by the Free Software Foundation; either
  9. * version 2.1 of the License, or (at your option) any later version.
  10. *
  11. * This library is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  14. * Lesser General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU Lesser General Public
  17. * License along with this library; if not, write to the Free Software
  18. * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
  19. *
  20. * Copyright 2007 Alan DeKok <aland@deployingradius.com>
  21. */
  22. #include <freeradius-devel/ident.h>
  23. RCSID("$Id$");
  24. #include <freeradius-devel/radiusd.h>
  25. #include <freeradius-devel/udpfromto.h>
  26. #include "vqp.h"
  27. #ifdef WITH_VMPS
  28. # define debug_pair(vp) do { if (fr_debug_flag && fr_log_fp) { \
  29. fputc('\t', fr_log_fp); \
  30. vp_print(fr_log_fp, vp); \
  31. fputc('\n', fr_log_fp); \
  32. } \
  33. } while(0)
  34. #define MAX_VMPS_LEN (MAX_STRING_LEN - 1)
  35. /*
  36. * http://www.openbsd.org/cgi-bin/cvsweb/src/usr.sbin/tcpdump/print-vqp.c
  37. *
  38. * Some of how it works:
  39. *
  40. * http://www.hackingciscoexposed.com/pdf/chapter12.pdf
  41. *
  42. * VLAN Query Protocol (VQP)
  43. *
  44. * 0 1 2 3
  45. * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
  46. * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  47. * | Version | Opcode | Response Code | Data Count |
  48. * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  49. * | Transaction ID |
  50. * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  51. * | Type (1) |
  52. * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  53. * | Length | Data /
  54. * / /
  55. * / /
  56. * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  57. * | Type (n) |
  58. * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  59. * | Length | Data /
  60. * / /
  61. * / /
  62. * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  63. *
  64. * VQP is layered over UDP. The default destination port is 1589.
  65. *
  66. */
  67. #define VQP_HDR_LEN (8)
  68. #define VQP_VERSION (1)
  69. #define VQP_MAX_ATTRIBUTES (12)
  70. /*
  71. * Wrapper for sendto which handles sendfromto, IPv6, and all
  72. * possible combinations.
  73. *
  74. * FIXME: This is just a copy of rad_sendto().
  75. * Duplicate code is bad.
  76. */
  77. static int vqp_sendto(int sockfd, void *data, size_t data_len, int flags,
  78. fr_ipaddr_t *src_ipaddr, fr_ipaddr_t *dst_ipaddr,
  79. int dst_port)
  80. {
  81. struct sockaddr_storage dst;
  82. socklen_t sizeof_dst;
  83. #ifdef WITH_UDPFROMTO
  84. struct sockaddr_storage src;
  85. socklen_t sizeof_src;
  86. fr_ipaddr2sockaddr(src_ipaddr, 0, &src, &sizeof_src);
  87. #else
  88. src_ipaddr = src_ipaddr; /* -Wunused */
  89. #endif
  90. if (!fr_ipaddr2sockaddr(dst_ipaddr, dst_port, &dst, &sizeof_dst)) {
  91. return -1; /* Unknown address family, Die Die Die! */
  92. }
  93. #ifdef WITH_UDPFROMTO
  94. /*
  95. * Only IPv4 is supported for udpfromto.
  96. *
  97. * And if they don't specify a source IP address, don't
  98. * use udpfromto.
  99. */
  100. if ((dst_ipaddr->af == AF_INET) ||
  101. (src_ipaddr->af != AF_UNSPEC)) {
  102. return sendfromto(sockfd, data, data_len, flags,
  103. (struct sockaddr *)&src, sizeof_src,
  104. (struct sockaddr *)&dst, sizeof_dst);
  105. }
  106. #else
  107. src_ipaddr = src_ipaddr; /* -Wunused */
  108. #endif
  109. /*
  110. * No udpfromto, OR an IPv6 socket, fail gracefully.
  111. */
  112. return sendto(sockfd, data, data_len, flags,
  113. (struct sockaddr *)&dst, sizeof_dst);
  114. }
  115. /*
  116. * Wrapper for recvfrom, which handles recvfromto, IPv6, and all
  117. * possible combinations.
  118. *
  119. * FIXME: This is copied from rad_recvfrom, with minor edits.
  120. */
  121. static ssize_t vqp_recvfrom(int sockfd, uint8_t **pbuf, int flags,
  122. fr_ipaddr_t *src_ipaddr, uint16_t *src_port,
  123. fr_ipaddr_t *dst_ipaddr, uint16_t *dst_port)
  124. {
  125. struct sockaddr_storage src;
  126. struct sockaddr_storage dst;
  127. socklen_t sizeof_src = sizeof(src);
  128. socklen_t sizeof_dst = sizeof(dst);
  129. ssize_t data_len;
  130. uint8_t header[4];
  131. void *buf;
  132. size_t len;
  133. int port;
  134. memset(&src, 0, sizeof_src);
  135. memset(&dst, 0, sizeof_dst);
  136. /*
  137. * Get address family, etc. first, so we know if we
  138. * need to do udpfromto.
  139. *
  140. * FIXME: udpfromto also does this, but it's not
  141. * a critical problem.
  142. */
  143. if (getsockname(sockfd, (struct sockaddr *)&dst,
  144. &sizeof_dst) < 0) return -1;
  145. /*
  146. * Read the length of the packet, from the packet.
  147. * This lets us allocate the buffer to use for
  148. * reading the rest of the packet.
  149. */
  150. data_len = recvfrom(sockfd, header, sizeof(header), MSG_PEEK,
  151. (struct sockaddr *)&src, &sizeof_src);
  152. if (data_len < 0) return -1;
  153. /*
  154. * Too little data is available, discard the packet.
  155. */
  156. if (data_len < 4) {
  157. recvfrom(sockfd, header, sizeof(header), flags,
  158. (struct sockaddr *)&src, &sizeof_src);
  159. return 0;
  160. /*
  161. * Invalid version, packet type, or too many
  162. * attributes. Die.
  163. */
  164. } else if ((header[0] != VQP_VERSION) ||
  165. (header[1] < 1) ||
  166. (header[1] > 4) ||
  167. (header[3] > VQP_MAX_ATTRIBUTES)) {
  168. recvfrom(sockfd, header, sizeof(header), flags,
  169. (struct sockaddr *)&src, &sizeof_src);
  170. return 0;
  171. } else { /* we got 4 bytes of data. */
  172. /*
  173. * We don't care about the contents for now...
  174. */
  175. #if 0
  176. /*
  177. * How many attributes are in the packet.
  178. */
  179. len = header[3];
  180. if ((header[1] == 1) || (header[1] == 3)) {
  181. if (len != VQP_MAX_ATTRIBUTES) {
  182. recvfrom(sockfd, header, sizeof(header), 0,
  183. (struct sockaddr *)&src, &sizeof_src);
  184. return 0;
  185. }
  186. /*
  187. * Maximum length we support.
  188. */
  189. len = (12 * (4 + 4 + MAX_VMPS_LEN));
  190. } else {
  191. if (len != 2) {
  192. recvfrom(sockfd, header, sizeof(header), 0,
  193. (struct sockaddr *)&src, &sizeof_src);
  194. return 0;
  195. }
  196. /*
  197. * Maximum length we support.
  198. */
  199. len = (12 * (4 + 4 + MAX_VMPS_LEN));
  200. }
  201. #endif
  202. }
  203. /*
  204. * For now, be generous.
  205. */
  206. len = (12 * (4 + 4 + MAX_VMPS_LEN));
  207. buf = malloc(len);
  208. if (!buf) return -1;
  209. /*
  210. * Receive the packet. The OS will discard any data in the
  211. * packet after "len" bytes.
  212. */
  213. #ifdef WITH_UDPFROMTO
  214. if (dst.ss_family == AF_INET) {
  215. data_len = recvfromto(sockfd, buf, len, flags,
  216. (struct sockaddr *)&src, &sizeof_src,
  217. (struct sockaddr *)&dst, &sizeof_dst);
  218. } else
  219. #endif
  220. /*
  221. * No udpfromto, OR an IPv6 socket. Fail gracefully.
  222. */
  223. data_len = recvfrom(sockfd, buf, len, flags,
  224. (struct sockaddr *)&src, &sizeof_src);
  225. if (data_len < 0) {
  226. free(buf);
  227. return data_len;
  228. }
  229. if (!fr_sockaddr2ipaddr(&src, sizeof_src, src_ipaddr, &port)) {
  230. free(buf);
  231. return -1; /* Unknown address family, Die Die Die! */
  232. }
  233. *src_port = port;
  234. fr_sockaddr2ipaddr(&dst, sizeof_dst, dst_ipaddr, &port);
  235. *dst_port = port;
  236. /*
  237. * Different address families should never happen.
  238. */
  239. if (src.ss_family != dst.ss_family) {
  240. free(buf);
  241. return -1;
  242. }
  243. /*
  244. * Tell the caller about the data
  245. */
  246. *pbuf = buf;
  247. return data_len;
  248. }
  249. RADIUS_PACKET *vqp_recv(int sockfd)
  250. {
  251. uint8_t *ptr;
  252. ssize_t length;
  253. uint32_t id;
  254. RADIUS_PACKET *packet;
  255. /*
  256. * Allocate the new request data structure
  257. */
  258. if ((packet = malloc(sizeof(*packet))) == NULL) {
  259. fr_strerror_printf("out of memory");
  260. return NULL;
  261. }
  262. memset(packet, 0, sizeof(*packet));
  263. packet->data_len = vqp_recvfrom(sockfd, &packet->data, 0,
  264. &packet->src_ipaddr, &packet->src_port,
  265. &packet->dst_ipaddr, &packet->dst_port);
  266. /*
  267. * Check for socket errors.
  268. */
  269. if (packet->data_len < 0) {
  270. fr_strerror_printf("Error receiving packet: %s", strerror(errno));
  271. /* packet->data is NULL */
  272. free(packet);
  273. return NULL;
  274. }
  275. /*
  276. * We can only receive packets formatted in a way we
  277. * expect. However, we accept MORE attributes in a
  278. * packet than normal implementations may send.
  279. */
  280. if (packet->data_len < VQP_HDR_LEN) {
  281. fr_strerror_printf("VQP packet is too short");
  282. rad_free(&packet);
  283. return NULL;
  284. }
  285. ptr = packet->data;
  286. if (0) {
  287. int i;
  288. for (i = 0; i < packet->data_len; i++) {
  289. if ((i & 0x0f) == 0) fprintf(stderr, "%02x: ", i);
  290. fprintf(stderr, "%02x ", ptr[i]);
  291. if ((i & 0x0f) == 0x0f) fprintf(stderr, "\n");
  292. }
  293. }
  294. if (ptr[3] > VQP_MAX_ATTRIBUTES) {
  295. fr_strerror_printf("Too many VQP attributes");
  296. rad_free(&packet);
  297. return NULL;
  298. }
  299. if (packet->data_len > VQP_HDR_LEN) {
  300. int attrlen;
  301. /*
  302. * Skip the header.
  303. */
  304. ptr += VQP_HDR_LEN;
  305. length = packet->data_len - VQP_HDR_LEN;
  306. while (length > 0) {
  307. if (length < 7) {
  308. fr_strerror_printf("Packet contains malformed attribute");
  309. rad_free(&packet);
  310. return NULL;
  311. }
  312. /*
  313. * Attributes are 4 bytes
  314. * 0x00000c01 ... 0x00000c08
  315. */
  316. if ((ptr[0] != 0) || (ptr[1] != 0) ||
  317. (ptr[2] != 0x0c) || (ptr[3] < 1) || (ptr[3] > 8)) {
  318. fr_strerror_printf("Packet contains invalid attribute");
  319. rad_free(&packet);
  320. return NULL;
  321. }
  322. /*
  323. * Length is 2 bytes
  324. *
  325. * We support lengths 1..253, for internal
  326. * server reasons. Also, there's no reason
  327. * for bigger lengths to exist... admins
  328. * won't be typing in a 32K vlan name.
  329. *
  330. * Except for received ethernet frames...
  331. * they get chopped to 253 internally.
  332. */
  333. if ((ptr[3] != 5) &&
  334. ((ptr[4] != 0) || (ptr[5] > MAX_VMPS_LEN))) {
  335. fr_strerror_printf("Packet contains attribute with invalid length %02x %02x", ptr[4], ptr[5]);
  336. rad_free(&packet);
  337. return NULL;
  338. }
  339. attrlen = (ptr[4] << 8) | ptr[5];
  340. ptr += 6 + attrlen;
  341. length -= (6 + attrlen);
  342. }
  343. }
  344. packet->sockfd = sockfd;
  345. packet->vps = NULL;
  346. /*
  347. * This is more than a bit of a hack.
  348. */
  349. packet->code = PW_AUTHENTICATION_REQUEST;
  350. memcpy(&id, packet->data + 4, 4);
  351. packet->id = ntohl(id);
  352. /*
  353. * FIXME: Create a fake "request authenticator", to
  354. * avoid duplicates? Or is the VQP sequence number
  355. * adequate for this purpose?
  356. */
  357. return packet;
  358. }
  359. /*
  360. * We do NOT mirror the old-style RADIUS code that does encode,
  361. * sign && send in one function. For VQP, the caller MUST perform
  362. * each task manually, and separately.
  363. */
  364. int vqp_send(RADIUS_PACKET *packet)
  365. {
  366. if (!packet || !packet->data || (packet->data_len < 8)) return -1;
  367. /*
  368. * Don't print out the attributes, they were printed out
  369. * when it was encoded.
  370. */
  371. /*
  372. * And send it on it's way.
  373. */
  374. return vqp_sendto(packet->sockfd, packet->data, packet->data_len, 0,
  375. &packet->src_ipaddr, &packet->dst_ipaddr,
  376. packet->dst_port);
  377. }
  378. int vqp_decode(RADIUS_PACKET *packet)
  379. {
  380. uint8_t *ptr, *end;
  381. int attribute, length;
  382. VALUE_PAIR *vp, **tail;
  383. if (!packet || !packet->data) return -1;
  384. if (packet->data_len < VQP_HDR_LEN) return -1;
  385. tail = &packet->vps;
  386. vp = paircreate(PW_VQP_PACKET_TYPE, PW_TYPE_OCTETS);
  387. if (!vp) {
  388. fr_strerror_printf("No memory");
  389. return -1;
  390. }
  391. vp->lvalue = packet->data[1];
  392. debug_pair(vp);
  393. *tail = vp;
  394. tail = &(vp->next);
  395. vp = paircreate(PW_VQP_ERROR_CODE, PW_TYPE_OCTETS);
  396. if (!vp) {
  397. fr_strerror_printf("No memory");
  398. return -1;
  399. }
  400. vp->lvalue = packet->data[2];
  401. debug_pair(vp);
  402. *tail = vp;
  403. tail = &(vp->next);
  404. vp = paircreate(PW_VQP_SEQUENCE_NUMBER, PW_TYPE_OCTETS);
  405. if (!vp) {
  406. fr_strerror_printf("No memory");
  407. return -1;
  408. }
  409. vp->lvalue = packet->id; /* already set by vqp_recv */
  410. debug_pair(vp);
  411. *tail = vp;
  412. tail = &(vp->next);
  413. ptr = packet->data + VQP_HDR_LEN;
  414. end = packet->data + packet->data_len;
  415. /*
  416. * Note that vqp_recv() MUST ensure that the packet is
  417. * formatted in a way we expect, and that vqp_recv() MUST
  418. * be called before vqp_decode().
  419. */
  420. while (ptr < end) {
  421. attribute = (ptr[2] << 8) | ptr[3];
  422. length = (ptr[4] << 8) | ptr[5];
  423. ptr += 6;
  424. /*
  425. * Hack to get the dictionaries to work correctly.
  426. */
  427. attribute |= 0x2000;
  428. vp = paircreate(attribute, PW_TYPE_OCTETS);
  429. if (!vp) {
  430. pairfree(&packet->vps);
  431. fr_strerror_printf("No memory");
  432. return -1;
  433. }
  434. switch (vp->type) {
  435. case PW_TYPE_IPADDR:
  436. if (length == 4) {
  437. memcpy(&vp->vp_ipaddr, ptr, 4);
  438. vp->length = 4;
  439. break;
  440. }
  441. vp->type = PW_TYPE_OCTETS;
  442. /* FALL-THROUGH */
  443. default:
  444. case PW_TYPE_OCTETS:
  445. case PW_TYPE_STRING:
  446. vp->length = (length > MAX_VMPS_LEN) ? MAX_VMPS_LEN : length;
  447. memcpy(vp->vp_octets, ptr, vp->length);
  448. vp->vp_octets[vp->length] = '\0';
  449. break;
  450. }
  451. ptr += length;
  452. debug_pair(vp);
  453. *tail = vp;
  454. tail = &(vp->next);
  455. }
  456. /*
  457. * FIXME: Map attributes to Calling-Station-Id, etc...
  458. */
  459. return 0;
  460. }
  461. /*
  462. * These are the MUST HAVE contents for a VQP packet.
  463. *
  464. * We don't allow the caller to give less than these, because
  465. * it won't work. We don't encode more than these, because the
  466. * clients will ignore it.
  467. *
  468. * FIXME: Be more generous? Look for CISCO + VQP attributes?
  469. */
  470. static int contents[5][VQP_MAX_ATTRIBUTES] = {
  471. { 0, 0, 0, 0, 0, 0 },
  472. { 0x0c01, 0x0c02, 0x0c03, 0x0c04, 0x0c07, 0x0c05 }, /* Join request */
  473. { 0x0c03, 0x0c08, 0, 0, 0, 0 }, /* Join Response */
  474. { 0x0c01, 0x0c02, 0x0c03, 0x0c04, 0x0c07, 0x0c08 }, /* Reconfirm */
  475. { 0x0c03, 0x0c08, 0, 0, 0, 0 }
  476. };
  477. int vqp_encode(RADIUS_PACKET *packet, RADIUS_PACKET *original)
  478. {
  479. int i, code, length;
  480. VALUE_PAIR *vp;
  481. uint8_t *ptr;
  482. VALUE_PAIR *vps[VQP_MAX_ATTRIBUTES];
  483. if (!packet) {
  484. fr_strerror_printf("Failed encoding VQP");
  485. return -1;
  486. }
  487. if (packet->data) return 0;
  488. vp = pairfind(packet->vps, PW_VQP_PACKET_TYPE);
  489. if (!vp) {
  490. fr_strerror_printf("Failed to find VQP-Packet-Type in response packet");
  491. return -1;
  492. }
  493. code = vp->lvalue;
  494. if ((code < 1) || (code > 4)) {
  495. fr_strerror_printf("Invalid value %d for VQP-Packet-Type", code);
  496. return -1;
  497. }
  498. length = VQP_HDR_LEN;
  499. memset(vps, 0, sizeof(vps));
  500. vp = pairfind(packet->vps, PW_VQP_ERROR_CODE);
  501. /*
  502. * FIXME: Map attributes from calling-station-Id, etc.
  503. *
  504. * Maybe do this via rlm_vqp? That's probably the
  505. * best place to add the code...
  506. */
  507. /*
  508. * No error: encode attributes.
  509. */
  510. if (!vp) for (i = 0; i < VQP_MAX_ATTRIBUTES; i++) {
  511. if (!contents[code][i]) break;
  512. vps[i] = pairfind(packet->vps, contents[code][i] | 0x2000);
  513. /*
  514. * FIXME: Print the name...
  515. */
  516. if (!vps[i]) {
  517. fr_strerror_printf("Failed to find VQP attribute %02x",
  518. contents[code][i]);
  519. return -1;
  520. }
  521. length += 6;
  522. length += vps[i]->length;
  523. }
  524. packet->data = malloc(length);
  525. if (!packet->data) {
  526. fr_strerror_printf("No memory");
  527. return -1;
  528. }
  529. packet->data_len = length;
  530. ptr = packet->data;
  531. ptr[0] = VQP_VERSION;
  532. ptr[1] = code;
  533. if (!vp) {
  534. ptr[2] = 0;
  535. } else {
  536. ptr[2] = vp->lvalue & 0xff;
  537. return 0;
  538. }
  539. /*
  540. * The number of attributes is hard-coded.
  541. */
  542. if ((code == 1) || (code == 3)) {
  543. uint32_t sequence;
  544. ptr[3] = VQP_MAX_ATTRIBUTES;
  545. sequence = htonl(packet->id);
  546. memcpy(ptr + 4, &sequence, 4);
  547. } else {
  548. if (!original) {
  549. fr_strerror_printf("Cannot send VQP response without request");
  550. return -1;
  551. }
  552. /*
  553. * Packet Sequence Number
  554. */
  555. memcpy(ptr + 4, original->data + 4, 4);
  556. ptr[3] = 2;
  557. }
  558. ptr += 8;
  559. /*
  560. * Encode the VP's.
  561. */
  562. for (i = 0; i < VQP_MAX_ATTRIBUTES; i++) {
  563. if (!vps[i]) break;
  564. vp = vps[i];
  565. debug_pair(vp);
  566. /*
  567. * Type. Note that we look at only the lower 8
  568. * bits, as the upper 8 bits have been hacked.
  569. * See also dictionary.vqp
  570. */
  571. ptr[0] = 0;
  572. ptr[1] = 0;
  573. ptr[2] = 0x0c;
  574. ptr[3] = vp->attribute & 0xff;
  575. /* Length */
  576. ptr[4] = 0;
  577. ptr[5] = vp->length & 0xff;
  578. ptr += 6;
  579. /* Data */
  580. switch (vp->type) {
  581. case PW_TYPE_IPADDR:
  582. memcpy(ptr, &vp->vp_ipaddr, 4);
  583. break;
  584. default:
  585. case PW_TYPE_OCTETS:
  586. case PW_TYPE_STRING:
  587. memcpy(ptr, vp->vp_octets, vp->length);
  588. break;
  589. }
  590. ptr += vp->length;
  591. }
  592. return 0;
  593. }
  594. #endif