/* ****************************************************************************** * INTERPEAK SOURCE FILE * * Copyright (C) 2007 Wind River Systems * ****************************************************************************** */ /* **************************************************************************** * 1 DESCRIPTION **************************************************************************** */ /* **************************************************************************** * 2 CONFIGURATION **************************************************************************** */ /* **************************************************************************** * 3 INCLUDE FILES **************************************************************************** */ #define IPCOM_USE_CLIB_PROTO #include #include #include #include #include #include #include #include #include #include #include #include "ipdhcpc6_duid.h" #include "ipdhcpc6_cvthex.h" #include "ipdhcpc6_authhash.h" #define IPDHCPC6_DUID_LL_ACTUALSIZE 10 #define IPDHCPC6_DUID_LLT_ACTUALSIZE 14 #define IPDHCPC6_DUID_EN_ACTUALSIZE 14 /* **************************************************************************** * 4 DEFINES **************************************************************************** */ /* **************************************************************************** * 5 TYPES **************************************************************************** */ /* **************************************************************************** * 6 EXTERN PROTOTYPES **************************************************************************** */ /* **************************************************************************** * 7 LOCAL PROTOTYPES **************************************************************************** */ /* **************************************************************************** * 8 DATA **************************************************************************** */ /* **************************************************************************** * 9 STATIC FUNCTIONS **************************************************************************** */ /* *=========================================================================== * ipdhcpc6_authdata_obj_hash *=========================================================================== * Description: Returns a hash value based on an authkey value. * Parameters: dbentry - The option to return a hash value for. * Returns: * */ IP_STATIC unsigned ipdhcpc6_authdb_obj_hash (Ipdhcpc6_authkey_dbentry *dbentry) { #ifdef DEBUG_DHCPV6_AUTH int i; unsigned char *cptr; ipcom_printf ("ipdhcpc6_authdb_obj_hash called: "); cptr = (unsigned char *) &(dbentry->duid.data.ll.link_addr); for (i = 0 ; i < 6 ; i++) { ipcom_printf ("%2x ", cptr [i]); } ipcom_printf ("\n"); #endif return ipcom_hash_update(&(dbentry->duid.data), dbentry->duid.len, 0); } /* *=========================================================================== * ipdhcpc6_authdb_key_hash *=========================================================================== * Description: Returns a hash value for a DUID key. * Parameters: key - Pointer to the Ipdhcpc6_authkey_duid structure * Returns: * */ IP_STATIC unsigned ipdhcpc6_authdb_key_hash(Ipdhcpc6_duid *key) { #ifdef DEBUG_DHCPV6_AUTH int i; unsigned char *cptr; ipcom_printf ("ipdhcpc6_authdb_key_hash called: "); cptr = (unsigned char *) &(key->data.ll.link_addr); for (i = 0 ; i < 6 ; i++) { ipcom_printf ("%2x ", cptr [i]); } ipcom_printf ("\n"); #endif return ipcom_hash_update(&(key->data), key->len, 0); } /* *=========================================================================== * ipdhcpc6_authdb_hash_cmp *=========================================================================== * Description: Compares a hash object and a hash key for equality. * Parameters: dbentry - An authkey_dbentry structure. * key - A pointer to the search key. * Returns: IP_TRUE if the search key matches the prefix entry. * */ IP_STATIC Ip_bool ipdhcpc6_authdb_hash_cmp(Ipdhcpc6_authkey_dbentry *dbentry, Ipdhcpc6_duid *key) { #ifdef DEBUG_DHCPV6_AUTH ipcom_printf ("ipdhcpc6_authdb_hash_cmp called\n"); #endif if (dbentry->duid.len != key->len) return IP_FALSE; return ipcom_memcmp(&(dbentry->duid.data), &(key->data), key->len) == 0; } /*************/ /* *=========================================================================== * ipdhcpc6_authtrans_obj_hash *=========================================================================== * Description: Returns a hash value based on an authkey value. * Parameters: dbentry - The option to return a hash value for. * Returns: * */ IP_STATIC unsigned ipdhcpc6_authtrans_obj_hash (Ipdhcpc6_authtrans_dbentry *dbentry) { #ifdef DEBUG_DHCPV6_AUTH int i; unsigned char *cptr; ipcom_printf ("ipdhcpc6_authtrans_obj_hash called: "); cptr = (unsigned char *) &(dbentry->duid.data); switch (dbentry->duid.data.generic.content) { case IPDHCPC6_DUID_LL: ipcom_printf ("LL content\n"); break; case IPDHCPC6_DUID_LLT: ipcom_printf ("LLT content\n"); break; case IPDHCPC6_DUID_EN: ipcom_printf ("EN content\n"); break; default: ipcom_printf ("default case content\n"); } for (i = 0 ; i < dbentry->duid.len ; i++) { ipcom_printf ("%2x ", cptr [i]); } ipcom_printf ("\n"); #endif return ipcom_hash_update(&(dbentry->duid.data), dbentry->duid.len, 0); } /* *=========================================================================== * ipdhcpc6_authtrans_key_hash *=========================================================================== * Description: Returns a hash value for a DUID key. * Parameters: key - Pointer to the Ipdhcpc6_authkey_duid structure * Returns: * */ IP_STATIC unsigned ipdhcpc6_authtrans_key_hash(Ipdhcpc6_duid *key) { #ifdef DEBUG_DHCPV6_AUTH int i; unsigned char *cptr; ipcom_printf ("ipdhcpc6_authtrans_key_hash called: "); cptr = (unsigned char *) &(key->data); switch (key->data.generic.content) { case IPDHCPC6_DUID_LL: ipcom_printf ("LL content\n"); break; case IPDHCPC6_DUID_LLT: ipcom_printf ("LLT content\n"); break; case IPDHCPC6_DUID_EN: ipcom_printf ("EN content\n"); break; default: ipcom_printf ("default case content\n"); } for (i = 0 ; i < key->len ; i++) { ipcom_printf ("%2x ", cptr [i]); } ipcom_printf ("\n"); #endif return ipcom_hash_update(&(key->data), key->len, 0); } /* *=========================================================================== * ipdhcpc6_authtrans_hash_cmp *=========================================================================== * Description: Compares a hash object and a hash key for equality. * Parameters: prefix - A prefix structure. * key - A pointer to the search key. * Returns: IP_TRUE if the search key matches the prefix entry. * */ IP_STATIC Ip_bool ipdhcpc6_authtrans_hash_cmp(Ipdhcpc6_authtrans_dbentry *dbentry, Ipdhcpc6_duid *key) { #ifdef DEBUG_DHCPV6_AUTH ipcom_printf ("ipdhcpc6_authtrans_hash_cmp called\n"); #endif if (dbentry->duid.len != key->len) return IP_FALSE; return ipcom_memcmp(&(dbentry->duid.data), &(key->data), key->len) == 0; } /*****************/ /* **************************************************************************** * 10 GLOBAL FUNCTIONS **************************************************************************** */ IP_STATIC Ipcom_hash *server_authkey_db; IP_STATIC Ipcom_hash *server_transaction_db; IP_GLOBAL Ipcom_hash * ipdhcpc6_authdb_hash_create(void) { return ipcom_hash_new ((Ipcom_hash_obj_func) ipdhcpc6_authdb_obj_hash, (Ipcom_hash_key_func) ipdhcpc6_authdb_key_hash, (Ipcom_hash_cmp_func) ipdhcpc6_authdb_hash_cmp); } IP_GLOBAL Ipcom_hash * ipdhcpc6_authtrans_hash_create(void) { return ipcom_hash_new ((Ipcom_hash_obj_func) ipdhcpc6_authtrans_obj_hash, (Ipcom_hash_key_func) ipdhcpc6_authtrans_key_hash, (Ipcom_hash_cmp_func) ipdhcpc6_authtrans_hash_cmp); } IP_GLOBAL Ipdhcpc6_duid *ipdhcpc6_authdb_create_ll_duid (int len, Ip_u16 hwtype, Ip_u8 *link_addr) { Ipdhcpc6_duid *retval = (Ipdhcpc6_duid *) ipcom_malloc (sizeof (struct Ipdhcpc6_duid_struct)); if (IP_NULL == retval) return IP_NULL; retval->len = IPDHCPC6_DUID_LL_ACTUALSIZE; retval->data.ll.content = IPDHCPC6_DUID_LL; retval->data.ll.hardware_type = hwtype; ipcom_memcpy (retval->data.ll.link_addr, link_addr, 6); return retval; } IP_GLOBAL Ip_err ipdhcpc6_add_server_authkey (Ipdhcpc6_duid *server_duid, Ip_u8 *key, int keylen) { Ip_u16 duid_content; Ipdhcpc6_authkey_dbentry *new_authdb_entry = (Ipdhcpc6_authkey_dbentry *) ipcom_malloc (sizeof (struct Ipdhcpc6_authkey_dbentry)); if (IP_NULL == new_authdb_entry) return IPCOM_ERR_FAILED; new_authdb_entry->secret.key = (Ip_u8 *) ipcom_malloc ((Ip_size_t)keylen); if (IP_NULL == new_authdb_entry->secret.key) { ipcom_free (new_authdb_entry); #if 0 IPCOM_LOG0 (DEBUG, "ipdhcpc6_add_server_authkey: no memory!\n"); #endif return IPCOM_ERR_NO_MEMORY; } /* * copy in key */ ipcom_memcpy (new_authdb_entry->secret.key, key, (Ip_size_t)keylen); new_authdb_entry->secret.len = keylen; /* * copy in duid data */ new_authdb_entry->duid.len = server_duid->len; duid_content = server_duid->data.ll.content; switch (duid_content) { case IPDHCPC6_DUID_LL: new_authdb_entry->duid.data.ll.hardware_type = server_duid->data.ll.hardware_type; new_authdb_entry->duid.data.ll.content = IPDHCPC6_DUID_LL; ipcom_memcpy (new_authdb_entry->duid.data.ll.link_addr, server_duid->data.ll.link_addr, 6); break; case IPDHCPC6_DUID_EN: /* not yet implemented */ case IPDHCPC6_DUID_LLT: /* not yet implemented */ default: ipcom_free (new_authdb_entry); return IPCOM_ERR_FAILED; } if (IPCOM_SUCCESS != ipcom_hash_add (server_authkey_db, new_authdb_entry)) { return IPCOM_ERR_FAILED; } return IPCOM_SUCCESS; } IP_GLOBAL Ipdhcpc6_authtrans_dbentry * ipdhcpc6_create_server_authtrans_entry (union Ipdhcpc6_duid_data *server, int len) { Ip_u16 server_duid_content; Ipdhcpc6_authtrans_dbentry *server_dbentry = IP_NULL; server_dbentry = (Ipdhcpc6_authtrans_dbentry *) ipcom_malloc (sizeof (struct Ipdhcpc6_authtrans_dbentry)); if (IP_NULL == server_dbentry) return IP_NULL; server_duid_content = ip_ntohs (server->generic.content); switch (server_duid_content) { case IPDHCPC6_DUID_LLT: server_dbentry->duid.len = IPDHCPC6_DUID_LLT_ACTUALSIZE; server_dbentry->duid.data.llt.content = IPDHCPC6_DUID_LLT; server_dbentry->duid.data.llt.hardware_type = server->llt.hardware_type; server_dbentry->duid.data.llt.time = server->llt.time; ipcom_memcpy (server_dbentry->duid.data.llt.link_addr, server->llt.link_addr, 6); break; case IPDHCPC6_DUID_EN: /* * FIXME * using the duid union structure we have, we know we will have * enough space to copy in the first 8 bytes of the enterprise * ID. This may or may not be enough to be unique; we'll need * to revisit this later. Look at "len" to see if we were given * less than 8 bytes for the ID; if so, only copy the bytes * given. */ server_dbentry->duid.len = (Ip_u16)(len < IPDHCPC6_DUID_EN_ACTUALSIZE ? len : IPDHCPC6_DUID_EN_ACTUALSIZE); server_dbentry->duid.data.en.content = IPDHCPC6_DUID_EN; server_dbentry->duid.data.en.enterprise_number_1 = server->en.enterprise_number_1; server_dbentry->duid.data.en.enterprise_number_2 = server->en.enterprise_number_2; ipcom_memcpy (server_dbentry->duid.data.en.id, server->en.id, (Ip_size_t)(8 - (IPDHCPC6_DUID_EN_ACTUALSIZE - len))); break; case IPDHCPC6_DUID_LL: server_dbentry->duid.len = IPDHCPC6_DUID_LL_ACTUALSIZE; server_dbentry->duid.data.ll.content = IPDHCPC6_DUID_LL; server_dbentry->duid.data.ll.hardware_type = server->ll.hardware_type; ipcom_memcpy (server_dbentry->duid.data.ll.link_addr, server->llt.link_addr, 6); break; #ifdef DEBUG_DHCPV6_AUTH default: ipcom_printf ("found %u (%u) duid\n", server_duid_content, ip_htons (server_duid_content)); #endif } /* * set replay counters */ server_dbentry->client_replay1 = 0xdeadbeef; server_dbentry->client_replay2 = 0x0; server_dbentry->server_replay1 = 0x0; server_dbentry->server_replay2 = 0x0; if (IPCOM_SUCCESS != ipcom_hash_add (server_transaction_db, server_dbentry)) return IP_NULL; return server_dbentry; } IP_GLOBAL Ip_err ipdhcpc6_server_no_auth (Ipdhcpc6_duid *server_dbkey) { Ipdhcpc6_authkey_dbentry *server_dbobj = IP_NULL; server_dbobj = ipcom_hash_get (server_authkey_db, server_dbkey); if (IP_NULL == server_dbobj) { ipcom_printf ("no server_dbobj\n"); return IPCOM_SUCCESS; } else { ipcom_hash_remove (server_authkey_db, server_dbobj); return IPCOM_SUCCESS; } } IP_GLOBAL Ipdhcpc6_authtrans_dbentry * ipdhcpc6_get_server_authtrans_entry (union Ipdhcpc6_duid_data *server, int len) { Ip_u16 server_duid_content; Ipdhcpc6_duid server_dbkey; Ipdhcpc6_authtrans_dbentry *server_dbobj = IP_NULL; server_duid_content = ip_ntohs (server->generic.content); switch (server_duid_content) { case IPDHCPC6_DUID_LLT: server_dbkey.len = IPDHCPC6_DUID_LLT_ACTUALSIZE; server_dbkey.data.llt.content = IPDHCPC6_DUID_LLT; server_dbkey.data.llt.hardware_type = server->llt.hardware_type; server_dbkey.data.llt.time = server->llt.time; ipcom_memcpy (server_dbkey.data.llt.link_addr, server->llt.link_addr, 6); break; case IPDHCPC6_DUID_EN: /* * FIXME * using the duid union structure we have, we know we will have * enough space to copy in the first 8 bytes of the enterprise * ID. This may or may not be enough to be unique; we'll need * to revisit this later. Look at "len" to see if we were given * less than 8 bytes for the ID; if so, only copy the bytes * given. */ server_dbkey.len = (Ip_u16)(len < IPDHCPC6_DUID_EN_ACTUALSIZE ? len : IPDHCPC6_DUID_EN_ACTUALSIZE); server_dbkey.data.en.content = IPDHCPC6_DUID_EN; server_dbkey.data.en.enterprise_number_1 = server->en.enterprise_number_1; server_dbkey.data.en.enterprise_number_2 = server->en.enterprise_number_2; ipcom_memcpy (server_dbkey.data.en.id, server->en.id, (Ip_size_t)(8 - (IPDHCPC6_DUID_EN_ACTUALSIZE - len))); break; case IPDHCPC6_DUID_LL: server_dbkey.len = IPDHCPC6_DUID_LL_ACTUALSIZE; server_dbkey.data.ll.content = IPDHCPC6_DUID_LL; server_dbkey.data.ll.hardware_type = server->ll.hardware_type; ipcom_memcpy (server_dbkey.data.ll.link_addr, server->llt.link_addr, 6); break; #ifdef DEBUG_DHCPV6_AUTH default: ipcom_printf ("found %u (%u) duid\n", server_duid_content, ip_htons (server_duid_content)); #endif } server_dbobj = ipcom_hash_get (server_transaction_db, &server_dbkey); if (IP_NULL == server_dbobj) { /* no entry yet. Make one. */ return ipdhcpc6_create_server_authtrans_entry (server, len); } return server_dbobj; } IP_STATIC Ip_u8 authKeyFromSysVars [132]; IP_STATIC Ip_size_t authKeyLenFromSysVars = 0; IP_STATIC Ip_u8 authKey [132]; IP_STATIC Ip_size_t authKeyLen = 0; #if 0 IP_STATIC Ip_u8 authRealmFromSysVars [128]; IP_STATIC Ip_size_t authRealmLenFromSysVars = 0; #endif IP_STATIC Ip_u8 ipdhcpc6_authRealm [128]; IP_STATIC Ip_size_t ipdhcpc6_authRealmlen = 0; IP_STATIC int ipdhcpc6_user_authdb_inited = 0; IP_GLOBAL Ip_err ipdhcpc6_get_default_auth_key (int *len, Ip_u8 **key) { *len = (int)authKeyLen; *key = authKey; return IPCOM_SUCCESS; } IP_GLOBAL Ip_err ipdhcpc6_set_default_auth_key (int len, Ip_u8 *key) { if (len <= (int)sizeof (authKey)) { authKeyLen = (Ip_size_t)len; ipcom_memcpy (authKey, key, (Ip_size_t)len); return IPCOM_SUCCESS; } else return IPCOM_ERR_FAILED; } IP_STATIC Ip_err ipdhcpc6_set_default_auth_key_from_sysvars (void) { authKeyLenFromSysVars = sizeof (authKeyFromSysVars); if (IP_NULL == ipcom_sysvar_get ("ipdhcpc6.authkey", (char *) authKeyFromSysVars, &authKeyLenFromSysVars)) { authKeyLen = 0; return IPCOM_ERR_FAILED; } /* * check if the value we read is a valid hex key; convert to * a key buffer. */ if (ipdhcpc6_check_is_hex_string ((char *) authKeyFromSysVars)) { /* we need at least 4 characters to proceed here; "0x--" or more */ if (authKeyLenFromSysVars >= 4) { char *kptr = (char *) authKeyFromSysVars; kptr += 2; /* get past leading "0x" */ authKeyLen = (authKeyLenFromSysVars - 2) / 2; if (! ipdhcpc6_cvt_hex_to_membuf (kptr, (unsigned char *) authKey, (int)authKeyLen) ) { authKeyLen = 0; return IPCOM_ERR_FAILED; } } } else { authKeyLen = 0; return IPCOM_ERR_FAILED; } return IPCOM_SUCCESS; } IP_GLOBAL Ip_err ipdhcpc6_get_auth_realm (int *len, Ip_u8 **realm) { *len = (int)ipdhcpc6_authRealmlen; *realm = ipdhcpc6_authRealm; return IPCOM_SUCCESS; } IP_GLOBAL Ip_err ipdhcpc6_set_auth_realm (int len, Ip_u8 *realm) { if (len <= (int)sizeof (ipdhcpc6_authRealm)) { ipdhcpc6_authRealmlen = (Ip_size_t)len; ipcom_memcpy (ipdhcpc6_authRealm, realm, (Ip_size_t)len); return IPCOM_SUCCESS; } else return IPCOM_ERR_FAILED; } IP_STATIC Ip_err ipdhcpc6_set_auth_realm_from_sysvars (void) { ipdhcpc6_authRealmlen = sizeof (ipdhcpc6_authRealm); if (IP_NULL == ipcom_sysvar_get("ipdhcpc6.authrealm", (char *) ipdhcpc6_authRealm, &ipdhcpc6_authRealmlen)) { ipdhcpc6_authRealmlen = 0; } return IPCOM_SUCCESS; } IP_GLOBAL void ipdhcpc6_authdb_init (void) { server_authkey_db = ipdhcpc6_authdb_hash_create (); server_transaction_db = ipdhcpc6_authtrans_hash_create (); ipdhcpc6_set_auth_realm_from_sysvars (); ipdhcpc6_set_default_auth_key_from_sysvars (); ipdhcpc6_user_authdb_inited = 0; /* test_server_specific (); */ }; IP_GLOBAL Ip_err ipdhcpc6_find_key_for_server (union Ipdhcpc6_duid_data *server, int duid_len, Ip_u8 **key, int *key_len) { Ip_u16 server_duid_content; Ipdhcpc6_duid server_dbkey; Ipdhcpc6_authkey_dbentry *server_dbobj = IP_NULL; server_duid_content = ip_ntohs (server->generic.content); switch (server_duid_content) { case IPDHCPC6_DUID_LLT: server_dbkey.len = IPDHCPC6_DUID_LL_ACTUALSIZE; server_dbkey.data.ll.content = IPDHCPC6_DUID_LL; server_dbkey.data.ll.hardware_type = server->llt.hardware_type; ipcom_memcpy (server_dbkey.data.ll.link_addr, server->llt.link_addr, 6); break; case IPDHCPC6_DUID_EN: /* * FIXME * using the duid union structure we have, we know we will have * enough space to copy in the first 8 bytes of the enterprise * ID. This may or may not be enough to be unique; we'll need * to revisit this later. Look at "len" to see if we were given * less than 8 bytes for the ID; if so, only copy the bytes * given. */ server_dbkey.len = (Ip_u16)(duid_len < IPDHCPC6_DUID_EN_ACTUALSIZE ? duid_len : IPDHCPC6_DUID_EN_ACTUALSIZE); server_dbkey.data.en.content = IPDHCPC6_DUID_EN; server_dbkey.data.en.enterprise_number_1 = server->en.enterprise_number_1; server_dbkey.data.en.enterprise_number_2 = server->en.enterprise_number_2; ipcom_memcpy (server_dbkey.data.en.id, server->en.id, (Ip_size_t)(8 - (IPDHCPC6_DUID_EN_ACTUALSIZE - duid_len))); break; case IPDHCPC6_DUID_LL: server_dbkey.len = IPDHCPC6_DUID_LL_ACTUALSIZE; server_dbkey.data.ll.content = IPDHCPC6_DUID_LL; server_dbkey.data.ll.hardware_type = server->ll.hardware_type; ipcom_memcpy (server_dbkey.data.ll.link_addr, server->llt.link_addr, 6); break; default: #ifdef DEBUG_DHCPV6_AUTH ipcom_printf ("find_key_for_server: found %u (%u) duid\n", server_duid_content, ip_htons (server_duid_content)); #endif return IPCOM_ERR_FAILED; } server_dbobj = ipcom_hash_get (server_authkey_db, &server_dbkey); if (IP_NULL == server_dbobj) { /* use default system auth key */ return IPCOM_ERR_FAILED; } else { /* use server specific key */ *key_len = server_dbobj->secret.len; *key = server_dbobj->secret.key; return IPCOM_SUCCESS; } } IP_GLOBAL void ipdhcpc6_block_on_user_authdb_config (void) { while (1) { if (ipdhcpc6_user_authdb_inited) return; ipcom_millisleep (250); } } IP_GLOBAL void ipdhcpc6_user_authdb_config_finished (void) { ipdhcpc6_user_authdb_inited = 1; } /* **************************************************************************** * END OF FILE **************************************************************************** */