浏览代码

Rework fix for the static buffer overflow with the content of the overly long payload types list. Instead of silently truncating the list, use dynamic buffer allocated on heap to keep that list growing it if necessary.

sobomax 16 年之前
父节点
当前提交
10250c1255
共有 1 个文件被更改,包括 162 次插入70 次删除
  1. 162 70
      modules_s/nathelper/nathelper.c

+ 162 - 70
modules_s/nathelper/nathelper.c

@@ -1916,6 +1916,52 @@ force_rtp_proxy2_f(struct sip_msg *msg, char *param1, char *param2)
 	return force_rtp_proxy(msg, param1, param2, offer);
 }
 
+struct options {
+	str s;
+	int oidx;
+};
+
+static int
+append_opts(struct options *op, char ch)
+{
+	void *p;
+
+	if (op->s.len <= op->oidx) {
+		p = pkg_realloc(op->s.s, op->oidx + 32);
+		if (p == NULL) {
+			return (-1);
+		}
+		op->s.s = p;
+		op->s.len = op->oidx + 32;
+	}
+	op->s.s[op->oidx++] = ch;
+	return (0);
+}
+
+static void
+free_opts(struct options *op1, struct options *op2, struct options *op3)
+{
+
+	if (op1->s.len > 0 && op1->s.s != NULL) {
+		pkg_free(op1->s.s);
+		op1->s.len = 0;
+	}
+	if (op2->s.len > 0 && op2->s.s != NULL) {
+		pkg_free(op2->s.s);
+		op2->s.len = 0;
+	}
+	if (op3->s.len > 0 && op3->s.s != NULL) {
+		pkg_free(op3->s.s);
+		op3->s.len = 0;
+	}
+}
+
+#define FORCE_RTP_PROXY_RET(e) \
+    do { \
+	free_opts(&opts, &rep_opts, &pt_opts); \
+	return (e); \
+    } while (0);
+
 static int
 force_rtp_proxy(struct sip_msg *msg, char *param1, char *param2, int offer)
 {
@@ -1923,18 +1969,19 @@ force_rtp_proxy(struct sip_msg *msg, char *param1, char *param2, int offer)
 	str callid, from_tag, to_tag, tmp, c1_oldip, payload_types;
 	str oldrtcp, newrtcp;
 	int create, port, len, asymmetric, flookup, argc, proxied, real, i;
-	int oidx, pf, pf1, force, c1_pf, rep_oidx;
+	int pf, pf1, force, c1_pf;
+	struct options opts, rep_opts, pt_opts;
 	unsigned int node_idx, oldport_i;
-	char opts[32];
-	char rep_opts[16];
 	char *cp, *cp1;
 	char  *cpend, *next;
 	char **ap, *argv[10];
 	struct lump* anchor;
 	struct rtpp_node *node;
-	struct iovec v[16] = {
-		{NULL, 0},	/* command */
-		{NULL, 0},	/* options */
+	struct iovec v[] = {
+		{NULL, 0},	/* reserved (cookie) */
+		{NULL, 0},	/* command & common options */
+		{NULL, 0},	/* per-media/per-node options 1 */
+		{NULL, 0},	/* per-media/per-node options 2 */
 		{" ", 1},	/* separator */
 		{NULL, 0},	/* callid */
 		{" ", 1},	/* separator */
@@ -1972,10 +2019,15 @@ force_rtp_proxy(struct sip_msg *msg, char *param1, char *param2, int offer)
 		return -1;
 	}
 
-	v[1].iov_base=opts;
+	memset(&opts, '\0', sizeof(opts));
+	memset(&rep_opts, '\0', sizeof(rep_opts));
+	memset(&pt_opts, '\0', sizeof(pt_opts));
+	/* Leave space for U/L prefix TBD later */
+	if (append_opts(&opts, '?') == -1) {
+		LM_ERR("out of pkg memory\n");
+		FORCE_RTP_PROXY_RET (-1);
+	}
 	asymmetric = flookup = force = real = 0;
-	oidx = 1;
-	rep_oidx = 0;
 	node_idx = -1;
 	for (i=0; i<str1.len; i++) {
 		switch (str1.s[i]) {
@@ -1985,25 +2037,35 @@ force_rtp_proxy(struct sip_msg *msg, char *param1, char *param2, int offer)
 
 		case 'a':
 		case 'A':
-			opts[oidx++] = 'A';
+			if (append_opts(&opts, 'A') == -1) {
+				LM_ERR("out of pkg memory\n");
+				FORCE_RTP_PROXY_RET (-1);
+			}
 			asymmetric = 1;
 			real = 1;
 			break;
 
 		case 'i':
 		case 'I':
-			opts[oidx++] = 'I';
+			if (append_opts(&opts, 'I') == -1) {
+				LM_ERR("out of pkg memory\n");
+				FORCE_RTP_PROXY_RET (-1);
+			}
 			break;
 
 		case 'e':
 		case 'E':
-			opts[oidx++] = 'E';
+			if (append_opts(&opts, 'E') == -1) {
+				LM_ERR("out of pkg memory\n");
+				FORCE_RTP_PROXY_RET (-1);
+			}
 			break;
 
 		case 'l':
 		case 'L':
-			if (offer == 0)
-				return -1;
+			if (offer == 0) {
+				FORCE_RTP_PROXY_RET (-1);
+			}
 			flookup = 1;
 			break;
 
@@ -2027,22 +2089,29 @@ force_rtp_proxy(struct sip_msg *msg, char *param1, char *param2, int offer)
 			if (s.len == 0) {
 				LOG(L_ERR, "ERROR: force_rtp_proxy2: non-negative integer"
 					"should follow N option\n");
-				return -1;
+				FORCE_RTP_PROXY_RET (-1);
 			}
 			str2int(&s, &node_idx);
 			break;
 
 		case 'z':
 		case 'Z':
-			rep_opts[rep_oidx++] = 'Z';
+			if (append_opts(&rep_opts, 'Z') == -1) {
+				LM_ERR("out of pkg memory\n");
+				FORCE_RTP_PROXY_RET (-1);
+			}
 			/* If there are any digits following Z copy them into the command */
-			for (; i < str1.len - 1 && isdigit(str1.s[i + 1]); i++)
-				rep_opts[rep_oidx++] = str1.s[i + 1];
+			for (; i < str1.len - 1 && isdigit(str1.s[i + 1]); i++) {
+				if (append_opts(&rep_opts, str1.s[i + 1]) == -1) {
+					LM_ERR("out of pkg memory\n");
+					FORCE_RTP_PROXY_RET (-1);
+				}
+			}
 			break;
 
 		default:
 			LOG(L_ERR, "ERROR: force_rtp_proxy2: unknown option `%c'\n", str1.s[i]);
-			return -1;
+			FORCE_RTP_PROXY_RET (-1);
 		}
 	}
 
@@ -2057,23 +2126,24 @@ force_rtp_proxy(struct sip_msg *msg, char *param1, char *param2, int offer)
 	if (extract_body(msg, &body) == -1) {
 		LOG(L_ERR, "ERROR: force_rtp_proxy2: can't extract body "
 		    "from the message\n");
-		return -1;
+		FORCE_RTP_PROXY_RET (-1);
 	}
 	if (get_callid(msg, &callid) == -1 || callid.len == 0) {
 		LOG(L_ERR, "ERROR: force_rtp_proxy2: can't get Call-Id field\n");
-		return -1;
+		FORCE_RTP_PROXY_RET (-1);
 	}
 	if (get_to_tag(msg, &to_tag) == -1) {
 		LOG(L_ERR, "ERROR: force_rtp_proxy2: can't get To tag\n");
-		return -1;
+		FORCE_RTP_PROXY_RET (-1);
 	}
 	if (get_from_tag(msg, &from_tag) == -1 || from_tag.len == 0) {
 		LOG(L_ERR, "ERROR: force_rtp_proxy2: can't get From tag\n");
-		return -1;
+		FORCE_RTP_PROXY_RET (-1);
 	}
 	if (flookup != 0 || (msg->first_line.type == SIP_REPLY && offer != 0)) {
-		if (to_tag.len == 0)
-			return -1;
+		if (to_tag.len == 0) {
+			FORCE_RTP_PROXY_RET (-1);
+		}
 		tmp = from_tag;
 		from_tag = to_tag;
 		to_tag = tmp;
@@ -2091,8 +2161,9 @@ force_rtp_proxy(struct sip_msg *msg, char *param1, char *param2, int offer)
 		}
 		cp = cp1 + ANORTPPROXY_LEN;
 	}
-	if (proxied != 0 && force == 0)
-		return -1;
+	if (proxied != 0 && force == 0) {
+		FORCE_RTP_PROXY_RET (-1);
+	}
 	/*
 	 * Parsing of SDP body.
 	 * It can contain a few session descriptions (each starts with
@@ -2109,12 +2180,20 @@ force_rtp_proxy(struct sip_msg *msg, char *param1, char *param2, int offer)
 	v1p = find_sdp_line(body.s, bodylimit, 'v');
 	if (v1p == NULL) {
 		LOG(L_ERR, "ERROR: force_rtp_proxy2: no sessions in SDP\n");
-		return -1;
+		FORCE_RTP_PROXY_RET (-1);
 	}
 	v2p = find_next_sdp_line(v1p, bodylimit, 'v', bodylimit);
 	media_multi = (v2p != bodylimit);
 	v2p = v1p;
 	medianum = 0;
+
+	opts.s.s[0] = (create == 0) ? 'L' : 'U';
+	v[1].iov_base = opts.s.s;
+	v[1].iov_len = opts.oidx;
+	STR2IOVEC(callid, v[5]);
+	STR2IOVEC(from_tag, v[11]);
+	STR2IOVEC(to_tag, v[15]);
+
 	for(;;) {
 		/* Per-session iteration. */
 		v1p = v2p;
@@ -2126,7 +2205,7 @@ force_rtp_proxy(struct sip_msg *msg, char *param1, char *param2, int offer)
 		/* Have this session media description? */
 		if (m1p == NULL) {
 			LOG(L_ERR, "ERROR: force_rtp_proxy2: no m= in session\n");
-			return -1;
+			FORCE_RTP_PROXY_RET (-1);
 		}
 		/*
 		 * Find c1p only between session begin and first media.
@@ -2163,8 +2242,9 @@ force_rtp_proxy(struct sip_msg *msg, char *param1, char *param2, int offer)
 				if (extract_mediaip(&tmpstr1, &oldip, &pf) == -1) {
 					LOG(L_ERR, "ERROR: force_rtp_proxy2: can't"
 					    " extract media IP from the message\n");
-					if (c1_oldip.s == NULL)
-						return -1;
+					if (c1_oldip.s == NULL) {
+						FORCE_RTP_PROXY_RET (-1);
+					}
 					/* Try to continue if we have the sess. header ip */
 					oldip = c1_oldip;
 					c2p = NULL;
@@ -2176,7 +2256,7 @@ force_rtp_proxy(struct sip_msg *msg, char *param1, char *param2, int offer)
 				/* No "c=" */
 				LOG(L_ERR, "ERROR: force_rtp_proxy2: can't"
 				    " find media IP in the message\n");
-				return -1;
+				FORCE_RTP_PROXY_RET (-1);
 			}
 			tmpstr1.s = m1p;
 			tmpstr1.len = m2p - m1p;
