Radius
RadiusMsg.h
1 // RadiusMsg.h
2 //
3 /// \mainpage RADIUS Library for Arduino
4 ///
5 /// This library provides services for creating and sending RADIUS requests and receiving and
6 /// decoding RADIUS replies. It also provides a class for managing a UDP Socket,
7 /// over which RADIUS requests are sent and received.
8 ///
9 /// It is designed to be used with the Arduino Ethernet shield and ArduinoMega.
10 /// The size of the binary is such that a complete RADIUS client is too big for an
11 /// Arduino Diecimila (which the
12 /// Ethernet shield is designed for), and you need an ArduinoMega or similar.
13 /// Unfortunately, you need to make some special hardware arrangements to make the
14 /// Ethernet shield work with Arduino Mega, although no soldering is required:
15 /// see http://mcukits.com/2009/04/06/arduino-ethernet-shield-mega-hack/
16 /// and http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1252386632
17 /// Note that 3 pins on the Ethernet shield must be bent out of the way to make this work.
18 ///
19 /// The latest version of this documentation can be downloaded from
20 /// http://www.airspayce.com/mikem/arduino/Radius
21 ///
22 /// The version of the package that this documentation refers to can be downloaded
23 /// from http://www.airspayce.com/mikem/arduino/Radius/Radius-1.2.zip
24 ///
25 /// The package includes a sample RadiusClient sketch, which gives a working example of
26 /// how to use the library.
27 ///
28 /// \par Open Source Licensing GPL V2
29 /// This is the appropriate option if you want to share the source code of your
30 /// application with everyone you distribute it to, and you also want to give them
31 /// the right to share who uses it. If you wish to use this software under Open
32 /// Source Licensing, you must contribute all your source code to the open source
33 /// community in accordance with the GPL Version 2 when your application is
34 /// distributed. See http://www.gnu.org/copyleft/gpl.html
35 ///
36 /// \par Commercial Licensing
37 /// This is the appropriate option if you are creating proprietary applications
38 /// and you are not prepared to distribute and share the source code of your
39 /// application. Contact info@airspayce.com for details.
40 ///
41 /// \par Revision History
42 /// \version 1.0 Initial release
43 /// \version 1.1 Builds on Arduino 1.0
44 /// \version 1.2 Updated author and distribution location details to airspayce.com
45 ///
46 /// \author Mike McCauley (mikem@airspayce.com)
47 // Copyright (C) 2009 Mike McCauley
48 // $Id: RadiusMsg.h,v 1.1 2009/10/13 05:07:28 mikem Exp mikem $
49 
50 #ifndef _RADIUSMSG_H_
51 #define _RADIUSMSG_H_
52 
53 #include "UDPSocket.h"
54 
55 #define RADIUS_AUTHENTICATOR_LENGTH 16
56 #define RADIUS_PASSWORD_BLOCK_SIZE 16
57 #define RADIUS_HEADER_LENGTH 20
58 // In actuality, the max permitted RADIUS packet size is 4096, but arduino cant handle that size
59 // so we artificially limit packets to 1000 octets
60 #define RADIUS_MAX_SIZE 1000
61 #define RADIUS_MAX_ATTRIBUTE_SIZE 253
62 typedef uint8_t RadiusAuthenticator[RADIUS_AUTHENTICATOR_LENGTH];
63 
64 // RADIUS message type
65 typedef enum
66 {
67  RadiusCodeAccessRequest = 1,
68  RadiusCodeAccessAccept = 2,
69  RadiusCodeAccessReject = 3,
70  RadiusCodeAccountingRequest = 4,
71  RadiusCodeAccountingResponse = 5,
72  RadiusCodeAccountingStatus = 6,
73  RadiusCodeAccessPasswordRequest = 7,
74  RadiusCodeAccessPasswordAck = 8,
75  RadiusCodeAccessPasswordReject = 9,
76  RadiusCodeAccountingMessage = 10,
77  RadiusCodeAccessChallenge = 11,
78  RadiusCodeStatusServer = 12,
79  RadiusCodeStatusClient = 13,
80  RadiusCodeResourceFreeRequest = 21,
81  RadiusCodeResourceFreeResponse = 22,
82  RadiusCodeResourceQueryRequest = 23,
83  RadiusCodeResourceQueryResponse = 24,
84  RadiusCodeAlternateResourceReclaimRequest = 25,
85  RadiusCodeNASRebootRequest = 26,
86  RadiusCodeNASRebootResponse = 27,
87  RadiusCodeAscendAccessNextCode = 29,
88  RadiusCodeAscendAccessNewPin = 30,
89  RadiusCodeAscendTerminateSession = 31,
90  RadiusCodeAscendPasswordExpired = 32,
91  RadiusCodeAscendAccessEventRequest = 33,
92  RadiusCodeAscendAccessEventResponse = 34,
93  RadiusCodeDisconnectRequest = 40,
94  RadiusCodeDisconnectRequestACKed = 41,
95  RadiusCodeDisconnectRequestNAKed = 42,
96  RadiusCodeChangeFilterRequest = 43,
97  RadiusCodeChangeFilterRequestACKed = 44,
98  RadiusCodeChangeFilterRequestNAKed = 45,
99  RadiusCodeIPAddressAllocate = 50,
100  RadiusCodeIPAddressRelease = 51,
101  // This is the next code after the last valid one
102  RadiusCodeStartInvalidCodes = 52
103 } RadiusCode;
104 
105 const unsigned RadiusAttrUserName = 1;
106 const unsigned RadiusAttrUserPassword = 2;
107 const unsigned RadiusAttrChapPassword = 3;
108 const unsigned RadiusAttrNasIPAddress = 4;
109 const unsigned RadiusAttrNASPort = 5;
110 const unsigned RadiusAttrServiceType = 6;
111 const unsigned RadiusAttrFramedProtocol = 7;
112 const unsigned RadiusAttrFramedIPAddress = 8;
113 const unsigned RadiusAttrFramedIPNetmask = 9;
114 const unsigned RadiusAttrFramedRouting = 10;
115 const unsigned RadiusAttrFilterId = 11;
116 const unsigned RadiusAttrFramedMTU = 12;
117 const unsigned RadiusAttrFramedCompression = 13;
118 const unsigned RadiusAttrLoginIPHost = 14;
119 const unsigned RadiusAttrLoginService = 15;
120 const unsigned RadiusAttrLoginTCPPort = 16;
121 const unsigned RadiusAttrOldPassword = 17;
122 const unsigned RadiusAttrReplyMessage = 18;
123 const unsigned RadiusAttrCallbackNumber = 19;
124 const unsigned RadiusAttrCallbackId = 20;
125 const unsigned RadiusAttrAscendPWExpiration = 21;
126 const unsigned RadiusAttrFramedRoute = 22;
127 const unsigned RadiusAttrFramedIPXNetwork = 23;
128 const unsigned RadiusAttrState = 24;
129 const unsigned RadiusAttrClass = 25;
130 const unsigned RadiusAttrVendorSpecific = 26;
131 const unsigned RadiusAttrSessionTimeout = 27;
132 const unsigned RadiusAttrIdleTimeout = 28;
133 const unsigned RadiusAttrTerminationAction = 29;
134 const unsigned RadiusAttrCalledStationId = 30;
135 const unsigned RadiusAttrCallingStationId = 31;
136 const unsigned RadiusAttrNASIdentifier = 32;
137 const unsigned RadiusAttrProxyState = 33;
138 const unsigned RadiusAttrLoginLATService = 34;
139 const unsigned RadiusAttrLoginLATNode = 35;
140 const unsigned RadiusAttrLoginLATGroup = 36;
141 const unsigned RadiusAttrFramedAppleTalkLink = 37;
142 const unsigned RadiusAttrFramedAppleTalkNetwork = 38;
143 const unsigned RadiusAttrFramedAppleTalkZone = 39;
144 const unsigned RadiusAttrAcctStatusType = 40;
145 const unsigned RadiusAttrAcctDelayTime = 41;
146 const unsigned RadiusAttrAcctInputOctets = 42;
147 const unsigned RadiusAttrAcctOutputOctets = 43;
148 const unsigned RadiusAttrAcctSessionId = 44;
149 const unsigned RadiusAttrAcctAuthentic = 45;
150 const unsigned RadiusAttrAcctSessionTime = 46;
151 const unsigned RadiusAttrAcctInputPackets = 47;
152 const unsigned RadiusAttrAcctOutputPackets = 48;
153 const unsigned RadiusAttrAcctTerminateCause = 49;
154 const unsigned RadiusAttrAcctMultiSessionId = 50;
155 const unsigned RadiusAttrAcctLinkCount = 51;
156 const unsigned RadiusAttrAcctInputGigawords = 52;
157 const unsigned RadiusAttrAcctOutputGigawords = 53;
158 
159 const unsigned RadiusAttrEventTimestamp = 55;
160 const unsigned RadiusAttrEgressVLANID = 56;
161 const unsigned RadiusAttrIngressFilters = 57;
162 const unsigned RadiusAttrEgressVLANName = 58;
163 const unsigned RadiusAttrUserPriorityTable = 59;
164 const unsigned RadiusAttrCHAPChallenge = 60;
165 const unsigned RadiusAttrNASPortType = 61;
166 const unsigned RadiusAttrPortLimit = 62;
167 const unsigned RadiusAttrLoginLATPort = 63;
168 const unsigned RadiusAttrTunnelType = 64;
169 const unsigned RadiusAttrTunnelMediumType = 65;
170 const unsigned RadiusAttrTunnelClientEndpoint = 66;
171 const unsigned RadiusAttrTunnelServerEndpoint = 67;
172 const unsigned RadiusAttrTunnelID = 68;
173 const unsigned RadiusAttrTunnelPassword = 69;
174 const unsigned RadiusAttrARAPPassword = 70;
175 const unsigned RadiusAttrARAPFeatures = 71;
176 const unsigned RadiusAttrARAPZoneAccess = 72;
177 const unsigned RadiusAttrARAPSecurity = 73;
178 const unsigned RadiusAttrARAPSecurityData = 74;
179 const unsigned RadiusAttrPasswordRetry = 75;
180 const unsigned RadiusAttrPrompt = 76;
181 const unsigned RadiusAttrConnectInfo = 77;
182 const unsigned RadiusAttrConfigurationToken = 78;
183 const unsigned RadiusAttrEAPMessage = 79;
184 const unsigned RadiusAttrMessageAuthenticator = 80;
185 const unsigned RadiusAttrTunnelPrivateGroupID = 81;
186 const unsigned RadiusAttrTunnelAssignmentID = 82;
187 const unsigned RadiusAttrTunnelPreference = 83;
188 const unsigned RadiusAttrARAPChallengeResponse = 84;
189 const unsigned RadiusAttrAcctInterimInterval = 85;
190 const unsigned RadiusAttrAcctTunnelPacketsLost = 86;
191 const unsigned RadiusAttrNASPortId = 87;
192 const unsigned RadiusAttrFramedPool = 88;
193 const unsigned RadiusAttrChargeableUserIdentity = 89;
194 const unsigned RadiusAttrTunnelClientAuthID = 90;
195 const unsigned RadiusAttrTunnelServerAuthID = 91;
196 const unsigned RadiusAttrNASFilterRule = 92;
197 const unsigned RadiusAttrOriginatingLineInfo = 94;
198 const unsigned RadiusAttrNASIPv6Address = 95;
199 const unsigned RadiusAttrFramedInterfaceId = 96;
200 const unsigned RadiusAttrFramedIPv6Prefix = 97;
201 const unsigned RadiusAttrLoginIPv6Host = 98;
202 const unsigned RadiusAttrFramedIPv6Route = 99;
203 const unsigned RadiusAttrFramedIPv6Pool = 100;
204 const unsigned RadiusAttrErrorCause = 101;
205 const unsigned RadiusAttrEAPKeyName = 102;
206 const unsigned RadiusAttrTimestamp = 103;
207 
208 const unsigned RadiusAttrDelegatedIPv6Prefix = 123;
209 
210 // Well known Radius Attribute Values
211 const unsigned RadiusValueAcctStatusTypeStart = 1;
212 const unsigned RadiusValueAcctStatusTypeStop = 2;
213 const unsigned RadiusValueAcctStatusTypeAlive = 3;
214 const unsigned RadiusValueAcctStatusTypeModemStart = 4;
215 const unsigned RadiusValueAcctStatusTypeModemStop = 5;
216 const unsigned RadiusValueAcctStatusTypeCancel = 6;
217 const unsigned RadiusValueAcctStatusTypeAccountingOn = 7;
218 const unsigned RadiusValueAcctStatusTypeAccountingOff = 8;
219 const unsigned RadiusValueAcctStatusTypeTunnelStart = 9;
220 const unsigned RadiusValueAcctStatusTypeTunnelStop = 10;
221 const unsigned RadiusValueAcctStatusTypeTunnelReject = 11;
222 const unsigned RadiusValueAcctStatusTypeTunnelLinkStart = 12;
223 const unsigned RadiusValueAcctStatusTypeTunnelLinkStop = 13;
224 const unsigned RadiusValueAcctStatusTypeTunnelLinkReject = 14;
225 const unsigned RadiusValueAcctStatusTypeFailed = 15;
226 
227 // Well know RADIUS vendors
228 const unsigned RadiusVendorCisco = 9;
229 const unsigned RadiusVendorMicrosoft = 311;
230 const unsigned RadiusVendorBreezecom = 710;
231 const unsigned RadiusVendorNortelAptis = 2637;
232 const unsigned RadiusVendorOpenSystemConsultants = 9048;
233 
234 // Well known Cisco VSAs
235 const unsigned RadiusVendorCiscoAttrCiscoAvpair = 1;
236 
237 // Well known Microsoft VSAs
238 const unsigned RadiusVendorMicrosoftAttrMSCHAPResponse = 1;
239 const unsigned RadiusVendorMicrosoftAttrMSCHAPChallenge = 11;
240 const unsigned RadiusVendorMicrosoftAttrMSCHAPMPPEKeys = 12;
241 const unsigned RadiusVendorMicrosoftAttrMSMPPESendKey = 16;
242 const unsigned RadiusVendorMicrosoftAttrMSMPPERecvKey = 17;
243 const unsigned RadiusVendorMicrosoftAttrMSCHAP2Response = 25;
244 
245 
246 /////////////////////////////////////////////////////////////////////
247 /// \struct RadiusPacket
248 /// Helper class for mapping RADIUS request packets, including the header
249 /// See RFC 2138
250 typedef struct
251 {
252  /// The RADIUS message type code
253  uint8_t code;
254 
255  /// RADIUS identifier
256  uint8_t identifier;
257 
258  /// Total length of the packet, including the header
259  uint16_t length;
260 
261  /// RADISU authenticator
262  uint8_t authenticator[RADIUS_AUTHENTICATOR_LENGTH];
263 
264  /// All attributes in serial packed format
265  uint8_t attrs[RADIUS_MAX_SIZE - RADIUS_HEADER_LENGTH];
266 
267 } RadiusPacket;
268 
269 /////////////////////////////////////////////////////////////////////
270 /// \struct RadiusAttrHeader
271 /// Maps a RADIUS Attribute, including the header
272 typedef struct
273 {
274  /// Attribute number
275  uint8_t type;
276 
277  /// Total length, including header
278  uint8_t length;
279 
280  /// Value of the attribute. May have some internal structure, such as for VSAs etc
281  uint8_t value[RADIUS_MAX_ATTRIBUTE_SIZE];
282 
284 
285 /////////////////////////////////////////////////////////////////////
286 /// \class RadiusMsg RadiusMsg.h <RadiusMsg.h>
287 /// \brief Class to create, format and send RADIUS requests and replies
288 ///
289 /// This class is used in conjunction with UDPSocket to create, format and send RADIUS requests,
290 /// and to receive, authenticate and decode RADIUS replies. Works with the Arduino Ethernet shield
291 /// to connect to a LAN and communicate with a RDAIUS server, such as Radiator RADIUS Server
292 /// (http://www.airspayce.com/radiator)
293 ///
294 /// Conforms broadly to RFC 2138 and 2139, with limitations:
295 /// \li Vendor Specific Attribautes are not supported
296 /// \li The only encrypted attribtute supported is User-Password
297 ///
298 /// There is no RADIUS dictionary: When adding attributes to a reque or getting attriburtes
299 /// from a reply, you are required to use the appropriate calls according to the attribute
300 /// type of the attribute you are using: binary, string or integer
302 {
303 private:
304  /// The formatted RADIUS packet, including header
305  RadiusPacket packet;
306 
307  /// The number of valid bytes in the packet, including the header, Min 20
308  uint16_t packetLength;
309 
310  /// Number of retries for retransmission
311  uint8_t retries;
312 
313  /// retransmission timeout in seconds
314  uint8_t timeout;
315 
316  /// The IP address of the peer destination address (when sent) or the sender address when received
317  IP4Address peerAddress;
318 
319  /// The port number of the peer
320  uint16_t peerPort;
321 
322 public:
323  /// Constructor for receiving
324  RadiusMsg();
325 
326  /// Constructor for sending. RADIUS message type code is initialised
327  RadiusMsg(RadiusCode code);
328 
329  /// Return the RADIUS message type code
330  /// \return RADIUS message type code
331  uint8_t code();
332 
333  /// Add an attribute to the request, binary octets
334  /// \param[in] type The RADIUS attribute number
335  /// \param[in] vendor The vendor number of the attribue (unused, set to 0)
336  /// \param[in] value Pointer to the octets of the value
337  /// \param[in] length Number of octets in the value
338  void addAttr(unsigned type, unsigned vendor, uint8_t* value, uint8_t length);
339 
340  /// Add a CString type attribute to the request
341  /// \param[in] type The RADIUS attribute number
342  /// \param[in] vendor The vendor number of the attribue (unused, set to 0)
343  /// \param[in] value CString value to set. String up to (but not including) the first NUL
344  /// are used to set th value
345  void addAttr(unsigned type, unsigned vendor, const char* value);
346 
347  /// Add a 32 bit unsigned integer type to the request
348  /// \param[in] type The RADIUS attribute number
349  /// \param[in] vendor The vendor number of the attribue (unused, set to 0)
350  /// \param[in] value 32 bit unsigned integer value
351  void addAttr(unsigned type, unsigned vendor, uint32_t value);
352 
353  /// Get the nth attribute with matching attribute number (and optional vendor number)
354  /// Skips over 'skip' attributes to get the 'skip'th matching attribute
355  /// \param[in] type The RADIUS attribute number
356  /// \param[in] vendor The vendor number of the attribue (unused, set to 0)
357  /// \param[in] value Destination to copy the value to
358  /// \param[in] length Caller sets this to the maximum permitted length available in value.
359  /// if return is 1, up to length octets will be copied, and *length will be set to the actual
360  /// number of octets copied.
361  /// \param[in] skip Number of matching attributes to skip (defaults to 0,
362  /// which means get the first matching one)
363  /// \return true if a match was found and the value copied
364  uint8_t getAttr(unsigned type, unsigned vendor, uint8_t* value, uint8_t* length, uint8_t skip = 0);
365 
366  /// Get the nth attribute with matching attribue number (and optional vendor number)
367  /// as a 32 bit unsigned integer
368  /// \param[in] type The RADIUS attribute number
369  /// \param[in] vendor The vendor number of the attribue (unused, set to 0)
370  /// \param[in] value Destination to copy the value to
371  /// \param[in] skip Number of matching attributes to skip (defaults to 0,
372  /// which means get the first matching one)
373  /// \return true if a match was found and the value copied
374  uint8_t getAttr(unsigned type, unsigned vendor, uint32_t* value, uint8_t skip = 0);
375 
376  /// Encrypts any parameters that require encryption, and sets the authethenticator
377  /// for RADIUS codes that require it. Uses the shared secret for encryption and signing.
378  /// \param[in] secret The RADIUS shared secret
379  /// \param[in] secretLength Length of the secret in octets
380  /// \param[in] original for RADIUS requests that are replies to an earlier request, this
381  /// points to the original requerst, which is required to correctly set the authenticator in the reply.
382  void sign(uint8_t* secret, uint8_t secretLength, RadiusMsg* original = 0);
383 
384  /// Sends this RADIUS message on a UDP Socket
385  /// \param[in] socket Instance of UDPSocket to use to send the message
386  /// \param[in] peer IPV4Address of the destination RADIUS peer
387  /// \param[in] port Port number of the destination RADIUS peer
388  /// \return Returns the sent packet size for success, else -1
389  uint16_t sendto(UDPSocket* socket, IP4Address peer, uint16_t port);
390 
391  /// Utility function for encryption passwords and other data in RADIUS RFC compliant fashion
392  /// \param[in] data The data octets to encrypt
393  /// \param[in] length Number of octets in data
394  /// \param[in] secret The RADIUS shared secret
395  /// \param[in] secretLength Length of the secret in octets
396  /// \param[in] iv The intialisation vector
397  void encryptPassword(uint8_t* data, uint8_t length, uint8_t* secret, uint8_t secretLength, uint8_t* iv);
398 
399  /// Fill the packet data in the RadiusMsg with the next packet received on socket.
400  /// Blocks until a packet is received. Packets that are received and which dont look
401  /// vaguely like a RADIUS essage are discarded
402  /// \param socket Pointer to the UDP socket to receive from
403  /// \return The number of octets in the received message else 0 if the message was discarded
404  uint16_t recv(UDPSocket* socket);
405 
406  /// Send a message to the destiantion server, and wait for a matching reply.
407  /// Implements timeouts and retries until a matching reply is received
408  /// Non-matching RADIUS requests are silently discarded.
409  /// Blocks until a satisfying reply is received or all retries are exhausted
410  /// \param[in] socket Pointer to the UDP socket used to send and receive
411  /// \param[in] server IP4Address of the destination server
412  /// \param[in] port The port number of the RADIUS server at the destination
413  /// \param[in] reply Pointer to a RadiusMsg which will be filled in with the reply (if any)
414  /// \return true if the request was snetr and a matchin reply received
415  uint8_t sendWaitReply(UDPSocket* socket, IP4Address server, uint16_t port, RadiusMsg* reply);
416 
417  /// Checks that the authenticator in the RadiusMsg is correct, and that therefore is
418  /// verified as being from the expected peer. For RADIUS replies, requires the
419  /// original request to be supplied.
420  /// \param[in] secret The RADIUS shared secret
421  /// \param[in] secretLength Length of the secret in octets
422  /// \param[in] original When checking the authenticator of a RADIUS reply, this must point to the
423  /// original request
424  /// \return true if authenticator is correct.
425  uint8_t checkAuthenticatorsWithOriginal(uint8_t* secret, uint8_t secretLength, RadiusMsg* original);
426 };
427 
428 
429 #endif