1 /*
2  * Copyright (C) 2012 Free Software Foundation
3  *
4  * Author: Martin Storsjo
5  *
6  * This file is part of GnuTLS.
7  *
8  * The GnuTLS is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Lesser General Public License
10  * as published by the Free Software Foundation; either version 2.1 of
11  * the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful, but
14  * WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public License
19  * along with this program.  If not, see <https://www.gnu.org/licenses/>
20  *
21  */
22 
23 #include "gnutls_int.h"
24 #include "auth.h"
25 #include "errors.h"
26 #include "num.h"
27 #include <ext/srtp.h>
28 
29 static int _gnutls_srtp_recv_params(gnutls_session_t session,
30 				    const uint8_t * data,
31 				    size_t data_size);
32 static int _gnutls_srtp_send_params(gnutls_session_t session,
33 				    gnutls_buffer_st * extdata);
34 
35 static int _gnutls_srtp_unpack(gnutls_buffer_st * ps,
36 			       gnutls_ext_priv_data_t * _priv);
37 static int _gnutls_srtp_pack(gnutls_ext_priv_data_t _priv,
38 			     gnutls_buffer_st * ps);
39 static void _gnutls_srtp_deinit_data(gnutls_ext_priv_data_t priv);
40 
41 
42 const hello_ext_entry_st ext_mod_srtp = {
43 	.name = "SRTP",
44 	.tls_id = 14,
45 	.gid = GNUTLS_EXTENSION_SRTP,
46 	.validity = GNUTLS_EXT_FLAG_TLS | GNUTLS_EXT_FLAG_DTLS | GNUTLS_EXT_FLAG_CLIENT_HELLO |
47 		    GNUTLS_EXT_FLAG_EE | GNUTLS_EXT_FLAG_TLS12_SERVER_HELLO,
48 	.client_parse_point = GNUTLS_EXT_APPLICATION,
49 	.server_parse_point = GNUTLS_EXT_APPLICATION,
50 	.recv_func = _gnutls_srtp_recv_params,
51 	.send_func = _gnutls_srtp_send_params,
52 	.pack_func = _gnutls_srtp_pack,
53 	.unpack_func = _gnutls_srtp_unpack,
54 	.deinit_func = _gnutls_srtp_deinit_data,
55 	.cannot_be_overriden = 1
56 };
57 
58 typedef struct {
59 	const char *name;
60 	gnutls_srtp_profile_t id;
61 	unsigned int key_length;
62 	unsigned int salt_length;
63 } srtp_profile_st;
64 
65 static const srtp_profile_st profile_names[] = {
66 	{
67 	 "SRTP_AES128_CM_HMAC_SHA1_80",
68 	 GNUTLS_SRTP_AES128_CM_HMAC_SHA1_80,
69 	 16, 14},
70 	{
71 	 "SRTP_AES128_CM_HMAC_SHA1_32",
72 	 GNUTLS_SRTP_AES128_CM_HMAC_SHA1_32,
73 	 16, 14},
74 	{
75 	 "SRTP_NULL_HMAC_SHA1_80",
76 	 GNUTLS_SRTP_NULL_HMAC_SHA1_80,
77 	 16, 14},
78 	{
79 	 "SRTP_NULL_SHA1_32",
80 	 GNUTLS_SRTP_NULL_HMAC_SHA1_32,
81 	 16, 14},
82 	{
83 	 NULL,
84 	 0, 0, 0}
85 };
86 
get_profile(gnutls_srtp_profile_t profile)87 static const srtp_profile_st *get_profile(gnutls_srtp_profile_t profile)
88 {
89 	const srtp_profile_st *p = profile_names;
90 	while (p->name != NULL) {
91 		if (p->id == profile)
92 			return p;
93 		p++;
94 	}
95 	return NULL;
96 }
97 
find_profile(const char * str,const char * end)98 static gnutls_srtp_profile_t find_profile(const char *str, const char *end)
99 {
100 	const srtp_profile_st *prof = profile_names;
101 	unsigned int len;
102 	if (end != NULL) {
103 		len = end - str;
104 	} else {
105 		len = strlen(str);
106 	}
107 
108 	while (prof->name != NULL) {
109 		if (strlen(prof->name) == len
110 		    && !strncmp(str, prof->name, len)) {
111 			return prof->id;
112 		}
113 		prof++;
114 	}
115 	return 0;
116 }
117 
118 /**
119  * gnutls_srtp_get_profile_id
120  * @name: The name of the profile to look up
121  * @profile: Will hold the profile id
122  *
123  * This function allows you to look up a profile based on a string.
124  *
125  * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned,
126  *   otherwise a negative error code is returned.
127  *
128  * Since 3.1.4
129  **/
gnutls_srtp_get_profile_id(const char * name,gnutls_srtp_profile_t * profile)130 int gnutls_srtp_get_profile_id(const char *name,
131 			       gnutls_srtp_profile_t * profile)
132 {
133 	*profile = find_profile(name, NULL);
134 	if (*profile == 0) {
135 		return GNUTLS_E_ILLEGAL_PARAMETER;
136 	}
137 	return 0;
138 }
139 
140 #define MAX_PROFILES_IN_SRTP_EXTENSION 256
141 
142 /**
143  * gnutls_srtp_get_profile_name
144  * @profile: The profile to look up a string for
145  *
146  * This function allows you to get the corresponding name for a
147  * SRTP protection profile.
148  *
149  * Returns: On success, the name of a SRTP profile as a string,
150  *   otherwise NULL.
151  *
152  * Since 3.1.4
153  **/
gnutls_srtp_get_profile_name(gnutls_srtp_profile_t profile)154 const char *gnutls_srtp_get_profile_name(gnutls_srtp_profile_t profile)
155 {
156 	const srtp_profile_st *p = get_profile(profile);
157 
158 	if (p != NULL)
159 		return p->name;
160 
161 	return NULL;
162 }
163 
164 static int
_gnutls_srtp_recv_params(gnutls_session_t session,const uint8_t * data,size_t data_size)165 _gnutls_srtp_recv_params(gnutls_session_t session,
166 			 const uint8_t * data, size_t data_size)
167 {
168 	unsigned int i;
169 	int ret;
170 	const uint8_t *p = data;
171 	size_t len;
172 	srtp_ext_st *priv;
173 	gnutls_ext_priv_data_t epriv;
174 	uint16_t profile;
175 
176 	ret =
177 	    _gnutls_hello_ext_get_priv(session, GNUTLS_EXTENSION_SRTP,
178 					 &epriv);
179 	if (ret < 0)
180 		return 0;
181 
182 	priv = epriv;
183 
184 	DECR_LENGTH_RET(data_size, 2, 0);
185 	len = _gnutls_read_uint16(p);
186 	p += 2;
187 
188 	if (len + 1 > data_size)
189 		return
190 		    gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET_LENGTH);
191 
192 	if (session->security_parameters.entity == GNUTLS_SERVER) {
193 		if (len > MAX_PROFILES_IN_SRTP_EXTENSION * 2)
194 			return 0;
195 	} else {
196 		if (len != 2)
197 			return
198 			    gnutls_assert_val
199 			    (GNUTLS_E_UNEXPECTED_PACKET_LENGTH);
200 	}
201 
202 	priv->selected_profile = 0;
203 
204 	while (len > 0) {
205 		DECR_LEN(data_size, 2);
206 		profile = _gnutls_read_uint16(p);
207 
208 		for (i = 0;
209 		     i < priv->profiles_size
210 		     && priv->selected_profile == 0; i++) {
211 			if (priv->profiles[i] == profile) {
212 				priv->selected_profile = profile;
213 				break;
214 			}
215 		}
216 		p += 2;
217 		len -= 2;
218 	}
219 
220 	DECR_LEN(data_size, 1);
221 	priv->mki_size = *p;
222 	p++;
223 
224 	if (priv->mki_size > 0) {
225 		DECR_LEN(data_size, priv->mki_size);
226 		memcpy(priv->mki, p, priv->mki_size);
227 		priv->mki_received = 1;
228 	}
229 
230 	return 0;
231 }
232 
233 static int
_gnutls_srtp_send_params(gnutls_session_t session,gnutls_buffer_st * extdata)234 _gnutls_srtp_send_params(gnutls_session_t session,
235 			 gnutls_buffer_st * extdata)
236 {
237 	unsigned i;
238 	int total_size = 0, ret;
239 	srtp_ext_st *priv;
240 	gnutls_ext_priv_data_t epriv;
241 
242 	ret =
243 	    _gnutls_hello_ext_get_priv(session, GNUTLS_EXTENSION_SRTP,
244 					 &epriv);
245 	if (ret < 0)
246 		return 0;
247 
248 	priv = epriv;
249 
250 	if (priv->profiles_size == 0)
251 		return 0;
252 
253 	if (session->security_parameters.entity == GNUTLS_SERVER) {
254 		/* Don't send anything if no matching profile was found */
255 		if (priv->selected_profile == 0)
256 			return 0;
257 
258 		ret = _gnutls_buffer_append_prefix(extdata, 16, 2);
259 		if (ret < 0)
260 			return gnutls_assert_val(ret);
261 		ret =
262 		    _gnutls_buffer_append_prefix(extdata, 16,
263 						 priv->selected_profile);
264 		if (ret < 0)
265 			return gnutls_assert_val(ret);
266 		total_size = 4;
267 	} else {
268 		ret =
269 		    _gnutls_buffer_append_prefix(extdata, 16,
270 						 2 * priv->profiles_size);
271 		if (ret < 0)
272 			return gnutls_assert_val(ret);
273 
274 		for (i = 0; i < priv->profiles_size; i++) {
275 			ret =
276 			    _gnutls_buffer_append_prefix(extdata, 16,
277 							 priv->
278 							 profiles[i]);
279 			if (ret < 0)
280 				return gnutls_assert_val(ret);
281 		}
282 		total_size = 2 + 2 * priv->profiles_size;
283 	}
284 
285 	/* use_mki */
286 	ret =
287 	    _gnutls_buffer_append_data_prefix(extdata, 8, priv->mki,
288 					      priv->mki_size);
289 	if (ret < 0)
290 		return gnutls_assert_val(ret);
291 	total_size += 1 + priv->mki_size;
292 
293 	return total_size;
294 }
295 
296 /**
297  * gnutls_srtp_get_selected_profile:
298  * @session: is a #gnutls_session_t type.
299  * @profile: will hold the profile
300  *
301  * This function allows you to get the negotiated SRTP profile.
302  *
303  * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned,
304  *   otherwise a negative error code is returned.
305  *
306  * Since 3.1.4
307  **/
308 int
gnutls_srtp_get_selected_profile(gnutls_session_t session,gnutls_srtp_profile_t * profile)309 gnutls_srtp_get_selected_profile(gnutls_session_t session,
310 				 gnutls_srtp_profile_t * profile)
311 {
312 	srtp_ext_st *priv;
313 	int ret;
314 	gnutls_ext_priv_data_t epriv;
315 
316 	ret =
317 	    _gnutls_hello_ext_get_priv(session, GNUTLS_EXTENSION_SRTP,
318 					 &epriv);
319 	if (ret < 0) {
320 		gnutls_assert();
321 		return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE;
322 	}
323 
324 	priv = epriv;
325 
326 	if (priv->selected_profile == 0) {
327 		return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE;
328 	}
329 
330 	*profile = priv->selected_profile;
331 
332 	return 0;
333 }
334 
335 /**
336  * gnutls_srtp_get_mki:
337  * @session: is a #gnutls_session_t type.
338  * @mki: will hold the MKI
339  *
340  * This function exports the negotiated Master Key Identifier,
341  * received by the peer if any. The returned value in @mki should be
342  * treated as constant and valid only during the session's lifetime.
343  *
344  * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned,
345  *   otherwise a negative error code is returned.
346  *
347  * Since 3.1.4
348  **/
gnutls_srtp_get_mki(gnutls_session_t session,gnutls_datum_t * mki)349 int gnutls_srtp_get_mki(gnutls_session_t session, gnutls_datum_t * mki)
350 {
351 	srtp_ext_st *priv;
352 	int ret;
353 	gnutls_ext_priv_data_t epriv;
354 
355 	ret =
356 	    _gnutls_hello_ext_get_priv(session, GNUTLS_EXTENSION_SRTP,
357 					 &epriv);
358 	if (ret < 0)
359 		return
360 		    gnutls_assert_val
361 		    (GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE);
362 
363 	priv = epriv;
364 
365 	if (priv->mki_received == 0)
366 		return
367 		    gnutls_assert_val
368 		    (GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE);
369 
370 	mki->data = priv->mki;
371 	mki->size = priv->mki_size;
372 
373 	return 0;
374 }
375 
376 /**
377  * gnutls_srtp_set_mki:
378  * @session: is a #gnutls_session_t type.
379  * @mki: holds the MKI
380  *
381  * This function sets the Master Key Identifier, to be
382  * used by this session (if any).
383  *
384  * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned,
385  *   otherwise a negative error code is returned.
386  *
387  * Since 3.1.4
388  **/
389 int
gnutls_srtp_set_mki(gnutls_session_t session,const gnutls_datum_t * mki)390 gnutls_srtp_set_mki(gnutls_session_t session, const gnutls_datum_t * mki)
391 {
392 	int ret;
393 	srtp_ext_st *priv;
394 	gnutls_ext_priv_data_t epriv;
395 
396 	ret =
397 	    _gnutls_hello_ext_get_priv(session, GNUTLS_EXTENSION_SRTP,
398 					 &epriv);
399 	if (ret < 0) {
400 		priv = gnutls_calloc(1, sizeof(*priv));
401 		if (priv == NULL) {
402 			gnutls_assert();
403 			return GNUTLS_E_MEMORY_ERROR;
404 		}
405 		epriv = priv;
406 		_gnutls_hello_ext_set_priv(session,
407 					     GNUTLS_EXTENSION_SRTP, epriv);
408 	} else
409 		priv = epriv;
410 
411 	if (mki->size > 0 && mki->size <= sizeof(priv->mki)) {
412 		priv->mki_size = mki->size;
413 		memcpy(priv->mki, mki->data, mki->size);
414 	} else
415 		return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
416 
417 	return 0;
418 }
419 
420 /**
421  * gnutls_srtp_set_profile:
422  * @session: is a #gnutls_session_t type.
423  * @profile: is the profile id to add.
424  *
425  * This function is to be used by both clients and servers, to declare
426  * what SRTP profiles they support, to negotiate with the peer.
427  *
428  * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned,
429  *   otherwise a negative error code is returned.
430  *
431  * Since 3.1.4
432  **/
433 int
gnutls_srtp_set_profile(gnutls_session_t session,gnutls_srtp_profile_t profile)434 gnutls_srtp_set_profile(gnutls_session_t session,
435 			gnutls_srtp_profile_t profile)
436 {
437 	int ret;
438 	srtp_ext_st *priv;
439 	gnutls_ext_priv_data_t epriv;
440 
441 	ret =
442 	    _gnutls_hello_ext_get_priv(session, GNUTLS_EXTENSION_SRTP,
443 					 &epriv);
444 	if (ret < 0) {
445 		priv = gnutls_calloc(1, sizeof(*priv));
446 		if (priv == NULL) {
447 			gnutls_assert();
448 			return GNUTLS_E_MEMORY_ERROR;
449 		}
450 		epriv = priv;
451 		_gnutls_hello_ext_set_priv(session,
452 					     GNUTLS_EXTENSION_SRTP, epriv);
453 	} else
454 		priv = epriv;
455 
456 	if (priv->profiles_size < MAX_SRTP_PROFILES)
457 		priv->profiles_size++;
458 	priv->profiles[priv->profiles_size - 1] = profile;
459 
460 	return 0;
461 }
462 
463 /**
464  * gnutls_srtp_set_profile_direct:
465  * @session: is a #gnutls_session_t type.
466  * @profiles: is a string that contains the supported SRTP profiles,
467  *   separated by colons.
468  * @err_pos: In case of an error this will have the position in the string the error occurred, may be NULL.
469  *
470  * This function is to be used by both clients and servers, to declare
471  * what SRTP profiles they support, to negotiate with the peer.
472  *
473  * Returns: On syntax error %GNUTLS_E_INVALID_REQUEST is returned,
474  * %GNUTLS_E_SUCCESS on success, or an error code.
475  *
476  * Since 3.1.4
477  **/
478 int
gnutls_srtp_set_profile_direct(gnutls_session_t session,const char * profiles,const char ** err_pos)479 gnutls_srtp_set_profile_direct(gnutls_session_t session,
480 			       const char *profiles, const char **err_pos)
481 {
482 	int ret;
483 	srtp_ext_st *priv;
484 	gnutls_ext_priv_data_t epriv;
485 	int set = 0;
486 	const char *col;
487 	gnutls_srtp_profile_t id;
488 
489 	ret =
490 	    _gnutls_hello_ext_get_priv(session, GNUTLS_EXTENSION_SRTP,
491 					 &epriv);
492 	if (ret < 0) {
493 		set = 1;
494 		priv = gnutls_calloc(1, sizeof(*priv));
495 		if (priv == NULL) {
496 			if (err_pos != NULL)
497 				*err_pos = profiles;
498 			gnutls_assert();
499 			return GNUTLS_E_MEMORY_ERROR;
500 		}
501 		epriv = priv;
502 	} else
503 		priv = epriv;
504 
505 	do {
506 		col = strchr(profiles, ':');
507 		id = find_profile(profiles, col);
508 		if (id == 0) {
509 			if (set != 0)
510 				gnutls_free(priv);
511 			if (err_pos != NULL)
512 				*err_pos = profiles;
513 			return GNUTLS_E_INVALID_REQUEST;
514 		}
515 
516 		if (priv->profiles_size < MAX_SRTP_PROFILES) {
517 			priv->profiles_size++;
518 		}
519 		priv->profiles[priv->profiles_size - 1] = id;
520 		profiles = col + 1;
521 	} while (col != NULL);
522 
523 	if (set != 0)
524 		_gnutls_hello_ext_set_priv(session,
525 					     GNUTLS_EXTENSION_SRTP, epriv);
526 
527 	return 0;
528 }
529 
530 /**
531  * gnutls_srtp_get_keys:
532  * @session: is a #gnutls_session_t type.
533  * @key_material: Space to hold the generated key material
534  * @key_material_size: The maximum size of the key material
535  * @client_key: The master client write key, pointing inside the key material
536  * @server_key: The master server write key, pointing inside the key material
537  * @client_salt: The master client write salt, pointing inside the key material
538  * @server_salt: The master server write salt, pointing inside the key material
539  *
540  * This is a helper function to generate the keying material for SRTP.
541  * It requires the space of the key material to be pre-allocated (should be at least
542  * 2x the maximum key size and salt size). The @client_key, @client_salt, @server_key
543  * and @server_salt are convenience datums that point inside the key material. They may
544  * be %NULL.
545  *
546  * Returns: On success the size of the key material is returned,
547  * otherwise, %GNUTLS_E_SHORT_MEMORY_BUFFER if the buffer given is not
548  * sufficient, or a negative error code.
549  *
550  * Since 3.1.4
551  **/
552 int
gnutls_srtp_get_keys(gnutls_session_t session,void * key_material,unsigned int key_material_size,gnutls_datum_t * client_key,gnutls_datum_t * client_salt,gnutls_datum_t * server_key,gnutls_datum_t * server_salt)553 gnutls_srtp_get_keys(gnutls_session_t session,
554 		     void *key_material,
555 		     unsigned int key_material_size,
556 		     gnutls_datum_t * client_key,
557 		     gnutls_datum_t * client_salt,
558 		     gnutls_datum_t * server_key,
559 		     gnutls_datum_t * server_salt)
560 {
561 	int ret;
562 	const srtp_profile_st *p;
563 	gnutls_srtp_profile_t profile;
564 	unsigned int msize;
565 	uint8_t *km = key_material;
566 
567 	ret = gnutls_srtp_get_selected_profile(session, &profile);
568 	if (ret < 0)
569 		return gnutls_assert_val(ret);
570 
571 	p = get_profile(profile);
572 	if (p == NULL)
573 		return gnutls_assert_val(GNUTLS_E_UNKNOWN_ALGORITHM);
574 
575 	msize = 2 * (p->key_length + p->salt_length);
576 	if (msize > key_material_size)
577 		return gnutls_assert_val(GNUTLS_E_SHORT_MEMORY_BUFFER);
578 
579 	if (msize == 0)
580 		return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
581 
582 	ret =
583 	    gnutls_prf(session, sizeof("EXTRACTOR-dtls_srtp") - 1,
584 		       "EXTRACTOR-dtls_srtp", 0, 0, NULL, msize,
585 		       key_material);
586 	if (ret < 0)
587 		return gnutls_assert_val(ret);
588 
589 	if (client_key) {
590 		client_key->data = km;
591 		client_key->size = p->key_length;
592 	}
593 
594 	if (server_key) {
595 		server_key->data = km + p->key_length;
596 		server_key->size = p->key_length;
597 	}
598 
599 	if (client_salt) {
600 		client_salt->data = km + 2 * p->key_length;
601 		client_salt->size = p->salt_length;
602 	}
603 
604 	if (server_salt) {
605 		server_salt->data =
606 		    km + 2 * p->key_length + p->salt_length;
607 		server_salt->size = p->salt_length;
608 	}
609 
610 	return msize;
611 }
612 
_gnutls_srtp_deinit_data(gnutls_ext_priv_data_t priv)613 static void _gnutls_srtp_deinit_data(gnutls_ext_priv_data_t priv)
614 {
615 	gnutls_free(priv);
616 }
617 
618 static int
_gnutls_srtp_pack(gnutls_ext_priv_data_t epriv,gnutls_buffer_st * ps)619 _gnutls_srtp_pack(gnutls_ext_priv_data_t epriv, gnutls_buffer_st * ps)
620 {
621 	srtp_ext_st *priv = epriv;
622 	unsigned int i;
623 	int ret;
624 
625 	BUFFER_APPEND_NUM(ps, priv->profiles_size);
626 	for (i = 0; i < priv->profiles_size; i++) {
627 		BUFFER_APPEND_NUM(ps, priv->profiles[i]);
628 	}
629 
630 	BUFFER_APPEND_NUM(ps, priv->mki_received);
631 	if (priv->mki_received) {
632 		BUFFER_APPEND_NUM(ps, priv->selected_profile);
633 		BUFFER_APPEND_PFX4(ps, priv->mki, priv->mki_size);
634 	}
635 	return 0;
636 }
637 
638 static int
_gnutls_srtp_unpack(gnutls_buffer_st * ps,gnutls_ext_priv_data_t * _priv)639 _gnutls_srtp_unpack(gnutls_buffer_st * ps, gnutls_ext_priv_data_t * _priv)
640 {
641 	srtp_ext_st *priv;
642 	unsigned int i;
643 	int ret;
644 	gnutls_ext_priv_data_t epriv;
645 
646 	priv = gnutls_calloc(1, sizeof(*priv));
647 	if (priv == NULL) {
648 		gnutls_assert();
649 		return GNUTLS_E_MEMORY_ERROR;
650 	}
651 
652 	BUFFER_POP_NUM(ps, priv->profiles_size);
653 	for (i = 0; i < priv->profiles_size; i++) {
654 		BUFFER_POP_NUM(ps, priv->profiles[i]);
655 	}
656 	BUFFER_POP_NUM(ps, priv->selected_profile);
657 
658 	BUFFER_POP_NUM(ps, priv->mki_received);
659 	if (priv->mki_received) {
660 		BUFFER_POP_NUM(ps, priv->mki_size);
661 		BUFFER_POP(ps, priv->mki, priv->mki_size);
662 	}
663 
664 	epriv = priv;
665 	*_priv = epriv;
666 
667 	return 0;
668 
669       error:
670 	gnutls_free(priv);
671 	return ret;
672 }
673