/** * Decode a r_contact from a binary data structure * @param x - binary data to decode from * @returns the r_contact* where the data has been decoded */ r_contact* bin_decode_r_contact(bin_data *x) { r_contact *c=0; r_public *p=0,*pn=0; int len,i; char k; unsigned short us; str st; len = sizeof(r_contact); c = (r_contact*) shm_malloc(len); if (!c) { LOG(L_ERR,"ERR:"M_NAME":bin_decode_r_contact: Error allocating %d bytes.\n",len); goto error; } memset(c,0,len); if (!bin_decode_str(x,&st)||!str_shm_dup(&(c->host),&st)) goto error; if (!bin_decode_ushort(x,&c->port)) goto error; if (!bin_decode_char(x,&c->transport)) goto error; c->hash = get_contact_hash(c->host,c->port,c->transport,r_hash_size); if (!bin_decode_r_security(x,&(c->security_temp))) goto error; if (!bin_decode_r_security(x,&(c->security))) goto error; if (!bin_decode_str(x,&st)||!str_shm_dup(&(c->uri),&st)) goto error; if (!bin_decode_char(x,&k)) goto error; c->reg_state = k; if (!bin_decode_time_t(x,&c->expires)) goto error; if (!bin_decode_ushort(x, &c->service_route_cnt)) goto error; len = sizeof(str)*c->service_route_cnt; c->service_route = (str*) shm_malloc(len); if (!c->service_route) { LOG(L_ERR,"ERR:"M_NAME":bin_decode_r_contact: Error allocating %d bytes.\n",len); goto error; } memset(c->service_route,0,len); for(i=0;i<c->service_route_cnt;i++) if (!bin_decode_str(x,&st)||!str_shm_dup(c->service_route+i,&st)) goto error; if (!bin_decode_pinhole(x,&(c->pinhole ))) goto error; if (!bin_decode_char(x,&k)) goto error; c->sos_flag = k; if (!bin_decode_str(x,&st)||!str_shm_dup(&(c->pcc_session_id),&st)) goto error; if (!bin_decode_ushort(x,&us)) goto error; for(i=0;i<us;i++){ p = bin_decode_r_public(x); if (!p) goto error; p->prev = c->tail; p->next = 0; if (c->tail) c->tail->next = p; c->tail = p; if (!c->head) c->head = p; } return c; error: LOG(L_ERR,"ERR:"M_NAME":bin_decode_r_contact: Error while decoding (at %d (%04x)).\n",x->max,x->max); if (c) { if (c->host.s) shm_free(c->host.s); if (c->security_temp) free_r_security(c->security_temp); if (c->security) free_r_security(c->security); if (c->uri.s) shm_free(c->uri.s); if (c->pinhole) shm_free(c->pinhole); while(c->head){ p = c->head; pn = p->next; free_r_public(p); c->head = pn; } shm_free(c); } return 0; }
/** * Saves the Contact Security information into the P-CSCF registrar for this contact. * @param req - REGISTER request * @param auth - WWW-Authenticate header * @param sec_hdr - Security header * @param type - Security Type * @param q - Preference Value */ r_contact* save_contact_security(struct sip_msg *req, str auth, str sec_hdr,r_security_type type,float q) { contact_t* c=0; contact_body_t* b=0; r_contact *rc; enum Reg_States reg_state=REG_PENDING; int expires,pending_expires=60; struct sip_uri puri; r_security *s=0; b = cscf_parse_contacts(req); if (!b||!b->contacts) { LOG(L_ERR,"ERR:"M_NAME":save_contact_security: No contacts found\n"); goto error; } if (b) c = b->contacts; r_act_time(); /* the Security works for just 1 contact/registration! */ if(c){ LOG(L_DBG,"DBG:"M_NAME":save_contact_security: <%.*s>\n",c->uri.len,c->uri.s); expires = time_now+pending_expires; if (parse_uri(c->uri.s,c->uri.len,&puri)<0){ LOG(L_DBG,"DBG:"M_NAME":save_contact_security: Error parsing Contact URI <%.*s>\n",c->uri.len,c->uri.s); goto error; } if (puri.port_no==0) puri.port_no=5060; LOG(L_DBG,"DBG:"M_NAME":save_contact_security: %d %.*s : %d\n", puri.proto, puri.host.len,puri.host.s,puri.port_no); if (type == SEC_TLS) puri.proto = PROTO_TLS; /* create the r_security structure */ s = new_r_security(sec_hdr,type,q); if (!s) goto error; switch(type) { case SEC_NONE: break; case SEC_TLS: // r_tls creation happens on 200 break; case SEC_IPSEC: { /* then parse the parameters */ r_ipsec *ipsec; str ck,ik,ealg,alg,tmp; str alg_setkey,ealg_setkey; unsigned int spi_uc,spi_us; unsigned int spi_pc,spi_ps; int port_uc,port_us; char ck_c[64],ik_c[64]; str ck_esp={ck_c,0},ik_esp={ik_c,0}; get_qparam(auth,s_ck,ck); LOG(L_DBG,"DBG:"M_NAME":save_contact_security: CK: <%.*s>\n", ck.len,ck.s); get_qparam(auth,s_ik,ik); LOG(L_DBG,"DBG:"M_NAME":save_contact_security: IK: <%.*s>\n", ik.len,ik.s); get_param(sec_hdr,s_ealg,ealg); LOG(L_DBG,"DBG:"M_NAME":save_contact_security: Enc Algorithm: <%.*s>\n", ealg.len,ealg.s); get_param(sec_hdr,s_alg,alg); LOG(L_DBG,"DBG:"M_NAME":save_contact_security: Int Algorithm: <%.*s>\n", alg.len,alg.s); /* and for spis */ get_param(sec_hdr,s_spi_c,tmp); strtoint(tmp,spi_uc); LOG(L_DBG,"DBG:"M_NAME":save_contact_security: SPI-C: %d\n", spi_uc); get_param(sec_hdr,s_spi_s,tmp); strtoint(tmp,spi_us); LOG(L_DBG,"DBG:"M_NAME":save_contact_security: SPI-S: %d\n", spi_us); /* and for ports */ get_param(sec_hdr,s_port_c,tmp); strtoint(tmp,port_uc); LOG(L_DBG,"DBG:"M_NAME":save_contact_security: Port-C: %d\n", port_uc); get_param(sec_hdr,s_port_s,tmp); strtoint(tmp,port_us); LOG(L_DBG,"DBG:"M_NAME":save_contact_security: Port-S: %d\n", port_us); ck_esp.s[ck_esp.len++]='0'; ck_esp.s[ck_esp.len++]='x'; if (ealg.len == s_des_in.len && strncasecmp(ealg.s,s_des_in.s,ealg.len)==0) { memcpy(ck_esp.s+ck_esp.len,ck.s,32);ck_esp.len+=32; memcpy(ck_esp.s+ck_esp.len,ck.s,16);ck_esp.len+=16; ealg_setkey = s_des_out; } else if (ealg.len == s_aes_in.len && strncasecmp(ealg.s,s_aes_in.s,ealg.len)==0) { memcpy(ck_esp.s+ck_esp.len,ck.s,ck.len);ck_esp.len+=ck.len; ealg_setkey = s_aes_out; }else { memcpy(ck_esp.s+ck_esp.len,ck.s,ck.len);ck_esp.len+=ck.len; ealg_setkey = s_null_out; ealg = s_null_out; } ik_esp.s[ik_esp.len++]='0'; ik_esp.s[ik_esp.len++]='x'; if (alg.len == s_md5_in.len && strncasecmp(alg.s,s_md5_in.s,alg.len)==0) { memcpy(ik_esp.s+ik_esp.len,ik.s,ik.len);ik_esp.len+=ik.len; alg_setkey = s_md5_out; } else if (alg.len == s_sha_in.len && strncasecmp(alg.s,s_sha_in.s,alg.len)==0) { memcpy(ik_esp.s+ik_esp.len,ik.s,ik.len);ik_esp.len+=ik.len; memcpy(ik_esp.s+ik_esp.len,"00000000",8);ik_esp.len+=8; alg_setkey = s_sha_out; }else{ LOG(L_ERR,"ERR:"M_NAME":save_contact_security: Unknown Integrity algorithm <%.*s>\n",alg.len,alg.s); goto error; } spi_pc=get_next_spi(); spi_ps=get_next_spi(); ipsec = new_r_ipsec(spi_uc,spi_us,spi_pc,spi_ps,port_uc,port_us, ealg_setkey,ealg, ck_esp,alg_setkey,alg, ik_esp); if (!ipsec) goto error; s->data.ipsec = ipsec; } break; } } rc = update_r_contact_sec(puri.host,puri.port_no,puri.proto, &(c->uri),®_state,&expires,s); return rc; error: if (s) free_r_security(s); return 0; }
/** * Process the 200 response for REGISTER and creates the first Security-Associations. * The rest of the SA are not set. * could come over that one. * @param rpl - the 200 response * @param str1 - not used * @param str2 - not used * @returns 1 if ok, 0 if not */ int P_security_200(struct sip_msg *rpl,char *str1, char *str2) { struct sip_msg *req; str sec_hdr; r_security_type sec_type; float sec_q; struct hdr_field *h; struct via_body *vb; r_contact *c; r_ipsec *i; int expires; unsigned long s_hash; char out_rpl[256],out_req[256],inc_rpl[256]; if (!pcscf_use_ipsec &&!pcscf_use_tls) goto ret_false; req = cscf_get_request_from_reply(rpl); if (!req){ LOG(L_ERR,"ERR:"M_NAME":P_security_200: No transactional request found.\n"); goto error; } sec_hdr = cscf_get_pref_security_header(req,s_security_client, &sec_type,&sec_q); if (!sec_hdr.len) { LOG(L_DBG,"DBG:"M_NAME":P_security_200: No Security-Verify header found.\n"); goto error; } /* find the expires (reg or dereg?) */ expires = cscf_get_max_expires(req); /* get the IPSec info from the registrar */ vb = cscf_get_first_via(req,&h); LOG(L_DBG,"DBG:"M_NAME":P_security_200: Looking for <%d://%.*s:%d> \n", vb->proto,vb->host.len,vb->host.s,vb->port); c = get_r_contact(vb->host,vb->port,vb->proto, ANY_REG); r_act_time(); if (!c){ LOG(L_ERR,"ERR:"M_NAME":P_security_200: Contact not found\n"); goto error; } if (c->security_temp){ if (c->security && c->security->type == SEC_TLS && (c->security->data.tls && c->security->data.tls->port_tls==req->rcv.src_port&& c->security->data.tls->session_hash!=0 && c->security->data.tls->session_hash == tls_get_session_hash(req))){ /* don't replace security when doing an integrity protected REGISTER with * possible attack-garbage from security_temp */ P_security_drop(c,c->security_temp); free_r_security(c->security_temp); c->security_temp = 0; } else { if (c->security) { P_security_drop(c,c->security); free_r_security(c->security); } c->security = c->security_temp; c->security_temp = 0; } } switch(sec_type){ case SEC_NONE: break; case SEC_TLS: if (c->security && pcscf_use_tls) { r_tls *tls; int port_tls = req->rcv.src_port; s_hash = get_tls_session_hash(req); if (!s_hash){ LOG(L_ERR,"ERR:"M_NAME":P_security_200: Session Hash could not be obtained !\n"); r_unlock(c->hash); goto error; } tls = new_r_tls(port_tls, s_hash); if (!tls) goto error; c->security->data.tls = tls; } r_unlock(c->hash); break; case SEC_IPSEC: if (!r_valid_contact(c)||!c->security||!c->security->data.ipsec ){ LOG(L_DBG,"DBG:"M_NAME":P_security_200: Contact expired or no IPSec info\n"); r_unlock(c->hash); goto error; } i = c->security->data.ipsec; /* P_Out_Rpl */ sprintf(out_rpl,"%s %.*s %hu %s %d %u %.*s %.*s %.*s %.*s %.*s %.*s", pcscf_ipsec_P_Out_Rpl, c->host.len,c->host.s, i->port_uc, pcscf_ipsec_host, pcscf_ipsec_port_s, i->spi_uc, i->ealg.len,i->ealg.s, i->ck.len,i->ck.s, i->alg.len,i->alg.s, i->ik.len,i->ik.s, i->prot.len,i->prot.s, i->mod.len,i->mod.s); /* P_Out_Req */ sprintf(out_req,"%s %.*s %hu %s %d %u %.*s %.*s %.*s %.*s %.*s %.*s", pcscf_ipsec_P_Out_Req, c->host.len,c->host.s, i->port_us, pcscf_ipsec_host, pcscf_ipsec_port_c, i->spi_us, i->ealg.len,i->ealg.s, i->ck.len,i->ck.s, i->alg.len,i->alg.s, i->ik.len,i->ik.s, i->prot.len,i->prot.s, i->mod.len,i->mod.s); /* P_Out_Inc_Rpl */ sprintf(inc_rpl,"%s %.*s %hu %s %d %u %.*s %.*s %.*s %.*s %.*s %.*s", pcscf_ipsec_P_Inc_Rpl, c->host.len,c->host.s, i->port_us, pcscf_ipsec_host, pcscf_ipsec_port_c, i->spi_pc, i->ealg.len,i->ealg.s, i->ck.len,i->ck.s, i->alg.len,i->alg.s, i->ik.len,i->ik.s, i->prot.len,i->prot.s, i->mod.len,i->mod.s); if (expires<=0) { /* Deregister */ c->reg_state = DEREGISTERED; r_act_time(); c->expires = time_now + 60; } r_unlock(c->hash); //print_r(L_CRIT); /* run the IPSec scripts */ /* Registration */ execute_cmd(out_rpl); execute_cmd(out_req); execute_cmd(inc_rpl); break; } return CSCF_RETURN_TRUE; ret_false: return CSCF_RETURN_FALSE; error: return CSCF_RETURN_ERROR; }