/**
 *	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;
}
Exemple #2
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),&reg_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;
}