CVE-2023-28095: Insufficient Information (NVD-CWE-noinfo)
value: OpenSIPS is a Session Initiation Protocol (SIP) server implementation. Versions prior to 3.1.7 and 3.2.4 have a potential issue in `msg_translator.c:2628` which might lead to a server crash. This issue was found while fuzzing the function `build_res_buf_from_sip_req` but could not be reproduced against a running instance of OpenSIPS. This issue could not be exploited against a running instance of OpenSIPS since no public function was found to make use of this vulnerable code. Even in the case of exploitation through unknown vectors, it is highly unlikely that this issue would lead to anything other than Denial of Service. This issue has been fixed in versions 3.1.7 and 3.2.4.
CWE description: There is insufficient information about the issue to classify it; details are unkown or unspecified.
CVSS v3 base score: 7.5
Published: 2023-03-15T22:15Z
Repository: https://github.com/OpenSIPS/opensips
Language: C
Files changed: msg_translator.c
Commit date: 2021-11-08 10:30:07 +0200
Commit message: [core] build_res_buf_from_sip_req(): fix hdr correlation

Issue discovered during OpenSIPS Security Audit 2021,
        by Alfred Farrugia & Sandro Gauci (Enable Security)

https://github.com/OpenSIPS/opensips/security/advisories/GHSA-7pf3-24qg-8v9h

Diff: diff --git a/msg_translator.c b/msg_translator.c
index 34cf9e404..47deaa2c0 100644
--- a/msg_translator.c
+++ b/msg_translator.c
@@ -132,13 +132,10 @@ int disable_503_translation = 0;
 	do{\
 		memcpy( (_dest) , (_src) , (_len) );\
 		(_dest) += (_len) ;\
 	}while(0);
 
-#define append_str_trans(_dest,_src,_len,_msg) \
-	append_str( (_dest), (_src), (_len) );
-
 extern char version[];
 extern int version_len;
 
 
 /*! \brief check if IP address in Via != source IP address of signaling */
@@ -2487,43 +2484,44 @@ char * build_res_buf_from_sip_req( unsigned int code, const str *text ,str *new_
 	}
 
 	/* first line */
 	len += SIP_VERSION_LEN + 1/*space*/ + 3/*code*/ + 1/*space*/ +
 		text->len + CRLF_LEN/*new line*/;
