File: | lib/auth/rsa_export.c |
Location: | line 412, column 3 |
Description: | Value stored to 'i' is never read |
1 | /* |
2 | * Copyright (C) 2000-2012 Free Software Foundation, Inc. |
3 | * |
4 | * Author: Nikos Mavrogiannopoulos |
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 3 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 <http://www.gnu.org/licenses/> |
20 | * |
21 | */ |
22 | |
23 | /* This file contains the RSA key exchange part of the certificate |
24 | * authentication. |
25 | */ |
26 | |
27 | #include "gnutls_int.h" |
28 | #include "gnutls_auth.h" |
29 | #include "gnutls_errors.h" |
30 | #include "gnutls_dh.h" |
31 | #include "gnutls_num.h" |
32 | #include "gnutls_datum.h" |
33 | #include <auth/cert.h> |
34 | #include <gnutls_pk.h> |
35 | #include <algorithms.h> |
36 | #include <gnutls_global.h> |
37 | #include "debug.h" |
38 | #include <gnutls_sig.h> |
39 | #include <gnutls_x509.h> |
40 | #include <gnutls_rsa_export.h> |
41 | #include <gnutls_state.h> |
42 | #include <random.h> |
43 | #include <abstract_int.h> |
44 | |
45 | int _gnutls_gen_rsa_client_kx (gnutls_session_t, gnutls_buffer_st*); |
46 | static int gen_rsa_export_server_kx (gnutls_session_t, gnutls_buffer_st*); |
47 | static int proc_rsa_export_server_kx (gnutls_session_t, opaque *, size_t); |
48 | static int proc_rsa_export_client_kx (gnutls_session_t session, opaque * data, |
49 | size_t _data_size); |
50 | |
51 | const mod_auth_st rsa_export_auth_struct = { |
52 | "RSA EXPORT", |
53 | _gnutls_gen_cert_server_certificate, |
54 | _gnutls_gen_cert_client_certificate, |
55 | gen_rsa_export_server_kx, |
56 | _gnutls_gen_rsa_client_kx, |
57 | _gnutls_gen_cert_client_cert_vrfy, /* gen client cert vrfy */ |
58 | _gnutls_gen_cert_server_cert_req, /* server cert request */ |
59 | |
60 | _gnutls_proc_certificate, |
61 | _gnutls_proc_certificate, |
62 | proc_rsa_export_server_kx, |
63 | proc_rsa_export_client_kx, /* proc client kx */ |
64 | _gnutls_proc_cert_client_cert_vrfy, /* proc client cert vrfy */ |
65 | _gnutls_proc_cert_cert_req /* proc server cert request */ |
66 | }; |
67 | |
68 | /* This function reads the RSA parameters from the private key |
69 | */ |
70 | static int |
71 | _gnutls_get_private_rsa_params (gnutls_session_t session, |
72 | gnutls_pk_params_st** params) |
73 | { |
74 | int ret; |
75 | gnutls_certificate_credentials_t cred; |
76 | gnutls_rsa_params_t rsa_params; |
77 | |
78 | cred = (gnutls_certificate_credentials_t) |
79 | _gnutls_get_cred (session->key, GNUTLS_CRD_CERTIFICATE, NULL((void*)0)); |
80 | if (cred == NULL((void*)0)) |
81 | { |
82 | gnutls_assert ()do { if (__builtin_expect((_gnutls_log_level >= 2), 0)) _gnutls_log ( 2, "ASSERT: %s:%d\n", "rsa_export.c",82); } while(0);; |
83 | return GNUTLS_E_INSUFFICIENT_CREDENTIALS-32; |
84 | } |
85 | |
86 | if (session->internals.selected_cert_list == NULL((void*)0)) |
87 | { |
88 | gnutls_assert ()do { if (__builtin_expect((_gnutls_log_level >= 2), 0)) _gnutls_log ( 2, "ASSERT: %s:%d\n", "rsa_export.c",88); } while(0);; |
89 | return GNUTLS_E_INSUFFICIENT_CREDENTIALS-32; |
90 | } |
91 | |
92 | ret = _gnutls_pubkey_is_over_rsa_512(session->internals.selected_cert_list[0].pubkey); |
93 | |
94 | if (_gnutls_cipher_suite_get_kx_algo |
95 | (session->security_parameters.cipher_suite) |
96 | != GNUTLS_KX_RSA_EXPORT || ret < 0) |
97 | { |
98 | gnutls_assert ()do { if (__builtin_expect((_gnutls_log_level >= 2), 0)) _gnutls_log ( 2, "ASSERT: %s:%d\n", "rsa_export.c",98); } while(0);; |
99 | return GNUTLS_E_INVALID_REQUEST-50; |
100 | } |
101 | |
102 | rsa_params = |
103 | _gnutls_certificate_get_rsa_params (cred->rsa_params, |
104 | cred->params_func, session); |
105 | /* EXPORT case: */ |
106 | if (rsa_params == NULL((void*)0)) |
107 | { |
108 | gnutls_assert ()do { if (__builtin_expect((_gnutls_log_level >= 2), 0)) _gnutls_log ( 2, "ASSERT: %s:%d\n", "rsa_export.c",108); } while(0);; |
109 | return GNUTLS_E_NO_TEMPORARY_RSA_PARAMS-84; |
110 | } |
111 | |
112 | /* In the export case, we do use temporary RSA params |
113 | * of 512 bits size. The params in the certificate are |
114 | * used to sign this temporary stuff. |
115 | */ |
116 | *params = &rsa_params->params; |
117 | |
118 | return 0; |
119 | } |
120 | |
121 | int |
122 | proc_rsa_export_client_kx (gnutls_session_t session, opaque * data, |
123 | size_t _data_size) |
124 | { |
125 | gnutls_datum_t plaintext; |
126 | gnutls_datum_t ciphertext; |
127 | int ret, dsize; |
128 | gnutls_pk_params_st *params; |
129 | int randomize_key = 0; |
130 | ssize_t data_size = _data_size; |
131 | |
132 | if (gnutls_protocol_get_version_gnutls_protocol_get_version (session) == GNUTLS_SSL3) |
133 | { |
134 | /* SSL 3.0 |
135 | */ |
136 | ciphertext.data = data; |
137 | ciphertext.size = data_size; |
138 | } |
139 | else |
140 | { |
141 | /* TLS 1.0 |
142 | */ |
143 | DECR_LEN (data_size, 2)do { data_size-=2; if (data_size<0) {do { if (__builtin_expect ((_gnutls_log_level >= 2), 0)) _gnutls_log( 2, "ASSERT: %s:%d\n" , "rsa_export.c",143); } while(0);; return -9;} } while (0); |
144 | ciphertext.data = &data[2]; |
145 | dsize = _gnutls_read_uint16 (data); |
146 | |
147 | if (dsize != data_size) |
148 | { |
149 | gnutls_assert ()do { if (__builtin_expect((_gnutls_log_level >= 2), 0)) _gnutls_log ( 2, "ASSERT: %s:%d\n", "rsa_export.c",149); } while(0);; |
150 | return GNUTLS_E_UNEXPECTED_PACKET_LENGTH-9; |
151 | } |
152 | ciphertext.size = dsize; |
153 | } |
154 | |
155 | ret = _gnutls_get_private_rsa_params (session, ¶ms); |
156 | if (ret < 0) |
157 | { |
158 | gnutls_assert ()do { if (__builtin_expect((_gnutls_log_level >= 2), 0)) _gnutls_log ( 2, "ASSERT: %s:%d\n", "rsa_export.c",158); } while(0);; |
159 | return ret; |
160 | } |
161 | |
162 | ret = _gnutls_pkcs1_rsa_decrypt (&plaintext, &ciphertext, params, 2); /* btype==2 */ |
163 | |
164 | if (ret < 0 || plaintext.size != GNUTLS_MASTER_SIZE48) |
165 | { |
166 | /* In case decryption fails then don't inform |
167 | * the peer. Just use a random key. (in order to avoid |
168 | * attack against pkcs-1 formating). |
169 | */ |
170 | gnutls_assert ()do { if (__builtin_expect((_gnutls_log_level >= 2), 0)) _gnutls_log ( 2, "ASSERT: %s:%d\n", "rsa_export.c",170); } while(0);; |
171 | _gnutls_audit_log (session, "auth_rsa: Possible PKCS #1 format attack\n"); |
172 | randomize_key = 1; |
173 | } |
174 | else |
175 | { |
176 | /* If the secret was properly formatted, then |
177 | * check the version number. |
178 | */ |
179 | if (_gnutls_get_adv_version_major (session)session->internals.adv_version_major != plaintext.data[0] |
180 | || _gnutls_get_adv_version_minor (session)session->internals.adv_version_minor != plaintext.data[1]) |
181 | { |
182 | /* No error is returned here, if the version number check |
183 | * fails. We proceed normally. |
184 | * That is to defend against the attack described in the paper |
185 | * "Attacking RSA-based sessions in SSL/TLS" by Vlastimil Klima, |
186 | * Ondej Pokorny and Tomas Rosa. |
187 | */ |
188 | gnutls_assert ()do { if (__builtin_expect((_gnutls_log_level >= 2), 0)) _gnutls_log ( 2, "ASSERT: %s:%d\n", "rsa_export.c",188); } while(0);; |
189 | _gnutls_audit_log |
190 | (session, "auth_rsa: Possible PKCS #1 version check format attack\n"); |
191 | } |
192 | } |
193 | |
194 | if (randomize_key != 0) |
195 | { |
196 | session->key->key.size = GNUTLS_MASTER_SIZE48; |
197 | session->key->key.data = gnutls_malloc (session->key->key.size); |
198 | if (session->key->key.data == NULL((void*)0)) |
199 | { |
200 | gnutls_assert ()do { if (__builtin_expect((_gnutls_log_level >= 2), 0)) _gnutls_log ( 2, "ASSERT: %s:%d\n", "rsa_export.c",200); } while(0);; |
201 | return GNUTLS_E_MEMORY_ERROR-25; |
202 | } |
203 | |
204 | /* we do not need strong random numbers here. |
205 | */ |
206 | ret = _gnutls_rnd (GNUTLS_RND_NONCE, session->key->key.data, |
207 | session->key->key.size); |
208 | if (ret < 0) |
209 | { |
210 | gnutls_assert ()do { if (__builtin_expect((_gnutls_log_level >= 2), 0)) _gnutls_log ( 2, "ASSERT: %s:%d\n", "rsa_export.c",210); } while(0);; |
211 | return ret; |
212 | } |
213 | |
214 | } |
215 | else |
216 | { |
217 | session->key->key.data = plaintext.data; |
218 | session->key->key.size = plaintext.size; |
219 | } |
220 | |
221 | /* This is here to avoid the version check attack |
222 | * discussed above. |
223 | */ |
224 | session->key->key.data[0] = _gnutls_get_adv_version_major (session)session->internals.adv_version_major; |
225 | session->key->key.data[1] = _gnutls_get_adv_version_minor (session)session->internals.adv_version_minor; |
226 | |
227 | return 0; |
228 | } |
229 | |
230 | static int |
231 | gen_rsa_export_server_kx (gnutls_session_t session, gnutls_buffer_st* data) |
232 | { |
233 | gnutls_rsa_params_t rsa_params; |
234 | const gnutls_pk_params_st *rsa_mpis; |
235 | int ret = 0; |
236 | gnutls_pcert_st *apr_cert_list; |
237 | gnutls_privkey_t apr_pkey; |
238 | int apr_cert_list_length; |
239 | gnutls_datum_t signature, ddata; |
240 | gnutls_certificate_credentials_t cred; |
241 | gnutls_sign_algorithm_t sign_algo; |
242 | unsigned int bits = 0; |
243 | |
244 | cred = (gnutls_certificate_credentials_t) |
245 | _gnutls_get_cred (session->key, GNUTLS_CRD_CERTIFICATE, NULL((void*)0)); |
246 | if (cred == NULL((void*)0)) |
247 | { |
248 | gnutls_assert ()do { if (__builtin_expect((_gnutls_log_level >= 2), 0)) _gnutls_log ( 2, "ASSERT: %s:%d\n", "rsa_export.c",248); } while(0);; |
249 | return GNUTLS_E_INSUFFICIENT_CREDENTIALS-32; |
250 | } |
251 | |
252 | /* find the appropriate certificate */ |
253 | if ((ret = |
254 | _gnutls_get_selected_cert (session, &apr_cert_list, |
255 | &apr_cert_list_length, &apr_pkey)) < 0) |
256 | { |
257 | gnutls_assert ()do { if (__builtin_expect((_gnutls_log_level >= 2), 0)) _gnutls_log ( 2, "ASSERT: %s:%d\n", "rsa_export.c",257); } while(0);; |
258 | return ret; |
259 | } |
260 | |
261 | /* abort sending this message if we have a certificate |
262 | * of 512 bits or less. |
263 | */ |
264 | gnutls_privkey_get_pk_algorithm (apr_pkey, &bits); |
265 | if (apr_pkey && bits <= 512) |
266 | { |
267 | gnutls_assert ()do { if (__builtin_expect((_gnutls_log_level >= 2), 0)) _gnutls_log ( 2, "ASSERT: %s:%d\n", "rsa_export.c",267); } while(0);; |
268 | return GNUTLS_E_INT_RET_0-1251; |
269 | } |
270 | |
271 | rsa_params = |
272 | _gnutls_certificate_get_rsa_params (cred->rsa_params, cred->params_func, |
273 | session); |
274 | rsa_mpis = _gnutls_rsa_params_to_mpi (rsa_params); |
275 | if (rsa_mpis == NULL((void*)0)) |
276 | { |
277 | gnutls_assert ()do { if (__builtin_expect((_gnutls_log_level >= 2), 0)) _gnutls_log ( 2, "ASSERT: %s:%d\n", "rsa_export.c",277); } while(0);; |
278 | return GNUTLS_E_NO_TEMPORARY_RSA_PARAMS-84; |
279 | } |
280 | |
281 | if ((ret = _gnutls_auth_info_set (session, GNUTLS_CRD_CERTIFICATE, |
282 | sizeof (cert_auth_info_st), 0)) < 0) |
283 | { |
284 | gnutls_assert ()do { if (__builtin_expect((_gnutls_log_level >= 2), 0)) _gnutls_log ( 2, "ASSERT: %s:%d\n", "rsa_export.c",284); } while(0);; |
285 | return ret; |
286 | } |
287 | |
288 | _gnutls_rsa_export_set_pubkey (session, rsa_mpis->params[1], rsa_mpis->params[0]); |
289 | |
290 | ret = _gnutls_buffer_append_mpi( data, 16, rsa_mpis->params[0], 0); |
291 | if (ret < 0) |
292 | return gnutls_assert_val(ret)gnutls_assert_val_int(ret, "rsa_export.c", 292); |
293 | |
294 | ret = _gnutls_buffer_append_mpi( data, 16, rsa_mpis->params[1], 0); |
295 | if (ret < 0) |
296 | return gnutls_assert_val(ret)gnutls_assert_val_int(ret, "rsa_export.c", 296); |
297 | |
298 | /* Generate the signature. */ |
299 | |
300 | ddata.data = data->data; |
301 | ddata.size = data->length; |
302 | |
303 | if (apr_cert_list_length > 0) |
304 | { |
305 | if ((ret = |
306 | _gnutls_handshake_sign_data (session, &apr_cert_list[0], |
307 | apr_pkey, &ddata, &signature, |
308 | &sign_algo)) < 0) |
309 | { |
310 | gnutls_assert ()do { if (__builtin_expect((_gnutls_log_level >= 2), 0)) _gnutls_log ( 2, "ASSERT: %s:%d\n", "rsa_export.c",310); } while(0);; |
311 | return ret; |
312 | } |
313 | } |
314 | else |
315 | { |
316 | gnutls_assert ()do { if (__builtin_expect((_gnutls_log_level >= 2), 0)) _gnutls_log ( 2, "ASSERT: %s:%d\n", "rsa_export.c",316); } while(0);; |
317 | return data->length; /* do not put a signature - ILLEGAL! */ |
318 | } |
319 | |
320 | ret = _gnutls_buffer_append_data_prefix( data, 16, signature.data, signature.size); |
321 | _gnutls_free_datum (&signature)_gnutls_free_datum_m(&signature, gnutls_free); |
322 | |
323 | if (ret < 0) |
324 | return gnutls_assert_val(ret)gnutls_assert_val_int(ret, "rsa_export.c", 324); |
325 | |
326 | return data->length; |
327 | } |
328 | |
329 | /* if the peer's certificate is of 512 bits or less, returns non (0). |
330 | */ |
331 | int |
332 | _gnutls_peers_cert_less_512 (gnutls_session_t session) |
333 | { |
334 | gnutls_pcert_st peer_cert; |
335 | int ret; |
336 | cert_auth_info_t info = _gnutls_get_auth_info (session); |
337 | |
338 | if (info == NULL((void*)0) || info->ncerts == 0) |
339 | { |
340 | gnutls_assert ()do { if (__builtin_expect((_gnutls_log_level >= 2), 0)) _gnutls_log ( 2, "ASSERT: %s:%d\n", "rsa_export.c",340); } while(0);; |
341 | /* we need this in order to get peer's certificate */ |
342 | return 0; |
343 | } |
344 | |
345 | if ((ret = |
346 | _gnutls_get_auth_info_pcert (&peer_cert, |
347 | session->security_parameters.cert_type, |
348 | info)) < 0) |
349 | { |
350 | gnutls_assert ()do { if (__builtin_expect((_gnutls_log_level >= 2), 0)) _gnutls_log ( 2, "ASSERT: %s:%d\n", "rsa_export.c",350); } while(0);; |
351 | return 0; |
352 | } |
353 | |
354 | if (gnutls_pubkey_get_pk_algorithm(peer_cert.pubkey, NULL((void*)0)) != GNUTLS_PK_RSA) |
355 | { |
356 | gnutls_assert ()do { if (__builtin_expect((_gnutls_log_level >= 2), 0)) _gnutls_log ( 2, "ASSERT: %s:%d\n", "rsa_export.c",356); } while(0);; |
357 | gnutls_pcert_deinit (&peer_cert); |
358 | return 0; |
359 | } |
360 | |
361 | if (_gnutls_pubkey_is_over_rsa_512(peer_cert.pubkey) < 0) |
362 | { |
363 | gnutls_pcert_deinit (&peer_cert); |
364 | return 1; |
365 | } |
366 | |
367 | gnutls_pcert_deinit (&peer_cert); |
368 | |
369 | return 0; |
370 | } |
371 | |
372 | static int |
373 | proc_rsa_export_server_kx (gnutls_session_t session, |
374 | opaque * data, size_t _data_size) |
375 | { |
376 | uint16_t n_m, n_e; |
377 | size_t _n_m, _n_e; |
378 | uint8_t *data_m; |
379 | uint8_t *data_e; |
380 | int i, sigsize; |
381 | gnutls_datum_t vparams, signature; |
382 | int ret; |
383 | ssize_t data_size = _data_size; |
384 | cert_auth_info_t info; |
385 | gnutls_pcert_st peer_cert; |
386 | |
387 | info = _gnutls_get_auth_info (session); |
388 | if (info == NULL((void*)0) || info->ncerts == 0) |
389 | { |
390 | gnutls_assert ()do { if (__builtin_expect((_gnutls_log_level >= 2), 0)) _gnutls_log ( 2, "ASSERT: %s:%d\n", "rsa_export.c",390); } while(0);; |
391 | /* we need this in order to get peer's certificate */ |
392 | return GNUTLS_E_INTERNAL_ERROR-59; |
393 | } |
394 | |
395 | |
396 | i = 0; |
397 | |
398 | DECR_LEN (data_size, 2)do { data_size-=2; if (data_size<0) {do { if (__builtin_expect ((_gnutls_log_level >= 2), 0)) _gnutls_log( 2, "ASSERT: %s:%d\n" , "rsa_export.c",398); } while(0);; return -9;} } while (0); |
399 | n_m = _gnutls_read_uint16 (&data[i]); |
400 | i += 2; |
401 | |
402 | DECR_LEN (data_size, n_m)do { data_size-=n_m; if (data_size<0) {do { if (__builtin_expect ((_gnutls_log_level >= 2), 0)) _gnutls_log( 2, "ASSERT: %s:%d\n" , "rsa_export.c",402); } while(0);; return -9;} } while (0); |
403 | data_m = &data[i]; |
404 | i += n_m; |
405 | |
406 | DECR_LEN (data_size, 2)do { data_size-=2; if (data_size<0) {do { if (__builtin_expect ((_gnutls_log_level >= 2), 0)) _gnutls_log( 2, "ASSERT: %s:%d\n" , "rsa_export.c",406); } while(0);; return -9;} } while (0); |
407 | n_e = _gnutls_read_uint16 (&data[i]); |
408 | i += 2; |
409 | |
410 | DECR_LEN (data_size, n_e)do { data_size-=n_e; if (data_size<0) {do { if (__builtin_expect ((_gnutls_log_level >= 2), 0)) _gnutls_log( 2, "ASSERT: %s:%d\n" , "rsa_export.c",410); } while(0);; return -9;} } while (0); |
411 | data_e = &data[i]; |
412 | i += n_e; |
Value stored to 'i' is never read | |
413 | |
414 | _n_e = n_e; |
415 | _n_m = n_m; |
416 | |
417 | if (_gnutls_mpi_scan_nz (&session->key->rsa[0], data_m, _n_m) != 0) |
418 | { |
419 | gnutls_assert ()do { if (__builtin_expect((_gnutls_log_level >= 2), 0)) _gnutls_log ( 2, "ASSERT: %s:%d\n", "rsa_export.c",419); } while(0);; |
420 | return GNUTLS_E_MPI_SCAN_FAILED-23; |
421 | } |
422 | |
423 | if (_gnutls_mpi_scan_nz (&session->key->rsa[1], data_e, _n_e) != 0) |
424 | { |
425 | gnutls_assert ()do { if (__builtin_expect((_gnutls_log_level >= 2), 0)) _gnutls_log ( 2, "ASSERT: %s:%d\n", "rsa_export.c",425); } while(0);; |
426 | return GNUTLS_E_MPI_SCAN_FAILED-23; |
427 | } |
428 | |
429 | _gnutls_rsa_export_set_pubkey (session, session->key->rsa[1], |
430 | session->key->rsa[0]); |
431 | |
432 | /* VERIFY SIGNATURE */ |
433 | |
434 | vparams.size = n_m + n_e + 4; |
435 | vparams.data = data; |
436 | |
437 | DECR_LEN (data_size, 2)do { data_size-=2; if (data_size<0) {do { if (__builtin_expect ((_gnutls_log_level >= 2), 0)) _gnutls_log( 2, "ASSERT: %s:%d\n" , "rsa_export.c",437); } while(0);; return -9;} } while (0); |
438 | sigsize = _gnutls_read_uint16 (&data[vparams.size]); |
439 | |
440 | DECR_LEN (data_size, sigsize)do { data_size-=sigsize; if (data_size<0) {do { if (__builtin_expect ((_gnutls_log_level >= 2), 0)) _gnutls_log( 2, "ASSERT: %s:%d\n" , "rsa_export.c",440); } while(0);; return -9;} } while (0); |
441 | signature.data = &data[vparams.size + 2]; |
442 | signature.size = sigsize; |
443 | |
444 | if ((ret = |
445 | _gnutls_get_auth_info_pcert (&peer_cert, |
446 | session->security_parameters.cert_type, |
447 | info)) < 0) |
448 | { |
449 | gnutls_assert ()do { if (__builtin_expect((_gnutls_log_level >= 2), 0)) _gnutls_log ( 2, "ASSERT: %s:%d\n", "rsa_export.c",449); } while(0);; |
450 | return ret; |
451 | } |
452 | |
453 | ret = |
454 | _gnutls_handshake_verify_data (session, &peer_cert, &vparams, &signature, |
455 | GNUTLS_SIGN_UNKNOWN); |
456 | |
457 | gnutls_pcert_deinit (&peer_cert); |
458 | if (ret < 0) |
459 | { |
460 | gnutls_assert ()do { if (__builtin_expect((_gnutls_log_level >= 2), 0)) _gnutls_log ( 2, "ASSERT: %s:%d\n", "rsa_export.c",460); } while(0);; |
461 | } |
462 | |
463 | return ret; |
464 | } |