CVE-2020-27507: Buffer Copy without Checking Size of Input ('Classic Buffer Overflow') (CWE-120)
value: The Kamailio SIP before 5.5.0 server mishandles INVITE requests with duplicated fields and overlength tag, leading to a buffer overflow that crashes the server or possibly have unspecified other impact.
CWE description: The product copies an input buffer to an output buffer without verifying that the size of the input buffer is less than the size of the output buffer, leading to a buffer overflow.
CVSS v3 base score: 9.8
Published: 2023-03-15T20:15Z
Repository: https://github.com/kamailio/kamailio
Language: C
Files changed: src/modules/tm/t_msgbuilder.c
Commit date: 2020-10-09 22:15:12 +0200
Commit message: tm: do not add duplicate headers in local requests

Diff: diff --git a/src/modules/tm/t_msgbuilder.c b/src/modules/tm/t_msgbuilder.c
index bd75b271be..cda84b6f5d 100644
--- a/src/modules/tm/t_msgbuilder.c
+++ b/src/modules/tm/t_msgbuilder.c
@@ -266,10 +266,11 @@ char *build_local_reparse(tm_cell_t *Trans,unsigned int branch,
 	int cancel_buf_len;
 	int reason_len, code_len;
 	struct hdr_field *reas1, *reas_last, *hdr;
 	int hadded = 0;
 	sr_cfgenv_t *cenv = NULL;
+	hdr_flags_t hdr_flags = 0;
 
 	invite_buf = Trans->uac[branch].request.buffer;
 	invite_len = Trans->uac[branch].request.buffer_len;
 
 	if (!invite_buf || invite_len<=0) {
@@ -359,10 +360,15 @@ char *build_local_reparse(tm_cell_t *Trans,unsigned int branch,
 		}
 
 		switch(hf_type) {
 			case HDR_CSEQ_T:
 				/* find the method name and replace it */
+				if(hdr_flags &  HDR_CSEQ_F) {
+					LM_DBG("duplicate CSeq header\n");
+					goto errorhdr;
+				}
+				hdr_flags |=  HDR_CSEQ_F;
 				while ((s < invite_buf_end)
 					&& ((*s == ':') || (*s == ' ') || (*s == '\t') ||
 						((*s >= '0') && (*s <= '9')))
 					) s++;
 				append_str(d, s1, s - s1);
@@ -379,10 +385,16 @@ char *build_local_reparse(tm_cell_t *Trans,unsigned int branch,
 					first_via = 0;
 				} /* else skip this line, we need olny the first via */
 				break;
 
 			case HDR_TO_T:
+				if(hdr_flags &  HDR_TO_F) {
+					LM_DBG("duplicate To header\n");
+					goto errorhdr;
+				}
+				hdr_flags |=  HDR_TO_F;
+
 				if (to_len == 0) {
 					/* there is no To tag required, just copy paste
 					 * the header */
 					s = lw_next_line(s, invite_buf_end);
 					append_str(d, s1, s - s1);
@@ -393,11 +405,29 @@ char *build_local_reparse(tm_cell_t *Trans,unsigned int branch,
 					s = lw_next_line(s, invite_buf_end);
 				}
 				break;
 
 			case HDR_FROM_T:
+				/* copy hf */
+				if(hdr_flags &  HDR_FROM_F) {
+					LM_DBG("duplicate From header\n");
+					goto errorhdr;
+				}
+				hdr_flags |=  HDR_FROM_F;
+				s = lw_next_line(s, invite_buf_end);
+				append_str(d, s1, s - s1);
+				break;
 			case HDR_CALLID_T:
+				/* copy hf */
+				if(hdr_flags &  HDR_CALLID_F) {
+					LM_DBG("duplicate Call-Id header\n");
+					goto errorhdr;
+				}
+				hdr_flags |=  HDR_CALLID_F;
+				s = lw_next_line(s, invite_buf_end);
+				append_str(d, s1, s - s1);
+				break;
 			case HDR_ROUTE_T:
 			case HDR_MAXFORWARDS_T:
 				/* copy hf */
 				s = lw_next_line(s, invite_buf_end);
 				append_str(d, s1, s - s1);
@@ -493,10 +523,11 @@ char *build_local_reparse(tm_cell_t *Trans,unsigned int branch,
 	}
 
 	/* HDR_EOH_T was not found in the buffer, the message is corrupt */
 	LM_ERR("HDR_EOH_T was not found\n");
 
+errorhdr:
 	shm_free(cancel_buf);
 error:
 	LM_ERR("cannot build %.*s request\n", method_len, method);
 	return NULL;
 

Fixed code: 	hdr_flags_t hdr_flags = 0;
				if(hdr_flags &  HDR_CSEQ_F) {
					LM_DBG("duplicate CSeq header\n");
					goto errorhdr;
				}
				hdr_flags |=  HDR_CSEQ_F;
				if(hdr_flags &  HDR_TO_F) {
					LM_DBG("duplicate To header\n");
					goto errorhdr;
				}
				hdr_flags |=  HDR_TO_F;

				/* copy hf */
				if(hdr_flags &  HDR_FROM_F) {
					LM_DBG("duplicate From header\n");
					goto errorhdr;
				}
				hdr_flags |=  HDR_FROM_F;
				s = lw_next_line(s, invite_buf_end);
				append_str(d, s1, s - s1);
				break;
				/* copy hf */
				if(hdr_flags &  HDR_CALLID_F) {
					LM_DBG("duplicate Call-Id header\n");
					goto errorhdr;
				}
				hdr_flags |=  HDR_CALLID_F;
				s = lw_next_line(s, invite_buf_end);
				append_str(d, s1, s - s1);
				break;
errorhdr: