File: 0007-RadiusBLAST

package info (click to toggle)
openvpn-auth-radius 2.1-9
  • links: PTS, VCS
  • area: main
  • in suites: trixie
  • size: 1,716 kB
  • sloc: cpp: 8,578; perl: 323; makefile: 46
file content (406 lines) | stat: -rw-r--r-- 13,718 bytes parent folder | download | duplicates (3)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
https://github.com/OpenVPN-Community/openvpn-radiusplugin/pull/9

commit bcd7cf10a3c8ad75cfe9231865df0aa43482bd91
Author: Samuel Thibault <samuel.thibault@ens-lyon.org>
Date:   Sun Oct 20 16:27:32 2024 +0200

    Add MessageAuthenticator support
    
    To address RadiusBLAST vulnerability.

diff --git a/RadiusClass/RadiusAttribute.cpp b/RadiusClass/RadiusAttribute.cpp
index da7d6d3..d3ca5d7 100755
--- a/RadiusClass/RadiusAttribute.cpp
+++ b/RadiusClass/RadiusAttribute.cpp
@@ -382,6 +382,14 @@ int RadiusAttribute::setValue(char *value)
 				
 			}
 			break;
+		case	ATTRIB_Message_Authenticator:
+			if(!(this->value=new Octet [16]))
+			{
+				return ALLOC_ERROR;
+			}
+			memcpy(this->value, value, 16);
+			this->length=(Octet)16;
+			break;
 			
 		//for datatype integer/enum	
 		case	ATTRIB_NAS_Port:					 
diff --git a/RadiusClass/RadiusConfig.cpp b/RadiusClass/RadiusConfig.cpp
index 18702bd..54c19c9 100755
--- a/RadiusClass/RadiusConfig.cpp
+++ b/RadiusClass/RadiusConfig.cpp
@@ -171,6 +171,17 @@ int RadiusConfig::parseConfigFile(const char * configfile)
 					{
 						tmpServer->setWait(atoi(line.substr(5).c_str()));
 					}