@@ -2184,7 +2264,7 @@ force_rtp_proxy(struct sip_msg *msg, char *param1, char *param2, int offer)
 			    &payload_types) == -1) {
 				LOG(L_ERR, "ERROR: force_rtp_proxy2: can't"
 				    " extract media port from the message\n");
-				return -1;
+				FORCE_RTP_PROXY_RET (-1);
 			}
 			/* Extract rtcp attribute */
 			tmpstr1.s = m1p;
@@ -2202,8 +2282,10 @@ force_rtp_proxy(struct sip_msg *msg, char *param1, char *param2, int offer)
 			}
 			/* XXX must compare address families in all addresses */
 			if (pf == AF_INET6) {
-				opts[oidx] = '6';
-				oidx++;
+				if (append_opts(&opts, '6') == -1) {
+					LM_ERR("out of pkg memory\n");
+					FORCE_RTP_PROXY_RET (-1);
+				}
 			}
 			str2int(&oldport, &oldport_i);
 			/*
@@ -2216,42 +2298,38 @@ force_rtp_proxy(struct sip_msg *msg, char *param1, char *param2, int offer)
 			snprintf(medianum_buf, sizeof medianum_buf, "%d", medianum);
 			medianum_str.s = medianum_buf;
 			medianum_str.len = strlen(medianum_buf);
-			opts[0] = (create == 0) ? 'L' : 'U';
-			v[1].iov_len = oidx;
-			STR2IOVEC(callid, v[3]);
-			STR2IOVEC(newip, v[5]);
-			STR2IOVEC(oldport, v[7]);
-			STR2IOVEC(from_tag, v[9]);
+			STR2IOVEC(newip, v[7]);
+			STR2IOVEC(oldport, v[9]);
 			if (1 || media_multi) /* XXX netch: can't choose now*/
 			{
-				STR2IOVEC(medianum_str, v[11]);
+				STR2IOVEC(medianum_str, v[13]);
 			} else {
-				v[10].iov_len = v[11].iov_len = 0;
+				v[12].iov_len = v[13].iov_len = 0;
 			}
