/** * Updates the r_contact with the new security values. * @param host - the host part of the contact, in string * @param port - the port number of the contact * @param transport - the transport of the contact * @param uri - URI of the contact * @param reg_state - Registration state * @param expires - expires in * @param ipsec - the new IPSec tunnel * @returns the newly added r_public, 0 on error */ r_contact* update_r_contact_sec(str host,int port,int transport, str *uri,enum Reg_States *reg_state,int *expires, r_security *s, int * sos_reg) { r_contact *c = NULL; r_reg_type sos_mask = NORMAL_REG; if(sos_reg && (*sos_reg)>0){ LOG(L_DBG,"DBG:"M_NAME":update_r_contact_sec: with sos uri param\n"); sos_mask = EMERG_REG; } c = get_r_contact(host,port,transport, sos_mask); if (!c){ if (uri&®_state){ c = add_r_contact(host,port,transport,*uri,*reg_state,*expires,(str*) 0,0,0,sos_mask); c->security_temp = s; r_unlock(c->hash); return c; } else return 0; }else{ if (c->security_temp){ P_security_drop(c,c->security_temp); free_r_security(c->security_temp); } c->security_temp = s; c->sos_flag = sos_mask; r_unlock(c->hash); return c; } }
/** * Gets the contact structure with the uri the same as the one given. * \note Aquires the lock on the hash_slot on success, so release it when you are done. * @param host - the host part of the contact, in string * @param port - the port number of the contact * @param transport - the transport of the contact * @return the r_nat_dest on success or NULL on failure */ r_nat_dest* get_r_nat_pinhole(str host, int port, int transport) { r_contact * contact; contact = get_r_contact(host, port, transport, ANY_REG); if(contact != NULL) { return contact -> pinhole; } LOG(L_ERR, "ERR"M_NAME":get_r_nat_pinhole: cannot find contact %d://%.*s:%d\n", transport,host.len, host.s, port); return NULL; }
/** * Updates the r_contact with the new expires, ua valu, path value. * \note If not found it is added * \note Must be called with a lock on the hash slot to avoid races * @param p - the r_public to add to * @param uri - the contact uri * @param expires - new expires value, NULL if not necessary * @param ua - new user agent string, NULL if no update necessary * @param path - Path header received at registration * @returns the updated r_contact or NULL on error */ r_contact* update_r_contact(r_public *p,str uri,int *expires, str *ua,str *path) { r_contact *c; if (!p) return 0; c = get_r_contact(p,uri); if (!c){ if (expires && ua && path) return add_r_contact(p,uri,*expires,*ua,*path); else return 0; }else{ if (expires) c->expires = *expires; if (ua){ if (c->ua.s) shm_free(c->ua.s); c->ua.s = shm_malloc(ua->len); if (!c->ua.s) { LOG(L_ERR,"ERR:"M_NAME":update_r_contact(): Error allocating %d bytes\n", ua->len); c->ua.len=0; return 0; } c->ua.len = ua->len; memcpy(c->ua.s,ua->s,ua->len); } if (path){ if (c->path.s) shm_free(c->path.s); c->path.s = shm_malloc(path->len); if (!c->path.s) { LOG(L_ERR,"ERR:"M_NAME":update_r_contact(): Error allocating %d bytes\n", path->len); c->path.len=0; return 0; } c->path.len = path->len; memcpy(c->path.s,path->s,path->len); } return c; } }
/** * 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); 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 %d %s %d %d %.*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 ); /* P_Out_Req */ sprintf(out_req,"%s %.*s %d %s %d %d %.*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 ); /* P_Out_Inc_Rpl */ sprintf(inc_rpl,"%s %.*s %d %s %d %d %.*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 ); 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; }
/** * Process the REGISTER and verify Client-Security. * @param req - Register request * @param str1 - not used * @param str2 - not used * @returns 1 if ok, 0 if not */ int P_verify_security(struct sip_msg *req,char *str1, char *str2) { str sec_hdr; struct hdr_field *h; struct via_body *vb; r_contact *c; r_security *s; r_security_type sec_type; float sec_q; str ealg,alg,tmp; unsigned int spi_pc,spi_ps;; int port_pc,port_ps; vb = cscf_get_first_via(req,&h); LOG(L_INFO,"DBG:"M_NAME":P_verify_security: 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); r_act_time(); if (!c){ //first register LOG(L_DBG,"DBG:"M_NAME":P_verify_security: No Contact found ! \n"); return CSCF_RETURN_TRUE; } if (!r_valid_contact(c) || !c->security_temp){ LOG(L_DBG,"DBG:"M_NAME":P_verify_security: No security temp !.\n"); r_unlock(c->hash); return CSCF_RETURN_TRUE; } sec_hdr = cscf_get_pref_security_header(req,s_security_verify, &sec_type,&sec_q); if (!sec_hdr.len) { LOG(L_DBG,"DBG:"M_NAME":P_verify_security: No Security-Verify header found.\n"); r_unlock(c->hash); return CSCF_RETURN_TRUE; } s = c->security_temp; switch (s->type) { case SEC_NONE: break; case SEC_TLS: if (sec_type != SEC_TLS || req->rcv.dst_port != pcscf_tls_port) goto error; break; case SEC_IPSEC: if (sec_type != SEC_IPSEC || req->rcv.dst_port != pcscf_ipsec_port_s) { LOG(L_INFO,"DBG:"M_NAME":P_verify_security: Not IPSEC tunnel!.\n"); r_unlock(c->hash); goto error; } get_param(sec_hdr,s_ealg,ealg); get_param(sec_hdr,s_alg,alg); /* and for spis */ get_param(sec_hdr,s_spi_c,tmp); strtoint(tmp,spi_pc); get_param(sec_hdr,s_spi_s,tmp); strtoint(tmp,spi_ps); /* and for ports */ get_param(sec_hdr,s_port_c,tmp); strtoint(tmp,port_pc); get_param(sec_hdr,s_port_s,tmp); strtoint(tmp,port_ps); if ((s->data.ipsec->r_ealg.len != ealg.len || strncasecmp(s->data.ipsec->r_ealg.s, ealg.s, ealg.len)) || (s->data.ipsec->r_alg.len != alg.len || strncasecmp(s->data.ipsec->r_alg.s, alg.s, alg.len)) || (s->data.ipsec->spi_pc != spi_pc) || (s->data.ipsec->spi_ps != spi_ps) || (pcscf_ipsec_port_c != port_pc) || (pcscf_ipsec_port_s != port_ps)) { LOG(L_INFO,"DBG:"M_NAME":P_verify_security: No valid Security-Verify header!.\n"); r_unlock(c->hash); goto error; } break; } r_unlock(c->hash); return CSCF_RETURN_TRUE; error: return CSCF_RETURN_FALSE; }
/** * 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_IPSec_200(struct sip_msg *rpl,char *str1, char *str2) { struct sip_msg *req; struct hdr_field *hdr; str sec_cli; struct hdr_field *h; struct via_body *vb; r_contact *c; r_ipsec *i; int expires; char out_rpl[256],out_req[256],inc_rpl[256]; req = cscf_get_request_from_reply(rpl); if (!req){ LOG(L_ERR,"ERR:"M_NAME":P_ipsec_200: No transactional request found.\n"); goto error; } /* just to jump out if no IPSec is employed - the info is already saved */ sec_cli = cscf_get_security_client(req,&hdr); if (!sec_cli.len){ LOG(L_DBG,"DBG:"M_NAME":P_ipsec_200: No Security-Client 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); // if (!h||!h->parsed){ // LOG(L_ERR,"ERR:"M_NAME":P_ipsec_200: Error extracting sender's id.\n"); // goto error; // } // vb = (struct via_body*) h->parsed; LOG(L_DBG,"DBG:"M_NAME":P_ipsec_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); r_act_time(); if (!c){ LOG(L_ERR,"ERR:"M_NAME":P_ipsec_200: Contact not found\n"); goto error; } if (!r_valid_contact(c)||!c->ipsec){ LOG(L_DBG,"DBG:"M_NAME":P_ipsec_200: Contact expired or no IPSec info\n"); r_unlock(c->hash); goto error; } i = c->ipsec; /* P_Out_Rpl */ sprintf(out_rpl,"%s %.*s %d %s %d %d %.*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 ); /* P_Out_Req */ sprintf(out_req,"%s %.*s %d %s %d %d %.*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 ); /* P_Out_Inc_Rpl */ sprintf(inc_rpl,"%s %.*s %d %s %d %d %.*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 ); 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); return 1; error: return 0; }
/** * Updates the r_contact with the new states * If not found, it will be inserted * \note Aquires the lock on the hash_slot on success, so release it when you are done. * @param host - the host part of the contact, in string * @param port - the port number of the contact * @param transport - the transport of the contact * @param uri - URI of the contact * @param reg_state - Registration state * @param expires - expires in * @param service_route - array of service routes * @param service_route_cnt - the size of the array above * @param pinhole - NAT pin hole * @param sos_flag - flag for Emergency Registration * @returns the updated added r_contact, 0 on error */ r_contact* update_r_contact(str host,int port,int transport, str *uri,enum Reg_States *reg_state,int *expires,str **service_route, int *service_route_cnt, r_nat_dest **pinhole, int *sos_flag) { r_contact *c=0; int i; r_reg_type sos_mask; if(sos_flag && (*sos_flag>0)) sos_mask = EMERG_REG; else sos_mask = NORMAL_REG; c = get_r_contact(host,port,transport, sos_mask); if (!c){ if (uri&®_state && expires && service_route && service_route_cnt) return pinhole?add_r_contact(host,port,transport,*uri,*reg_state,*expires,*service_route,*service_route_cnt, *pinhole, sos_mask): add_r_contact(host,port,transport,*uri,*reg_state,*expires,*service_route,*service_route_cnt, 0, sos_mask); else return 0; }else{ /* first drop the old temporary public ids */ if (c->reg_state==DEREGISTERED && reg_state && *reg_state==REGISTERED){ while(c->head){ del_r_public(c,c->head); } } if (uri) { if (c->uri.s) shm_free(c->uri.s); STR_SHM_DUP(c->uri,*uri,"update_r_contact"); } if (reg_state) c->reg_state = *reg_state; if (expires) c->expires = *expires; if (service_route){ if (c->service_route){ for(i=0;i<c->service_route_cnt;i++) if (c->service_route[i].s) shm_free(c->service_route[i].s); shm_free(c->service_route); c->service_route=0; c->service_route_cnt=0; } if (*service_route&&*service_route_cnt){ c->service_route = shm_malloc((*service_route_cnt)*sizeof(str)); if (!c->service_route){ LOG(L_ERR,"ERR:"M_NAME":new_r_contact(): Unable to alloc %d bytes\n", (*service_route_cnt)*sizeof(str)); goto out_of_memory; }else{ for(i=0;i<*service_route_cnt;i++) STR_SHM_DUP(c->service_route[i],(*service_route)[i],"new_r_contact"); c->service_route_cnt = *service_route_cnt; } } } if (pinhole) c->pinhole = *pinhole; c->sos_flag = sos_mask; return c; } out_of_memory: return c; }