+					if (strncmp(line.c_str(),"requirema=",10)==0)
+					{
+						if (strcmp(line.substr(10).c_str(),"yes") == 0)
+							tmpServer->setRequireMA(1);
+						else if (strcmp(line.substr(10).c_str(),"no") == 0)
+							tmpServer->setRequireMA(0);
+						else if (strcmp(line.substr(10).c_str(),"auto") == 0)
+							tmpServer->setRequireMA(-1);
+						else
+							return PARSING_ERROR;
+					}
 				}
 				if(strstr(line.c_str(),"}"))
 				{
diff --git a/RadiusClass/RadiusPacket.cpp b/RadiusClass/RadiusPacket.cpp
index d97dcd1..3b1726c 100755
--- a/RadiusClass/RadiusPacket.cpp
+++ b/RadiusClass/RadiusPacket.cpp
@@ -65,6 +65,7 @@ RadiusPacket::RadiusPacket(Octet code)
 	this->length=sizeof(Octet)*(RADIUS_PACKET_AUTHENTICATOR_LEN+4);
 	this->sendbuffer=NULL;
 	this->sendbufferlen=0;
+	this->message_authenticator=0;
 	this->recvbuffer=NULL;
 	this->recvbufferlen=0;
 	this->sock=0;
@@ -195,6 +196,12 @@ int RadiusPacket::shapeRadiusPacket(const char * sharedsecret)
 	//add the attributes to the buffer
 	for (multimap<Octet, RadiusAttribute>::iterator it = attribs.begin(); it != attribs.end(); it++)
 	{
+		if (it->second.getType()==ATTRIB_Message_Authenticator)
+		{
+			// Record offset for filling it below.
+			this->message_authenticator = this->sendbufferlen+2;
+		}
+
 		//if the attribute is a password, build the hashedpassword
 		if (it->second.getType()==ATTRIB_User_Password)
 		{
@@ -406,6 +413,11 @@ int RadiusPacket::radiusSend(list<RadiusServer>::iterator server)
 		this->calcacctdigest(server->getSharedSecret().c_str());
 	
 	}
+
+	if (this->code==ACCESS_REQUEST && this->message_authenticator)
+	{
+		this->calcmadigest(server->getSharedSecret().c_str());
+	}
 	
 	//save the authenticator field for packet authentication on receiving a packet
 	memcpy(this->authenticator, this->req_authenticator, 16);
@@ -526,7 +538,7 @@ int RadiusPacket::radiusReceive(list<RadiusServer> *serverlist)
 					return UNSHAPE_ERROR;
 				}
 				
-				if (this->authenticateReceivedPacket(server->getSharedSecret().c_str())!=0)
+				if (this->authenticateReceivedPacket(&*(server))!=0)
 				{
 					
 					return WRONG_AUTHENTICATOR_IN_RECV_PACKET;
@@ -599,6 +611,44 @@ void RadiusPacket::calcacctdigest(const char *secret)
 	gcry_md_close(context);
 }
 
+/** Sets the message authenticator field if the packet is
+ * an access request. It is a MD5 hash over the whole packet
+ * (the authenticator field itself is set to 0) with the shared
+ * secret used as Hmac key.
+ * The authenticator is updated in the message authenticator option.
+ * @param secret The shared secret of the server in plaintext.
+ */
+void RadiusPacket::calcmadigest(const char *secret)
+{
+	//Octet		digest[MD5_DIGEST_LENGTH];
+	gcry_md_hd_t	context;
+
+	//Zero out the auth_vector in the received packet.
+	//Then calculate the MD5 HMAC with the shared secret as key.
+
+	memset((this->sendbuffer+this->message_authenticator), 0, 16);
+	//build the hash
+	if (!gcry_control (GCRYCTL_ANY_INITIALIZATION_P))
+	{ /* No other library has already initialized libgcrypt. */
+
+	  gcry_control(GCRYCTL_SET_THREAD_CBS,&gcry_threads_pthread);
+
+	  if (!gcry_check_version (NEED_LIBGCRYPT_VERSION) )
+	    {
+		cerr << "libgcrypt is too old (need " << NEED_LIBGCRYPT_VERSION << ", have " << gcry_check_version (NULL) << ")\n";
+	    }
+	    /* Disable secure memory.  */
+          gcry_control (GCRYCTL_DISABLE_SECMEM, 0);
+	  gcry_control (GCRYCTL_INITIALIZATION_FINISHED);
+	}
+	gcry_md_open (&context, GCRY_MD_MD5, GCRY_MD_FLAG_HMAC);
+	gcry_md_setkey(context, secret, strlen(secret));
+	gcry_md_write(context, this->sendbuffer, this->length);
+	//copy the digest to the packet
+	memcpy(this->sendbuffer+this->message_authenticator, gcry_md_read(context, GCRY_MD_MD5), 16);
+	gcry_md_close(context);
+}
+
 
 /** Returns a pointer to the authenticator field.
  * @return A pointer to the authenticator field.
@@ -653,9 +703,11 @@ pair<multimap<Octet,RadiusAttribute>::iterator,multimap<Octet,RadiusAttribute>::
  * @return A an integer, 0 if the authenticator field is ok, else WRONG_AUTHENTICATOR_IN_RECV_PACKET.
  */
 
-int	RadiusPacket::authenticateReceivedPacket(const char *secret)
+int	RadiusPacket::authenticateReceivedPacket(RadiusServer *server)
 {
+	const char *secret = server->getSharedSecret().c_str();
 	gcry_md_hd_t	context;
+	int res;
 	
 	Octet * cpy_recvpacket;
 	//make a copy of the received packet 
@@ -684,21 +736,53 @@ int	RadiusPacket::authenticateReceivedPacket(const char *secret)
 	gcry_md_open (&context, GCRY_MD_MD5, 0);
 	gcry_md_write(context, cpy_recvpacket, this->recvbufferlen);
 	gcry_md_write(context, secret, strlen(secret));
-	
-	delete[] cpy_recvpacket;
-	
+
 	//compare the received and the built authenticator
-	if (memcmp(this->recvbuffer+4, gcry_md_read(context, GCRY_MD_MD5), 16)!=0)
+	res = memcmp(this->recvbuffer+4, gcry_md_read(context, GCRY_MD_MD5), 16);
+	gcry_md_close(context);
+
+	if (res!=0)
 	{
-		gcry_md_close(context);
+		//Failed
+		delete[] cpy_recvpacket;
 		return WRONG_AUTHENTICATOR_IN_RECV_PACKET;
 	}
-	else
-	{ 
+
+	if (server->getRequireMA() == -1)
+		//We did't know yet whether this server sends Message-Authenticator
+		server->setRequireMA(this->recvbuffer[20] == 80);
+
+	if (server->getRequireMA() == 1)
+	{
+		//This server normally sends Message-Authenticator, check that
+		if (this->recvbufferlen < 20 + 18 || this->recvbuffer[20] != 80 || this->recvbuffer[21] != 18)
+		{
+			//No MA!
+			delete[] cpy_recvpacket;
+			return WRONG_AUTHENTICATOR_IN_RECV_PACKET;
+		}
+
+		//Clear MA hash for computation
+		memset(cpy_recvpacket+20+2, 0, 16);
+		//Hash
+		gcry_md_open (&context, GCRY_MD_MD5, GCRY_MD_FLAG_HMAC);
+		gcry_md_setkey(context, secret, strlen(secret));
+		gcry_md_write(context, cpy_recvpacket, this->recvbufferlen);
+
+		//Compare
+		res = memcmp(this->recvbuffer+20+2, gcry_md_read(context, GCRY_MD_MD5), 16);
 		gcry_md_close(context);
-		return 0;
+
+		if (res != 0)
+		{
+			//Failed
+			delete[] cpy_recvpacket;
+			return WRONG_AUTHENTICATOR_IN_RECV_PACKET;
+		}
 	}
-		
+
+	delete[] cpy_recvpacket;
+	return 0;
 }
 
 
diff --git a/RadiusClass/RadiusPacket.h b/RadiusClass/RadiusPacket.h
index 7b02408..847c23d 100755
--- a/RadiusClass/RadiusPacket.h
+++ b/RadiusClass/RadiusPacket.h
@@ -73,10 +73,13 @@ private:
 	
 	Octet				*sendbuffer;  			/**<Buffer for sending the packet over the network.*/
 	int					sendbufferlen; 			/**<Length of the buffer.*/
+	int					message_authenticator;	/**<Offset of the message authenticator digest, if any.*/
 	Octet				*recvbuffer;  			/**<Buffer for recveing the packet over the network.*/
 	int					recvbufferlen; 			/**<Length of the buffer.*/
 	void            	calcacctdigest(const char *secret); /**Method to generate the hash 
 	for the authenticator in Accounting-Requests.*/
+	void            	calcmadigest(const char *secret); /**Method to generate the hash 
+	for the message authenticator in Access-Requests.*/
 	
 	//private functions
 	void 			getRandom(int len, Octet *num);
@@ -101,7 +104,7 @@ public:
 	
 	int				getCode(void);
 	
-	int				authenticateReceivedPacket(const char *secret);
+	int				authenticateReceivedPacket(RadiusServer *server);
 	
 	pair<multimap<Octet,RadiusAttribute>::iterator,multimap<Octet,RadiusAttribute>::iterator> findAttributes(int type);
 	
diff --git a/RadiusClass/RadiusServer.cpp b/RadiusClass/RadiusServer.cpp
index 27f2011..a90c9b3 100755
--- a/RadiusClass/RadiusServer.cpp
+++ b/RadiusClass/RadiusServer.cpp
@@ -32,13 +32,14 @@
 	 * @param int wait : The time (in seconds) to wait on a response of the radius server.
 	 */
 RadiusServer::RadiusServer(string name, string secret,
-	int authport,  int acctport, int retry, int wait)
+	int authport,  int acctport, int retry, int wait, int requirema)
 {
 	this->acctport=acctport;
 	this->authport=authport;
 	this->name=name;
 	this->retry=retry;
 	this->wait=wait;
+	this->requirema=requirema;
 	this->sharedsecret=secret;
 	
 	
@@ -62,6 +63,7 @@ RadiusServer &RadiusServer::operator=(const RadiusServer &s)
 	this->retry=s.retry;
 	this->acctport=s.acctport;
 	this->authport=s.authport;
+	this->requirema=s.requirema;
 	this->sharedsecret=s.sharedsecret;
 	return (*this);
 }
@@ -186,6 +188,24 @@ void RadiusServer::setWait(int w)
 	}
 }
 
