xref: /freebsd/crypto/heimdal/lib/krb5/scache.c (revision 39ee7a7a6bdd1557b1c3532abf60d139798ac88b)
1 /*
2  * Copyright (c) 2008 Kungliga Tekniska Högskolan
3  * (Royal Institute of Technology, Stockholm, Sweden).
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  *
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  *
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  *
17  * 3. Neither the name of the Institute nor the names of its contributors
18  *    may be used to endorse or promote products derived from this software
19  *    without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
22  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24  * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
25  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31  * SUCH DAMAGE.
32  */
33 
34 #include "krb5_locl.h"
35 
36 #ifdef HAVE_SCC
37 
38 #include <sqlite3.h>
39 
40 typedef struct krb5_scache {
41     char *name;
42     char *file;
43     sqlite3 *db;
44 
45     sqlite_uint64 cid;
46 
47     sqlite3_stmt *icred;
48     sqlite3_stmt *dcred;
49     sqlite3_stmt *iprincipal;
50 
51     sqlite3_stmt *icache;
52     sqlite3_stmt *ucachen;
53     sqlite3_stmt *ucachep;
54     sqlite3_stmt *dcache;
55     sqlite3_stmt *scache;
56     sqlite3_stmt *scache_name;
57     sqlite3_stmt *umaster;
58 
59 } krb5_scache;
60 
61 #define	SCACHE(X)	((krb5_scache *)(X)->data.data)
62 
63 #define SCACHE_DEF_NAME		"Default-cache"
64 #ifdef KRB5_USE_PATH_TOKENS
65 #define KRB5_SCACHE_DB	"%{TEMP}/krb5scc_%{uid}"
66 #else
67 #define KRB5_SCACHE_DB	"/tmp/krb5scc_%{uid}"
68 #endif
69 #define KRB5_SCACHE_NAME	"SCC:"  SCACHE_DEF_NAME ":" KRB5_SCACHE_DB
70 
71 #define SCACHE_INVALID_CID	((sqlite_uint64)-1)
72 
73 /*
74  *
75  */
76 
77 #define SQL_CMASTER ""				\
78 	"CREATE TABLE master ("			\
79         "oid INTEGER PRIMARY KEY,"		\
80 	"version INTEGER NOT NULL,"		\
81 	"defaultcache TEXT NOT NULL"		\
82 	")"
83 
84 #define SQL_SETUP_MASTER \
85 	"INSERT INTO master (version,defaultcache) VALUES(2, \"" SCACHE_DEF_NAME "\")"
86 #define SQL_UMASTER "UPDATE master SET defaultcache=? WHERE version=2"
87 
88 #define SQL_CCACHE ""				\
89 	"CREATE TABLE caches ("			\
90         "oid INTEGER PRIMARY KEY,"		\
91 	"principal TEXT,"			\
92 	"name TEXT NOT NULL"			\
93 	")"
94 
95 #define SQL_TCACHE ""						\
96 	"CREATE TRIGGER CacheDropCreds AFTER DELETE ON caches "	\
97 	"FOR EACH ROW BEGIN "					\
98 	"DELETE FROM credentials WHERE cid=old.oid;"		\
99 	"END"
100 
101 #define SQL_ICACHE "INSERT INTO caches (name) VALUES(?)"
102 #define SQL_UCACHE_NAME "UPDATE caches SET name=? WHERE OID=?"
103 #define SQL_UCACHE_PRINCIPAL "UPDATE caches SET principal=? WHERE OID=?"
104 #define SQL_DCACHE "DELETE FROM caches WHERE OID=?"
105 #define SQL_SCACHE "SELECT principal,name FROM caches WHERE OID=?"
106 #define SQL_SCACHE_NAME "SELECT oid FROM caches WHERE NAME=?"
107 
108 #define SQL_CCREDS ""				\
109 	"CREATE TABLE credentials ("		\
110         "oid INTEGER PRIMARY KEY,"		\
111 	"cid INTEGER NOT NULL,"			\
112 	"kvno INTEGER NOT NULL,"		\
113 	"etype INTEGER NOT NULL,"		\
114         "created_at INTEGER NOT NULL,"		\
115 	"cred BLOB NOT NULL"			\
116 	")"
117 
118 #define SQL_TCRED ""							\
119 	"CREATE TRIGGER credDropPrincipal AFTER DELETE ON credentials "	\
120 	"FOR EACH ROW BEGIN "						\
121 	"DELETE FROM principals WHERE credential_id=old.oid;"		\
122 	"END"
123 
124 #define SQL_ICRED "INSERT INTO credentials (cid, kvno, etype, cred, created_at) VALUES (?,?,?,?,?)"
125 #define SQL_DCRED "DELETE FROM credentials WHERE cid=?"
126 
127 #define SQL_CPRINCIPALS ""			\
128 	"CREATE TABLE principals ("		\
129         "oid INTEGER PRIMARY KEY,"		\
130 	"principal TEXT NOT NULL,"		\
131 	"type INTEGER NOT NULL,"		\
132 	"credential_id INTEGER NOT NULL"	\
133 	")"
134 
135 #define SQL_IPRINCIPAL "INSERT INTO principals (principal, type, credential_id) VALUES (?,?,?)"
136 
137 /*
138  * sqlite destructors
139  */
140 
141 static void
142 free_data(void *data)
143 {
144     free(data);
145 }
146 
147 static void
148 free_krb5(void *str)
149 {
150     krb5_xfree(str);
151 }
152 
153 static void
154 scc_free(krb5_scache *s)
155 {
156     if (s->file)
157 	free(s->file);
158     if (s->name)
159 	free(s->name);
160 
161     if (s->icred)
162 	sqlite3_finalize(s->icred);
163     if (s->dcred)
164 	sqlite3_finalize(s->dcred);
165     if (s->iprincipal)
166 	sqlite3_finalize(s->iprincipal);
167     if (s->icache)
168 	sqlite3_finalize(s->icache);
169     if (s->ucachen)
170 	sqlite3_finalize(s->ucachen);
171     if (s->ucachep)
172 	sqlite3_finalize(s->ucachep);
173     if (s->dcache)
174 	sqlite3_finalize(s->dcache);
175     if (s->scache)
176 	sqlite3_finalize(s->scache);
177     if (s->scache_name)
178 	sqlite3_finalize(s->scache_name);
179     if (s->umaster)
180 	sqlite3_finalize(s->umaster);
181 
182     if (s->db)
183 	sqlite3_close(s->db);
184     free(s);
185 }
186 
187 #ifdef TRACEME
188 static void
189 trace(void* ptr, const char * str)
190 {
191     printf("SQL: %s\n", str);
192 }
193 #endif
194 
195 static krb5_error_code
196 prepare_stmt(krb5_context context, sqlite3 *db,
197 	     sqlite3_stmt **stmt, const char *str)
198 {
199     int ret;
200 
201     ret = sqlite3_prepare_v2(db, str, -1, stmt, NULL);
202     if (ret != SQLITE_OK) {
203 	krb5_set_error_message(context, ENOENT,
204 			       N_("Failed to prepare stmt %s: %s", ""),
205 			       str, sqlite3_errmsg(db));
206 	return ENOENT;
207     }
208     return 0;
209 }
210 
211 static krb5_error_code
212 exec_stmt(krb5_context context, sqlite3 *db, const char *str,
213 	  krb5_error_code code)
214 {
215     int ret;
216 
217     ret = sqlite3_exec(db, str, NULL, NULL, NULL);
218     if (ret != SQLITE_OK && code) {
219 	krb5_set_error_message(context, code,
220 			       N_("scache execute %s: %s", ""), str,
221 			       sqlite3_errmsg(db));
222 	return code;
223     }
224     return 0;
225 }
226 
227 static krb5_error_code
228 default_db(krb5_context context, sqlite3 **db)
229 {
230     char *name;
231     int ret;
232 
233     ret = _krb5_expand_default_cc_name(context, KRB5_SCACHE_DB, &name);
234     if (ret)
235 	return ret;
236 
237     ret = sqlite3_open_v2(name, db, SQLITE_OPEN_READWRITE, NULL);
238     free(name);
239     if (ret != SQLITE_OK) {
240 	krb5_clear_error_message(context);
241 	return ENOENT;
242     }
243 
244 #ifdef TRACEME
245     sqlite3_trace(*db, trace, NULL);
246 #endif
247 
248     return 0;
249 }
250 
251 static krb5_error_code
252 get_def_name(krb5_context context, char **str)
253 {
254     krb5_error_code ret;
255     sqlite3_stmt *stmt;
256     const char *name;
257     sqlite3 *db;
258 
259     ret = default_db(context, &db);
260     if (ret)
261 	return ret;
262 
263     ret = prepare_stmt(context, db, &stmt, "SELECT defaultcache FROM master");
264     if (ret) {
265 	sqlite3_close(db);
266 	return ret;
267     }
268 
269     ret = sqlite3_step(stmt);
270     if (ret != SQLITE_ROW)
271 	goto out;
272 
273     if (sqlite3_column_type(stmt, 0) != SQLITE_TEXT)
274 	goto out;
275 
276     name = (const char *)sqlite3_column_text(stmt, 0);
277     if (name == NULL)
278 	goto out;
279 
280     *str = strdup(name);
281     if (*str == NULL)
282 	goto out;
283 
284     sqlite3_finalize(stmt);
285     sqlite3_close(db);
286     return 0;
287 out:
288     sqlite3_finalize(stmt);
289     sqlite3_close(db);
290     krb5_clear_error_message(context);
291     return ENOENT;
292 }
293 
294 
295 
296 static krb5_scache * KRB5_CALLCONV
297 scc_alloc(krb5_context context, const char *name)
298 {
299     krb5_error_code ret;
300     krb5_scache *s;
301 
302     ALLOC(s, 1);
303     if(s == NULL)
304 	return NULL;
305 
306     s->cid = SCACHE_INVALID_CID;
307 
308     if (name) {
309 	char *file;
310 
311 	if (*name == '\0') {
312 	    krb5_error_code ret;
313 	    ret = get_def_name(context, &s->name);
314 	    if (ret)
315 		s->name = strdup(SCACHE_DEF_NAME);
316 	} else
317 	    s->name = strdup(name);
318 
319 	file = strrchr(s->name, ':');
320 	if (file) {
321 	    *file++ = '\0';
322 	    s->file = strdup(file);
323 	    ret = 0;
324 	} else {
325 	    ret = _krb5_expand_default_cc_name(context, KRB5_SCACHE_DB, &s->file);
326 	}
327     } else {
328 	_krb5_expand_default_cc_name(context, KRB5_SCACHE_DB, &s->file);
329 	ret = asprintf(&s->name, "unique-%p", s);
330     }
331     if (ret < 0 || s->file == NULL || s->name == NULL) {
332 	scc_free(s);
333 	return NULL;
334     }
335 
336     return s;
337 }
338 
339 static krb5_error_code
340 open_database(krb5_context context, krb5_scache *s, int flags)
341 {
342     int ret;
343 
344     ret = sqlite3_open_v2(s->file, &s->db, SQLITE_OPEN_READWRITE|flags, NULL);
345     if (ret) {
346 	if (s->db) {
347 	    krb5_set_error_message(context, ENOENT,
348 				   N_("Error opening scache file %s: %s", ""),
349 				   s->file, sqlite3_errmsg(s->db));
350 	    sqlite3_close(s->db);
351 	    s->db = NULL;
352 	} else
353 	    krb5_set_error_message(context, ENOENT,
354 				   N_("malloc: out of memory", ""));
355 	return ENOENT;
356     }
357     return 0;
358 }
359 
360 static krb5_error_code
361 create_cache(krb5_context context, krb5_scache *s)
362 {
363     int ret;
364 
365     sqlite3_bind_text(s->icache, 1, s->name, -1, NULL);
366     do {
367 	ret = sqlite3_step(s->icache);
368     } while (ret == SQLITE_ROW);
369     if (ret != SQLITE_DONE) {
370 	krb5_set_error_message(context, KRB5_CC_IO,
371 			       N_("Failed to add scache: %d", ""), ret);
372 	return KRB5_CC_IO;
373     }
374     sqlite3_reset(s->icache);
375 
376     s->cid = sqlite3_last_insert_rowid(s->db);
377 
378     return 0;
379 }
380 
381 static krb5_error_code
382 make_database(krb5_context context, krb5_scache *s)
383 {
384     int created_file = 0;
385     int ret;
386 
387     if (s->db)
388 	return 0;
389 
390     ret = open_database(context, s, 0);
391     if (ret) {
392 	mode_t oldumask = umask(077);
393 	ret = open_database(context, s, SQLITE_OPEN_CREATE);
394 	umask(oldumask);
395 	if (ret) goto out;
396 
397 	created_file = 1;
398 
399 	ret = exec_stmt(context, s->db, SQL_CMASTER, KRB5_CC_IO);
400 	if (ret) goto out;
401 	ret = exec_stmt(context, s->db, SQL_CCACHE, KRB5_CC_IO);
402 	if (ret) goto out;
403 	ret = exec_stmt(context, s->db, SQL_CCREDS, KRB5_CC_IO);
404 	if (ret) goto out;
405 	ret = exec_stmt(context, s->db, SQL_CPRINCIPALS, KRB5_CC_IO);
406 	if (ret) goto out;
407 	ret = exec_stmt(context, s->db, SQL_SETUP_MASTER, KRB5_CC_IO);
408 	if (ret) goto out;
409 
410 	ret = exec_stmt(context, s->db, SQL_TCACHE, KRB5_CC_IO);
411 	if (ret) goto out;
412 	ret = exec_stmt(context, s->db, SQL_TCRED, KRB5_CC_IO);
413 	if (ret) goto out;
414     }
415 
416 #ifdef TRACEME
417     sqlite3_trace(s->db, trace, NULL);
418 #endif
419 
420     ret = prepare_stmt(context, s->db, &s->icred, SQL_ICRED);
421     if (ret) goto out;
422     ret = prepare_stmt(context, s->db, &s->dcred, SQL_DCRED);
423     if (ret) goto out;
424     ret = prepare_stmt(context, s->db, &s->iprincipal, SQL_IPRINCIPAL);
425     if (ret) goto out;
426     ret = prepare_stmt(context, s->db, &s->icache, SQL_ICACHE);
427     if (ret) goto out;
428     ret = prepare_stmt(context, s->db, &s->ucachen, SQL_UCACHE_NAME);
429     if (ret) goto out;
430     ret = prepare_stmt(context, s->db, &s->ucachep, SQL_UCACHE_PRINCIPAL);
431     if (ret) goto out;
432     ret = prepare_stmt(context, s->db, &s->dcache, SQL_DCACHE);
433     if (ret) goto out;
434     ret = prepare_stmt(context, s->db, &s->scache, SQL_SCACHE);
435     if (ret) goto out;
436     ret = prepare_stmt(context, s->db, &s->scache_name, SQL_SCACHE_NAME);
437     if (ret) goto out;
438     ret = prepare_stmt(context, s->db, &s->umaster, SQL_UMASTER);
439     if (ret) goto out;
440 
441     return 0;
442 
443 out:
444     if (s->db)
445 	sqlite3_close(s->db);
446     if (created_file)
447 	unlink(s->file);
448 
449     return ret;
450 }
451 
452 static krb5_error_code
453 bind_principal(krb5_context context,
454 	       sqlite3 *db,
455 	       sqlite3_stmt *stmt,
456 	       int col,
457 	       krb5_const_principal principal)
458 {
459     krb5_error_code ret;
460     char *str;
461 
462     ret = krb5_unparse_name(context, principal, &str);
463     if (ret)
464 	return ret;
465 
466     ret = sqlite3_bind_text(stmt, col, str, -1, free_krb5);
467     if (ret != SQLITE_OK) {
468 	krb5_xfree(str);
469 	krb5_set_error_message(context, ENOMEM,
470 			       N_("scache bind principal: %s", ""),
471 			       sqlite3_errmsg(db));
472 	return ENOMEM;
473     }
474     return 0;
475 }
476 
477 /*
478  *
479  */
480 
481 static const char* KRB5_CALLCONV
482 scc_get_name(krb5_context context,
483 	     krb5_ccache id)
484 {
485     return SCACHE(id)->name;
486 }
487 
488 static krb5_error_code KRB5_CALLCONV
489 scc_resolve(krb5_context context, krb5_ccache *id, const char *res)
490 {
491     krb5_scache *s;
492     int ret;
493 
494     s = scc_alloc(context, res);
495     if (s == NULL) {
496 	krb5_set_error_message(context, KRB5_CC_NOMEM,
497 			       N_("malloc: out of memory", ""));
498 	return KRB5_CC_NOMEM;
499     }
500 
501     ret = make_database(context, s);
502     if (ret) {
503 	scc_free(s);
504 	return ret;
505     }
506 
507     ret = sqlite3_bind_text(s->scache_name, 1, s->name, -1, NULL);
508     if (ret != SQLITE_OK) {
509 	krb5_set_error_message(context, ENOMEM,
510 			       "bind name: %s", sqlite3_errmsg(s->db));
511 	scc_free(s);
512 	return ENOMEM;
513     }
514 
515     if (sqlite3_step(s->scache_name) == SQLITE_ROW) {
516 
517 	if (sqlite3_column_type(s->scache_name, 0) != SQLITE_INTEGER) {
518 	    sqlite3_reset(s->scache_name);
519 	    krb5_set_error_message(context, KRB5_CC_END,
520 				   N_("Cache name of wrong type "
521 				      "for scache %s", ""),
522 				   s->name);
523 	    scc_free(s);
524 	    return KRB5_CC_END;
525 	}
526 
527 	s->cid = sqlite3_column_int(s->scache_name, 0);
528     } else {
529 	s->cid = SCACHE_INVALID_CID;
530     }
531     sqlite3_reset(s->scache_name);
532 
533     (*id)->data.data = s;
534     (*id)->data.length = sizeof(*s);
535 
536     return 0;
537 }
538 
539 static krb5_error_code KRB5_CALLCONV
540 scc_gen_new(krb5_context context, krb5_ccache *id)
541 {
542     krb5_scache *s;
543 
544     s = scc_alloc(context, NULL);
545 
546     if (s == NULL) {
547 	krb5_set_error_message(context, KRB5_CC_NOMEM,
548 			       N_("malloc: out of memory", ""));
549 	return KRB5_CC_NOMEM;
550     }
551 
552     (*id)->data.data = s;
553     (*id)->data.length = sizeof(*s);
554 
555     return 0;
556 }
557 
558 static krb5_error_code KRB5_CALLCONV
559 scc_initialize(krb5_context context,
560 	       krb5_ccache id,
561 	       krb5_principal primary_principal)
562 {
563     krb5_scache *s = SCACHE(id);
564     krb5_error_code ret;
565 
566     ret = make_database(context, s);
567     if (ret)
568 	return ret;
569 
570     ret = exec_stmt(context, s->db, "BEGIN IMMEDIATE TRANSACTION", KRB5_CC_IO);
571     if (ret) return ret;
572 
573     if (s->cid == SCACHE_INVALID_CID) {
574 	ret = create_cache(context, s);
575 	if (ret)
576 	    goto rollback;
577     } else {
578 	sqlite3_bind_int(s->dcred, 1, s->cid);
579 	do {
580 	    ret = sqlite3_step(s->dcred);
581 	} while (ret == SQLITE_ROW);
582 	sqlite3_reset(s->dcred);
583 	if (ret != SQLITE_DONE) {
584 	    ret = KRB5_CC_IO;
585 	    krb5_set_error_message(context, ret,
586 				   N_("Failed to delete old "
587 				      "credentials: %s", ""),
588 				   sqlite3_errmsg(s->db));
589 	    goto rollback;
590 	}
591     }
592 
593     ret = bind_principal(context, s->db, s->ucachep, 1, primary_principal);
594     if (ret)
595 	goto rollback;
596     sqlite3_bind_int(s->ucachep, 2, s->cid);
597 
598     do {
599 	ret = sqlite3_step(s->ucachep);
600     } while (ret == SQLITE_ROW);
601     sqlite3_reset(s->ucachep);
602     if (ret != SQLITE_DONE) {
603 	ret = KRB5_CC_IO;
604 	krb5_set_error_message(context, ret,
605 			       N_("Failed to bind principal to cache %s", ""),
606 			       sqlite3_errmsg(s->db));
607 	goto rollback;
608     }
609 
610     ret = exec_stmt(context, s->db, "COMMIT", KRB5_CC_IO);
611     if (ret) return ret;
612 
613     return 0;
614 
615 rollback:
616     exec_stmt(context, s->db, "ROLLBACK", 0);
617 
618     return ret;
619 
620 }
621 
622 static krb5_error_code KRB5_CALLCONV
623 scc_close(krb5_context context,
624 	  krb5_ccache id)
625 {
626     scc_free(SCACHE(id));
627     return 0;
628 }
629 
630 static krb5_error_code KRB5_CALLCONV
631 scc_destroy(krb5_context context,
632 	    krb5_ccache id)
633 {
634     krb5_scache *s = SCACHE(id);
635     int ret;
636 
637     if (s->cid == SCACHE_INVALID_CID)
638 	return 0;
639 
640     sqlite3_bind_int(s->dcache, 1, s->cid);
641     do {
642 	ret = sqlite3_step(s->dcache);
643     } while (ret == SQLITE_ROW);
644     sqlite3_reset(s->dcache);
645     if (ret != SQLITE_DONE) {
646 	krb5_set_error_message(context, KRB5_CC_IO,
647 			       N_("Failed to destroy cache %s: %s", ""),
648 			       s->name, sqlite3_errmsg(s->db));
649 	return KRB5_CC_IO;
650     }
651     return 0;
652 }
653 
654 static krb5_error_code
655 encode_creds(krb5_context context, krb5_creds *creds, krb5_data *data)
656 {
657     krb5_error_code ret;
658     krb5_storage *sp;
659 
660     sp = krb5_storage_emem();
661     if (sp == NULL) {
662 	krb5_set_error_message(context, ENOMEM,
663 			       N_("malloc: out of memory", ""));
664 	return ENOMEM;
665     }
666 
667     ret = krb5_store_creds(sp, creds);
668     if (ret) {
669 	krb5_set_error_message(context, ret,
670 			       N_("Failed to store credential in scache", ""));
671 	krb5_storage_free(sp);
672 	return ret;
673     }
674 
675     ret = krb5_storage_to_data(sp, data);
676     krb5_storage_free(sp);
677     if (ret)
678 	krb5_set_error_message(context, ret,
679 			       N_("Failed to encode credential in scache", ""));
680     return ret;
681 }
682 
683 static krb5_error_code
684 decode_creds(krb5_context context, const void *data, size_t length,
685 	     krb5_creds *creds)
686 {
687     krb5_error_code ret;
688     krb5_storage *sp;
689 
690     sp = krb5_storage_from_readonly_mem(data, length);
691     if (sp == NULL) {
692 	krb5_set_error_message(context, ENOMEM,
693 			       N_("malloc: out of memory", ""));
694 	return ENOMEM;
695     }
696 
697     ret = krb5_ret_creds(sp, creds);
698     krb5_storage_free(sp);
699     if (ret) {
700 	krb5_set_error_message(context, ret,
701 			       N_("Failed to read credential in scache", ""));
702 	return ret;
703     }
704     return 0;
705 }
706 
707 
708 static krb5_error_code KRB5_CALLCONV
709 scc_store_cred(krb5_context context,
710 	       krb5_ccache id,
711 	       krb5_creds *creds)
712 {
713     sqlite_uint64 credid;
714     krb5_scache *s = SCACHE(id);
715     krb5_error_code ret;
716     krb5_data data;
717 
718     ret = make_database(context, s);
719     if (ret)
720 	return ret;
721 
722     ret = encode_creds(context, creds, &data);
723     if (ret)
724 	return ret;
725 
726     sqlite3_bind_int(s->icred, 1, s->cid);
727     {
728 	krb5_enctype etype = 0;
729 	int kvno = 0;
730 	Ticket t;
731 	size_t len;
732 
733 	ret = decode_Ticket(creds->ticket.data,
734 			    creds->ticket.length, &t, &len);
735 	if (ret == 0) {
736 	    if(t.enc_part.kvno)
737 		kvno = *t.enc_part.kvno;
738 
739 	    etype = t.enc_part.etype;
740 
741 	    free_Ticket(&t);
742 	}
743 
744 	sqlite3_bind_int(s->icred, 2, kvno);
745 	sqlite3_bind_int(s->icred, 3, etype);
746 
747     }
748 
749     sqlite3_bind_blob(s->icred, 4, data.data, data.length, free_data);
750     sqlite3_bind_int(s->icred, 5, time(NULL));
751 
752     ret = exec_stmt(context, s->db, "BEGIN IMMEDIATE TRANSACTION", KRB5_CC_IO);
753     if (ret) return ret;
754 
755     do {
756 	ret = sqlite3_step(s->icred);
757     } while (ret == SQLITE_ROW);
758     sqlite3_reset(s->icred);
759     if (ret != SQLITE_DONE) {
760 	ret = KRB5_CC_IO;
761 	krb5_set_error_message(context, ret,
762 			       N_("Failed to add credential: %s", ""),
763 			       sqlite3_errmsg(s->db));
764 	goto rollback;
765     }
766 
767     credid = sqlite3_last_insert_rowid(s->db);
768 
769     {
770 	bind_principal(context, s->db, s->iprincipal, 1, creds->server);
771 	sqlite3_bind_int(s->iprincipal, 2, 1);
772 	sqlite3_bind_int(s->iprincipal, 3, credid);
773 
774 	do {
775 	    ret = sqlite3_step(s->iprincipal);
776 	} while (ret == SQLITE_ROW);
777 	sqlite3_reset(s->iprincipal);
778 	if (ret != SQLITE_DONE) {
779 	    ret = KRB5_CC_IO;
780 	    krb5_set_error_message(context, ret,
781 				   N_("Failed to add principal: %s", ""),
782 				   sqlite3_errmsg(s->db));
783 	    goto rollback;
784 	}
785     }
786 
787     {
788 	bind_principal(context, s->db, s->iprincipal, 1, creds->client);
789 	sqlite3_bind_int(s->iprincipal, 2, 0);
790 	sqlite3_bind_int(s->iprincipal, 3, credid);
791 
792 	do {
793 	    ret = sqlite3_step(s->iprincipal);
794 	} while (ret == SQLITE_ROW);
795 	sqlite3_reset(s->iprincipal);
796 	if (ret != SQLITE_DONE) {
797 	    ret = KRB5_CC_IO;
798 	    krb5_set_error_message(context, ret,
799 				   N_("Failed to add principal: %s", ""),
800 				   sqlite3_errmsg(s->db));
801 	    goto rollback;
802 	}
803     }
804 
805     ret = exec_stmt(context, s->db, "COMMIT", KRB5_CC_IO);
806     if (ret) return ret;
807 
808     return 0;
809 
810 rollback:
811     exec_stmt(context, s->db, "ROLLBACK", 0);
812 
813     return ret;
814 }
815 
816 static krb5_error_code KRB5_CALLCONV
817 scc_get_principal(krb5_context context,
818 		  krb5_ccache id,
819 		  krb5_principal *principal)
820 {
821     krb5_scache *s = SCACHE(id);
822     krb5_error_code ret;
823     const char *str;
824 
825     *principal = NULL;
826 
827     ret = make_database(context, s);
828     if (ret)
829 	return ret;
830 
831     sqlite3_bind_int(s->scache, 1, s->cid);
832 
833     if (sqlite3_step(s->scache) != SQLITE_ROW) {
834 	sqlite3_reset(s->scache);
835 	krb5_set_error_message(context, KRB5_CC_END,
836 			       N_("No principal for cache SCC:%s:%s", ""),
837 			       s->name, s->file);
838 	return KRB5_CC_END;
839     }
840 
841     if (sqlite3_column_type(s->scache, 0) != SQLITE_TEXT) {
842 	sqlite3_reset(s->scache);
843 	krb5_set_error_message(context, KRB5_CC_END,
844 			       N_("Principal data of wrong type "
845 				  "for SCC:%s:%s", ""),
846 			       s->name, s->file);
847 	return KRB5_CC_END;
848     }
849 
850     str = (const char *)sqlite3_column_text(s->scache, 0);
851     if (str == NULL) {
852 	sqlite3_reset(s->scache);
853 	krb5_set_error_message(context, KRB5_CC_END,
854 			       N_("Principal not set for SCC:%s:%s", ""),
855 			       s->name, s->file);
856 	return KRB5_CC_END;
857     }
858 
859     ret = krb5_parse_name(context, str, principal);
860 
861     sqlite3_reset(s->scache);
862 
863     return ret;
864 }
865 
866 struct cred_ctx {
867     char *drop;
868     sqlite3_stmt *stmt;
869     sqlite3_stmt *credstmt;
870 };
871 
872 static krb5_error_code KRB5_CALLCONV
873 scc_get_first (krb5_context context,
874 	       krb5_ccache id,
875 	       krb5_cc_cursor *cursor)
876 {
877     krb5_scache *s = SCACHE(id);
878     krb5_error_code ret;
879     struct cred_ctx *ctx;
880     char *str = NULL, *name = NULL;
881 
882     *cursor = NULL;
883 
884     ctx = calloc(1, sizeof(*ctx));
885     if (ctx == NULL) {
886 	krb5_set_error_message(context, ENOMEM,
887 			       N_("malloc: out of memory", ""));
888 	return ENOMEM;
889     }
890 
891     ret = make_database(context, s);
892     if (ret) {
893 	free(ctx);
894 	return ret;
895     }
896 
897     if (s->cid == SCACHE_INVALID_CID) {
898 	krb5_set_error_message(context, KRB5_CC_END,
899 			       N_("Iterating a invalid scache %s", ""),
900 			       s->name);
901 	free(ctx);
902 	return KRB5_CC_END;
903     }
904 
905     ret = asprintf(&name, "credIteration%pPid%d",
906                    ctx, (int)getpid());
907     if (ret < 0 || name == NULL) {
908 	krb5_set_error_message(context, ENOMEM,
909 			       N_("malloc: out of memory", ""));
910 	free(ctx);
911 	return ENOMEM;
912     }
913 
914     ret = asprintf(&ctx->drop, "DROP TABLE %s", name);
915     if (ret < 0 || ctx->drop == NULL) {
916 	krb5_set_error_message(context, ENOMEM,
917 			       N_("malloc: out of memory", ""));
918 	free(name);
919 	free(ctx);
920 	return ENOMEM;
921     }
922 
923     ret = asprintf(&str, "CREATE TEMPORARY TABLE %s "
924 	     "AS SELECT oid,created_at FROM credentials WHERE cid = %lu",
925 	     name, (unsigned long)s->cid);
926     if (ret < 0 || str == NULL) {
927 	free(ctx->drop);
928 	free(name);
929 	free(ctx);
930 	return ENOMEM;
931     }
932 
933     ret = exec_stmt(context, s->db, str, KRB5_CC_IO);
934     free(str);
935     str = NULL;
936     if (ret) {
937 	free(ctx->drop);
938 	free(name);
939 	free(ctx);
940 	return ret;
941     }
942 
943     ret = asprintf(&str, "SELECT oid FROM %s ORDER BY created_at", name);
944     if (ret < 0 || str == NULL) {
945 	exec_stmt(context, s->db, ctx->drop, 0);
946 	free(ctx->drop);
947 	free(name);
948 	free(ctx);
949 	return ret;
950     }
951 
952     ret = prepare_stmt(context, s->db, &ctx->stmt, str);
953     free(str);
954     str = NULL;
955     free(name);
956     if (ret) {
957 	exec_stmt(context, s->db, ctx->drop, 0);
958 	free(ctx->drop);
959 	free(ctx);
960 	return ret;
961     }
962 
963     ret = prepare_stmt(context, s->db, &ctx->credstmt,
964 		       "SELECT cred FROM credentials WHERE oid = ?");
965     if (ret) {
966 	sqlite3_finalize(ctx->stmt);
967 	exec_stmt(context, s->db, ctx->drop, 0);
968 	free(ctx->drop);
969 	free(ctx);
970 	return ret;
971     }
972 
973     *cursor = ctx;
974 
975     return 0;
976 }
977 
978 static krb5_error_code KRB5_CALLCONV
979 scc_get_next (krb5_context context,
980 	      krb5_ccache id,
981 	      krb5_cc_cursor *cursor,
982 	      krb5_creds *creds)
983 {
984     struct cred_ctx *ctx = *cursor;
985     krb5_scache *s = SCACHE(id);
986     krb5_error_code ret;
987     sqlite_uint64 oid;
988     const void *data = NULL;
989     size_t len = 0;
990 
991 next:
992     ret = sqlite3_step(ctx->stmt);
993     if (ret == SQLITE_DONE) {
994 	krb5_clear_error_message(context);
995         return KRB5_CC_END;
996     } else if (ret != SQLITE_ROW) {
997 	krb5_set_error_message(context, KRB5_CC_IO,
998 			       N_("scache Database failed: %s", ""),
999 			       sqlite3_errmsg(s->db));
1000         return KRB5_CC_IO;
1001     }
1002 
1003     oid = sqlite3_column_int64(ctx->stmt, 0);
1004 
1005     /* read cred from credentials table */
1006 
1007     sqlite3_bind_int(ctx->credstmt, 1, oid);
1008 
1009     ret = sqlite3_step(ctx->credstmt);
1010     if (ret != SQLITE_ROW) {
1011 	sqlite3_reset(ctx->credstmt);
1012 	goto next;
1013     }
1014 
1015     if (sqlite3_column_type(ctx->credstmt, 0) != SQLITE_BLOB) {
1016 	krb5_set_error_message(context, KRB5_CC_END,
1017 			       N_("credential of wrong type for SCC:%s:%s", ""),
1018 			       s->name, s->file);
1019 	sqlite3_reset(ctx->credstmt);
1020 	return KRB5_CC_END;
1021     }
1022 
1023     data = sqlite3_column_blob(ctx->credstmt, 0);
1024     len = sqlite3_column_bytes(ctx->credstmt, 0);
1025 
1026     ret = decode_creds(context, data, len, creds);
1027     sqlite3_reset(ctx->credstmt);
1028     return ret;
1029 }
1030 
1031 static krb5_error_code KRB5_CALLCONV
1032 scc_end_get (krb5_context context,
1033 	     krb5_ccache id,
1034 	     krb5_cc_cursor *cursor)
1035 {
1036     struct cred_ctx *ctx = *cursor;
1037     krb5_scache *s = SCACHE(id);
1038 
1039     sqlite3_finalize(ctx->stmt);
1040     sqlite3_finalize(ctx->credstmt);
1041 
1042     exec_stmt(context, s->db, ctx->drop, 0);
1043 
1044     free(ctx->drop);
1045     free(ctx);
1046 
1047     return 0;
1048 }
1049 
1050 static krb5_error_code KRB5_CALLCONV
1051 scc_remove_cred(krb5_context context,
1052 		 krb5_ccache id,
1053 		 krb5_flags which,
1054 		 krb5_creds *mcreds)
1055 {
1056     krb5_scache *s = SCACHE(id);
1057     krb5_error_code ret;
1058     sqlite3_stmt *stmt;
1059     sqlite_uint64 credid = 0;
1060     const void *data = NULL;
1061     size_t len = 0;
1062 
1063     ret = make_database(context, s);
1064     if (ret)
1065 	return ret;
1066 
1067     ret = prepare_stmt(context, s->db, &stmt,
1068 		       "SELECT cred,oid FROM credentials "
1069 		       "WHERE cid = ?");
1070     if (ret)
1071 	return ret;
1072 
1073     sqlite3_bind_int(stmt, 1, s->cid);
1074 
1075     /* find credential... */
1076     while (1) {
1077 	krb5_creds creds;
1078 
1079 	ret = sqlite3_step(stmt);
1080 	if (ret == SQLITE_DONE) {
1081 	    ret = 0;
1082 	    break;
1083 	} else if (ret != SQLITE_ROW) {
1084 	    ret = KRB5_CC_IO;
1085 	    krb5_set_error_message(context, ret,
1086 				   N_("scache Database failed: %s", ""),
1087 				   sqlite3_errmsg(s->db));
1088 	    break;
1089 	}
1090 
1091 	if (sqlite3_column_type(stmt, 0) != SQLITE_BLOB) {
1092 	    ret = KRB5_CC_END;
1093 	    krb5_set_error_message(context, ret,
1094 				   N_("Credential of wrong type "
1095 				      "for SCC:%s:%s", ""),
1096 				   s->name, s->file);
1097 	    break;
1098 	}
1099 
1100 	data = sqlite3_column_blob(stmt, 0);
1101 	len = sqlite3_column_bytes(stmt, 0);
1102 
1103 	ret = decode_creds(context, data, len, &creds);
1104 	if (ret)
1105 	    break;
1106 
1107 	ret = krb5_compare_creds(context, which, mcreds, &creds);
1108 	krb5_free_cred_contents(context, &creds);
1109 	if (ret) {
1110 	    credid = sqlite3_column_int64(stmt, 1);
1111 	    ret = 0;
1112 	    break;
1113 	}
1114     }
1115 
1116     sqlite3_finalize(stmt);
1117 
1118     if (id) {
1119 	ret = prepare_stmt(context, s->db, &stmt,
1120 			   "DELETE FROM credentials WHERE oid=?");
1121 	if (ret)
1122 	    return ret;
1123 	sqlite3_bind_int(stmt, 1, credid);
1124 
1125 	do {
1126 	    ret = sqlite3_step(stmt);
1127 	} while (ret == SQLITE_ROW);
1128 	sqlite3_finalize(stmt);
1129 	if (ret != SQLITE_DONE) {
1130 	    ret = KRB5_CC_IO;
1131 	    krb5_set_error_message(context, ret,
1132 				   N_("failed to delete scache credental", ""));
1133 	} else
1134 	    ret = 0;
1135     }
1136 
1137     return ret;
1138 }
1139 
1140 static krb5_error_code KRB5_CALLCONV
1141 scc_set_flags(krb5_context context,
1142 	      krb5_ccache id,
1143 	      krb5_flags flags)
1144 {
1145     return 0; /* XXX */
1146 }
1147 
1148 struct cache_iter {
1149     char *drop;
1150     sqlite3 *db;
1151     sqlite3_stmt *stmt;
1152 };
1153 
1154 static krb5_error_code KRB5_CALLCONV
1155 scc_get_cache_first(krb5_context context, krb5_cc_cursor *cursor)
1156 {
1157     struct cache_iter *ctx;
1158     krb5_error_code ret;
1159     char *name = NULL, *str = NULL;
1160 
1161     *cursor = NULL;
1162 
1163     ctx = calloc(1, sizeof(*ctx));
1164     if (ctx == NULL) {
1165 	krb5_set_error_message(context, ENOMEM,
1166 			       N_("malloc: out of memory", ""));
1167 	return ENOMEM;
1168     }
1169 
1170     ret = default_db(context, &ctx->db);
1171     if (ctx->db == NULL) {
1172 	free(ctx);
1173 	return ret;
1174     }
1175 
1176     ret = asprintf(&name, "cacheIteration%pPid%d",
1177                    ctx, (int)getpid());
1178     if (ret < 0 || name == NULL) {
1179 	krb5_set_error_message(context, ENOMEM,
1180 			       N_("malloc: out of memory", ""));
1181 	sqlite3_close(ctx->db);
1182 	free(ctx);
1183 	return ENOMEM;
1184     }
1185 
1186     ret = asprintf(&ctx->drop, "DROP TABLE %s", name);
1187     if (ret < 0 || ctx->drop == NULL) {
1188 	krb5_set_error_message(context, ENOMEM,
1189 			       N_("malloc: out of memory", ""));
1190 	sqlite3_close(ctx->db);
1191 	free(name);
1192 	free(ctx);
1193 	return ENOMEM;
1194     }
1195 
1196     ret = asprintf(&str, "CREATE TEMPORARY TABLE %s AS SELECT name FROM caches",
1197 	     name);
1198     if (ret < 0 || str == NULL) {
1199 	krb5_set_error_message(context, ENOMEM,
1200 			       N_("malloc: out of memory", ""));
1201 	sqlite3_close(ctx->db);
1202 	free(name);
1203 	free(ctx->drop);
1204 	free(ctx);
1205 	return ENOMEM;
1206     }
1207 
1208     ret = exec_stmt(context, ctx->db, str, KRB5_CC_IO);
1209     free(str);
1210     str = NULL;
1211     if (ret) {
1212 	sqlite3_close(ctx->db);
1213 	free(name);
1214 	free(ctx->drop);
1215 	free(ctx);
1216 	return ret;
1217     }
1218 
1219     ret = asprintf(&str, "SELECT name FROM %s", name);
1220     free(name);
1221     if (ret < 0 || str == NULL) {
1222 	exec_stmt(context, ctx->db, ctx->drop, 0);
1223 	sqlite3_close(ctx->db);
1224 	free(name);
1225 	free(ctx->drop);
1226 	free(ctx);
1227 	return ENOMEM;
1228     }
1229 
1230     ret = prepare_stmt(context, ctx->db, &ctx->stmt, str);
1231     free(str);
1232     if (ret) {
1233 	exec_stmt(context, ctx->db, ctx->drop, 0);
1234 	sqlite3_close(ctx->db);
1235 	free(ctx->drop);
1236 	free(ctx);
1237 	return ret;
1238     }
1239 
1240     *cursor = ctx;
1241 
1242     return 0;
1243 }
1244 
1245 static krb5_error_code KRB5_CALLCONV
1246 scc_get_cache_next(krb5_context context,
1247 		   krb5_cc_cursor cursor,
1248 		   krb5_ccache *id)
1249 {
1250     struct cache_iter *ctx = cursor;
1251     krb5_error_code ret;
1252     const char *name;
1253 
1254 again:
1255     ret = sqlite3_step(ctx->stmt);
1256     if (ret == SQLITE_DONE) {
1257 	krb5_clear_error_message(context);
1258         return KRB5_CC_END;
1259     } else if (ret != SQLITE_ROW) {
1260 	krb5_set_error_message(context, KRB5_CC_IO,
1261 			       N_("Database failed: %s", ""),
1262 			       sqlite3_errmsg(ctx->db));
1263         return KRB5_CC_IO;
1264     }
1265 
1266     if (sqlite3_column_type(ctx->stmt, 0) != SQLITE_TEXT)
1267 	goto again;
1268 
1269     name = (const char *)sqlite3_column_text(ctx->stmt, 0);
1270     if (name == NULL)
1271 	goto again;
1272 
1273     ret = _krb5_cc_allocate(context, &krb5_scc_ops, id);
1274     if (ret)
1275 	return ret;
1276 
1277     return scc_resolve(context, id, name);
1278 }
1279 
1280 static krb5_error_code KRB5_CALLCONV
1281 scc_end_cache_get(krb5_context context, krb5_cc_cursor cursor)
1282 {
1283     struct cache_iter *ctx = cursor;
1284 
1285     exec_stmt(context, ctx->db, ctx->drop, 0);
1286     sqlite3_finalize(ctx->stmt);
1287     sqlite3_close(ctx->db);
1288     free(ctx->drop);
1289     free(ctx);
1290     return 0;
1291 }
1292 
1293 static krb5_error_code KRB5_CALLCONV
1294 scc_move(krb5_context context, krb5_ccache from, krb5_ccache to)
1295 {
1296     krb5_scache *sfrom = SCACHE(from);
1297     krb5_scache *sto = SCACHE(to);
1298     krb5_error_code ret;
1299 
1300     if (strcmp(sfrom->file, sto->file) != 0) {
1301 	krb5_set_error_message(context, KRB5_CC_BADNAME,
1302 			       N_("Can't handle cross database "
1303 				  "credential move: %s -> %s", ""),
1304 			       sfrom->file, sto->file);
1305 	return KRB5_CC_BADNAME;
1306     }
1307 
1308     ret = make_database(context, sfrom);
1309     if (ret)
1310 	return ret;
1311 
1312     ret = exec_stmt(context, sfrom->db,
1313 		    "BEGIN IMMEDIATE TRANSACTION", KRB5_CC_IO);
1314     if (ret) return ret;
1315 
1316     if (sto->cid != SCACHE_INVALID_CID) {
1317 	/* drop old cache entry */
1318 
1319 	sqlite3_bind_int(sfrom->dcache, 1, sto->cid);
1320 	do {
1321 	    ret = sqlite3_step(sfrom->dcache);
1322 	} while (ret == SQLITE_ROW);
1323 	sqlite3_reset(sfrom->dcache);
1324 	if (ret != SQLITE_DONE) {
1325 	    krb5_set_error_message(context, KRB5_CC_IO,
1326 				   N_("Failed to delete old cache: %d", ""),
1327 				   (int)ret);
1328 	    goto rollback;
1329 	}
1330     }
1331 
1332     sqlite3_bind_text(sfrom->ucachen, 1, sto->name, -1, NULL);
1333     sqlite3_bind_int(sfrom->ucachen, 2, sfrom->cid);
1334 
1335     do {
1336 	ret = sqlite3_step(sfrom->ucachen);
1337     } while (ret == SQLITE_ROW);
1338     sqlite3_reset(sfrom->ucachen);
1339     if (ret != SQLITE_DONE) {
1340 	krb5_set_error_message(context, KRB5_CC_IO,
1341 			       N_("Failed to update new cache: %d", ""),
1342 			       (int)ret);
1343 	goto rollback;
1344     }
1345 
1346     sto->cid = sfrom->cid;
1347 
1348     ret = exec_stmt(context, sfrom->db, "COMMIT", KRB5_CC_IO);
1349     if (ret) return ret;
1350 
1351     scc_free(sfrom);
1352 
1353     return 0;
1354 
1355 rollback:
1356     exec_stmt(context, sfrom->db, "ROLLBACK", 0);
1357     scc_free(sfrom);
1358 
1359     return KRB5_CC_IO;
1360 }
1361 
1362 static krb5_error_code KRB5_CALLCONV
1363 scc_get_default_name(krb5_context context, char **str)
1364 {
1365     krb5_error_code ret;
1366     char *name;
1367 
1368     *str = NULL;
1369 
1370     ret = get_def_name(context, &name);
1371     if (ret)
1372 	return _krb5_expand_default_cc_name(context, KRB5_SCACHE_NAME, str);
1373 
1374     ret = asprintf(str, "SCC:%s", name);
1375     free(name);
1376     if (ret < 0 || *str == NULL) {
1377 	krb5_set_error_message(context, ENOMEM,
1378 			       N_("malloc: out of memory", ""));
1379 	return ENOMEM;
1380     }
1381     return 0;
1382 }
1383 
1384 static krb5_error_code KRB5_CALLCONV
1385 scc_set_default(krb5_context context, krb5_ccache id)
1386 {
1387     krb5_scache *s = SCACHE(id);
1388     krb5_error_code ret;
1389 
1390     if (s->cid == SCACHE_INVALID_CID) {
1391 	krb5_set_error_message(context, KRB5_CC_IO,
1392 			       N_("Trying to set a invalid cache "
1393 				  "as default %s", ""),
1394 			       s->name);
1395 	return KRB5_CC_IO;
1396     }
1397 
1398     ret = sqlite3_bind_text(s->umaster, 1, s->name, -1, NULL);
1399     if (ret) {
1400 	sqlite3_reset(s->umaster);
1401 	krb5_set_error_message(context, KRB5_CC_IO,
1402 			       N_("Failed to set name of default cache", ""));
1403 	return KRB5_CC_IO;
1404     }
1405 
1406     do {
1407 	ret = sqlite3_step(s->umaster);
1408     } while (ret == SQLITE_ROW);
1409     sqlite3_reset(s->umaster);
1410     if (ret != SQLITE_DONE) {
1411 	krb5_set_error_message(context, KRB5_CC_IO,
1412 			       N_("Failed to update default cache", ""));
1413 	return KRB5_CC_IO;
1414     }
1415 
1416     return 0;
1417 }
1418 
1419 /**
1420  * Variable containing the SCC based credential cache implemention.
1421  *
1422  * @ingroup krb5_ccache
1423  */
1424 
1425 KRB5_LIB_VARIABLE const krb5_cc_ops krb5_scc_ops = {
1426     KRB5_CC_OPS_VERSION,
1427     "SCC",
1428     scc_get_name,
1429     scc_resolve,
1430     scc_gen_new,
1431     scc_initialize,
1432     scc_destroy,
1433     scc_close,
1434     scc_store_cred,
1435     NULL, /* scc_retrieve */
1436     scc_get_principal,
1437     scc_get_first,
1438     scc_get_next,
1439     scc_end_get,
1440     scc_remove_cred,
1441     scc_set_flags,
1442     NULL,
1443     scc_get_cache_first,
1444     scc_get_cache_next,
1445     scc_end_cache_get,
1446     scc_move,
1447     scc_get_default_name,
1448     scc_set_default
1449 };
1450 
1451 #endif
1452