All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
soh.c
Go to the documentation of this file.
1 /*
2  * This program is free software; you can redistribute it and/or modify
3  * it under the terms of the GNU General Public License as published by
4  * the Free Software Foundation; either version 2 of the License, or
5  * (at your option) any later version.
6  *
7  * This program is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10  * GNU General Public License for more details.
11  *
12  * You should have received a copy of the GNU General Public License
13  * along with this program; if not, write to the Free Software
14  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
15  */
16 
17 /**
18  * $Id: 16013ec2805fb4a532e68f9e9cc216dba3204e05 $
19  *
20  * @file soh.c
21  * @brief Implements the MS-SOH parsing code. This is called from rlm_eap_peap
22  *
23  * @copyright 2010 Phil Mayers <p.mayers@imperial.ac.uk>
24  */
25 
26 RCSID("$Id: 16013ec2805fb4a532e68f9e9cc216dba3204e05 $")
27 
28 #include <freeradius-devel/radiusd.h>
29 #include <freeradius-devel/soh.h>
30 #include <freeradius-devel/rad_assert.h>
31 
32 /*
33  * This code implements parsing of MS-SOH data into FreeRadius AVPs
34  * allowing for FreeRadius MS-NAP policies
35  */
36 
37 /**
38  * EAP-SOH packet
39  */
40 typedef struct {
41  uint16_t tlv_type; /**< ==7 for EAP-SOH */
42  uint16_t tlv_len;
43  uint32_t tlv_vendor;
44 
45  /**
46  * @name soh-payload
47  * @brief either an soh request or response */
48  uint16_t soh_type; /**< ==2 for request, 1 for response */
49  uint16_t soh_len;
50 
51  /* an soh-response may now follow... */
52 } eap_soh;
53 
54 /**
55  * SOH response payload
56  * Send by client to server
57  */
58 typedef struct {
59  uint16_t outer_type;
60  uint16_t outer_len;
61  uint32_t vendor;
62  uint16_t inner_type;
63  uint16_t inner_len;
64 } soh_response;
65 
66 /**
67  * SOH mode subheader
68  * Typical microsoft binary blob nonsense
69  */
70 typedef struct {
71  uint16_t outer_type;
72  uint16_t outer_len;
73  uint32_t vendor;
74  uint8_t corrid[24];
75  uint8_t intent;
76  uint8_t content_type;
78 
79 /**
80  * SOH type-length-value header
81  */
82 typedef struct {
83  uint16_t tlv_type;
84  uint16_t tlv_len;
85 } soh_tlv;
86 
87 /** Read big-endian 2-byte unsigned from p
88  *
89  * caller must ensure enough data exists at "p"
90  */
91 uint16_t soh_pull_be_16(uint8_t const *p) {
92  uint16_t r;
93 
94  r = *p++ << 8;
95  r += *p++;
96 
97  return r;
98 }
99 
100 /** Read big-endian 3-byte unsigned from p
101  *
102  * caller must ensure enough data exists at "p"
103  */
104 uint32_t soh_pull_be_24(uint8_t const *p) {
105  uint32_t r;
106 
107  r = *p++ << 16;
108  r += *p++ << 8;
109  r += *p++;
110 
111  return r;
112 }
113 
114 /** Read big-endian 4-byte unsigned from p
115  *
116  * caller must ensure enough data exists at "p"
117  */
118 uint32_t soh_pull_be_32(uint8_t const *p) {
119  uint32_t r;
120 
121  r = *p++ << 24;
122  r += *p++ << 16;
123  r += *p++ << 8;
124  r += *p++;
125 
126  return r;
127 }
128 
129 static int eap_peap_soh_mstlv(REQUEST *request, uint8_t const *p, unsigned int data_len) CC_HINT(nonnull);
130 
131 /** Parses the MS-SOH type/value (note: NOT type/length/value) data and update the sohvp list
132  *
133  * See section 2.2.4 of MS-SOH. Because there's no "length" field we CANNOT just skip
134  * unknown types; we need to know their length ahead of time. Therefore, we abort
135  * if we find an unknown type. Note that sohvp may still have been modified in the
136  * failure case.
137  *
138  * @param request Current request
139  * @param p binary blob
140  * @param data_len length of blob
141  * @return
142  * - 0 on success.
143  * - -1 on failure.
144  */
145 static int eap_peap_soh_mstlv(REQUEST *request, uint8_t const *p, unsigned int data_len)
146 {
147  VALUE_PAIR *vp;
148  uint8_t c;
149  int t;
150 
151  while (data_len > 0) {
152  c = *p++;
153  data_len--;
154 
155  switch (c) {
156  case 1:
157  /* MS-Machine-Inventory-Packet
158  * MS-SOH section 2.2.4.1
159  */
160  if (data_len < 18) {
161  RDEBUG("insufficient data for MS-Machine-Inventory-Packet");
162  return 0;
163  }
164  data_len -= 18;
165 
166  vp = pair_make_request("SoH-MS-Machine-OS-vendor", "Microsoft", T_OP_EQ);
167  if (!vp) return 0;
168 
169  vp = pair_make_request("SoH-MS-Machine-OS-version", NULL, T_OP_EQ);
170  if (!vp) return 0;
171 
172  vp->vp_integer = soh_pull_be_32(p); p+=4;
173 
174  vp = pair_make_request("SoH-MS-Machine-OS-release", NULL, T_OP_EQ);
175  if (!vp) return 0;
176 
177  vp->vp_integer = soh_pull_be_32(p); p+=4;
178 
179  vp = pair_make_request("SoH-MS-Machine-OS-build", NULL, T_OP_EQ);
180  if (!vp) return 0;
181 
182  vp->vp_integer = soh_pull_be_32(p); p+=4;
183 
184  vp = pair_make_request("SoH-MS-Machine-SP-version", NULL, T_OP_EQ);
185  if (!vp) return 0;
186 
187  vp->vp_integer = soh_pull_be_16(p); p+=2;
188 
189  vp = pair_make_request("SoH-MS-Machine-SP-release", NULL, T_OP_EQ);
190  if (!vp) return 0;
191 
192  vp->vp_integer = soh_pull_be_16(p); p+=2;
193 
194  vp = pair_make_request("SoH-MS-Machine-Processor", NULL, T_OP_EQ);
195  if (!vp) return 0;
196 
197  vp->vp_integer = soh_pull_be_16(p); p+=2;
198  break;
199 
200  case 2:
201  /* MS-Quarantine-State - FIXME: currently unhandled
202  * MS-SOH 2.2.4.1
203  *
204  * 1 byte reserved
205  * 1 byte flags
206  * 8 bytes NT Time field (100-nanosec since 1 Jan 1601)
207  * 2 byte urilen
208  * N bytes uri
209  */
210  p += 10;
211  t = soh_pull_be_16(p); /* t == uri len */
212  p += 2;
213  p += t;
214  data_len -= 12 + t;
215  break;
216 
217  case 3:
218  /* MS-Packet-Info
219  * MS-SOH 2.2.4.3
220  */
221  RDEBUG3("SoH MS-Packet-Info %s vers=%i", *p & 0x10 ? "request" : "response", *p & 0xf);
222  p++;
223  data_len--;
224  break;
225 
226  case 4:
227  /* MS-SystemGenerated-Ids - FIXME: currently unhandled
228  * MS-SOH 2.2.4.4
229  *
230  * 2 byte length
231  * N bytes (3 bytes IANA enterprise# + 1 byte component id#)
232  */
233  t = soh_pull_be_16(p);
234  p += 2;
235  p += t;
236  data_len -= 2 + t;
237  break;
238 
239  case 5:
240  /* MS-MachineName
241  * MS-SOH 2.2.4.5
242  *
243  * 1 byte namelen
244  * N bytes name
245  */
246  t = soh_pull_be_16(p);
247  p += 2;
248 
249  vp = pair_make_request("SoH-MS-Machine-Name", NULL, T_OP_EQ);
250  if (!vp) return 0;
251 
252  fr_pair_value_bstrncpy(vp, p, t);
253 
254  p += t;
255  data_len -= 2 + t;
256  break;
257 
258  case 6:
259  /* MS-CorrelationId
260  * MS-SOH 2.2.4.6
261  *
262  * 24 bytes opaque binary which we might, in future, have
263  * to echo back to the client in a final SoHR
264  */
265  vp = pair_make_request("SoH-MS-Correlation-Id", NULL, T_OP_EQ);
266  if (!vp) return 0;
267 
268  fr_pair_value_memcpy(vp, p, 24);
269  p += 24;
270  data_len -= 24;
271  break;
272 
273  case 7:
274  /* MS-Installed-Shvs - FIXME: currently unhandled
275  * MS-SOH 2.2.4.7
276  *
277  * 2 bytes length
278  * N bytes (3 bytes IANA enterprise# + 1 byte component id#)
279  */
280  t = soh_pull_be_16(p);
281  p += 2;
282  p += t;
283  data_len -= 2 + t;
284  break;
285 
286  case 8:
287  /* MS-Machine-Inventory-Ex
288  * MS-SOH 2.2.4.8
289  *
290  * 4 bytes reserved
291  * 1 byte product type (client=1 domain_controller=2 server=3)
292  */
293  p += 4;
294  vp = pair_make_request("SoH-MS-Machine-Role", NULL, T_OP_EQ);
295  if (!vp) return 0;
296 
297  vp->vp_integer = *p;
298  p++;
299  data_len -= 5;
300  break;
301 
302  default:
303  RDEBUG("SoH Unknown MS TV %i stopping", c);
304  return 0;
305  }
306  }
307  return 1;
308 }
309 /** Convert windows Health Class status into human-readable string
310  *
311  * Tedious, really, really tedious...
312  */
313 static char const* clientstatus2str(uint32_t hcstatus) {
314  switch (hcstatus) {
315  /* this lot should all just be for windows updates */
316  case 0xff0005:
317  return "wua-ok";
318 
319  case 0xff0006:
320  return "wua-missing";
321 
322  case 0xff0008:
323  return "wua-not-started";
324 
325  case 0xc0ff000c:
326  return "wua-no-wsus-server";
327 
328  case 0xc0ff000d:
329  return "wua-no-wsus-clientid";
330 
331  case 0xc0ff000e:
332  return "wua-disabled";
333 
334  case 0xc0ff000f:
335  return "wua-comm-failure";
336 
337  /* these next 3 are for all health-classes */
338  case 0xc0ff0002:
339  return "not-installed";
340 
341  case 0xc0ff0003:
342  return "down";
343 
344  case 0xc0ff0018:
345  return "not-started";
346  }
347  return NULL;
348 }
349 
350 /** Convert a Health Class into a string
351  *
352  */
353 static char const* healthclass2str(uint8_t hc) {
354  switch (hc) {
355  case 0:
356  return "firewall";
357 
358  case 1:
359  return "antivirus";
360 
361  case 2:
362  return "antispyware";
363 
364  case 3:
365  return "updates";
366 
367  case 4:
368  return "security-updates";
369  }
370  return NULL;
371 }
372 
373 /** Parse the MS-SOH response in data and update sohvp
374  *
375  * Note that sohvp might still have been updated in event of a failure.
376  *
377  * @param request Current request
378  * @param data MS-SOH blob
379  * @param data_len length of MS-SOH blob
380  *
381  * @return
382  * - 0 on success.
383  * - -1 on failure.
384  */
385 int soh_verify(REQUEST *request, uint8_t const *data, unsigned int data_len) {
386 
387  VALUE_PAIR *vp;
388  eap_soh hdr;
389  soh_response resp;
390  soh_mode_subheader mode;
391  soh_tlv tlv;
392  int curr_shid=-1, curr_shid_c=-1, curr_hc=-1;
393 
394  rad_assert(request->packet != NULL);
395 
396  hdr.tlv_type = soh_pull_be_16(data); data += 2;
397  hdr.tlv_len = soh_pull_be_16(data); data += 2;
398  hdr.tlv_vendor = soh_pull_be_32(data); data += 4;
399 
400  if (hdr.tlv_type != 7 || hdr.tlv_vendor != 0x137) {
401  RDEBUG("SoH payload is %i %08x not a ms-vendor packet", hdr.tlv_type, hdr.tlv_vendor);
402  return -1;
403  }
404 
405  hdr.soh_type = soh_pull_be_16(data); data += 2;
406  hdr.soh_len = soh_pull_be_16(data); data += 2;
407  if (hdr.soh_type != 1) {
408  RDEBUG("SoH tlv %04x is not a response", hdr.soh_type);
409  return -1;
410  }
411 
412  /* FIXME: check for sufficient data */
413  resp.outer_type = soh_pull_be_16(data); data += 2;
414  resp.outer_len = soh_pull_be_16(data); data += 2;
415  resp.vendor = soh_pull_be_32(data); data += 4;
416  resp.inner_type = soh_pull_be_16(data); data += 2;
417  resp.inner_len = soh_pull_be_16(data); data += 2;
418 
419 
420  if (resp.outer_type!=7 || resp.vendor != 0x137) {
421  RDEBUG("SoH response outer type %i/vendor %08x not recognised", resp.outer_type, resp.vendor);
422  return -1;
423  }
424  switch (resp.inner_type) {
425  case 1:
426  /* no mode sub-header */
427  RDEBUG("SoH without mode subheader");
428  break;
429 
430  case 2:
431  mode.outer_type = soh_pull_be_16(data); data += 2;
432  mode.outer_len = soh_pull_be_16(data); data += 2;
433  mode.vendor = soh_pull_be_32(data); data += 4;
434  memcpy(mode.corrid, data, 24); data += 24;
435  mode.intent = data[0];
436  mode.content_type = data[1];
437  data += 2;
438 
439  if (mode.outer_type != 7 || mode.vendor != 0x137 || mode.content_type != 0) {
440  RDEBUG3("SoH mode subheader outer type %i/vendor %08x/content type %i invalid", mode.outer_type, mode.vendor, mode.content_type);
441  return -1;
442  }
443  RDEBUG3("SoH with mode subheader");
444  break;
445 
446  default:
447  RDEBUG("SoH invalid inner type %i", resp.inner_type);
448  return -1;
449  }
450 
451  /* subtract off the relevant amount of data */
452  if (resp.inner_type==2) {
453  data_len = resp.inner_len - 34;
454  } else {
455  data_len = resp.inner_len;
456  }
457 
458  /* TLV
459  * MS-SOH 2.2.1
460  * See also 2.2.3
461  *
462  * 1 bit mandatory
463  * 1 bit reserved
464  * 14 bits tlv type
465  * 2 bytes tlv length
466  * N bytes payload
467  *
468  */
469  while (data_len >= 4) {
470  tlv.tlv_type = soh_pull_be_16(data); data += 2;
471  tlv.tlv_len = soh_pull_be_16(data); data += 2;
472 
473  data_len -= 4;
474 
475  switch (tlv.tlv_type) {
476  case 2:
477  /* System-Health-Id TLV
478  * MS-SOH 2.2.3.1
479  *
480  * 3 bytes IANA/SMI vendor code
481  * 1 byte component (i.e. within vendor, which SoH component
482  */
483  curr_shid = soh_pull_be_24(data);
484  curr_shid_c = data[3];
485  RDEBUG2("SoH System-Health-ID vendor %08x component=%i", curr_shid, curr_shid_c);
486  break;
487 
488  case 7:
489  /* Vendor-Specific packet
490  * MS-SOH 2.2.3.3
491  *
492  * 4 bytes vendor, supposedly ignored by NAP
493  * N bytes payload; for Microsoft component#0 this is the MS TV stuff
494  */
495  if (curr_shid==0x137 && curr_shid_c==0) {
496  RDEBUG2("SoH MS type-value payload");
497  eap_peap_soh_mstlv(request, data + 4, tlv.tlv_len - 4);
498  } else {
499  RDEBUG2("SoH unhandled vendor-specific TLV %08x/component=%i %i bytes payload",
500  curr_shid, curr_shid_c, tlv.tlv_len);
501  }
502  break;
503 
504  case 8:
505  /* Health-Class
506  * MS-SOH 2.2.3.5.6
507  *
508  * 1 byte integer
509  */
510  RDEBUG2("SoH Health-Class %i", data[0]);
511  curr_hc = data[0];
512  break;
513 
514  case 9:
515  /* Software-Version
516  * MS-SOH 2.2.3.5.7
517  *
518  * 1 byte integer
519  */
520  RDEBUG2("SoH Software-Version %i", data[0]);
521  break;
522 
523  case 11:
524  /* Health-Class status
525  * MS-SOH 2.2.3.5.9
526  *
527  * variable data; for the MS System Health vendor, these are 4-byte
528  * integers which are a really, really dumb format:
529  *
530  * 28 bits ignore
531  * 1 bit - 1==product snoozed
532  * 1 bit - 1==microsoft product
533  * 1 bit - 1==product up-to-date
534  * 1 bit - 1==product enabled
535  */
536  RDEBUG2("SoH Health-Class-Status - current shid=%08x component=%i", curr_shid, curr_shid_c);
537 
538  if (curr_shid == 0x137 && curr_shid_c == 128) {
539  char const *s, *t;
540  uint32_t hcstatus = soh_pull_be_32(data);
541 
542  RDEBUG2("SoH Health-Class-Status microsoft DWORD=%08x", hcstatus);
543 
544  vp = pair_make_request("SoH-MS-Windows-Health-Status", NULL, T_OP_EQ);
545  if (!vp) return 0;
546 
547  switch (curr_hc) {
548  case 4:
549  /* security updates */
550  s = "security-updates";
551  switch (hcstatus) {
552  case 0xff0005:
553  fr_pair_value_snprintf(vp, "%s ok all-installed", s);
554  break;
555 
556  case 0xff0006:
557  fr_pair_value_snprintf(vp, "%s warn some-missing", s);
558  break;
559 
560  case 0xff0008:
561  fr_pair_value_snprintf(vp, "%s warn never-started", s);
562  break;
563 
564  case 0xc0ff000c:
565  fr_pair_value_snprintf(vp, "%s error no-wsus-srv", s);
566  break;
567 
568  case 0xc0ff000d:
569  fr_pair_value_snprintf(vp, "%s error no-wsus-clid", s);
570  break;
571 
572  case 0xc0ff000e:
573  fr_pair_value_snprintf(vp, "%s warn wsus-disabled", s);
574  break;
575 
576  case 0xc0ff000f:
577  fr_pair_value_snprintf(vp, "%s error comm-failure", s);
578  break;
579 
580  case 0xc0ff0010:
581  fr_pair_value_snprintf(vp, "%s warn needs-reboot", s);
582  break;
583 
584  default:
585  fr_pair_value_snprintf(vp, "%s error %08x", s, hcstatus);
586  break;
587  }
588  break;
589 
590  case 3:
591  /* auto updates */
592  s = "auto-updates";
593  switch (hcstatus) {
594  case 1:
595  fr_pair_value_snprintf(vp, "%s warn disabled", s);
596  break;
597 
598  case 2:
599  fr_pair_value_snprintf(vp, "%s ok action=check-only", s);
600  break;
601 
602  case 3:
603  fr_pair_value_snprintf(vp, "%s ok action=download", s);
604  break;
605 
606  case 4:
607  fr_pair_value_snprintf(vp, "%s ok action=install", s);
608  break;
609 
610  case 5:
611  fr_pair_value_snprintf(vp, "%s warn unconfigured", s);
612  break;
613 
614  case 0xc0ff0003:
615  fr_pair_value_snprintf(vp, "%s warn service-down", s);
616  break;
617 
618  case 0xc0ff0018:
619  fr_pair_value_snprintf(vp, "%s warn never-started", s);
620  break;
621 
622  default:
623  fr_pair_value_snprintf(vp, "%s error %08x", s, hcstatus);
624  break;
625  }
626  break;
627 
628  default:
629  /* other - firewall, antivirus, antispyware */
630  s = healthclass2str(curr_hc);
631  if (s) {
632  /* bah. this is vile. stupid microsoft
633  */
634  if (hcstatus & 0xff000000) {
635  /* top octet non-zero means an error
636  * FIXME: is this always correct? MS-WSH 2.2.8 is unclear
637  */
638  t = clientstatus2str(hcstatus);
639  if (t) {
640  fr_pair_value_snprintf(vp, "%s error %s", s, t);
641  } else {
642  fr_pair_value_snprintf(vp, "%s error %08x", s, hcstatus);
643  }
644  } else {
646  "%s ok snoozed=%i microsoft=%i up2date=%i enabled=%i",
647  s,
648  hcstatus & 0x8 ? 1 : 0,
649  hcstatus & 0x4 ? 1 : 0,
650  hcstatus & 0x2 ? 1 : 0,
651  hcstatus & 0x1 ? 1 : 0
652  );
653  }
654  } else {
655  fr_pair_value_snprintf(vp, "%i unknown %08x", curr_hc, hcstatus);
656  }
657  break;
658  }
659  } else {
660  vp = pair_make_request("SoH-MS-Health-Other", NULL, T_OP_EQ);
661  if (!vp) return 0;
662 
663  /* FIXME: what to do with the payload? */
664  fr_pair_value_snprintf(vp, "%08x/%i ?", curr_shid, curr_shid_c);
665  }
666  break;
667 
668  default:
669  RDEBUG("SoH Unknown TLV %i len=%i", tlv.tlv_type, tlv.tlv_len);
670  break;
671  }
672 
673  data += tlv.tlv_len;
674  data_len -= tlv.tlv_len;
675  }
676 
677  return 0;
678 }
uint16_t tlv_len
Definition: soh.c:42
uint16_t outer_len
Definition: soh.c:72
static char const * healthclass2str(uint8_t hc)
Convert a Health Class into a string.
Definition: soh.c:353
#define CC_HINT(_x)
Definition: build.h:71
SOH mode subheader Typical microsoft binary blob nonsense.
Definition: soh.c:70
uint16_t outer_type
Definition: soh.c:71
uint16_t tlv_type
Definition: soh.c:83
#define pair_make_request(_a, _b, _c)
Definition: radiusd.h:545
Definition: token.h:46
uint32_t tlv_vendor
Definition: soh.c:43
uint16_t soh_len
Definition: soh.c:49
int soh_verify(REQUEST *request, uint8_t const *data, unsigned int data_len)
Parse the MS-SOH response in data and update sohvp.
Definition: soh.c:385
uint16_t soh_pull_be_16(uint8_t const *p)
Read big-endian 2-byte unsigned from p.
Definition: soh.c:91
uint16_t tlv_len
Definition: soh.c:84
SOH response payload Send by client to server.
Definition: soh.c:58
#define rad_assert(expr)
Definition: rad_assert.h:38
uint8_t intent
Definition: soh.c:75
uint32_t soh_pull_be_24(uint8_t const *p)
Read big-endian 3-byte unsigned from p.
Definition: soh.c:104
uint16_t outer_type
Definition: soh.c:59
static char const * clientstatus2str(uint32_t hcstatus)
Convert windows Health Class status into human-readable string.
Definition: soh.c:313
Stores an attribute, a value and various bits of other data.
Definition: pair.h:112
uint16_t outer_len
Definition: soh.c:60
uint16_t tlv_type
==7 for EAP-SOH
Definition: soh.c:41
uint16_t soh_type
==2 for request, 1 for response
Definition: soh.c:48
#define RDEBUG2(fmt,...)
Definition: log.h:244
uint8_t data[]
Definition: eap_pwd.h:625
RADIUS_PACKET * packet
Incoming request.
Definition: radiusd.h:221
uint8_t corrid[24]
Definition: soh.c:74
uint32_t vendor
Definition: soh.c:61
uint32_t soh_pull_be_32(uint8_t const *p)
Read big-endian 4-byte unsigned from p.
Definition: soh.c:118
EAP-SOH packet.
Definition: soh.c:40
void fr_pair_value_bstrncpy(VALUE_PAIR *vp, void const *src, size_t len)
Copy data into an "string" data type.
Definition: pair.c:2043
uint8_t content_type
Definition: soh.c:76
uint32_t vendor
Definition: soh.c:73
static int eap_peap_soh_mstlv(REQUEST *request, uint8_t const *p, unsigned int data_len) CC_HINT(nonnull)
Parses the MS-SOH type/value (note: NOT type/length/value) data and update the sohvp list...
Definition: soh.c:145
SOH type-length-value header.
Definition: soh.c:82
#define RCSID(id)
Definition: build.h:135
static int r
Definition: rbmonkey.c:66
uint16_t inner_len
Definition: soh.c:63
uint16_t inner_type
Definition: soh.c:62
void fr_pair_value_snprintf(VALUE_PAIR *vp, char const *fmt,...) CC_HINT(format(printf
#define RDEBUG(fmt,...)
Definition: log.h:243
void fr_pair_value_memcpy(VALUE_PAIR *vp, uint8_t const *src, size_t len)
Copy data into an "octets" data type.
Definition: pair.c:1905
#define RDEBUG3(fmt,...)
Definition: log.h:245