+/** The getter method for the private member requirema
+ * @return A integer 0 when MA Message-Authenticator is not required, 1 if it
+ * is required, -1 if automatically determined from first answer.
+ */
+int RadiusServer::getRequireMA(void)
+{
+	return this->requirema;
+}
+
+
+/** The setter method for the private member requirema
+ * @param ma integer 0 when MA Message-Authenticator is not required, 1 if it
+ */
+void RadiusServer::setRequireMA(int ma)
+{
+	this->requirema=ma;
+}
+
 ostream& operator << (ostream& os, RadiusServer& server)
 {
      os << "\n\nRadiusServer:";
@@ -194,6 +214,7 @@ ostream& operator << (ostream& os, RadiusServer& server)
      os << "\nAccounting-Port: " << server.acctport;
      os << "\nRetries: " << server.retry;
      os << "\nWait: " << server.wait;
+     os << "\nRequireMA: " << server.requirema;
      os << "\nSharedSecret: *******";
  	return os;
  	
diff --git a/RadiusClass/RadiusServer.h b/RadiusClass/RadiusServer.h
index f29f5ca..73d0dbc 100755
--- a/RadiusClass/RadiusServer.h
+++ b/RadiusClass/RadiusServer.h
@@ -36,11 +36,12 @@ private:
 	int 	retry; 				/**< The number of retries how many times a radius ticket is send to the server, if it doesn#t answer.*/
 	string sharedsecret;		/**< The sharedsecret, the maximum space is 16 chars.*/
 	int 	wait;				/**< The time to wait for a response of the server.*/
+	int 	requirema;			/**< Whether to required Message-Authenticator from the server.*/
 
 public:
 	
 	
-	RadiusServer(string name="127.0.0.1",string secret = "", int authport=1812, int acctport=1813, int retry=3, int wait=1);
+	RadiusServer(string name="127.0.0.1",string secret = "", int authport=1812, int acctport=1813, int retry=3, int wait=1, int requirema=-1);
 	~RadiusServer();
 	RadiusServer &operator=(const RadiusServer &);
 	
@@ -62,6 +63,9 @@ public:
 	string getName();
 	void setName(string);
 	
+	void setRequireMA(int);
+	int getRequireMA(void);
+
 	friend ostream& operator << (ostream& os, RadiusServer& server);
 };
 
