Exemple #1
0
/*! \brief handles io from a tcp worker process
 * \param  tcp_c - pointer in the tcp_children array, to the entry for
 *                 which an io event was detected
 * \param  fd_i  - fd index in the fd_array (useful for optimizing
 *                 io_watch_deletes)
 * \return handle_* return convention: -1 on error, 0 on EAGAIN (no more
 *           io events queued), >0 on success. success/error refer only to
 *           the reads from the fd.
 */
inline static int handle_tcp_worker(struct tcp_child* tcp_c, int fd_i)
{
	struct tcp_connection* tcpconn;
	long response[2];
	int cmd;
	int bytes;

	if (tcp_c->unix_sock<=0){
		/* (we can't have a fd==0, 0 is never closed )*/
		LM_CRIT("fd %d for %d (pid %d, ser no %d)\n", tcp_c->unix_sock,
				(int)(tcp_c-&tcp_children[0]), tcp_c->pid, tcp_c->proc_no);
		goto error;
	}
	/* read until sizeof(response)
	 * (this is a SOCK_STREAM so read is not atomic) */
	bytes=recv_all(tcp_c->unix_sock, response, sizeof(response), MSG_DONTWAIT);
	if (bytes<(int)sizeof(response)){
		if (bytes==0){
			/* EOF -> bad, child has died */
			LM_DBG("dead tcp worker %d (pid %d, no %d)"
					" (shutting down?)\n", (int)(tcp_c-&tcp_children[0]),
					tcp_c->pid, tcp_c->proc_no );
			/* don't listen on it any more */
			reactor_del_reader( tcp_c->unix_sock, fd_i, 0/*flags*/);
			/* eof. so no more io here, it's ok to return error */
			goto error;
		}else if (bytes<0){
			/* EAGAIN is ok if we try to empty the buffer
			 * e.g.: SIGIO_RT overflow mode or EPOLL ET */
			if ((errno!=EAGAIN) && (errno!=EWOULDBLOCK)){
				LM_CRIT("read from tcp worker %ld (pid %d, no %d) %s [%d]\n",
						(long)(tcp_c-&tcp_children[0]), tcp_c->pid,
						tcp_c->proc_no, strerror(errno), errno );
			}else{
				bytes=0;
			}
			/* try to ignore ? */
			goto end;
		}else{
			/* should never happen */
			LM_CRIT("too few bytes received (%d)\n", bytes );
			bytes=0; /* something was read so there is no error; otoh if
					  receive_fd returned less then requested => the receive
					  buffer is empty => no more io queued on this fd */
			goto end;
		}
	}

	LM_DBG("reader response= %lx, %ld from %d \n",
		response[0], response[1], (int)(tcp_c-&tcp_children[0]));

	cmd=response[1];
	tcpconn=(struct tcp_connection*)response[0];
	if (tcpconn==0){
		/* should never happen */
		LM_CRIT("null tcpconn pointer received from tcp child %d (pid %d):"
			"%lx, %lx\n", (int)(tcp_c-&tcp_children[0]), tcp_c->pid,
			response[0], response[1]) ;
		goto end;
	}
	switch(cmd){
		case CONN_RELEASE:
			tcp_c->busy--;
			if (tcpconn->state==S_CONN_BAD){
				tcpconn_destroy(tcpconn);
				break;
			}
			tcpconn_put(tcpconn);
			/* must be after the de-ref*/
			reactor_add_reader( tcpconn->s, F_TCPCONN, RCT_PRIO_NET, tcpconn);
			tcpconn->flags&=~F_CONN_REMOVED;
			break;
		case ASYNC_WRITE:
			tcp_c->busy--;
			if (tcpconn->state==S_CONN_BAD){
				tcpconn_destroy(tcpconn);
				break;
			}
			tcpconn_put(tcpconn);
			/* must be after the de-ref*/
			reactor_add_writer( tcpconn->s, F_TCPCONN, RCT_PRIO_NET, tcpconn);
			tcpconn->flags&=~F_CONN_REMOVED;
			break;
		case CONN_ERROR:
		case CONN_DESTROY:
		case CONN_EOF:
			/* WARNING: this will auto-dec. refcnt! */
			tcp_c->busy--;
			/* main doesn't listen on it => we don't have to delete it
			 if (tcpconn->s!=-1)
				io_watch_del(&io_h, tcpconn->s, -1, IO_FD_CLOSING);
			*/
			tcpconn_destroy(tcpconn); /* closes also the fd */
			break;
		default:
			LM_CRIT("unknown cmd %d from tcp reader %d\n",
				cmd, (int)(tcp_c-&tcp_children[0]));
	}
end:
	return bytes;
error:
	return -1;
}
Exemple #2
0
static int get_alt(str* res, int local, int type, sip_msg_t* msg)
{
	static char buf[1024];
	int n, found = 0;
	STACK_OF(GENERAL_NAME)* names = 0;
	GENERAL_NAME* nm;
	X509* cert;
	struct tcp_connection* c;
	str text;
	struct ip_addr ip;

	if (get_cert(&cert, &c, msg, local) < 0) return -1;

	names = X509_get_ext_d2i(cert, NID_subject_alt_name, NULL, NULL);
	if (!names) {
		DBG("Cannot get certificate alternative subject\n");
		goto err;

	}

	for (n = 0; n < sk_GENERAL_NAME_num(names); n++) {
		nm = sk_GENERAL_NAME_value(names, n);
		if (nm->type != type) continue;
		switch(type) {
		case GEN_EMAIL:
		case GEN_DNS:
		case GEN_URI:
			text.s = (char*)nm->d.ia5->data;
			text.len = nm->d.ia5->length;
			if (text.len >= 1024) {
				ERR("Alternative subject text too long\n");
				goto err;
			}
			memcpy(buf, text.s, text.len);
			res->s = buf;
			res->len = text.len;
			found = 1;
			break;

		case GEN_IPADD:
			ip.len = nm->d.iPAddress->length;
			ip.af = (ip.len == 16) ? AF_INET6 : AF_INET;
			memcpy(ip.u.addr, nm->d.iPAddress->data, ip.len);
			text.s = ip_addr2a(&ip);
			text.len = strlen(text.s);
			memcpy(buf, text.s, text.len);
			res->s = buf;
			res->len = text.len;
			found = 1;
			break;
		}
		break;
	}
	if (!found) goto err;

	if (names) sk_GENERAL_NAME_pop_free(names, GENERAL_NAME_free);
	if (!local) X509_free(cert);
	tcpconn_put(c);
	return 0;
 err:
	if (names) sk_GENERAL_NAME_pop_free(names, GENERAL_NAME_free);
	if (!local) X509_free(cert);
	tcpconn_put(c);
	return -1;
}
Exemple #3
0
int ws_handle_handshake(struct sip_msg *msg)
{
	str key = {0, 0}, headers = {0, 0}, reply_key = {0, 0}, origin = {0, 0};
	unsigned char sha1[SHA_DIGEST_LENGTH];
	unsigned int hdr_flags = 0, sub_protocol = 0;
	int version = 0;
	struct hdr_field *hdr = msg->headers;
	struct tcp_connection *con;
	ws_connection_t *wsc;

	/* Make sure that the connection is closed after the response _and_
	   the existing connection (from the request) is reused for the
	   response.  The close flag will be unset later if the handshake is
	   successful. */
	msg->rpl_send_flags.f |= SND_F_CON_CLOSE;
	msg->rpl_send_flags.f |= SND_F_FORCE_CON_REUSE;

	if (cfg_get(websocket, ws_cfg, enabled) == 0)
	{
		LM_INFO("disabled: bouncing handshake\n");
		ws_send_reply(msg, 503, &str_status_service_unavailable,
				NULL);
		return 0;
	}

	/* Retrieve TCP/TLS connection */
	if ((con = tcpconn_get(msg->rcv.proto_reserved1, 0, 0, 0, 0)) == NULL)
	{
		LM_ERR("retrieving connection\n");
		ws_send_reply(msg, 500, &str_status_internal_server_error,
				NULL);
		return 0;
	}

	if (con->type != PROTO_TCP && con->type != PROTO_TLS)
	{
		LM_ERR("unsupported transport: %d", con->type);
		goto end;
	}

	if (parse_headers(msg, HDR_EOH_F, 0) < 0)
	{
		LM_ERR("error parsing headers\n");
		ws_send_reply(msg, 500, &str_status_internal_server_error,
				NULL);
		goto end;
	}

	/* Process HTTP headers */
	while (hdr != NULL)
	{
		/* Decode and validate Connection */
		if (cmp_hdrname_strzn(&hdr->name,
				str_hdr_connection.s,
				str_hdr_connection.len) == 0)
		{
			strlower(&hdr->body);
			if (str_search(&hdr->body, &str_upgrade) != NULL)
			{
				LM_DBG("found %.*s: %.*s\n",

					hdr->name.len, hdr->name.s,
					hdr->body.len, hdr->body.s);
				hdr_flags |= CONNECTION;
			}
		}
		/* Decode and validate Upgrade */
		else if (cmp_hdrname_strzn(&hdr->name,
				str_hdr_upgrade.s,
				str_hdr_upgrade.len) == 0)
		{
			strlower(&hdr->body);
			if (str_search(&hdr->body, &str_websocket) != NULL)
			{
				LM_DBG("found %.*s: %.*s\n",
					hdr->name.len, hdr->name.s,
					hdr->body.len, hdr->body.s);
				hdr_flags |= UPGRADE;
			}
		}
		/* Decode and validate Sec-WebSocket-Key */
		else if (cmp_hdrname_strzn(&hdr->name,
				str_hdr_sec_websocket_key.s, 
				str_hdr_sec_websocket_key.len) == 0) 
		{
			if (hdr_flags & SEC_WEBSOCKET_KEY)
			{
				LM_WARN("%.*s found multiple times\n",
					hdr->name.len, hdr->name.s);
				ws_send_reply(msg, 400,
						&str_status_bad_request,
						NULL);
				goto end;
			}

			LM_DBG("found %.*s: %.*s\n",
				hdr->name.len, hdr->name.s,
				hdr->body.len, hdr->body.s);
			key = hdr->body;
			hdr_flags |= SEC_WEBSOCKET_KEY;
		}
		/* Decode and validate Sec-WebSocket-Protocol */
		else if (cmp_hdrname_strzn(&hdr->name,
				str_hdr_sec_websocket_protocol.s,
				str_hdr_sec_websocket_protocol.len) == 0)
		{
			strlower(&hdr->body);
			if (str_search(&hdr->body, &str_sip) != NULL)
			{
				LM_DBG("found %.*s: %.*s\n",
					hdr->name.len, hdr->name.s,
					hdr->body.len, hdr->body.s);
				hdr_flags |= SEC_WEBSOCKET_PROTOCOL;
				sub_protocol |= SUB_PROTOCOL_SIP;
			}
			if (str_search(&hdr->body, &str_msrp) != NULL)
			{
				LM_DBG("found %.*s: %.*s\n",
					hdr->name.len, hdr->name.s,
					hdr->body.len, hdr->body.s);
				hdr_flags |= SEC_WEBSOCKET_PROTOCOL;
				sub_protocol |= SUB_PROTOCOL_MSRP;
			}
		}
		/* Decode and validate Sec-WebSocket-Version */
		else if (cmp_hdrname_strzn(&hdr->name,
				str_hdr_sec_websocket_version.s,
				str_hdr_sec_websocket_version.len) == 0)
		{
			if (hdr_flags & SEC_WEBSOCKET_VERSION)
			{
				LM_WARN("%.*s found multiple times\n",
					hdr->name.len, hdr->name.s);
				ws_send_reply(msg, 400,
						&str_status_bad_request,
						NULL);
				goto end;
			}

			str2sint(&hdr->body, &version);

			if (version != WS_VERSION)
			{
				LM_WARN("Unsupported protocol version %.*s\n",
					hdr->body.len, hdr->body.s);
				headers.s = headers_buf;
				headers.len = snprintf(headers.s, HDR_BUF_LEN,
					"%.*s: %d\r\n",
					str_hdr_sec_websocket_version.len,
					str_hdr_sec_websocket_version.s,
					WS_VERSION);
				ws_send_reply(msg, 426,
						&str_status_upgrade_required,
						&headers);
				goto end;
			}

			LM_DBG("found %.*s: %.*s\n",
				hdr->name.len, hdr->name.s,
				hdr->body.len, hdr->body.s);
			hdr_flags |= SEC_WEBSOCKET_VERSION;
		}
		/* Decode Origin */
		else if (cmp_hdrname_strzn(&hdr->name,
				str_hdr_origin.s,
				str_hdr_origin.len) == 0)
		{
			if (hdr_flags & ORIGIN)
			{
				LM_WARN("%.*s found multiple times\n",
					hdr->name.len, hdr->name.s);
				ws_send_reply(msg, 400,
						&str_status_bad_request,
						NULL);
				goto end;
			}

			LM_DBG("found %.*s: %.*s\n",
				hdr->name.len, hdr->name.s,
				hdr->body.len, hdr->body.s);
			origin = hdr->body;
			hdr_flags |= ORIGIN;
		}

		hdr = hdr->next;
	}

	/* Final check that all required headers/values were found */
	sub_protocol &= ws_sub_protocols;
	if ((hdr_flags & REQUIRED_HEADERS) != REQUIRED_HEADERS
			|| sub_protocol == 0)
	{

		LM_WARN("required headers not present\n");
		headers.s = headers_buf;
		headers.len = 0;

		if (ws_sub_protocols & SUB_PROTOCOL_SIP)
			headers.len += snprintf(headers.s + headers.len,
						HDR_BUF_LEN - headers.len,
						"%.*s: %.*s\r\n",
					str_hdr_sec_websocket_protocol.len,
					str_hdr_sec_websocket_protocol.s,
					str_sip.len, str_sip.s);

		if (ws_sub_protocols & SUB_PROTOCOL_MSRP)
			headers.len += snprintf(headers.s + headers.len,
						HDR_BUF_LEN - headers.len,
						"%.*s: %.*s\r\n",
					str_hdr_sec_websocket_protocol.len,
					str_hdr_sec_websocket_protocol.s,
					str_msrp.len, str_msrp.s);

		headers.len += snprintf(headers.s + headers.len,
					HDR_BUF_LEN - headers.len,
					"%.*s: %d\r\n",
					str_hdr_sec_websocket_version.len,
					str_hdr_sec_websocket_version.s,
					WS_VERSION);
		ws_send_reply(msg, 400, &str_status_bad_request, &headers);
		goto end;
	}

	/* Construct reply_key */
	reply_key.s = (char *) pkg_malloc(
				(key.len + str_ws_guid.len) * sizeof(char)); 
	if (reply_key.s == NULL)
	{
		LM_ERR("allocating pkg memory\n");
		ws_send_reply(msg, 500, &str_status_internal_server_error,
				NULL);
		goto end;
	}
	memcpy(reply_key.s, key.s, key.len);
	memcpy(reply_key.s + key.len, str_ws_guid.s, str_ws_guid.len);
	reply_key.len = key.len + str_ws_guid.len;
	SHA1((const unsigned char *) reply_key.s, reply_key.len, sha1);
	pkg_free(reply_key.s);
	reply_key.s = key_buf;
	reply_key.len = base64_enc(sha1, SHA_DIGEST_LENGTH,
				(unsigned char *) reply_key.s,
				base64_enc_len(SHA_DIGEST_LENGTH));

	/* Add the connection to the WebSocket connection table */
	wsconn_add(msg->rcv, sub_protocol);

	/* Make sure Kamailio core sends future messages on this connection
	   directly to this module */
	if (con->type == PROTO_TLS)
		con->type = con->rcv.proto = PROTO_WSS;
	else
		con->type = con->rcv.proto = PROTO_WS;

	/* Now Kamailio is ready to receive WebSocket frames build and send a
	   101 reply */
	headers.s = headers_buf;
	headers.len = 0;

	if (ws_cors_mode == CORS_MODE_ANY)
		headers.len += snprintf(headers.s + headers.len,
					HDR_BUF_LEN - headers.len,
					"%.*s: *\r\n",
					str_hdr_access_control_allow_origin.len,
					str_hdr_access_control_allow_origin.s);
	else if (ws_cors_mode == CORS_MODE_ORIGIN && origin.len > 0)
		headers.len += snprintf(headers.s + headers.len,
					HDR_BUF_LEN - headers.len,
					"%.*s: %.*s\r\n",
					str_hdr_access_control_allow_origin.len,
					str_hdr_access_control_allow_origin.s,
					origin.len,
					origin.s);

	if (sub_protocol & SUB_PROTOCOL_SIP)
		headers.len += snprintf(headers.s + headers.len,
					HDR_BUF_LEN - headers.len,
					"%.*s: %.*s\r\n",
					str_hdr_sec_websocket_protocol.len,
					str_hdr_sec_websocket_protocol.s,
					str_sip.len, str_sip.s);
	else if (sub_protocol & SUB_PROTOCOL_MSRP)
		headers.len += snprintf(headers.s + headers.len,
					HDR_BUF_LEN - headers.len,
					"%.*s: %.*s\r\n",
					str_hdr_sec_websocket_protocol.len,
					str_hdr_sec_websocket_protocol.s,
					str_msrp.len, str_msrp.s);

	headers.len += snprintf(headers.s + headers.len,
				HDR_BUF_LEN - headers.len,
				"%.*s: %.*s\r\n"
				"%.*s: %.*s\r\n"
				"%.*s: %.*s\r\n",
				str_hdr_upgrade.len, str_hdr_upgrade.s,
				str_websocket.len, str_websocket.s,
				str_hdr_connection.len, str_hdr_connection.s,
				str_upgrade.len, str_upgrade.s,
				str_hdr_sec_websocket_accept.len,
				str_hdr_sec_websocket_accept.s, reply_key.len,
				reply_key.s);
	msg->rpl_send_flags.f &= ~SND_F_CON_CLOSE;
	if (ws_send_reply(msg, 101, &str_status_switching_protocols,
				&headers) < 0)
	{
		if ((wsc = wsconn_get(msg->rcv.proto_reserved1)) != NULL)
			wsconn_rm(wsc, WSCONN_EVENTROUTE_NO);

		goto end;
	}
	else
	{
		if (sub_protocol & SUB_PROTOCOL_SIP)
			update_stat(ws_sip_successful_handshakes, 1);
		else if (sub_protocol & SUB_PROTOCOL_MSRP)
			update_stat(ws_msrp_successful_handshakes, 1);
	}

	tcpconn_put(con);
	return 1;
end:
	if (con)
		tcpconn_put(con);
	return 0;
}
Exemple #4
0
/*! \brief Finds a tcpconn & sends on it */
int tcp_send(struct socket_info* send_sock, int type, char* buf, unsigned len,
			union sockaddr_union* to, int id)
{
	struct tcp_connection *c;
	struct tcp_connection *tmp;
	struct ip_addr ip;
	int port;
	int fd;
	long response[2];
	int n;
	struct timeval get,rcv,snd;
	