-			STR2IOVEC(to_tag, v[13]);
-			STR2IOVEC(medianum_str, v[15]);
+			STR2IOVEC(medianum_str, v[17]);
 			do {
 				node = select_rtpp_node(callid, 1, node_idx);
 				if (!node) {
 					LOG(L_ERR, "ERROR: force_rtp_proxy2: no available proxies\n");
-					return -1;
+					FORCE_RTP_PROXY_RET (-1);
 				}
-				len = v[1].iov_len;
-				if (rep_oidx > 0) {
+				if (rep_opts.oidx > 0) {
 					if (node->rn_rep_supported == 0) {
 						LOG(L_WARN, "WARNING: force_rtp_proxy2: "
 						    "re-packetization is requested but is not "
 						    "supported by the selected RTP proxy node\n");
+						v[2].iov_len = 0;
 					} else {
-						memcpy((char *)v[1].iov_base + v[1].iov_len,
-						    rep_opts, rep_oidx);
-						v[1].iov_len += rep_oidx;
+						v[2].iov_base = rep_opts.s.s;
+						v[2].iov_len += rep_opts.oidx;
 					}
 				}
 				if (payload_types.len > 0 && node->rn_ptl_supported != 0) {
-					cp1 = (char *)v[1].iov_base + v[1].iov_len;
-					*cp1 = 'c';
-					cp1++;
+					pt_opts.oidx = 0;
+					if (append_opts(&pt_opts, 'c') == -1) {
+						LM_ERR("out of pkg memory\n");
+						FORCE_RTP_PROXY_RET (-1);
+					}
 					/*
 					 * Convert space-separated payload types list into
 					 * a comma-separated list.
@@ -2259,22 +2337,31 @@ force_rtp_proxy(struct sip_msg *msg, char *param1, char *param2, int offer)
 					for (cp = payload_types.s;
 					    cp < payload_types.s + payload_types.len; cp++) {
 						if (isdigit(*cp)) {
-							*cp1 = *cp;
-							cp1++;
+							if (append_opts(&pt_opts, *cp) == -1) {
+								LM_ERR("out of pkg memory\n");
+								FORCE_RTP_PROXY_RET (-1);
+							}
 							continue;
 						}
-						*cp1 = ',';
-						cp1++;
 						do {
 							cp++;
 						} while (!isdigit(*cp) &&
 						    cp < payload_types.s + payload_types.len);
+						/* Check EOL */
+						if (cp >= payload_types.s + payload_types.len)
+							break;
+						if (append_opts(&pt_opts, ',') == -1) {
+							LM_ERR("out of pkg memory\n");
+							FORCE_RTP_PROXY_RET (-1);
+						}
 						cp--;
 					}
-					v[1].iov_len = cp1 - (char *)v[1].iov_base;
+					v[3].iov_base = pt_opts.s.s;
+					v[3].iov_len = pt_opts.oidx;
+				} else {
+					v[3].iov_len = 0;
 				}
 				cp = send_rtpp_command(node, v, (to_tag.len > 0) ? 16 : 12);
-				v[1].iov_len = len;
 			} while (cp == NULL);
 			LOG(L_DBG, "force_rtp_proxy2: proxy reply: %s\n", cp);
 			/* Parse proxy reply to <argc,argv> */
