#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "bucket.h" #include "security_maat.h" #define ENFORCER_RULE_UUID_NUM 128 enum PACKET_RESPONSE_MODE { PACKET_RESPONSE_MODE_NONE=0, PACKET_RESPONSE_MODE_REPLACE, PACKET_RESPONSE_MODE_HIJACK, PACKET_RESPONSE_MODE_MAX }; struct maat_plugin_table { const char *name; maat_ex_new_func_t *ex_new; maat_ex_free_func_t *ex_free; maat_ex_dup_func_t *ex_dup; }; enum SECURITY_MAAT_PLUGIN { SECURITY_PLUGIN_UNKNOWN=0, SECURITY_PLUGIN_SECURITY_RULE, SECURITY_PLUGIN_RESPONSE_PAGE, SECURITY_PLUGIN_DNS_RESOURCE_RECORD, SECURITY_PLUGIN_MAX }; struct security_enforcer_env { int exdata_idx; struct logger *logger; struct scanner *scanner; struct module_manager *mod_mgr; struct packet_manager *pkt_mgr; struct session_manager *sess_mgr; int packet_response_mode; ctemplate::Template *tpl_403; ctemplate::Template *tpl_404; ctemplate::Template *tpl_200; ctemplate::Template *tpl_204; ctemplate::Template *tpl_303; struct maat *cm_maat; struct maat_plugin_table plugin_table[SECURITY_PLUGIN_MAX]; }; struct security_enforcer_per_session_context { enum RULE_SUB_ACTION sub_action_type; union { struct sub_action_drop drop; struct leaky_bucket *bucket; struct sub_action_tamper tamper; }; uint64_t packet_count; // packet contains payload }; static void toml_string_get1(struct logger *logger, const char *module, const char *toml_path, const char *table_key, const char *key, char **value) { FILE *fp=fopen(toml_path, "r"); if (NULL==fp) { STELLAR_LOG_FATAL(logger, module, "toml_bool_get can't open config file: %s", toml_path); return ; } fclose(fp); char errbuf[256]={0}; toml_table_t *root=toml_parse_file(fp, errbuf, sizeof(errbuf)); if(NULL==root) { return ; } toml_table_t *table=toml_table_in(root, table_key); if(NULL==table) { STELLAR_LOG_FATAL(logger, module, "toml_string_get1 can't find key: [%s] in config file: %s", table_key, toml_path); toml_free(root); return ; } toml_datum_t val=toml_string_in(table, key); if(val.ok>0) { (*value)=val.u.s; } else { (*value)=NULL; } toml_free(root); } uint32_t random_integer_generate(int min, int max) { if(max>min) { return (rand()%(max-min+1)); } return 0; } void uint32_append(char *payload, size_t payload_sz, size_t *payload_offset, uint32_t value) { if(payload_sz>(*payload_offset)+4) { *(uint32_t *)(payload+*payload_offset)=(uint32_t)(value); (*payload_offset)+=4; } } void uint16_hton_append(char *payload, size_t payload_sz, size_t *payload_offset, uint16_t value) { if(payload_sz>(*payload_offset)+2) { *(uint16_t *)(payload+*payload_offset)=(uint16_t)htons(value); (*payload_offset)+=2; } } void uint32_hton_append(char *payload, size_t payload_sz, size_t *payload_offset, uint32_t value) { if(payload_sz>(*payload_offset)+4) { *(uint32_t *)(payload+*payload_offset)=(uint32_t)htonl(value); (*payload_offset)+=4; } } // https://datatracker.ietf.org/doc/html/rfc1071#section-4.1 short computing_ip_checksum(void *payload, int payload_sz) { int nleft=payload_sz; long reverse_checksum=0; uint16_t *ushort_ptr=(uint16_t *)payload; while(nleft>1) { reverse_checksum+=*ushort_ptr++; nleft-=2; } if(nleft==1) { reverse_checksum=*(unsigned char *)ushort_ptr; } while(reverse_checksum>>16) { reverse_checksum=(reverse_checksum&0xFFFF)+(reverse_checksum>>16); } return (~reverse_checksum &0xFFFF); } #define ICMPV4_SRCPACKET_MAX_LEN 576 #define ICMPV6_SRCPACKET_MTU 1280 // void deny_sub_action_icmp_enforce(struct session *ss) // { // uint16_t l3hdr_len=0; // uint16_t origin_ip_total_len=0; // struct iphdr *origin_ipv4=NULL; // struct ip6_hdr *origin_ipv6=NULL; // struct iphdr *built_ipv4=NULL; // struct ip6_hdr *built_ipv6=NULL; // struct icmphdr *icmp4=NULL; // struct icmp6_hdr *icmp6=NULL; // uint16_t ip_icmp_hdr_sz=0; // uint16_t icmp_packe_sz=0; // char icmp_packet[1500]={0}; // const char *l3hdr=session_get0_current_l3_header(ss); // if(l3hdr==NULL) // { // return ; // } // enum session_addr_type addr_type=__SESSION_ADDR_TYPE_MAX; // session_get0_addr(ss, &addr_type); // switch(addr_type) // { // case SESSION_ADDR_TYPE_IPV4_TCP: // case SESSION_ADDR_TYPE_IPV4_UDP: // origin_ipv4=(struct iphdr *)l3hdr; // l3hdr_len=origin_ipv4->ihl*4; // memcpy(icmp_packet, l3hdr, l3hdr_len); // origin_ip_total_len=ntohs(origin_ipv4->tot_len); // built_ipv4=(struct iphdr *)icmp_packet; // built_ipv4->saddr=origin_ipv4->daddr; // built_ipv4->daddr=origin_ipv4->saddr; // //built_ipv4->check=computing_ip_checksum(icmp_packet, sizeof(struct iphdr)); // ip_icmp_hdr_sz=sizeof(struct iphdr)+sizeof(struct icmphdr); // icmp_packe_sz=MIN(origin_ip_total_len, ICMPV4_SRCPACKET_MAX_LEN-ip_icmp_hdr_sz)+ip_icmp_hdr_sz; // built_ipv4->tot_len=htons(icmp_packe_sz); // built_ipv4->protocol=IPPROTO_ICMP; // icmp4=(struct icmphdr *)(icmp_packet+l3hdr_len); // icmp4->type=ICMP_UNREACH; // icmp4->code=ICMP_UNREACH_FILTER_PROHIB; // memcpy(icmp_packet+ip_icmp_hdr_sz, l3hdr, icmp_packe_sz-ip_icmp_hdr_sz); // sendpacket_do_checksum((unsigned char *)icmp_packet, IPPROTO_ICMP, icmp_packe_sz-l3hdr_len); // sendpacket_do_checksum((unsigned char *)icmp_packet, IPPROTO_IP, l3hdr_len); // //icmp4->checksum=computing_ip_checksum(icmp_packet+l3hdr_len, sizeof(struct icmphdr)); // break; // case SESSION_ADDR_TYPE_IPV6_TCP: // case SESSION_ADDR_TYPE_IPV6_UDP: // origin_ipv6=(struct ip6_hdr *)l3hdr; // l3hdr_len=sizeof(struct ip6_hdr); // memcpy(icmp_packet, l3hdr, l3hdr_len); // origin_ip_total_len=ntohs(origin_ipv6->ip6_ctlun.ip6_un1.ip6_un1_plen); // built_ipv6=(struct ip6_hdr *)icmp_packet; // memcpy((void *)&(built_ipv6->ip6_src), (void *)&(origin_ipv6->ip6_dst), sizeof(struct in6_addr)); // memcpy((void *)&(built_ipv6->ip6_dst), (void *)&(origin_ipv6->ip6_src), sizeof(struct in6_addr)); // ip_icmp_hdr_sz=sizeof(struct ip6_hdr)+sizeof(struct icmp6_hdr); // icmp_packe_sz=MIN(origin_ip_total_len, ICMPV6_SRCPACKET_MTU-ip_icmp_hdr_sz)+ip_icmp_hdr_sz; // icmp_packe_sz=icmp_packe_sz-(icmp_packe_sz%4); // 4 bytes alignment // built_ipv6->ip6_ctlun.ip6_un1.ip6_un1_plen=htons(icmp_packe_sz); // built_ipv6->ip6_ctlun.ip6_un1.ip6_un1_nxt=IPPROTO_ICMPV6; // icmp6=(struct icmp6_hdr *)(icmp_packet+l3hdr_len); // icmp6->icmp6_type=ICMP6_DST_UNREACH; // icmp6->icmp6_code=ICMP6_DST_UNREACH; // memcpy(icmp_packet+ip_icmp_hdr_sz, l3hdr, icmp_packe_sz-ip_icmp_hdr_sz); // //icmp6->icmp6_cksum=computing_ip_checksum(icmp_packet+l3hdr_len, icmp_packe_sz); // sendpacket_do_checksum((unsigned char *)icmp_packet, IPPROTO_ICMPV6, icmp_packe_sz-l3hdr_len); // break; // default: // return ; // } // int ret=firewall_packet_inject(ss, icmp_packet, icmp_packe_sz, FALSE); // } void security_enforcer_get_origin_packet(const struct session *sess, const struct packet *rawpkt, const struct packet **c2s_origin_pkt, const struct packet **s2c_origin_pkt, uint32_t *th_seq, uint32_t *th_ack) { int layer_count=packet_get_layer_count(rawpkt); if(layer_count<=0) { return ; } const struct layer *l4_layer=packet_get_layer_by_idx(rawpkt, layer_count-1); if(l4_layer==NULL) { return ; } switch(session_get_flow_type(sess)) { case FLOW_TYPE_C2S: (*th_seq)=l4_layer->hdr.tcp->th_seq; (*th_ack)=l4_layer->hdr.tcp->th_ack; if(c2s_origin_pkt!=NULL) { (*c2s_origin_pkt)=rawpkt; } if(s2c_origin_pkt!=NULL) { (*s2c_origin_pkt)=session_get_first_packet(sess, FLOW_TYPE_S2C); } break; case FLOW_TYPE_S2C: (*th_seq)=l4_layer->hdr.tcp->th_ack; (*th_ack)=l4_layer->hdr.tcp->th_seq; if(s2c_origin_pkt!=NULL) { (*s2c_origin_pkt)=rawpkt; } if(c2s_origin_pkt!=NULL) { (*c2s_origin_pkt)=session_get_first_packet(sess, FLOW_TYPE_C2S); } break; default: break; } } void security_enforcer_enforce_reset(struct security_enforcer_env *enforcer_env, const struct session *sess, const struct packet *rawpkt) { enum session_type sess_type=session_get_type(sess); if(sess_type!=SESSION_TYPE_TCP) { return ; } uint32_t th_seq=0; uint32_t th_ack=0; const struct packet *c2s_origin_pkt=NULL; const struct packet *s2c_origin_pkt=NULL; security_enforcer_get_origin_packet(sess, rawpkt, &c2s_origin_pkt, &s2c_origin_pkt, &th_seq, &th_ack); if(c2s_origin_pkt!=NULL) { struct packet *c2s_rst_pkt=packet_manager_build_tcp_packet(enforcer_env->pkt_mgr, module_manager_get_thread_id(enforcer_env->mod_mgr), c2s_origin_pkt, th_seq, th_ack, TH_ACK|TH_RST, NULL, 0, NULL, 0); packet_manager_schedule_packet(enforcer_env->pkt_mgr, module_manager_get_thread_id(enforcer_env->mod_mgr), c2s_rst_pkt, PACKET_STAGE_POSTROUTING); } if(s2c_origin_pkt!=NULL) { struct packet *s2c_rst_pkt=packet_manager_build_tcp_packet(enforcer_env->pkt_mgr, module_manager_get_thread_id(enforcer_env->mod_mgr), s2c_origin_pkt, th_ack, th_seq, TH_ACK|TH_RST, NULL, 0, NULL, 0); packet_manager_schedule_packet(enforcer_env->pkt_mgr, module_manager_get_thread_id(enforcer_env->mod_mgr), s2c_rst_pkt, PACKET_STAGE_POSTROUTING); } } void security_enforcer_enforce_drop(struct security_enforcer_env *enforcer_env, const struct session *sess, const struct packet *rawpkt, struct sub_action_drop *drop) { if(enforcer_env==NULL || rawpkt==NULL || drop==NULL) { return; } packet_set_action((struct packet *)rawpkt, PACKET_ACTION_DROP); session_manager_discard_session(enforcer_env->sess_mgr, module_manager_get_thread_id(enforcer_env->mod_mgr), (struct session *)sess); // if(drop->send_icmp_enable) // { // deny_sub_action_icmp_enforce(sess); // } if(drop->send_reset_enable) { security_enforcer_enforce_reset(enforcer_env, sess, rawpkt); } } // TODO: implement this function // size_t sip_response_body_append(char *response_body, size_t response_body_sz, struct utable *putable, const char *field_name, const char *field_prefix) // { // char *sip_field=NULL; // size_t sip_field_sz=0; // size_t field_prefix_sz=strlen(field_prefix); // utable_get0_cstring_value(putable, field_name, &sip_field, &sip_field_sz); // if(response_body_szcode) // { // case 480: // //"SIP/2.0 480 Temporarily Unavailable\r\n" // offset=strlen("SIP/2.0 480 Temporarily Unavailable\r\n"); // memcpy(payload, "SIP/2.0 480 Temporarily Unavailable\r\n", offset); // break; // case 500: // //"SIP/2.0 500 Server Internal Error\r\n", // offset=strlen("SIP/2.0 500 Server Internal Error\r\n"); // memcpy(payload, "SIP/2.0 500 Server Internal Error\r\n", offset); // break; // default: // firewall_session_deny_para_set(ss, SESSION_DENY_PARA_DROP_CURRENT_PACKET); // return ; // } // offset+=sip_response_body_append(payload+offset, sizeof(payload)-offset, putable, firewall_attribute_schema[SCHEMA_ATTRIBUTE_SIP_VIA].log_field_name, "Via: "); // offset+=sip_response_body_append(payload+offset, sizeof(payload)-offset, putable, firewall_attribute_schema[SCHEMA_ATTRIBUTE_SIP_ORIGINATOR_DESCRIPTION].log_field_name, "\r\nFrom: "); // // offset+=sip_response_body_append(payload+offset, sizeof(payload)-offset, putable, firewall_attribute_schema[SCHEMA_ATTRIBUTE_SIP_ORIGINATOR_TAGS].log_field_name, "tag="); // offset+=sip_response_body_append(payload+offset, sizeof(payload)-offset, putable, firewall_attribute_schema[SCHEMA_ATTRIBUTE_SIP_RESPONDER_DESCRIPTION].log_field_name, "\r\nTo: "); // offset+=sip_response_body_append(payload+offset, sizeof(payload)-offset, putable, firewall_attribute_schema[SCHEMA_ATTRIBUTE_SIP_CALL_ID].log_field_name, "\r\nCall-ID: "); // offset+=sip_response_body_append(payload+offset, sizeof(payload)-offset, putable, firewall_attribute_schema[SCHEMA_ATTRIBUTE_SIP_CSEQ].log_field_name, "\r\nCSeq: "); // const char *p="\r\nServer: (Very nice Sip Registrar/Proxy Server)\r\nAllow: ACK,BYE,CANCEL,INVITE,REGISTER,OPTIONS,INFO,MESSAGE\r\nContent-Length: 0\r\n\r\n"; // size_t p_sz=MIN(sizeof(payload)-offset, strlen(p)); // if(p_sz>0) // { // memcpy(payload+offset, p, p_sz); // offset+=p_sz; // } // const struct packet *pkt=session_get0_current_packet(ss); // int exchange_direction=(packet_get_direction(pkt)==PACKET_DIRECTION_C2S) ? TRUE : FALSE; // int ret=firewall_payload_inject(ss, payload, offset, exchange_direction); // } void security_enforcer_enforce_mail_block(struct security_enforcer_env *enforcer_env, const struct session *sess, const struct packet *rawpkt, struct sub_action_response *response) { if(enforcer_env==NULL || rawpkt==NULL || response==NULL) { return ; } uint32_t th_seq=0; uint32_t th_ack=0; const struct packet *c2s_origin_pkt=NULL; const struct packet *s2c_origin_pkt=NULL; security_enforcer_get_origin_packet(sess, rawpkt, &c2s_origin_pkt, &s2c_origin_pkt, &th_seq, &th_ack); if(c2s_origin_pkt==NULL) { return ; } char *block_buff=NULL; switch(response->code) { case 550: block_buff=(char *)"550 Mail was identified as spam.\r\n"; break; case 551: block_buff=(char *)"551 User not local; please try \r\n"; break; default: break; } if(block_buff!=NULL) { struct packet *redirect_pkt=packet_manager_build_tcp_packet(enforcer_env->pkt_mgr, module_manager_get_thread_id(enforcer_env->mod_mgr), c2s_origin_pkt, th_seq, th_ack, TH_ACK|TH_PUSH, NULL, 0, block_buff, strlen(block_buff)); packet_manager_schedule_packet(enforcer_env->pkt_mgr, module_manager_get_thread_id(enforcer_env->mod_mgr), redirect_pkt, PACKET_STAGE_POSTROUTING); } packet_set_action((struct packet *)rawpkt, PACKET_ACTION_DROP); session_manager_discard_session(enforcer_env->sess_mgr, module_manager_get_thread_id(enforcer_env->mod_mgr), (struct session *)sess); } void policy_user_define_variable_replace(ctemplate::TemplateDictionary *tpl_dict, uuid_t rule_uuid, char *client_ip, char *subscriber) { char rule_uuid_str[UUID_STR_LEN]={0}; uuid_unparse_lower(rule_uuid, rule_uuid_str); tpl_dict->SetFormattedValue("tsg_policy_uuid", "%s", rule_uuid_str); tpl_dict->SetFormattedValue("tsg_subscriber_id", "%s", (subscriber!=NULL) ? subscriber : ""); tpl_dict->SetFormattedValue("tsg_client_ip", "%s", (client_ip!=NULL) ? client_ip : ""); } void http_response_body_template_value_get(const struct packet *rawpkt __attribute__((unused)), ctemplate::Template *pg_template, uuid_t rule_uuid, const char* message, char **output, size_t *output_sz) { std::string page_output, msg_output; ctemplate::TemplateDictionary dict("pg_page_dict"); //dict is automatically finalized after function returned. if(message!=NULL) { ctemplate::Template *tpl_message=ctemplate::Template::StringToTemplate(message, strlen(message), ctemplate::DO_NOT_STRIP); if(tpl_message!=NULL) { ctemplate::TemplateDictionary dict_msg("msg_dict"); //dict is automatically finalized after function returned. char *client_ip=NULL; char *subscriber=NULL; policy_user_define_variable_replace(&dict_msg, rule_uuid, client_ip, subscriber); tpl_message->Expand(&msg_output, &dict_msg); size_t output_msg_sz=msg_output.length(); char *output_msg=(char *)CALLOC(char, output_msg_sz+1); memcpy(output_msg, msg_output.c_str(), output_msg_sz); output_msg[output_msg_sz]='\0'; dict.SetValue("msg", output_msg); FREE(output_msg); output_msg=NULL; delete tpl_message; } else { dict.SetValue("msg", message); } } else { dict.SetValue("msg", "NULL"); } pg_template->Expand(&page_output, &dict); *output_sz=page_output.length(); *output=(char *)CALLOC(char, *output_sz); memcpy(*output, page_output.c_str(), *output_sz); } void security_enforcer_enforce_http_block(struct security_enforcer_env *enforcer_env, const struct session *sess, const struct packet *rawpkt, struct sub_action_response *response, uuid_t rule_uuid) { if(enforcer_env==NULL || rawpkt==NULL || response==NULL) { return ; } packet_set_action((struct packet *)rawpkt, PACKET_ACTION_DROP); session_manager_discard_session(enforcer_env->sess_mgr, module_manager_get_thread_id(enforcer_env->mod_mgr), (struct session *)sess); uint16_t http_hdr_len=0; char http_hdr[512]={0}; ctemplate::Template *pg_template=NULL; switch(response->code) { case 200: http_hdr_len=snprintf(http_hdr, sizeof(http_hdr), "HTTP/1.1 %d OK\r\nContent-Type: text/html\r\n\r\n", response->code); pg_template=enforcer_env->tpl_200; break; case 204: http_hdr_len=snprintf(http_hdr, sizeof(http_hdr), "HTTP/1.1 %d No Content\r\nContent-Type: text/html\r\n\r\n", response->code); pg_template=enforcer_env->tpl_204; break; case 403: http_hdr_len=snprintf(http_hdr, sizeof(http_hdr), "HTTP/1.1 %d Forbidden\r\nContent-Type: text/html\r\n\r\n", response->code); pg_template=enforcer_env->tpl_403; break; case 404: http_hdr_len=snprintf(http_hdr, sizeof(http_hdr), "HTTP/1.1 %d Not Found\r\nContent-Type: text/html\r\n\r\n", response->code); pg_template=enforcer_env->tpl_404; break; default: return ; } char *response_body=NULL; size_t response_body_sz=0; switch(response->rtype) { case RESPONSE_TEXT: case RESPONSE_TEMPLATE: http_response_body_template_value_get(rawpkt, pg_template, rule_uuid, response->message, &response_body, &response_body_sz); break; case RESPONSE_PROFILE: { const struct response_page *page=security_enforcer_get_response_page(enforcer_env->cm_maat, enforcer_env->plugin_table[SECURITY_PLUGIN_RESPONSE_PAGE].name, rule_uuid); if(page==NULL) { break; } switch(page->rtype) { case RESPONSE_PROFILE: response_body=(char *)CALLOC(char, page->content_sz); memcpy(response_body, page->content, page->content_sz); response_body_sz=page->content_sz; break; case RESPONSE_TEMPLATE: http_response_body_template_value_get(rawpkt, pg_template, rule_uuid, page->content, &response_body, &response_body_sz); break; default: break; } } break; case RESPONSE_NO_CONTENT: break; default: return ; } if((response_body==NULL || response_body_sz==0) && http_hdr_len==0) { return ; } if(response_body_sz>0) { // TODO: set http_action_file_size } uint32_t th_seq=0; uint32_t th_ack=0; const struct packet *c2s_origin_pkt=NULL; const struct packet *s2c_origin_pkt=NULL; security_enforcer_get_origin_packet(sess, rawpkt, &c2s_origin_pkt, &s2c_origin_pkt, &th_seq, &th_ack); if(c2s_origin_pkt==NULL) { return ; } char one_payload[1460]={0}; uint16_t th_seq_offset=0; uint16_t one_payload_len=0; uint16_t max_segment_size=1400; for(size_t i=0; i<=response_body_sz; i+=one_payload_len) { if(http_hdr_len>0) { memcpy(one_payload, http_hdr, MIN(http_hdr_len, sizeof(one_payload))); } one_payload_len=MIN(response_body_sz-i, (size_t)(max_segment_size-http_hdr_len)); memcpy(one_payload+http_hdr_len, response_body+i, one_payload_len); struct packet *redirect_pkt=packet_manager_build_tcp_packet(enforcer_env->pkt_mgr, module_manager_get_thread_id(enforcer_env->mod_mgr), c2s_origin_pkt, th_seq+i, th_ack, TH_ACK|TH_PUSH, NULL, 0, one_payload, http_hdr_len+one_payload_len); packet_manager_schedule_packet(enforcer_env->pkt_mgr, module_manager_get_thread_id(enforcer_env->mod_mgr), redirect_pkt, PACKET_STAGE_POSTROUTING); th_seq_offset+=(one_payload_len+http_hdr_len); http_hdr_len=0; } FREE(response_body); response_body=NULL; struct packet *c2s_fin_pkt=packet_manager_build_tcp_packet(enforcer_env->pkt_mgr, module_manager_get_thread_id(enforcer_env->mod_mgr), c2s_origin_pkt, th_seq+th_seq_offset, th_ack, TH_ACK|TH_FIN, NULL, 0, NULL, 0); packet_manager_schedule_packet(enforcer_env->pkt_mgr, module_manager_get_thread_id(enforcer_env->mod_mgr), c2s_fin_pkt, PACKET_STAGE_POSTROUTING); struct packet *c2s_rst_pkt=packet_manager_build_tcp_packet(enforcer_env->pkt_mgr, module_manager_get_thread_id(enforcer_env->mod_mgr), c2s_origin_pkt, th_seq+th_seq_offset, th_ack, TH_ACK|TH_RST, NULL, 0, NULL, 0); packet_manager_schedule_packet(enforcer_env->pkt_mgr, module_manager_get_thread_id(enforcer_env->mod_mgr), c2s_rst_pkt, PACKET_STAGE_POSTROUTING); if(s2c_origin_pkt!=NULL) { struct packet *s2c_fin_pkt=packet_manager_build_tcp_packet(enforcer_env->pkt_mgr, module_manager_get_thread_id(enforcer_env->mod_mgr), s2c_origin_pkt, th_ack, th_seq+th_seq_offset, TH_ACK|TH_FIN, NULL, 0, NULL, 0); packet_manager_schedule_packet(enforcer_env->pkt_mgr, module_manager_get_thread_id(enforcer_env->mod_mgr), s2c_fin_pkt, PACKET_STAGE_POSTROUTING); struct packet *s2c_rst_pkt=packet_manager_build_tcp_packet(enforcer_env->pkt_mgr, module_manager_get_thread_id(enforcer_env->mod_mgr), s2c_origin_pkt, th_ack, th_seq+th_seq_offset, TH_ACK|TH_RST, NULL, 0, NULL, 0); packet_manager_schedule_packet(enforcer_env->pkt_mgr, module_manager_get_thread_id(enforcer_env->mod_mgr), s2c_rst_pkt, PACKET_STAGE_POSTROUTING); } } // void dns_response_header_append(struct utable *putable, struct _dns_hdr *dnshdr, int record_num) // { // dnshdr->qr=1; // 1bit: Response // dnshdr->opcode=0; // 4bits: Query // dnshdr->aa=0; // 1bit: authoritative answer // dnshdr->tc=0; // 1bit: Not truncated // dnshdr->rd=1; // 1bit: Recursion Desired // dnshdr->ra=1; // 1bit: Recursion Available // dnshdr->z=0; // 3bits: Reserved for future use: Must be zero in all queries and responses // dnshdr->rcode=0; // 4bits: 0: No error condition // dnshdr->ancount=record_num; // int64_t message_id=0; // utable_get0_integer_value(putable, firewall_attribute_schema[SCHEMA_ATTRIBUTE_DNS_MESSAGE_ID].log_field_name, &message_id); // dnshdr->id=(uint16_t)message_id; // dnshdr->id=htons(dnshdr->id); // dnshdr->qdcount=1; // dnshdr->qdcount=htons(dnshdr->qdcount); // 16bits: QDCOUNT: number of questions // dnshdr->ancount=htons(dnshdr->ancount); // 16bits: ANCOUNT: number of answer resource records // dnshdr->aucount=htons(dnshdr->aucount); // 16bits: NSCOUNT: number of authority resource records // dnshdr->adcount=htons(dnshdr->adcount); // 16bits: ARCOUNT: number of additional resource records // } // static int dns_qanme_compress(char *qname, int qname_sz, char *compress_qname, int compress_qname_sz) // { // int c_offset=1; // int d_offset=0; // if(qname==NULL || qname_sz<=0 || compress_qname==NULL || compress_qname_sz<=0 || qname_sz>=compress_qname_sz) // { // return 0; // } // if(('.'==qname[0]) || ('.'==qname[qname_sz-1])) // { // return 0; // } // while((qname[d_offset]!='\n') && (qname[d_offset]!='\0') && (c_offset=qname_sz) || (qname[d_offset]=='\n') || (qname[d_offset]=='\0')) // { // break; // } // c_offset++; // d_offset++; // } // compress_qname[c_offset++]='\0'; // return c_offset; // } // void dns_response_question_append(struct utable *putable, char *payload, size_t payload_sz, size_t *payload_offset) // { // size_t qname_sz=0; // char *qname=NULL; // utable_get0_cstring_value(putable, firewall_attribute_schema[SCHEMA_ATTRIBUTE_DNS_QNAME].log_field_name, &qname, &qname_sz); // if(qname==NULL || qname_sz<=0) // { // return ; // } // (*payload_offset)+=dns_qanme_compress(qname, qname_sz, payload+(*payload_offset), payload_sz-(*payload_offset)); // int64_t qtype=0; // utable_get0_integer_value(putable, firewall_attribute_schema[SCHEMA_ATTRIBUTE_DNS_QTYPE].log_field_name, &qtype); // uint16_hton_append(payload, payload_sz, payload_offset, (uint16_t)qtype); // int64_t qclass=0; // utable_get0_integer_value(putable, firewall_attribute_schema[SCHEMA_ATTRIBUTE_DNS_QCLASS].log_field_name, &qclass); // uint16_hton_append(payload, payload_sz, payload_offset, (uint16_t)qclass); // } // void dns_response_answer_records_append(UT_array *records, char *payload, size_t payload_sz, size_t *payload_offset, int selected_num, int min_ttl, int max_ttl, uint16_t *n_records) // { // if(records==NULL || selected_num<=0) // { // return ; // } // uint32_t idx=0; // uint32_t n=MIN(utarray_len(records), (uint32_t)selected_num); // if(utarray_len(records) > (uint32_t)selected_num) // { // idx=random_integer_generate(0, n-selected_num); // } // for(uint32_t i=idx; iatype)); // uint16_hton_append(payload, payload_sz, payload_offset, (uint16_t)1); // int ttl=random_integer_generate(min_ttl, max_ttl)+min_ttl; // uint32_hton_append(payload, payload_sz, payload_offset, (uint32_t)ttl); // switch(answer_record->atype) // { // case RR_TYPE_A: // if(payload_sz-(*payload_offset) > 6) // { // uint16_hton_append(payload, payload_sz, payload_offset, (uint16_t)4); // uint32_append(payload, payload_sz, payload_offset, (uint32_t)(answer_record->v4_addr.s_addr)); // } // break; // case RR_TYPE_AAAA: // if(payload_sz-(*payload_offset) > 18) // { // uint16_hton_append(payload, payload_sz, payload_offset, (uint16_t)16); // memcpy(payload+(*payload_offset), answer_record->v6_addr.s6_addr, 16); // (*payload_offset)+=16; // } // break; // case RR_TYPE_CNAME: // { // char compress_cname[1024]={0}; // int compress_sz=dns_qanme_compress(answer_record->cname, strlen(answer_record->cname), compress_cname, sizeof(compress_cname)); // uint16_hton_append(payload, payload_sz, payload_offset, (uint16_t)compress_sz); // compress_sz=MIN((size_t)compress_sz, payload_sz-(*payload_offset)); // memcpy(payload+(*payload_offset), compress_cname, compress_sz); // (*payload_offset)+=compress_sz; // } // break; // default: // continue; // } // (*n_records)++; // } // } // //TODO: get dns profile records // void security_enforcer_enforce_dns_redirect(struct security_enforcer_env *enforcer_env, const struct packet *rawpkt, struct dns_setting_details *dns_redirect) // { // // if(enforcer_env==NULL || dns_redirect==NULL || rawpkt==NULL) // // { // // return ; // // } // // int64_t dns_qr=0; // // utable_get0_integer_value(putable, firewall_attribute_schema[SCHEMA_ATTRIBUTE_DNS_QR].log_field_name, &dns_qr); // // switch(firewall_rule_enforce_runtime.packet_response_mode) // // { // // case PACKET_RESPONSE_MODE_HIJACK: // // if(dns_qr==1) // dns_qr==1: response // // { // // return ; // // } // // break; // // case PACKET_RESPONSE_MODE_REPLACE: // // if(dns_qr==0) // dns_qr==0: query // // { // // return ; // // } // // break; // // default: // // return ; // // } // // char payload[1024]={0}; // // size_t payload_sz=sizeof(payload); // // size_t payload_offset=0; // // payload_offset+=sizeof(struct _dns_hdr); // // dns_response_question_append(putable, payload, payload_sz, &payload_offset); // // int64_t qtype=0; // // utable_get0_integer_value(putable, firewall_attribute_schema[SCHEMA_ATTRIBUTE_DNS_QTYPE].log_field_name, &qtype); // // uint16_t n_records=0; // // for(uint32_t i=0; ianswer_array[i], 0); // // if(record==NULL || record->qtype!=qtype) // // { // // continue; // // } // // switch(record->rtype) // // { // // case RESPONSE_PROFILE: // // { // // struct dns_profile_records *profile_records=(struct dns_profile_records *)plugin_ex_data_dns_profile_records_get(firewall_cm_maat, record->profile_uuid); // // if(profile_records==NULL) // // { // // return ; // // } // // dns_response_answer_records_append(profile_records->answer_array, payload, payload_sz, &payload_offset, record->selected_num, record->min_ttl, record->max_ttl, &n_records); // // } // // break; // // case RESPONSE_TEXT: // // { // // dns_response_answer_records_append(dns_redirect->answer_array[i], payload, payload_sz, &payload_offset, record->selected_num, record->min_ttl, record->max_ttl, &n_records); // // } // // break; // // default: // // break; // // } // // } // // if(n_records<=0) // // { // // return ; // // } // // dns_response_header_append(putable, (struct _dns_hdr *)payload, n_records); // // const struct packet *pkt=session_get0_current_packet(ss); // // int exchange_direction=(packet_get_direction(pkt)==PACKET_DIRECTION_C2S) ? TRUE : FALSE; // // int ret=firewall_payload_inject(ss, payload, payload_offset, exchange_direction); // // // TODO: get session from pkt // // const struct packet *c2s_first_pkt=session_get_first_packet(const struct session *sess, FLOW_TYPE_C2S); // // struct packet *redirect_pkt=packet_manager_build_tcp_packet(enforcer_env->pkt_mgr, module_manager_get_thread_id(enforcer_env->mod_mgr), c2s_first_pkt, innermost->tcp->th_seq, innermost->tcp->th_ack, innermost->tcp->th_flags, NULL, 0, payload, payload_offset); // // packet_manager_schedule_packet(enforcer_env->pkt_mgr, module_manager_get_thread_id(enforcer_env->mod_mgr), redirect_pkt, PACKET_STAGE_POSTROUTING); // // packet_set_action(rawpkt, PACKET_ACTION_DROP); // } void security_enforcer_enforce_http_redirect(struct security_enforcer_env *enforcer_env, const struct session *sess, const struct packet *rawpkt, struct sub_action_response *response, uuid_t rule_uuid) { if(enforcer_env==NULL || rawpkt==NULL || response==NULL || response->code!=303) { return ; } char payload[1024]={0}; uint16_t payload_offset=0; ctemplate::Template *tpl_303=ctemplate::Template::StringToTemplate(response->redirect_url_to, strlen(response->redirect_url_to), ctemplate::DO_NOT_STRIP); if(tpl_303!=NULL) { ctemplate::TemplateDictionary dict_303("url_dict"); //dict is automatically finalized after function returned. char *client_ip=NULL; char *subscriber=NULL; policy_user_define_variable_replace(&dict_303, rule_uuid, client_ip, subscriber); std::string output; tpl_303->Expand(&output, &dict_303); size_t output_url_sz=output.length(); char *output_url=(char *)CALLOC(char, output_url_sz+1); memcpy(output_url, output.c_str(), output_url_sz); output_url[output_url_sz]='\0'; payload_offset=snprintf(payload, sizeof(payload), "HTTP/1.1 %d See Other\r\nLocation: %s\r\n\r\n", response->code, output_url); FREE(output_url); output_url=NULL; delete tpl_303; } else { payload_offset=snprintf(payload, sizeof(payload), "HTTP/1.1 %d See Other\r\nLocation: %s\r\n\r\n", response->code, response->redirect_url_to); } uint32_t th_seq=0; uint32_t th_ack=0; const struct packet *c2s_origin_pkt=NULL; security_enforcer_get_origin_packet(sess, rawpkt, &c2s_origin_pkt, NULL, &th_seq, &th_ack); if(c2s_origin_pkt!=NULL) { struct packet *redirect_pkt=packet_manager_build_tcp_packet(enforcer_env->pkt_mgr, module_manager_get_thread_id(enforcer_env->mod_mgr), c2s_origin_pkt, th_seq, th_ack, TH_PUSH, NULL, 0, payload, payload_offset); packet_manager_schedule_packet(enforcer_env->pkt_mgr, module_manager_get_thread_id(enforcer_env->mod_mgr), redirect_pkt, PACKET_STAGE_POSTROUTING); packet_set_action((struct packet *)rawpkt, PACKET_ACTION_DROP); session_manager_discard_session(enforcer_env->sess_mgr, module_manager_get_thread_id(enforcer_env->mod_mgr), (struct session *)sess); } } size_t payload_first_last_2bytes_swap(char *payload, uint16_t payload_sz) { uint16_t swap_count=0; for(uint16_t i=0, j=payload_sz-1; i i; i++, j--) { if(swap_count>=2) { break; } if(payload[i]==payload[j]) { continue; } char temp=payload[i]; payload[i]=payload[j]; payload[j]=temp; swap_count++; } return swap_count; } #define TAMPER_MIN_PAYLOAD_LEN 4 void security_enforcer_enforce_tamper(struct security_enforcer_env *enforcer_env, const struct packet *rawpkt) { if(rawpkt==NULL) { return ; } int layer_count=packet_get_layer_count(rawpkt); if(layer_count<=0) { return ; } const struct layer *innermost=packet_get_layer_by_idx(rawpkt, layer_count-1); if(innermost==NULL) { return ; } const char *data=packet_get_raw_data(rawpkt); uint16_t data_sz=packet_get_raw_len(rawpkt); if(data==NULL || data_szproto) { case LAYER_PROTO_TCP: { uint16_t options_len=((innermost->hdr_len == sizeof(struct tcphdr)) ? 0 : (innermost->hdr_len - sizeof(struct tcphdr))); const char *options=((options_len==0) ? NULL : innermost->hdr.raw + sizeof(struct tcphdr)); tamper_pkt=packet_manager_build_tcp_packet(enforcer_env->pkt_mgr, module_manager_get_thread_id(enforcer_env->mod_mgr), rawpkt, innermost->hdr.tcp->th_seq, innermost->hdr.tcp->th_ack, innermost->hdr.tcp->th_flags, options, options_len, payload, payload_sz); } break; case LAYER_PROTO_UDP: tamper_pkt=packet_manager_build_udp_packet(enforcer_env->pkt_mgr, module_manager_get_thread_id(enforcer_env->mod_mgr), rawpkt, payload, payload_sz); break; default: return ; } if(tamper_pkt==NULL) { return ; } packet_manager_schedule_packet(enforcer_env->pkt_mgr, module_manager_get_thread_id(enforcer_env->mod_mgr), tamper_pkt, PACKET_STAGE_POSTROUTING); packet_set_action((struct packet *)rawpkt, PACKET_ACTION_DROP); } void security_enforcer_enforce_ratelimit(struct security_enforcer_env *enforcer_env __attribute__((unused)), const struct packet *rawpkt, struct leaky_bucket *bucket) { if(rawpkt==NULL || bucket==NULL) { return ; } size_t rawpkt_sz=packet_get_raw_len(rawpkt); if((is_permit_pass(bucket, rawpkt_sz*8))==0) { packet_set_action((struct packet *)rawpkt, PACKET_ACTION_DROP); } } size_t maat_state_compile(struct maat_state *state __attribute__((unused)), const char *rule_table_name __attribute__((unused)), uuid_t rule_array[] __attribute__((unused)), void *rule_exdata[] __attribute__((unused)), size_t rule_array_num __attribute__((unused))) { return 0; } void packet_based_security_enforcer_node_entry(struct packet *rawpkt, struct module *mod_enforcer) { if(rawpkt==NULL || mod_enforcer==NULL) { return ; } struct security_enforcer_env *enforcer_env=(struct security_enforcer_env *)module_get_ctx(mod_enforcer); if(enforcer_env==NULL) { return ; } struct session *sess=session_manager_lookup_session_by_packet(enforcer_env->sess_mgr, module_manager_get_thread_id(enforcer_env->mod_mgr), rawpkt); if(sess!=NULL) { return ; } size_t rule_array_capacity=ENFORCER_RULE_UUID_NUM; uuid_t rule_array[rule_array_capacity]; struct security_rule *rule_exdata[rule_array_capacity]={NULL}; struct maat_state *scan_state=scanner_get_maat_state_from_packet(enforcer_env->scanner, rawpkt); size_t rule_array_offset=maat_state_compile(scan_state, enforcer_env->plugin_table[SECURITY_PLUGIN_SECURITY_RULE].name, rule_array, (void **)rule_exdata, rule_array_capacity); if(rule_array_offset==0 || rule_exdata[0]==NULL) { return ; } // int ipproto=packet_get_ip_proto(rawpkt); // uint64_t pkt_tag_key_bits=0; // uint64_t pkt_tag_val_bits=0; // packet_tag_get(rawpkt, &pkt_tag_key_bits, &pkt_tag_val_bits); } void session_based_security_enforcer_node_entry(struct packet *rawpkt, struct module *mod_enforcer) { if(rawpkt==NULL || mod_enforcer==NULL) { return ; } struct security_enforcer_env *enforcer_env=(struct security_enforcer_env *)module_get_ctx(mod_enforcer); if(enforcer_env==NULL) { return ; } struct session *sess=packet_exdata_to_session(enforcer_env->sess_mgr, rawpkt); if(sess==NULL) { return ; } size_t rule_array_capacity=ENFORCER_RULE_UUID_NUM; uuid_t rule_array[rule_array_capacity]; struct override_sub_action *override_action=NULL; struct security_enforcer_per_session_context *per_sess_ctx=(struct security_enforcer_per_session_context *)session_get_exdata(sess, enforcer_env->exdata_idx); if(per_sess_ctx!=NULL) { return ; } else { struct security_rule *rule_exdata[rule_array_capacity]={NULL}; struct maat_state *scan_state=scanner_get_maat_state_from_packet(enforcer_env->scanner, rawpkt); size_t rule_array_offset=maat_state_compile(scan_state, enforcer_env->plugin_table[SECURITY_PLUGIN_SECURITY_RULE].name, rule_array, (void **)rule_exdata, rule_array_capacity); if(rule_array_offset==0 || rule_exdata[0]==NULL) { return ; } if(rule_exdata[0]->action==SECURITY_RULE_ACTION_ALLOW) { return ; } else if(rule_exdata[0]->action==SECURITY_RULE_ACTION_DENY) { } switch(rule_exdata[0]->deny->origin) { case origin_app_id_dict: break; case origin_override: override_action=rule_exdata[0]->deny->override_action; break; default: break; } if(override_action==NULL) { return ; } per_sess_ctx=(struct security_enforcer_per_session_context *)CALLOC(struct security_enforcer_per_session_context, 1); session_set_exdata(sess, enforcer_env->exdata_idx, (void *)per_sess_ctx); } switch(override_action->sub_action_type) { case RULE_SUB_ACTION_DROP: security_enforcer_enforce_drop(enforcer_env, sess, rawpkt, &(override_action->drop)); break; case RULE_SUB_ACTION_ALERT: security_enforcer_enforce_http_block(enforcer_env, sess, rawpkt, override_action->http_alert, rule_array[0]); break; case RULE_SUB_ACTION_TAMPER: security_enforcer_enforce_tamper(enforcer_env, rawpkt); break; case RULE_SUB_ACTION_RATE_LIMIT: security_enforcer_enforce_ratelimit(enforcer_env, rawpkt, per_sess_ctx->bucket); break; case RULE_SUB_ACTION_SIP_BLOCK: break; case RULE_SUB_ACTION_HTTP_BLOCK: security_enforcer_enforce_http_block(enforcer_env, sess, rawpkt, override_action->block, rule_array[0]); break; case RULE_SUB_ACTION_MAIL_BLOCK: security_enforcer_enforce_mail_block(enforcer_env, sess, rawpkt, override_action->block); break; case RULE_SUB_ACTION_HTTP_REDIRECT: security_enforcer_enforce_http_redirect(enforcer_env, sess, rawpkt, override_action->http_redirect, rule_array[0]); break; case RULE_SUB_ACTION_DNS_REDIRECT: break; default: break; } } void security_enforcer_module_exit(struct module_manager *mod_mgr, struct module *mod) { if(mod_mgr==NULL)return; if(mod) { struct security_enforcer_env *enforcer_env=(struct security_enforcer_env *)module_get_ctx(mod); FREE(enforcer_env); module_free(mod); } } struct module *security_enforcer_module_init(struct module_manager *mod_mgr) { if(mod_mgr==NULL)return NULL; char *page_403_path=NULL; char *page_404_path=NULL; char *page_200_path=NULL; char *packet_response_mode=NULL; // int max_thread_num=module_manager_get_max_thread_num(mod_mgr); const char *toml_path=module_manager_get_toml_path(mod_mgr); struct security_enforcer_env *enforcer_env=CALLOC(struct security_enforcer_env, 1); struct module *mod=module_new(SECUIRTY_ENFORCER_MODULE_NAME, (void *)enforcer_env); if(mod==NULL) { goto INIT_ERROR; } enforcer_env->mod_mgr=mod_mgr; enforcer_env->logger=module_manager_get_logger(mod_mgr); toml_string_get1(enforcer_env->logger, SECUIRTY_ENFORCER_MODULE_NAME, toml_path, "security_enforcer", "http_page_403", &page_403_path); if(page_403_path==NULL) { goto INIT_ERROR; } enforcer_env->tpl_403=ctemplate::Template::GetTemplate(page_403_path, ctemplate::DO_NOT_STRIP); free(page_403_path); toml_string_get1(enforcer_env->logger, SECUIRTY_ENFORCER_MODULE_NAME, toml_path, "security_enforcer", "http_page_404", &page_404_path); if(page_404_path==NULL) { goto INIT_ERROR; } enforcer_env->tpl_404=ctemplate::Template::GetTemplate(page_404_path, ctemplate::DO_NOT_STRIP); free(page_404_path); toml_string_get1(enforcer_env->logger, SECUIRTY_ENFORCER_MODULE_NAME, toml_path, "security_enforcer", "http_page_200", &page_200_path); if(page_200_path==NULL) { goto INIT_ERROR; } enforcer_env->tpl_200=ctemplate::Template::GetTemplate(page_200_path, ctemplate::DO_NOT_STRIP); free(page_200_path); toml_string_get1(enforcer_env->logger, SECUIRTY_ENFORCER_MODULE_NAME, toml_path, "security_enforcer", "response_packet_mode", &packet_response_mode); if(packet_response_mode==NULL) { goto INIT_ERROR; } if((strlen(packet_response_mode)==strlen("replace")) && (strcasecmp(packet_response_mode, "replace")==0)) { enforcer_env->packet_response_mode=PACKET_RESPONSE_MODE_REPLACE; free(packet_response_mode); } else { enforcer_env->packet_response_mode=PACKET_RESPONSE_MODE_HIJACK; free(packet_response_mode); } enforcer_env->plugin_table[SECURITY_PLUGIN_UNKNOWN]=(struct maat_plugin_table){ .name="unknown", .ex_new=NULL, .ex_free=NULL, .ex_dup=NULL }; enforcer_env->plugin_table[SECURITY_PLUGIN_SECURITY_RULE]=(struct maat_plugin_table){ .name="SECURITY_RULE", .ex_new=security_rule_new, .ex_free=security_rule_free, .ex_dup=security_rule_dup }; enforcer_env->plugin_table[SECURITY_PLUGIN_RESPONSE_PAGE]=(struct maat_plugin_table){ .name="RESPONSE_PAGE", .ex_new=response_page_new, .ex_free=response_page_free, .ex_dup=response_page_dup }; enforcer_env->plugin_table[SECURITY_PLUGIN_DNS_RESOURCE_RECORD]=(struct maat_plugin_table){ .name="DNS_RESOURCE_RECORD", .ex_new=dns_resource_record_new, .ex_free=dns_resource_record_free, .ex_dup=dns_resource_record_dup }; enforcer_env->scanner=scanner_module_to_scanner(module_manager_get_module(mod_mgr, SCANNER_MODULE_NAME)); enforcer_env->cm_maat=scanner_get_maat_instance(enforcer_env->scanner); if(enforcer_env->cm_maat==NULL) { STELLAR_LOG_FATAL(enforcer_env->logger, SECUIRTY_ENFORCER_MODULE_NAME, "scanner_get_maat_instance/scanner_module_to_scanner/module_manager_get_module failed, module_name: %s", SCANNER_MODULE_NAME); goto INIT_ERROR; } for(int i=SECURITY_PLUGIN_UNKNOWN; iplugin_table[i].name==NULL) { continue; } int ret=maat_plugin_table_ex_schema_register(enforcer_env->cm_maat, enforcer_env->plugin_table[i].name, enforcer_env->plugin_table[i].ex_new, enforcer_env->plugin_table[i].ex_free, enforcer_env->plugin_table[i].ex_dup, 0, NULL ); if(ret<0) { STELLAR_LOG_FATAL(enforcer_env->logger, SECUIRTY_ENFORCER_MODULE_NAME, "maat_plugin_table_ex_schema_register failed, table_name: %s", enforcer_env->plugin_table[i].name); goto INIT_ERROR; } } return mod; INIT_ERROR: security_enforcer_module_exit(mod_mgr, mod); exit(-1); return NULL; }