aaa.c revision 7c478bd95313f5f23a4c958a745db2134aa03244
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (the "License"). You may not use this file except in compliance
* with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 1999-2003 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
/*
* file: aaa.c
*
* This file processes the Diameter AAA requests from mipagent.
* This file also contains the routines used to parse and process the
* Diameter messages.
*
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <syslog.h>
#include "mip.h"
#include "agent.h"
#include "setup.h"
#include "hash.h"
#include "aaa.h"
static int gbl_hashInitialized = 0;
char gbl_aaaHost[MAX_SERVER_NAME_LEN];
extern char maNai[];
extern HashTable mipAgentHash;
extern HashTable faVisitorHash;
extern AAA_Protocol_Code aaaProtocol;
extern ForeignAgentCounters faCounters;
static pthread_t aaaThreadId = 0;
/* External prototypes . . . this should be somewhere else -- WORK:todo */
extern int hexdump(char *, unsigned char *, int);
extern void FreeMessageHdr(MessageHdr *);
extern void enableService();
/*
* Internal Prototypes
*/
static void *mainAAAThread();
#ifdef TEST_DIAMETER
static uint32_t aaaGenerateSpi();
#endif
/* Not prototyped in .h file for cyclic dependency problems */
extern int logVerbosity; /* WORK -- This should be in a .h file. PRC? */
extern struct hash_table maAdvConfigHash;
/*
* Function: aaaAddAvp
*
* Arguments: unsigned char *dest, size_t destLen, uint32_t avpCode,
* void *data, size_t dataLen
*
* Description: This function will build an AVP perform all byte-ordering/
* copying operations, and will return the length of the
* destination AVP.
*
* Returns: size_t (length of block added, zero on error)
*/
static size_t
{
/* First, check to make sure it will fit */
"avpSize = %d, destSize = %d, avpCode = %d)",
return (0);
}
/* Now, build the avp */
/* Note: dataLen = size of header */
/* LINTED E_BAD_PTR_CAST_ALIGN */
switch (avpCode) {
/* These fields require no mangling */
case MOBILE_NODE_NAI:
case FOREIGN_AGENT_NAI:
case REGISTRATION_REQUEST:
case MOBILE_NODE_RESPONSE:
case REGISTRATION_REPLY:
case MN_FA_KEY:
case FA_HA_KEY:
case HA_FA_KEY:
case FA_MN_KEY:
case MN_HA_KEY:
case HA_MN_KEY:
case MN_FA_CHALLENGE_VALUE:
break;
/* These fields require mangling (They're 32 bit numbers) */
case MOBILE_NODE_HOME_ADDRESS:
case HOME_AGENT_ADDRESS:
case RESULT_CODE:
case MN_FA_SPI:
case FA_HA_SPI:
case SESSION_TIMEOUT:
case MN_HA_SPI:
case SESSION_TIMEOUT_1:
case SESSION_TIME:
case FOREIGN_AGENT_ADDRESS:
case IS_FROM_HA:
case MN_AAA_SPI:
case REV_TUN:
case MN_HANDLE:
case RELEASE_INDICATOR:
"length of %d, not %d!", sizeof (uint32_t),
dataLen);
return (0);
}
break;
default: /* Error! */
return (0);
} /* switch (avpCode) */
} /* aaaAddAvp */
/*
* Function: addNaiToHash
*
* Arguments: char *mnNAI, size_t mnNAILen,
* unsigned char *mnChallenge, uint32_t mnChallengeLen,
* ipaddr_t homeAddress, ipaddr_t homeAgentAddress,
* void *messageHdr
*
* Description: This function will add the nai to our local hash. It
* checks to make sure the add was successful (value unique)
* It is called from the foreign agent or home agent when it first
* receives a NAI that it has not seen before. The has entry is
* used to store any interim data that is needed for either
* the home or foreign agents.
*
* Returns: int (zero on success)
*/
static int
void *messageHdr)
{
AAA_HashEntry *p;
int rc;
/* Allocate and initialize HashEntry */
if (!p) {
return (-1);
}
(void) memset(p, 0, sizeof (AAA_HashEntry));
p->homeAddress = homeAddress;
p->timeOut = 0;
p->handle = 0;
p->messageHdr = messageHdr;
/* Now link it. */
if (rc != 0) {
"ERROR: Unable to add entry to hash! (unique?)");
free(p);
return (-2);
}
return (0);
} /* addNaiToHash */
/*
* Function: removeFromHash
*
* Arguments: char *mnNAI, size_t mnNAILen
*
* Description: This function will delete the nai from our local hash.
* It is called in the foreign agent when the CloseSession
* Answer returns. It should be called in the home agent when
* the Accounting Stop arrives.
*
* Returns: int (zero on success)
*/
static int
{
int rc;
AAA_HashEntry *p;
/*
* Lock it for writing.
*/
NULL);
if (!p) {
return (-1);
}
/* And, finally, free our data */
/* WARNING: Check return value xxx WORK */
(void) rw_unlock(&p->aaaNodeLock);
(void) rwlock_destroy(&p->aaaNodeLock);
free(p);
return (rc);
} /* removeFromHash */
/*
* Function: aaaUpdateHash
*
* Arguments: AAA_Packet *packet, char *mnNAI, size_t mnNAILen
*
* Description: This function will update the hash information for the
* given node. It gets called when any response comes from the
* AAA server. The only field that is currently updated is the
* handle. The handle is ONLY updated if our current copy is a
* zero.
*
* Also, since we probably sent out accounting messages with a
* handle of zero, it accepts any response that contains a handle
* of zero (but does not update our local information).
*
* Returns: int
*/
static AAA_HashEntry *
{
AAA_HashEntry *p;
/*
* Since we only have one thread reading from the socket, and
* that thread is the only thread that will update these data
* items, no locking is necessary.
*/
if (p) {
if (p->handle) {
/*
* We already have a handle .. . check it
* But, it's ok if we get a zero . . .just means that
* the sender doesn't know that the handle is set yet.
*/
/* Error! */
"Error: incoming handle does not match"
" handle in hash: (%d <> %d)\n",
p->handle);
(void) rw_unlock(&p->aaaNodeLock);
return (NULL);
}
} else {
/* Since we don't have a handle on file, update it */
}
} else {
return (NULL);
}
return (p);
} /* aaaUpdateHash */
/*
* Function: aaaLookupHandle
*
* Arguments: unsigned char *mnNAI, size_t mnNAILen
*
* Description: This function will lookup the NAI in the hash, and will return
* the handle associated with it.
*
* Returns: int32_t (-1 on error)
*/
static int32_t
{
AAA_HashEntry *p;
/*
* Since we only have one thread reading from the socket, and
* that thread is the only thread that will update these data
* items, no locking is necessary.
*/
if (p) {
(void) rw_unlock(&p->aaaNodeLock);
return (handle);
} else {
return (-1);
}
} /* aaaLookupHandle */
/*
* Function: aaaFindAvpInt
*
* Arguments: AAA_Packet *packet, AAA_AVPCode avpCode, int32_t *dest
*
* Description: This routine will return the long specified by avpCode.
* On error, it will return _B_FALSE.
*
* Returns: boolean_t (B_FALSE on error)
*/
static boolean_t
{
if (!avp)
return (_B_FALSE);
/* Make our static copy */
/* subtract the header size */
return (_B_FALSE);
}
return (_B_TRUE);
} /* aaaFindAvpInt */
/*
* Function: aaaFindAvpPtr
*
* Arguments: AAA_Packet *packet, AAA_AVPCode avpCode, size_t *length
*
* Description: This routine will return the data specified by avpCode.
* It will set the length to the length of the data.
* On error, it will return null.
*
* Returns: uint32_t (defaultValue on error)
*/
static void *
{
*length = 0; /* Initialize this first */
if (!avp)
return (NULL);
/* Make our static copy */
/* subtract the header size */
} /* aaaFindAvpPtr */
/*
* Function: aaaFindAvpByCode
*
* Arguments: packet containing avps, avpCode
*
* Description: This function will walk through the AVPS and return a
* pointer to the avp that matches the given code.
* This function is not efficient, so if the number of
* avps expected grows over 15 or so, we should index them
* once, then call an indexed lookup.
*
* Returns: pointer to the avp, or NULL
*
*/
static AAA_AVP *
{
unsigned char *buffer;
/* First, get the length of the packet, so we don't overshoot */
/* Now, set buffer to point to the start of the AVPs */
currentPosition = 0;
/* LINTED E_BAD_PTR_CAST_ALIGN */
/*
* now avp points to the right place. copy the
* data somewhere byte aligned.
*/
/* Bad packet */
return (NULL);
}
/* We found it! return our position */
return (avp);
}
/* That wasn't it, so move on to the next one */
}
/* We didn't find it! */
return (NULL);
} /* aaaFindAvpByCode */
/*
* Function: aaaCreateKey
*
* Arguments: AAA_Packet *packet, int spiTag, int keyTag
*
* Description: This routine will create the SA for the given key / SPI.
* If the SA already exists, and it is dynamic, then replace the
* key information.
*
* Returns: int (zero on success)
*/
int
{
"Unable to create SA for Mobile Node (SPI %d)\n", spi);
return (-1);
}
/*
* The Create function ends up locking the node, so
* we need to free it.
*/
return (0);
} /* aaaCreateKey */
/*
* Function: aaaCreateAgent
*
* Arguments: AAA_Packet *packet, int addrTag, uint32_t spi,
* uint32_t SessionTimeout
*
* Description: This function will create an agent from the data in the
* packet. It is used to create bothe the foreign and home
* agent entries. (The FA would create a corresponding HA entry,
* and vice versa)
*
* Returns: void
*/
static void
{
/* Check the Agent Address */
/* BAD error . . . malformed packet */
return;
}
/*
* On the Foreign Agent, we will need to create the Mobility
* Agent entry so that we can find the Home Agent's SPI
* when forwarding messages to it.
*/
/* Find it. */
LOCK_WRITE, NULL, 0, 0, 0);
/* Error! */
return;
}
}
/*
* Now, make sure the entry is a dynamic one, and update the SPI
*/
if (maEntry->maIsEntryDynamic) {
} else {
"Error: received an SPI that does not match static"
" Agent entry");
}
}
/*
* The Create function ends up locking the node, so
* we need to free it.
*/
} /* aaaCreateAgent */
/*
* Function: checkResultCode
*
* Arguments: AAA_Packet *packet
*
* Description: This routine checks the ResultCode field of the packet, and
* returns it (or -1 or -2 on error)
*
* Returns: int (resultCode, -1 or -2 on error)
*/
static int
{
/* BAD error . . . malformed packet */
}
return (resultCode);
} /* checkResultCode */
/*
* Function: startAAATaskThread
*
* Arguments:
*
* Description: This function starts our AAA thread.
*
* Returns: int (zero on success)
*/
int
{
int result;
if (result) {
return (-1);
}
/*
* We now create a thread to deal with all periodic task.
*/
(void *(*)()) mainAAAThread, (void *)NULL);
if (result) {
return (-1);
}
/*
* In order for system resources the be properly cleaned up,
* we need to detach the thread. Otherwise, we need to wait for
* a pthread_join(), which we do not want.
*/
if (result) {
return (-1);
}
return (0);
} /* StartAAATaskThread */
/*
* Function: killAAATaskThread
*
* Arguments:
*
* Description: This function kills our AAA task thread.
*
* Returns: int
*/
int
{
int result;
if (aaaThreadId) {
/*
* Next we need to kill the dispatching thread.
*/
if (result) {
/*
* Well, there's not much we can do here..
*/
return (-1);
}
}
return (0);
} /* killAAATaskThread */
/*
* Function: mainAAAThread
*
* Arguments:
*
* Description: This is our main AAA thread. It receives the messages, and
* processes them based on command code.
*
* Returns: void *
*/
static void *
{
unsigned char buffer[MAX_TCP_LEN];
char *mobileNodeNAI;
int rc;
/* The below is an endless loop that will not give any lint warnings */
for (; ; ) {
mipverbose(("readTCPPacket Failed errno is %d\n",
errno));
(void) sleep(1);
/*
* Clean up MN binding & visitor entries and tunnels
* when a down link between mobility agent and AAA
* infrastructure is down. readTCPPacket will only
* return 0 when this link is initally down. A
* reconnection try will result in rc being -1 so
* docleanup will only be called once - when link
* goes down.
*/
if (rc == 0) {
mipverbose(("AAA readTCPPacket returned 0\n"));
docleanup();
/*
* need to reconnect
*/
(void) close(gbl_TCPSocket);
gbl_TCPSocket = -1;
}
continue;
}
/* LINTED E_BAD_PTR_CAST_ALIGN */
/*
* First, lookup this in our hash, and update the handle
* if necessary.
*/
/* Malformed packet */
"Error: bad packet(no MOBILE_NODE_NAI)");
continue;
}
switch (commandCode) {
/* Update the handle in the hash */
"Error: Received packet for "
"non-pending NAI (ANSWER)");
continue;
}
/* Check for a good result code */
if (resultCode != 0) {
mipverbose(("result code was %d\n",
resultCode));
/*
* Remember we had preserved the messageHdr
* if Radius used, let's free it now.
*/
if (aaaProtocol == RADIUS) {
}
}
if (aaaProtocol == RADIUS) {
/*
* MOBILE_IP_OPEN_SESSION_ANSWER && RADIUS:
*
* IS_FROM_HA must be present so that
* the mobility agent will know that
* this is for the Home Agent vs the
* Foreign Agent.
*
* In this case the IS_FROM_HA means
* that this packet is for the Home
* Agent (if the value of the AVP is 1).
* If the packet is for the Home Agent,
* call processOpenSessionAnswerRadiusHA.
* If the packet is for the Foreign Agent,
* callprocessOpenSessionAnswerRadius().
*/
" (no IS_FROM_HA)");
continue;
}
} else { /* FA */
NaiEntry);
}
} else {
}
break;
/* Check the handle in the hash */
"Error: Received packet for "
"non-pending NAI");
continue;
}
/* Check the result code */
if (resultCode != 0) {
" resultCode = %d", commandCode,
}
if (commandCode == MOBILE_IP_ACCOUNTING_STOP_ANSWER) {
}
break;
/* Check the handle */
mobileNodeNAILen)) == NULL) {
"Error: Received packet for "
"non-pending NAI");
continue;
}
/* Check the result code */
if (resultCode != 0) {
"Error: ACCOUNTING_START_ANSWER"
" resultCode = %d", resultCode);
}
/* And finally, remove the hash entry */
break;
if (aaaProtocol != DIAMETER) {
"Error: "
"MOBILE_IP_OPEN_SESSION_INDICATION "
"AAA protocol should be DIAMETER not "
"%d", aaaProtocol);
break;
}
/*
* We get this message from diameter when we are
* acting as a home agent.
*/
break;
/* We should not get these. */
#ifdef TEST_DIAMETER
/*
* We are faking a diameter connection. Accept the
* OPEN_SESSION, and respond with an OPEN_SESSION
* response. Generate keys too.
*/
break;
/*
* We are faking a diameter connection. Accept the
* OPEN_SESSION_INDICATION_RESPONSE as the foreign
* agent, and convert it to a OpenSessionAnswer
*/
break;
#else
#endif
/*
* AAA server can force mipagent to de-register
* a MN. No need to send a Accounting Stop, since
* AAA already knows this MN is de-registering.
* This message applies to both AAAH and AAAF.
*/
result = 1;
"ERROR: bad packet "
"(no agent addresses or homeaddr)");
} else {
0, *homeAgentaddr);
}
if (result != 0) {
"while replying to AAA. ");
}
break;
default:
"Error: Received invalid commandCode <%d>",
}
}
/* LINTED E_STMT_NOT_REACHED */
return (NULL);
} /* mainAAAThread */
#ifdef TEST_DIAMETER
/*
* Function: processOpenSessionRequest
*
* Arguments: AAA_Packet *packet, char *mnNAI, size_t mnNAILen
*
* Description: This is a DEBUG ONLY routine used to test the code without
* DIAMETER. It should normally be conditionally compiled
* OUT of the code. This routine takes an OpenSessionRequest,
* and generates a OpenSessionIndication. It then calls
* processOpenSessionIndication.
* THIS ROUTINE DISTRUCTIVELY MODIFIES packet IN PLACE
*
* Returns: void
*/
static void
{
unsigned char buffer[MAX_TCP_LEN];
unsigned char *regReq;
char *faNai;
unsigned char *mnResponse;
unsigned char MNFAKey[MAX_GENERATE_KEY_LEN];
unsigned char FAHAKey[MAX_GENERATE_KEY_LEN];
unsigned char MNHAKey[MAX_GENERATE_KEY_LEN];
/* These are encrypted versions of the above */
unsigned char HAFAKey[MAX_GENERATE_KEY_LEN];
unsigned char FAMNKey[MAX_GENERATE_KEY_LEN];
unsigned char HAMNKey[MAX_GENERATE_KEY_LEN];
/* FOREIGN_AGENT_NAI */
return;
}
/* REGISTRATION_REQUEST */
return;
}
/* NUMBER_OF_CHALLENGE_BYTES_IN_RR */
if (!aaaFindAvpInt(packet,
"NUMBER_OF_CHALLENGE_BYTES_IN_RR)");
return;
}
/* MOBILE_NODE_RESPONSE */
if (!mnResponse || !mnResponseLen) {
return;
}
/* MOBILE_NODE_HOME_ADDRESS */
&homeAddress)) {
"ERROR: bad packet (no MOBILE_NODE_HOME_ADDRESS)");
return;
}
/* HOME_AGENT_ADDRESS */
&homeAgentAddress)) {
return;
}
/* FOREIGN_AGENT_ADDRESS */
&foreignAgentAddress)) {
"ERROR: bad packet (no FOREIGN_AGENT_ADDRESS)");
return;
}
/* ******** Build Fake Packet ******** */
/*
* Generate our keys
*/
/* These should be computed. (MD5?) */
MNFASpi = aaaGenerateSpi();
FAHASpi = aaaGenerateSpi();
MNHASpi = aaaGenerateSpi();
/* Build our response (An OpenSessionIndication) */
/* LINTED E_BAD_PTR_CAST_ALIGN */
length = sizeof (AAA_Packet);
/* Mobile Node NAI */
/* Foreign Agent NAI */
/* Foreign Agent Address */
/* Registration Request Packet */
/* Mobile Node Home Address */
/* Home Agent Address */
/* fa-ha spi */
/* FA-HA key */
/* HA-FA Key */
/* MN-FA SPI */
/* MN-FA key */
/* FA-MN Key */
/* MN-HA SPI */
/* MN-HA key */
/* HA-MN Key */
/* Send a fake session time out */
sessionTimeout = 60;
} /* processOpenSessionRequest */
/*
* Function: processOpenSessionIndicationResponse
*
* Arguments: AAA_Packet *packet
*
* Description: This function will handle converting packets from
* OpenSessionIndicationResponses into OpenSessionAnswers,
* for testing without diameter. (This is the foreign agent
* side catching the home agent's response.)
*
* Returns: void
*/
static void
{
mipverbose(("Processing OpenSessionIndicationResponse (DEBUG)\n"));
} /* processOpenSessionIndicationResponse */
#endif /* TEST_DIAMETER */
/*
* Setup the basic message handling fields. I think that we
* could possibly re-use ifEntry here as a pointer back to the
* AAA server. Otherwise, we can create a new field in the
* message header structure.
*/
static void
{
} /* aaaSetupMessageHdr */
/*
* Function: processOpenSessionAnswer
*
* Arguments: AAA_Packet *packet
*
* Description: This function handles the message to the foreign node, from
* DIAMETER.
*
* Returns: void
*/
static void
/* LINTED E_FUNC_ARG_UNUSED */
{
uint32_t sessionTimeout = 0;
unsigned char *regResponse;
/* Check the SessionTimeout */
/* BAD error . . . malformed packet */
}
/* Now, make sure we have a response field */
if (!regResponse) {
}
/* FA-HA Spi */
}
/* FA-HA Key */
if (!FAHAKey || !FAHAKeyLen) {
}
/* MN-FA Spi */
}
/* MN-FA Key */
if (!FAMNKey || !FAMNKeyLen) {
}
/* Session Timeout */
/* BAD error . . . malformed packet */
}
/*
* Create our keys
*/
if ((FAMNKeyLen != 0) &&
}
if ((FAHAKeyLen != 0) &&
}
/*
* If we don't already have a message header,
* allocate one.
*/
if (messageHdr == NULL) {
"Unable to allocate a message header");
return;
}
}
/*
* Dispatch the message!
*/
(void) dispatchMsgToThread(&messageHdr);
} /* processOpenSessionAnswer */
/*
* Function: processOpenSessionAnswerRadius
*
* Arguments: AAA_Packet *packet, char *mnNAI, size_t mnNAILen
*
* Description: This function handles the message to the FA, from
* RADIUS.
*
* Returns: void
*/
static void
{
int code = 0;
mipverbose(("Processing OpenSessionAnswer for RADIUS (FA Side)\n"));
/*
* We are here after seeing that the FA received a positive response
* from the RADIUS server. That completes the auth check for the MN
* which is attempting to register through this FA. Now we need to go
* back and forward this registration request to HA.
*
* We shouldn't get any lifetime info from Radius server. FA has been
* advertising the lifetime value it's willing to support. It has also
* already rejected if MN was asking more than that. Now Radius server
* coming along and forcing FA to lower the lifetime doesn't make sense,
* and not compliant with RFC2002.
*
* If we use this function for HA, it's a different story. Radius has
* the authority to dictate lifetime.
*/
/*
* Let's make sure this is not a replay of an already recevied
* OpenSessionAnswer. If we had freed the msgHdr before, that means
* this is a replay (not necessarily an attack :).
*/
mipverbose(("This was a repeat OpenSessionAnswer\n"));
return;
}
/* REV_TUN */
"ERROR: OpenSessionAnswer (no or bad REV_TUN)");
if (revtun != 0) {
"ERROR: OpenSessionAnswer bad REV_TUN value %d\n",
revtun);
} else {
"ERROR: OpenSessionAnswer (no REV_TUN)");
}
return;
}
/*
* Check if revtun value matches with T bit value of request.
* FAprocessRegRequest already checks first whether
* FA supports Reverse tunnel when there is a request
* with T bit. According to IS-835, the rule for radius:
* revtun = 0; Reverse tunnel is not required
* revtun = 1; Reverse tunnel is required
* So, if FA is reverse tunnel capable, we allow reverse tunnel
* when revtun = REVTUN_NOTREQUIRED(0) and MN requests reverse tunnel
*/
/* LINTED */
revtun == REVTUN_REQUIRED) {
/*
* AAA recommends reverse tunnel for this MN, it must
* request reverse tunnel in registration request
*/
mipverbose(("Mobile node must request reverse tunnel\n"));
}
/* Release Read lock now */
if (code == FA_REVERSE_TUNNEL_REQUIRED) {
/* Cleanup the pending visitor entry now */
mkPendingFAVEHashLookup, _B_FALSE, 0, 0);
if (visitor_entry != NULL) {
/* Pending entry found */
}
return;
}
/*
* Now we are done with the messageHdr stored in NaiEntry, let's
* free it, as we promised before.
*/
} /* processOpenSessionAnswerRadius */
/*
* Function: processOpenSessionAnswerRadiusHA
*
* Arguments: AAA_Packet *packet, char *mnNAI, size_t mnNAILen, uint32_t
* resultCode
*
* Description: This function handles the message to the HA, from
* RADIUS client.
*
* Returns: void
*/
/* ARGSUSED */
static void
{
uint32_t sessionTimeout = 0;
unsigned char *MNHAKey;
int code = 0;
/*
* AVPs expected to receive:
*
* SESSION_TIMEOUT
* MN_HA_SPI
* MN_HA_KEY
* HOME AGENT ADDRESS
* Mobile Node Home Address
*/
mipverbose(("Processing OpenSessionAnswer for RADIUS (HA Side)\n"));
/*
* Let's make sure this is not a replay of an already recevied
* OpenSessionAnswer. If we had freed the messsageHdr before, that means
* this is a replay (not necessarily an attack :).
*/
if (messageHdr == NULL) {
mipverbose(("This was a repeat OpenSessionAnswer\n"));
return;
}
/* Check the SessionTimeout */
/* BAD error . . . malformed packet */
}
/* MN-HA Spi */
}
/* MN-HA Key */
if (!MNHAKey || !MNHAKeyLen) {
}
/* REV_TUN */
"ERROR: bad packet (no or bad REV_TUN from RADIUS)");
if (revtun != 0) {
"ERROR: REV_TUN value %d\n", revtun);
} else {
"ERROR: (no REV_TUN from RADIUS)");
}
} else {
mipverbose(("processOpenSessionAnswerHA:"
"HA received reverse tunnel value from RADIUS %d\n",
revtun));
/* Found valid revtun entry */
/* LINTED */
revtun == REVTUN_REQUIRED) {
mipverbose(("processOpenSessionAnswerHA:"
"T bit required in regRequest\n"));
}
}
}
if (code == 0)
messageHdr->mnAAASPI = 0;
/*
* Dispatch the message!
*/
(void) dispatchMsgToThread(&messageHdr);
} /* processOpenSessionAnswerRadiusHA */
/*
* Function: processOpenSessionIndication
*
* Arguments: AAA_Packet *packet, char *mnNAI, size_t mnNAILen
*
* Description: This routine will handle the OPEN_SESSION_INDICATION message.
* (The message from Diameter to the Home Agent)
*
* Returns: void
*/
static void
{
unsigned char *regReq;
char *faNai;
unsigned char *MNFAKey;
unsigned char *MNHAKey;
/* These are encrypted versions of the above */
unsigned char *HAFAKey;
unsigned char *HAMNKey;
/*
* Initialize hash on HA side.
*/
/* Check to see if we are initialized */
if (!gbl_hashInitialized) {
gbl_hashInitialized = 1;
}
/* FOREIGN_AGENT_NAI */
return;
}
/* REGISTRATION_REQUEST */
return;
}
/* MOBILE_NODE_HOME_ADDRESS */
(int *)&homeAddress)) {
"ERROR: bad packet (no MOBILE_NODE_HOME_ADDRESS)");
return;
}
/* HOME_AGENT_ADDRESS */
if (!aaaFindAvpInt(packet,
HOME_AGENT_ADDRESS, (int *)&homeAgentAddress)) {
return;
}
/* FOREIGN_AGENT_ADDRESS */
(int *)&foreignAgentAddress)) {
"ERROR: bad packet (no FOREIGN_AGENT_ADDRESS)");
return;
}
/* FA-HA Spi */
"FA_HA_SPI)");
return;
}
/* HA-FA Key */
if (!HAFAKey || !HAFAKeyLen) {
return;
}
/* MN-FA Key */
if (!MNFAKey || !MNFAKeyLen) {
return;
}
/* MN-HA Spi */
return;
}
/* MN-HA Key */
if (!MNHAKey || !MNHAKeyLen) {
return;
}
/* HA-MN Key */
if (!HAMNKey || !HAMNKeyLen) {
return;
}
/* Session Timeout */
/* BAD error . . . malformed packet */
return;
}
/*
*
* Add NAI to hash on home agent side so accounting messages
* can be generated.
*/
/*
* Create our keys
*/
return;
}
return;
}
/*
* If we don't already have a message header,
* allocate one.
*/
if (messageHdr == NULL) {
"Unable to allocate a message header");
return;
}
}
/* Don't worry about byte ordering this. */
/* Copy our fa NAI */
"Unable to allocate a faNAI in message header");
return;
}
/*
* Dispatch the message!
*/
(void) dispatchMsgToThread(&messageHdr);
} /* processOpenSessionIndication */
/*
* Function: processAuthFailure
*
* Arguments:
* MessageHdr * - pointer to message hdr
* mnNAI - nai
* mnNAILen - nai len
* result - result code returned by RADIUS
*
* Description: This function will handle a failure. (will send an error)
*
* Returns: void
*/
static void
{
switch (result) {
break;
break;
break;
case MIP_REASON_UNSPECIFIED:
default:
break;
}
} /* processAuthFailure */
#ifdef TEST_DIAMETER
static void
{
int i;
/* Seed the random number generator once */
if (initialized == _B_FALSE) {
}
if (keyLen % 4) {
"ERROR: Key length must be a multiple of 4 (len = %d)",
keyLen);
}
/* LINTED E_BAD_PTR_CAST_ALIGN */
/* Build the key, 4 bytes at a time */
for (i = 0; i < (keyLen / 4); i++) {
}
} /* aaaGenerateKey */
static uint32_t
{
return (SPI++);
} /* return a psuedo random spi */
#endif /* TEST_DIAMETER */
/*
* Function: aaaSendRegistrationReply
*
* Arguments: MessageHdr *messageHdr
*
* Description: This function will lookup the relivant data, and send
* a response back to DIAMETER. This function is called from
* the HomeAgent, in response to a OpenSessionIndication.
*
* Returns: int
*/
int
{
unsigned char buffer[MAX_TCP_LEN];
int resultCode = 0; /* WORK -- pass this in */
/* Check to see if we are initialized */
if (!gbl_hashInitialized) {
gbl_hashInitialized = 1;
}
/* Build the message */
/* LINTED E_BAD_PTR_CAST_ALIGN */
length = sizeof (AAA_Packet);
/* Add our AVPs */
/* Mobile Node NAI */
/* Foreign Agent NAI */
/* Registration Reply */
replyLen);
/* Mobile Node Home Address */
sizeof (ipaddr_t));
/* Home Agent Address */
sizeof (ipaddr_t));
/* Session Timeout */
sizeof (uint32_t));
/* Result Code */
sizeof (uint32_t));
} /* sendRegistrationReply */
/*
* Function: AAAAuthenticateRegReq
*
* Arguments:
*
* Description: This function is called from the foreign agent. It is the
* first function that is called for a given mobile node, so
* we do all startup code here. (We add the entry to the
* hash.)
*
* Returns: int
*
*/
int
{
unsigned char buffer[MAX_TCP_LEN];
/* Check to see if we are initialized */
if (!gbl_hashInitialized) {
}
/* Build the message */
/* LINTED E_BAD_PTR_CAST_ALIGN */
length = sizeof (AAA_Packet);
/* Add our AVPs */
/*
* Radius server needs to know if this is coming from HA,
* in which case it'll send back the MN-HA key.
*/
if (aaaProtocol == RADIUS) {
}
/* Mobile Node NAI */
/* Foreign Agent NAI */
/* Foreign Agent Address */
/* Registration Request Packet */
/* Challenge Bytes */
&mnChallengeLen, sizeof (uint32_t));
/* Mobile Node Response */
/* Mobile Node Home Address */
/* Home Agent Address */
/* MN-AAA SPI */
/* Radius needs MN-FA Challenge Value for authentication */
if (aaaProtocol == RADIUS) {
}
/* MN_HANDLE (only send if from FA in which case inIfindex is nonzero */
if (inIfindex != 0) {
}
/* Add entry to hash */
messageHdr)) {
/* Error! */
return (-1);
}
} /* AAAAuthenticateRegReq */
/*
* Function: sendCloseSession
*
* Arguments: AAA_Packet *packet, unsigned char *nai, size_t naiLen
*
* Description: This function will send the CLOSE_SESSION message
* to AAA server. It is called by the main thread when an
* MOBILE_IP_ACCOUNTING_STOP_ANSWER is received.
*
* Returns: int
*/
static int
{
unsigned char buffer[MAX_TCP_LEN];
char *faNai;
/* Lookup the NAI to make sure it exists. */
if (handle < 0) {
return (handle);
}
/* Retrieve Our Fields */
if (!faNai) {
return (-1);
}
(int *)&homeAddress)) {
return (-1);
}
(int *)&homeAgentAddress)) {
return (-1);
}
return (-1);
}
/* Build the message */
/* LINTED E_BAD_PTR_CAST_ALIGN */
length = sizeof (AAA_Packet);
/* Add our AVPs */
/* Mobile Node NAI */
/* Foreign Agent NAI */
/* Mobile Node Home Address */
/* Home Agent Address */
/* Mobile Node Session Time */
} /* sendCloseSession */
static int
{
unsigned char buffer[MAX_TCP_LEN];
/* LINTED */
length = sizeof (AAA_Packet);
/* Add our AVPs */
/* Mobile Node NAI */
/* Result code */
&resultCode, sizeof (uint32_t));
}
/*
* Function: sendAccountingRecord
*
* Arguments: commandCode record, unsigned char *mnNAI, size_t mnNAILen,
* ipaddr_t homeAddr, ipaddr_t coaAddr,
* ipaddr_t homeAgentAddr, int32_t sessionLifetime)
*
* Description: This function will send an accounting start record.
* ToDo: coaAddr is specified in this function calls' prototype,
* but it is not in the diameter api protocol. (PRC TODO)
*
* Returns: int
*
*/
int
/* LINTED E_FUNC_ARG_UNUSED */
{
unsigned char buffer[MAX_TCP_LEN];
/* Lookup the NAI to make sure it exists. */
if (handle < 0) {
return (handle);
}
switch (code) {
/* These codes are good */
break;
/* Everything else is an error */
default:
"sendAccountingRecord (%d)", code);
return (-1);
} /* switch code */
/* Build the message */
/* LINTED E_BAD_PTR_CAST_ALIGN */
length = sizeof (AAA_Packet);
/* Add our AVPs */
/* Mobile Node NAI */
/* Foreign Agent NAI */
/* Mobile Node Home Address */
/* Home Agent Address */
/* Mobile Node Session Time */
/* Release Indicator (RADIUS ONLY) */
if (aaaProtocol == RADIUS) {
}
} /* sendAccountingRecord */
void
{
unsigned char buffer[MAX_TCP_LEN];
/*
* If we don't already have a handle, look for it.
*/
if (handle == 0) {
/* Lookup the NAI to make sure it exists. */
if (((int)handle) < 0) {
handle = 0;
}
}
/* Build the message */
/* LINTED E_BAD_PTR_CAST_ALIGN */
/* this function is only used for DIAMETER */
length = sizeof (AAA_Packet);
/* Mobile Node NAI */
/* resultCode */
/* Send packet */
} /* aaaSendErrorResponse */
/*
* Function: initTCPSocket
*
* Arguments: in_port_t port
*
* Description: This routine binds a TCP socket to the specified port.
*
* Returns: int (the socket fd)
*
*/
static int
{
struct sockaddr_in sin;
int sinLength;
int rc;
/*
* Make sure only one thread at a time executes this code.
*/
if (rc < 0) {
/* Wierd error! */
return (-1);
}
if (gbl_TCPSocket != -1) {
/* We are already initialized. Exit */
"initTCPSocket: Warning: socket already initialized");
(void) pthread_mutex_unlock(&lock);
return (gbl_TCPSocket);
}
/*
* Get a socket.
*/
"initTCPSocket: socket failed (%d:%s)", errno,
gbl_TCPSocket = -1;
(void) pthread_mutex_unlock(&lock);
return (gbl_TCPSocket);
}
/*
* Initialize the sockaddr.
*/
sinLength = sizeof (struct sockaddr_in);
/*
* Get server's listening port number
*/
"initTCPSocket: connect failed (%d:%s)", errno,
(void) close(gbl_TCPSocket);
gbl_TCPSocket = -1;
(void) pthread_mutex_unlock(&lock);
return (gbl_TCPSocket);
}
/*
* If connected, advBusy flag should be set to True.
* This code will only be invoked when the connection
* is first started or a reconnection happens. Thus,
* in mipagent.
*/
}
/* Success! */
(void) pthread_mutex_unlock(&lock);
return (gbl_TCPSocket);
} /* initTCPSocket */
/*
* Function: sendTCPPacket
*
* Arguments: buffer, length
*
* Description: Sends the buffer on the TCP socket, and adds it to the queue
*
* Returns: int (zero on success)
*
*/
static int
{
int rc;
/* Make sure the socket is open */
if (gbl_TCPSocket == -1) {
(void) initTCPSocket(gbl_aaaPort);
if (gbl_TCPSocket == -1) {
return (-1);
}
}
/* finally, send it */
do {
if (rc < 0) {
(void) close(gbl_TCPSocket);
return (rc);
}
return (0);
} /* sendTCPPacket */
/*
* Function: readTCPPacket
*
* Arguments: unsigned char *buffer, uint32_t bufLen
*
* Description: Reads from the socket, ignoring EINTR, until a record is
* read. If it gets an error, it closes the socket, which will
* be re-opened on the next read or write.
*
* Returns: int
*/
static int
{
int rc;
/* Make sure the socket is open */
if (gbl_TCPSocket == -1) {
(void) initTCPSocket(gbl_aaaPort);
if (gbl_TCPSocket == -1) {
return (-1);
}
}
/* Read, ignoring EINTRs */
do {
if (rc == -1) {
(void) close(gbl_TCPSocket);
}
return (rc);
} /* readTCPPacket */
#ifdef TEST_AAA
#define TEST_NAI1 "test@sun.com"
#define TEST_NAI2 "test2@sun.com"
#define FA_NAI "foreignAgent@agents.everywhere.com"
int
{
int rc;
unsigned char reqPtr[] = {
/* Just some random data for testing */
0x1, 0x3, 0x1, 0x3, 0x1, 0x3, 0x1, 0x3, 0x1, 0x3,
0x1, 0x3, 0x1, 0x3, 0x1, 0x3, 0x1, 0x3, 0x1, 0x3,
0x1, 0x3, 0x1, 0x3, 0x1, 0x3, 0x1, 0x3, 0x1, 0x3,
0x1, 0x3 };
unsigned char mnChallenge[] = {
0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xa
};
if (argc != 2) {
return (-1);
}
if ((rc = startAAATaskThread()) != 0) {
"Error: rc = %d when calling startAAATaskThread\n",
rc);
return (rc);
}
mnChallenge, sizeof (mnChallenge),
if (rc != 0) {
"Error: rc = %d when calling"
" AAAAuthenticatateRegReq\n", rc);
/* return (rc); */
}
if (rc != 0) {
"Error: rc = %d when calling"
" sendAccountingStartRecord\n",
rc);
}
(void) sleep(1);
if (rc != 0) {
"sendAccountingInterimRecord\n", rc);
}
if (rc == 0) {
"sendAccountingInterimRecord\n", rc);
}
if (rc != 0) {
"sendAccountingStopRecord\n", rc);
}
(void) sleep(2);
"Sending a straggling message . . . should be an error\n");
if (rc == 0) {
"sendAccountingInterimRecord\n", rc);
}
for (;;) {
}
return (rc);
} /* main */
#endif /* TEST_AAA */
/* fin aaa.c */