📄 fa_reply.c
字号:
/* $Id: fa_reply.c,v 1.56 2001/09/16 17:21:59 jm Exp $ * Foreign Agent - registration reply handling * * Dynamic hierarchial IP tunnel * Copyright (C) 1998-2001, Dynamics group * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. See README and COPYING for * more details. */#ifdef HAVE_CONFIG_H#include <config.h>#endif#include <stdlib.h>#include <sys/socket.h>#include <netinet/in.h>#include <arpa/inet.h>#include <syslog.h>#include <string.h>#include <assert.h>#include "fa.h"#include "debug.h"#include "agent_utils.h"#include "md5_mac.h"#include "util.h"extern struct fa_config *config;extern struct hashtable *tunnels_hash;extern struct bindingtable *bindings_hash;extern struct binding_counters bcounters;static int forward_reply(struct bindingentry *binding, struct msg_extensions *ext, int error_reply, struct unconfirmed_request *unc);/* returns: * 0 = auth. ext. ok * 1 = ff_auth missing/invalid * 2 = fh_auth missing/invalid */static intcheck_rep_auth_ext(struct packet_from_info *info, struct msg_extensions *ext){ struct fa_spi_entry *fa_spi; fa_spi = get_fa_spi(0, info->src.sin_addr, SPI_AGENT_FA); if (fa_spi != NULL) { if (ext->ff_auth == NULL || !auth_check_vendor(AUTH_ALG_MD5, fa_spi->shared_secret, fa_spi->shared_secret_len, (unsigned char *) ext->rep, ext->ff_auth)) return 1; else return 0; } fa_spi = get_fa_spi(0, info->src.sin_addr, SPI_AGENT_HA); if (fa_spi != NULL) { if (ext->fh_auth == NULL || !auth_check(fa_spi->alg, fa_spi->shared_secret, fa_spi->shared_secret_len, (unsigned char *) ext->rep, ext->fh_auth)) return 2; else return 0; } return 0;}/* Returns: * 0: valid reply * 1: valid reply, but no Dynamics extensions * -1: invalid reply */static intvalidate_reply(struct msg_extensions* ext, struct bindingentry *binding, struct packet_from_info *info, struct unconfirmed_request *unc){ int error_code = 0, i; char *error_text = NULL; struct sockaddr_in addr; struct tunnel_data *t_data; t_data = (struct tunnel_data *) binding->data; if ((i = check_rep_auth_ext(info, ext)) != 0) { if (i == 2) { error_text = "invalid hf_auth"; error_code = REGREP_HA_FAILED_AUTH_FA; } else { /* actually this is FA-FA auth. failure, but there is * not currently any error code for it */ error_text = "invalid ff_auth"; error_code = REGREP_REASON_UNSPEC_FA; } } else if (ext->double_auth_ext > 0) { error_text = "duplicate authentication extension"; if (ext->double_auth_ext & DOUBLE_FH_AUTH) error_code = REGREP_HA_FAILED_AUTH_FA; else error_code = REGREP_REASON_UNSPEC_FA;#if 0 /* This check causes some interoperability problems at least with * PSU Mobile IP implementation. Since the src address is not really * protected, this check does not affect security and it is thus * removed at least for now. */ } else if (ext->rep->code != REGREP_UNKNOWN_HA_HA && config->highest_FA && info->src.sin_addr.s_addr != (unc ? unc->ha_addr.s_addr : binding->ha_addr.s_addr)) { error_text = "reply's source address does not match with the " "HA address"; error_code = REGREP_ADMIN_PROHIBITED_FA;#endif } else if (!config->highest_FA && info->src.sin_addr.s_addr != config->upper_fa_addr.s_addr) { error_text = "reply's source address does not match with " "the upper FA address"; error_code = REGREP_ADMIN_PROHIBITED_FA; } else if (ext->rep->code != REGREP_UNKNOWN_HA_HA && ext->rep->ha_addr.s_addr != (unc ? unc->ha_addr.s_addr : binding->ha_addr.s_addr)) { error_text = "HA address changed from request"; error_code = REGREP_ADMIN_PROHIBITED_FA; } else if (ext->rep->ha_addr.s_addr == 0) { error_text = "missing home agent"; error_code = REGREP_MISSING_HOME_AGENT_FA; } else if (ext->rep->home_addr.s_addr == 0) { error_text = "missing home address"; error_code = REGREP_MISSING_HOMEADDR_FA; } else if (unc && unc->mn_nai_included && ext->mn_nai == NULL) { error_text = "missing MN NAI"; error_code = REGREP_MISSING_NAI_FA; } else if (unc && unc->challenge && (ext->challenge == NULL || !equal_challenge(unc->challenge, ext->challenge))) { error_text = "missing challenge"; error_code = REGREP_MISSING_CHALLENGE_FA; } else if (ext->unknown_cvse) { error_text = "unknown CVSE"; error_code = REGREP_UNSUPP_VENDOR_ID_HA_FA; } else if (config->require_mnfa_sec_assoc && ext->mn_fa_key_material_aaa == NULL && get_fa_spi(0, ext->rep->home_addr, SPI_AGENT_MN) == NULL) { error_text = "missing MN-FA sec. assoc."; error_code = REGREP_MISSING_MN_FA_FA; } if (error_code > 0) { char mn[30], ha[30]; struct interface_entry *iface; dynamics_strlcpy(mn, inet_ntoa(ext->rep->home_addr), 30); dynamics_strlcpy(ha, inet_ntoa(ext->rep->ha_addr), 30); if (error_text != NULL) { LOG2(LOG_WARNING, "SRC=%s, MN=%s, HA=%s - " "reply denied: %s\n", inet_ntoa(info->src.sin_addr), mn, ha, error_text); report_discarded_msg(ext->start, ext->len, &info->src, error_text); } addr.sin_family = AF_INET; addr.sin_addr = unc ? unc->info.src.sin_addr : binding->lower_addr; addr.sin_port = unc ? unc->info.src.sin_port : binding->lower_port; iface = unc ? unc->info.iface : t_data->info.iface; if (iface == NULL || addr.sin_addr.s_addr == 0 || addr.sin_port == 0) { DEBUG(DEBUG_FLAG, "validate_reply: unknown lower end (%s:%u:%p)\n", inet_ntoa(addr.sin_addr), ntohs(addr.sin_port), iface); return -1; } send_failure_reply(error_code, ext, unc ? &unc->info : &t_data->info, NULL, 0); return -1; } /* check the existance of the Dynamics extensions */ if (!ext->sk_auth && IS_REGREP_ACCEPTED(ext->rep->code)) { DEBUG(DEBUG_FLAG, "validate_reply: missing sk_auth\n"); return 1; } if (ext->rep->lifetime != 0 && !ext->fa_pubkeyrep && !ext->fa_keyrep) { DEBUG(DEBUG_FLAG, "validate_reply: missing fa_keyrep/fa_pubkeyrep\n"); return 1; } return 0;}#ifdef USE_TEARDOWN/* Forward tear down message in the special case when the binding id field * has changed in the lower part of the tunnel; i.e. the id must be changed * by this FA and new sk_auth MAC must be calculated. * This function removes a possible mh_auth as it cannot be recalculated, so * this should be called only for tear down messages. */static inthandle_teardown_changed_id(struct bindingentry *binding, struct msg_extensions *ext, struct unconfirmed_request *unc){ unsigned char msg[MAXMSG]; int len, ret; struct reg_rep *reply; struct registration_ext_dynamics *dyn_ext; struct tunnel_data *t_data; struct in_addr addr; int port; DEBUG(DEBUG_FLAG, "handle_teardown_changed_id\n"); t_data = (struct tunnel_data *) binding->data; if (unc != NULL) { addr = unc->lower_addr; port = unc->lower_port; } else { addr = binding->lower_addr; port = binding->lower_port; } memcpy(msg, ext->rep, sizeof(struct reg_rep)); reply = (struct reg_rep *) msg; reply->id[0] = htonl(binding->id[0]); reply->id[1] = htonl(binding->id[1]); len = sizeof(struct reg_rep); if (ext->mh_auth != NULL) DEBUG(DEBUG_FLAG, "\tdropping mh_auth\n"); /* add Dynamics options extension */ dyn_ext = (struct registration_ext_dynamics *) &msg[len]; dyn_ext->type = VENDOR_EXT_TYPE2; dyn_ext->length = sizeof(struct registration_ext_dynamics) - 2; dyn_ext->reserved = 0; dyn_ext->vendor_id = htonl(VENDOR_ID_DYNAMICS); dyn_ext->sub_type = htons(VENDOR_EXT_DYNAMICS_OPTIONS); dyn_ext->version = VENDOR_EXT_VERSION; dyn_ext->opts = REG_EXT_OWN_TEAR_DOWN; dyn_ext->seq = 0; len += sizeof(struct registration_ext_dynamics); len += auth_add_vendor(AUTH_ALG_MD5, binding->key, binding->keylen, msg, (struct vendor_msg_auth *) (msg + len), VENDOR_EXT_DYNAMICS_SK_AUTH, htonl(binding->spi)); len += add_fa_auth_ext(addr, reply->home_addr, (unsigned char *) reply, msg + len); LOG2(LOG_WARNING, "handle_teardown_changed_id - " "forwarding to %s:%i\n", inet_ntoa(addr), ntohs(port)); assert(t_data->iface != NULL); ret = own_sendto(t_data->iface->udp_sock, addr, port, msg, len); dynamics_check_sendto(ret, len, "handle_teardown_changed_id"); return 0;}#endifenum { ID_NOT_FOUND, ID_CURRENT,#ifdef USE_TEARDOWN ID_CHANGED,#endif ID_NEW };static struct unconfirmed_request*find_matching_request(struct bindingentry *binding, __u32 id, int *type){ struct unconfirmed_request *unc; struct tunnel_data *t_data; t_data = (struct tunnel_data *) binding->data; if (t_data->confirmed) { if (id == binding->id[1]) { DEBUG(DEBUG_FLAG, "Reply to current confirmed data\n"); *type = ID_CURRENT; return NULL; }#ifdef USE_TEARDOWN if (id == t_data->upper_id[1]) { DEBUG(DEBUG_FLAG, "Reply to confirmed data - ID " "changed\n"); *type = ID_CHANGED; return NULL; /* use current data */ }#endif } /* check the unconfirmed request list for id match */ unc = t_data->unc_req; while (unc != NULL) { if (unc->id[1] == id) { DEBUG(DEBUG_FLAG, "Reply to unconfirmed data\n"); *type = ID_NEW; return unc; } unc = unc->next; } *type = ID_NOT_FOUND; return NULL;}/* Takes care of the disconnect reply from HA or Switching FA */static inthandle_reply_disconnect(struct bindingentry *binding, struct unconfirmed_request *unc, struct msg_extensions *ext, int idtype){ struct tunnel_data *t_data; int is_lowest_fa; t_data = binding->data; if (idtype == ID_NEW && unc != NULL) { /* lower end changed */ is_lowest_fa = unc->is_lowest_fa; } else is_lowest_fa = t_data->is_lowest_fa; /* Reply is forwarded to lower mobility agent in any case if it is * a FA; if it is a MN, the tear down message is not forwarded */#ifdef USE_TEARDOWN if (!is_lowest_fa && idtype == ID_CHANGED && binding->id[1] != ntohl(ext->rep->id[1]) && ext->ext_dyn != NULL && (ext->ext_dyn->opts & REG_EXT_OWN_TEAR_DOWN) != 0) { handle_teardown_changed_id(binding, ext, unc); } else#endif if (!t_data->is_lowest_fa || ext->ext_dyn == NULL ||
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -