Files
m241Neelima/components/ip_net2-6.9/ipdhcpc6/src/ipdhcpc6_auth.c
T
2026-07-30 11:19:44 +05:30

647 lines
19 KiB
C

/*
******************************************************************************
* INTERPEAK SOURCE FILE
******************************************************************************
*/
/*
* Copyright (C) 2007 Wind River Systems Inc.
*/
/*
****************************************************************************
* 1 DESCRIPTION
****************************************************************************
*/
/*
****************************************************************************
* 2 CONFIGURATION
****************************************************************************
*/
#include "ipdhcpc6_config.h"
/*
****************************************************************************
* 3 INCLUDE FILES
****************************************************************************
*/
#define IPCOM_USE_CLIB_PROTO
#include <ipcom_clib.h>
#include <ipcom_err.h>
#include <ipcom_hash.h>
#include <ipcom_list.h>
#include <ipcom_sock.h>
#include <ipcom_sock6.h>
#include <ipcom_syslog.h>
#include <ipcom_time.h>
#include <ipcom_tmo2.h>
#include "ipdhcpc6.h"
#include "ipdhcpc6_h.h"
#include "ipdhcpc6_msg.h"
#include "ipdhcpc6_if.h"
#include "ipdhcpc6_info.h"
#include "ipdhcpc6_ia.h"
#include "ipdhcpc6_sock.h"
#include "ipdhcpc6_msg.h"
#include "ipdhcpc6_duid.h"
#include "ipdhcpc6_authhash.h"
#include "ipdhcpc6_auth.h"
#ifdef OPENSSL_FIPS
#include <openssl/fips.h>
#endif
/*
****************************************************************************
* 4 DEFINES
****************************************************************************
*/
/*
****************************************************************************
* 5 TYPES
****************************************************************************
*/
/*
****************************************************************************
* 6 EXTERN PROTOTYPES
****************************************************************************
*/
/*
****************************************************************************
* 7 LOCAL PROTOTYPES
****************************************************************************
*/
/*
****************************************************************************
* 8 DATA
****************************************************************************
*/
/*
****************************************************************************
* 9 STATIC FUNCTIONS
****************************************************************************
*/
/* IPCOM crypto */
#ifdef IPMCRYPTO
#include <ip/openssl/md5.h>
#else
#include <openssl/md5.h>
#endif
/*
*===========================================================================
* ipdhcpc6_hmac_md5
*===========================================================================
* Description: Create a md5 hmac of key and data
* Parameters:
* Returns:
*
*/
IP_GLOBAL void
ipdhcpc6_hmac_md5(Ip_u8 *key, Ip_u32 key_len, Ip_u8 *data, Ip_u32 len, Ip_u8 *mac)
{
MD5_CTX context;
Ip_u8 k_ipad[65];
Ip_u8 k_opad[65];
Ip_u8 tk[16];
int i;
int status;
#define IPDHCPC6_HMAC_ERR_OUT(log)\
{\
IPCOM_LOG1(WARNING, "ipdhcpc6: %s error\n",log);\
(void)ipcom_errno_set(IPCOM_ERR_AUTH_FAILED);\
return;\
}
#ifdef OPENSSL_FIPS
/* MD5 cannot be used */
if (FIPS_mode())
{
/* Authentication failed. */
IPCOM_LOG0(WARNING, "ipdhcpc6: MD5 is not supported when FIPS mode is enabled!");
(void)ipcom_errno_set(IPCOM_ERR_AUTH_FAILED);
return;
}
#endif
/* Key longer than 64 bytes reset it to key = MD5(key) */
if (key_len > 64)
{
/* MD5_XXX returns 0 when fails */
status = MD5_Init(&context);
if (0 == status)
{
IPDHCPC6_HMAC_ERR_OUT("MD5_Init");
}
status = MD5_Update(&context, key, key_len);
if (0 == status)
{
IPDHCPC6_HMAC_ERR_OUT("MD5_Update");
}
status = MD5_Final(tk, &context);
if (0 == status)
{
IPDHCPC6_HMAC_ERR_OUT("MD5_Final");
}
key = tk;
key_len = 16;
}
/* Start storing key in pads */
ipcom_memset(k_ipad, 0, sizeof(k_ipad));
ipcom_memset(k_opad, 0, sizeof(k_opad));
ipcom_memcpy(k_ipad, key, key_len);
ipcom_memcpy(k_opad, key, key_len);
/* XOR key with ipad and opad values */
for (i = 0; i < 64; i++)
{
k_ipad[i] ^= 0x36;
k_opad[i] ^= 0x5c;
}
/* Do inner MD5 */
status = MD5_Init(&context);
if (0 == status)
{
IPDHCPC6_HMAC_ERR_OUT("MD5_Init");
}
status = MD5_Update(&context, k_ipad, 64);
if (0 == status)
{
IPDHCPC6_HMAC_ERR_OUT("MD5_Update");
}
status = MD5_Update(&context, data, len);
if (0 == status)
{
IPDHCPC6_HMAC_ERR_OUT("MD5_Update");
}
status = MD5_Final(mac, &context);
if (0 == status)
{
IPDHCPC6_HMAC_ERR_OUT("MD5_Final");
}
/* Do outer MD5 */
status = MD5_Init(&context);
if (0 == status)
{
IPDHCPC6_HMAC_ERR_OUT("MD5_Init");
}
status = MD5_Update(&context, k_opad, 64);
if (0 == status)
{
IPDHCPC6_HMAC_ERR_OUT("MD5_Update");
}
status = MD5_Update(&context, mac, 16);
if (0 == status)
{
IPDHCPC6_HMAC_ERR_OUT("MD5_Update");
}
status = MD5_Final(mac, &context);
if (0 == status)
{
IPDHCPC6_HMAC_ERR_OUT("MD5_Final");
}
return;
}
IP_STATIC Ip_u32 ipdhcpc6_replay_ctr_high = 0xdeadbeef;
IP_STATIC Ip_u32 ipdhcpc6_replay_ctr_low = 0x0;
IP_GLOBAL void ipdhcpc6_get_replay_counters (Ip_u32 *high, Ip_u32 *low)
{
*high = ipdhcpc6_replay_ctr_high;
*low = ipdhcpc6_replay_ctr_low;
}
IP_GLOBAL void ipdhcpc6_set_replay_counters (Ip_u32 high, Ip_u32 low)
{
ipdhcpc6_replay_ctr_high = high;
ipdhcpc6_replay_ctr_low = low;
}
/*
****************************************************************************
* 10 GLOBAL FUNCTIONS
****************************************************************************
*/
IP_GLOBAL Ip_bool
ipdhcps6_verify_auth (Ipdhcpc6_msg_transaction_t *trans,
Ipdhcpc6_msg_hdr_t *msg,
Ip_size_t length)
{
Ipdhcpc6_msg_option_hdr_t *auth_opt = IP_NULL;
Ip_size_t auth_opt_size = 0;
Ipdhcpc6_msg_option_hdr_t *serverid_opt = IP_NULL;
Ip_size_t serverid_opt_size = 0;
Ip_u8 hmac_digest [16];
Ip_u8 *auth_key;
int auth_key_len = 0;
union Ipdhcpc6_duid_data *server_duid;
Ip_u16 hmac_offset = 0;
Ip_u16 replay_offset = 0;
Ip_u32 replay1, replay2;
Ip_u8 *auth_realm;
int auth_realm_len = 0;
Ip_u8 *msg_buf = (Ip_u8 *) msg;
ipdhcpc6_get_auth_realm (&auth_realm_len, &auth_realm);
if (0 == auth_realm_len)
{ /* no auth realm set; thus no verification can be done */
return IP_TRUE;
}
serverid_opt = ipdhcpc6_msg_option_find(IPDHCPC6_MSG_OPTION_SERVERID,
&serverid_opt_size,
msg_buf + 4,
length - 4,
IP_NULL,
IP_NULL);
auth_opt = ipdhcpc6_msg_option_find(IPDHCPC6_MSG_OPTION_AUTH,
&auth_opt_size,
msg_buf + 4,
length - 4,
IP_NULL,
IP_NULL);
if (serverid_opt)
{
server_duid = (union Ipdhcpc6_duid_data *)
(((Ip_u8 *) serverid_opt) + 4);
/* 4 == sizeof dhcpv6 option hdr */
}
else
{ return IP_FALSE; /* no server DUID; reject packet */
}
if (auth_opt)
{ Ip_u8 *buffer = msg_buf;
Ip_u8 digest [16];
Ipdhcpc6_authtrans_dbentry *server_dbentry = IP_NULL;
if (IPCOM_SUCCESS != ipdhcpc6_get_default_auth_key (&auth_key_len,
&auth_key))
{ /* no default auth key set; thus no verification can be done */
return IP_FALSE;
}
/*
* if we have a server-spcific key, get it and use it.
*/
ipdhcpc6_find_key_for_server (server_duid, (int)serverid_opt_size,
&auth_key, &auth_key_len);
server_dbentry = ipdhcpc6_get_server_authtrans_entry (server_duid,
(int)serverid_opt_size);
if (IP_NULL == server_dbentry)
{
return IP_FALSE;
}
/*
* check and verify replay info from server
*/
replay_offset = (Ip_u16)((Ip_u8 *) auth_opt - (msg_buf));
replay_offset += (Ip_u16)7; /* option hdr len (4) + protocol + alg + rdm */
buffer = msg_buf + replay_offset;
ipcom_memcpy (&replay1, buffer, 4);
ipcom_memcpy (&replay2, buffer + 4, 4);
replay1 = ipcom_ntohl (replay1);
replay2 = ipcom_ntohl (replay2);
if (replay1 < server_dbentry->server_replay1)
{
return (IP_FALSE);
}
if ( replay1 == server_dbentry->server_replay1
&& replay2 < server_dbentry->server_replay2
)
{
return (IP_FALSE);
}
server_dbentry->server_replay1 = replay1;
server_dbentry->server_replay2 = replay2;
/*
* check and verify auth info, if needed
*/
hmac_offset = (Ip_u16)((Ip_u8 *) auth_opt - (msg_buf));
hmac_offset += (Ip_u16)((auth_opt_size + 4 /* dhcp opt hdr siz */) - 16);
buffer = msg_buf + hmac_offset;
/*
* save given digest
*/
ipcom_memcpy (digest, buffer, 16);
/*
* zero it
*/
ipcom_memset (buffer, 0, 16);
/* compute actual digest */
ipdhcpc6_hmac_md5(auth_key, auth_key_len,
msg_buf,
(Ip_u32)length,
hmac_digest);
/*
* compare it to given digest
*/
if (0 == ipcom_memcmp (hmac_digest, digest, 16))
{
return IP_TRUE;
}
else
{
return IP_FALSE;
}
}
return IP_TRUE;
}
IP_GLOBAL Ip_err
ipdhcpc6_add_auth_header (Ipdhcpc6_msg_transaction_t *trans, Ip_u16 elapsed_time)
{
Ipdhcpc6_msg_option_hdr_t *serverid_opt = IP_NULL;
Ip_size_t serverid_opt_size = 0;
union Ipdhcpc6_duid_data *server_duid;
Ip_u8 *msg_buf = (Ip_u8 *) trans->buffer.head;
Ip_size_t length = trans->buffer.offset;
Ip_u8 *auth_realm = (Ip_u8 *) "";
int auth_realm_len = 0;
ipdhcpc6_get_auth_realm (&auth_realm_len, &auth_realm);
if (0 == auth_realm_len)
{ /* no auth realm set; thus no auth handling is wanted */
/* this case is not an error. */
return IPCOM_SUCCESS;
}
/* if SOLICIT, add initial stub auth header */
if (1 == (int) trans->buffer.head [0])
{
Ip_u8 auth_req_option [] =
{
0x0, 0xb, /* option AUTH */
0x0, 0xb, /* option length (also 11) */
0x2, 0x1, /* protocol 2, algorithm 1 */
0x0, /* replay detection; monotonic counter */
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0
};
Ipdhcpc6_msg_option_hdr_t *auth_opt = IP_NULL;
Ip_size_t auth_opt_size = 0;
auth_opt = ipdhcpc6_msg_option_find(IPDHCPC6_MSG_OPTION_AUTH,
&auth_opt_size,
trans->buffer.head + 4,
trans->buffer.offset - 4,
IP_NULL,
IP_NULL);
if (auth_opt)
{
}
else
{
ipcom_memcpy (trans->buffer.head + trans->buffer.offset,
auth_req_option, 15);
/* adjust buffer size */
trans->buffer.offset += 15;
}
}
else
{
Ip_u16 elapsed_time_opt[3];
Ipdhcpc6_authtrans_dbentry *server_dbentry = IP_NULL;
Ip_u8 auth_req_option_hdr [] =
{
0x0, 0xb, /* option AUTH */
0x0, 0x0, /* option len (will fill in later) */
0x2, 0x1, /* protocol 2, algorithm 1 */
0x0, /* replay detection; monotonic counter */
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0
};
Ip_u8 auth_key_id [] = { 0x0, 0x0, 0x0, 0x1 }; /* key id 1 */
Ip_u8 hmac_digest [16];
Ip_u8 *auth_key = (Ip_u8 *) "";
int auth_key_len = 0;
Ip_u16 option_length;
Ip_u16 hmac_offset = 0;
Ipdhcpc6_msg_option_hdr_t *auth_opt = IP_NULL;
Ip_size_t auth_opt_size = 0;
serverid_opt = ipdhcpc6_msg_option_find(IPDHCPC6_MSG_OPTION_SERVERID,
&serverid_opt_size,
msg_buf + 4, /* dhcpv6 hdr size */
length - 4,
IP_NULL,
IP_NULL);
if (serverid_opt)
{
Ip_u8 *buf;
buf = (Ip_u8 *) serverid_opt;
server_duid = (union Ipdhcpc6_duid_data *)
(buf + 4);
/* 4 == sizeof dhcpv6 option hdr */
}
else
{ return IPCOM_ERR_FAILED; /* no server DUID; we cant do anything */
}
/* RFC 3315, chapter 22.9
All DHCPv6 client message must contain the Elapsed Time Option */
elapsed_time_opt[0] = ip_htons(IPDHCPC6_MSG_OPTION_ELAPSED_TIME);
elapsed_time_opt[1] = ip_htons(sizeof(elapsed_time));
elapsed_time_opt[2] = ip_htons(elapsed_time);
if (IPCOM_SUCCESS != ipdhcpc6_get_default_auth_key (&auth_key_len, &auth_key))
{
}
/*
* if we have a server-spcific key, get it and use it.
*/
ipdhcpc6_find_key_for_server (server_duid, (int)serverid_opt_size,
&auth_key, &auth_key_len);
server_dbentry = ipdhcpc6_get_server_authtrans_entry (server_duid,
(int)serverid_opt_size);
if (IP_NULL == server_dbentry)
{
return IP_FALSE;
}
auth_opt = ipdhcpc6_msg_option_find(IPDHCPC6_MSG_OPTION_AUTH,
&auth_opt_size,
trans->buffer.head + 4,
trans->buffer.offset - 4,
IP_NULL,
IP_NULL);
if (auth_opt)
{ /*
* this transaction packet already has an auth header.
* We'll just reuse it and recompute the digest section.
*/
hmac_offset = (Ip_u8 *) auth_opt - (trans->buffer.head);
hmac_offset += ((auth_opt_size + 4 /* dhcp option hdr siz */) - 16);
}
else
{
option_length = (Ip_u16)((sizeof (auth_req_option_hdr) - 4) +
auth_realm_len +
sizeof (auth_key_id) +
sizeof (hmac_digest));
ipcom_memset (hmac_digest, 0, 16);
/* fill in actual option length */
auth_req_option_hdr [2] = (Ip_u8) (option_length >> 8);
auth_req_option_hdr [3] = (Ip_u8) (option_length & 0xFF);
/* fill in replay counter */
ipdhcpc6_replay_ctr_low++;
if (0 == ipdhcpc6_replay_ctr_low++)
ipdhcpc6_replay_ctr_high++;
ipcom_memcpy (auth_req_option_hdr + 7, &ipdhcpc6_replay_ctr_high, 4);
ipcom_memcpy (auth_req_option_hdr + 11, &ipdhcpc6_replay_ctr_low, 4);
/* add auth option header */
ipdhcpc6_util_buffer_append(&(trans->buffer),
auth_req_option_hdr, sizeof (auth_req_option_hdr));
/* add auth option realm */
ipdhcpc6_util_buffer_append(&(trans->buffer),
auth_realm, auth_realm_len);
ipdhcpc6_util_buffer_append(&(trans->buffer),
auth_key_id, sizeof (auth_key_id));
/* save ptr to hmac digest */
hmac_offset = trans->buffer.offset;
ipdhcpc6_util_buffer_append(&(trans->buffer),
hmac_digest, sizeof (hmac_digest));
/*
* before we compute the digest, we append the
* same elapsed time info that ipdhcpc6_sock_send()
* is going to add to the packet.
*/
ipcom_memcpy (trans->buffer.head + trans->buffer.offset,
(Ip_u8 *) elapsed_time_opt, 6);
trans->buffer.offset += 6; /* size of elapsed time option */
/* compute actual digest */
ipdhcpc6_hmac_md5(auth_key, 14,
trans->buffer.head,
(int)trans->buffer.offset,
hmac_digest);
/* copy it in */
ipcom_memcpy (trans->buffer.head + hmac_offset, hmac_digest, 16);
/*
* now forget that we ever added the elapsed time info.
*/
trans->buffer.offset -= 6; /* size of elapsed time option */
}
}
return IPCOM_SUCCESS;
}
/*
****************************************************************************
* 11 PUBLIC FUNCTIONS
****************************************************************************
*/
/*
****************************************************************************
* END OF FILE
****************************************************************************
*/