diff --git a/RadiusClass/exampleconfig b/RadiusClass/exampleconfig
index 6a303d8..ec8e3ec 100644
--- a/RadiusClass/exampleconfig
+++ b/RadiusClass/exampleconfig
@@ -11,6 +11,7 @@ server
 	retry=1
 	wait=1
 	sharedsecret=testpw
+	requirema=auto
 
 }
 
@@ -22,4 +23,5 @@ server
 	retry=1
 	wait=1
 	sharedsecret=xxxx
+	requirema=auto
 }
diff --git a/UserAuth.cpp b/UserAuth.cpp
index 8b44256..525b80d 100755
--- a/UserAuth.cpp
+++ b/UserAuth.cpp
@@ -48,8 +48,10 @@ int UserAuth::sendAcceptRequestPacket(PluginContext * context)
 {
 	list<RadiusServer> * serverlist;
 	list<RadiusServer>::iterator server;
+	std::string		zero("\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0", 16);
 	RadiusPacket		packet(ACCESS_REQUEST);
-	RadiusAttribute		ra1(ATTRIB_User_Name,this->getUsername().c_str()),
+	RadiusAttribute		ra0(ATTRIB_Message_Authenticator,zero),
+				ra1(ATTRIB_User_Name,this->getUsername().c_str()),
 				ra2(ATTRIB_User_Password),
 				ra3(ATTRIB_NAS_Port,this->getPortnumber()),
 				ra4(ATTRIB_Calling_Station_Id,this->getCallingStationId()),
@@ -72,8 +74,13 @@ int UserAuth::sendAcceptRequestPacket(PluginContext * context)
 	
 	if (DEBUG (context->getVerbosity()))
 		cerr << getTime() << "RADIUS-PLUGIN: Build password packet:  password: *****, sharedSecret: *****.\n";
-	
+
 	//add the attributes
+	if(packet.addRadiusAttribute(&ra0))
+	{
+		cerr << getTime() << "RADIUS-PLUGIN: Fail to add attribute ATTRIB_Message_Authenticator.\n";
+	}
+
 	ra2.setValue(this->password);
 	if(packet.addRadiusAttribute(&ra1))
 	{
diff --git a/radiusplugin.cnf b/radiusplugin.cnf
index c7ff9a0..f6988bc 100755
--- a/radiusplugin.cnf
+++ b/radiusplugin.cnf
@@ -97,6 +97,9 @@ server
 	wait=1
 	# The shared secret.
 	sharedsecret=testpw
+	# Whether to require Message-Authenticator (yes) or not (no), or require if first answer included it (auto)
+	# For better security against RadiusBLAST, set to yes once it is confirmed that your radius server always sends it
+	requirema=auto
 }
 
 server
@@ -113,5 +116,8 @@ server
 	wait=1
 	# The shared secret.
 	sharedsecret=testpw
+	# Whether to require Message-Authenticator (yes) or not (no), or require if first answer included it (auto)
+	# For better security against RadiusBLAST, set to yes once it is confirmed that your radius server always sends it
+	requirema=auto
 }