PageRenderTime 46ms CodeModel.GetById 14ms RepoModel.GetById 0ms app.codeStats 0ms

/src/lib/vqp.c

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