diff options
Diffstat (limited to 'usr/src/cmd/cmd-inet/usr.lib/mipagent/aaa.c')
-rw-r--r-- | usr/src/cmd/cmd-inet/usr.lib/mipagent/aaa.c | 2687 |
1 files changed, 2687 insertions, 0 deletions
diff --git a/usr/src/cmd/cmd-inet/usr.lib/mipagent/aaa.c b/usr/src/cmd/cmd-inet/usr.lib/mipagent/aaa.c new file mode 100644 index 0000000000..678d8fcba6 --- /dev/null +++ b/usr/src/cmd/cmd-inet/usr.lib/mipagent/aaa.c @@ -0,0 +1,2687 @@ +/* + * 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 + * or http://www.opensolaris.org/os/licensing. + * 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 <sys/time.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.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_TCPSocket = -1; /* Global socket */ + +static int gbl_hashInitialized = 0; +in_port_t gbl_aaaPort = AAA_PORT; +char gbl_aaaHost[MAX_SERVER_NAME_LEN]; +static HashTable naiHash; + +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 forwardFromFAToHA(MessageHdr *, MaAdvConfigEntry *, boolean_t); +extern void rejectFromFAToMN(MessageHdr *, MaAdvConfigEntry *, int); +extern void FreeMessageHdr(MessageHdr *); +MessageHdr *AllocateMessageHdr(); +extern boolean_t forcefullyDeregisterMN(ipaddr_t *, ipaddr_t, ipaddr_t); +extern boolean_t mkPendingFAVEHashLookup(void *, uint32_t, uint32_t, uint32_t); +extern void delFAVE(HashTable *, FaVisitorEntry **, ipaddr_t, uint32_t); +extern void enableService(); +/* + * Internal Prototypes + */ + +static int sendCloseSessionAnswer(AAA_Packet *, char *, size_t, boolean_t); +static size_t aaaAddAvp(AAA_AVPCode avpCode, unsigned char *dest, + size_t destLen, void *data, size_t dataLen); +static AAA_AVP *aaaFindAvpByCode(AAA_Packet *packet, AAA_AVPCode avpCode); + +static int readTCPPacket(unsigned char *buffer, uint32_t bufLen); +static void *mainAAAThread(); +static int sendTCPPacket(unsigned char *buffer, uint32_t length); +static void processAuthFailure(MessageHdr *msgHdr, + char *mnNAI, size_t mnNAILen, uint32_t result); +#ifdef TEST_DIAMETER +static void processOpenSessionRequest(AAA_Packet *packet, char *mnNAI, + size_t mnNAILen); +static void processOpenSessionIndicationResponse(AAA_Packet *packet, + char *mnNAI, size_t mnNAILen); +static void aaaGenerateKey(unsigned char *buffer, size_t buffLen); +static uint32_t aaaGenerateSpi(); +#endif +static void processOpenSessionAnswer(AAA_Packet *packet, char *mnNAI, + size_t mnNAILen, uint32_t resultCode); +static void processOpenSessionAnswerRadius(AAA_Packet *packet, char *mnNAI, + size_t mnNAILen, AAA_HashEntry *); +static void processOpenSessionAnswerRadiusHA(AAA_Packet *packet, char *mnNAI, + size_t mnNAILen, AAA_HashEntry *, uint32_t resultCode); +static void processOpenSessionIndication(AAA_Packet *packet, char *mnNAI, + size_t mnNAILen); +static void *aaaFindAvpPtr(AAA_Packet *packet, AAA_AVPCode avpCode, + size_t *length); +static boolean_t aaaFindAvpInt(AAA_Packet *, AAA_AVPCode, int32_t *); +static int sendCloseSession(AAA_Packet *srcPacket, char *mnNAI, + size_t mnNAILen); +static void aaaSendErrorResponse(uint32_t commandCode, int32_t returnCode, + char *mnNAI, size_t mnNAILen, uint32_t handle); + +/* Not prototyped in .h file for cyclic dependency problems */ +int aaaSendRegistrationReply(MessageHdr *, size_t, ipaddr_t, ipaddr_t); + + +extern int logVerbosity; /* WORK -- This should be in a .h file. PRC? */ +extern ipaddr_t getClosestInterfaceAddr(ipaddr_t dest); /* Where? WORK */ +extern MaAdvConfigEntry *getFirstInterface(); /* Where? WORK */ +extern int dispatchMsgToThread(MessageHdr **messageHdr); +extern boolean_t advBusy; +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 +aaaAddAvp(AAA_AVPCode avpCode, unsigned char *dest, size_t destLen, + void *data, size_t dataLen) +{ + AAA_AVP staticAvp; + AAA_AVP *avp; + int32_t TempInt; + + /* First, check to make sure it will fit */ + if ((dataLen + (2 * sizeof (uint32_t))) > destLen) { + syslog(LOG_ERR, "ERROR: avp will not fit in dest! (" + "avpSize = %d, destSize = %d, avpCode = %d)", + dataLen + (2 * sizeof (uint32_t)), destLen, avpCode); + return (0); + } + + /* Now, build the avp */ + staticAvp.avpCode = htonl(avpCode); + + /* Note: dataLen = size of header */ + staticAvp.length = htonl(2 * sizeof (uint32_t) + dataLen); + + /* LINTED E_BAD_PTR_CAST_ALIGN */ + avp = (AAA_AVP *)dest; + (void) memcpy(avp, &staticAvp, 2 * sizeof (uint32_t)); + + 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: + (void) memcpy(avp->data, data, dataLen); + break; + + /* These fields require mangling (They're 32 bit numbers) */ + case NUMBER_OF_CHALLENGE_BYTES_IN_RR: + 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: + if (dataLen != sizeof (uint32_t)) { + syslog(LOG_ERR, "Internal error: avp should have a " + "length of %d, not %d!", sizeof (uint32_t), + dataLen); + return (0); + } + (void) memcpy(&TempInt, data, sizeof (uint32_t)); + TempInt = htonl(TempInt); + (void) memcpy(avp->data, &TempInt, sizeof (uint32_t)); + break; + + default: /* Error! */ + syslog(LOG_ERR, "ERROR: Invalid AVP Code! <%d>\n", avpCode); + return (0); + } /* switch (avpCode) */ + + return (ntohl(staticAvp.length)); +} /* 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 +addNaiToHash(char *mnNAI, size_t mnNAILen, + unsigned char *mnChallenge, uint32_t mnChallengeLen, + ipaddr_t homeAddress, ipaddr_t homeAgentAddress, + void *messageHdr) +{ + AAA_HashEntry *p; + int rc; + + /* Allocate and initialize HashEntry */ + p = (AAA_HashEntry *)malloc(sizeof (AAA_HashEntry)); + if (!p) { + syslog(LOG_CRIT, "FATAL: Unable to allocate memory!"); + return (-1); + } + + (void) memset(p, 0, sizeof (AAA_HashEntry)); + (void) strncpy(p->mnNAI, mnNAI, MIN(MAX_NAI_LEN - 1, mnNAILen)); + p->mnNAI[MIN(MAX_NAI_LEN - 1, mnNAILen)] = '\0'; + (void) memcpy(p->mnChallenge, mnChallenge, MIN(MAX_CHALLENGE_LEN - 1, + mnChallengeLen)); + + p->homeAddress = homeAddress; + p->homeAgentAddress = homeAgentAddress; + p->timeOut = 0; + p->handle = 0; + p->messageHdr = messageHdr; + + /* Now link it. */ + rc = linkHashTableEntryString(&naiHash, (unsigned char *)mnNAI, + mnNAILen, p, LOCK_NONE); + if (rc != 0) { + syslog(LOG_ERR, + "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 +removeFromHash(char *mnNAI, size_t mnNAILen) +{ + int rc; + AAA_HashEntry *p; + + /* + * Lock it for writing. + */ + p = (AAA_HashEntry *)findHashTableEntryString(&naiHash, + (unsigned char *)mnNAI, mnNAILen, LOCK_WRITE, NULL, NULL, NULL, + NULL); + if (!p) { + syslog(LOG_ERR, "ERROR: Unable to find NAI <%.*s> in hash!", + mnNAILen, mnNAI); + return (-1); + } + rc = delHashTableEntryString(&naiHash, p, (unsigned char *)mnNAI, + mnNAILen, LOCK_NONE); + + /* 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 * +aaaUpdateHash(AAA_Packet *packet, char *mnNAI, size_t mnNAILen) +{ + 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. + */ + p = (AAA_HashEntry *)findHashTableEntryString(&naiHash, + (unsigned char *)mnNAI, mnNAILen, LOCK_WRITE, NULL, NULL, + NULL, NULL); + + 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. + */ + if ((p->handle != ntohl(packet->handle)) && + (ntohl(packet->handle) != 0)) { + /* Error! */ + syslog(LOG_ERR, + "Error: incoming handle does not match" + " handle in hash: (%d <> %d)\n", + ntohl(packet->handle), + p->handle); + (void) rw_unlock(&p->aaaNodeLock); + return (NULL); + } + } else { + /* Since we don't have a handle on file, update it */ + p->handle = ntohl(packet->handle); + } + } else { + syslog(LOG_ERR, "Error: Unable to find nai (%*s) in hash!", + mnNAILen, mnNAI); + 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 +aaaLookupHandle(char *mnNAI, size_t mnNAILen) +{ + AAA_HashEntry *p; + int32_t handle; + + /* + * 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. + */ + p = (AAA_HashEntry *)findHashTableEntryString(&naiHash, (unsigned + char *)mnNAI, mnNAILen, LOCK_READ, NULL, NULL, NULL, NULL); + + if (p) { + handle = p->handle; + (void) rw_unlock(&p->aaaNodeLock); + + return (handle); + } else { + syslog(LOG_ERR, "Error: Unable to find nai (%.*s) in hash!", + mnNAILen, mnNAI); + 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 +aaaFindAvpInt(AAA_Packet *packet, AAA_AVPCode avpCode, int32_t *dest) +{ + AAA_AVP *avp, staticAvp; + + avp = aaaFindAvpByCode(packet, avpCode); + if (!avp) + return (_B_FALSE); + + /* Make our static copy */ + (void) memcpy(&staticAvp, avp, 2 * sizeof (uint32_t)); + staticAvp.length = ntohl(staticAvp.length); + + /* subtract the header size */ + staticAvp.length -= sizeof (uint32_t) * 2; + + if (staticAvp.length != sizeof (uint32_t)) { + syslog(LOG_ERR, "Error: aaaFindAvpInt: bad length for int." + " avp code = %d, length = %d", staticAvp.avpCode, + staticAvp.length); + return (_B_FALSE); + } + + (void) memcpy(dest, avp->data, sizeof (uint32_t)); + + 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 * +aaaFindAvpPtr(AAA_Packet *packet, AAA_AVPCode avpCode, size_t *length) +{ + AAA_AVP *avp, staticAvp; + + *length = 0; /* Initialize this first */ + + avp = aaaFindAvpByCode(packet, avpCode); + if (!avp) + return (NULL); + + /* Make our static copy */ + (void) memcpy(&staticAvp, avp, 2 * sizeof (uint32_t)); + staticAvp.length = ntohl(staticAvp.length); + + /* subtract the header size */ + staticAvp.length -= sizeof (uint32_t) * 2; + + *length = staticAvp.length; + + return (avp->data); + +} /* 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 * +aaaFindAvpByCode(AAA_Packet *packet, AAA_AVPCode avpCode) +{ + AAA_AVP *avp; + uint32_t packetLength; + uint32_t currentPosition; + AAA_AVP staticAVP; + unsigned char *buffer; + + /* First, get the length of the packet, so we don't overshoot */ + packetLength = ntohl(packet->length); + + /* Now, set buffer to point to the start of the AVPs */ + buffer = (unsigned char *)&packet[1]; + + currentPosition = 0; + while (currentPosition < (packetLength - (sizeof (AAA_AVP)))) { + /* LINTED E_BAD_PTR_CAST_ALIGN */ + avp = (AAA_AVP*)&buffer[currentPosition]; + + /* + * now avp points to the right place. copy the + * data somewhere byte aligned. + */ + + (void) memcpy(&staticAVP, avp, sizeof (uint32_t)*2); + staticAVP.length = ntohl(staticAVP.length); + staticAVP.avpCode = ntohl(staticAVP.avpCode); + + if (staticAVP.length <= (2 * sizeof (uint32_t))) { + /* Bad packet */ + syslog(LOG_ERR, "Error: bad packet avp code = %d, " + "length = %d", staticAVP.avpCode, + staticAVP.length); + return (NULL); + } + + if (staticAVP.avpCode == avpCode) { + /* We found it! return our position */ + return (avp); + } + + + /* That wasn't it, so move on to the next one */ + currentPosition += staticAVP.length; + + } + + /* 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 +aaaCreateKey(int spi, unsigned char *key, size_t keyLen, + uint32_t sessionTimeout) +{ + MipSecAssocEntry *entry; + + entry = CreateSecAssocEntry(_B_TRUE, spi, TIMESTAMPS, MD5, PREFIXSUFFIX, + keyLen, (char *)key, sessionTimeout); + + if (entry == NULL) { + syslog(LOG_ERR, + "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. + */ + (void) rw_unlock(&entry->mipSecNodeLock); + 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 +aaaCreateAgent(AAA_Packet *packet, int addrTag, uint32_t spi, + uint32_t sessionTimeout) +{ + MobilityAgentEntry *maEntry; + ipaddr_t peerAddress; + + /* Check the Agent Address */ + if (!aaaFindAvpInt(packet, addrTag, (int *)&peerAddress)) { + /* BAD error . . . malformed packet */ + syslog(LOG_ERR, "ERROR: bad packet (no ADDR:%d)", addrTag); + 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. + */ + maEntry = CreateMobilityAgentEntry(_B_TRUE, peerAddress, spi, + sessionTimeout); + if (maEntry == NULL) { + /* Find it. */ + maEntry = findHashTableEntryUint(&mipAgentHash, peerAddress, + LOCK_WRITE, NULL, 0, 0, 0); + if (maEntry == NULL) { + /* Error! */ + syslog(LOG_ERR, "Error: Unable to create the maEntry!"); + return; + } + } + + /* + * Now, make sure the entry is a dynamic one, and update the SPI + */ + if (maEntry->maSPI != spi) { + if (maEntry->maIsEntryDynamic) { + maEntry->maSPI = spi; + } else { + syslog(LOG_ERR, + "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. + */ + (void) rw_unlock(&maEntry->maNodeLock); + +} /* 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 +checkResultCode(AAA_Packet *packet) +{ + int32_t resultCode; + + if (!aaaFindAvpInt(packet, RESULT_CODE, &resultCode)) { + /* BAD error . . . malformed packet */ + syslog(LOG_ERR, "ERROR: bad packet (no RESULT_CODE)"); + } + + return (resultCode); +} /* checkResultCode */ + +/* + * Function: startAAATaskThread + * + * Arguments: + * + * Description: This function starts our AAA thread. + * + * Returns: int (zero on success) + */ +int +startAAATaskThread() +{ + pthread_attr_t pthreadAttribute; + int result; + + result = pthread_attr_init(&pthreadAttribute); + + if (result) { + syslog(LOG_CRIT, "Error Initializing AAA pthread."); + return (-1); + } + + /* + * We now create a thread to deal with all periodic task. + */ + result = pthread_create(&aaaThreadId, &pthreadAttribute, + (void *(*)()) mainAAAThread, (void *)NULL); + + if (result) { + syslog(LOG_CRIT, "pthread_create() failed."); + 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. + */ + result = pthread_detach(aaaThreadId); + + if (result) { + syslog(LOG_CRIT, "pthread_detach() failed."); + return (-1); + } + + return (0); +} /* StartAAATaskThread */ + +/* + * Function: killAAATaskThread + * + * Arguments: + * + * Description: This function kills our AAA task thread. + * + * Returns: int + */ +int +killAAATaskThread() +{ + int result; + + if (aaaThreadId) { + /* + * Next we need to kill the dispatching thread. + */ + result = pthread_cancel(aaaThreadId); + + if (result) { + /* + * Well, there's not much we can do here.. + */ + syslog(LOG_CRIT, "Unable to kill AAA thread"); + 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 * +mainAAAThread() +{ + unsigned char buffer[MAX_TCP_LEN]; + uint32_t commandCode; + AAA_Packet *packet; + AAA_HashEntry *NaiEntry; + uint32_t resultCode; + boolean_t result; + char *mobileNodeNAI; + size_t mobileNodeNAILen; + ipaddr_t *homeaddr = NULL; + ipaddr_t *homeAgentaddr = NULL; + size_t homeaddrLen; + MessageHdr *msgHdr; + int rc; + uint32_t forHA; + + /* The below is an endless loop that will not give any lint warnings */ + for (; ; ) { + if ((rc = readTCPPacket(buffer, MAX_TCP_LEN - 1)) <= 0) { + syslog(LOG_ERR, "Error: <%d> reading packet (%d:%s)", + rc, errno, strerror(errno)); + 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) { + syslog(LOG_ERR, "AAA readTCPPacket returned 0"); + mipverbose(("AAA readTCPPacket returned 0\n")); + docleanup(); + disableService(&maAdvConfigHash); + /* + * need to reconnect + */ + (void) close(gbl_TCPSocket); + gbl_TCPSocket = -1; + } + continue; + } + + /* LINTED E_BAD_PTR_CAST_ALIGN */ + packet = (AAA_Packet *)buffer; + + commandCode = ntohl(packet->commandCode); + /* + * First, lookup this in our hash, and update the handle + * if necessary. + */ + mobileNodeNAI = aaaFindAvpPtr(packet, MOBILE_NODE_NAI, + &mobileNodeNAILen); + if ((mobileNodeNAI == NULL) || (mobileNodeNAILen == 0)) { + /* Malformed packet */ + syslog(LOG_ERR, + "Error: bad packet(no MOBILE_NODE_NAI)"); + continue; + } + + switch (commandCode) { + case MOBILE_IP_OPEN_SESSION_ANSWER: + /* Update the handle in the hash */ + + NaiEntry = aaaUpdateHash(packet, mobileNodeNAI, + mobileNodeNAILen); + if (NaiEntry == NULL) { + syslog(LOG_ERR, + "Error: Received packet for " + "non-pending NAI (ANSWER)"); + continue; + } + (void) rw_unlock(&NaiEntry->aaaNodeLock); + + /* Check for a good result code */ + resultCode = checkResultCode(packet); + 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) { + (void) rw_wrlock(&NaiEntry->aaaNodeLock); + msgHdr = (MessageHdr *)NaiEntry->messageHdr; + processAuthFailure(msgHdr, mobileNodeNAI, + mobileNodeNAILen, resultCode); + + msgHdr->dontDeleteNow = _B_FALSE; + FreeMessageHdr(msgHdr); + NaiEntry->messageHdr = NULL; + (void) rw_unlock(&NaiEntry->aaaNodeLock); + } + + + } + 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(). + */ + + if (!aaaFindAvpInt(packet, IS_FROM_HA, (int *)&forHA)) { + syslog(LOG_ERR, "ERROR: bad packet" + " (no IS_FROM_HA)"); + continue; + } + if (forHA == 1) { /* HA */ + processOpenSessionAnswerRadiusHA(packet, + mobileNodeNAI, mobileNodeNAILen, + NaiEntry, resultCode); + } else { /* FA */ + processOpenSessionAnswerRadius(packet, + mobileNodeNAI, mobileNodeNAILen, + NaiEntry); + } + + } else { + processOpenSessionAnswer(packet, mobileNodeNAI, + mobileNodeNAILen, resultCode); + } + break; + + case MOBILE_IP_ACCOUNTING_START_ANSWER: + case MOBILE_IP_ACCOUNTING_INTERIM_ANSWER: + case MOBILE_IP_ACCOUNTING_STOP_ANSWER: + /* Check the handle in the hash */ + NaiEntry = aaaUpdateHash(packet, mobileNodeNAI, + mobileNodeNAILen); + if (NaiEntry == NULL) { + syslog(LOG_ERR, + "Error: Received packet for " + "non-pending NAI"); + continue; + } + (void) rw_unlock(&NaiEntry->aaaNodeLock); + + /* Check the result code */ + resultCode = checkResultCode(packet); + if (resultCode != 0) { + syslog(LOG_ERR, "Error: commandCode: %d" + " resultCode = %d", commandCode, + resultCode); + } + if (commandCode == MOBILE_IP_ACCOUNTING_STOP_ANSWER) { + (void) sendCloseSession(packet, mobileNodeNAI, + mobileNodeNAILen); + } + break; + + case MOBILE_IP_CLOSE_SESSION_ANSWER: + /* Check the handle */ + if ((NaiEntry = aaaUpdateHash(packet, mobileNodeNAI, + mobileNodeNAILen)) == NULL) { + syslog(LOG_ERR, + "Error: Received packet for " + "non-pending NAI"); + continue; + } + (void) rw_unlock(&NaiEntry->aaaNodeLock); + /* Check the result code */ + resultCode = checkResultCode(packet); + if (resultCode != 0) { + syslog(LOG_ERR, + "Error: ACCOUNTING_START_ANSWER" + " resultCode = %d", resultCode); + } + /* And finally, remove the hash entry */ + (void) removeFromHash(mobileNodeNAI, mobileNodeNAILen); + break; + + case MOBILE_IP_OPEN_SESSION_INDICATION: + if (aaaProtocol != DIAMETER) { + syslog(LOG_ERR, + "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. + */ + processOpenSessionIndication(packet, mobileNodeNAI, + mobileNodeNAILen); + break; + + /* We should not get these. */ +#ifdef TEST_DIAMETER + case MOBILE_IP_OPEN_SESSION_REQUEST: + /* + * We are faking a diameter connection. Accept the + * OPEN_SESSION, and respond with an OPEN_SESSION + * response. Generate keys too. + */ + processOpenSessionRequest(packet, mobileNodeNAI, + mobileNodeNAILen); + break; + + case MOBILE_IP_OPEN_SESSION_INDICATION_RESPONSE: + /* + * We are faking a diameter connection. Accept the + * OPEN_SESSION_INDICATION_RESPONSE as the foreign + * agent, and convert it to a OpenSessionAnswer + */ + processOpenSessionIndicationResponse(packet, + mobileNodeNAI, mobileNodeNAILen); + break; +#else + case MOBILE_IP_OPEN_SESSION_REQUEST: + case MOBILE_IP_OPEN_SESSION_INDICATION_RESPONSE: +#endif + case MOBILE_IP_CLOSE_SESSION_REQUEST: + /* + * 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. + */ + homeaddr = aaaFindAvpPtr(packet, + MOBILE_NODE_HOME_ADDRESS, &homeaddrLen); + homeAgentaddr = aaaFindAvpPtr(packet, + HOME_AGENT_ADDRESS, &homeaddrLen); + + if (homeaddr == NULL || homeAgentaddr == NULL) { + result = 1; + syslog(LOG_ERR, + "ERROR: bad packet " + "(no agent addresses or homeaddr)"); + } else { + result = forcefullyDeregisterMN(homeaddr, + 0, *homeAgentaddr); + } + result = sendCloseSessionAnswer(packet, mobileNodeNAI, + mobileNodeNAILen, result); + if (result != 0) { + syslog(LOG_ERR, "sendto failed at mipagent " + "while replying to AAA. "); + } + break; + + case MOBILE_IP_ACCOUNTING_START_REQUEST: + case MOBILE_IP_ACCOUNTING_INTERIM_REQUEST: + case MOBILE_IP_ACCOUNTING_STOP_REQUEST: + default: + syslog(LOG_ERR, + "Error: Received invalid commandCode <%d>", + commandCode); + } + } + + /* 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 +processOpenSessionRequest(AAA_Packet *packet, char *mnNAI, size_t mnNAILen) +{ + unsigned char buffer[MAX_TCP_LEN]; + AAA_Packet *response; + size_t length; + unsigned char *regReq; + size_t regReqLen; + char *faNai; + size_t faNaiLen; + uint32_t numChallengeBytes; + unsigned char *mnResponse; + size_t mnResponseLen; + ipaddr_t homeAddress, homeAgentAddress, foreignAgentAddress; + uint32_t sessionTimeout; + uint32_t MNFASpi, FAHASpi, MNHASpi; + 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 */ + faNai = aaaFindAvpPtr(packet, FOREIGN_AGENT_NAI, &faNaiLen); + if (!faNai || !faNaiLen) { + syslog(LOG_ERR, "ERROR: bad packet (no FOREIGN_AGENT_NAI)"); + return; + } + + /* REGISTRATION_REQUEST */ + regReq = aaaFindAvpPtr(packet, REGISTRATION_REQUEST, ®ReqLen); + if (!regReq || !regReqLen) { + syslog(LOG_ERR, "ERROR: bad packet (no REGISTRATION_REQUEST)"); + return; + } + + /* NUMBER_OF_CHALLENGE_BYTES_IN_RR */ + if (!aaaFindAvpInt(packet, + NUMBER_OF_CHALLENGE_BYTES_IN_RR, &numChallengeBytes)) { + syslog(LOG_ERR, "ERROR: bad packet (no " + "NUMBER_OF_CHALLENGE_BYTES_IN_RR)"); + return; + } + + /* MOBILE_NODE_RESPONSE */ + mnResponse = aaaFindAvpPtr(packet, MOBILE_NODE_RESPONSE, + &mnResponseLen); + if (!mnResponse || !mnResponseLen) { + syslog(LOG_ERR, "ERROR: bad packet (no MOBILE_NODE_RESPONSE)"); + return; + } + + /* MOBILE_NODE_HOME_ADDRESS */ + if (!aaaFindAvpInt(packet, MOBILE_NODE_HOME_ADDRESS, + &homeAddress)) { + syslog(LOG_ERR, + "ERROR: bad packet (no MOBILE_NODE_HOME_ADDRESS)"); + return; + } + + /* HOME_AGENT_ADDRESS */ + if (!aaaFindAvpInt(packet, HOME_AGENT_ADDRESS, + &homeAgentAddress)) { + syslog(LOG_ERR, "ERROR: bad packet (no HOME_AGENT_ADDRESS)"); + return; + } + + /* FOREIGN_AGENT_ADDRESS */ + if (!aaaFindAvpInt(packet, FOREIGN_AGENT_ADDRESS, + &foreignAgentAddress)) { + syslog(LOG_ERR, + "ERROR: bad packet (no FOREIGN_AGENT_ADDRESS)"); + return; + } + + /* ******** Build Fake Packet ******** */ + + /* + * Generate our keys + */ + aaaGenerateKey(FAHAKey, MAX_GENERATE_KEY_LEN); + aaaGenerateKey(MNFAKey, MAX_GENERATE_KEY_LEN); + aaaGenerateKey(MNHAKey, MAX_GENERATE_KEY_LEN); + + /* These should be computed. (MD5?) */ + aaaGenerateKey(HAFAKey, MAX_GENERATE_KEY_LEN); + aaaGenerateKey(FAMNKey, MAX_GENERATE_KEY_LEN); + aaaGenerateKey(HAMNKey, MAX_GENERATE_KEY_LEN); + + MNFASpi = aaaGenerateSpi(); + FAHASpi = aaaGenerateSpi(); + MNHASpi = aaaGenerateSpi(); + + + /* Build our response (An OpenSessionIndication) */ + /* LINTED E_BAD_PTR_CAST_ALIGN */ + response = (AAA_Packet *)buffer; + response->commandCode = htonl(MOBILE_IP_OPEN_SESSION_INDICATION); + response->handle = packet->handle; /* assume ordered correctly */ + + length = sizeof (AAA_Packet); + + /* Mobile Node NAI */ + length += aaaAddAvp(MOBILE_NODE_NAI, &buffer[length], + MAX_TCP_LEN - length, mnNAI, mnNAILen); + + /* Foreign Agent NAI */ + length += aaaAddAvp(FOREIGN_AGENT_NAI, &buffer[length], + MAX_TCP_LEN - length, maNai, strlen(maNai)); + + /* Foreign Agent Address */ + length += aaaAddAvp(FOREIGN_AGENT_ADDRESS, &buffer[length], + MAX_TCP_LEN - length, &foreignAgentAddress, sizeof (uint32_t)); + + /* Registration Request Packet */ + length += aaaAddAvp(REGISTRATION_REQUEST, &buffer[length], + MAX_TCP_LEN - length, regReq, regReqLen); + + /* Mobile Node Home Address */ + length += aaaAddAvp(MOBILE_NODE_HOME_ADDRESS, &buffer[length], + MAX_TCP_LEN - length, &homeAddress, sizeof (uint32_t)); + + /* Home Agent Address */ + length += aaaAddAvp(HOME_AGENT_ADDRESS, &buffer[length], + MAX_TCP_LEN - length, &homeAgentAddress, sizeof (uint32_t)); + + /* fa-ha spi */ + length += aaaAddAvp(FA_HA_SPI, &buffer[length], + MAX_TCP_LEN - length, &FAHASpi, sizeof (uint32_t)); + + /* FA-HA key */ + length += aaaAddAvp(FA_HA_KEY, &buffer[length], + MAX_TCP_LEN - length, &FAHAKey, MAX_GENERATE_KEY_LEN); + + /* HA-FA Key */ + length += aaaAddAvp(HA_FA_KEY, &buffer[length], + MAX_TCP_LEN - length, &HAFAKey, MAX_GENERATE_KEY_LEN); + + /* MN-FA SPI */ + length += aaaAddAvp(MN_FA_SPI, &buffer[length], + MAX_TCP_LEN - length, &MNFASpi, sizeof (uint32_t)); + + /* MN-FA key */ + length += aaaAddAvp(MN_FA_KEY, &buffer[length], + MAX_TCP_LEN - length, &MNFAKey, MAX_GENERATE_KEY_LEN); + + /* FA-MN Key */ + length += aaaAddAvp(FA_MN_KEY, &buffer[length], + MAX_TCP_LEN - length, &FAMNKey, MAX_GENERATE_KEY_LEN); + + /* MN-HA SPI */ + length += aaaAddAvp(MN_HA_SPI, &buffer[length], + MAX_TCP_LEN - length, &MNHASpi, sizeof (uint32_t)); + + /* MN-HA key */ + length += aaaAddAvp(MN_HA_KEY, &buffer[length], + MAX_TCP_LEN - length, &MNHAKey, MAX_GENERATE_KEY_LEN); + + /* HA-MN Key */ + length += aaaAddAvp(HA_MN_KEY, &buffer[length], + MAX_TCP_LEN - length, &HAMNKey, MAX_GENERATE_KEY_LEN); + + /* Send a fake session time out */ + sessionTimeout = 60; + length += aaaAddAvp(SESSION_TIMEOUT, &buffer[length], + MAX_TCP_LEN - length, &sessionTimeout, sizeof (uint32_t)); + + response->length = htonl(length); + + + processOpenSessionIndication(response, mnNAI, mnNAILen); +} /* 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 +processOpenSessionIndicationResponse(AAA_Packet *packet, char *mnNAI, + size_t mnNAILen) +{ + mipverbose(("Processing OpenSessionIndicationResponse (DEBUG)\n")); + + processOpenSessionAnswer(packet, mnNAI, mnNAILen, 0); +} /* 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(MessageHdr *messageHdr, uint32_t resultCode) +{ + messageHdr->pktSource = MIP_PKT_FROM_AAA; + messageHdr->ifType = ON_UNICAST_SOCK; + messageHdr->pktType = PKT_UDP; + messageHdr->ifEntry = getFirstInterface(); + messageHdr->aaaResultCode = resultCode; + +} /* 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 */ +processOpenSessionAnswer(AAA_Packet *packet, char *mnNAI, size_t mnNAILen, + uint32_t resultCode) +{ + static MessageHdr *messageHdr = NULL; + unsigned char *FAMNKey, *FAHAKey; + size_t FAMNKeyLen = 0, FAHAKeyLen = 0; + uint32_t FAHASpi = 0, FAMNSpi = 0; + uint32_t sessionTimeout = 0; + unsigned char *regResponse; + size_t regResponseLen; + + /* Check the SessionTimeout */ + if (!aaaFindAvpInt(packet, SESSION_TIMEOUT, (int *)&sessionTimeout)) { + /* BAD error . . . malformed packet */ + syslog(LOG_ERR, "ERROR: bad packet (no SESSION_TIMEOUT)"); + } + + /* Now, make sure we have a response field */ + regResponse = aaaFindAvpPtr(packet, REGISTRATION_REPLY, + ®ResponseLen); + + if (!regResponse) { + syslog(LOG_ERR, "ERROR: bad packet (no REGISTRATION_REPLY)"); + } + + /* FA-HA Spi */ + if (!aaaFindAvpInt(packet, FA_HA_SPI, (int *)&FAHASpi)) { + syslog(LOG_ERR, "ERROR: bad packet (no FA_HA_SPI)"); + } + /* FA-HA Key */ + FAHAKey = aaaFindAvpPtr(packet, FA_HA_KEY, &FAHAKeyLen); + if (!FAHAKey || !FAHAKeyLen) { + syslog(LOG_ERR, "ERROR: bad packet (no FA_HA_KEY)"); + } + /* MN-FA Spi */ + if (!aaaFindAvpInt(packet, MN_FA_SPI, (int *)&FAMNSpi)) { + syslog(LOG_ERR, "ERROR: bad packet (no MN_FA_SPI)"); + } + /* MN-FA Key */ + FAMNKey = aaaFindAvpPtr(packet, FA_MN_KEY, &FAMNKeyLen); + if (!FAMNKey || !FAMNKeyLen) { + syslog(LOG_ERR, "ERROR: bad packet (no MN_FA_KEY)"); + } + + /* Session Timeout */ + if (!aaaFindAvpInt(packet, SESSION_TIMEOUT, (int *)&sessionTimeout)) { + /* BAD error . . . malformed packet */ + syslog(LOG_ERR, "ERROR: bad packet (no SESSION_TIMEOUT)"); + } + + /* + * Create our keys + */ + if ((FAMNKeyLen != 0) && + (aaaCreateKey(FAMNSpi, FAMNKey, FAMNKeyLen, sessionTimeout) < 0)) { + syslog(LOG_ERR, "Error: Invalid MN-FA SPI/Key pair"); + } + + if ((FAHAKeyLen != 0) && + (aaaCreateKey(FAHASpi, FAHAKey, FAHAKeyLen, sessionTimeout) < 0)) { + syslog(LOG_ERR, "Error: Invalid FA-HA SPI/Key pair"); + } + + aaaCreateAgent(packet, HOME_AGENT_ADDRESS, FAHASpi, sessionTimeout); + + /* + * If we don't already have a message header, + * allocate one. + */ + if (messageHdr == NULL) { + if ((messageHdr = AllocateMessageHdr()) == NULL) { + syslog(LOG_CRIT, + "Unable to allocate a message header"); + return; + } + } + aaaSetupMessageHdr(messageHdr, resultCode); + (void) memcpy(messageHdr->pkt, regResponse, regResponseLen); + messageHdr->pktLen = regResponseLen; + + messageHdr->mnFaSPI = FAMNSpi; + messageHdr->aaaSessionTimeout = sessionTimeout; + + /* + * 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 +processOpenSessionAnswerRadius(AAA_Packet *packet, char *mnNAI, size_t mnNAILen, + AAA_HashEntry *NaiEntry) +{ + MessageHdr *msgHdr; + regRequest *requestPtr; + uint32_t revtun; + 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 :). + */ + (void) rw_rdlock(&(NaiEntry->aaaNodeLock)); + msgHdr = (MessageHdr *)NaiEntry->messageHdr; + if (msgHdr == NULL) { + mipverbose(("This was a repeat OpenSessionAnswer\n")); + (void) rw_unlock(&NaiEntry->aaaNodeLock); + return; + } + + /* REV_TUN */ + if (!aaaFindAvpInt(packet, REV_TUN, (int *)&revtun) || + (revtun != REVTUN_REQUIRED && revtun != REVTUN_NOTREQUIRED)) { + syslog(LOG_ERR, + "ERROR: OpenSessionAnswer (no or bad REV_TUN)"); + if (revtun != 0) { + syslog(LOG_ERR, + "ERROR: OpenSessionAnswer bad REV_TUN value %d\n", + revtun); + } else { + syslog(LOG_ERR, + "ERROR: OpenSessionAnswer (no REV_TUN)"); + } + (void) rw_unlock(&NaiEntry->aaaNodeLock); + 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 + */ + mipverbose(("Radius returned REV_TUN value %d", revtun)); + /* LINTED */ + requestPtr = (regRequest *) msgHdr->pkt; + if (!(requestPtr->regFlags & REG_REVERSE_TUNNEL) && + 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")); + code = FA_REVERSE_TUNNEL_REQUIRED; + faCounters.faReverseTunnelRequiredCnt++; + } + /* Release Read lock now */ + (void) rw_unlock(&NaiEntry->aaaNodeLock); + + if (code == FA_REVERSE_TUNNEL_REQUIRED) { + FaVisitorEntry *visitor_entry = NULL; + + (void) rw_wrlock(&NaiEntry->aaaNodeLock); + (void) rejectFromFAToMN(msgHdr, msgHdr->ifEntry, code); + + /* Cleanup the pending visitor entry now */ + visitor_entry = findHashTableEntryString(&faVisitorHash, + (unsigned char *)mnNAI, mnNAILen, LOCK_WRITE, + mkPendingFAVEHashLookup, _B_FALSE, 0, 0); + if (visitor_entry != NULL) { + /* Pending entry found */ + delFAVE(&faVisitorHash, &visitor_entry, + requestPtr->homeAddr, REG_REVOKED); + } + msgHdr->dontDeleteNow = _B_FALSE; + FreeMessageHdr(msgHdr); + NaiEntry->messageHdr = NULL; + (void) rw_unlock(&NaiEntry->aaaNodeLock); + return; + } + + + (void) forwardFromFAToHA(msgHdr, msgHdr->ifEntry, _B_TRUE); + + /* + * Now we are done with the messageHdr stored in NaiEntry, let's + * free it, as we promised before. + */ + (void) rw_wrlock(&NaiEntry->aaaNodeLock); + msgHdr->dontDeleteNow = _B_FALSE; + FreeMessageHdr(msgHdr); + NaiEntry->messageHdr = NULL; + (void) rw_unlock(&NaiEntry->aaaNodeLock); + +} /* 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 +processOpenSessionAnswerRadiusHA(AAA_Packet *packet, char *mnNAI, + size_t mnNAILen, AAA_HashEntry *NaiEntry, uint32_t resultCode) +{ + static MessageHdr *messageHdr = NULL; + regRequest *requestPtr; + uint32_t sessionTimeout = 0; + uint32_t revtun; + uint32_t MNHASpi; + unsigned char *MNHAKey; + size_t MNHAKeyLen; + 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 :). + */ + (void) rw_rdlock(&(NaiEntry->aaaNodeLock)); + messageHdr = (MessageHdr *)NaiEntry->messageHdr; + if (messageHdr == NULL) { + mipverbose(("This was a repeat OpenSessionAnswer\n")); + (void) rw_unlock(&NaiEntry->aaaNodeLock); + return; + } + (void) rw_unlock(&NaiEntry->aaaNodeLock); + + /* Check the SessionTimeout */ + if (!aaaFindAvpInt(packet, SESSION_TIMEOUT, (int *)&sessionTimeout)) { + /* BAD error . . . malformed packet */ + syslog(LOG_ERR, "ERROR: bad packet (no SESSION_TIMEOUT)"); + code = HA_MN_AUTH_FAILURE; + } + + /* MN-HA Spi */ + if (!aaaFindAvpInt(packet, MN_HA_SPI, (int *)&MNHASpi)) { + syslog(LOG_ERR, "ERROR: bad packet (no MN_HA_SPI)"); + code = HA_MN_AUTH_FAILURE; + } + /* MN-HA Key */ + MNHAKey = aaaFindAvpPtr(packet, MN_HA_KEY, &MNHAKeyLen); + if (!MNHAKey || !MNHAKeyLen) { + syslog(LOG_ERR, "ERROR: bad packet (no MN_HA_KEY)"); + code = HA_MN_AUTH_FAILURE; + } + + /* REV_TUN */ + if (!aaaFindAvpInt(packet, REV_TUN, (int *)&revtun) || + (revtun != REVTUN_REQUIRED && revtun != REVTUN_NOTREQUIRED)) { + syslog(LOG_ERR, + "ERROR: bad packet (no or bad REV_TUN from RADIUS)"); + if (revtun != 0) { + syslog(LOG_ERR, + "ERROR: REV_TUN value %d\n", revtun); + } else { + syslog(LOG_ERR, + "ERROR: (no REV_TUN from RADIUS)"); + } + code = HA_MN_AUTH_FAILURE; + } else { + mipverbose(("processOpenSessionAnswerHA:" + "HA received reverse tunnel value from RADIUS %d\n", + revtun)); + /* Found valid revtun entry */ + (void) rw_rdlock(&NaiEntry->aaaNodeLock); + /* LINTED */ + requestPtr = (regRequest *) messageHdr->pkt; + if (!(requestPtr->regFlags & REG_REVERSE_TUNNEL) && + revtun == REVTUN_REQUIRED) { + mipverbose(("processOpenSessionAnswerHA:" + "T bit required in regRequest\n")); + code = HA_REVERSE_TUNNEL_REQUIRED; + } + (void) rw_unlock(&NaiEntry->aaaNodeLock); + } + + if (aaaCreateKey(MNHASpi, MNHAKey, MNHAKeyLen, sessionTimeout) < 0) { + syslog(LOG_ERR, "Error: Invalid MN-HA SPI/Key pair"); + code = HA_MN_AUTH_FAILURE; + } + + if (code == 0) + code = resultCode; /* resultCode is from pkt from Radius */ + + (void) rw_wrlock(&NaiEntry->aaaNodeLock); + messageHdr->pktSource = MIP_PKT_FROM_RADIUS; + messageHdr->ifType = ON_UNICAST_SOCK; + messageHdr->pktType = PKT_UDP; + messageHdr->aaaResultCode = code; + + messageHdr->mnAAASPI = 0; + messageHdr->algorithm = MD5; + messageHdr->mnHaSPI = MNHASpi; + (void) memcpy(messageHdr->mnHaKey, MNHAKey, MNHAKeyLen); + messageHdr->mnHaKeyLen = MNHAKeyLen; + + messageHdr->aaaSessionTimeout = sessionTimeout; + + (void) rw_unlock(&NaiEntry->aaaNodeLock); + + /* + * 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 +processOpenSessionIndication(AAA_Packet *packet, char *mnNAI, size_t mnNAILen) +{ + static MessageHdr *messageHdr = NULL; + unsigned char *regReq; + size_t regReqLen; + char *faNai; + size_t faNaiLen; + ipaddr_t homeAddress, homeAgentAddress, foreignAgentAddress; + uint32_t sessionTimeout; + uint32_t FAHASpi, MNHASpi; + unsigned char *MNFAKey; + size_t MNFAKeyLen; + unsigned char *MNHAKey; + size_t MNHAKeyLen; + /* These are encrypted versions of the above */ + unsigned char *HAFAKey; + size_t HAFAKeyLen; + unsigned char *HAMNKey; + size_t HAMNKeyLen; + + /* + * Initialize hash on HA side. + */ + /* Check to see if we are initialized */ + if (!gbl_hashInitialized) { + (void) InitHash(&naiHash); + naiHash.uniqueData = 1; /* Set our unique flag */ + gbl_hashInitialized = 1; + } + + (void) hexdump("Got message:", (unsigned char *)packet, + ntohl(packet->length)); + + /* FOREIGN_AGENT_NAI */ + faNai = aaaFindAvpPtr(packet, FOREIGN_AGENT_NAI, &faNaiLen); + if (!faNai || !faNaiLen) { + syslog(LOG_ERR, "ERROR: bad packet (no FOREIGN_AGENT_NAI)"); + aaaSendErrorResponse(MOBILE_IP_OPEN_SESSION_INDICATION_RESPONSE, + -1, mnNAI, mnNAILen, packet->handle); + return; + } + + /* REGISTRATION_REQUEST */ + regReq = aaaFindAvpPtr(packet, REGISTRATION_REQUEST, ®ReqLen); + if (!regReq || !regReqLen) { + syslog(LOG_ERR, "ERROR: bad packet (no REGISTRATION_REQUEST)"); + aaaSendErrorResponse(MOBILE_IP_OPEN_SESSION_INDICATION_RESPONSE, + -2, mnNAI, mnNAILen, packet->handle); + return; + } + + /* MOBILE_NODE_HOME_ADDRESS */ + if (!aaaFindAvpInt(packet, MOBILE_NODE_HOME_ADDRESS, + (int *)&homeAddress)) { + syslog(LOG_ERR, + "ERROR: bad packet (no MOBILE_NODE_HOME_ADDRESS)"); + aaaSendErrorResponse(MOBILE_IP_OPEN_SESSION_INDICATION_RESPONSE, + -3, mnNAI, mnNAILen, packet->handle); + return; + } + + /* HOME_AGENT_ADDRESS */ + if (!aaaFindAvpInt(packet, + HOME_AGENT_ADDRESS, (int *)&homeAgentAddress)) { + syslog(LOG_ERR, "ERROR: bad packet (no HOME_AGENT_ADDRESS)"); + aaaSendErrorResponse(MOBILE_IP_OPEN_SESSION_INDICATION_RESPONSE, + -4, mnNAI, mnNAILen, packet->handle); + return; + } + + /* FOREIGN_AGENT_ADDRESS */ + if (!aaaFindAvpInt(packet, FOREIGN_AGENT_ADDRESS, + (int *)&foreignAgentAddress)) { + syslog(LOG_ERR, + "ERROR: bad packet (no FOREIGN_AGENT_ADDRESS)"); + aaaSendErrorResponse(MOBILE_IP_OPEN_SESSION_INDICATION_RESPONSE, + -5, mnNAI, mnNAILen, packet->handle); + return; + } + + /* FA-HA Spi */ + if (!aaaFindAvpInt(packet, FA_HA_SPI, (int *)&FAHASpi)) { + syslog(LOG_ERR, "ERROR: bad packet (no " + "FA_HA_SPI)"); + aaaSendErrorResponse(MOBILE_IP_OPEN_SESSION_INDICATION_RESPONSE, + -6, mnNAI, mnNAILen, packet->handle); + return; + } + + /* HA-FA Key */ + HAFAKey = aaaFindAvpPtr(packet, HA_FA_KEY, &HAFAKeyLen); + if (!HAFAKey || !HAFAKeyLen) { + syslog(LOG_ERR, "ERROR: bad packet (no HA_FA_KEY)"); + aaaSendErrorResponse(MOBILE_IP_OPEN_SESSION_INDICATION_RESPONSE, + -8, mnNAI, mnNAILen, packet->handle); + return; + } + + + /* MN-FA Key */ + MNFAKey = aaaFindAvpPtr(packet, MN_FA_KEY, &MNFAKeyLen); + if (!MNFAKey || !MNFAKeyLen) { + syslog(LOG_ERR, "ERROR: bad packet (no MN_FA_KEY)"); + aaaSendErrorResponse(MOBILE_IP_OPEN_SESSION_INDICATION_RESPONSE, + -10, mnNAI, mnNAILen, packet->handle); + return; + } + /* MN-HA Spi */ + if (!aaaFindAvpInt(packet, MN_HA_SPI, (int *)&MNHASpi)) { + syslog(LOG_ERR, "ERROR: bad packet (no MN_HA_SPI)"); + aaaSendErrorResponse(MOBILE_IP_OPEN_SESSION_INDICATION_RESPONSE, + -12, mnNAI, mnNAILen, packet->handle); + return; + } + /* MN-HA Key */ + MNHAKey = aaaFindAvpPtr(packet, MN_HA_KEY, &MNHAKeyLen); + if (!MNHAKey || !MNHAKeyLen) { + syslog(LOG_ERR, "ERROR: bad packet (no MN_HA_KEY)"); + aaaSendErrorResponse(MOBILE_IP_OPEN_SESSION_INDICATION_RESPONSE, + -13, mnNAI, mnNAILen, packet->handle); + return; + } + /* HA-MN Key */ + HAMNKey = aaaFindAvpPtr(packet, HA_MN_KEY, &HAMNKeyLen); + if (!HAMNKey || !HAMNKeyLen) { + syslog(LOG_ERR, "ERROR: bad packet (no HA_MN_KEY)"); + aaaSendErrorResponse(MOBILE_IP_OPEN_SESSION_INDICATION_RESPONSE, + -14, mnNAI, mnNAILen, packet->handle); + return; + } + + /* Session Timeout */ + if (!aaaFindAvpInt(packet, SESSION_TIMEOUT, (int *)&sessionTimeout)) { + /* BAD error . . . malformed packet */ + syslog(LOG_ERR, "ERROR: bad packet (no SESSION_TIMEOUT)"); + aaaSendErrorResponse(MOBILE_IP_OPEN_SESSION_INDICATION_RESPONSE, + -15, mnNAI, mnNAILen, packet->handle); + return; + } + + /* + * + * Add NAI to hash on home agent side so accounting messages + * can be generated. + */ + (void) addNaiToHash(mnNAI, mnNAILen, NULL, 0, homeAddress, + homeAgentAddress, messageHdr); + + /* + * Create our keys + */ + if (aaaCreateKey(FAHASpi, HAFAKey, HAFAKeyLen, sessionTimeout) < 0) { + syslog(LOG_ERR, "Error: Invalid FA-HA SPI/Key pair"); + aaaSendErrorResponse(MOBILE_IP_OPEN_SESSION_INDICATION_RESPONSE, + -17, mnNAI, mnNAILen, packet->handle); + return; + } + + if (aaaCreateKey(MNHASpi, HAMNKey, HAMNKeyLen, sessionTimeout) < 0) { + syslog(LOG_ERR, "Error: Invalid MN-HA SPI/Key pair"); + aaaSendErrorResponse(MOBILE_IP_OPEN_SESSION_INDICATION_RESPONSE, + -18, mnNAI, mnNAILen, packet->handle); + return; + } + aaaCreateAgent(packet, HOME_AGENT_ADDRESS, FAHASpi, sessionTimeout); + + /* + * If we don't already have a message header, + * allocate one. + */ + if (messageHdr == NULL) { + if ((messageHdr = AllocateMessageHdr()) == NULL) { + syslog(LOG_CRIT, + "Unable to allocate a message header"); + aaaSendErrorResponse( + MOBILE_IP_OPEN_SESSION_INDICATION_RESPONSE, + -19, mnNAI, mnNAILen, packet->handle); + return; + } + } + aaaSetupMessageHdr(messageHdr, 0); + (void) memcpy(messageHdr->pkt, regReq, regReqLen); + messageHdr->pktLen = regReqLen; + + /* Don't worry about byte ordering this. */ + messageHdr->messageHandle = packet->handle; + + /* Copy our fa NAI */ + messageHdr->faNAI = malloc(faNaiLen + 1); + if (messageHdr->faNAI == NULL) { + syslog(LOG_CRIT, + "Unable to allocate a faNAI in message header"); + aaaSendErrorResponse( + MOBILE_IP_OPEN_SESSION_INDICATION_RESPONSE, + -20, faNai, faNaiLen, packet->handle); + return; + } + + (void) memcpy(messageHdr->faNAI, faNai, faNaiLen); + messageHdr->faNAI[faNaiLen] = 0; /* Drop the null */ + messageHdr->faNAILen = faNaiLen; + + messageHdr->mnAAASPI = 0; /* WORK - PRC - where do we get this */ + messageHdr->algorithm = MD5; + messageHdr->mnHaSPI = MNHASpi; + (void) memcpy(messageHdr->mnHaKey, MNHAKey, MNHAKeyLen); + messageHdr->mnHaKeyLen = MNHAKeyLen; + + (void) memcpy(messageHdr->mnFaKey, MNFAKey, MNFAKeyLen); + messageHdr->mnFaKeyLen = MNFAKeyLen; + + + messageHdr->faHaSPI = FAHASpi; + + messageHdr->aaaSessionTimeout = sessionTimeout; + /* + * 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 +processAuthFailure(MessageHdr *msgHdr, char *mnNAI, size_t mnNAILen, + uint32_t result) +{ + uint32_t code; + + syslog(LOG_ERR, "Error: processAuthFailure: %*.*", + mnNAILen, mnNAILen, mnNAI); + + switch (result) { + case MIP_ADMINISTRATIVELY_PROHIBITED: + code = FA_ADM_PROHIBITED; + break; + case MIP_INSUFFICIENT_RESOURCES: + code = FA_INSUFFICIENT_RESOURCES; + break; + case MIP_FAILED_AUTHENTICATION: + code = FA_MN_AUTH_FAILURE; + break; + case MIP_REASON_UNSPECIFIED: + default: + code = FA_REASON_UNSPECIFIED; + break; + } + + rejectFromFAToMN(msgHdr, msgHdr->ifEntry, code); + + +} /* processAuthFailure */ + +#ifdef TEST_DIAMETER +static void +aaaGenerateKey(unsigned char *key, size_t keyLen) +{ + static boolean_t initialized = _B_FALSE; + int32_t *intPtr; + int i; + + /* Seed the random number generator once */ + if (initialized == _B_FALSE) { + srand(time(NULL)); + initialized = _B_TRUE; + } + + if (keyLen % 4) { + syslog(LOG_ERR, + "ERROR: Key length must be a multiple of 4 (len = %d)", + keyLen); + } + + /* LINTED E_BAD_PTR_CAST_ALIGN */ + intPtr = (int32_t *)key; + + /* Build the key, 4 bytes at a time */ + for (i = 0; i < (keyLen / 4); i++) { + intPtr[i] = rand(); + } +} /* aaaGenerateKey */ + +static uint32_t +aaaGenerateSpi() +{ + static uint32_t SPI = 0x80000000; + 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 +aaaSendRegistrationReply(MessageHdr *messageHdr, size_t replyLen, + ipaddr_t homeAddress, ipaddr_t homeAgentAddress) +{ + unsigned char buffer[MAX_TCP_LEN]; + AAA_Packet *packet; + uint32_t length; + int resultCode = 0; /* WORK -- pass this in */ + + /* Check to see if we are initialized */ + if (!gbl_hashInitialized) { + (void) InitHash(&naiHash); + naiHash.uniqueData = 1; /* Set our unique flag */ + gbl_hashInitialized = 1; + } + + /* Build the message */ + /* LINTED E_BAD_PTR_CAST_ALIGN */ + packet = (AAA_Packet *)buffer; + packet->protocol = htonl(DIAMETER); + packet->commandCode = + htonl(MOBILE_IP_OPEN_SESSION_INDICATION_RESPONSE); + + packet->handle = messageHdr->messageHandle; + + length = sizeof (AAA_Packet); + + /* Add our AVPs */ + + /* Mobile Node NAI */ + length += aaaAddAvp(MOBILE_NODE_NAI, &buffer[length], + MAX_TCP_LEN - length, messageHdr->mnNAI, + messageHdr->mnNAILen); + + /* Foreign Agent NAI */ + length += aaaAddAvp(FOREIGN_AGENT_NAI, &buffer[length], + MAX_TCP_LEN - length, messageHdr->faNAI, + messageHdr->faNAILen); + + /* Registration Reply */ + length += aaaAddAvp(REGISTRATION_REPLY, &buffer[length], + MAX_TCP_LEN - length, messageHdr->pkt, + replyLen); + + /* Mobile Node Home Address */ + length += aaaAddAvp(MOBILE_NODE_HOME_ADDRESS, &buffer[length], + MAX_TCP_LEN - length, &homeAddress, + sizeof (ipaddr_t)); + + /* Home Agent Address */ + length += aaaAddAvp(HOME_AGENT_ADDRESS, &buffer[length], + MAX_TCP_LEN - length, &homeAgentAddress, + sizeof (ipaddr_t)); + + /* Session Timeout */ + length += aaaAddAvp(SESSION_TIMEOUT, &buffer[length], + MAX_TCP_LEN - length, &messageHdr->aaaSessionTimeout, + sizeof (uint32_t)); + + /* Result Code */ + length += aaaAddAvp(RESULT_CODE, &buffer[length], + MAX_TCP_LEN - length, &resultCode, + sizeof (uint32_t)); + + packet->length = htonl(length); + + return (sendTCPPacket(buffer, length)); +} /* 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 +AAAAuthenticateRegReq(unsigned char *reqPtr, uint32_t reqLen, + unsigned char *mnNAI, size_t mnNAILen, uint32_t aaaSPI, + unsigned char *mnChallengeResponse, uint32_t mnChallengeResponseLen, + uint32_t mnChallengeLen, + ipaddr_t homeAddress, ipaddr_t homeAgentAddress, boolean_t isFromHA, + uint32_t inIfindex, void *messageHdr, unsigned char *MNFAChallengeValue, + uint32_t MNFAChallengeValueLen) +{ + unsigned char buffer[MAX_TCP_LEN]; + AAA_Packet *packet; + uint32_t length; + ipaddr_t faAddr; + uint32_t one = 1; + uint32_t zero = 0; + + + /* Check to see if we are initialized */ + if (!gbl_hashInitialized) { + (void) InitHash(&naiHash); + naiHash.uniqueData = 1; /* Set our unique flag */ + } + + /* Build the message */ + /* LINTED E_BAD_PTR_CAST_ALIGN */ + packet = (AAA_Packet *)buffer; + packet->protocol = htonl(aaaProtocol); + packet->commandCode = htonl(MOBILE_IP_OPEN_SESSION_REQUEST); + packet->handle = htonl(0); + + 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) { + length += aaaAddAvp(IS_FROM_HA, &buffer[length], + MAX_TCP_LEN - length, + (isFromHA) ? &one : &zero, sizeof (uint32_t)); + } + + /* Mobile Node NAI */ + length += aaaAddAvp(MOBILE_NODE_NAI, &buffer[length], + MAX_TCP_LEN - length, mnNAI, mnNAILen); + + /* Foreign Agent NAI */ + length += aaaAddAvp(FOREIGN_AGENT_NAI, &buffer[length], + MAX_TCP_LEN - length, maNai, strlen(maNai)); + + /* Foreign Agent Address */ + faAddr = getClosestInterfaceAddr(homeAgentAddress); + length += aaaAddAvp(FOREIGN_AGENT_ADDRESS, &buffer[length], + MAX_TCP_LEN - length, &faAddr, sizeof (uint32_t)); + + /* Registration Request Packet */ + length += aaaAddAvp(REGISTRATION_REQUEST, &buffer[length], + MAX_TCP_LEN - length, reqPtr, reqLen); + + /* Challenge Bytes */ + length += aaaAddAvp(NUMBER_OF_CHALLENGE_BYTES_IN_RR, + &buffer[length], MAX_TCP_LEN - length, + &mnChallengeLen, sizeof (uint32_t)); + + /* Mobile Node Response */ + length += aaaAddAvp(MOBILE_NODE_RESPONSE, + &buffer[length], MAX_TCP_LEN - length, + mnChallengeResponse, mnChallengeResponseLen); + + + /* Mobile Node Home Address */ + length += aaaAddAvp(MOBILE_NODE_HOME_ADDRESS, &buffer[length], + MAX_TCP_LEN - length, &homeAddress, sizeof (uint32_t)); + + /* Home Agent Address */ + length += aaaAddAvp(HOME_AGENT_ADDRESS, &buffer[length], + MAX_TCP_LEN - length, &homeAgentAddress, sizeof (uint32_t)); + + /* MN-AAA SPI */ + length += aaaAddAvp(MN_AAA_SPI, &buffer[length], + MAX_TCP_LEN - length, &aaaSPI, sizeof (uint32_t)); + + /* Radius needs MN-FA Challenge Value for authentication */ + if (aaaProtocol == RADIUS) { + length += aaaAddAvp(MN_FA_CHALLENGE_VALUE, &buffer[length], + MAX_TCP_LEN - length, MNFAChallengeValue, + MNFAChallengeValueLen); + } + + /* MN_HANDLE (only send if from FA in which case inIfindex is nonzero */ + if (inIfindex != 0) { + length += aaaAddAvp(MN_HANDLE, &buffer[length], + MAX_TCP_LEN - length, &inIfindex, sizeof (uint32_t)); + } + + packet->length = htonl(length); + + /* Add entry to hash */ + if (addNaiToHash((char *)mnNAI, mnNAILen, mnChallengeResponse, + mnChallengeResponseLen, homeAddress, homeAgentAddress, + messageHdr)) { + /* Error! */ + return (-1); + } + return (sendTCPPacket(buffer, length)); +} /* 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 +sendCloseSession(AAA_Packet *srcPacket, char *mnNAI, size_t mnNAILen) +{ + unsigned char buffer[MAX_TCP_LEN]; + AAA_Packet *destPacket; + size_t length; + int32_t handle; + char *faNai; + size_t faNaiLen; + ipaddr_t homeAgentAddress, homeAddress; + uint32_t sessionTime; + + /* Lookup the NAI to make sure it exists. */ + handle = aaaLookupHandle(mnNAI, mnNAILen); + if (handle < 0) { + syslog(LOG_ERR, "Error: NAI not found!"); + return (handle); + } + + /* Retrieve Our Fields */ + faNai = (char *)aaaFindAvpPtr(srcPacket, FOREIGN_AGENT_NAI, &faNaiLen); + if (!faNai) { + syslog(LOG_ERR, "Error: Foreign Agent NAI not found!"); + return (-1); + } + + if (!aaaFindAvpInt(srcPacket, MOBILE_NODE_HOME_ADDRESS, + (int *)&homeAddress)) { + syslog(LOG_ERR, "Error: Home Address not found!"); + return (-1); + } + if (!aaaFindAvpInt(srcPacket, HOME_AGENT_ADDRESS, + (int *)&homeAgentAddress)) { + syslog(LOG_ERR, "Error: Home Agent Address not found!"); + return (-1); + } + if (!aaaFindAvpInt(srcPacket, SESSION_TIME, (int *)&sessionTime)) { + syslog(LOG_ERR, "Error: Session Time not found!"); + return (-1); + } + + /* Build the message */ + /* LINTED E_BAD_PTR_CAST_ALIGN */ + destPacket = (AAA_Packet *)buffer; + destPacket->protocol = htonl(aaaProtocol); + destPacket->commandCode = htonl(MOBILE_IP_CLOSE_SESSION_REQUEST); + destPacket->handle = htonl(handle); + + length = sizeof (AAA_Packet); + + /* Add our AVPs */ + /* Mobile Node NAI */ + length += aaaAddAvp(MOBILE_NODE_NAI, &buffer[length], + MAX_TCP_LEN - length, mnNAI, mnNAILen); + + /* Foreign Agent NAI */ + length += aaaAddAvp(FOREIGN_AGENT_NAI, &buffer[length], + MAX_TCP_LEN - length, faNai, faNaiLen); + + /* Mobile Node Home Address */ + length += aaaAddAvp(MOBILE_NODE_HOME_ADDRESS, &buffer[length], + MAX_TCP_LEN - length, &homeAddress, sizeof (uint32_t)); + + /* Home Agent Address */ + length += aaaAddAvp(HOME_AGENT_ADDRESS, &buffer[length], + MAX_TCP_LEN - length, &homeAgentAddress, sizeof (uint32_t)); + + /* Mobile Node Session Time */ + length += aaaAddAvp(SESSION_TIME, &buffer[length], + MAX_TCP_LEN - length, &sessionTime, sizeof (uint32_t)); + + destPacket->length = htonl(length); + + return (sendTCPPacket(buffer, length)); +} /* sendCloseSession */ + +static int +sendCloseSessionAnswer(AAA_Packet *srcPacket, char *mnNAI, size_t mnNAILen, + boolean_t result) +{ + unsigned char buffer[MAX_TCP_LEN]; + AAA_Packet *destPacket; + size_t length; + uint32_t resultCode; + + /* LINTED */ + destPacket = (AAA_Packet *)buffer; + destPacket->protocol = htonl(srcPacket->protocol); + destPacket->commandCode = htonl(MOBILE_IP_CLOSE_SESSION_ANSWER); + destPacket->handle = srcPacket->handle; + + length = sizeof (AAA_Packet); + + /* Add our AVPs */ + + /* Mobile Node NAI */ + length += aaaAddAvp(MOBILE_NODE_NAI, &buffer[length], + MAX_TCP_LEN - length, mnNAI, mnNAILen); + + /* Result code */ + resultCode = result ? 0 : 1; + length += aaaAddAvp(RESULT_CODE, &buffer[length], MAX_TCP_LEN - length, + &resultCode, sizeof (uint32_t)); + + destPacket->length = htonl(length); + + return (sendTCPPacket(buffer, length)); +} + + + + +/* + * 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 +sendAccountingRecord(AAA_CommandCode code, unsigned char *mnNAI, + /* LINTED E_FUNC_ARG_UNUSED */ + size_t mnNAILen, ipaddr_t homeAddress, ipaddr_t coaAddr, + ipaddr_t homeAgentAddress, uint32_t sessionTime, int32_t relindicator) +{ + unsigned char buffer[MAX_TCP_LEN]; + AAA_Packet *packet; + uint32_t length; + int32_t handle; + + /* Lookup the NAI to make sure it exists. */ + handle = aaaLookupHandle((char *)mnNAI, mnNAILen); + if (handle < 0) { + syslog(LOG_ERR, "Error: NAI not found!"); + return (handle); + } + + switch (code) { + /* These codes are good */ + case MOBILE_IP_ACCOUNTING_START_REQUEST: + case MOBILE_IP_ACCOUNTING_INTERIM_REQUEST: + case MOBILE_IP_ACCOUNTING_STOP_REQUEST: + break; + /* Everything else is an error */ + default: + syslog(LOG_ERR, "ERROR: invalid code passed to " + "sendAccountingRecord (%d)", code); + return (-1); + } /* switch code */ + + /* Build the message */ + /* LINTED E_BAD_PTR_CAST_ALIGN */ + packet = (AAA_Packet *)buffer; + packet->protocol = htonl(aaaProtocol); + packet->commandCode = htonl(code); + packet->handle = htonl(handle); + + length = sizeof (AAA_Packet); + + /* Add our AVPs */ + + /* Mobile Node NAI */ + length += aaaAddAvp(MOBILE_NODE_NAI, &buffer[length], + MAX_TCP_LEN - length, mnNAI, mnNAILen); + + /* Foreign Agent NAI */ + length += aaaAddAvp(FOREIGN_AGENT_NAI, &buffer[length], + MAX_TCP_LEN - length, maNai, strlen(maNai)); + + /* Mobile Node Home Address */ + length += aaaAddAvp(MOBILE_NODE_HOME_ADDRESS, &buffer[length], + MAX_TCP_LEN - length, &homeAddress, sizeof (uint32_t)); + + /* Home Agent Address */ + length += aaaAddAvp(HOME_AGENT_ADDRESS, &buffer[length], + MAX_TCP_LEN - length, &homeAgentAddress, sizeof (uint32_t)); + + /* Mobile Node Session Time */ + length += aaaAddAvp(SESSION_TIME, &buffer[length], + MAX_TCP_LEN - length, &sessionTime, sizeof (uint32_t)); + + /* Release Indicator (RADIUS ONLY) */ + if (aaaProtocol == RADIUS) { + length += aaaAddAvp(RELEASE_INDICATOR, &buffer[length], + MAX_TCP_LEN - length, &relindicator, sizeof (uint32_t)); + } + packet->length = htonl(length); + + return (sendTCPPacket(buffer, length)); + +} /* sendAccountingRecord */ + +void +aaaSendErrorResponse(uint32_t commandCode, int32_t returnCode, char *mnNAI, + size_t mnNAILen, uint32_t handle) +{ + unsigned char buffer[MAX_TCP_LEN]; + AAA_Packet *packet; + uint32_t length; + + /* + * If we don't already have a handle, look for it. + */ + if (handle == 0) { + /* Lookup the NAI to make sure it exists. */ + handle = aaaLookupHandle(mnNAI, mnNAILen); + if (((int)handle) < 0) { + syslog(LOG_ERR, "Error: NAI not found!"); + handle = 0; + } + } + + /* Build the message */ + /* LINTED E_BAD_PTR_CAST_ALIGN */ + packet = (AAA_Packet *)buffer; + /* this function is only used for DIAMETER */ + packet->protocol = htonl(DIAMETER); + packet->commandCode = htonl(commandCode); + packet->handle = htonl(handle); + + length = sizeof (AAA_Packet); + + /* Mobile Node NAI */ + length += aaaAddAvp(MOBILE_NODE_NAI, &buffer[length], + MAX_TCP_LEN - length, mnNAI, mnNAILen); + + /* resultCode */ + length += aaaAddAvp(RESULT_CODE, &buffer[length], + MAX_TCP_LEN - length, &returnCode, sizeof (uint32_t)); + + packet->length = htonl(length); + + /* Send packet */ + (void) sendTCPPacket(buffer, length); +} /* 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 +initTCPSocket(in_port_t port) +{ + struct sockaddr_in sin; + int sinLength; + static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER; + int rc; + + /* + * Make sure only one thread at a time executes this code. + */ + + rc = pthread_mutex_lock(&lock); + if (rc < 0) { + /* Wierd error! */ + syslog(LOG_CRIT, "initTCPSocket: Error: Unable to lock mutex!"); + return (-1); + } + + if (gbl_TCPSocket != -1) { + /* We are already initialized. Exit */ + syslog(LOG_WARNING, + "initTCPSocket: Warning: socket already initialized"); + (void) pthread_mutex_unlock(&lock); + return (gbl_TCPSocket); + } + + /* + * Get a socket. + */ + if ((gbl_TCPSocket = socket(AF_INET, SOCK_STREAM, 0)) < 0) { + syslog(LOG_CRIT, + "initTCPSocket: socket failed (%d:%s)", errno, + strerror(errno)); + gbl_TCPSocket = -1; + (void) pthread_mutex_unlock(&lock); + return (gbl_TCPSocket); + } + + /* + * Initialize the sockaddr. + */ + sinLength = sizeof (struct sockaddr_in); + (void) memset((char *)&sin, '\0', sinLength); + + /* + * Get server's listening port number + */ + sin.sin_port = htons(port); + sin.sin_family = AF_INET; + sin.sin_addr.s_addr = inet_addr(gbl_aaaHost); + + if (connect(gbl_TCPSocket, (struct sockaddr *)&sin, sinLength) < 0) { + syslog(LOG_ERR, + "initTCPSocket: connect failed (%d:%s)", errno, + strerror(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, + * advBusy can be set/unset for other future purposes + * in mipagent. + */ + if (advBusy == _B_TRUE) { + enableService(); + } + + /* 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 +sendTCPPacket(unsigned char *buffer, uint32_t length) +{ + + 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 { + rc = send(gbl_TCPSocket, buffer, length, 0); + } while ((rc == -1) && (errno == EINTR)); + + if (rc < 0) { + syslog(LOG_ERR, "sendTCPPacket: Error: send: !(%d:%s)", errno, + strerror(errno)); + (void) close(gbl_TCPSocket); + gbl_TCPSocket = -1; /* Force a re-connect */ + 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 +readTCPPacket(unsigned char *buffer, uint32_t bufLen) +{ + 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 { + rc = recv(gbl_TCPSocket, buffer, bufLen, 0); + } while ((rc == -1) && (errno == EINTR)); + + if (rc == -1) { + syslog(LOG_ERR, "Error %d reading socket (%d:%s)", rc, errno, + strerror(errno)); + (void) close(gbl_TCPSocket); + gbl_TCPSocket = -1; /* Force a re-connect */ + } + + 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 +main(int argc, char *argv[]) +{ + 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 + }; + ipaddr_t homeAddress; + ipaddr_t homeAgentAddress; + + if (argc != 2) { + (void) fprintf(stderr, "USAGE: %s <port>\n", argv[0]); + return (-1); + } + + homeAddress = inet_addr("192.168.168.2"); + homeAgentAddress = inet_addr("192.168.168.1"); + gbl_aaaPort = (in_port_t)atoi(argv[1]); + + (void) strcpy(maNai, FA_NAI); + + if ((rc = startAAATaskThread()) != 0) { + (void) fprintf(stderr, + "Error: rc = %d when calling startAAATaskThread\n", + rc); + return (rc); + } + + rc = AAAAuthenticateRegReq(reqPtr, sizeof (reqPtr), + TEST_NAI1, strlen(TEST_NAI1), + mnChallenge, sizeof (mnChallenge), + homeAddress, homeAgentAddress, _B_FALSE, NULL); + if (rc != 0) { + (void) fprintf(stderr, + "Error: rc = %d when calling" + " AAAAuthenticatateRegReq\n", rc); + /* return (rc); */ + } + + rc = sendAccountingStartRecord((unsigned char *)TEST_NAI1, + strlen(TEST_NAI1), 0, 1, 2, 3); + if (rc != 0) { + (void) fprintf(stderr, + "Error: rc = %d when calling" + " sendAccountingStartRecord\n", + rc); + } + + (void) fprintf(stderr, "Sleeping for 1\n"); fflush(stderr); + (void) sleep(1); + rc = sendAccountingInterimRecord((unsigned char *)TEST_NAI1, + strlen(TEST_NAI1), 0, 1, 2, 3); + if (rc != 0) { + (void) fprintf(stderr, "Error: rc = %d when calling " + "sendAccountingInterimRecord\n", rc); + } + + rc = sendAccountingInterimRecord((unsigned char *)TEST_NAI2, + strlen(TEST_NAI1), 0, 1, 2, 3); + if (rc == 0) { + (void) fprintf(stderr, "Error: rc = %d when calling " + "sendAccountingInterimRecord\n", rc); + } + + rc = sendAccountingStopRecord((unsigned char *)TEST_NAI1, + strlen(TEST_NAI1), 0, 1, 2, 3); + if (rc != 0) { + (void) fprintf(stderr, "Error: rc = %d when calling " + "sendAccountingStopRecord\n", rc); + } + + (void) fprintf(stderr, "Sleeping for a sec . . . "); + (void) sleep(2); + (void) fprintf(stderr, + "Sending a straggling message . . . should be an error\n"); + rc = sendAccountingInterimRecord((unsigned char *)TEST_NAI1, + strlen(TEST_NAI1), 0, 1, 2, 3); + if (rc == 0) { + (void) fprintf(stderr, "Error: rc = %d when calling " + "sendAccountingInterimRecord\n", rc); + } + + (void) fprintf(stderr, "Thread hanging . . .\n"); fflush(stderr); + for (;;) { + (void) sleep(1); fflush(stderr); fflush(stdout); + } + + return (rc); +} /* main */ + +#endif /* TEST_AAA */ + +/* fin aaa.c */ |