Add support for RADIUS passwords longer than 16 octets

Started by Marko Tiikkajaover 10 years ago3 messages
#1Marko Tiikkaja
marko@joh.to
1 attachment(s)

Hi,

The attached patch adds support for RADIUS passwords longer than 16 octets.

.m

Attachments:

radius_long_passwords.patchtext/plain; charset=UTF-8; name=radius_long_passwords.patch; x-mac-creator=0; x-mac-type=0Download
*** a/src/backend/libpq/auth.c
--- b/src/backend/libpq/auth.c
***************
*** 2168,2173 **** CheckCertAuth(Port *port)
--- 2168,2174 ----
  
  #define RADIUS_VECTOR_LENGTH 16
  #define RADIUS_HEADER_LENGTH 20
+ #define RADIUS_MAX_PASSWORD_LENGTH 128
  
  typedef struct
  {
***************
*** 2241,2247 **** CheckRADIUSAuth(Port *port)
  	radius_packet *receivepacket = (radius_packet *) receive_buffer;
  	int32		service = htonl(RADIUS_AUTHENTICATE_ONLY);
  	uint8	   *cryptvector;
! 	uint8		encryptedpassword[RADIUS_VECTOR_LENGTH];
  	int			packetlength;
  	pgsocket	sock;
  
--- 2242,2250 ----
  	radius_packet *receivepacket = (radius_packet *) receive_buffer;
  	int32		service = htonl(RADIUS_AUTHENTICATE_ONLY);
  	uint8	   *cryptvector;
! 	int			encryptedpasswordlen;
! 	uint8		encryptedpassword[RADIUS_MAX_PASSWORD_LENGTH];
! 	uint8	   *previousxor;
  	int			packetlength;
  	pgsocket	sock;
  
***************
*** 2259,2264 **** CheckRADIUSAuth(Port *port)
--- 2262,2268 ----
  	fd_set		fdset;
  	struct timeval endtime;
  	int			i,
+ 				j,
  				r;
  
  	/* Make sure struct alignment is correct */
***************
*** 2316,2325 **** CheckRADIUSAuth(Port *port)
  		return STATUS_ERROR;
  	}
  
! 	if (strlen(passwd) > RADIUS_VECTOR_LENGTH)
  	{
  		ereport(LOG,
! 				(errmsg("RADIUS authentication does not support passwords longer than 16 characters")));
  		return STATUS_ERROR;
  	}
  
--- 2320,2329 ----
  		return STATUS_ERROR;
  	}
  
! 	if (strlen(passwd) > RADIUS_MAX_PASSWORD_LENGTH)
  	{
  		ereport(LOG,
! 				(errmsg("RADIUS authentication does not support passwords longer than %d characters", RADIUS_MAX_PASSWORD_LENGTH)));
  		return STATUS_ERROR;
  	}
  
***************
*** 2344,2371 **** CheckRADIUSAuth(Port *port)
  	radius_add_attribute(packet, RADIUS_NAS_IDENTIFIER, (unsigned char *) identifier, strlen(identifier));
  
  	/*
! 	 * RADIUS password attributes are calculated as: e[0] = p[0] XOR
! 	 * MD5(secret + vector)
  	 */
! 	cryptvector = palloc(RADIUS_VECTOR_LENGTH + strlen(port->hba->radiussecret));
  	memcpy(cryptvector, port->hba->radiussecret, strlen(port->hba->radiussecret));
! 	memcpy(cryptvector + strlen(port->hba->radiussecret), packet->vector, RADIUS_VECTOR_LENGTH);
! 	if (!pg_md5_binary(cryptvector, RADIUS_VECTOR_LENGTH + strlen(port->hba->radiussecret), encryptedpassword))
! 	{
! 		ereport(LOG,
! 				(errmsg("could not perform MD5 encryption of password")));
! 		pfree(cryptvector);
! 		return STATUS_ERROR;
! 	}
! 	pfree(cryptvector);
! 	for (i = 0; i < RADIUS_VECTOR_LENGTH; i++)
  	{
! 		if (i < strlen(passwd))
! 			encryptedpassword[i] = passwd[i] ^ encryptedpassword[i];
  		else
! 			encryptedpassword[i] = '\0' ^ encryptedpassword[i];
  	}
! 	radius_add_attribute(packet, RADIUS_PASSWORD, encryptedpassword, RADIUS_VECTOR_LENGTH);
  
  	/* Length need to be in network order on the wire */
  	packetlength = packet->length;
--- 2348,2396 ----
  	radius_add_attribute(packet, RADIUS_NAS_IDENTIFIER, (unsigned char *) identifier, strlen(identifier));
  
  	/*
! 	 * RADIUS password attributes are calculated as:
! 	 *   e[0] = p[0] XOR MD5(secret + vector)
! 	 * for the first vector, and then:
! 	 *   e[i] = p[i] XOR MD5(secret + p[i-1])
! 	 * for the following ones (if necessary)
  	 */
