radius.c 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275
  1. /*
  2. * hostapd / RADIUS message processing
  3. * Copyright (c) 2002-2008, Jouni Malinen <j@w1.fi>
  4. *
  5. * This program is free software; you can redistribute it and/or modify
  6. * it under the terms of the GNU General Public License version 2 as
  7. * published by the Free Software Foundation.
  8. *
  9. * Alternatively, this software may be distributed under the terms of BSD
  10. * license.
  11. *
  12. * See README and COPYING for more details.
  13. */
  14. #include "includes.h"
  15. #include "common.h"
  16. #include "radius.h"
  17. #include "md5.h"
  18. #include "crypto.h"
  19. static struct radius_attr_hdr *
  20. radius_get_attr_hdr(struct radius_msg *msg, int idx)
  21. {
  22. return (struct radius_attr_hdr *) (msg->buf + msg->attr_pos[idx]);
  23. }
  24. struct radius_msg *radius_msg_new(u8 code, u8 identifier)
  25. {
  26. struct radius_msg *msg;
  27. msg = os_malloc(sizeof(*msg));
  28. if (msg == NULL)
  29. return NULL;
  30. if (radius_msg_initialize(msg, RADIUS_DEFAULT_MSG_SIZE)) {
  31. os_free(msg);
  32. return NULL;
  33. }
  34. radius_msg_set_hdr(msg, code, identifier);
  35. return msg;
  36. }
  37. int radius_msg_initialize(struct radius_msg *msg, size_t init_len)
  38. {
  39. if (msg == NULL || init_len < sizeof(struct radius_hdr))
  40. return -1;
  41. os_memset(msg, 0, sizeof(*msg));
  42. msg->buf = os_zalloc(init_len);
  43. if (msg->buf == NULL)
  44. return -1;
  45. msg->buf_size = init_len;
  46. msg->hdr = (struct radius_hdr *) msg->buf;
  47. msg->buf_used = sizeof(*msg->hdr);
  48. msg->attr_pos =
  49. os_zalloc(RADIUS_DEFAULT_ATTR_COUNT * sizeof(*msg->attr_pos));
  50. if (msg->attr_pos == NULL) {
  51. os_free(msg->buf);
  52. msg->buf = NULL;
  53. msg->hdr = NULL;
  54. return -1;
  55. }
  56. msg->attr_size = RADIUS_DEFAULT_ATTR_COUNT;
  57. msg->attr_used = 0;
  58. return 0;
  59. }
  60. void radius_msg_set_hdr(struct radius_msg *msg, u8 code, u8 identifier)
  61. {
  62. msg->hdr->code = code;
  63. msg->hdr->identifier = identifier;
  64. }
  65. void radius_msg_free(struct radius_msg *msg)
  66. {
  67. os_free(msg->buf);
  68. msg->buf = NULL;
  69. msg->hdr = NULL;
  70. msg->buf_size = msg->buf_used = 0;
  71. os_free(msg->attr_pos);
  72. msg->attr_pos = NULL;
  73. msg->attr_size = msg->attr_used = 0;
  74. }
  75. static const char *radius_code_string(u8 code)
  76. {
  77. switch (code) {
  78. case RADIUS_CODE_ACCESS_REQUEST: return "Access-Request";
  79. case RADIUS_CODE_ACCESS_ACCEPT: return "Access-Accept";
  80. case RADIUS_CODE_ACCESS_REJECT: return "Access-Reject";
  81. case RADIUS_CODE_ACCOUNTING_REQUEST: return "Accounting-Request";
  82. case RADIUS_CODE_ACCOUNTING_RESPONSE: return "Accounting-Response";
  83. case RADIUS_CODE_ACCESS_CHALLENGE: return "Access-Challenge";
  84. case RADIUS_CODE_STATUS_SERVER: return "Status-Server";
  85. case RADIUS_CODE_STATUS_CLIENT: return "Status-Client";
  86. case RADIUS_CODE_RESERVED: return "Reserved";
  87. default: return "?Unknown?";
  88. }
  89. }
  90. struct radius_attr_type {
  91. u8 type;
  92. char *name;
  93. enum {
  94. RADIUS_ATTR_UNDIST, RADIUS_ATTR_TEXT, RADIUS_ATTR_IP,
  95. RADIUS_ATTR_HEXDUMP, RADIUS_ATTR_INT32, RADIUS_ATTR_IPV6
  96. } data_type;
  97. };
  98. static struct radius_attr_type radius_attrs[] =
  99. {
  100. { RADIUS_ATTR_USER_NAME, "User-Name", RADIUS_ATTR_TEXT },
  101. { RADIUS_ATTR_USER_PASSWORD, "User-Password", RADIUS_ATTR_UNDIST },
  102. { RADIUS_ATTR_NAS_IP_ADDRESS, "NAS-IP-Address", RADIUS_ATTR_IP },
  103. { RADIUS_ATTR_NAS_PORT, "NAS-Port", RADIUS_ATTR_INT32 },
  104. { RADIUS_ATTR_FRAMED_MTU, "Framed-MTU", RADIUS_ATTR_INT32 },
  105. { RADIUS_ATTR_REPLY_MESSAGE, "Reply-Message", RADIUS_ATTR_TEXT },
  106. { RADIUS_ATTR_STATE, "State", RADIUS_ATTR_UNDIST },
  107. { RADIUS_ATTR_CLASS, "Class", RADIUS_ATTR_UNDIST },
  108. { RADIUS_ATTR_VENDOR_SPECIFIC, "Vendor-Specific", RADIUS_ATTR_UNDIST },
  109. { RADIUS_ATTR_SESSION_TIMEOUT, "Session-Timeout", RADIUS_ATTR_INT32 },
  110. { RADIUS_ATTR_IDLE_TIMEOUT, "Idle-Timeout", RADIUS_ATTR_INT32 },
  111. { RADIUS_ATTR_TERMINATION_ACTION, "Termination-Action",
  112. RADIUS_ATTR_INT32 },
  113. { RADIUS_ATTR_CALLED_STATION_ID, "Called-Station-Id",
  114. RADIUS_ATTR_TEXT },
  115. { RADIUS_ATTR_CALLING_STATION_ID, "Calling-Station-Id",
  116. RADIUS_ATTR_TEXT },
  117. { RADIUS_ATTR_NAS_IDENTIFIER, "NAS-Identifier", RADIUS_ATTR_TEXT },
  118. { RADIUS_ATTR_PROXY_STATE, "Proxy-State", RADIUS_ATTR_UNDIST },
  119. { RADIUS_ATTR_ACCT_STATUS_TYPE, "Acct-Status-Type",
  120. RADIUS_ATTR_INT32 },
  121. { RADIUS_ATTR_ACCT_DELAY_TIME, "Acct-Delay-Time", RADIUS_ATTR_INT32 },
  122. { RADIUS_ATTR_ACCT_INPUT_OCTETS, "Acct-Input-Octets",
  123. RADIUS_ATTR_INT32 },
  124. { RADIUS_ATTR_ACCT_OUTPUT_OCTETS, "Acct-Output-Octets",
  125. RADIUS_ATTR_INT32 },
  126. { RADIUS_ATTR_ACCT_SESSION_ID, "Acct-Session-Id", RADIUS_ATTR_TEXT },
  127. { RADIUS_ATTR_ACCT_AUTHENTIC, "Acct-Authentic", RADIUS_ATTR_INT32 },
  128. { RADIUS_ATTR_ACCT_SESSION_TIME, "Acct-Session-Time",
  129. RADIUS_ATTR_INT32 },
  130. { RADIUS_ATTR_ACCT_INPUT_PACKETS, "Acct-Input-Packets",
  131. RADIUS_ATTR_INT32 },
  132. { RADIUS_ATTR_ACCT_OUTPUT_PACKETS, "Acct-Output-Packets",
  133. RADIUS_ATTR_INT32 },
  134. { RADIUS_ATTR_ACCT_TERMINATE_CAUSE, "Acct-Terminate-Cause",
  135. RADIUS_ATTR_INT32 },
  136. { RADIUS_ATTR_ACCT_MULTI_SESSION_ID, "Acct-Multi-Session-Id",
  137. RADIUS_ATTR_TEXT },
  138. { RADIUS_ATTR_ACCT_LINK_COUNT, "Acct-Link-Count", RADIUS_ATTR_INT32 },
  139. { RADIUS_ATTR_ACCT_INPUT_GIGAWORDS, "Acct-Input-Gigawords",
  140. RADIUS_ATTR_INT32 },
  141. { RADIUS_ATTR_ACCT_OUTPUT_GIGAWORDS, "Acct-Output-Gigawords",
  142. RADIUS_ATTR_INT32 },
  143. { RADIUS_ATTR_EVENT_TIMESTAMP, "Event-Timestamp",
  144. RADIUS_ATTR_INT32 },
  145. { RADIUS_ATTR_NAS_PORT_TYPE, "NAS-Port-Type", RADIUS_ATTR_INT32 },
  146. { RADIUS_ATTR_TUNNEL_TYPE, "Tunnel-Type", RADIUS_ATTR_HEXDUMP },
  147. { RADIUS_ATTR_TUNNEL_MEDIUM_TYPE, "Tunnel-Medium-Type",
  148. RADIUS_ATTR_HEXDUMP },
  149. { RADIUS_ATTR_CONNECT_INFO, "Connect-Info", RADIUS_ATTR_TEXT },
  150. { RADIUS_ATTR_EAP_MESSAGE, "EAP-Message", RADIUS_ATTR_UNDIST },
  151. { RADIUS_ATTR_MESSAGE_AUTHENTICATOR, "Message-Authenticator",
  152. RADIUS_ATTR_UNDIST },
  153. { RADIUS_ATTR_TUNNEL_PRIVATE_GROUP_ID, "Tunnel-Private-Group-Id",
  154. RADIUS_ATTR_HEXDUMP },
  155. { RADIUS_ATTR_ACCT_INTERIM_INTERVAL, "Acct-Interim-Interval",
  156. RADIUS_ATTR_INT32 },
  157. { RADIUS_ATTR_CHARGEABLE_USER_IDENTITY, "Chargable-User-Identity",
  158. RADIUS_ATTR_TEXT },
  159. { RADIUS_ATTR_NAS_IPV6_ADDRESS, "NAS-IPv6-Address", RADIUS_ATTR_IPV6 },
  160. };
  161. #define RADIUS_ATTRS (sizeof(radius_attrs) / sizeof(radius_attrs[0]))
  162. static struct radius_attr_type *radius_get_attr_type(u8 type)
  163. {
  164. size_t i;
  165. for (i = 0; i < RADIUS_ATTRS; i++) {
  166. if (type == radius_attrs[i].type)
  167. return &radius_attrs[i];
  168. }
  169. return NULL;
  170. }
  171. static void print_char(char c)
  172. {
  173. if (c >= 32 && c < 127)
  174. printf("%c", c);
  175. else
  176. printf("<%02x>", c);
  177. }
  178. static void radius_msg_dump_attr(struct radius_attr_hdr *hdr)
  179. {
  180. struct radius_attr_type *attr;
  181. int i, len;
  182. unsigned char *pos;
  183. attr = radius_get_attr_type(hdr->type);
  184. printf(" Attribute %d (%s) length=%d\n",
  185. hdr->type, attr ? attr->name : "?Unknown?", hdr->length);
  186. if (attr == NULL)
  187. return;
  188. len = hdr->length - sizeof(struct radius_attr_hdr);
  189. pos = (unsigned char *) (hdr + 1);
  190. switch (attr->data_type) {
  191. case RADIUS_ATTR_TEXT:
  192. printf(" Value: '");
  193. for (i = 0; i < len; i++)
  194. print_char(pos[i]);
  195. printf("'\n");
  196. break;
  197. case RADIUS_ATTR_IP:
  198. if (len == 4) {
  199. struct in_addr addr;
  200. os_memcpy(&addr, pos, 4);
  201. printf(" Value: %s\n", inet_ntoa(addr));
  202. } else
  203. printf(" Invalid IP address length %d\n", len);
  204. break;
  205. #ifdef CONFIG_IPV6
  206. case RADIUS_ATTR_IPV6:
  207. if (len == 16) {
  208. char buf[128];
  209. const char *atxt;
  210. struct in6_addr *addr = (struct in6_addr *) pos;
  211. atxt = inet_ntop(AF_INET6, addr, buf, sizeof(buf));
  212. printf(" Value: %s\n", atxt ? atxt : "?");
  213. } else
  214. printf(" Invalid IPv6 address length %d\n", len);
  215. break;
  216. #endif /* CONFIG_IPV6 */
  217. case RADIUS_ATTR_HEXDUMP:
  218. case RADIUS_ATTR_UNDIST:
  219. printf(" Value:");
  220. for (i = 0; i < len; i++)
  221. printf(" %02x", pos[i]);
  222. printf("\n");
  223. break;
  224. case RADIUS_ATTR_INT32:
  225. if (len == 4)
  226. printf(" Value: %u\n", WPA_GET_BE32(pos));
  227. else
  228. printf(" Invalid INT32 length %d\n", len);
  229. break;
  230. default:
  231. break;
  232. }
  233. }
  234. void radius_msg_dump(struct radius_msg *msg)
  235. {
  236. size_t i;
  237. printf("RADIUS message: code=%d (%s) identifier=%d length=%d\n",
  238. msg->hdr->code, radius_code_string(msg->hdr->code),
  239. msg->hdr->identifier, ntohs(msg->hdr->length));
  240. for (i = 0; i < msg->attr_used; i++) {
  241. struct radius_attr_hdr *attr = radius_get_attr_hdr(msg, i);
  242. radius_msg_dump_attr(attr);
  243. }
  244. }
  245. int radius_msg_finish(struct radius_msg *msg, const u8 *secret,
  246. size_t secret_len)
  247. {
  248. if (secret) {
  249. u8 auth[MD5_MAC_LEN];
  250. struct radius_attr_hdr *attr;
  251. os_memset(auth, 0, MD5_MAC_LEN);
  252. attr = radius_msg_add_attr(msg,
  253. RADIUS_ATTR_MESSAGE_AUTHENTICATOR,
  254. auth, MD5_MAC_LEN);
  255. if (attr == NULL) {
  256. printf("WARNING: Could not add "
  257. "Message-Authenticator\n");
  258. return -1;
  259. }
  260. msg->hdr->length = htons(msg->buf_used);
  261. hmac_md5(secret, secret_len, msg->buf, msg->buf_used,
  262. (u8 *) (attr + 1));
  263. } else
  264. msg->hdr->length = htons(msg->buf_used);
  265. if (msg->buf_used > 0xffff) {
  266. printf("WARNING: too long RADIUS message (%lu)\n",
  267. (unsigned long) msg->buf_used);
  268. return -1;
  269. }
  270. return 0;
  271. }
  272. int radius_msg_finish_srv(struct radius_msg *msg, const u8 *secret,
  273. size_t secret_len, const u8 *req_authenticator)
  274. {
  275. u8 auth[MD5_MAC_LEN];
  276. struct radius_attr_hdr *attr;
  277. const u8 *addr[4];
  278. size_t len[4];
  279. os_memset(auth, 0, MD5_MAC_LEN);
  280. attr = radius_msg_add_attr(msg, RADIUS_ATTR_MESSAGE_AUTHENTICATOR,
  281. auth, MD5_MAC_LEN);
  282. if (attr == NULL) {
  283. printf("WARNING: Could not add Message-Authenticator\n");
  284. return -1;
  285. }
  286. msg->hdr->length = htons(msg->buf_used);
  287. os_memcpy(msg->hdr->authenticator, req_authenticator,
  288. sizeof(msg->hdr->authenticator));
  289. hmac_md5(secret, secret_len, msg->buf, msg->buf_used,
  290. (u8 *) (attr + 1));
  291. /* ResponseAuth = MD5(Code+ID+Length+RequestAuth+Attributes+Secret) */
  292. addr[0] = (u8 *) msg->hdr;
  293. len[0] = 1 + 1 + 2;
  294. addr[1] = req_authenticator;
  295. len[1] = MD5_MAC_LEN;
  296. addr[2] = (u8 *) (msg->hdr + 1);
  297. len[2] = msg->buf_used - sizeof(*msg->hdr);
  298. addr[3] = secret;
  299. len[3] = secret_len;
  300. md5_vector(4, addr, len, msg->hdr->authenticator);
  301. if (msg->buf_used > 0xffff) {
  302. printf("WARNING: too long RADIUS message (%lu)\n",
  303. (unsigned long) msg->buf_used);
  304. return -1;
  305. }
  306. return 0;
  307. }
  308. void radius_msg_finish_acct(struct radius_msg *msg, const u8 *secret,
  309. size_t secret_len)
  310. {
  311. const u8 *addr[2];
  312. size_t len[2];
  313. msg->hdr->length = htons(msg->buf_used);
  314. os_memset(msg->hdr->authenticator, 0, MD5_MAC_LEN);
  315. addr[0] = msg->buf;
  316. len[0] = msg->buf_used;
  317. addr[1] = secret;
  318. len[1] = secret_len;
  319. md5_vector(2, addr, len, msg->hdr->authenticator);
  320. if (msg->buf_used > 0xffff) {
  321. printf("WARNING: too long RADIUS messages (%lu)\n",
  322. (unsigned long) msg->buf_used);
  323. }
  324. }
  325. static int radius_msg_add_attr_to_array(struct radius_msg *msg,
  326. struct radius_attr_hdr *attr)
  327. {
  328. if (msg->attr_used >= msg->attr_size) {
  329. size_t *nattr_pos;
  330. int nlen = msg->attr_size * 2;
  331. nattr_pos = os_realloc(msg->attr_pos,
  332. nlen * sizeof(*msg->attr_pos));
  333. if (nattr_pos == NULL)
  334. return -1;
  335. msg->attr_pos = nattr_pos;
  336. msg->attr_size = nlen;
  337. }
  338. msg->attr_pos[msg->attr_used++] = (unsigned char *) attr - msg->buf;
  339. return 0;
  340. }
  341. struct radius_attr_hdr *radius_msg_add_attr(struct radius_msg *msg, u8 type,
  342. const u8 *data, size_t data_len)
  343. {
  344. size_t buf_needed;
  345. struct radius_attr_hdr *attr;
  346. if (data_len > RADIUS_MAX_ATTR_LEN) {
  347. printf("radius_msg_add_attr: too long attribute (%lu bytes)\n",
  348. (unsigned long) data_len);
  349. return NULL;
  350. }
  351. buf_needed = msg->buf_used + sizeof(*attr) + data_len;
  352. if (msg->buf_size < buf_needed) {
  353. /* allocate more space for message buffer */
  354. unsigned char *nbuf;
  355. size_t nlen = msg->buf_size;
  356. while (nlen < buf_needed)
  357. nlen *= 2;
  358. nbuf = os_realloc(msg->buf, nlen);
  359. if (nbuf == NULL)
  360. return NULL;
  361. msg->buf = nbuf;
  362. msg->hdr = (struct radius_hdr *) msg->buf;
  363. os_memset(msg->buf + msg->buf_size, 0, nlen - msg->buf_size);
  364. msg->buf_size = nlen;
  365. }
  366. attr = (struct radius_attr_hdr *) (msg->buf + msg->buf_used);
  367. attr->type = type;
  368. attr->length = sizeof(*attr) + data_len;
  369. if (data_len > 0)
  370. os_memcpy(attr + 1, data, data_len);
  371. msg->buf_used += sizeof(*attr) + data_len;
  372. if (radius_msg_add_attr_to_array(msg, attr))
  373. return NULL;
  374. return attr;
  375. }
  376. struct radius_msg *radius_msg_parse(const u8 *data, size_t len)
  377. {
  378. struct radius_msg *msg;
  379. struct radius_hdr *hdr;
  380. struct radius_attr_hdr *attr;
  381. size_t msg_len;
  382. unsigned char *pos, *end;
  383. if (data == NULL || len < sizeof(*hdr))
  384. return NULL;
  385. hdr = (struct radius_hdr *) data;
  386. msg_len = ntohs(hdr->length);
  387. if (msg_len < sizeof(*hdr) || msg_len > len) {
  388. printf("Invalid RADIUS message length\n");
  389. return NULL;
  390. }
  391. if (msg_len < len) {
  392. printf("Ignored %lu extra bytes after RADIUS message\n",
  393. (unsigned long) len - msg_len);
  394. }
  395. msg = os_malloc(sizeof(*msg));
  396. if (msg == NULL)
  397. return NULL;
  398. if (radius_msg_initialize(msg, msg_len)) {
  399. os_free(msg);
  400. return NULL;
  401. }
  402. os_memcpy(msg->buf, data, msg_len);
  403. msg->buf_size = msg->buf_used = msg_len;
  404. /* parse attributes */
  405. pos = (unsigned char *) (msg->hdr + 1);
  406. end = msg->buf + msg->buf_used;
  407. while (pos < end) {
  408. if ((size_t) (end - pos) < sizeof(*attr))
  409. goto fail;
  410. attr = (struct radius_attr_hdr *) pos;
  411. if (pos + attr->length > end || attr->length < sizeof(*attr))
  412. goto fail;
  413. /* TODO: check that attr->length is suitable for attr->type */
  414. if (radius_msg_add_attr_to_array(msg, attr))
  415. goto fail;
  416. pos += attr->length;
  417. }
  418. return msg;
  419. fail:
  420. radius_msg_free(msg);
  421. os_free(msg);
  422. return NULL;
  423. }
  424. int radius_msg_add_eap(struct radius_msg *msg, const u8 *data, size_t data_len)
  425. {
  426. const u8 *pos = data;
  427. size_t left = data_len;
  428. while (left > 0) {
  429. int len;
  430. if (left > RADIUS_MAX_ATTR_LEN)
  431. len = RADIUS_MAX_ATTR_LEN;
  432. else
  433. len = left;
  434. if (!radius_msg_add_attr(msg, RADIUS_ATTR_EAP_MESSAGE,
  435. pos, len))
  436. return 0;
  437. pos += len;
  438. left -= len;
  439. }
  440. return 1;
  441. }
  442. u8 *radius_msg_get_eap(struct radius_msg *msg, size_t *eap_len)
  443. {
  444. u8 *eap, *pos;
  445. size_t len, i;
  446. struct radius_attr_hdr *attr;
  447. if (msg == NULL)
  448. return NULL;
  449. len = 0;
  450. for (i = 0; i < msg->attr_used; i++) {
  451. attr = radius_get_attr_hdr(msg, i);
  452. if (attr->type == RADIUS_ATTR_EAP_MESSAGE)
  453. len += attr->length - sizeof(struct radius_attr_hdr);
  454. }
  455. if (len == 0)
  456. return NULL;
  457. eap = os_malloc(len);
  458. if (eap == NULL)
  459. return NULL;
  460. pos = eap;
  461. for (i = 0; i < msg->attr_used; i++) {
  462. attr = radius_get_attr_hdr(msg, i);
  463. if (attr->type == RADIUS_ATTR_EAP_MESSAGE) {
  464. int flen = attr->length - sizeof(*attr);
  465. os_memcpy(pos, attr + 1, flen);
  466. pos += flen;
  467. }
  468. }
  469. if (eap_len)
  470. *eap_len = len;
  471. return eap;
  472. }
  473. int radius_msg_verify_msg_auth(struct radius_msg *msg, const u8 *secret,
  474. size_t secret_len, const u8 *req_auth)
  475. {
  476. u8 auth[MD5_MAC_LEN], orig[MD5_MAC_LEN];
  477. u8 orig_authenticator[16];
  478. struct radius_attr_hdr *attr = NULL, *tmp;
  479. size_t i;
  480. for (i = 0; i < msg->attr_used; i++) {
  481. tmp = radius_get_attr_hdr(msg, i);
  482. if (tmp->type == RADIUS_ATTR_MESSAGE_AUTHENTICATOR) {
  483. if (attr != NULL) {
  484. printf("Multiple Message-Authenticator "
  485. "attributes in RADIUS message\n");
  486. return 1;
  487. }
  488. attr = tmp;
  489. }
  490. }
  491. if (attr == NULL) {
  492. printf("No Message-Authenticator attribute found\n");
  493. return 1;
  494. }
  495. os_memcpy(orig, attr + 1, MD5_MAC_LEN);
  496. os_memset(attr + 1, 0, MD5_MAC_LEN);
  497. if (req_auth) {
  498. os_memcpy(orig_authenticator, msg->hdr->authenticator,
  499. sizeof(orig_authenticator));
  500. os_memcpy(msg->hdr->authenticator, req_auth,
  501. sizeof(msg->hdr->authenticator));
  502. }
  503. hmac_md5(secret, secret_len, msg->buf, msg->buf_used, auth);
  504. os_memcpy(attr + 1, orig, MD5_MAC_LEN);
  505. if (req_auth) {
  506. os_memcpy(msg->hdr->authenticator, orig_authenticator,
  507. sizeof(orig_authenticator));
  508. }
  509. if (os_memcmp(orig, auth, MD5_MAC_LEN) != 0) {
  510. printf("Invalid Message-Authenticator!\n");
  511. return 1;
  512. }
  513. return 0;
  514. }
  515. int radius_msg_verify(struct radius_msg *msg, const u8 *secret,
  516. size_t secret_len, struct radius_msg *sent_msg, int auth)
  517. {
  518. const u8 *addr[4];
  519. size_t len[4];
  520. u8 hash[MD5_MAC_LEN];
  521. if (sent_msg == NULL) {
  522. printf("No matching Access-Request message found\n");
  523. return 1;
  524. }
  525. if (auth &&
  526. radius_msg_verify_msg_auth(msg, secret, secret_len,
  527. sent_msg->hdr->authenticator)) {
  528. return 1;
  529. }
  530. /* ResponseAuth = MD5(Code+ID+Length+RequestAuth+Attributes+Secret) */
  531. addr[0] = (u8 *) msg->hdr;
  532. len[0] = 1 + 1 + 2;
  533. addr[1] = sent_msg->hdr->authenticator;
  534. len[1] = MD5_MAC_LEN;
  535. addr[2] = (u8 *) (msg->hdr + 1);
  536. len[2] = msg->buf_used - sizeof(*msg->hdr);
  537. addr[3] = secret;
  538. len[3] = secret_len;
  539. md5_vector(4, addr, len, hash);
  540. if (os_memcmp(hash, msg->hdr->authenticator, MD5_MAC_LEN) != 0) {
  541. printf("Response Authenticator invalid!\n");
  542. return 1;
  543. }
  544. return 0;
  545. }
  546. int radius_msg_copy_attr(struct radius_msg *dst, struct radius_msg *src,
  547. u8 type)
  548. {
  549. struct radius_attr_hdr *attr;
  550. size_t i;
  551. int count = 0;
  552. for (i = 0; i < src->attr_used; i++) {
  553. attr = radius_get_attr_hdr(src, i);
  554. if (attr->type == type) {
  555. if (!radius_msg_add_attr(dst, type, (u8 *) (attr + 1),
  556. attr->length - sizeof(*attr)))
  557. return -1;
  558. count++;
  559. }
  560. }
  561. return count;
  562. }
  563. /* Create Request Authenticator. The value should be unique over the lifetime
  564. * of the shared secret between authenticator and authentication server.
  565. * Use one-way MD5 hash calculated from current timestamp and some data given
  566. * by the caller. */
  567. void radius_msg_make_authenticator(struct radius_msg *msg,
  568. const u8 *data, size_t len)
  569. {
  570. struct os_time tv;
  571. long int l;
  572. const u8 *addr[3];
  573. size_t elen[3];
  574. os_get_time(&tv);
  575. l = os_random();
  576. addr[0] = (u8 *) &tv;
  577. elen[0] = sizeof(tv);
  578. addr[1] = data;
  579. elen[1] = len;
  580. addr[2] = (u8 *) &l;
  581. elen[2] = sizeof(l);
  582. md5_vector(3, addr, elen, msg->hdr->authenticator);
  583. }
  584. /* Get Vendor-specific RADIUS Attribute from a parsed RADIUS message.
  585. * Returns the Attribute payload and sets alen to indicate the length of the
  586. * payload if a vendor attribute with subtype is found, otherwise returns NULL.
  587. * The returned payload is allocated with os_malloc() and caller must free it
  588. * by calling os_free().
  589. */
  590. static u8 *radius_msg_get_vendor_attr(struct radius_msg *msg, u32 vendor,
  591. u8 subtype, size_t *alen)
  592. {
  593. u8 *data, *pos;
  594. size_t i, len;
  595. if (msg == NULL)
  596. return NULL;
  597. for (i = 0; i < msg->attr_used; i++) {
  598. struct radius_attr_hdr *attr = radius_get_attr_hdr(msg, i);
  599. size_t left;
  600. u32 vendor_id;
  601. struct radius_attr_vendor *vhdr;
  602. if (attr->type != RADIUS_ATTR_VENDOR_SPECIFIC)
  603. continue;
  604. left = attr->length - sizeof(*attr);
  605. if (left < 4)
  606. continue;
  607. pos = (u8 *) (attr + 1);
  608. os_memcpy(&vendor_id, pos, 4);
  609. pos += 4;
  610. left -= 4;
  611. if (ntohl(vendor_id) != vendor)
  612. continue;
  613. while (left >= sizeof(*vhdr)) {
  614. vhdr = (struct radius_attr_vendor *) pos;
  615. if (vhdr->vendor_length > left ||
  616. vhdr->vendor_length < sizeof(*vhdr)) {
  617. left = 0;
  618. break;
  619. }
  620. if (vhdr->vendor_type != subtype) {
  621. pos += vhdr->vendor_length;
  622. left -= vhdr->vendor_length;
  623. continue;
  624. }
  625. len = vhdr->vendor_length - sizeof(*vhdr);
  626. data = os_malloc(len);
  627. if (data == NULL)
  628. return NULL;
  629. os_memcpy(data, pos + sizeof(*vhdr), len);
  630. if (alen)
  631. *alen = len;
  632. return data;
  633. }
  634. }
  635. return NULL;
  636. }
  637. static u8 * decrypt_ms_key(const u8 *key, size_t len,
  638. const u8 *req_authenticator,
  639. const u8 *secret, size_t secret_len, size_t *reslen)
  640. {
  641. u8 *plain, *ppos, *res;
  642. const u8 *pos;
  643. size_t left, plen;
  644. u8 hash[MD5_MAC_LEN];
  645. int i, first = 1;
  646. const u8 *addr[3];
  647. size_t elen[3];
  648. /* key: 16-bit salt followed by encrypted key info */
  649. if (len < 2 + 16)
  650. return NULL;
  651. pos = key + 2;
  652. left = len - 2;
  653. if (left % 16) {
  654. printf("Invalid ms key len %lu\n", (unsigned long) left);
  655. return NULL;
  656. }
  657. plen = left;
  658. ppos = plain = os_malloc(plen);
  659. if (plain == NULL)
  660. return NULL;
  661. plain[0] = 0;
  662. while (left > 0) {
  663. /* b(1) = MD5(Secret + Request-Authenticator + Salt)
  664. * b(i) = MD5(Secret + c(i - 1)) for i > 1 */
  665. addr[0] = secret;
  666. elen[0] = secret_len;
  667. if (first) {
  668. addr[1] = req_authenticator;
  669. elen[1] = MD5_MAC_LEN;
  670. addr[2] = key;
  671. elen[2] = 2; /* Salt */
  672. } else {
  673. addr[1] = pos - MD5_MAC_LEN;
  674. elen[1] = MD5_MAC_LEN;
  675. }
  676. md5_vector(first ? 3 : 2, addr, elen, hash);
  677. first = 0;
  678. for (i = 0; i < MD5_MAC_LEN; i++)
  679. *ppos++ = *pos++ ^ hash[i];
  680. left -= MD5_MAC_LEN;
  681. }
  682. if (plain[0] == 0 || plain[0] > plen - 1) {
  683. printf("Failed to decrypt MPPE key\n");
  684. os_free(plain);
  685. return NULL;
  686. }
  687. res = os_malloc(plain[0]);
  688. if (res == NULL) {
  689. os_free(plain);
  690. return NULL;
  691. }
  692. os_memcpy(res, plain + 1, plain[0]);
  693. if (reslen)
  694. *reslen = plain[0];
  695. os_free(plain);
  696. return res;
  697. }
  698. static void encrypt_ms_key(const u8 *key, size_t key_len, u16 salt,
  699. const u8 *req_authenticator,
  700. const u8 *secret, size_t secret_len,
  701. u8 *ebuf, size_t *elen)
  702. {
  703. int i, len, first = 1;
  704. u8 hash[MD5_MAC_LEN], saltbuf[2], *pos;
  705. const u8 *addr[3];
  706. size_t _len[3];
  707. WPA_PUT_BE16(saltbuf, salt);
  708. len = 1 + key_len;
  709. if (len & 0x0f) {
  710. len = (len & 0xf0) + 16;
  711. }
  712. os_memset(ebuf, 0, len);
  713. ebuf[0] = key_len;
  714. os_memcpy(ebuf + 1, key, key_len);
  715. *elen = len;
  716. pos = ebuf;
  717. while (len > 0) {
  718. /* b(1) = MD5(Secret + Request-Authenticator + Salt)
  719. * b(i) = MD5(Secret + c(i - 1)) for i > 1 */
  720. addr[0] = secret;
  721. _len[0] = secret_len;
  722. if (first) {
  723. addr[1] = req_authenticator;
  724. _len[1] = MD5_MAC_LEN;
  725. addr[2] = saltbuf;
  726. _len[2] = sizeof(saltbuf);
  727. } else {
  728. addr[1] = pos - MD5_MAC_LEN;
  729. _len[1] = MD5_MAC_LEN;
  730. }
  731. md5_vector(first ? 3 : 2, addr, _len, hash);
  732. first = 0;
  733. for (i = 0; i < MD5_MAC_LEN; i++)
  734. *pos++ ^= hash[i];
  735. len -= MD5_MAC_LEN;
  736. }
  737. }
  738. struct radius_ms_mppe_keys *
  739. radius_msg_get_ms_keys(struct radius_msg *msg, struct radius_msg *sent_msg,
  740. const u8 *secret, size_t secret_len)
  741. {
  742. u8 *key;
  743. size_t keylen;
  744. struct radius_ms_mppe_keys *keys;
  745. if (msg == NULL || sent_msg == NULL)
  746. return NULL;
  747. keys = os_zalloc(sizeof(*keys));
  748. if (keys == NULL)
  749. return NULL;
  750. key = radius_msg_get_vendor_attr(msg, RADIUS_VENDOR_ID_MICROSOFT,
  751. RADIUS_VENDOR_ATTR_MS_MPPE_SEND_KEY,
  752. &keylen);
  753. if (key) {
  754. keys->send = decrypt_ms_key(key, keylen,
  755. sent_msg->hdr->authenticator,
  756. secret, secret_len,
  757. &keys->send_len);
  758. os_free(key);
  759. }
  760. key = radius_msg_get_vendor_attr(msg, RADIUS_VENDOR_ID_MICROSOFT,
  761. RADIUS_VENDOR_ATTR_MS_MPPE_RECV_KEY,
  762. &keylen);
  763. if (key) {
  764. keys->recv = decrypt_ms_key(key, keylen,
  765. sent_msg->hdr->authenticator,
  766. secret, secret_len,
  767. &keys->recv_len);
  768. os_free(key);
  769. }
  770. return keys;
  771. }
  772. struct radius_ms_mppe_keys *
  773. radius_msg_get_cisco_keys(struct radius_msg *msg, struct radius_msg *sent_msg,
  774. const u8 *secret, size_t secret_len)
  775. {
  776. u8 *key;
  777. size_t keylen;
  778. struct radius_ms_mppe_keys *keys;
  779. if (msg == NULL || sent_msg == NULL)
  780. return NULL;
  781. keys = os_zalloc(sizeof(*keys));
  782. if (keys == NULL)
  783. return NULL;
  784. key = radius_msg_get_vendor_attr(msg, RADIUS_VENDOR_ID_CISCO,
  785. RADIUS_CISCO_AV_PAIR, &keylen);
  786. if (key && keylen == 51 &&
  787. os_memcmp(key, "leap:session-key=", 17) == 0) {
  788. keys->recv = decrypt_ms_key(key + 17, keylen - 17,
  789. sent_msg->hdr->authenticator,
  790. secret, secret_len,
  791. &keys->recv_len);
  792. }
  793. os_free(key);
  794. return keys;
  795. }
  796. int radius_msg_add_mppe_keys(struct radius_msg *msg,
  797. const u8 *req_authenticator,
  798. const u8 *secret, size_t secret_len,
  799. const u8 *send_key, size_t send_key_len,
  800. const u8 *recv_key, size_t recv_key_len)
  801. {
  802. struct radius_attr_hdr *attr;
  803. u32 vendor_id = htonl(RADIUS_VENDOR_ID_MICROSOFT);
  804. u8 *buf;
  805. struct radius_attr_vendor *vhdr;
  806. u8 *pos;
  807. size_t elen;
  808. int hlen;
  809. u16 salt;
  810. hlen = sizeof(vendor_id) + sizeof(*vhdr) + 2;
  811. /* MS-MPPE-Send-Key */
  812. buf = os_malloc(hlen + send_key_len + 16);
  813. if (buf == NULL) {
  814. return 0;
  815. }
  816. pos = buf;
  817. os_memcpy(pos, &vendor_id, sizeof(vendor_id));
  818. pos += sizeof(vendor_id);
  819. vhdr = (struct radius_attr_vendor *) pos;
  820. vhdr->vendor_type = RADIUS_VENDOR_ATTR_MS_MPPE_SEND_KEY;
  821. pos = (u8 *) (vhdr + 1);
  822. salt = os_random() | 0x8000;
  823. WPA_PUT_BE16(pos, salt);
  824. pos += 2;
  825. encrypt_ms_key(send_key, send_key_len, salt, req_authenticator, secret,
  826. secret_len, pos, &elen);
  827. vhdr->vendor_length = hlen + elen - sizeof(vendor_id);
  828. attr = radius_msg_add_attr(msg, RADIUS_ATTR_VENDOR_SPECIFIC,
  829. buf, hlen + elen);
  830. os_free(buf);
  831. if (attr == NULL) {
  832. return 0;
  833. }
  834. /* MS-MPPE-Recv-Key */
  835. buf = os_malloc(hlen + send_key_len + 16);
  836. if (buf == NULL) {
  837. return 0;
  838. }
  839. pos = buf;
  840. os_memcpy(pos, &vendor_id, sizeof(vendor_id));
  841. pos += sizeof(vendor_id);
  842. vhdr = (struct radius_attr_vendor *) pos;
  843. vhdr->vendor_type = RADIUS_VENDOR_ATTR_MS_MPPE_RECV_KEY;
  844. pos = (u8 *) (vhdr + 1);
  845. salt ^= 1;
  846. WPA_PUT_BE16(pos, salt);
  847. pos += 2;
  848. encrypt_ms_key(recv_key, recv_key_len, salt, req_authenticator, secret,
  849. secret_len, pos, &elen);
  850. vhdr->vendor_length = hlen + elen - sizeof(vendor_id);
  851. attr = radius_msg_add_attr(msg, RADIUS_ATTR_VENDOR_SPECIFIC,
  852. buf, hlen + elen);
  853. os_free(buf);
  854. if (attr == NULL) {
  855. return 0;
  856. }
  857. return 1;
  858. }
  859. /* Add User-Password attribute to a RADIUS message and encrypt it as specified
  860. * in RFC 2865, Chap. 5.2 */
  861. struct radius_attr_hdr *
  862. radius_msg_add_attr_user_password(struct radius_msg *msg,
  863. const u8 *data, size_t data_len,
  864. const u8 *secret, size_t secret_len)
  865. {
  866. u8 buf[128];
  867. int padlen, i;
  868. size_t buf_len, pos;
  869. const u8 *addr[2];
  870. size_t len[2];
  871. u8 hash[16];
  872. if (data_len > 128)
  873. return NULL;
  874. os_memcpy(buf, data, data_len);
  875. buf_len = data_len;
  876. padlen = data_len % 16;
  877. if (padlen) {
  878. padlen = 16 - padlen;
  879. os_memset(buf + data_len, 0, padlen);
  880. buf_len += padlen;
  881. }
  882. addr[0] = secret;
  883. len[0] = secret_len;
  884. addr[1] = msg->hdr->authenticator;
  885. len[1] = 16;
  886. md5_vector(2, addr, len, hash);
  887. for (i = 0; i < 16; i++)
  888. buf[i] ^= hash[i];
  889. pos = 16;
  890. while (pos < buf_len) {
  891. addr[0] = secret;
  892. len[0] = secret_len;
  893. addr[1] = &buf[pos - 16];
  894. len[1] = 16;
  895. md5_vector(2, addr, len, hash);
  896. for (i = 0; i < 16; i++)
  897. buf[pos + i] ^= hash[i];
  898. pos += 16;
  899. }
  900. return radius_msg_add_attr(msg, RADIUS_ATTR_USER_PASSWORD,
  901. buf, buf_len);
  902. }
  903. int radius_msg_get_attr(struct radius_msg *msg, u8 type, u8 *buf, size_t len)
  904. {
  905. struct radius_attr_hdr *attr = NULL, *tmp;
  906. size_t i, dlen;
  907. for (i = 0; i < msg->attr_used; i++) {
  908. tmp = radius_get_attr_hdr(msg, i);
  909. if (tmp->type == type) {
  910. attr = tmp;
  911. break;
  912. }
  913. }
  914. if (!attr)
  915. return -1;
  916. dlen = attr->length - sizeof(*attr);
  917. if (buf)
  918. os_memcpy(buf, (attr + 1), dlen > len ? len : dlen);
  919. return dlen;
  920. }
  921. int radius_msg_get_attr_ptr(struct radius_msg *msg, u8 type, u8 **buf,
  922. size_t *len, const u8 *start)
  923. {
  924. size_t i;
  925. struct radius_attr_hdr *attr = NULL, *tmp;
  926. for (i = 0; i < msg->attr_used; i++) {
  927. tmp = radius_get_attr_hdr(msg, i);
  928. if (tmp->type == type &&
  929. (start == NULL || (u8 *) tmp > start)) {
  930. attr = tmp;
  931. break;
  932. }
  933. }
  934. if (!attr)
  935. return -1;
  936. *buf = (u8 *) (attr + 1);
  937. *len = attr->length - sizeof(*attr);
  938. return 0;
  939. }
  940. int radius_msg_count_attr(struct radius_msg *msg, u8 type, int min_len)
  941. {
  942. size_t i;
  943. int count;
  944. for (count = 0, i = 0; i < msg->attr_used; i++) {
  945. struct radius_attr_hdr *attr = radius_get_attr_hdr(msg, i);
  946. if (attr->type == type &&
  947. attr->length >= sizeof(struct radius_attr_hdr) + min_len)
  948. count++;
  949. }
  950. return count;
  951. }
  952. struct radius_tunnel_attrs {
  953. int tag_used;
  954. int type; /* Tunnel-Type */
  955. int medium_type; /* Tunnel-Medium-Type */
  956. int vlanid;
  957. };
  958. /**
  959. * radius_msg_get_vlanid - Parse RADIUS attributes for VLAN tunnel information
  960. * @msg: RADIUS message
  961. * Returns: VLAN ID for the first tunnel configuration of -1 if none is found
  962. */
  963. int radius_msg_get_vlanid(struct radius_msg *msg)
  964. {
  965. struct radius_tunnel_attrs tunnel[RADIUS_TUNNEL_TAGS], *tun;
  966. size_t i;
  967. struct radius_attr_hdr *attr = NULL;
  968. const u8 *data;
  969. char buf[10];
  970. size_t dlen;
  971. os_memset(&tunnel, 0, sizeof(tunnel));
  972. for (i = 0; i < msg->attr_used; i++) {
  973. attr = radius_get_attr_hdr(msg, i);
  974. data = (const u8 *) (attr + 1);
  975. dlen = attr->length - sizeof(*attr);
  976. if (attr->length < 3)
  977. continue;
  978. if (data[0] >= RADIUS_TUNNEL_TAGS)
  979. tun = &tunnel[0];
  980. else
  981. tun = &tunnel[data[0]];
  982. switch (attr->type) {
  983. case RADIUS_ATTR_TUNNEL_TYPE:
  984. if (attr->length != 6)
  985. break;
  986. tun->tag_used++;
  987. tun->type = WPA_GET_BE24(data + 1);
  988. break;
  989. case RADIUS_ATTR_TUNNEL_MEDIUM_TYPE:
  990. if (attr->length != 6)
  991. break;
  992. tun->tag_used++;
  993. tun->medium_type = WPA_GET_BE24(data + 1);
  994. break;
  995. case RADIUS_ATTR_TUNNEL_PRIVATE_GROUP_ID:
  996. if (data[0] < RADIUS_TUNNEL_TAGS) {
  997. data++;
  998. dlen--;
  999. }
  1000. if (dlen >= sizeof(buf))
  1001. break;
  1002. os_memcpy(buf, data, dlen);
  1003. buf[dlen] = '\0';
  1004. tun->tag_used++;
  1005. tun->vlanid = atoi(buf);
  1006. break;
  1007. }
  1008. }
  1009. for (i = 0; i < RADIUS_TUNNEL_TAGS; i++) {
  1010. tun = &tunnel[i];
  1011. if (tun->tag_used &&
  1012. tun->type == RADIUS_TUNNEL_TYPE_VLAN &&
  1013. tun->medium_type == RADIUS_TUNNEL_MEDIUM_TYPE_802 &&
  1014. tun->vlanid > 0)
  1015. return tun->vlanid;
  1016. }
  1017. return -1;
  1018. }
  1019. void radius_free_class(struct radius_class_data *c)
  1020. {
  1021. size_t i;
  1022. if (c == NULL)
  1023. return;
  1024. for (i = 0; i < c->count; i++)
  1025. os_free(c->attr[i].data);
  1026. os_free(c->attr);
  1027. c->attr = NULL;
  1028. c->count = 0;
  1029. }
  1030. int radius_copy_class(struct radius_class_data *dst,
  1031. const struct radius_class_data *src)
  1032. {
  1033. size_t i;
  1034. if (src->attr == NULL)
  1035. return 0;
  1036. dst->attr = os_zalloc(src->count * sizeof(struct radius_attr_data));
  1037. if (dst->attr == NULL)
  1038. return -1;
  1039. dst->count = 0;
  1040. for (i = 0; i < src->count; i++) {
  1041. dst->attr[i].data = os_malloc(src->attr[i].len);
  1042. if (dst->attr[i].data == NULL)
  1043. break;
  1044. dst->count++;
  1045. os_memcpy(dst->attr[i].data, src->attr[i].data,
  1046. src->attr[i].len);
  1047. dst->attr[i].len = src->attr[i].len;
  1048. }
  1049. return 0;
  1050. }