Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/crypto/krb5/src/ccapi/lib/ccapi_context.c
39536 views
1
/* ccapi/lib/ccapi_context.c */
2
/*
3
* Copyright 2006, 2007 Massachusetts Institute of Technology.
4
* All Rights Reserved.
5
*
6
* Export of this software from the United States of America may
7
* require a specific license from the United States Government.
8
* It is the responsibility of any person or organization contemplating
9
* export to obtain such a license before exporting.
10
*
11
* WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
12
* distribute this software and its documentation for any purpose and
13
* without fee is hereby granted, provided that the above copyright
14
* notice appear in all copies and that both that copyright notice and
15
* this permission notice appear in supporting documentation, and that
16
* the name of M.I.T. not be used in advertising or publicity pertaining
17
* to distribution of the software without specific, written prior
18
* permission. Furthermore if you modify this software you must label
19
* your software as modified software and not distribute it in such a
20
* fashion that it might be confused with the original M.I.T. software.
21
* M.I.T. makes no representations about the suitability of
22
* this software for any purpose. It is provided "as is" without express
23
* or implied warranty.
24
*/
25
26
#include "ccapi_context.h"
27
28
#include "k5-platform.h"
29
30
#include "ccapi_ccache.h"
31
#include "ccapi_ccache_iterator.h"
32
#include "ccapi_string.h"
33
#include "ccapi_ipc.h"
34
#include "ccapi_context_change_time.h"
35
#include "ccapi_err.h"
36
37
#include <CredentialsCache2.h>
38
39
typedef struct cci_context_d {
40
cc_context_f *functions;
41
#if TARGET_OS_MAC
42
cc_context_f *vector_functions;
43
#endif
44
cci_identifier_t identifier;
45
cc_uint32 synchronized;
46
cc_time_t last_wait_for_change_time;
47
} *cci_context_t;
48
49
/* ------------------------------------------------------------------------ */
50
51
struct cci_context_d cci_context_initializer = {
52
NULL
53
VECTOR_FUNCTIONS_INITIALIZER,
54
NULL,
55
0,
56
0
57
};
58
59
cc_context_f cci_context_f_initializer = {
60
ccapi_context_release,
61
ccapi_context_get_change_time,
62
ccapi_context_get_default_ccache_name,
63
ccapi_context_open_ccache,
64
ccapi_context_open_default_ccache,
65
ccapi_context_create_ccache,
66
ccapi_context_create_default_ccache,
67
ccapi_context_create_new_ccache,
68
ccapi_context_new_ccache_iterator,
69
ccapi_context_lock,
70
ccapi_context_unlock,
71
ccapi_context_compare,
72
ccapi_context_wait_for_change
73
};
74
75
static cc_int32 cci_context_sync (cci_context_t in_context,
76
cc_uint32 in_launch);
77
78
#ifdef TARGET_OS_MAC
79
#pragma mark -
80
#endif
81
82
MAKE_INIT_FUNCTION(cci_process_init);
83
MAKE_FINI_FUNCTION(cci_process_fini);
84
85
/* ------------------------------------------------------------------------ */
86
87
static int cci_process_init (void)
88
{
89
cc_int32 err = ccNoError;
90
91
if (!err) {
92
err = cci_context_change_time_thread_init ();
93
}
94
95
if (!err) {
96
err = cci_ipc_process_init ();
97
}
98
99
if (!err) {
100
add_error_table (&et_CAPI_error_table);
101
}
102
103
return err;
104
}
105
106
/* ------------------------------------------------------------------------ */
107
108
static void cci_process_fini (void)
109
{
110
if (!INITIALIZER_RAN (cci_process_init) || PROGRAM_EXITING ()) {
111
return;
112
}
113
114
remove_error_table(&et_CAPI_error_table);
115
cci_context_change_time_thread_fini ();
116
}
117
118
119
#ifdef TARGET_OS_MAC
120
#pragma mark -
121
#endif
122
123
/* ------------------------------------------------------------------------ */
124
125
cc_int32 cc_initialize (cc_context_t *out_context,
126
cc_int32 in_version,
127
cc_int32 *out_supported_version,
128
char const **out_vendor)
129
{
130
cc_int32 err = ccNoError;
131
cci_context_t context = NULL;
132
static char *vendor_string = "MIT Kerberos CCAPI";
133
134
if (!out_context) { err = cci_check_error (ccErrBadParam); }
135
136
if (!err) {
137
err = CALL_INIT_FUNCTION (cci_process_init);
138
}
139
140
if (!err) {
141
switch (in_version) {
142
case ccapi_version_2:
143
case ccapi_version_3:
144
case ccapi_version_4:
145
case ccapi_version_5:
146
case ccapi_version_6:
147
case ccapi_version_7:
148
break;
149
150
default:
151
err = ccErrBadAPIVersion;
152
break;
153
}
154
}
155
156
if (!err) {
157
context = malloc (sizeof (*context));
158
if (context) {
159
*context = cci_context_initializer;
160
} else {
161
err = cci_check_error (ccErrNoMem);
162
}
163
}
164
165
if (!err) {
166
context->functions = malloc (sizeof (*context->functions));
167
if (context->functions) {
168
*context->functions = cci_context_f_initializer;
169
} else {
170
err = cci_check_error (ccErrNoMem);
171
}
172
}
173
174
if (!err) {
175
context->identifier = cci_identifier_uninitialized;
176
177
*out_context = (cc_context_t) context;
178
context = NULL; /* take ownership */
179
180
if (out_supported_version) {
181
*out_supported_version = ccapi_version_max;
182
}
183
184
if (out_vendor) {
185
*out_vendor = vendor_string;
186
}
187
}
188
189
ccapi_context_release ((cc_context_t) context);
190
191
return cci_check_error (err);
192
}
193
194
#ifdef TARGET_OS_MAC
195
#pragma mark -
196
#endif
197
198
/* ------------------------------------------------------------------------ */
199
/*
200
* Currently does not need to talk to the server since the server must
201
* handle cleaning up resources from crashed clients anyway.
202
*
203
* NOTE: if server communication is ever added here, make sure that
204
* krb5_stdcc_shutdown calls an internal function which does not talk to the
205
* server. krb5_stdcc_shutdown is called from thread fini functions and may
206
* crash talking to the server depending on what order the OS calls the fini
207
* functions (ie: if the ipc layer fini function is called first).
208
*/
209
210
cc_int32 ccapi_context_release (cc_context_t in_context)
211
{
212
cc_int32 err = ccNoError;
213
cci_context_t context = (cci_context_t) in_context;
214
215
if (!in_context) { err = ccErrBadParam; }
216
217
if (!err) {
218
cci_identifier_release (context->identifier);
219
free (context->functions);
220
free (context);
221
}
222
223
return err;
224
}
225
226
/* ------------------------------------------------------------------------ */
227
228
cc_int32 ccapi_context_get_change_time (cc_context_t in_context,
229
cc_time_t *out_change_time)
230
{
231
cc_int32 err = ccNoError;
232
cci_context_t context = (cci_context_t) in_context;
233
k5_ipc_stream reply = NULL;
234
235
if (!in_context ) { err = cci_check_error (ccErrBadParam); }
236
if (!out_change_time) { err = cci_check_error (ccErrBadParam); }
237
238
if (!err) {
239
err = cci_context_sync (context, 0);
240
}
241
242
if (!err) {
243
err = cci_ipc_send_no_launch (cci_context_get_change_time_msg_id,
244
context->identifier,
245
NULL, &reply);
246
}
247
248
if (!err && krb5int_ipc_stream_size (reply) > 0) {
249
cc_time_t change_time = 0;
250
251
/* got a response from the server */
252
err = krb5int_ipc_stream_read_time (reply, &change_time);
253
254
if (!err) {
255
err = cci_context_change_time_update (context->identifier,
256
change_time);
257
}
258
}
259
260
if (!err) {
261
err = cci_context_change_time_get (out_change_time);
262
}
263
264
krb5int_ipc_stream_release (reply);
265
266
return cci_check_error (err);
267
}
268
269
/* ------------------------------------------------------------------------ */
270
271
cc_int32 ccapi_context_wait_for_change (cc_context_t in_context)
272
{
273
cc_int32 err = ccNoError;
274
cci_context_t context = (cci_context_t) in_context;
275
k5_ipc_stream request = NULL;
276
k5_ipc_stream reply = NULL;
277
278
if (!in_context) { err = cci_check_error (ccErrBadParam); }
279
280
if (!err) {
281
err = krb5int_ipc_stream_new (&request);
282
}
283
284
if (!err) {
285
err = krb5int_ipc_stream_write_time (request, context->last_wait_for_change_time);
286
}
287
288
if (!err) {
289
err = cci_context_sync (context, 1);
290
}
291
292
if (!err) {
293
err = cci_ipc_send (cci_context_wait_for_change_msg_id,
294
context->identifier,
295
request,
296
&reply);
297
}
298
299
if (!err) {
300
err = krb5int_ipc_stream_read_time (reply, &context->last_wait_for_change_time);
301
}
302
303
krb5int_ipc_stream_release (request);
304
krb5int_ipc_stream_release (reply);
305
306
return cci_check_error (err);
307
}
308
309
/* ------------------------------------------------------------------------ */
310
311
cc_int32 ccapi_context_get_default_ccache_name (cc_context_t in_context,
312
cc_string_t *out_name)
313
{
314
cc_int32 err = ccNoError;
315
cci_context_t context = (cci_context_t) in_context;
316
k5_ipc_stream reply = NULL;
317
char *reply_name = NULL;
318
char *name = NULL;
319
320
if (!in_context) { err = cci_check_error (ccErrBadParam); }
321
if (!out_name ) { err = cci_check_error (ccErrBadParam); }
322
323
if (!err) {
324
err = cci_context_sync (context, 0);
325
}
326
327
if (!err) {
328
err = cci_ipc_send_no_launch (cci_context_get_default_ccache_name_msg_id,
329
context->identifier,
330
NULL,
331
&reply);
332
}
333
334
if (!err) {
335
if (krb5int_ipc_stream_size (reply) > 0) {
336
/* got a response from the server */
337
err = krb5int_ipc_stream_read_string (reply, &reply_name);
338
339
if (!err) {
340
name = reply_name;
341
}
342
} else {
343
name = k_cci_context_initial_ccache_name;
344
}
345
}
346
347
if (!err) {
348
err = cci_string_new (out_name, name);
349
}
350
351
krb5int_ipc_stream_release (reply);
352
krb5int_ipc_stream_free_string (reply_name);
353
354
return cci_check_error (err);
355
}
356
357
/* ------------------------------------------------------------------------ */
358
359
cc_int32 ccapi_context_open_ccache (cc_context_t in_context,
360
const char *in_name,
361
cc_ccache_t *out_ccache)
362
{
363
cc_int32 err = ccNoError;
364
cci_context_t context = (cci_context_t) in_context;
365
k5_ipc_stream request = NULL;
366
k5_ipc_stream reply = NULL;
367
cci_identifier_t identifier = NULL;
368
369
if (!in_context ) { err = cci_check_error (ccErrBadParam); }
370
if (!in_name ) { err = cci_check_error (ccErrBadParam); }
371
if (!out_ccache ) { err = cci_check_error (ccErrBadParam); }
372
373
if (!err) {
374
err = krb5int_ipc_stream_new (&request);
375
}
376
377
if (!err) {
378
err = krb5int_ipc_stream_write_string (request, in_name);
379
}
380
381
if (!err) {
382
err = cci_context_sync (context, 0);
383
}
384
385
if (!err) {
386
err = cci_ipc_send_no_launch (cci_context_open_ccache_msg_id,
387
context->identifier,
388
request,
389
&reply);
390
}
391
392
if (!err && !(krb5int_ipc_stream_size (reply) > 0)) {
393
err = ccErrCCacheNotFound;
394
}
395
396
if (!err) {
397
err = cci_identifier_read (&identifier, reply);
398
}
399
400
if (!err) {
401
err = cci_ccache_new (out_ccache, identifier);
402
}
403
404
cci_identifier_release (identifier);
405
krb5int_ipc_stream_release (reply);
406
krb5int_ipc_stream_release (request);
407
408
return cci_check_error (err);
409
}
410
411
/* ------------------------------------------------------------------------ */
412
413
cc_int32 ccapi_context_open_default_ccache (cc_context_t in_context,
414
cc_ccache_t *out_ccache)
415
{
416
cc_int32 err = ccNoError;
417
cci_context_t context = (cci_context_t) in_context;
418
k5_ipc_stream reply = NULL;
419
cci_identifier_t identifier = NULL;
420
421
if (!in_context) { err = cci_check_error (ccErrBadParam); }
422
if (!out_ccache) { err = cci_check_error (ccErrBadParam); }
423
424
if (!err) {
425
err = cci_context_sync (context, 0);
426
}
427
428
if (!err) {
429
err = cci_ipc_send_no_launch (cci_context_open_default_ccache_msg_id,
430
context->identifier,
431
NULL,
432
&reply);
433
}
434
435
if (!err && !(krb5int_ipc_stream_size (reply) > 0)) {
436
err = ccErrCCacheNotFound;
437
}
438
439
if (!err) {
440
err = cci_identifier_read (&identifier, reply);
441
}
442
443
if (!err) {
444
err = cci_ccache_new (out_ccache, identifier);
445
}
446
447
cci_identifier_release (identifier);
448
krb5int_ipc_stream_release (reply);
449
450
return cci_check_error (err);
451
}
452
453
/* ------------------------------------------------------------------------ */
454
455
cc_int32 ccapi_context_create_ccache (cc_context_t in_context,
456
const char *in_name,
457
cc_uint32 in_cred_vers,
458
const char *in_principal,
459
cc_ccache_t *out_ccache)
460
{
461
cc_int32 err = ccNoError;
462
cci_context_t context = (cci_context_t) in_context;
463
k5_ipc_stream request = NULL;
464
k5_ipc_stream reply = NULL;
465
cci_identifier_t identifier = NULL;
466
467
if (!in_context ) { err = cci_check_error (ccErrBadParam); }
468
if (!in_name ) { err = cci_check_error (ccErrBadParam); }
469
if (!in_principal) { err = cci_check_error (ccErrBadParam); }
470
if (!out_ccache ) { err = cci_check_error (ccErrBadParam); }
471
472
if (!err) {
473
err = krb5int_ipc_stream_new (&request);
474
}
475
476
if (!err) {
477
err = krb5int_ipc_stream_write_string (request, in_name);
478
}
479
480
if (!err) {
481
err = krb5int_ipc_stream_write_uint32 (request, in_cred_vers);
482
}
483
484
if (!err) {
485
err = krb5int_ipc_stream_write_string (request, in_principal);
486
}
487
488
if (!err) {
489
err = cci_context_sync (context, 1);
490
}
491
492
if (!err) {
493
err = cci_ipc_send (cci_context_create_ccache_msg_id,
494
context->identifier,
495
request,
496
&reply);
497
}
498
499
if (!err) {
500
err = cci_identifier_read (&identifier, reply);
501
}
502
503
if (!err) {
504
err = cci_ccache_new (out_ccache, identifier);
505
}
506
507
cci_identifier_release (identifier);
508
krb5int_ipc_stream_release (reply);
509
krb5int_ipc_stream_release (request);
510
511
return cci_check_error (err);
512
}
513
514
/* ------------------------------------------------------------------------ */
515
516
cc_int32 ccapi_context_create_default_ccache (cc_context_t in_context,
517
cc_uint32 in_cred_vers,
518
const char *in_principal,
519
cc_ccache_t *out_ccache)
520
{
521
cc_int32 err = ccNoError;
522
cci_context_t context = (cci_context_t) in_context;
523
k5_ipc_stream request = NULL;
524
k5_ipc_stream reply = NULL;
525
cci_identifier_t identifier = NULL;
526
527
if (!in_context ) { err = cci_check_error (ccErrBadParam); }
528
if (!in_principal) { err = cci_check_error (ccErrBadParam); }
529
if (!out_ccache ) { err = cci_check_error (ccErrBadParam); }
530
531
if (!err) {
532
err = krb5int_ipc_stream_new (&request);
533
}
534
535
if (!err) {
536
err = krb5int_ipc_stream_write_uint32 (request, in_cred_vers);
537
}
538
539
if (!err) {
540
err = krb5int_ipc_stream_write_string (request, in_principal);
541
}
542
543
if (!err) {
544
err = cci_context_sync (context, 1);
545
}
546
547
if (!err) {
548
err = cci_ipc_send (cci_context_create_default_ccache_msg_id,
549
context->identifier,
550
request,
551
&reply);
552
}
553
554
if (!err) {
555
err = cci_identifier_read (&identifier, reply);
556
}
557
558
if (!err) {
559
err = cci_ccache_new (out_ccache, identifier);
560
}
561
562
cci_identifier_release (identifier);
563
krb5int_ipc_stream_release (reply);
564
krb5int_ipc_stream_release (request);
565
566
return cci_check_error (err);
567
}
568
569
/* ------------------------------------------------------------------------ */
570
571
cc_int32 ccapi_context_create_new_ccache (cc_context_t in_context,
572
cc_uint32 in_cred_vers,
573
const char *in_principal,
574
cc_ccache_t *out_ccache)
575
{
576
cc_int32 err = ccNoError;
577
cci_context_t context = (cci_context_t) in_context;
578
k5_ipc_stream request = NULL;
579
k5_ipc_stream reply = NULL;
580
cci_identifier_t identifier = NULL;
581
582
if (!in_context ) { err = cci_check_error (ccErrBadParam); }
583
if (!in_principal) { err = cci_check_error (ccErrBadParam); }
584
if (!out_ccache ) { err = cci_check_error (ccErrBadParam); }
585
586
if (!err) {
587
err = krb5int_ipc_stream_new (&request);
588
}
589
590
if (!err) {
591
err = krb5int_ipc_stream_write_uint32 (request, in_cred_vers);
592
}
593
594
if (!err) {
595
err = krb5int_ipc_stream_write_string (request, in_principal);
596
}
597
598
if (!err) {
599
err = cci_context_sync (context, 1);
600
}
601
602
if (!err) {
603
err = cci_ipc_send (cci_context_create_new_ccache_msg_id,
604
context->identifier,
605
request,
606
&reply);
607
}
608
609
if (!err) {
610
err = cci_identifier_read (&identifier, reply);
611
}
612
613
if (!err) {
614
err = cci_ccache_new (out_ccache, identifier);
615
}
616
617
cci_identifier_release (identifier);
618
krb5int_ipc_stream_release (reply);
619
krb5int_ipc_stream_release (request);
620
621
return cci_check_error (err);
622
}
623
624
/* ------------------------------------------------------------------------ */
625
626
cc_int32 ccapi_context_new_ccache_iterator (cc_context_t in_context,
627
cc_ccache_iterator_t *out_iterator)
628
{
629
cc_int32 err = ccNoError;
630
cci_context_t context = (cci_context_t) in_context;
631
k5_ipc_stream reply = NULL;
632
cci_identifier_t identifier = NULL;
633
634
if (!in_context ) { err = cci_check_error (ccErrBadParam); }
635
if (!out_iterator) { err = cci_check_error (ccErrBadParam); }
636
637
if (!err) {
638
err = cci_context_sync (context, 0);
639
}
640
641
if (!err) {
642
err = cci_ipc_send_no_launch (cci_context_new_ccache_iterator_msg_id,
643
context->identifier,
644
NULL,
645
&reply);
646
}
647
648
if (!err) {
649
if (krb5int_ipc_stream_size (reply) > 0) {
650
err = cci_identifier_read (&identifier, reply);
651
} else {
652
identifier = cci_identifier_uninitialized;
653
}
654
}
655
656
if (!err) {
657
err = cci_ccache_iterator_new (out_iterator, identifier);
658
}
659
660
krb5int_ipc_stream_release (reply);
661
cci_identifier_release (identifier);
662
663
return cci_check_error (err);
664
}
665
666
/* ------------------------------------------------------------------------ */
667
668
cc_int32 ccapi_context_lock (cc_context_t in_context,
669
cc_uint32 in_lock_type,
670
cc_uint32 in_block)
671
{
672
cc_int32 err = ccNoError;
673
cci_context_t context = (cci_context_t) in_context;
674
k5_ipc_stream request = NULL;
675
676
if (!in_context) { err = cci_check_error (ccErrBadParam); }
677
678
if (!err) {
679
err = krb5int_ipc_stream_new (&request);
680
}
681
682
if (!err) {
683
err = krb5int_ipc_stream_write_uint32 (request, in_lock_type);
684
}
685
686
if (!err) {
687
err = krb5int_ipc_stream_write_uint32 (request, in_block);
688
}
689
690
if (!err) {
691
err = cci_context_sync (context, 1);
692
}
693
694
if (!err) {
695
err = cci_ipc_send (cci_context_lock_msg_id,
696
context->identifier,
697
request,
698
NULL);
699
}
700
701
krb5int_ipc_stream_release (request);
702
703
return cci_check_error (err);
704
}
705
706
/* ------------------------------------------------------------------------ */
707
708
cc_int32 ccapi_context_unlock (cc_context_t in_context)
709
{
710
cc_int32 err = ccNoError;
711
cci_context_t context = (cci_context_t) in_context;
712
713
if (!in_context) { err = cci_check_error (ccErrBadParam); }
714
715
if (!err) {
716
err = cci_context_sync (context, 1);
717
}
718
719
if (!err) {
720
err = cci_ipc_send (cci_context_unlock_msg_id,
721
context->identifier,
722
NULL,
723
NULL);
724
}
725
726
return cci_check_error (err);
727
}
728
729
/* ------------------------------------------------------------------------ */
730
731
cc_int32 ccapi_context_compare (cc_context_t in_context,
732
cc_context_t in_compare_to_context,
733
cc_uint32 *out_equal)
734
{
735
cc_int32 err = ccNoError;
736
cci_context_t context = (cci_context_t) in_context;
737
cci_context_t compare_to_context = (cci_context_t) in_compare_to_context;
738
739
if (!in_context ) { err = cci_check_error (ccErrBadParam); }
740
if (!in_compare_to_context) { err = cci_check_error (ccErrBadParam); }
741
if (!out_equal ) { err = cci_check_error (ccErrBadParam); }
742
743
if (!err) {
744
err = cci_context_sync (context, 0);
745
}
746
747
if (!err) {
748
err = cci_context_sync (compare_to_context, 0);
749
}
750
751
if (!err) {
752
/* If both contexts can't talk to the server, then
753
* we assume they are equivalent */
754
err = cci_identifier_compare (context->identifier,
755
compare_to_context->identifier,
756
out_equal);
757
}
758
759
return cci_check_error (err);
760
}
761
762
#ifdef TARGET_OS_MAC
763
#pragma mark -
764
#endif
765
766
/* ------------------------------------------------------------------------ */
767
768
static cc_int32 cci_context_sync (cci_context_t in_context,
769
cc_uint32 in_launch)
770
{
771
cc_int32 err = ccNoError;
772
cci_context_t context = (cci_context_t) in_context;
773
k5_ipc_stream reply = NULL;
774
cci_identifier_t new_identifier = NULL;
775
776
if (!in_context) { err = cci_check_error (ccErrBadParam); }
777
778
if (!err) {
779
/* Use the uninitialized identifier because we may be talking */
780
/* to a different server which would reject our identifier and */
781
/* the point of this message is to sync with the server's id */
782
if (in_launch) {
783
err = cci_ipc_send (cci_context_sync_msg_id,
784
cci_identifier_uninitialized,
785
NULL,
786
&reply);
787
} else {
788
err = cci_ipc_send_no_launch (cci_context_sync_msg_id,
789
cci_identifier_uninitialized,
790
NULL,
791
&reply);
792
}
793
}
794
795
if (!err) {
796
if (krb5int_ipc_stream_size (reply) > 0) {
797
err = cci_identifier_read (&new_identifier, reply);
798
} else {
799
new_identifier = cci_identifier_uninitialized;
800
}
801
}
802
803
if (!err) {
804
cc_uint32 equal = 0;
805
806
err = cci_identifier_compare (context->identifier, new_identifier, &equal);
807
808
if (!err && !equal) {
809
if (context->identifier) {
810
cci_identifier_release (context->identifier);
811
}
812
context->identifier = new_identifier;
813
new_identifier = NULL; /* take ownership */
814
}
815
}
816
817
if (!err && context->synchronized) {
818
err = cci_context_change_time_sync (context->identifier);
819
}
820
821
if (!err && !context->synchronized) {
822
/* Keep state about whether this is the first call to avoid always */
823
/* modifying the global change time on the context's first ipc call. */
824
context->synchronized = 1;
825
}
826
827
cci_identifier_release (new_identifier);
828
krb5int_ipc_stream_release (reply);
829
830
return cci_check_error (err);
831
}
832
833