! 	encryptedpasswordlen = ((strlen(passwd) + RADIUS_VECTOR_LENGTH - 1) / RADIUS_VECTOR_LENGTH) * RADIUS_VECTOR_LENGTH;
! 	cryptvector = palloc(strlen(port->hba->radiussecret) + RADIUS_VECTOR_LENGTH);
  	memcpy(cryptvector, port->hba->radiussecret, strlen(port->hba->radiussecret));
! 
! 	for (i = 0; i < encryptedpasswordlen; i += RADIUS_VECTOR_LENGTH)
  	{
! 		if (i == 0)
! 		{
! 			/* for the first iteration, we use the Request Authenticator vector */
! 			memcpy(cryptvector + strlen(port->hba->radiussecret), packet->vector, RADIUS_VECTOR_LENGTH);
! 		}
  		else
! 		{
! 			/* .. and for the following iterations the result of the previous XOR */
! 			memcpy(cryptvector + strlen(port->hba->radiussecret), previousxor, RADIUS_VECTOR_LENGTH);
! 		}
! 
! 		if (!pg_md5_binary(cryptvector, strlen(port->hba->radiussecret) + RADIUS_VECTOR_LENGTH, encryptedpassword + i))
! 		{
! 			ereport(LOG,
! 					(errmsg("could not perform MD5 encryption of password")));
! 			pfree(cryptvector);
! 			return STATUS_ERROR;
! 		}
! 
! 		for (j = i; j < i+RADIUS_VECTOR_LENGTH; j++)
! 		{
! 			if (j < strlen(passwd))
! 				encryptedpassword[j] = passwd[j] ^ encryptedpassword[j];
! 			else
! 				encryptedpassword[j] = '\0' ^ encryptedpassword[j];
! 		}
! 		previousxor = encryptedpassword + i;
  	}
! 	pfree(cryptvector);
! 
! 	radius_add_attribute(packet, RADIUS_PASSWORD, encryptedpassword, encryptedpasswordlen);
  
  	/* Length need to be in network order on the wire */
  	packetlength = packet->length;
#2Marko Tiikkaja
marko@joh.to
In reply to: Marko Tiikkaja (#1)
1 attachment(s)
Re: Add support for RADIUS passwords longer than 16 octets

On 2015-08-15 17:55, I wrote:

The attached patch adds support for RADIUS passwords longer than 16 octets.

Improved the coding and comments a bit, new version attached.

.m

Attachments:

radius_long_passwords.v2.patchtext/plain; charset=UTF-8; name=radius_long_passwords.v2.patch; x-mac-creator=0; x-mac-type=0Download
*** a/src/backend/libpq/auth.c
--- b/src/backend/libpq/auth.c
***************
*** 2168,2173 **** CheckCertAuth(Port *port)
--- 2168,2174 ----
  
  #define RADIUS_VECTOR_LENGTH 16
  #define RADIUS_HEADER_LENGTH 20
+ #define RADIUS_MAX_PASSWORD_LENGTH 128
  
  typedef struct
  {
***************
*** 2241,2247 **** CheckRADIUSAuth(Port *port)
  	radius_packet *receivepacket = (radius_packet *) receive_buffer;
  	int32		service = htonl(RADIUS_AUTHENTICATE_ONLY);
  	uint8	   *cryptvector;
! 	uint8		encryptedpassword[RADIUS_VECTOR_LENGTH];
  	int			packetlength;
  	pgsocket	sock;
  
--- 2242,2250 ----
  	radius_packet *receivepacket = (radius_packet *) receive_buffer;
  	int32		service = htonl(RADIUS_AUTHENTICATE_ONLY);
  	uint8	   *cryptvector;
! 	int			encryptedpasswordlen;
! 	uint8		encryptedpassword[RADIUS_MAX_PASSWORD_LENGTH];
! 	uint8	   *md5trailer;
  	int			packetlength;
  	pgsocket	sock;
  
***************
*** 2259,2264 **** CheckRADIUSAuth(Port *port)
--- 2262,2268 ----
  	fd_set		fdset;
  	struct timeval endtime;
  	int			i,
+ 				j,
  				r;
  
  	/* Make sure struct alignment is correct */
***************
*** 2316,2325 **** CheckRADIUSAuth(Port *port)
  		return STATUS_ERROR;
  	}
  
! 	if (strlen(passwd) > RADIUS_VECTOR_LENGTH)
  	{
  		ereport(LOG,
! 				(errmsg("RADIUS authentication does not support passwords longer than 16 characters")));
  		return STATUS_ERROR;
  	}
  
--- 2320,2329 ----
  		return STATUS_ERROR;
  	}
  
! 	if (strlen(passwd) > RADIUS_MAX_PASSWORD_LENGTH)
  	{
  		ereport(LOG,
! 				(errmsg("RADIUS authentication does not support passwords longer than %d characters", RADIUS_MAX_PASSWORD_LENGTH)));
  		return STATUS_ERROR;
  	}
  