	port=0;

	reset_tcp_vars(tcpthreshold);
	start_expire_timer(get,tcpthreshold);

	if (to){
		su2ip_addr(&ip, to);
		port=su_getport(to);
		c=tcpconn_get(id, &ip, port, tcp_con_lifetime); 
	}else if (id){
		c=tcpconn_get(id, 0, 0, tcp_con_lifetime);
	}else{
		LM_CRIT("tcp_send called with null id & to\n");
		get_time_difference(get,tcpthreshold,tcp_timeout_con_get);
		return -1;
	}
	
	if (id){
		if (c==0) {
			if (to){
				/* try again w/o id */
				c=tcpconn_get(0, &ip, port, tcp_con_lifetime);
				goto no_id;
			}else{
				LM_ERR("id %d not found, dropping\n", id);
				get_time_difference(get,tcpthreshold,tcp_timeout_con_get);
				return -1;
			}
		}else goto get_fd;
	}
no_id:
		if (c==0){
			if (tcp_no_new_conn) {
				return -1;
			}
			LM_DBG("no open tcp connection found, opening new one\n");
			/* create tcp connection */
			if ((c=tcpconn_connect(send_sock, to, type))==0){
				LM_ERR("connect failed\n");
				get_time_difference(get,tcpthreshold,tcp_timeout_con_get);
				return -1;
			}
			c->refcnt++; /* safe to do it w/o locking, it's not yet
							available to the rest of the world */
			fd=c->s;
			
			/* send the new tcpconn to "tcp main" */
			response[0]=(long)c;
			response[1]=CONN_NEW;
			n=send_fd(unix_tcp_sock, response, sizeof(response), c->s);
			get_time_difference(get,tcpthreshold,tcp_timeout_con_get);
			if (n<=0){
				LM_ERR("failed send_fd: %s (%d)\n",	strerror(errno), errno);
				n=-1;
				goto end;
			}	
			goto send_it;
		}
get_fd:
		get_time_difference(get,tcpthreshold,tcp_timeout_con_get);
			/* todo: see if this is not the same process holding
			 *  c  and if so send directly on c->fd */
			LM_DBG("tcp connection found (%p), acquiring fd\n", c);
			/* get the fd */
			response[0]=(long)c;
			response[1]=CONN_GET_FD;
			start_expire_timer(rcv,tcpthreshold);
			n=send_all(unix_tcp_sock, response, sizeof(response));
			if (n<=0){
				LM_ERR("failed to get fd(write):%s (%d)\n",	
						strerror(errno), errno);
				n=-1;
				get_time_difference(rcv,tcpthreshold,tcp_timeout_receive_fd);
				goto release_c;
			}
			LM_DBG("c= %p, n=%d\n", c, n);
			tmp=c;
			n=receive_fd(unix_tcp_sock, &c, sizeof(c), &fd, MSG_WAITALL);
			get_time_difference(rcv,tcpthreshold,tcp_timeout_receive_fd);
			if (n<=0){
				LM_ERR("failed to get fd(receive_fd):"
							" %s (%d)\n", strerror(errno), errno);
				n=-1;
				goto release_c;
			}
			if (c!=tmp){
				LM_CRIT("got different connection:"
						"  %p (id= %d, refcnt=%d state=%d != "
						"  %p (id= %d, refcnt=%d state=%d (n=%d)\n",
						  c,   c->id,   c->refcnt,   c->state,
						  tmp, tmp->id, tmp->refcnt, tmp->state, n
				   );
				n=-1; /* fail */
				goto end;
			}
			LM_DBG("after receive_fd: c= %p n=%d fd=%d\n",c, n, fd);
		
	
	
send_it:
	LM_DBG("sending...\n");
	lock_get(&c->write_lock);
#ifdef USE_TLS
	if (c->type==PROTO_TLS)
		n=tls_blocking_write(c, fd, buf, len);
	else
#endif
		/* n=tcp_blocking_write(c, fd, buf, len); */
		start_expire_timer(snd,tcpthreshold);
		n=tsend_stream(fd, buf, len, tcp_send_timeout*1000); 
		get_time_difference(snd,tcpthreshold,tcp_timeout_send);
	