@@ -2293,7 +2380,7 @@ force_rtp_proxy(struct sip_msg *msg, char *param1, char *param2, int offer)
 			}
 			if (argc < 1) {
 				LOG(L_ERR, "force_rtp_proxy2: no reply from rtp proxy\n");
-				return -1;
+				FORCE_RTP_PROXY_RET (-1);
 			}
 			port = atoi(argv[0]);
 			if (port <= 0 || port > 65535) {
@@ -2305,7 +2392,7 @@ force_rtp_proxy(struct sip_msg *msg, char *param1, char *param2, int offer)
 				 */
 				if (port != 0 || flookup == 0)
 					LOG(L_ERR, "force_rtp_proxy2: incorrect port in reply from rtp proxy\n");
-				return -1;
+				FORCE_RTP_PROXY_RET (-1);
 			}
 
 			pf1 = (argc >= 3 && argv[2][0] == '6') ? AF_INET6 : AF_INET;
@@ -2340,8 +2427,9 @@ force_rtp_proxy(struct sip_msg *msg, char *param1, char *param2, int offer)
 				/* Alter port. */
 				body1.s = m1p;
 				body1.len = bodylimit - body1.s;
-				if (alter_mediaport(msg, &body1, &oldport, &newport, 0) == -1)
-					return -1;
+				if (alter_mediaport(msg, &body1, &oldport, &newport, 0) == -1) {
+					FORCE_RTP_PROXY_RET (-1);
+				}
 			}
 			/*
 			 * Alter RTCP attribute if present. Inserting RTP port + 1 (as allocated
@@ -2354,8 +2442,9 @@ force_rtp_proxy(struct sip_msg *msg, char *param1, char *param2, int offer)
 				/* Alter port. */
 				body1.s = m1p;
 				body1.len = bodylimit - body1.s;