***************
*** 2344,2371 **** CheckRADIUSAuth(Port *port)
  	radius_add_attribute(packet, RADIUS_NAS_IDENTIFIER, (unsigned char *) identifier, strlen(identifier));
  
  	/*
! 	 * RADIUS password attributes are calculated as: e[0] = p[0] XOR
! 	 * MD5(secret + vector)
  	 */
! 	cryptvector = palloc(RADIUS_VECTOR_LENGTH + strlen(port->hba->radiussecret));
  	memcpy(cryptvector, port->hba->radiussecret, strlen(port->hba->radiussecret));
! 	memcpy(cryptvector + strlen(port->hba->radiussecret), packet->vector, RADIUS_VECTOR_LENGTH);
! 	if (!pg_md5_binary(cryptvector, RADIUS_VECTOR_LENGTH + strlen(port->hba->radiussecret), encryptedpassword))
  	{
! 		ereport(LOG,
! 				(errmsg("could not perform MD5 encryption of password")));
! 		pfree(cryptvector);
! 		return STATUS_ERROR;
  	}
  	pfree(cryptvector);
! 	for (i = 0; i < RADIUS_VECTOR_LENGTH; i++)
! 	{
! 		if (i < strlen(passwd))
! 			encryptedpassword[i] = passwd[i] ^ encryptedpassword[i];
! 		else
! 			encryptedpassword[i] = '\0' ^ encryptedpassword[i];
! 	}
! 	radius_add_attribute(packet, RADIUS_PASSWORD, encryptedpassword, RADIUS_VECTOR_LENGTH);
  
  	/* Length need to be in network order on the wire */
  	packetlength = packet->length;
--- 2348,2390 ----
  	radius_add_attribute(packet, RADIUS_NAS_IDENTIFIER, (unsigned char *) identifier, strlen(identifier));
  
  	/*
! 	 * RADIUS password attributes are calculated as:
! 	 *   e[0] = p[0] XOR MD5(secret + Request Authenticator)
! 	 * for the first group of 16 octets, and then:
! 	 *   e[i] = p[i] XOR MD5(secret + e[i-1])
! 	 * for the following ones (if necessary)
  	 */
! 	encryptedpasswordlen = ((strlen(passwd) + RADIUS_VECTOR_LENGTH - 1) / RADIUS_VECTOR_LENGTH) * RADIUS_VECTOR_LENGTH;
! 	cryptvector = palloc(strlen(port->hba->radiussecret) + RADIUS_VECTOR_LENGTH);
  	memcpy(cryptvector, port->hba->radiussecret, strlen(port->hba->radiussecret));
! 
! 	/* for the first iteration, we use the Request Authenticator vector */
! 	md5trailer = packet->vector;
! 	for (i = 0; i < encryptedpasswordlen; i += RADIUS_VECTOR_LENGTH)
  	{
! 		memcpy(cryptvector + strlen(port->hba->radiussecret), md5trailer, RADIUS_VECTOR_LENGTH);
! 		/* .. and for subsequent iterations the result of the previous XOR (calculated below) */
! 		md5trailer = encryptedpassword + i;
! 
! 		if (!pg_md5_binary(cryptvector, strlen(port->hba->radiussecret) + RADIUS_VECTOR_LENGTH, encryptedpassword + i))
! 		{
! 			ereport(LOG,
! 					(errmsg("could not perform MD5 encryption of password")));
! 			pfree(cryptvector);
! 			return STATUS_ERROR;
! 		}
! 
! 		for (j = i; j < i+RADIUS_VECTOR_LENGTH; j++)
! 		{
! 			if (j < strlen(passwd))
! 				encryptedpassword[j] = passwd[j] ^ encryptedpassword[j];
! 			else
! 				encryptedpassword[j] = '\0' ^ encryptedpassword[j];
! 		}
  	}
  	pfree(cryptvector);
! 
! 	radius_add_attribute(packet, RADIUS_PASSWORD, encryptedpassword, encryptedpasswordlen);
  
  	/* Length need to be in network order on the wire */
  	packetlength = packet->length;
#3Magnus Hagander
magnus@hagander.net
In reply to: Marko Tiikkaja (#2)
Re: Add support for RADIUS passwords longer than 16 octets

On Tue, Aug 18, 2015 at 11:36 PM, Marko Tiikkaja <marko@joh.to> wrote:

On 2015-08-15 17:55, I wrote:

The attached patch adds support for RADIUS passwords longer than 16
octets.

Improved the coding and comments a bit, new version attached.

Looks good to me. Applied, thanks!

As a note - psql truncates passwords at 100 characters when it reads it
from the problem (in the connstr it's fine). Not sure we care enough to
change that, though - I doubt it's a very common usecase for psql...

--
Magnus Hagander
Me: http://www.hagander.net/
Work: http://www.redpill-linpro.com/