1/*
2 * Copyright (C) 2000 Alfredo Andres Omella.  All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 *
8 *   1. Redistributions of source code must retain the above copyright
9 *      notice, this list of conditions and the following disclaimer.
10 *   2. Redistributions in binary form must reproduce the above copyright
11 *      notice, this list of conditions and the following disclaimer in
12 *      the documentation and/or other materials provided with the
13 *      distribution.
14 *   3. The names of the authors may not be used to endorse or promote
15 *      products derived from this software without specific prior
16 *      written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
19 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
20 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
21 */
22
23/* \summary: Radius protocol printer */
24
25/*
26 * Radius printer routines as specified on:
27 *
28 * RFC 2865:
29 *      "Remote Authentication Dial In User Service (RADIUS)"
30 *
31 * RFC 2866:
32 *      "RADIUS Accounting"
33 *
34 * RFC 2867:
35 *      "RADIUS Accounting Modifications for Tunnel Protocol Support"
36 *
37 * RFC 2868:
38 *      "RADIUS Attributes for Tunnel Protocol Support"
39 *
40 * RFC 2869:
41 *      "RADIUS Extensions"
42 *
43 * RFC 3580:
44 *      "IEEE 802.1X Remote Authentication Dial In User Service (RADIUS)"
45 *      "Usage Guidelines"
46 *
47 * RFC 4675:
48 *      "RADIUS Attributes for Virtual LAN and Priority Support"
49 *
50 * RFC 5176:
51 *      "Dynamic Authorization Extensions to RADIUS"
52 *
53 * Alfredo Andres Omella (aandres@s21sec.com) v0.1 2000/09/15
54 *
55 * TODO: Among other things to print ok MacIntosh and Vendor values
56 */
57
58#ifdef HAVE_CONFIG_H
59#include "config.h"
60#endif
61
62#include <netdissect-stdinc.h>
63
64#include <string.h>
65
66#include "netdissect.h"
67#include "addrtoname.h"
68#include "extract.h"
69#include "oui.h"
70
71static const char tstr[] = " [|radius]";
72
73#define TAM_SIZE(x) (sizeof(x)/sizeof(x[0]) )
74
75#define PRINT_HEX(bytes_len, ptr_data)                               \
76           while(bytes_len)                                          \
77           {                                                         \
78              ND_PRINT((ndo, "%02X", *ptr_data ));                   \
79              ptr_data++;                                            \
80              bytes_len--;                                           \
81           }
82
83
84/* Radius packet codes */
85#define RADCMD_ACCESS_REQ   1 /* Access-Request      */
86#define RADCMD_ACCESS_ACC   2 /* Access-Accept       */
87#define RADCMD_ACCESS_REJ   3 /* Access-Reject       */
88#define RADCMD_ACCOUN_REQ   4 /* Accounting-Request  */
89#define RADCMD_ACCOUN_RES   5 /* Accounting-Response */
90#define RADCMD_ACCESS_CHA  11 /* Access-Challenge    */
91#define RADCMD_STATUS_SER  12 /* Status-Server       */
92#define RADCMD_STATUS_CLI  13 /* Status-Client       */
93#define RADCMD_DISCON_REQ  40 /* Disconnect-Request  */
94#define RADCMD_DISCON_ACK  41 /* Disconnect-ACK      */
95#define RADCMD_DISCON_NAK  42 /* Disconnect-NAK      */
96#define RADCMD_COA_REQ     43 /* CoA-Request         */
97#define RADCMD_COA_ACK     44 /* CoA-ACK             */
98#define RADCMD_COA_NAK     45 /* CoA-NAK             */
99#define RADCMD_RESERVED   255 /* Reserved            */
100
101static const struct tok radius_command_values[] = {
102    { RADCMD_ACCESS_REQ, "Access-Request" },
103    { RADCMD_ACCESS_ACC, "Access-Accept" },
104    { RADCMD_ACCESS_REJ, "Access-Reject" },
105    { RADCMD_ACCOUN_REQ, "Accounting-Request" },
106    { RADCMD_ACCOUN_RES, "Accounting-Response" },
107    { RADCMD_ACCESS_CHA, "Access-Challenge" },
108    { RADCMD_STATUS_SER, "Status-Server" },
109    { RADCMD_STATUS_CLI, "Status-Client" },
110    { RADCMD_DISCON_REQ, "Disconnect-Request" },
111    { RADCMD_DISCON_ACK, "Disconnect-ACK" },
112    { RADCMD_DISCON_NAK, "Disconnect-NAK" },
113    { RADCMD_COA_REQ,    "CoA-Request" },
114    { RADCMD_COA_ACK,    "CoA-ACK" },
115    { RADCMD_COA_NAK,    "CoA-NAK" },
116    { RADCMD_RESERVED,   "Reserved" },
117    { 0, NULL}
118};
119
120/********************************/
121/* Begin Radius Attribute types */
122/********************************/
123#define SERV_TYPE    6
124#define FRM_IPADDR   8
125#define LOG_IPHOST  14
126#define LOG_SERVICE 15
127#define FRM_IPX     23
128#define SESSION_TIMEOUT   27
129#define IDLE_TIMEOUT      28
130#define FRM_ATALK_LINK    37
131#define FRM_ATALK_NETWORK 38
132
133#define ACCT_DELAY        41
134#define ACCT_SESSION_TIME 46
135
136#define EGRESS_VLAN_ID   56
137#define EGRESS_VLAN_NAME 58
138
139#define TUNNEL_TYPE        64
140#define TUNNEL_MEDIUM      65
141#define TUNNEL_CLIENT_END  66
142#define TUNNEL_SERVER_END  67
143#define TUNNEL_PASS        69
144
145#define ARAP_PASS          70
146#define ARAP_FEATURES      71
147
148#define TUNNEL_PRIV_GROUP  81
149#define TUNNEL_ASSIGN_ID   82
150#define TUNNEL_PREFERENCE  83
151
152#define ARAP_CHALLENGE_RESP 84
153#define ACCT_INT_INTERVAL   85
154
155#define TUNNEL_CLIENT_AUTH 90
156#define TUNNEL_SERVER_AUTH 91
157/********************************/
158/* End Radius Attribute types */
159/********************************/
160
161#define RFC4675_TAGGED   0x31
162#define RFC4675_UNTAGGED 0x32
163
164static const struct tok rfc4675_tagged[] = {
165    { RFC4675_TAGGED,   "Tagged" },
166    { RFC4675_UNTAGGED, "Untagged" },
167    { 0, NULL}
168};
169
170
171static void print_attr_string(netdissect_options *, register const u_char *, u_int, u_short );
172static void print_attr_num(netdissect_options *, register const u_char *, u_int, u_short );
173static void print_vendor_attr(netdissect_options *, register const u_char *, u_int, u_short );
174static void print_attr_address(netdissect_options *, register const u_char *, u_int, u_short);
175static void print_attr_time(netdissect_options *, register const u_char *, u_int, u_short);
176static void print_attr_strange(netdissect_options *, register const u_char *, u_int, u_short);
177
178
179struct radius_hdr { uint8_t  code; /* Radius packet code  */
180                    uint8_t  id;   /* Radius packet id    */
181                    uint16_t len;  /* Radius total length */
182                    uint8_t  auth[16]; /* Authenticator   */
183                  };
184
185#define MIN_RADIUS_LEN	20
186
187struct radius_attr { uint8_t type; /* Attribute type   */
188                     uint8_t len;  /* Attribute length */
189                   };
190
191
192/* Service-Type Attribute standard values */
193static const char *serv_type[]={ NULL,
194                                "Login",
195                                "Framed",
196                                "Callback Login",
197                                "Callback Framed",
198                                "Outbound",
199                                "Administrative",
200                                "NAS Prompt",
201                                "Authenticate Only",
202                                "Callback NAS Prompt",
203                                "Call Check",
204                                "Callback Administrative",
205                               };
206
207/* Framed-Protocol Attribute standard values */
208static const char *frm_proto[]={ NULL,
209                                 "PPP",
210                                 "SLIP",
211                                 "ARAP",
212                                 "Gandalf proprietary",
213                                 "Xylogics IPX/SLIP",
214                                 "X.75 Synchronous",
215                               };
216
217/* Framed-Routing Attribute standard values */
218static const char *frm_routing[]={ "None",
219                                   "Send",
220                                   "Listen",
221                                   "Send&Listen",
222                                 };
223
224/* Framed-Compression Attribute standard values */
225static const char *frm_comp[]={ "None",
226                                "VJ TCP/IP",
227                                "IPX",
228                                "Stac-LZS",
229                              };
230
231/* Login-Service Attribute standard values */
232static const char *login_serv[]={ "Telnet",
233                                  "Rlogin",
234                                  "TCP Clear",
235                                  "PortMaster(proprietary)",
236                                  "LAT",
237                                  "X.25-PAD",
238                                  "X.25-T3POS",
239                                  "Unassigned",
240                                  "TCP Clear Quiet",
241                                };
242
243
244/* Termination-Action Attribute standard values */
245static const char *term_action[]={ "Default",
246                                   "RADIUS-Request",
247                                 };
248
249/* Ingress-Filters Attribute standard values */
250static const char *ingress_filters[]={ NULL,
251                                       "Enabled",
252                                       "Disabled",
253                                     };
254
255/* NAS-Port-Type Attribute standard values */
256static const char *nas_port_type[]={ "Async",
257                                     "Sync",
258                                     "ISDN Sync",
259                                     "ISDN Async V.120",
260                                     "ISDN Async V.110",
261                                     "Virtual",
262                                     "PIAFS",
263                                     "HDLC Clear Channel",
264                                     "X.25",
265                                     "X.75",
266                                     "G.3 Fax",
267                                     "SDSL",
268                                     "ADSL-CAP",
269                                     "ADSL-DMT",
270                                     "ISDN-DSL",
271                                     "Ethernet",
272                                     "xDSL",
273                                     "Cable",
274                                     "Wireless - Other",
275                                     "Wireless - IEEE 802.11",
276                                   };
277
278/* Acct-Status-Type Accounting Attribute standard values */
279static const char *acct_status[]={ NULL,
280                                   "Start",
281                                   "Stop",
282                                   "Interim-Update",
283                                   "Unassigned",
284                                   "Unassigned",
285                                   "Unassigned",
286                                   "Accounting-On",
287                                   "Accounting-Off",
288                                   "Tunnel-Start",
289                                   "Tunnel-Stop",
290                                   "Tunnel-Reject",
291                                   "Tunnel-Link-Start",
292                                   "Tunnel-Link-Stop",
293                                   "Tunnel-Link-Reject",
294                                   "Failed",
295                                 };
296
297/* Acct-Authentic Accounting Attribute standard values */
298static const char *acct_auth[]={ NULL,
299                                 "RADIUS",
300                                 "Local",
301                                 "Remote",
302                               };
303
304/* Acct-Terminate-Cause Accounting Attribute standard values */
305static const char *acct_term[]={ NULL,
306                                 "User Request",
307                                 "Lost Carrier",
308                                 "Lost Service",
309                                 "Idle Timeout",
310                                 "Session Timeout",
311                                 "Admin Reset",
312                                 "Admin Reboot",
313                                 "Port Error",
314                                 "NAS Error",
315                                 "NAS Request",
316                                 "NAS Reboot",
317                                 "Port Unneeded",
318                                 "Port Preempted",
319                                 "Port Suspended",
320                                 "Service Unavailable",
321                                 "Callback",
322                                 "User Error",
323                                 "Host Request",
324                               };
325
326/* Tunnel-Type Attribute standard values */
327static const char *tunnel_type[]={ NULL,
328                                   "PPTP",
329                                   "L2F",
330                                   "L2TP",
331                                   "ATMP",
332                                   "VTP",
333                                   "AH",
334                                   "IP-IP",
335                                   "MIN-IP-IP",
336                                   "ESP",
337                                   "GRE",
338                                   "DVS",
339                                   "IP-in-IP Tunneling",
340                                   "VLAN",
341                                 };
342
343/* Tunnel-Medium-Type Attribute standard values */
344static const char *tunnel_medium[]={ NULL,
345                                     "IPv4",
346                                     "IPv6",
347                                     "NSAP",
348                                     "HDLC",
349                                     "BBN 1822",
350                                     "802",
351                                     "E.163",
352                                     "E.164",
353                                     "F.69",
354                                     "X.121",
355                                     "IPX",
356                                     "Appletalk",
357                                     "Decnet IV",
358                                     "Banyan Vines",
359                                     "E.164 with NSAP subaddress",
360                                   };
361
362/* ARAP-Zone-Access Attribute standard values */
363static const char *arap_zone[]={ NULL,
364                                 "Only access to dfl zone",
365                                 "Use zone filter inc.",
366                                 "Not used",
367                                 "Use zone filter exc.",
368                               };
369
370static const char *prompt[]={ "No Echo",
371                              "Echo",
372                            };
373
374
375static struct attrtype {
376                  const char *name;      /* Attribute name                 */
377                  const char **subtypes; /* Standard Values (if any)       */
378                  u_char siz_subtypes;   /* Size of total standard values  */
379                  u_char first_subtype;  /* First standard value is 0 or 1 */
380                  void (*print_func)(netdissect_options *, register const u_char *, u_int, u_short);
381                } attr_type[]=
382  {
383     { NULL,                              NULL, 0, 0, NULL               },
384     { "User-Name",                       NULL, 0, 0, print_attr_string  },
385     { "User-Password",                   NULL, 0, 0, NULL               },
386     { "CHAP-Password",                   NULL, 0, 0, NULL               },
387     { "NAS-IP-Address",                  NULL, 0, 0, print_attr_address },
388     { "NAS-Port",                        NULL, 0, 0, print_attr_num     },
389     { "Service-Type",                    serv_type, TAM_SIZE(serv_type)-1, 1, print_attr_num },
390     { "Framed-Protocol",                 frm_proto, TAM_SIZE(frm_proto)-1, 1, print_attr_num },
391     { "Framed-IP-Address",               NULL, 0, 0, print_attr_address },
392     { "Framed-IP-Netmask",               NULL, 0, 0, print_attr_address },
393     { "Framed-Routing",                  frm_routing, TAM_SIZE(frm_routing), 0, print_attr_num },
394     { "Filter-Id",                       NULL, 0, 0, print_attr_string  },
395     { "Framed-MTU",                      NULL, 0, 0, print_attr_num     },
396     { "Framed-Compression",              frm_comp, TAM_SIZE(frm_comp),   0, print_attr_num },
397     { "Login-IP-Host",                   NULL, 0, 0, print_attr_address },
398     { "Login-Service",                   login_serv, TAM_SIZE(login_serv), 0, print_attr_num },
399     { "Login-TCP-Port",                  NULL, 0, 0, print_attr_num     },
400     { "Unassigned",                      NULL, 0, 0, NULL }, /*17*/
401     { "Reply-Message",                   NULL, 0, 0, print_attr_string },
402     { "Callback-Number",                 NULL, 0, 0, print_attr_string },
403     { "Callback-Id",                     NULL, 0, 0, print_attr_string },
404     { "Unassigned",                      NULL, 0, 0, NULL }, /*21*/
405     { "Framed-Route",                    NULL, 0, 0, print_attr_string },
406     { "Framed-IPX-Network",              NULL, 0, 0, print_attr_num    },
407     { "State",                           NULL, 0, 0, print_attr_string },
408     { "Class",                           NULL, 0, 0, print_attr_string },
409     { "Vendor-Specific",                 NULL, 0, 0, print_vendor_attr },
410     { "Session-Timeout",                 NULL, 0, 0, print_attr_num    },
411     { "Idle-Timeout",                    NULL, 0, 0, print_attr_num    },
412     { "Termination-Action",              term_action, TAM_SIZE(term_action), 0, print_attr_num },
413     { "Called-Station-Id",               NULL, 0, 0, print_attr_string },
414     { "Calling-Station-Id",              NULL, 0, 0, print_attr_string },
415     { "NAS-Identifier",                  NULL, 0, 0, print_attr_string },
416     { "Proxy-State",                     NULL, 0, 0, print_attr_string },
417     { "Login-LAT-Service",               NULL, 0, 0, print_attr_string },
418     { "Login-LAT-Node",                  NULL, 0, 0, print_attr_string },
419     { "Login-LAT-Group",                 NULL, 0, 0, print_attr_string },
420     { "Framed-AppleTalk-Link",           NULL, 0, 0, print_attr_num    },
421     { "Framed-AppleTalk-Network",        NULL, 0, 0, print_attr_num    },
422     { "Framed-AppleTalk-Zone",           NULL, 0, 0, print_attr_string },
423     { "Acct-Status-Type",                acct_status, TAM_SIZE(acct_status)-1, 1, print_attr_num },
424     { "Acct-Delay-Time",                 NULL, 0, 0, print_attr_num    },
425     { "Acct-Input-Octets",               NULL, 0, 0, print_attr_num    },
426     { "Acct-Output-Octets",              NULL, 0, 0, print_attr_num    },
427     { "Acct-Session-Id",                 NULL, 0, 0, print_attr_string },
428     { "Acct-Authentic",                  acct_auth, TAM_SIZE(acct_auth)-1, 1, print_attr_num },
429     { "Acct-Session-Time",               NULL, 0, 0, print_attr_num },
430     { "Acct-Input-Packets",              NULL, 0, 0, print_attr_num },
431     { "Acct-Output-Packets",             NULL, 0, 0, print_attr_num },
432     { "Acct-Terminate-Cause",            acct_term, TAM_SIZE(acct_term)-1, 1, print_attr_num },
433     { "Acct-Multi-Session-Id",           NULL, 0, 0, print_attr_string },
434     { "Acct-Link-Count",                 NULL, 0, 0, print_attr_num },
435     { "Acct-Input-Gigawords",            NULL, 0, 0, print_attr_num },
436     { "Acct-Output-Gigawords",           NULL, 0, 0, print_attr_num },
437     { "Unassigned",                      NULL, 0, 0, NULL }, /*54*/
438     { "Event-Timestamp",                 NULL, 0, 0, print_attr_time },
439     { "Egress-VLANID",                   NULL, 0, 0, print_attr_num },
440     { "Ingress-Filters",                 ingress_filters, TAM_SIZE(ingress_filters)-1, 1, print_attr_num },
441     { "Egress-VLAN-Name",                NULL, 0, 0, print_attr_string },
442     { "User-Priority-Table",             NULL, 0, 0, NULL },
443     { "CHAP-Challenge",                  NULL, 0, 0, print_attr_string },
444     { "NAS-Port-Type",                   nas_port_type, TAM_SIZE(nas_port_type), 0, print_attr_num },
445     { "Port-Limit",                      NULL, 0, 0, print_attr_num },
446     { "Login-LAT-Port",                  NULL, 0, 0, print_attr_string }, /*63*/
447     { "Tunnel-Type",                     tunnel_type, TAM_SIZE(tunnel_type)-1, 1, print_attr_num },
448     { "Tunnel-Medium-Type",              tunnel_medium, TAM_SIZE(tunnel_medium)-1, 1, print_attr_num },
449     { "Tunnel-Client-Endpoint",          NULL, 0, 0, print_attr_string },
450     { "Tunnel-Server-Endpoint",          NULL, 0, 0, print_attr_string },
451     { "Acct-Tunnel-Connection",          NULL, 0, 0, print_attr_string },
452     { "Tunnel-Password",                 NULL, 0, 0, print_attr_string  },
453     { "ARAP-Password",                   NULL, 0, 0, print_attr_strange },
454     { "ARAP-Features",                   NULL, 0, 0, print_attr_strange },
455     { "ARAP-Zone-Access",                arap_zone, TAM_SIZE(arap_zone)-1, 1, print_attr_num }, /*72*/
456     { "ARAP-Security",                   NULL, 0, 0, print_attr_string },
457     { "ARAP-Security-Data",              NULL, 0, 0, print_attr_string },
458     { "Password-Retry",                  NULL, 0, 0, print_attr_num    },
459     { "Prompt",                          prompt, TAM_SIZE(prompt), 0, print_attr_num },
460     { "Connect-Info",                    NULL, 0, 0, print_attr_string   },
461     { "Configuration-Token",             NULL, 0, 0, print_attr_string   },
462     { "EAP-Message",                     NULL, 0, 0, print_attr_string   },
463     { "Message-Authenticator",           NULL, 0, 0, print_attr_string }, /*80*/
464     { "Tunnel-Private-Group-ID",         NULL, 0, 0, print_attr_string },
465     { "Tunnel-Assignment-ID",            NULL, 0, 0, print_attr_string },
466     { "Tunnel-Preference",               NULL, 0, 0, print_attr_num    },
467     { "ARAP-Challenge-Response",         NULL, 0, 0, print_attr_strange },
468     { "Acct-Interim-Interval",           NULL, 0, 0, print_attr_num     },
469     { "Acct-Tunnel-Packets-Lost",        NULL, 0, 0, print_attr_num }, /*86*/
470     { "NAS-Port-Id",                     NULL, 0, 0, print_attr_string },
471     { "Framed-Pool",                     NULL, 0, 0, print_attr_string },
472     { "CUI",                             NULL, 0, 0, print_attr_string },
473     { "Tunnel-Client-Auth-ID",           NULL, 0, 0, print_attr_string },
474     { "Tunnel-Server-Auth-ID",           NULL, 0, 0, print_attr_string },
475     { "Unassigned",                      NULL, 0, 0, NULL }, /*92*/
476     { "Unassigned",                      NULL, 0, 0, NULL }  /*93*/
477  };
478
479
480/*****************************/
481/* Print an attribute string */
482/* value pointed by 'data'   */
483/* and 'length' size.        */
484/*****************************/
485/* Returns nothing.          */
486/*****************************/
487static void
488print_attr_string(netdissect_options *ndo,
489                  register const u_char *data, u_int length, u_short attr_code)
490{
491   register u_int i;
492
493   ND_TCHECK2(data[0],length);
494
495   switch(attr_code)
496   {
497      case TUNNEL_PASS:
498           if (length < 3)
499              goto trunc;
500           if (*data && (*data <=0x1F) )
501              ND_PRINT((ndo, "Tag[%u] ", *data));
502           else
503              ND_PRINT((ndo, "Tag[Unused] "));
504           data++;
505           length--;
506           ND_PRINT((ndo, "Salt %u ", EXTRACT_16BITS(data)));
507           data+=2;
508           length-=2;
509        break;
510      case TUNNEL_CLIENT_END:
511      case TUNNEL_SERVER_END:
512      case TUNNEL_PRIV_GROUP:
513      case TUNNEL_ASSIGN_ID:
514      case TUNNEL_CLIENT_AUTH:
515      case TUNNEL_SERVER_AUTH:
516           if (*data <= 0x1F)
517           {
518              if (length < 1)
519                 goto trunc;
520              if (*data)
521                ND_PRINT((ndo, "Tag[%u] ", *data));
522              else
523                ND_PRINT((ndo, "Tag[Unused] "));
524              data++;
525              length--;
526           }
527        break;
528      case EGRESS_VLAN_NAME:
529           if (length < 1)
530              goto trunc;
531           ND_PRINT((ndo, "%s (0x%02x) ",
532                  tok2str(rfc4675_tagged,"Unknown tag",*data),
533                  *data));
534           data++;
535           length--;
536        break;
537   }
538
539   for (i=0; i < length && *data; i++, data++)
540       ND_PRINT((ndo, "%c", (*data < 32 || *data > 126) ? '.' : *data));
541
542   return;
543
544   trunc:
545      ND_PRINT((ndo, "%s", tstr));
546}
547
548/*
549 * print vendor specific attributes
550 */
551static void
552print_vendor_attr(netdissect_options *ndo,
553                  register const u_char *data, u_int length, u_short attr_code _U_)
554{
555    u_int idx;
556    u_int vendor_id;
557    u_int vendor_type;
558    u_int vendor_length;
559
560    if (length < 4)
561        goto trunc;
562    ND_TCHECK2(*data, 4);
563    vendor_id = EXTRACT_32BITS(data);
564    data+=4;
565    length-=4;
566
567    ND_PRINT((ndo, "Vendor: %s (%u)",
568           tok2str(smi_values,"Unknown",vendor_id),
569           vendor_id));
570
571    while (length >= 2) {
572	ND_TCHECK2(*data, 2);
573
574        vendor_type = *(data);
575        vendor_length = *(data+1);
576
577        if (vendor_length < 2)
578        {
579            ND_PRINT((ndo, "\n\t    Vendor Attribute: %u, Length: %u (bogus, must be >= 2)",
580                   vendor_type,
581                   vendor_length));
582            return;
583        }
584        if (vendor_length > length)
585        {
586            ND_PRINT((ndo, "\n\t    Vendor Attribute: %u, Length: %u (bogus, goes past end of vendor-specific attribute)",
587                   vendor_type,
588                   vendor_length));
589            return;
590        }
591        data+=2;
592        vendor_length-=2;
593        length-=2;
594	ND_TCHECK2(*data, vendor_length);
595
596        ND_PRINT((ndo, "\n\t    Vendor Attribute: %u, Length: %u, Value: ",
597               vendor_type,
598               vendor_length));
599        for (idx = 0; idx < vendor_length ; idx++, data++)
600            ND_PRINT((ndo, "%c", (*data < 32 || *data > 126) ? '.' : *data));
601        length-=vendor_length;
602    }
603    return;
604
605   trunc:
606     ND_PRINT((ndo, "%s", tstr));
607}
608
609/******************************/
610/* Print an attribute numeric */
611/* value pointed by 'data'    */
612/* and 'length' size.         */
613/******************************/
614/* Returns nothing.           */
615/******************************/
616static void
617print_attr_num(netdissect_options *ndo,
618               register const u_char *data, u_int length, u_short attr_code)
619{
620   uint32_t timeout;
621
622   if (length != 4)
623   {
624       ND_PRINT((ndo, "ERROR: length %u != 4", length));
625       return;
626   }
627
628   ND_TCHECK2(data[0],4);
629                          /* This attribute has standard values */
630   if (attr_type[attr_code].siz_subtypes)
631   {
632      static const char **table;
633      uint32_t data_value;
634      table = attr_type[attr_code].subtypes;
635
636      if ( (attr_code == TUNNEL_TYPE) || (attr_code == TUNNEL_MEDIUM) )
637      {
638         if (!*data)
639            ND_PRINT((ndo, "Tag[Unused] "));
640         else
641            ND_PRINT((ndo, "Tag[%d] ", *data));
642         data++;
643         data_value = EXTRACT_24BITS(data);
644      }
645      else
646      {
647         data_value = EXTRACT_32BITS(data);
648      }
649      if ( data_value <= (uint32_t)(attr_type[attr_code].siz_subtypes - 1 +
650            attr_type[attr_code].first_subtype) &&
651	   data_value >= attr_type[attr_code].first_subtype )
652         ND_PRINT((ndo, "%s", table[data_value]));
653      else
654         ND_PRINT((ndo, "#%u", data_value));
655   }
656   else
657   {
658      switch(attr_code) /* Be aware of special cases... */
659      {
660        case FRM_IPX:
661             if (EXTRACT_32BITS( data) == 0xFFFFFFFE )
662                ND_PRINT((ndo, "NAS Select"));
663             else
664                ND_PRINT((ndo, "%d", EXTRACT_32BITS(data)));
665          break;
666
667        case SESSION_TIMEOUT:
668        case IDLE_TIMEOUT:
669        case ACCT_DELAY:
670        case ACCT_SESSION_TIME:
671        case ACCT_INT_INTERVAL:
672             timeout = EXTRACT_32BITS( data);
673             if ( timeout < 60 )
674                ND_PRINT((ndo,  "%02d secs", timeout));
675             else
676             {
677                if ( timeout < 3600 )
678                   ND_PRINT((ndo,  "%02d:%02d min",
679                          timeout / 60, timeout % 60));
680                else
681                   ND_PRINT((ndo, "%02d:%02d:%02d hours",
682                          timeout / 3600, (timeout % 3600) / 60,
683                          timeout % 60));
684             }
685          break;
686
687        case FRM_ATALK_LINK:
688             if (EXTRACT_32BITS(data) )
689                ND_PRINT((ndo, "%d", EXTRACT_32BITS(data)));
690             else
691                ND_PRINT((ndo, "Unnumbered"));
692          break;
693
694        case FRM_ATALK_NETWORK:
695             if (EXTRACT_32BITS(data) )
696                ND_PRINT((ndo, "%d", EXTRACT_32BITS(data)));
697             else
698                ND_PRINT((ndo, "NAS assigned"));
699          break;
700
701        case TUNNEL_PREFERENCE:
702            if (*data)
703               ND_PRINT((ndo, "Tag[%d] ", *data));
704            else
705               ND_PRINT((ndo, "Tag[Unused] "));
706            data++;
707            ND_PRINT((ndo, "%d", EXTRACT_24BITS(data)));
708          break;
709
710        case EGRESS_VLAN_ID:
711            ND_PRINT((ndo, "%s (0x%02x) ",
712                   tok2str(rfc4675_tagged,"Unknown tag",*data),
713                   *data));
714            data++;
715            ND_PRINT((ndo, "%d", EXTRACT_24BITS(data)));
716          break;
717
718        default:
719             ND_PRINT((ndo, "%d", EXTRACT_32BITS(data)));
720          break;
721
722      } /* switch */
723
724   } /* if-else */
725
726   return;
727
728   trunc:
729     ND_PRINT((ndo, "%s", tstr));
730}
731
732/*****************************/
733/* Print an attribute IPv4   */
734/* address value pointed by  */
735/* 'data' and 'length' size. */
736/*****************************/
737/* Returns nothing.          */
738/*****************************/
739static void
740print_attr_address(netdissect_options *ndo,
741                   register const u_char *data, u_int length, u_short attr_code)
742{
743   if (length != 4)
744   {
745       ND_PRINT((ndo, "ERROR: length %u != 4", length));
746       return;
747   }
748
749   ND_TCHECK2(data[0],4);
750
751   switch(attr_code)
752   {
753      case FRM_IPADDR:
754      case LOG_IPHOST:
755           if (EXTRACT_32BITS(data) == 0xFFFFFFFF )
756              ND_PRINT((ndo, "User Selected"));
757           else
758              if (EXTRACT_32BITS(data) == 0xFFFFFFFE )
759                 ND_PRINT((ndo, "NAS Select"));
760              else
761                 ND_PRINT((ndo, "%s",ipaddr_string(ndo, data)));
762      break;
763
764      default:
765          ND_PRINT((ndo, "%s", ipaddr_string(ndo, data)));
766      break;
767   }
768
769   return;
770
771   trunc:
772     ND_PRINT((ndo, "%s", tstr));
773}
774
775/*************************************/
776/* Print an attribute of 'secs since */
777/* January 1, 1970 00:00 UTC' value  */
778/* pointed by 'data' and 'length'    */
779/* size.                             */
780/*************************************/
781/* Returns nothing.                  */
782/*************************************/
783static void
784print_attr_time(netdissect_options *ndo,
785                register const u_char *data, u_int length, u_short attr_code _U_)
786{
787   time_t attr_time;
788   char string[26];
789
790   if (length != 4)
791   {
792       ND_PRINT((ndo, "ERROR: length %u != 4", length));
793       return;
794   }
795
796   ND_TCHECK2(data[0],4);
797
798   attr_time = EXTRACT_32BITS(data);
799   strlcpy(string, ctime(&attr_time), sizeof(string));
800   /* Get rid of the newline */
801   string[24] = '\0';
802   ND_PRINT((ndo, "%.24s", string));
803   return;
804
805   trunc:
806     ND_PRINT((ndo, "%s", tstr));
807}
808
809/***********************************/
810/* Print an attribute of 'strange' */
811/* data format pointed by 'data'   */
812/* and 'length' size.              */
813/***********************************/
814/* Returns nothing.                */
815/***********************************/
816static void
817print_attr_strange(netdissect_options *ndo,
818                   register const u_char *data, u_int length, u_short attr_code)
819{
820   u_short len_data;
821
822   switch(attr_code)
823   {
824      case ARAP_PASS:
825           if (length != 16)
826           {
827               ND_PRINT((ndo, "ERROR: length %u != 16", length));
828               return;
829           }
830           ND_PRINT((ndo, "User_challenge ("));
831           ND_TCHECK2(data[0],8);
832           len_data = 8;
833           PRINT_HEX(len_data, data);
834           ND_PRINT((ndo, ") User_resp("));
835           ND_TCHECK2(data[0],8);
836           len_data = 8;
837           PRINT_HEX(len_data, data);
838           ND_PRINT((ndo, ")"));
839        break;
840
841      case ARAP_FEATURES:
842           if (length != 14)
843           {
844               ND_PRINT((ndo, "ERROR: length %u != 14", length));
845               return;
846           }
847           ND_TCHECK2(data[0],1);
848           if (*data)
849              ND_PRINT((ndo, "User can change password"));
850           else
851              ND_PRINT((ndo, "User cannot change password"));
852           data++;
853           ND_TCHECK2(data[0],1);
854           ND_PRINT((ndo, ", Min password length: %d", *data));
855           data++;
856           ND_PRINT((ndo, ", created at: "));
857           ND_TCHECK2(data[0],4);
858           len_data = 4;
859           PRINT_HEX(len_data, data);
860           ND_PRINT((ndo, ", expires in: "));
861           ND_TCHECK2(data[0],4);
862           len_data = 4;
863           PRINT_HEX(len_data, data);
864           ND_PRINT((ndo, ", Current Time: "));
865           ND_TCHECK2(data[0],4);
866           len_data = 4;
867           PRINT_HEX(len_data, data);
868        break;
869
870      case ARAP_CHALLENGE_RESP:
871           if (length < 8)
872           {
873               ND_PRINT((ndo, "ERROR: length %u != 8", length));
874               return;
875           }
876           ND_TCHECK2(data[0],8);
877           len_data = 8;
878           PRINT_HEX(len_data, data);
879        break;
880   }
881   return;
882
883   trunc:
884     ND_PRINT((ndo, "%s", tstr));
885}
886
887static void
888radius_attrs_print(netdissect_options *ndo,
889                   register const u_char *attr, u_int length)
890{
891   register const struct radius_attr *rad_attr = (const struct radius_attr *)attr;
892   const char *attr_string;
893
894   while (length > 0)
895   {
896     if (length < 2)
897        goto trunc;
898     ND_TCHECK(*rad_attr);
899
900     if (rad_attr->type > 0 && rad_attr->type < TAM_SIZE(attr_type))
901	attr_string = attr_type[rad_attr->type].name;
902     else
903	attr_string = "Unknown";
904     if (rad_attr->len < 2)
905     {
906	ND_PRINT((ndo, "\n\t  %s Attribute (%u), length: %u (bogus, must be >= 2)",
907               attr_string,
908               rad_attr->type,
909               rad_attr->len));
910	return;
911     }
912     if (rad_attr->len > length)
913     {
914	ND_PRINT((ndo, "\n\t  %s Attribute (%u), length: %u (bogus, goes past end of packet)",
915               attr_string,
916               rad_attr->type,
917               rad_attr->len));
918        return;
919     }
920     ND_PRINT((ndo, "\n\t  %s Attribute (%u), length: %u, Value: ",
921            attr_string,
922            rad_attr->type,
923            rad_attr->len));
924
925     if (rad_attr->type < TAM_SIZE(attr_type))
926     {
927         if (rad_attr->len > 2)
928         {
929             if ( attr_type[rad_attr->type].print_func )
930                 (*attr_type[rad_attr->type].print_func)(
931                     ndo, ((const u_char *)(rad_attr+1)),
932                     rad_attr->len - 2, rad_attr->type);
933         }
934     }
935     /* do we also want to see a hex dump ? */
936     if (ndo->ndo_vflag> 1)
937         print_unknown_data(ndo, (const u_char *)rad_attr+2, "\n\t    ", (rad_attr->len)-2);
938
939     length-=(rad_attr->len);
940     rad_attr = (const struct radius_attr *)( ((const char *)(rad_attr))+rad_attr->len);
941   }
942   return;
943
944trunc:
945   ND_PRINT((ndo, "%s", tstr));
946}
947
948void
949radius_print(netdissect_options *ndo,
950             const u_char *dat, u_int length)
951{
952   register const struct radius_hdr *rad;
953   u_int len, auth_idx;
954
955   ND_TCHECK2(*dat, MIN_RADIUS_LEN);
956   rad = (const struct radius_hdr *)dat;
957   len = EXTRACT_16BITS(&rad->len);
958
959   if (len < MIN_RADIUS_LEN)
960   {
961	  ND_PRINT((ndo, "%s", tstr));
962	  return;
963   }
964
965   if (len > length)
966	  len = length;
967
968   if (ndo->ndo_vflag < 1) {
969       ND_PRINT((ndo, "RADIUS, %s (%u), id: 0x%02x length: %u",
970              tok2str(radius_command_values,"Unknown Command",rad->code),
971              rad->code,
972              rad->id,
973              len));
974       return;
975   }
976   else {
977       ND_PRINT((ndo, "RADIUS, length: %u\n\t%s (%u), id: 0x%02x, Authenticator: ",
978              len,
979              tok2str(radius_command_values,"Unknown Command",rad->code),
980              rad->code,
981              rad->id));
982
983       for(auth_idx=0; auth_idx < 16; auth_idx++)
984            ND_PRINT((ndo, "%02x", rad->auth[auth_idx]));
985   }
986
987   if (len > MIN_RADIUS_LEN)
988      radius_attrs_print(ndo, dat + MIN_RADIUS_LEN, len - MIN_RADIUS_LEN);
989   return;
990
991trunc:
992   ND_PRINT((ndo, "%s", tstr));
993}
994