-				if (alter_rtcp(msg, &body1, &oldrtcp, &newrtcp) == -1)
-					return -1;
+				if (alter_rtcp(msg, &body1, &oldrtcp, &newrtcp) == -1) {
+					FORCE_RTP_PROXY_RET (-1);
+				}
 			}
 			/*
 			 * Alter IP. Don't alter IP common for the session
@@ -2368,18 +2457,21 @@ force_rtp_proxy(struct sip_msg *msg, char *param1, char *param2, int offer)
 				body1.s = c1p;
 				body1.len = bodylimit - body1.s;
 				if (alter_mediaip(msg, &body1, &c1_oldip, c1_pf,
-				    &newip, pf1, 0) == -1)
-					return -1;
+				    &newip, pf1, 0) == -1) {
+					FORCE_RTP_PROXY_RET (-1);
+				}
 				c1p_altered = 1;
 			}
 			if (c2p != NULL && oldip.s != c1_oldip.s) {
 				body1.s = c2p;
 				body1.len = bodylimit - body1.s;
-				if (alter_mediaip(msg, &body1, &oldip, pf, &newip, pf1, 0) == -1)
-					return -1;
+				if (alter_mediaip(msg, &body1, &oldip, pf, &newip, pf1, 0) == -1) {
+					FORCE_RTP_PROXY_RET (-1);
+				}
 			}
 		} /* Iterate medias in session */
 	} /* Iterate sessions */
+	free_opts(&opts, &rep_opts, &pt_opts);
 
 	if (proxied == 0) {
 		cp = pkg_malloc((ANORTPPROXY_LEN + CRLF_LEN) * sizeof(char));