-	/*headers that will be copied (TO, FROM, CSEQ,CALLID,VIA)*/
-	for ( hdr=msg->headers ; hdr ; hdr=hdr->next ) {
-		switch (hdr->type) {
-			case HDR_TO_T:
-				if (new_tag && new_tag->len) {
-					to_tag=get_to(msg)->tag_value;
-					if (to_tag.len )
-						len+=new_tag->len-to_tag.len;
-					else
-						len+=new_tag->len+TOTAG_TOKEN_LEN/*";tag="*/;
-				}
-				len += hdr->len;
-				break;
-			case HDR_VIA_T:
-				/* we always add CRLF to via*/
-				len+=(hdr->body.s+hdr->body.len)-hdr->name.s+CRLF_LEN;
-				if (hdr==msg->h_via1) len += received_len+rport_len;
-				break;
-			case HDR_RECORDROUTE_T:
-				/* RR only for 1xx and 2xx replies */
-				if (code<180 || code>=300)
-					break;
-			case HDR_FROM_T:
-			case HDR_CALLID_T:
-			case HDR_CSEQ_T:
-				/* we keep the original termination for these headers*/
-				len += hdr->len;
-				break;
-			default:
-				/* do nothing, we are interested only in the above headers */
-				;
+	/* copy the TO hdr */
+	if (msg->to) {
+		if (new_tag && new_tag->len) {
+			to_tag=get_to(msg)->tag_value;
+			if (to_tag.len )
+				len+=new_tag->len-to_tag.len;
+			else
+				len+=new_tag->len+TOTAG_TOKEN_LEN/*";tag="*/;
 		}
-	}
+		len += msg->to->len;
+	}
+
+	/* copy all VIA hdrs */
+	for( hdr=msg->h_via1 ; hdr ; hdr=hdr->sibling) {
+		/* we always add CRLF to via*/
+		len+=(hdr->body.s+hdr->body.len)-hdr->name.s+CRLF_LEN;
+		if (hdr==msg->h_via1) len += received_len+rport_len;
+	}
+	/* copy all Record-Route hdrs */
+	for( hdr=msg->record_route ; hdr ; hdr=hdr->sibling) {
+		/* RR only for 1xx and 2xx replies */
+		if (code>=180 && code<300)
+			len += hdr->len;
+	}
+	/* copy the FROM hdr */
+	if (msg->from)
+		len += msg->from->len;
+	/* copy the CALLID hdr */
+	if (msg->callid)
+		len += msg->callid->len;
+	/* copy the CSEQ hdr */
+	if (msg->cseq)
+		len += msg->cseq->len;
+
 	/* lumps length */
 	for(lump=msg->reply_lump;lump;lump=lump->next) {
 		len += lump->text.len;
 		if (lump->flags&LUMP_RPL_BODY)
 			body = lump;
@@ -2568,104 +2566,107 @@ char * build_res_buf_from_sip_req( unsigned int code, const str *text ,str *new_
 	*(p++) = ' ' ;
 	memcpy( p , text->s , text->len );
 	p += text->len;
 	memcpy( p, CRLF, CRLF_LEN );
 	p+=CRLF_LEN;
-	/* headers*/
-	for ( hdr=msg->headers ; hdr ; hdr=hdr->next ) {
-		switch (hdr->type)
-		{
-			case HDR_VIA_T:
-				if (hdr==msg->h_via1){
-					i = 0;
-					if (received_buf) {
-						i = msg->via1->host.s - msg->via1->hdr.s +
-							msg->via1->host.len + (msg->via1->port?
-							msg->via1->port_str.len + 1 : 0);
-						/* copy via1 up to params */
-						append_str( p, hdr->name.s, i);
-						/* copy received param */
-						append_str( p, received_buf, received_len);
-					}
-					if (rport_buf){
-						if (msg->via1->rport){ /* delete the old one */
-							/* copy until rport */
-							append_str_trans( p, hdr->name.s+i ,
-								msg->via1->rport->start-hdr->name.s-1-i,msg);
-							/* copy new rport */
-							append_str(p, rport_buf, rport_len);
-							/* copy the rest of the via */
-							append_str_trans(p, msg->via1->rport->start+
-												msg->via1->rport->size,
-												hdr->body.s+hdr->body.len-
-												msg->via1->rport->start-
-												msg->via1->rport->size, msg);
-						}else{ /* just copy rport and rest of hdr */
-							append_str(p, rport_buf, rport_len);
-							append_str_trans( p, hdr->name.s+i ,
-								(hdr->body.s+hdr->body.len)-hdr->name.s-i,msg);
-						}
-					}else{
-						/* normal whole via copy */
-						append_str_trans( p, hdr->name.s+i ,
-							(hdr->body.s+hdr->body.len)-hdr->name.s-i, msg);
-					}
-				}else{
-					/* normal whole via copy */
-					append_str_trans( p, hdr->name.s,
-							(hdr->body.s+hdr->body.len)-hdr->name.s, msg);
-				}
-				append_str( p, CRLF,CRLF_LEN);
-				break;
-			case HDR_RECORDROUTE_T:
-				/* RR only for 1xx and 2xx replies */
-				if (code<180 || code>=300) break;
-				append_str(p, hdr->name.s, hdr->len);
-				break;
-			case HDR_TO_T:
-				if (new_tag && new_tag->len){
-					if (to_tag.len ) { /* replacement */
-						/* before to-tag */
-						append_str( p, hdr->name.s, to_tag.s-hdr->name.s);
-						/* to tag replacement */
-						bmark->to_tag_val.s=p;
-						bmark->to_tag_val.len=new_tag->len;
-						append_str( p, new_tag->s,new_tag->len);
-						/* the rest after to-tag */
-						append_str( p, to_tag.s+to_tag.len,
-							hdr->name.s+hdr->len-(to_tag.s+to_tag.len));
-					}else{ /* adding a new to-tag */
-						after_body=hdr->body.s+hdr->body.len;
-						append_str( p, hdr->name.s, after_body-hdr->name.s);
-						append_str(p, TOTAG_TOKEN, TOTAG_TOKEN_LEN);
-						bmark->to_tag_val.s=p;
-						bmark->to_tag_val.len=new_tag->len;
-						append_str( p, new_tag->s,new_tag->len);
-						append_str( p, after_body,
-										hdr->name.s+hdr->len-after_body);
-					}
-					break;
-				} /* no new to-tag -- proceed to 1:1 copying  */
-				totags=((struct to_body*)(hdr->parsed))->tag_value.s;
-				if (totags) {
-					bmark->to_tag_val.s=p+(totags-hdr->name.s);
-					bmark->to_tag_val.len=
-							((struct to_body*)(hdr->parsed))->tag_value.len;
-				} else {
-					bmark->to_tag_val.s = NULL;
-					bmark->to_tag_val.len = 0;
-				}
-			case HDR_FROM_T:
-			case HDR_CALLID_T:
-			case HDR_CSEQ_T:
-					append_str(p, hdr->name.s, hdr->len);
-					break;
-			default:
-				/* do nothing, we are interested only in the above headers */
-				;
-		} /* end switch */
-	} /* end for */
+	/* VIA headers */
+	if ( (hdr=msg->h_via1)!=NULL ) {
+		/* handle the VIA1, subject to changes */
+		i = 0;
+		if (received_buf) {
+			i = msg->via1->host.s - msg->via1->hdr.s +
+				msg->via1->host.len + (msg->via1->port?
+				msg->via1->port_str.len + 1 : 0);
+			/* copy via1 up to params */
+			append_str( p, hdr->name.s, i);
+			/* copy received param */
+			append_str( p, received_buf, received_len);
+		}
+		if (rport_buf){
+			if (msg->via1->rport){ /* delete the old one */
+				/* copy until rport */
+				append_str( p, hdr->name.s+i ,
+					msg->via1->rport->start-hdr->name.s-1-i);
+				/* copy new rport */
+				append_str(p, rport_buf, rport_len);
+				/* copy the rest of the via */
+				append_str(p, msg->via1->rport->start+
+									msg->via1->rport->size,
+									hdr->body.s+hdr->body.len-
+									msg->via1->rport->start-
+									msg->via1->rport->size);
+			}else{ /* just copy rport and rest of hdr */
+				append_str(p, rport_buf, rport_len);
+				append_str( p, hdr->name.s+i ,
+					(hdr->body.s+hdr->body.len)-hdr->name.s-i);
+			}
+		}else{
+			/* normal whole via copy */
+			append_str( p, hdr->name.s+i ,
+				(hdr->body.s+hdr->body.len)-hdr->name.s-i);
+		}
+		append_str( p, CRLF,CRLF_LEN);
+		/* and now the rest of the VIA hdrs */
+		for( hdr=hdr->sibling ; hdr ; hdr=hdr->sibling) {
+			/* normal whole via copy */
+			append_str( p, hdr->name.s,
+					(hdr->body.s+hdr->body.len)-hdr->name.s);
+			append_str( p, CRLF,CRLF_LEN);
+		}
+	}
+	/* Record-Route headers */
+	for( hdr=msg->record_route ; hdr ; hdr=hdr->sibling) {
+		/* RR only for 1xx and 2xx replies */
+		if (code>=180 && code<300)
+			append_str(p, hdr->name.s, hdr->len);
+	}
+	/* TO hdr */
+	if ( (hdr=msg->to)!=NULL ) {
+		if (new_tag && new_tag->len){
+			if (to_tag.len ) { /* replacement */
+				/* before to-tag */
+				append_str( p, hdr->name.s, to_tag.s-hdr->name.s);
+				/* to tag replacement */
+				bmark->to_tag_val.s=p;
+				bmark->to_tag_val.len=new_tag->len;
+				append_str( p, new_tag->s,new_tag->len);
+				/* the rest after to-tag */
+				append_str( p, to_tag.s+to_tag.len,
+					hdr->name.s+hdr->len-(to_tag.s+to_tag.len));
+			}else{ /* adding a new to-tag */
+				after_body=hdr->body.s+hdr->body.len;
+				append_str( p, hdr->name.s, after_body-hdr->name.s);
+				append_str(p, TOTAG_TOKEN, TOTAG_TOKEN_LEN);
+				bmark->to_tag_val.s=p;
+				bmark->to_tag_val.len=new_tag->len;
+				append_str( p, new_tag->s,new_tag->len);
+				append_str( p, after_body,
+								hdr->name.s+hdr->len-after_body);
+			}
+		} else {
+			/* no new to-tag -- proceed to 1:1 copying  */
+			totags=((struct to_body*)(hdr->parsed))->tag_value.s;
+			if (totags) {
+				bmark->to_tag_val.s=p+(totags-hdr->name.s);
+				bmark->to_tag_val.len=
+					((struct to_body*)(hdr->parsed))->tag_value.len;
+			} else {
+				bmark->to_tag_val.s = NULL;
+				bmark->to_tag_val.len = 0;
+			}
+			append_str(p, hdr->name.s, hdr->len);
+		}
+	}
+	/* FROM header */
+	if (msg->from)
+		append_str(p, msg->from->name.s, msg->from->len);
+	/* CALLID header */
+	if (msg->callid)
+		append_str(p, msg->callid->name.s, msg->callid->len);
+	/* copy the CSEQ hdr */
+	if (msg->cseq)
+		append_str(p, msg->cseq->name.s, msg->cseq->len);
 	/* lumps */
 	for(lump=msg->reply_lump;lump;lump=lump->next)
 		if (lump->flags&LUMP_RPL_HDR){
 			memcpy(p,lump->text.s,lump->text.len);
 			p += lump->text.len;

Vulnerable code: #define append_str_trans(_dest,_src,_len,_msg) \
	append_str( (_dest), (_src), (_len) );

	/*headers that will be copied (TO, FROM, CSEQ,CALLID,VIA)*/
	for ( hdr=msg->headers ; hdr ; hdr=hdr->next ) {
		switch (hdr->type) {
			case HDR_TO_T:
				if (new_tag && new_tag->len) {
					to_tag=get_to(msg)->tag_value;
					if (to_tag.len )
						len+=new_tag->len-to_tag.len;
					else
						len+=new_tag->len+TOTAG_TOKEN_LEN/*";tag="*/;
				}
				len += hdr->len;
				break;
			case HDR_VIA_T:
				/* we always add CRLF to via*/
				len+=(hdr->body.s+hdr->body.len)-hdr->name.s+CRLF_LEN;
				if (hdr==msg->h_via1) len += received_len+rport_len;
				break;
			case HDR_RECORDROUTE_T:
				/* RR only for 1xx and 2xx replies */
				if (code<180 || code>=300)
					break;
			case HDR_FROM_T:
			case HDR_CALLID_T:
			case HDR_CSEQ_T:
				/* we keep the original termination for these headers*/
				len += hdr->len;
				break;
			default:
				/* do nothing, we are interested only in the above headers */
				;
	}
	/* headers*/
	for ( hdr=msg->headers ; hdr ; hdr=hdr->next ) {
		switch (hdr->type)
		{
			case HDR_VIA_T:
				if (hdr==msg->h_via1){
					i = 0;
					if (received_buf) {
						i = msg->via1->host.s - msg->via1->hdr.s +
							msg->via1->host.len + (msg->via1->port?
							msg->via1->port_str.len + 1 : 0);
						/* copy via1 up to params */
						append_str( p, hdr->name.s, i);
						/* copy received param */
						append_str( p, received_buf, received_len);
					}
					if (rport_buf){
						if (msg->via1->rport){ /* delete the old one */
							/* copy until rport */
							append_str_trans( p, hdr->name.s+i ,
								msg->via1->rport->start-hdr->name.s-1-i,msg);
							/* copy new rport */
							append_str(p, rport_buf, rport_len);
							/* copy the rest of the via */
							append_str_trans(p, msg->via1->rport->start+
												msg->via1->rport->size,
												hdr->body.s+hdr->body.len-
												msg->via1->rport->start-
												msg->via1->rport->size, msg);
						}else{ /* just copy rport and rest of hdr */
							append_str(p, rport_buf, rport_len);
							append_str_trans( p, hdr->name.s+i ,
								(hdr->body.s+hdr->body.len)-hdr->name.s-i,msg);
						}
					}else{
						/* normal whole via copy */
						append_str_trans( p, hdr->name.s+i ,
							(hdr->body.s+hdr->body.len)-hdr->name.s-i, msg);
					}
				}else{
					/* normal whole via copy */
					append_str_trans( p, hdr->name.s,
							(hdr->body.s+hdr->body.len)-hdr->name.s, msg);
				}
				append_str( p, CRLF,CRLF_LEN);
				break;
			case HDR_RECORDROUTE_T:
				/* RR only for 1xx and 2xx replies */
				if (code<180 || code>=300) break;
				append_str(p, hdr->name.s, hdr->len);
				break;
			case HDR_TO_T:
				if (new_tag && new_tag->len){
					if (to_tag.len ) { /* replacement */
						/* before to-tag */
						append_str( p, hdr->name.s, to_tag.s-hdr->name.s);
						/* to tag replacement */
						bmark->to_tag_val.s=p;
						bmark->to_tag_val.len=new_tag->len;
						append_str( p, new_tag->s,new_tag->len);
						/* the rest after to-tag */
						append_str( p, to_tag.s+to_tag.len,
							hdr->name.s+hdr->len-(to_tag.s+to_tag.len));
					}else{ /* adding a new to-tag */
						after_body=hdr->body.s+hdr->body.len;
						append_str( p, hdr->name.s, after_body-hdr->name.s);
						append_str(p, TOTAG_TOKEN, TOTAG_TOKEN_LEN);
						bmark->to_tag_val.s=p;
						bmark->to_tag_val.len=new_tag->len;
						append_str( p, new_tag->s,new_tag->len);
						append_str( p, after_body,
										hdr->name.s+hdr->len-after_body);
					}
					break;
				} /* no new to-tag -- proceed to 1:1 copying  */
				totags=((struct to_body*)(hdr->parsed))->tag_value.s;
				if (totags) {
					bmark->to_tag_val.s=p+(totags-hdr->name.s);
					bmark->to_tag_val.len=
							((struct to_body*)(hdr->parsed))->tag_value.len;
				} else {
					bmark->to_tag_val.s = NULL;
					bmark->to_tag_val.len = 0;
				}
			case HDR_FROM_T:
			case HDR_CALLID_T:
			case HDR_CSEQ_T:
					append_str(p, hdr->name.s, hdr->len);
					break;
			default:
				/* do nothing, we are interested only in the above headers */
				;
		} /* end switch */
	} /* end for */
Fixed code: 	/* copy the TO hdr */
	if (msg->to) {
		if (new_tag && new_tag->len) {
			to_tag=get_to(msg)->tag_value;
			if (to_tag.len )
				len+=new_tag->len-to_tag.len;
			else
				len+=new_tag->len+TOTAG_TOKEN_LEN/*";tag="*/;
		len += msg->to->len;
	}

	/* copy all VIA hdrs */
	for( hdr=msg->h_via1 ; hdr ; hdr=hdr->sibling) {
		/* we always add CRLF to via*/
		len+=(hdr->body.s+hdr->body.len)-hdr->name.s+CRLF_LEN;
		if (hdr==msg->h_via1) len += received_len+rport_len;
	}
	/* copy all Record-Route hdrs */
	for( hdr=msg->record_route ; hdr ; hdr=hdr->sibling) {
		/* RR only for 1xx and 2xx replies */
		if (code>=180 && code<300)
			len += hdr->len;
	}
	/* copy the FROM hdr */
	if (msg->from)
		len += msg->from->len;
	/* copy the CALLID hdr */
	if (msg->callid)
		len += msg->callid->len;
	/* copy the CSEQ hdr */
	if (msg->cseq)
		len += msg->cseq->len;

	/* VIA headers */
	if ( (hdr=msg->h_via1)!=NULL ) {
		/* handle the VIA1, subject to changes */
		i = 0;
		if (received_buf) {
			i = msg->via1->host.s - msg->via1->hdr.s +
				msg->via1->host.len + (msg->via1->port?
				msg->via1->port_str.len + 1 : 0);
			/* copy via1 up to params */
			append_str( p, hdr->name.s, i);
			/* copy received param */
			append_str( p, received_buf, received_len);
		}
		if (rport_buf){
			if (msg->via1->rport){ /* delete the old one */
				/* copy until rport */
				append_str( p, hdr->name.s+i ,
					msg->via1->rport->start-hdr->name.s-1-i);
				/* copy new rport */
				append_str(p, rport_buf, rport_len);
				/* copy the rest of the via */
				append_str(p, msg->via1->rport->start+
									msg->via1->rport->size,
									hdr->body.s+hdr->body.len-
									msg->via1->rport->start-
									msg->via1->rport->size);
			}else{ /* just copy rport and rest of hdr */
				append_str(p, rport_buf, rport_len);
				append_str( p, hdr->name.s+i ,
					(hdr->body.s+hdr->body.len)-hdr->name.s-i);
			}
		}else{
			/* normal whole via copy */
			append_str( p, hdr->name.s+i ,
				(hdr->body.s+hdr->body.len)-hdr->name.s-i);
		}
		append_str( p, CRLF,CRLF_LEN);
		/* and now the rest of the VIA hdrs */
		for( hdr=hdr->sibling ; hdr ; hdr=hdr->sibling) {
			/* normal whole via copy */
			append_str( p, hdr->name.s,
					(hdr->body.s+hdr->body.len)-hdr->name.s);
			append_str( p, CRLF,CRLF_LEN);
		}
	}
	/* Record-Route headers */
	for( hdr=msg->record_route ; hdr ; hdr=hdr->sibling) {
		/* RR only for 1xx and 2xx replies */
		if (code>=180 && code<300)
			append_str(p, hdr->name.s, hdr->len);
	}
	/* TO hdr */
	if ( (hdr=msg->to)!=NULL ) {
		if (new_tag && new_tag->len){
			if (to_tag.len ) { /* replacement */
				/* before to-tag */
				append_str( p, hdr->name.s, to_tag.s-hdr->name.s);
				/* to tag replacement */
				bmark->to_tag_val.s=p;
				bmark->to_tag_val.len=new_tag->len;
				append_str( p, new_tag->s,new_tag->len);
				/* the rest after to-tag */
				append_str( p, to_tag.s+to_tag.len,
					hdr->name.s+hdr->len-(to_tag.s+to_tag.len));
			}else{ /* adding a new to-tag */
				after_body=hdr->body.s+hdr->body.len;
				append_str( p, hdr->name.s, after_body-hdr->name.s);
				append_str(p, TOTAG_TOKEN, TOTAG_TOKEN_LEN);
				bmark->to_tag_val.s=p;
				bmark->to_tag_val.len=new_tag->len;
				append_str( p, new_tag->s,new_tag->len);
				append_str( p, after_body,
								hdr->name.s+hdr->len-after_body);
			}
		} else {
			/* no new to-tag -- proceed to 1:1 copying  */
			totags=((struct to_body*)(hdr->parsed))->tag_value.s;
			if (totags) {
				bmark->to_tag_val.s=p+(totags-hdr->name.s);
				bmark->to_tag_val.len=
					((struct to_body*)(hdr->parsed))->tag_value.len;
			} else {
				bmark->to_tag_val.s = NULL;
				bmark->to_tag_val.len = 0;
			}
			append_str(p, hdr->name.s, hdr->len);
		}
	}
	/* FROM header */
	if (msg->from)
		append_str(p, msg->from->name.s, msg->from->len);
	/* CALLID header */
	if (msg->callid)
		append_str(p, msg->callid->name.s, msg->callid->len);
	/* copy the CSEQ hdr */
	if (msg->cseq)
		append_str(p, msg->cseq->name.s, msg->cseq->len);