		stop_expire_timer(get,tcpthreshold,0,buf,(int)len,1);
	lock_release(&c->write_lock);
	LM_DBG("after write: c= %p n=%d fd=%d\n",c, n, fd);
	LM_DBG("buf=\n%.*s\n", (int)len, buf);
	if (n<0){
		LM_ERR("failed to send\n");
		/* error on the connection , mark it as bad and set 0 timeout */
		c->state=S_CONN_BAD;
		c->timeout=0;
		/* tell "main" it should drop this (optional it will t/o anyway?)*/
		response[0]=(long)c;
		response[1]=CONN_ERROR;
		n=send_all(unix_tcp_sock, response, sizeof(response));
		/* CONN_ERROR will auto-dec refcnt => we must not call tcpconn_put !!*/
		if (n<=0){
			LM_ERR("return failed (write):%s (%d)\n",
					strerror(errno), errno);
		}
		close(fd);
		return -1; /* error return, no tcpconn_put */
	}
end:
	close(fd);
release_c:
	tcpconn_put(c); /* release c (lock; dec refcnt; unlock) */
	return n;
}
Exemple #5
0
static int is_peer_verified(struct sip_msg* msg, char* foo, char* foo2)
{
	struct tcp_connection *c;
	SSL *ssl;
	long ssl_verify;
	X509 *x509_cert;

	DBG("started...\n");
	if (msg->rcv.proto != PROTO_TLS) {
		ERR("proto != TLS --> peer can't be verified, return -1\n");
		return -1;
	}

	DBG("trying to find TCP connection of received message...\n");

	c = tcpconn_get(msg->rcv.proto_reserved1, 0, 0, 0,
					cfg_get(tls, tls_cfg, con_lifetime));
	if (!c) {
		ERR("connection no longer exits\n");
		return -1;
	}

	if(c->type != PROTO_TLS) {
		ERR("Connection found but is not TLS\n");
		tcpconn_put(c);
		return -1;
	}

	if (!c->extra_data) {
		LM_ERR("no extra_data specified in TLS/TCP connection found."
				" This should not happen... return -1\n");
		tcpconn_put(c);
		return -1;
	}

	ssl = ((struct tls_extra_data*)c->extra_data)->ssl;

	ssl_verify = SSL_get_verify_result(ssl);
	if ( ssl_verify != X509_V_OK ) {
		LM_WARN("verification of presented certificate failed... return -1\n");
		tcpconn_put(c);
		return -1;
	}

	/* now, we have only valid peer certificates or peers without certificates.
	 * Thus we have to check for the existence of a peer certificate
	 */
	x509_cert = SSL_get_peer_certificate(ssl);
	if ( x509_cert == NULL ) {
		LM_INFO("tlsops:is_peer_verified: WARNING: peer did not present "
			"a certificate. Thus it could not be verified... return -1\n");
		tcpconn_put(c);
		return -1;
	}

	X509_free(x509_cert);

	tcpconn_put(c);

	LM_DBG("tlsops:is_peer_verified: peer is successfully verified"
		"...done\n");
	return 1;
}
Exemple #6
0
void tcp_main_loop()
{
	int r;
	int n;
	fd_set master_set;
	fd_set sel_set;
	int maxfd;
	struct tcp_connection* tcpconn;
	unsigned h;
	long response[2];
	int cmd;
	int bytes;
	struct timeval timeout;
	int fd;
	struct socket_info* si;

	/*init */
	maxfd=0;
	FD_ZERO(&master_set);
	/* set all the listen addresses */
	for (si=tcp_listen; si; si=si->next){
		if ((si->proto==PROTO_TCP) &&(si->socket!=-1)){
			FD_SET(si->socket, &master_set);
			if (si->socket>maxfd) maxfd=si->socket;
		}else{
			LOG(L_CRIT, "BUG: tcp_main_loop: non tcp address in tcp_listen\n");
		}
	}
#ifdef USE_TLS
	if (!tls_disable){
		for (si=tls_listen; si; si=si->next){
			if ((si->proto==PROTO_TLS) && (si->socket!=-1)){
				FD_SET(si->socket, &master_set);
				if (si->socket>maxfd) maxfd=si->socket;
			}else{
				LOG(L_CRIT, "BUG: tcp_main_loop: non tls address"
						" in tls_listen\n");
			}
		}
	}
#endif
	/* set all the unix sockets used for child comm */
	for (r=1; r<process_no; r++){
		if (pt[r].unix_sock>0){ /* we can't have 0, we never close it!*/
			FD_SET(pt[r].unix_sock, &master_set);
			if (pt[r].unix_sock>maxfd) maxfd=pt[r].unix_sock;
		}
	}
	for (r=0; r<tcp_children_no; r++){
		if (tcp_children[r].unix_sock>0){ /* we can't have 0, 
											 we never close it!*/
			FD_SET(tcp_children[r].unix_sock, &master_set);
			if (tcp_children[r].unix_sock>maxfd)
				maxfd=tcp_children[r].unix_sock;
		}
	}
	
	
	/* main loop*/
	
	while(1){
		sel_set=master_set;
		timeout.tv_sec=TCP_MAIN_SELECT_TIMEOUT;
		timeout.tv_usec=0;
		n=select(maxfd+1, &sel_set, 0 ,0 , &timeout);
		if (n<0){
			if (errno==EINTR) continue; /* just a signal */
			/* errors */
			LOG(L_ERR, "ERROR: tcp_main_loop: select:(%d) %s\n", errno,
					strerror(errno));
			n=0;
		}
		
		for (si=tcp_listen; si && n; si=si->next)
			handle_new_connect(si, &sel_set, &n);
#ifdef USE_TLS
			if (!tls_disable)
				for (si=tls_listen; si && n; si=si->next)
					handle_new_connect(si, &sel_set, &n);
#endif
		
		/* check all the read fds (from the tcpconn_addr_hash ) */
		for (h=0; h<TCP_ID_HASH_SIZE; h++){
			for(tcpconn=tcpconn_id_hash[h]; tcpconn && n; 
					tcpconn=tcpconn->id_next){
				/* FIXME: is refcnt==0 really necessary? */
				if ((tcpconn->refcnt==0)&&(FD_ISSET(tcpconn->s, &sel_set))){
					/* new data available */
					n--;
					/* pass it to child, so remove it from select list */
					DBG("tcp_main_loop: data available on %p [h:%d] %d\n",
							tcpconn, h, tcpconn->s);
					FD_CLR(tcpconn->s, &master_set);
					tcpconn_ref(tcpconn); /* refcnt ++ */
					if (send2child(tcpconn)<0){
						LOG(L_ERR,"ERROR: tcp_main_loop: no "
									"children available\n");
						TCPCONN_LOCK;
						tcpconn->refcnt--;
						if (tcpconn->refcnt==0){
							fd=tcpconn->s;
							_tcpconn_rm(tcpconn);
							close(fd);
						}else tcpconn->timeout=0; /* force expire*/
						TCPCONN_UNLOCK;
					}
				}
			}
		}
		/* check unix sockets & listen | destroy connections */
		/* tcp_children readers first */
		for (r=0; r<tcp_children_no && n; r++){
			if ( (tcp_children[r].unix_sock>0) && 
					FD_ISSET(tcp_children[r].unix_sock, &sel_set)){
				/* (we can't have a fd==0, 0 is never closed )*/
				n--;
				/* read until sizeof(response)
				 * (this is a SOCK_STREAM so read is not atomic */
				bytes=recv_all(tcp_children[r].unix_sock, response,
								sizeof(response));
				if (bytes==0){
					/* EOF -> bad, child has died */
					DBG("DBG: tcp_main_loop: dead tcp child %d"
							" (shutting down?)\n", r);
					/* don't listen on it any more */
					FD_CLR(tcp_children[r].unix_sock, &master_set);
					/*exit(-1);*/
					continue; /* skip this and try the next one */
				}else if (bytes<0){
					LOG(L_CRIT, "ERROR: tcp_main_loop: read from tcp child %d "
							"%s\n", r, strerror(errno));
					/* try to ignore ? */
					continue; /* skip this and try the next one */
				}
					
				DBG("tcp_main_loop: reader response= %lx, %ld from %d \n",
						response[0], response[1], r);
				cmd=response[1];
				tcpconn=(struct tcp_connection*)response[0];
				switch(cmd){
					case CONN_RELEASE:
						tcp_children[r].busy--;
						if (tcpconn){
								if (tcpconn->state==S_CONN_BAD){ 
									tcpconn_destroy(tcpconn);
									break;
								}
								FD_SET(tcpconn->s, &master_set);
								if (maxfd<tcpconn->s) maxfd=tcpconn->s;
								/* update the timeout*/
								tcpconn->timeout=get_ticks()+TCP_CON_TIMEOUT;
								tcpconn_put(tcpconn);
								DBG("tcp_main_loop: CONN_RELEASE  %p"
										" refcnt= %d\n", 
										tcpconn, tcpconn->refcnt);
						}
						break;
					case CONN_ERROR:
					case CONN_DESTROY:
					case CONN_EOF:
						/* WARNING: this will auto-dec. refcnt! */
						tcp_children[r].busy--;
						if (tcpconn){
							if (tcpconn->s!=-1)
								FD_CLR(tcpconn->s, &master_set);
							tcpconn_destroy(tcpconn);
						}
						break;
					default:
							LOG(L_CRIT, "BUG: tcp_main_loop:  unknown cmd %d"
										" from tcp reader %d\n",
									cmd, r);
				}
			}
		}
		/* check "send" unix sockets & listen | destroy connections */
		/* start from 1, the "main" process does not transmit anything*/
		for (r=1; r<process_no && n; r++){
			if ( (pt[r].unix_sock>0) && FD_ISSET(pt[r].unix_sock, &sel_set)){
				/* (we can't have a fd==0, 0 is never closed )*/
				n--;
				/* read until sizeof(response)
				 * (this is a SOCK_STREAM so read is not atomic */
				bytes=recv_all(pt[r].unix_sock, response, sizeof(response));
				if (bytes==0){
					/* EOF -> bad, child has died */
					DBG("DBG: tcp_main_loop: dead child %d"
							" (shutting down?)\n", r);
					/* don't listen on it any more */
					FD_CLR(pt[r].unix_sock, &master_set);
					/*exit(-1);*/
					continue; /* skip this and try the next one */
				}else if (bytes<0){
					LOG(L_CRIT, "ERROR: tcp_main_loop: read from child:  %s\n",
							strerror(errno));
					/* try to ignore ? */
					continue; /* skip this and try the next one */
				}
					
				DBG("tcp_main_loop: read response= %lx, %ld from %d (%d)\n",
						response[0], response[1], r, pt[r].pid);
				cmd=response[1];
				tcpconn=(struct tcp_connection*)response[0];
				switch(cmd){
					case CONN_ERROR:
						if (tcpconn){
							if (tcpconn->s!=-1)
								FD_CLR(tcpconn->s, &master_set);
							tcpconn_destroy(tcpconn);
						}
						break;
					case CONN_GET_FD:
						/* send the requested FD  */
						/* WARNING: take care of setting refcnt properly to
						 * avoid race condition */
						if (tcpconn){
							if (send_fd(pt[r].unix_sock, &tcpconn,
										sizeof(tcpconn), tcpconn->s)<=0){
								LOG(L_ERR, "ERROR: tcp_main_loop:"
										"send_fd failed\n");
							}
						}else{
							LOG(L_CRIT, "BUG: tcp_main_loop: null pointer\n");
						}
						break;
					case CONN_NEW:
						/* update the fd in the requested tcpconn*/
						/* WARNING: take care of setting refcnt properly to
						 * avoid race condition */
						if (tcpconn){
							bytes=receive_fd(pt[r].unix_sock, &tcpconn,
									sizeof(tcpconn), &tcpconn->s);
								if (bytes<(int)sizeof(tcpconn)){
									if (bytes<0){
										LOG(L_CRIT, "BUG: tcp_main_loop:"
												" CONN_NEW: receive_fd "
												"failed\n");
									}else{
										LOG(L_CRIT, "BUG: tcp_main_loop:"
												" CONN_NEW: to few bytes "
												"received (%d)\n", bytes );
									}
									break; /* try to ignore */
								}
							/* add tcpconn to the list*/
							tcpconn_add(tcpconn);
							FD_SET(tcpconn->s, &master_set);
							if (maxfd<tcpconn->s) maxfd=tcpconn->s;
							/* update the timeout*/
							tcpconn->timeout=get_ticks()+TCP_CON_TIMEOUT;
						}else{
							LOG(L_CRIT, "BUG: tcp_main_loop: null pointer\n");
						}
						break;
					default:
							LOG(L_CRIT, "BUG: tcp_main_loop: unknown cmd %d\n",
									cmd);
				}
			}
		} /* for */
		
		/* remove old connections */
		tcpconn_timeout(&master_set);
	
	}
}
Exemple #7
0
int tlsops_alt(struct sip_msg *msg, pv_param_t *param,
		pv_value_t *res)
{
	static char buf[1024];
	int type = GEN_URI, my = 0, n, found = 0, ind_local;
	STACK_OF(GENERAL_NAME)* names = 0;
	GENERAL_NAME* nm;
	X509* cert;
	struct tcp_connection* c;
	str text;
	struct ip_addr ip;

	ind_local = param->pvn.u.isname.name.n;

	if (ind_local & CERT_PEER) {
		my = 0;
		ind_local = ind_local ^ CERT_PEER;
	} else if (ind_local & CERT_LOCAL) {
		my = 1;
		ind_local = ind_local ^ CERT_LOCAL;
	} else {
		LM_CRIT("could not determine certificate\n");
		return pv_get_null(msg, param, res);
	}

	switch(ind_local) {
		case COMP_E:    type = GEN_EMAIL; break;
		case COMP_HOST: type = GEN_DNS;   break;
		case COMP_URI:  type = GEN_URI;   break;
		case COMP_IP:   type = GEN_IPADD; break;
		default:
			LM_CRIT("ind_local=%d\n", ind_local);
			return pv_get_null(msg, param, res);
	}

	if (get_cert(&cert, &c, msg, my) < 0) return -1;

	names = X509_get_ext_d2i(cert, NID_subject_alt_name, NULL, NULL);
	if (!names) {
		LM_ERR("cannot get certificate alternative subject\n");
		goto err;

	}

	for (n = 0; n < sk_GENERAL_NAME_num(names); n++) {
		nm = sk_GENERAL_NAME_value(names, n);
		if (nm->type != type) continue;
		switch(type) {
		case GEN_EMAIL:
		case GEN_DNS:
		case GEN_URI:
			text.s = (char*)nm->d.ia5->data;
			text.len = nm->d.ia5->length;
			if (text.len >= 1024) {
				LM_ERR("alternative subject text too long\n");
				goto err;
			}
			memcpy(buf, text.s, text.len);
			res->rs.s = buf;
			res->rs.len = text.len;
			res->flags = PV_VAL_STR;
			found = 1;
			break;

		case GEN_IPADD:
			ip.len = nm->d.iPAddress->length;
			ip.af = (ip.len == 16) ? AF_INET6 : AF_INET;
			memcpy(ip.u.addr, nm->d.iPAddress->data, ip.len);
			text.s = ip_addr2a(&ip);
			text.len = strlen(text.s);
			memcpy(buf, text.s, text.len);
			res->rs.s = buf;
			res->rs.len = text.len;
			res->flags = PV_VAL_STR;
			found = 1;
			break;
		}
		break;
	}
	if (!found) goto err;

	if (names) sk_GENERAL_NAME_pop_free(names, GENERAL_NAME_free);
	if (!my) X509_free(cert);
	tcpconn_put(c);
	return 0;
 err:
	if (names) sk_GENERAL_NAME_pop_free(names, GENERAL_NAME_free);
	if (!my) X509_free(cert);
	tcpconn_put(c);
	return pv_get_null(msg, param, res);
}
Exemple #8
0
int tlsops_comp(struct sip_msg *msg, pv_param_t *param,
		pv_value_t *res)
{
	static char buf[1024];
	X509* cert;
	struct tcp_connection* c;
	X509_NAME* name;
	X509_NAME_ENTRY* e;
	ASN1_STRING* asn1;
	int nid = NID_commonName, index, my = 0, issuer = 0, ind_local;
	char* elem;
	str text;

	text.s = 0;
	/* copy callback value as we modify it */
	ind_local = param->pvn.u.isname.name.n;

	LM_DBG("ind_local = %x", ind_local);
	if (ind_local & CERT_PEER) {
		my = 0;
		ind_local = ind_local ^ CERT_PEER;
	} else if (ind_local & CERT_LOCAL) {
		my = 1;
		ind_local = ind_local ^ CERT_LOCAL;
	} else {
		LM_CRIT("could not determine certificate\n");
		return pv_get_null(msg, param, res);
	}

	if (ind_local & CERT_SUBJECT) {
		issuer = 0;
		ind_local = ind_local ^ CERT_SUBJECT;
	} else if (ind_local & CERT_ISSUER) {
		issuer = 1;
		ind_local = ind_local ^ CERT_ISSUER;
	} else {
		LM_CRIT("could not determine subject or issuer\n");
		return pv_get_null(msg, param, res);
	}

	switch(ind_local) {
		case COMP_CN: nid = NID_commonName;             break;
		case COMP_O:  nid = NID_organizationName;       break;
		case COMP_OU: nid = NID_organizationalUnitName; break;
		case COMP_C:  nid = NID_countryName;            break;
		case COMP_ST: nid = NID_stateOrProvinceName;    break;
		case COMP_L:  nid = NID_localityName;           break;
		default:      nid = NID_undef;
	}

	if (get_cert(&cert, &c, msg, my) < 0) return -1;

	name = issuer ? X509_get_issuer_name(cert) : X509_get_subject_name(cert);
	if (!name) {
		LM_ERR("cannot extract subject or issuer name from peer"
				" certificate\n");
		goto err;
	}

	if (nid == NID_undef) { /* dump the whole cert info into buf */
		X509_NAME_oneline(name, buf, sizeof(buf));
		res->rs.s = buf;
		res->rs.len = strlen(buf);
		res->flags = PV_VAL_STR;
	} else {
		index = X509_NAME_get_index_by_NID(name, nid, -1);
		if (index == -1) {
			switch(ind_local) {
			case COMP_CN: elem = "CommonName";              break;
			case COMP_O:  elem = "OrganizationName";        break;
			case COMP_OU: elem = "OrganizationalUnitUname"; break;
			case COMP_C:  elem = "CountryName";             break;
			case COMP_ST: elem = "StateOrProvinceName";     break;
			case COMP_L:  elem = "LocalityName";            break;
			default:      elem = "Unknown";                 break;
			}
			LM_DBG("element %s not found in "
				"certificate subject/issuer\n", elem);
			goto err;
		}
	
		e = X509_NAME_get_entry(name, index);
		asn1 = X509_NAME_ENTRY_get_data(e);
		text.len = ASN1_STRING_to_UTF8((unsigned char**)(void*)&text.s, asn1);
		if (text.len < 0 || text.len >= 1024) {
			LM_ERR("failed to convert ASN1 string\n");
			goto err;
		}
		memcpy(buf, text.s, text.len);
		res->rs.s = buf;
		res->rs.len = text.len;
		res->flags = PV_VAL_STR;
	
		OPENSSL_free(text.s);
	}
	if (!my) X509_free(cert);
	tcpconn_put(c);
	return 0;

 err:
	if (text.s) OPENSSL_free(text.s);
	if (!my) X509_free(cert);
	tcpconn_put(c);
	return pv_get_null(msg, param, res);
}
Exemple #9
0
static int is_peer_verified(struct sip_msg* msg, char* foo, char* foo2)
{
	struct tcp_connection *c;
	SSL *ssl;
	long ssl_verify;
	X509 *x509_cert;

	LM_DBG("started...\n");
	if (msg->rcv.proto != PROTO_TLS) {
		LM_ERR("proto != TLS --> peer can't be verified, return -1\n");
		return -1;
	}

	LM_DBG("trying to find TCP connection of received message...\n");
	/* what if we have multiple connections to the same remote socket? e.g. we can have 
	     connection 1: localIP1:localPort1 <--> remoteIP:remotePort
	     connection 2: localIP2:localPort2 <--> remoteIP:remotePort
	   but I think the is very unrealistic */
	c=tcpconn_get(0, &(msg->rcv.src_ip), msg->rcv.src_port, tcp_con_lifetime);
	if (!c) {
		LM_ERR("no corresponding TLS/TCP connection found."
				" This should not happen... return -1\n");
		return -1;
	}
	LM_DBG("corresponding TLS/TCP connection found. s=%d, fd=%d, id=%d\n",
			c->s, c->fd, c->id);

	if (!c->extra_data) {
		LM_ERR("no extra_data specified in TLS/TCP connection found."
				" This should not happen... return -1\n");
		tcpconn_put(c);
		return -1;
	}

	ssl = (SSL *) c->extra_data;		

	ssl_verify = SSL_get_verify_result(ssl);
	if ( ssl_verify != X509_V_OK ) {
		LM_WARN("verification of presented certificate failed... return -1\n");
		tcpconn_put(c);
		return -1;
	}
	
	/* now, we have only valid peer certificates or peers without certificates.
	 * Thus we have to check for the existence of a peer certificate
	 */
	x509_cert = SSL_get_peer_certificate(ssl);
	if ( x509_cert == NULL ) {
		LM_WARN("tlsops:is_peer_verified: WARNING: peer did not presented "
			"a certificate. Thus it could not be verified... return -1\n");
		tcpconn_put(c);
		return -1;
	}
	
	X509_free(x509_cert);
	
	tcpconn_put(c);
	
	LM_DBG("tlsops:is_peer_verified: peer is successfuly verified"
		"...done\n");
	return 1;
}
Exemple #10
0
static int get_comp(str* res, int local, int issuer, int nid, sip_msg_t* msg)
{
    static char buf[1024];
    X509* cert;
    struct tcp_connection* c;
    X509_NAME* name;
    X509_NAME_ENTRY* e;
    ASN1_STRING* asn1;
    int index, text_len;
    char* elem;
    unsigned char* text_s;

    text_s = 0;

    if (get_cert(&cert, &c, msg, local) < 0) return -1;

    name = issuer ? X509_get_issuer_name(cert) : X509_get_subject_name(cert);
    if (!name) {
        ERR("Cannot extract subject or issuer name from peer certificate\n");
        goto err;
    }

    index = X509_NAME_get_index_by_NID(name, nid, -1);
    if (index == -1) {
        switch(nid) {
        case NID_commonName:
            elem = "CommonName";
            break;
        case NID_organizationName:
            elem = "OrganizationName";
            break;
        case NID_organizationalUnitName:
            elem = "OrganizationalUnitUname";
            break;
        case NID_countryName:
            elem = "CountryName";
            break;
        case NID_stateOrProvinceName:
            elem = "StateOrProvinceName";
            break;
        case NID_localityName:
            elem = "LocalityName";
            break;
        default:
            elem = "Unknown";
            break;
        }
        DBG("Element %s not found in certificate subject/issuer\n", elem);
        goto err;
    }

    e = X509_NAME_get_entry(name, index);
    asn1 = X509_NAME_ENTRY_get_data(e);
    text_len = ASN1_STRING_to_UTF8(&text_s, asn1);
    if (text_len < 0 || text_len >= 1024) {
        ERR("Error converting ASN1 string\n");
        goto err;
    }
    memcpy(buf, text_s, text_len);
    res->s = buf;
    res->len = text_len;

    OPENSSL_free(text_s);
    if (!local) X509_free(cert);
    tcpconn_put(c);
    return 0;

err:
    if (text_s) OPENSSL_free(text_s);
    if (!local) X509_free(cert);
    tcpconn_put(c);
    return -1;
}