Branch data Line data Source code
1 : : /* ccache.c --- Credential Cache compatibility ticket set handling.
2 : : * Copyright (C) 2002, 2003, 2004, 2006, 2007, 2008, 2009, 2010 Simon Josefsson
3 : : *
4 : : * This file is part of Shishi.
5 : : *
6 : : * Shishi is free software; you can redistribute it and/or modify it
7 : : * under the terms of the GNU General Public License as published by
8 : : * the Free Software Foundation; either version 3 of the License, or
9 : : * (at your option) any later version.
10 : : *
11 : : * Shishi is distributed in the hope that it will be useful, but
12 : : * WITHOUT ANY WARRANTY; without even the implied warranty of
13 : : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 : : * GNU General Public License for more details.
15 : : *
16 : : * You should have received a copy of the GNU General Public License
17 : : * along with Shishi; if not, see http://www.gnu.org/licenses or write
18 : : * to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
19 : : * Floor, Boston, MA 02110-1301, USA
20 : : *
21 : : */
22 : :
23 : : #include "internal.h"
24 : : #include "ccache.h"
25 : :
26 : : #include "utils.h"
27 : :
28 : : /**
29 : : * shishi_tkts_default_ccache_guess:
30 : : * @handle: Shishi library handle create by shishi_init().
31 : : *
32 : : * Guesses the default ccache ticket filename; it is the contents of
33 : : * the environment variable KRB5CCNAME or /tmp/krb5cc_UID where UID is
34 : : * the user's identity in decimal, as returned by getuid().
35 : : *
36 : : * Return value: Returns default ccache filename as a string that has
37 : : * to be deallocated with free() by the caller.
38 : : **/
39 : : char *
40 : 0 : shishi_tkts_default_ccache_guess (Shishi * handle)
41 : : {
42 : : char *envfile;
43 : :
44 : 0 : envfile = getenv ("KRB5CCNAME");
45 [ # # ]: 0 : if (envfile)
46 : 0 : return xstrdup (envfile);
47 : :
48 : : #if HAVE_GETUID
49 : 0 : return xasprintf ("/tmp/krb5cc_%lu", (unsigned long) getuid ());
50 : : #else
51 : : return xasprintf ("/tmp/krb5cc_0");
52 : : #endif
53 : : }
54 : :
55 : : /**
56 : : * shishi_tkts_default_ccache:
57 : : * @handle: Shishi library handle create by shishi_init().
58 : : *
59 : : * Get filename of default ccache filename.
60 : : *
61 : : * Return value: Returns the default ccache filename used in the
62 : : * library. The string is not a copy, so don't modify or deallocate
63 : : * it.
64 : : **/
65 : : const char *
66 : 0 : shishi_tkts_default_ccache (Shishi * handle)
67 : : {
68 [ # # ]: 0 : if (!handle->ccachedefault)
69 : : {
70 : : char *p;
71 : :
72 : 0 : p = shishi_tkts_default_ccache_guess (handle);
73 : 0 : shishi_tkts_default_ccache_set (handle, p);
74 : 0 : free (p);
75 : : }
76 : :
77 : 0 : return handle->ccachedefault;
78 : : }
79 : :
80 : : /**
81 : : * shishi_tkts_default_ccache_set:
82 : : * @handle: Shishi library handle create by shishi_init().
83 : : * @ccache: string with new default ccache filename, or
84 : : * NULL to reset to default.
85 : : *
86 : : * Set the default ccache filename used in the library. The string is
87 : : * copied into the library, so you can dispose of the variable
88 : : * immediately after calling this function.
89 : : **/
90 : : void
91 : 0 : shishi_tkts_default_ccache_set (Shishi * handle, const char *ccache)
92 : : {
93 : 0 : free (handle->ccachedefault);
94 [ # # ]: 0 : if (ccache)
95 : 0 : handle->ccachedefault = xstrdup (ccache);
96 : : else
97 : 0 : handle->ccachedefault = NULL;
98 : 0 : }
99 : :
100 : : /**
101 : : * shishi_tkts_add_ccache_mem:
102 : : * @handle: shishi handle as allocated by shishi_init().
103 : : * @data: constant memory buffer with ccache of @len size.
104 : : * @len: size of memory buffer with ccache data.
105 : : * @tkts: allocated key set to store tickets in.
106 : : *
107 : : * Read tickets from a ccache data structure, and add them to the
108 : : * ticket set.
109 : : *
110 : : * The ccache format is proprietary, and this function support (at
111 : : * least) the 0x0504 format. See the section The Credential Cache
112 : : * Binary File Format in the Shishi manual for a description of the
113 : : * file format.
114 : : *
115 : : * Returns: Returns %SHISHI_CCACHE_ERROR if the data does not
116 : : * represent a valid ccache structure, and %SHISHI_OK on success.
117 : : **/
118 : : int
119 : 1 : shishi_tkts_add_ccache_mem (Shishi * handle,
120 : : const char *data, size_t len, Shishi_tkts * tkts)
121 : : {
122 : 1 : int rc = SHISHI_OK;
123 : : struct ccache ccache;
124 : :
125 [ - + ]: 1 : if (VERBOSENOISE (handle))
126 : : {
127 : 0 : printf ("ccache len %d (0x%x)\n", len, len);
128 : 0 : _shishi_hexprint (data, len);
129 : : }
130 : :
131 : 1 : rc = ccache_parse (data, len, &ccache);
132 [ - + ]: 1 : if (rc < 0)
133 : 0 : return SHISHI_CCACHE_ERROR;
134 : :
135 [ - + ]: 1 : if (VERBOSENOISE (handle))
136 : 0 : ccache_print (&ccache);
137 : :
138 [ + + ]: 3 : while (ccache.credentialslen)
139 : : {
140 : : struct ccache_credential cred;
141 : : Shishi_tkt *tkt;
142 : : Shishi_asn1 ticket;
143 : : size_t n;
144 : :
145 : 2 : rc = ccache_parse_credential (ccache.credentials,
146 : : ccache.credentialslen, &cred, &n);
147 [ - + ]: 2 : if (rc < 0)
148 : 0 : return SHISHI_CCACHE_ERROR;
149 : :
150 [ - + ]: 2 : if (VERBOSENOISE (handle))
151 : 0 : ccache_print_credential (&cred);
152 : :
153 : : /* Sanity check credential first. */
154 : :
155 [ - + ]: 2 : if (shishi_cipher_keylen (cred.key.keytype) != cred.key.keylen)
156 : 0 : continue;
157 : :
158 : 2 : ticket = shishi_der2asn1_ticket (handle, cred.ticket.data,
159 : : cred.ticket.length);
160 [ - + ]: 2 : if (!ticket)
161 : 0 : continue;
162 : :
163 : : /* Let's create a new ticket... */
164 : :
165 : 2 : rc = shishi_tkt (handle, &tkt);
166 [ - + ]: 2 : if (rc != SHISHI_OK)
167 : 0 : return rc;
168 : :
169 : 2 : shishi_tkt_ticket_set (tkt, ticket);
170 : :
171 : : {
172 : : const char *cname[CCACHE_MAX_COMPONENTS + 1];
173 : : size_t i;
174 : :
175 [ + + ][ + - ]: 4 : for (i = 0; i < cred.client.num_components
176 : 2 : && i < CCACHE_MAX_COMPONENTS; i++)
177 : 2 : cname[i] = cred.client.components[i].data;
178 : 2 : cname[i] = NULL;
179 : :
180 : 2 : rc = shishi_kdcrep_crealm_set (handle,
181 : : shishi_tkt_kdcrep (tkt),
182 : 2 : cred.client.realm.data);
183 [ - + ]: 2 : if (rc != SHISHI_OK)
184 : 0 : return rc;
185 : :
186 : 2 : rc = shishi_kdcrep_cname_set (handle,
187 : : shishi_tkt_kdcrep (tkt),
188 : 2 : cred.client.name_type, cname);
189 [ - + ]: 2 : if (rc != SHISHI_OK)
190 : 0 : return rc;
191 : : }
192 : :
193 : : {
194 : : char *sname[CCACHE_MAX_COMPONENTS + 1];
195 : : size_t i;
196 : :
197 [ + + ][ + - ]: 6 : for (i = 0; i < cred.server.num_components
198 : 4 : && i < CCACHE_MAX_COMPONENTS; i++)
199 : 4 : sname[i] = cred.server.components[i].data;
200 : 2 : sname[i] = NULL;
201 : :
202 : 2 : rc = shishi_enckdcreppart_srealm_set (handle,
203 : : shishi_tkt_enckdcreppart (tkt),
204 : 2 : cred.server.realm.data);
205 [ - + ]: 2 : if (rc != SHISHI_OK)
206 : 0 : return rc;
207 : :
208 : 2 : rc = shishi_enckdcreppart_sname_set (handle,
209 : : shishi_tkt_enckdcreppart (tkt),
210 : 2 : cred.server.name_type, sname);
211 [ - + ]: 2 : if (rc != SHISHI_OK)
212 : 0 : return rc;
213 : : }
214 : :
215 : 2 : rc = shishi_tkt_flags_set (tkt, cred.tktflags);
216 [ - + ]: 2 : if (rc != SHISHI_OK)
217 : 0 : return rc;
218 : :
219 : 2 : rc = shishi_enckdcreppart_authtime_set
220 : : (handle,
221 : : shishi_tkt_enckdcreppart (tkt),
222 : 2 : shishi_generalize_time (handle, cred.authtime));
223 [ - + ]: 2 : if (rc != SHISHI_OK)
224 : 0 : return rc;
225 : :
226 [ + - ]: 4 : rc = shishi_enckdcreppart_starttime_set
227 : : (handle,
228 : : shishi_tkt_enckdcreppart (tkt),
229 : 2 : cred.starttime ? shishi_generalize_time (handle, cred.starttime)
230 : : : NULL);
231 [ - + ]: 2 : if (rc != SHISHI_OK)
232 : 0 : return rc;
233 : :
234 : 2 : rc = shishi_enckdcreppart_endtime_set
235 : : (handle,
236 : : shishi_tkt_enckdcreppart (tkt),
237 : 2 : shishi_generalize_time (handle, cred.endtime));
238 [ - + ]: 2 : if (rc != SHISHI_OK)
239 : 0 : return rc;
240 : :
241 [ - + ]: 2 : rc = shishi_enckdcreppart_renew_till_set
242 : : (handle,
243 : : shishi_tkt_enckdcreppart (tkt),
244 : 0 : cred.renew_till ? shishi_generalize_time (handle, cred.renew_till)
245 : : : NULL);
246 [ - + ]: 2 : if (rc != SHISHI_OK)
247 : 0 : return rc;
248 : :
249 : : {
250 : 2 : uint32_t nonce = 0;
251 : 2 : rc = shishi_enckdcreppart_nonce_set (handle,
252 : : shishi_tkt_enckdcreppart (tkt),
253 : : nonce);
254 [ - + ]: 2 : if (rc != SHISHI_OK)
255 : 0 : return rc;
256 : : }
257 : :
258 : 2 : rc = shishi_kdcrep_set_ticket (handle, shishi_tkt_kdcrep (tkt),
259 : : shishi_tkt_ticket (tkt));
260 [ - + ]: 2 : if (rc != SHISHI_OK)
261 : 0 : return rc;
262 : :
263 : 2 : rc = shishi_kdcrep_set_enc_part (handle, shishi_tkt_kdcrep (tkt),
264 : : 0, 0, "", 0);
265 [ - + ]: 2 : if (rc != SHISHI_OK)
266 : 0 : return rc;
267 : :
268 : : /* Add key. */
269 : :
270 : : {
271 : : Shishi_key *key;
272 : :
273 : 2 : rc = shishi_key (handle, &key);
274 [ - + ]: 2 : if (rc != SHISHI_OK)
275 : 0 : return rc;
276 : :
277 : 2 : shishi_key_type_set (key, cred.key.keytype);
278 : 2 : shishi_key_value_set (key, cred.key.keyvalue);
279 : 2 : rc = shishi_tkt_key_set (tkt, key);
280 [ - + ]: 2 : if (rc != SHISHI_OK)
281 : 0 : return rc;
282 : :
283 : 2 : shishi_key_done (key);
284 : : }
285 : :
286 : : /* Add new ticket to the set... */
287 : :
288 : 2 : rc = shishi_tkts_add (tkts, tkt);
289 [ - + ]: 2 : if (rc != SHISHI_OK)
290 : 0 : return rc;
291 : :
292 : 2 : ccache.credentials += n;
293 : 2 : ccache.credentialslen -= n;
294 : : }
295 : :
296 : : #if 0
297 : : {
298 : : char *data;
299 : : size_t len;
300 : : rc = shishi_tkts_to_ccache_mem (handle, tkts, &data, &len);
301 : : printf ("gaah res %d\n", rc);
302 : : }
303 : : #endif
304 : :
305 : 1 : return rc;
306 : : }
307 : :
308 : : /**
309 : : * shishi_tkts_add_ccache_file:
310 : : * @handle: shishi handle as allocated by shishi_init().
311 : : * @filename: name of file to read.
312 : : * @tkts: allocated ticket set to store tickets in.
313 : : *
314 : : * Read tickets from a ccache data structure, and add them to the
315 : : * ticket set.
316 : : *
317 : : * The ccache format is proprietary, and this function support (at
318 : : * least) the 0x0504 format. See the section The Credential Cache
319 : : * Binary File Format in the Shishi manual for a description of the
320 : : * file format.
321 : : *
322 : : * Returns: Returns %SHISHI_IO_ERROR if the file cannot be read,
323 : : * %SHISHI_CCACHE_ERROR if the data cannot be parsed as a valid ccache
324 : : * structure, and %SHISHI_OK on success.
325 : : **/
326 : : int
327 : 1 : shishi_tkts_add_ccache_file (Shishi * handle,
328 : : const char *filename, Shishi_tkts * tkts)
329 : : {
330 : : size_t len;
331 : 1 : char *ccache = read_binary_file (filename, &len);
332 : : int rc;
333 : :
334 [ - + ]: 1 : if (!ccache)
335 : 0 : return SHISHI_IO_ERROR;
336 : :
337 : 1 : rc = shishi_tkts_add_ccache_mem (handle, ccache, len, tkts);
338 : :
339 : 1 : free (ccache);
340 : :
341 : 1 : return rc;
342 : : }
343 : :
344 : : /**
345 : : * shishi_tkts_from_ccache_mem:
346 : : * @handle: shishi handle as allocated by shishi_init().
347 : : * @data: constant memory buffer with ccache of @len size.
348 : : * @len: size of memory buffer with ccache data.
349 : : * @outtkts: pointer to ticket set that will be allocated and populated,
350 : : * must be deallocated by caller on succes.
351 : : *
352 : : * Read tickets from a ccache data structure, and add them to the
353 : : * ticket set.
354 : : *
355 : : * The ccache format is proprietary, and this function support (at
356 : : * least) the 0x0504 format. See the section The Credential Cache
357 : : * Binary File Format in the Shishi manual for a description of the
358 : : * file format.
359 : : *
360 : : * Returns: Returns %SHISHI_CCACHE_ERROR if the data does not
361 : : * represent a valid ccache structure, and %SHISHI_OK on success.
362 : : **/
363 : : int
364 : 0 : shishi_tkts_from_ccache_mem (Shishi * handle,
365 : : const char *data, size_t len,
366 : : Shishi_tkts ** outtkts)
367 : : {
368 : : int rc;
369 : :
370 : 0 : rc = shishi_tkts (handle, outtkts);
371 [ # # ]: 0 : if (rc != SHISHI_OK)
372 : 0 : return rc;
373 : :
374 : 0 : rc = shishi_tkts_add_ccache_mem (handle, data, len, *outtkts);
375 [ # # ]: 0 : if (rc != SHISHI_OK)
376 : : {
377 : 0 : shishi_tkts_done (outtkts);
378 : 0 : return rc;
379 : : }
380 : :
381 : 0 : return SHISHI_OK;
382 : : }
383 : :
384 : : /**
385 : : * shishi_tkts_from_ccache_file:
386 : : * @handle: shishi handle as allocated by shishi_init().
387 : : * @filename: name of file to read.
388 : : * @outtkts: pointer to ticket set that will be allocated and populated,
389 : : * must be deallocated by caller on succes.
390 : : *
391 : : * Read tickets from a ccache data structure, and add them to the
392 : : * ticket set.
393 : : *
394 : : * The ccache format is proprietary, and this function support (at
395 : : * least) the 0x0504 format. See the section The Credential Cache
396 : : * Binary File Format in the Shishi manual for a description of the
397 : : * file format.
398 : : *
399 : : * Returns: Returns %SHISHI_IO_ERROR if the file cannot be read,
400 : : * %SHISHI_CCACHE_ERROR if the data cannot be parsed as a valid ccache
401 : : * structure, and %SHISHI_OK on success.
402 : : **/
403 : : int
404 : 1 : shishi_tkts_from_ccache_file (Shishi * handle,
405 : : const char *filename, Shishi_tkts ** outtkts)
406 : : {
407 : : int rc;
408 : :
409 : 1 : rc = shishi_tkts (handle, outtkts);
410 [ - + ]: 1 : if (rc != SHISHI_OK)
411 : 0 : return rc;
412 : :
413 : 1 : rc = shishi_tkts_add_ccache_file (handle, filename, *outtkts);
414 [ - + ]: 1 : if (rc != SHISHI_OK)
415 : : {
416 : 0 : shishi_tkts_done (outtkts);
417 : 0 : return rc;
418 : : }
419 : :
420 : 1 : return SHISHI_OK;
421 : : }
422 : :
423 : : extern int
424 : : shishi_tkt_to_ccache_mem (Shishi * handle, Shishi_tkt * tkt,
425 : : char **data, size_t * len);
426 : :
427 : : int
428 : 0 : shishi_tkt_to_ccache_mem (Shishi * handle,
429 : : Shishi_tkt * tkt, char **data, size_t * len)
430 : : {
431 : : #if 0
432 : : struct ccache_credential cred;
433 : : char tmp[1024];
434 : : size_t i;
435 : : int rc;
436 : :
437 : : memset (&cred, 0, sizeof (cred));
438 : :
439 : : rc = shishi_asn1_to_der (handle, shishi_tkt_ticket (tkt),
440 : : &cred.ticket.data, &cred.ticket.length);
441 : : if (rc != SHISHI_OK)
442 : : return rc;
443 : :
444 : : /* Sanity check credential first. */
445 : :
446 : : if (shishi_key_length (shishi_tkt_key (tkt)) > CCACHE_MAX_KEYLEN)
447 : : return SHISHI_CCACHE_ERROR;
448 : :
449 : : rc = shishi_asn1_read (handle, shishi_tkt_kdcrep (tkt), "crealm",
450 : : &cred.client.realm.data, &cred.client.realm.length);
451 : : if (rc != SHISHI_OK)
452 : : return rc;
453 : :
454 : : rc = shishi_asn1_read (handle, shishi_tkt_enckdcreppart (tkt), "srealm",
455 : : &cred.server.realm.data, &cred.server.realm.length);
456 : : if (rc != SHISHI_OK)
457 : : return rc;
458 : :
459 : : return SHISHI_OK;
460 : :
461 : : #if 0
462 : : {
463 : : char *cname[CCACHE_MAX_COMPONENTS + 1];
464 : : size_t i;
465 : :
466 : : for (i = 0; i < cred.client.num_components
467 : : && i < CCACHE_MAX_COMPONENTS; i++)
468 : : cname[i] = cred.client.components[i].data;
469 : : cname[i] = NULL;
470 : :
471 : : rc = shishi_kdcrep_crealm_set (handle,
472 : : shishi_tkt_kdcrep (tkt),
473 : : cred.client.realm.data);
474 : : if (rc != SHISHI_OK)
475 : : return rc;
476 : :
477 : : rc = shishi_kdcrep_cname_set (handle,
478 : : shishi_tkt_kdcrep (tkt),
479 : : cred.client.name_type, cname);
480 : : if (rc != SHISHI_OK)
481 : : return rc;
482 : : }
483 : :
484 : : {
485 : : char *sname[CCACHE_MAX_COMPONENTS + 1];
486 : : size_t i;
487 : :
488 : : for (i = 0; i < cred.server.num_components
489 : : && i < CCACHE_MAX_COMPONENTS; i++)
490 : : sname[i] = cred.server.components[i].data;
491 : : sname[i] = NULL;
492 : :
493 : : rc = shishi_enckdcreppart_srealm_set (handle,
494 : : shishi_tkt_enckdcreppart (tkt),
495 : : cred.server.realm.data);
496 : : if (rc != SHISHI_OK)
497 : : return rc;
498 : :
499 : : rc = shishi_enckdcreppart_sname_set (handle,
500 : : shishi_tkt_enckdcreppart (tkt),
501 : : cred.server.name_type, sname);
502 : : if (rc != SHISHI_OK)
503 : : return rc;
504 : : }
505 : : #endif
506 : :
507 : : rc = shishi_tkt_flags (tkt, &cred.tktflags);
508 : : if (rc != SHISHI_OK)
509 : : return rc;
510 : :
511 : : {
512 : : time_t t;
513 : : rc = shishi_ctime (handle, shishi_tkt_enckdcreppart (tkt),
514 : : "authtime", &t);
515 : : if (rc != SHISHI_OK)
516 : : return rc;
517 : : cred.authtime = t;
518 : : }
519 : :
520 : : {
521 : : time_t t;
522 : : rc = shishi_ctime (handle, shishi_tkt_enckdcreppart (tkt),
523 : : "starttime", &t);
524 : : if (rc == SHISHI_ASN1_NO_ELEMENT)
525 : : cred.starttime = 0;
526 : : else if (rc != SHISHI_OK)
527 : : return rc;
528 : : cred.starttime = t;
529 : : }
530 : :
531 : : {
532 : : time_t t;
533 : : rc = shishi_ctime (handle, shishi_tkt_enckdcreppart (tkt), "endtime", &t);
534 : : if (rc != SHISHI_OK)
535 : : return rc;
536 : : cred.endtime = t;
537 : : }
538 : :
539 : : {
540 : : time_t t;
541 : : rc = shishi_ctime (handle, shishi_tkt_enckdcreppart (tkt),
542 : : "renew-till", &t);
543 : : if (rc == SHISHI_ASN1_NO_ELEMENT)
544 : : cred.renew_till = 0;
545 : : else if (rc != SHISHI_OK)
546 : : return rc;
547 : : cred.renew_till = t;
548 : : }
549 : :
550 : : cred.key.keylen = shishi_key_length (shishi_tkt_key (tkt));
551 : : cred.key.keytype = shishi_key_type (shishi_tkt_key (tkt));
552 : : memcpy (cred.key.storage, shishi_key_value (shishi_tkt_key (tkt)),
553 : : shishi_key_length (shishi_tkt_key (tkt)));
554 : : cred.key.keyvalue = &cred.key.storage[0];
555 : :
556 : : i = 1024;
557 : : rc = ccache_pack_credential (&cred, tmp, &i);
558 : : printf ("rc %d len %d\n", rc, i);
559 : :
560 : : {
561 : : struct ccache_credential foo;
562 : : size_t n;
563 : :
564 : : rc = ccache_parse_credential (tmp, i, &foo, &n);
565 : : if (rc < 0)
566 : : return SHISHI_CCACHE_ERROR;
567 : :
568 : : printf ("packed:");
569 : : ccache_print_credential (&foo);
570 : : }
571 : : _shishi_escapeprint (tmp, i);
572 : : #endif
573 : :
574 : 0 : return SHISHI_CCACHE_ERROR;
575 : : }
576 : :
577 : : extern int
578 : : shishi_tkts_to_ccache_mem (Shishi * handle, Shishi_tkts * tkts,
579 : : char **data, size_t * len);
580 : :
581 : : int
582 : 0 : shishi_tkts_to_ccache_mem (Shishi * handle,
583 : : Shishi_tkts * tkts, char **data, size_t * len)
584 : : {
585 : 0 : return SHISHI_CCACHE_ERROR;
586 : :
587 : : #if 0
588 : : struct ccache info;
589 : : int rc = SHISHI_OK;
590 : : size_t i;
591 : :
592 : : for (i = 0; i < shishi_tkts_size (tkts); i++)
593 : : {
594 : : Shishi_tkt *tkt = shishi_tkts_nth (tkts, i);
595 : : struct ccache_credential cred;
596 : :
597 : : printf ("ccache %d\n", i);
598 : :
599 : : if (!tkt)
600 : : return SHISHI_INVALID_TKTS;
601 : :
602 : : rc = shishi_tkt_to_ccache_mem (handle, tkt, data, len);
603 : : printf ("f %d\n", rc);
604 : : }
605 : :
606 : : memset (&info, 0, sizeof (info));
607 : :
608 : : rc = ccache_pack (&info, *data, *len);
609 : : printf ("pack res %d len %d\n", rc, *len);
610 : :
611 : : return rc;
612 : : #endif
613 : : }
|