1226031Sstas/*
2226031Sstas * Copyright (c) 2008 Kungliga Tekniska H��gskolan
3226031Sstas * (Royal Institute of Technology, Stockholm, Sweden).
4226031Sstas * All rights reserved.
5226031Sstas *
6226031Sstas * Redistribution and use in source and binary forms, with or without
7226031Sstas * modification, are permitted provided that the following conditions
8226031Sstas * are met:
9226031Sstas *
10226031Sstas * 1. Redistributions of source code must retain the above copyright
11226031Sstas *    notice, this list of conditions and the following disclaimer.
12226031Sstas *
13226031Sstas * 2. Redistributions in binary form must reproduce the above copyright
14226031Sstas *    notice, this list of conditions and the following disclaimer in the
15226031Sstas *    documentation and/or other materials provided with the distribution.
16226031Sstas *
17226031Sstas * 3. Neither the name of the Institute nor the names of its contributors
18226031Sstas *    may be used to endorse or promote products derived from this software
19226031Sstas *    without specific prior written permission.
20226031Sstas *
21226031Sstas * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
22226031Sstas * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23226031Sstas * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24226031Sstas * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
25226031Sstas * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26226031Sstas * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27226031Sstas * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28226031Sstas * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29226031Sstas * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30226031Sstas * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31226031Sstas * SUCH DAMAGE.
32226031Sstas */
33226031Sstas
34226031Sstas#include "krb5_locl.h"
35226031Sstas
36226031Sstas#ifdef HAVE_SCC
37226031Sstas
38226031Sstas#include <sqlite3.h>
39226031Sstas
40226031Sstastypedef struct krb5_scache {
41226031Sstas    char *name;
42226031Sstas    char *file;
43226031Sstas    sqlite3 *db;
44226031Sstas
45226031Sstas    sqlite_uint64 cid;
46226031Sstas
47226031Sstas    sqlite3_stmt *icred;
48226031Sstas    sqlite3_stmt *dcred;
49226031Sstas    sqlite3_stmt *iprincipal;
50226031Sstas
51226031Sstas    sqlite3_stmt *icache;
52226031Sstas    sqlite3_stmt *ucachen;
53226031Sstas    sqlite3_stmt *ucachep;
54226031Sstas    sqlite3_stmt *dcache;
55226031Sstas    sqlite3_stmt *scache;
56226031Sstas    sqlite3_stmt *scache_name;
57226031Sstas    sqlite3_stmt *umaster;
58226031Sstas
59226031Sstas} krb5_scache;
60226031Sstas
61226031Sstas#define	SCACHE(X)	((krb5_scache *)(X)->data.data)
62226031Sstas
63226031Sstas#define SCACHE_DEF_NAME		"Default-cache"
64226031Sstas#ifdef KRB5_USE_PATH_TOKENS
65226031Sstas#define KRB5_SCACHE_DB	"%{TEMP}/krb5scc_%{uid}"
66226031Sstas#else
67226031Sstas#define KRB5_SCACHE_DB	"/tmp/krb5scc_%{uid}"
68226031Sstas#endif
69226031Sstas#define KRB5_SCACHE_NAME	"SCC:"  SCACHE_DEF_NAME ":" KRB5_SCACHE_DB
70226031Sstas
71226031Sstas#define SCACHE_INVALID_CID	((sqlite_uint64)-1)
72226031Sstas
73226031Sstas/*
74226031Sstas *
75226031Sstas */
76226031Sstas
77226031Sstas#define SQL_CMASTER ""				\
78226031Sstas	"CREATE TABLE master ("			\
79226031Sstas        "oid INTEGER PRIMARY KEY,"		\
80226031Sstas	"version INTEGER NOT NULL,"		\
81226031Sstas	"defaultcache TEXT NOT NULL"		\
82226031Sstas	")"
83226031Sstas
84226031Sstas#define SQL_SETUP_MASTER \
85226031Sstas	"INSERT INTO master (version,defaultcache) VALUES(2, \"" SCACHE_DEF_NAME "\")"
86226031Sstas#define SQL_UMASTER "UPDATE master SET defaultcache=? WHERE version=2"
87226031Sstas
88226031Sstas#define SQL_CCACHE ""				\
89226031Sstas	"CREATE TABLE caches ("			\
90226031Sstas        "oid INTEGER PRIMARY KEY,"		\
91226031Sstas	"principal TEXT,"			\
92226031Sstas	"name TEXT NOT NULL"			\
93226031Sstas	")"
94226031Sstas
95226031Sstas#define SQL_TCACHE ""						\
96226031Sstas	"CREATE TRIGGER CacheDropCreds AFTER DELETE ON caches "	\
97226031Sstas	"FOR EACH ROW BEGIN "					\
98226031Sstas	"DELETE FROM credentials WHERE cid=old.oid;"		\
99226031Sstas	"END"
100226031Sstas
101226031Sstas#define SQL_ICACHE "INSERT INTO caches (name) VALUES(?)"
102226031Sstas#define SQL_UCACHE_NAME "UPDATE caches SET name=? WHERE OID=?"
103226031Sstas#define SQL_UCACHE_PRINCIPAL "UPDATE caches SET principal=? WHERE OID=?"
104226031Sstas#define SQL_DCACHE "DELETE FROM caches WHERE OID=?"
105226031Sstas#define SQL_SCACHE "SELECT principal,name FROM caches WHERE OID=?"
106226031Sstas#define SQL_SCACHE_NAME "SELECT oid FROM caches WHERE NAME=?"
107226031Sstas
108226031Sstas#define SQL_CCREDS ""				\
109226031Sstas	"CREATE TABLE credentials ("		\
110226031Sstas        "oid INTEGER PRIMARY KEY,"		\
111226031Sstas	"cid INTEGER NOT NULL,"			\
112226031Sstas	"kvno INTEGER NOT NULL,"		\
113226031Sstas	"etype INTEGER NOT NULL,"		\
114226031Sstas        "created_at INTEGER NOT NULL,"		\
115226031Sstas	"cred BLOB NOT NULL"			\
116226031Sstas	")"
117226031Sstas
118226031Sstas#define SQL_TCRED ""							\
119226031Sstas	"CREATE TRIGGER credDropPrincipal AFTER DELETE ON credentials "	\
120226031Sstas	"FOR EACH ROW BEGIN "						\
121226031Sstas	"DELETE FROM principals WHERE credential_id=old.oid;"		\
122226031Sstas	"END"
123226031Sstas
124226031Sstas#define SQL_ICRED "INSERT INTO credentials (cid, kvno, etype, cred, created_at) VALUES (?,?,?,?,?)"
125226031Sstas#define SQL_DCRED "DELETE FROM credentials WHERE cid=?"
126226031Sstas
127226031Sstas#define SQL_CPRINCIPALS ""			\
128226031Sstas	"CREATE TABLE principals ("		\
129226031Sstas        "oid INTEGER PRIMARY KEY,"		\
130226031Sstas	"principal TEXT NOT NULL,"		\
131226031Sstas	"type INTEGER NOT NULL,"		\
132226031Sstas	"credential_id INTEGER NOT NULL"	\
133226031Sstas	")"
134226031Sstas
135226031Sstas#define SQL_IPRINCIPAL "INSERT INTO principals (principal, type, credential_id) VALUES (?,?,?)"
136226031Sstas
137226031Sstas/*
138226031Sstas * sqlite destructors
139226031Sstas */
140226031Sstas
141226031Sstasstatic void
142226031Sstasfree_data(void *data)
143226031Sstas{
144226031Sstas    free(data);
145226031Sstas}
146226031Sstas
147226031Sstasstatic void
148226031Sstasfree_krb5(void *str)
149226031Sstas{
150226031Sstas    krb5_xfree(str);
151226031Sstas}
152226031Sstas
153226031Sstasstatic void
154226031Sstasscc_free(krb5_scache *s)
155226031Sstas{
156226031Sstas    if (s->file)
157226031Sstas	free(s->file);
158226031Sstas    if (s->name)
159226031Sstas	free(s->name);
160226031Sstas
161226031Sstas    if (s->icred)
162226031Sstas	sqlite3_finalize(s->icred);
163226031Sstas    if (s->dcred)
164226031Sstas	sqlite3_finalize(s->dcred);
165226031Sstas    if (s->iprincipal)
166226031Sstas	sqlite3_finalize(s->iprincipal);
167226031Sstas    if (s->icache)
168226031Sstas	sqlite3_finalize(s->icache);
169226031Sstas    if (s->ucachen)
170226031Sstas	sqlite3_finalize(s->ucachen);
171226031Sstas    if (s->ucachep)
172226031Sstas	sqlite3_finalize(s->ucachep);
173226031Sstas    if (s->dcache)
174226031Sstas	sqlite3_finalize(s->dcache);
175226031Sstas    if (s->scache)
176226031Sstas	sqlite3_finalize(s->scache);
177226031Sstas    if (s->scache_name)
178226031Sstas	sqlite3_finalize(s->scache_name);
179226031Sstas    if (s->umaster)
180226031Sstas	sqlite3_finalize(s->umaster);
181226031Sstas
182226031Sstas    if (s->db)
183226031Sstas	sqlite3_close(s->db);
184226031Sstas    free(s);
185226031Sstas}
186226031Sstas
187226031Sstas#ifdef TRACEME
188226031Sstasstatic void
189226031Sstastrace(void* ptr, const char * str)
190226031Sstas{
191226031Sstas    printf("SQL: %s\n", str);
192226031Sstas}
193226031Sstas#endif
194226031Sstas
195226031Sstasstatic krb5_error_code
196226031Sstasprepare_stmt(krb5_context context, sqlite3 *db,
197226031Sstas	     sqlite3_stmt **stmt, const char *str)
198226031Sstas{
199226031Sstas    int ret;
200226031Sstas
201226031Sstas    ret = sqlite3_prepare_v2(db, str, -1, stmt, NULL);
202226031Sstas    if (ret != SQLITE_OK) {
203226031Sstas	krb5_set_error_message(context, ENOENT,
204226031Sstas			       N_("Failed to prepare stmt %s: %s", ""),
205226031Sstas			       str, sqlite3_errmsg(db));
206226031Sstas	return ENOENT;
207226031Sstas    }
208226031Sstas    return 0;
209226031Sstas}
210226031Sstas
211226031Sstasstatic krb5_error_code
212226031Sstasexec_stmt(krb5_context context, sqlite3 *db, const char *str,
213226031Sstas	  krb5_error_code code)
214226031Sstas{
215226031Sstas    int ret;
216226031Sstas
217226031Sstas    ret = sqlite3_exec(db, str, NULL, NULL, NULL);
218226031Sstas    if (ret != SQLITE_OK && code) {
219226031Sstas	krb5_set_error_message(context, code,
220226031Sstas			       N_("scache execute %s: %s", ""), str,
221226031Sstas			       sqlite3_errmsg(db));
222226031Sstas	return code;
223226031Sstas    }
224226031Sstas    return 0;
225226031Sstas}
226226031Sstas
227226031Sstasstatic krb5_error_code
228226031Sstasdefault_db(krb5_context context, sqlite3 **db)
229226031Sstas{
230226031Sstas    char *name;
231226031Sstas    int ret;
232226031Sstas
233226031Sstas    ret = _krb5_expand_default_cc_name(context, KRB5_SCACHE_DB, &name);
234226031Sstas    if (ret)
235226031Sstas	return ret;
236226031Sstas
237226031Sstas    ret = sqlite3_open_v2(name, db, SQLITE_OPEN_READWRITE, NULL);
238226031Sstas    free(name);
239226031Sstas    if (ret != SQLITE_OK) {
240226031Sstas	krb5_clear_error_message(context);
241226031Sstas	return ENOENT;
242226031Sstas    }
243226031Sstas
244226031Sstas#ifdef TRACEME
245226031Sstas    sqlite3_trace(*db, trace, NULL);
246226031Sstas#endif
247226031Sstas
248226031Sstas    return 0;
249226031Sstas}
250226031Sstas
251226031Sstasstatic krb5_error_code
252226031Sstasget_def_name(krb5_context context, char **str)
253226031Sstas{
254226031Sstas    krb5_error_code ret;
255226031Sstas    sqlite3_stmt *stmt;
256226031Sstas    const char *name;
257226031Sstas    sqlite3 *db;
258226031Sstas
259226031Sstas    ret = default_db(context, &db);
260226031Sstas    if (ret)
261226031Sstas	return ret;
262226031Sstas
263226031Sstas    ret = prepare_stmt(context, db, &stmt, "SELECT defaultcache FROM master");
264226031Sstas    if (ret) {
265226031Sstas	sqlite3_close(db);
266226031Sstas	return ret;
267226031Sstas    }
268226031Sstas
269226031Sstas    ret = sqlite3_step(stmt);
270226031Sstas    if (ret != SQLITE_ROW)
271226031Sstas	goto out;
272226031Sstas
273226031Sstas    if (sqlite3_column_type(stmt, 0) != SQLITE_TEXT)
274226031Sstas	goto out;
275226031Sstas
276226031Sstas    name = (const char *)sqlite3_column_text(stmt, 0);
277226031Sstas    if (name == NULL)
278226031Sstas	goto out;
279226031Sstas
280226031Sstas    *str = strdup(name);
281226031Sstas    if (*str == NULL)
282226031Sstas	goto out;
283226031Sstas
284226031Sstas    sqlite3_finalize(stmt);
285226031Sstas    sqlite3_close(db);
286226031Sstas    return 0;
287226031Sstasout:
288226031Sstas    sqlite3_finalize(stmt);
289226031Sstas    sqlite3_close(db);
290226031Sstas    krb5_clear_error_message(context);
291226031Sstas    return ENOENT;
292226031Sstas}
293226031Sstas
294226031Sstas
295226031Sstas
296226031Sstasstatic krb5_scache * KRB5_CALLCONV
297226031Sstasscc_alloc(krb5_context context, const char *name)
298226031Sstas{
299226031Sstas    krb5_error_code ret;
300226031Sstas    krb5_scache *s;
301226031Sstas
302226031Sstas    ALLOC(s, 1);
303226031Sstas    if(s == NULL)
304226031Sstas	return NULL;
305226031Sstas
306226031Sstas    s->cid = SCACHE_INVALID_CID;
307226031Sstas
308226031Sstas    if (name) {
309226031Sstas	char *file;
310226031Sstas
311226031Sstas	if (*name == '\0') {
312226031Sstas	    krb5_error_code ret;
313226031Sstas	    ret = get_def_name(context, &s->name);
314226031Sstas	    if (ret)
315226031Sstas		s->name = strdup(SCACHE_DEF_NAME);
316226031Sstas	} else
317226031Sstas	    s->name = strdup(name);
318226031Sstas
319226031Sstas	file = strrchr(s->name, ':');
320226031Sstas	if (file) {
321226031Sstas	    *file++ = '\0';
322226031Sstas	    s->file = strdup(file);
323226031Sstas	    ret = 0;
324226031Sstas	} else {
325226031Sstas	    ret = _krb5_expand_default_cc_name(context, KRB5_SCACHE_DB, &s->file);
326226031Sstas	}
327226031Sstas    } else {
328226031Sstas	_krb5_expand_default_cc_name(context, KRB5_SCACHE_DB, &s->file);
329226031Sstas	ret = asprintf(&s->name, "unique-%p", s);
330226031Sstas    }
331226031Sstas    if (ret < 0 || s->file == NULL || s->name == NULL) {
332226031Sstas	scc_free(s);
333226031Sstas	return NULL;
334226031Sstas    }
335226031Sstas
336226031Sstas    return s;
337226031Sstas}
338226031Sstas
339226031Sstasstatic krb5_error_code
340226031Sstasopen_database(krb5_context context, krb5_scache *s, int flags)
341226031Sstas{
342226031Sstas    int ret;
343226031Sstas
344226031Sstas    ret = sqlite3_open_v2(s->file, &s->db, SQLITE_OPEN_READWRITE|flags, NULL);
345226031Sstas    if (ret) {
346226031Sstas	if (s->db) {
347226031Sstas	    krb5_set_error_message(context, ENOENT,
348226031Sstas				   N_("Error opening scache file %s: %s", ""),
349226031Sstas				   s->file, sqlite3_errmsg(s->db));
350226031Sstas	    sqlite3_close(s->db);
351226031Sstas	    s->db = NULL;
352226031Sstas	} else
353226031Sstas	    krb5_set_error_message(context, ENOENT,
354226031Sstas				   N_("malloc: out of memory", ""));
355226031Sstas	return ENOENT;
356226031Sstas    }
357226031Sstas    return 0;
358226031Sstas}
359226031Sstas
360226031Sstasstatic krb5_error_code
361226031Sstascreate_cache(krb5_context context, krb5_scache *s)
362226031Sstas{
363226031Sstas    int ret;
364226031Sstas
365226031Sstas    sqlite3_bind_text(s->icache, 1, s->name, -1, NULL);
366226031Sstas    do {
367226031Sstas	ret = sqlite3_step(s->icache);
368226031Sstas    } while (ret == SQLITE_ROW);
369226031Sstas    if (ret != SQLITE_DONE) {
370226031Sstas	krb5_set_error_message(context, KRB5_CC_IO,
371226031Sstas			       N_("Failed to add scache: %d", ""), ret);
372226031Sstas	return KRB5_CC_IO;
373226031Sstas    }
374226031Sstas    sqlite3_reset(s->icache);
375226031Sstas
376226031Sstas    s->cid = sqlite3_last_insert_rowid(s->db);
377226031Sstas
378226031Sstas    return 0;
379226031Sstas}
380226031Sstas
381226031Sstasstatic krb5_error_code
382226031Sstasmake_database(krb5_context context, krb5_scache *s)
383226031Sstas{
384226031Sstas    int created_file = 0;
385226031Sstas    int ret;
386226031Sstas
387226031Sstas    if (s->db)
388226031Sstas	return 0;
389226031Sstas
390226031Sstas    ret = open_database(context, s, 0);
391226031Sstas    if (ret) {
392226031Sstas	mode_t oldumask = umask(077);
393226031Sstas	ret = open_database(context, s, SQLITE_OPEN_CREATE);
394226031Sstas	umask(oldumask);
395226031Sstas	if (ret) goto out;
396226031Sstas
397226031Sstas	created_file = 1;
398226031Sstas
399226031Sstas	ret = exec_stmt(context, s->db, SQL_CMASTER, KRB5_CC_IO);
400226031Sstas	if (ret) goto out;
401226031Sstas	ret = exec_stmt(context, s->db, SQL_CCACHE, KRB5_CC_IO);
402226031Sstas	if (ret) goto out;
403226031Sstas	ret = exec_stmt(context, s->db, SQL_CCREDS, KRB5_CC_IO);
404226031Sstas	if (ret) goto out;
405226031Sstas	ret = exec_stmt(context, s->db, SQL_CPRINCIPALS, KRB5_CC_IO);
406226031Sstas	if (ret) goto out;
407226031Sstas	ret = exec_stmt(context, s->db, SQL_SETUP_MASTER, KRB5_CC_IO);
408226031Sstas	if (ret) goto out;
409226031Sstas
410226031Sstas	ret = exec_stmt(context, s->db, SQL_TCACHE, KRB5_CC_IO);
411226031Sstas	if (ret) goto out;
412226031Sstas	ret = exec_stmt(context, s->db, SQL_TCRED, KRB5_CC_IO);
413226031Sstas	if (ret) goto out;
414226031Sstas    }
415226031Sstas
416226031Sstas#ifdef TRACEME
417226031Sstas    sqlite3_trace(s->db, trace, NULL);
418226031Sstas#endif
419226031Sstas
420226031Sstas    ret = prepare_stmt(context, s->db, &s->icred, SQL_ICRED);
421226031Sstas    if (ret) goto out;
422226031Sstas    ret = prepare_stmt(context, s->db, &s->dcred, SQL_DCRED);
423226031Sstas    if (ret) goto out;
424226031Sstas    ret = prepare_stmt(context, s->db, &s->iprincipal, SQL_IPRINCIPAL);
425226031Sstas    if (ret) goto out;
426226031Sstas    ret = prepare_stmt(context, s->db, &s->icache, SQL_ICACHE);
427226031Sstas    if (ret) goto out;
428226031Sstas    ret = prepare_stmt(context, s->db, &s->ucachen, SQL_UCACHE_NAME);
429226031Sstas    if (ret) goto out;
430226031Sstas    ret = prepare_stmt(context, s->db, &s->ucachep, SQL_UCACHE_PRINCIPAL);
431226031Sstas    if (ret) goto out;
432226031Sstas    ret = prepare_stmt(context, s->db, &s->dcache, SQL_DCACHE);
433226031Sstas    if (ret) goto out;
434226031Sstas    ret = prepare_stmt(context, s->db, &s->scache, SQL_SCACHE);
435226031Sstas    if (ret) goto out;
436226031Sstas    ret = prepare_stmt(context, s->db, &s->scache_name, SQL_SCACHE_NAME);
437226031Sstas    if (ret) goto out;
438226031Sstas    ret = prepare_stmt(context, s->db, &s->umaster, SQL_UMASTER);
439226031Sstas    if (ret) goto out;
440226031Sstas
441226031Sstas    return 0;
442226031Sstas
443226031Sstasout:
444226031Sstas    if (s->db)
445226031Sstas	sqlite3_close(s->db);
446226031Sstas    if (created_file)
447226031Sstas	unlink(s->file);
448226031Sstas
449226031Sstas    return ret;
450226031Sstas}
451226031Sstas
452226031Sstasstatic krb5_error_code
453226031Sstasbind_principal(krb5_context context,
454226031Sstas	       sqlite3 *db,
455226031Sstas	       sqlite3_stmt *stmt,
456226031Sstas	       int col,
457226031Sstas	       krb5_const_principal principal)
458226031Sstas{
459226031Sstas    krb5_error_code ret;
460226031Sstas    char *str;
461226031Sstas
462226031Sstas    ret = krb5_unparse_name(context, principal, &str);
463226031Sstas    if (ret)
464226031Sstas	return ret;
465226031Sstas
466226031Sstas    ret = sqlite3_bind_text(stmt, col, str, -1, free_krb5);
467226031Sstas    if (ret != SQLITE_OK) {
468226031Sstas	krb5_xfree(str);
469226031Sstas	krb5_set_error_message(context, ENOMEM,
470226031Sstas			       N_("scache bind principal: %s", ""),
471226031Sstas			       sqlite3_errmsg(db));
472226031Sstas	return ENOMEM;
473226031Sstas    }
474226031Sstas    return 0;
475226031Sstas}
476226031Sstas
477226031Sstas/*
478226031Sstas *
479226031Sstas */
480226031Sstas
481226031Sstasstatic const char* KRB5_CALLCONV
482226031Sstasscc_get_name(krb5_context context,
483226031Sstas	     krb5_ccache id)
484226031Sstas{
485226031Sstas    return SCACHE(id)->name;
486226031Sstas}
487226031Sstas
488226031Sstasstatic krb5_error_code KRB5_CALLCONV
489226031Sstasscc_resolve(krb5_context context, krb5_ccache *id, const char *res)
490226031Sstas{
491226031Sstas    krb5_scache *s;
492226031Sstas    int ret;
493226031Sstas
494226031Sstas    s = scc_alloc(context, res);
495226031Sstas    if (s == NULL) {
496226031Sstas	krb5_set_error_message(context, KRB5_CC_NOMEM,
497226031Sstas			       N_("malloc: out of memory", ""));
498226031Sstas	return KRB5_CC_NOMEM;
499226031Sstas    }
500226031Sstas
501226031Sstas    ret = make_database(context, s);
502226031Sstas    if (ret) {
503226031Sstas	scc_free(s);
504226031Sstas	return ret;
505226031Sstas    }
506226031Sstas
507226031Sstas    ret = sqlite3_bind_text(s->scache_name, 1, s->name, -1, NULL);
508226031Sstas    if (ret != SQLITE_OK) {
509226031Sstas	krb5_set_error_message(context, ENOMEM,
510226031Sstas			       "bind name: %s", sqlite3_errmsg(s->db));
511226031Sstas	scc_free(s);
512226031Sstas	return ENOMEM;
513226031Sstas    }
514226031Sstas
515226031Sstas    if (sqlite3_step(s->scache_name) == SQLITE_ROW) {
516226031Sstas
517226031Sstas	if (sqlite3_column_type(s->scache_name, 0) != SQLITE_INTEGER) {
518226031Sstas	    sqlite3_reset(s->scache_name);
519226031Sstas	    krb5_set_error_message(context, KRB5_CC_END,
520226031Sstas				   N_("Cache name of wrong type "
521226031Sstas				      "for scache %s", ""),
522226031Sstas				   s->name);
523226031Sstas	    scc_free(s);
524226031Sstas	    return KRB5_CC_END;
525226031Sstas	}
526226031Sstas
527226031Sstas	s->cid = sqlite3_column_int(s->scache_name, 0);
528226031Sstas    } else {
529226031Sstas	s->cid = SCACHE_INVALID_CID;
530226031Sstas    }
531226031Sstas    sqlite3_reset(s->scache_name);
532226031Sstas
533226031Sstas    (*id)->data.data = s;
534226031Sstas    (*id)->data.length = sizeof(*s);
535226031Sstas
536226031Sstas    return 0;
537226031Sstas}
538226031Sstas
539226031Sstasstatic krb5_error_code KRB5_CALLCONV
540226031Sstasscc_gen_new(krb5_context context, krb5_ccache *id)
541226031Sstas{
542226031Sstas    krb5_scache *s;
543226031Sstas
544226031Sstas    s = scc_alloc(context, NULL);
545226031Sstas
546226031Sstas    if (s == NULL) {
547226031Sstas	krb5_set_error_message(context, KRB5_CC_NOMEM,
548226031Sstas			       N_("malloc: out of memory", ""));
549226031Sstas	return KRB5_CC_NOMEM;
550226031Sstas    }
551226031Sstas
552226031Sstas    (*id)->data.data = s;
553226031Sstas    (*id)->data.length = sizeof(*s);
554226031Sstas
555226031Sstas    return 0;
556226031Sstas}
557226031Sstas
558226031Sstasstatic krb5_error_code KRB5_CALLCONV
559226031Sstasscc_initialize(krb5_context context,
560226031Sstas	       krb5_ccache id,
561226031Sstas	       krb5_principal primary_principal)
562226031Sstas{
563226031Sstas    krb5_scache *s = SCACHE(id);
564226031Sstas    krb5_error_code ret;
565226031Sstas
566226031Sstas    ret = make_database(context, s);
567226031Sstas    if (ret)
568226031Sstas	return ret;
569226031Sstas
570226031Sstas    ret = exec_stmt(context, s->db, "BEGIN IMMEDIATE TRANSACTION", KRB5_CC_IO);
571226031Sstas    if (ret) return ret;
572226031Sstas
573226031Sstas    if (s->cid == SCACHE_INVALID_CID) {
574226031Sstas	ret = create_cache(context, s);
575226031Sstas	if (ret)
576226031Sstas	    goto rollback;
577226031Sstas    } else {
578226031Sstas	sqlite3_bind_int(s->dcred, 1, s->cid);
579226031Sstas	do {
580226031Sstas	    ret = sqlite3_step(s->dcred);
581226031Sstas	} while (ret == SQLITE_ROW);
582226031Sstas	sqlite3_reset(s->dcred);
583226031Sstas	if (ret != SQLITE_DONE) {
584226031Sstas	    ret = KRB5_CC_IO;
585226031Sstas	    krb5_set_error_message(context, ret,
586226031Sstas				   N_("Failed to delete old "
587226031Sstas				      "credentials: %s", ""),
588226031Sstas				   sqlite3_errmsg(s->db));
589226031Sstas	    goto rollback;
590226031Sstas	}
591226031Sstas    }
592226031Sstas
593226031Sstas    ret = bind_principal(context, s->db, s->ucachep, 1, primary_principal);
594226031Sstas    if (ret)
595226031Sstas	goto rollback;
596226031Sstas    sqlite3_bind_int(s->ucachep, 2, s->cid);
597226031Sstas
598226031Sstas    do {
599226031Sstas	ret = sqlite3_step(s->ucachep);
600226031Sstas    } while (ret == SQLITE_ROW);
601226031Sstas    sqlite3_reset(s->ucachep);
602226031Sstas    if (ret != SQLITE_DONE) {
603226031Sstas	ret = KRB5_CC_IO;
604226031Sstas	krb5_set_error_message(context, ret,
605226031Sstas			       N_("Failed to bind principal to cache %s", ""),
606226031Sstas			       sqlite3_errmsg(s->db));
607226031Sstas	goto rollback;
608226031Sstas    }
609226031Sstas
610226031Sstas    ret = exec_stmt(context, s->db, "COMMIT", KRB5_CC_IO);
611226031Sstas    if (ret) return ret;
612226031Sstas
613226031Sstas    return 0;
614226031Sstas
615226031Sstasrollback:
616226031Sstas    exec_stmt(context, s->db, "ROLLBACK", 0);
617226031Sstas
618226031Sstas    return ret;
619226031Sstas
620226031Sstas}
621226031Sstas
622226031Sstasstatic krb5_error_code KRB5_CALLCONV
623226031Sstasscc_close(krb5_context context,
624226031Sstas	  krb5_ccache id)
625226031Sstas{
626226031Sstas    scc_free(SCACHE(id));
627226031Sstas    return 0;
628226031Sstas}
629226031Sstas
630226031Sstasstatic krb5_error_code KRB5_CALLCONV
631226031Sstasscc_destroy(krb5_context context,
632226031Sstas	    krb5_ccache id)
633226031Sstas{
634226031Sstas    krb5_scache *s = SCACHE(id);
635226031Sstas    int ret;
636226031Sstas
637226031Sstas    if (s->cid == SCACHE_INVALID_CID)
638226031Sstas	return 0;
639226031Sstas
640226031Sstas    sqlite3_bind_int(s->dcache, 1, s->cid);
641226031Sstas    do {
642226031Sstas	ret = sqlite3_step(s->dcache);
643226031Sstas    } while (ret == SQLITE_ROW);
644226031Sstas    sqlite3_reset(s->dcache);
645226031Sstas    if (ret != SQLITE_DONE) {
646226031Sstas	krb5_set_error_message(context, KRB5_CC_IO,
647226031Sstas			       N_("Failed to destroy cache %s: %s", ""),
648226031Sstas			       s->name, sqlite3_errmsg(s->db));
649226031Sstas	return KRB5_CC_IO;
650226031Sstas    }
651226031Sstas    return 0;
652226031Sstas}
653226031Sstas
654226031Sstasstatic krb5_error_code
655226031Sstasencode_creds(krb5_context context, krb5_creds *creds, krb5_data *data)
656226031Sstas{
657226031Sstas    krb5_error_code ret;
658226031Sstas    krb5_storage *sp;
659226031Sstas
660226031Sstas    sp = krb5_storage_emem();
661226031Sstas    if (sp == NULL) {
662226031Sstas	krb5_set_error_message(context, ENOMEM,
663226031Sstas			       N_("malloc: out of memory", ""));
664226031Sstas	return ENOMEM;
665226031Sstas    }
666226031Sstas
667226031Sstas    ret = krb5_store_creds(sp, creds);
668226031Sstas    if (ret) {
669226031Sstas	krb5_set_error_message(context, ret,
670226031Sstas			       N_("Failed to store credential in scache", ""));
671226031Sstas	krb5_storage_free(sp);
672226031Sstas	return ret;
673226031Sstas    }
674226031Sstas
675226031Sstas    ret = krb5_storage_to_data(sp, data);
676226031Sstas    krb5_storage_free(sp);
677226031Sstas    if (ret)
678226031Sstas	krb5_set_error_message(context, ret,
679226031Sstas			       N_("Failed to encode credential in scache", ""));
680226031Sstas    return ret;
681226031Sstas}
682226031Sstas
683226031Sstasstatic krb5_error_code
684226031Sstasdecode_creds(krb5_context context, const void *data, size_t length,
685226031Sstas	     krb5_creds *creds)
686226031Sstas{
687226031Sstas    krb5_error_code ret;
688226031Sstas    krb5_storage *sp;
689226031Sstas
690226031Sstas    sp = krb5_storage_from_readonly_mem(data, length);
691226031Sstas    if (sp == NULL) {
692226031Sstas	krb5_set_error_message(context, ENOMEM,
693226031Sstas			       N_("malloc: out of memory", ""));
694226031Sstas	return ENOMEM;
695226031Sstas    }
696226031Sstas
697226031Sstas    ret = krb5_ret_creds(sp, creds);
698226031Sstas    krb5_storage_free(sp);
699226031Sstas    if (ret) {
700226031Sstas	krb5_set_error_message(context, ret,
701226031Sstas			       N_("Failed to read credential in scache", ""));
702226031Sstas	return ret;
703226031Sstas    }
704226031Sstas    return 0;
705226031Sstas}
706226031Sstas
707226031Sstas
708226031Sstasstatic krb5_error_code KRB5_CALLCONV
709226031Sstasscc_store_cred(krb5_context context,
710226031Sstas	       krb5_ccache id,
711226031Sstas	       krb5_creds *creds)
712226031Sstas{
713226031Sstas    sqlite_uint64 credid;
714226031Sstas    krb5_scache *s = SCACHE(id);
715226031Sstas    krb5_error_code ret;
716226031Sstas    krb5_data data;
717226031Sstas
718226031Sstas    ret = make_database(context, s);
719226031Sstas    if (ret)
720226031Sstas	return ret;
721226031Sstas
722226031Sstas    ret = encode_creds(context, creds, &data);
723226031Sstas    if (ret)
724226031Sstas	return ret;
725226031Sstas
726226031Sstas    sqlite3_bind_int(s->icred, 1, s->cid);
727226031Sstas    {
728226031Sstas	krb5_enctype etype = 0;
729226031Sstas	int kvno = 0;
730226031Sstas	Ticket t;
731226031Sstas	size_t len;
732226031Sstas
733226031Sstas	ret = decode_Ticket(creds->ticket.data,
734226031Sstas			    creds->ticket.length, &t, &len);
735226031Sstas	if (ret == 0) {
736226031Sstas	    if(t.enc_part.kvno)
737226031Sstas		kvno = *t.enc_part.kvno;
738226031Sstas
739226031Sstas	    etype = t.enc_part.etype;
740226031Sstas
741226031Sstas	    free_Ticket(&t);
742226031Sstas	}
743226031Sstas
744226031Sstas	sqlite3_bind_int(s->icred, 2, kvno);
745226031Sstas	sqlite3_bind_int(s->icred, 3, etype);
746226031Sstas
747226031Sstas    }
748226031Sstas
749226031Sstas    sqlite3_bind_blob(s->icred, 4, data.data, data.length, free_data);
750226031Sstas    sqlite3_bind_int(s->icred, 5, time(NULL));
751226031Sstas
752226031Sstas    ret = exec_stmt(context, s->db, "BEGIN IMMEDIATE TRANSACTION", KRB5_CC_IO);
753226031Sstas    if (ret) return ret;
754226031Sstas
755226031Sstas    do {
756226031Sstas	ret = sqlite3_step(s->icred);
757226031Sstas    } while (ret == SQLITE_ROW);
758226031Sstas    sqlite3_reset(s->icred);
759226031Sstas    if (ret != SQLITE_DONE) {
760226031Sstas	ret = KRB5_CC_IO;
761226031Sstas	krb5_set_error_message(context, ret,
762226031Sstas			       N_("Failed to add credential: %s", ""),
763226031Sstas			       sqlite3_errmsg(s->db));
764226031Sstas	goto rollback;
765226031Sstas    }
766226031Sstas
767226031Sstas    credid = sqlite3_last_insert_rowid(s->db);
768226031Sstas
769226031Sstas    {
770226031Sstas	bind_principal(context, s->db, s->iprincipal, 1, creds->server);
771226031Sstas	sqlite3_bind_int(s->iprincipal, 2, 1);
772226031Sstas	sqlite3_bind_int(s->iprincipal, 3, credid);
773226031Sstas
774226031Sstas	do {
775226031Sstas	    ret = sqlite3_step(s->iprincipal);
776226031Sstas	} while (ret == SQLITE_ROW);
777226031Sstas	sqlite3_reset(s->iprincipal);
778226031Sstas	if (ret != SQLITE_DONE) {
779226031Sstas	    ret = KRB5_CC_IO;
780226031Sstas	    krb5_set_error_message(context, ret,
781226031Sstas				   N_("Failed to add principal: %s", ""),
782226031Sstas				   sqlite3_errmsg(s->db));
783226031Sstas	    goto rollback;
784226031Sstas	}
785226031Sstas    }
786226031Sstas
787226031Sstas    {
788226031Sstas	bind_principal(context, s->db, s->iprincipal, 1, creds->client);
789226031Sstas	sqlite3_bind_int(s->iprincipal, 2, 0);
790226031Sstas	sqlite3_bind_int(s->iprincipal, 3, credid);
791226031Sstas
792226031Sstas	do {
793226031Sstas	    ret = sqlite3_step(s->iprincipal);
794226031Sstas	} while (ret == SQLITE_ROW);
795226031Sstas	sqlite3_reset(s->iprincipal);
796226031Sstas	if (ret != SQLITE_DONE) {
797226031Sstas	    ret = KRB5_CC_IO;
798226031Sstas	    krb5_set_error_message(context, ret,
799226031Sstas				   N_("Failed to add principal: %s", ""),
800226031Sstas				   sqlite3_errmsg(s->db));
801226031Sstas	    goto rollback;
802226031Sstas	}
803226031Sstas    }
804226031Sstas
805226031Sstas    ret = exec_stmt(context, s->db, "COMMIT", KRB5_CC_IO);
806226031Sstas    if (ret) return ret;
807226031Sstas
808226031Sstas    return 0;
809226031Sstas
810226031Sstasrollback:
811226031Sstas    exec_stmt(context, s->db, "ROLLBACK", 0);
812226031Sstas
813226031Sstas    return ret;
814226031Sstas}
815226031Sstas
816226031Sstasstatic krb5_error_code KRB5_CALLCONV
817226031Sstasscc_get_principal(krb5_context context,
818226031Sstas		  krb5_ccache id,
819226031Sstas		  krb5_principal *principal)
820226031Sstas{
821226031Sstas    krb5_scache *s = SCACHE(id);
822226031Sstas    krb5_error_code ret;
823226031Sstas    const char *str;
824226031Sstas
825226031Sstas    *principal = NULL;
826226031Sstas
827226031Sstas    ret = make_database(context, s);
828226031Sstas    if (ret)
829226031Sstas	return ret;
830226031Sstas
831226031Sstas    sqlite3_bind_int(s->scache, 1, s->cid);
832226031Sstas
833226031Sstas    if (sqlite3_step(s->scache) != SQLITE_ROW) {
834226031Sstas	sqlite3_reset(s->scache);
835226031Sstas	krb5_set_error_message(context, KRB5_CC_END,
836226031Sstas			       N_("No principal for cache SCC:%s:%s", ""),
837226031Sstas			       s->name, s->file);
838226031Sstas	return KRB5_CC_END;
839226031Sstas    }
840226031Sstas
841226031Sstas    if (sqlite3_column_type(s->scache, 0) != SQLITE_TEXT) {
842226031Sstas	sqlite3_reset(s->scache);
843226031Sstas	krb5_set_error_message(context, KRB5_CC_END,
844226031Sstas			       N_("Principal data of wrong type "
845226031Sstas				  "for SCC:%s:%s", ""),
846226031Sstas			       s->name, s->file);
847226031Sstas	return KRB5_CC_END;
848226031Sstas    }
849226031Sstas
850226031Sstas    str = (const char *)sqlite3_column_text(s->scache, 0);
851226031Sstas    if (str == NULL) {
852226031Sstas	sqlite3_reset(s->scache);
853226031Sstas	krb5_set_error_message(context, KRB5_CC_END,
854226031Sstas			       N_("Principal not set for SCC:%s:%s", ""),
855226031Sstas			       s->name, s->file);
856226031Sstas	return KRB5_CC_END;
857226031Sstas    }
858226031Sstas
859226031Sstas    ret = krb5_parse_name(context, str, principal);
860226031Sstas
861226031Sstas    sqlite3_reset(s->scache);
862226031Sstas
863226031Sstas    return ret;
864226031Sstas}
865226031Sstas
866226031Sstasstruct cred_ctx {
867226031Sstas    char *drop;
868226031Sstas    sqlite3_stmt *stmt;
869226031Sstas    sqlite3_stmt *credstmt;
870226031Sstas};
871226031Sstas
872226031Sstasstatic krb5_error_code KRB5_CALLCONV
873226031Sstasscc_get_first (krb5_context context,
874226031Sstas	       krb5_ccache id,
875226031Sstas	       krb5_cc_cursor *cursor)
876226031Sstas{
877226031Sstas    krb5_scache *s = SCACHE(id);
878226031Sstas    krb5_error_code ret;
879226031Sstas    struct cred_ctx *ctx;
880226031Sstas    char *str = NULL, *name = NULL;
881226031Sstas
882226031Sstas    *cursor = NULL;
883226031Sstas
884226031Sstas    ctx = calloc(1, sizeof(*ctx));
885226031Sstas    if (ctx == NULL) {
886226031Sstas	krb5_set_error_message(context, ENOMEM,
887226031Sstas			       N_("malloc: out of memory", ""));
888226031Sstas	return ENOMEM;
889226031Sstas    }
890226031Sstas
891226031Sstas    ret = make_database(context, s);
892226031Sstas    if (ret) {
893226031Sstas	free(ctx);
894226031Sstas	return ret;
895226031Sstas    }
896226031Sstas
897226031Sstas    if (s->cid == SCACHE_INVALID_CID) {
898226031Sstas	krb5_set_error_message(context, KRB5_CC_END,
899226031Sstas			       N_("Iterating a invalid scache %s", ""),
900226031Sstas			       s->name);
901226031Sstas	free(ctx);
902226031Sstas	return KRB5_CC_END;
903226031Sstas    }
904226031Sstas
905226031Sstas    ret = asprintf(&name, "credIteration%pPid%d",
906226031Sstas                   ctx, (int)getpid());
907226031Sstas    if (ret < 0 || name == NULL) {
908226031Sstas	krb5_set_error_message(context, ENOMEM,
909226031Sstas			       N_("malloc: out of memory", ""));
910226031Sstas	free(ctx);
911226031Sstas	return ENOMEM;
912226031Sstas    }
913226031Sstas
914226031Sstas    ret = asprintf(&ctx->drop, "DROP TABLE %s", name);
915226031Sstas    if (ret < 0 || ctx->drop == NULL) {
916226031Sstas	krb5_set_error_message(context, ENOMEM,
917226031Sstas			       N_("malloc: out of memory", ""));
918226031Sstas	free(name);
919226031Sstas	free(ctx);
920226031Sstas	return ENOMEM;
921226031Sstas    }
922226031Sstas
923226031Sstas    ret = asprintf(&str, "CREATE TEMPORARY TABLE %s "
924226031Sstas	     "AS SELECT oid,created_at FROM credentials WHERE cid = %lu",
925226031Sstas	     name, (unsigned long)s->cid);
926226031Sstas    if (ret < 0 || str == NULL) {
927226031Sstas	free(ctx->drop);
928226031Sstas	free(name);
929226031Sstas	free(ctx);
930226031Sstas	return ENOMEM;
931226031Sstas    }
932226031Sstas
933226031Sstas    ret = exec_stmt(context, s->db, str, KRB5_CC_IO);
934226031Sstas    free(str);
935226031Sstas    str = NULL;
936226031Sstas    if (ret) {
937226031Sstas	free(ctx->drop);
938226031Sstas	free(name);
939226031Sstas	free(ctx);
940226031Sstas	return ret;
941226031Sstas    }
942226031Sstas
943226031Sstas    ret = asprintf(&str, "SELECT oid FROM %s ORDER BY created_at", name);
944226031Sstas    if (ret < 0 || str == NULL) {
945226031Sstas	exec_stmt(context, s->db, ctx->drop, 0);
946226031Sstas	free(ctx->drop);
947226031Sstas	free(name);
948226031Sstas	free(ctx);
949226031Sstas	return ret;
950226031Sstas    }
951226031Sstas
952226031Sstas    ret = prepare_stmt(context, s->db, &ctx->stmt, str);
953226031Sstas    free(str);
954226031Sstas    str = NULL;
955226031Sstas    free(name);
956226031Sstas    if (ret) {
957226031Sstas	exec_stmt(context, s->db, ctx->drop, 0);
958226031Sstas	free(ctx->drop);
959226031Sstas	free(ctx);
960226031Sstas	return ret;
961226031Sstas    }
962226031Sstas
963226031Sstas    ret = prepare_stmt(context, s->db, &ctx->credstmt,
964226031Sstas		       "SELECT cred FROM credentials WHERE oid = ?");
965226031Sstas    if (ret) {
966226031Sstas	sqlite3_finalize(ctx->stmt);
967226031Sstas	exec_stmt(context, s->db, ctx->drop, 0);
968226031Sstas	free(ctx->drop);
969226031Sstas	free(ctx);
970226031Sstas	return ret;
971226031Sstas    }
972226031Sstas
973226031Sstas    *cursor = ctx;
974226031Sstas
975226031Sstas    return 0;
976226031Sstas}
977226031Sstas
978226031Sstasstatic krb5_error_code KRB5_CALLCONV
979226031Sstasscc_get_next (krb5_context context,
980226031Sstas	      krb5_ccache id,
981226031Sstas	      krb5_cc_cursor *cursor,
982226031Sstas	      krb5_creds *creds)
983226031Sstas{
984226031Sstas    struct cred_ctx *ctx = *cursor;
985226031Sstas    krb5_scache *s = SCACHE(id);
986226031Sstas    krb5_error_code ret;
987226031Sstas    sqlite_uint64 oid;
988226031Sstas    const void *data = NULL;
989226031Sstas    size_t len = 0;
990226031Sstas
991226031Sstasnext:
992226031Sstas    ret = sqlite3_step(ctx->stmt);
993226031Sstas    if (ret == SQLITE_DONE) {
994226031Sstas	krb5_clear_error_message(context);
995226031Sstas        return KRB5_CC_END;
996226031Sstas    } else if (ret != SQLITE_ROW) {
997226031Sstas	krb5_set_error_message(context, KRB5_CC_IO,
998226031Sstas			       N_("scache Database failed: %s", ""),
999226031Sstas			       sqlite3_errmsg(s->db));
1000226031Sstas        return KRB5_CC_IO;
1001226031Sstas    }
1002226031Sstas
1003226031Sstas    oid = sqlite3_column_int64(ctx->stmt, 0);
1004226031Sstas
1005226031Sstas    /* read cred from credentials table */
1006226031Sstas
1007226031Sstas    sqlite3_bind_int(ctx->credstmt, 1, oid);
1008226031Sstas
1009226031Sstas    ret = sqlite3_step(ctx->credstmt);
1010226031Sstas    if (ret != SQLITE_ROW) {
1011226031Sstas	sqlite3_reset(ctx->credstmt);
1012226031Sstas	goto next;
1013226031Sstas    }
1014226031Sstas
1015226031Sstas    if (sqlite3_column_type(ctx->credstmt, 0) != SQLITE_BLOB) {
1016226031Sstas	krb5_set_error_message(context, KRB5_CC_END,
1017226031Sstas			       N_("credential of wrong type for SCC:%s:%s", ""),
1018226031Sstas			       s->name, s->file);
1019226031Sstas	sqlite3_reset(ctx->credstmt);
1020226031Sstas	return KRB5_CC_END;
1021226031Sstas    }
1022226031Sstas
1023226031Sstas    data = sqlite3_column_blob(ctx->credstmt, 0);
1024226031Sstas    len = sqlite3_column_bytes(ctx->credstmt, 0);
1025226031Sstas
1026226031Sstas    ret = decode_creds(context, data, len, creds);
1027226031Sstas    sqlite3_reset(ctx->credstmt);
1028226031Sstas    return ret;
1029226031Sstas}
1030226031Sstas
1031226031Sstasstatic krb5_error_code KRB5_CALLCONV
1032226031Sstasscc_end_get (krb5_context context,
1033226031Sstas	     krb5_ccache id,
1034226031Sstas	     krb5_cc_cursor *cursor)
1035226031Sstas{
1036226031Sstas    struct cred_ctx *ctx = *cursor;
1037226031Sstas    krb5_scache *s = SCACHE(id);
1038226031Sstas
1039226031Sstas    sqlite3_finalize(ctx->stmt);
1040226031Sstas    sqlite3_finalize(ctx->credstmt);
1041226031Sstas
1042226031Sstas    exec_stmt(context, s->db, ctx->drop, 0);
1043226031Sstas
1044226031Sstas    free(ctx->drop);
1045226031Sstas    free(ctx);
1046226031Sstas
1047226031Sstas    return 0;
1048226031Sstas}
1049226031Sstas
1050226031Sstasstatic krb5_error_code KRB5_CALLCONV
1051226031Sstasscc_remove_cred(krb5_context context,
1052226031Sstas		 krb5_ccache id,
1053226031Sstas		 krb5_flags which,
1054226031Sstas		 krb5_creds *mcreds)
1055226031Sstas{
1056226031Sstas    krb5_scache *s = SCACHE(id);
1057226031Sstas    krb5_error_code ret;
1058226031Sstas    sqlite3_stmt *stmt;
1059226031Sstas    sqlite_uint64 credid = 0;
1060226031Sstas    const void *data = NULL;
1061226031Sstas    size_t len = 0;
1062226031Sstas
1063226031Sstas    ret = make_database(context, s);
1064226031Sstas    if (ret)
1065226031Sstas	return ret;
1066226031Sstas
1067226031Sstas    ret = prepare_stmt(context, s->db, &stmt,
1068226031Sstas		       "SELECT cred,oid FROM credentials "
1069226031Sstas		       "WHERE cid = ?");
1070226031Sstas    if (ret)
1071226031Sstas	return ret;
1072226031Sstas
1073226031Sstas    sqlite3_bind_int(stmt, 1, s->cid);
1074226031Sstas
1075226031Sstas    /* find credential... */
1076226031Sstas    while (1) {
1077226031Sstas	krb5_creds creds;
1078226031Sstas
1079226031Sstas	ret = sqlite3_step(stmt);
1080226031Sstas	if (ret == SQLITE_DONE) {
1081226031Sstas	    ret = 0;
1082226031Sstas	    break;
1083226031Sstas	} else if (ret != SQLITE_ROW) {
1084226031Sstas	    ret = KRB5_CC_IO;
1085226031Sstas	    krb5_set_error_message(context, ret,
1086226031Sstas				   N_("scache Database failed: %s", ""),
1087226031Sstas				   sqlite3_errmsg(s->db));
1088226031Sstas	    break;
1089226031Sstas	}
1090226031Sstas
1091226031Sstas	if (sqlite3_column_type(stmt, 0) != SQLITE_BLOB) {
1092226031Sstas	    ret = KRB5_CC_END;
1093226031Sstas	    krb5_set_error_message(context, ret,
1094226031Sstas				   N_("Credential of wrong type "
1095226031Sstas				      "for SCC:%s:%s", ""),
1096226031Sstas				   s->name, s->file);
1097226031Sstas	    break;
1098226031Sstas	}
1099226031Sstas
1100226031Sstas	data = sqlite3_column_blob(stmt, 0);
1101226031Sstas	len = sqlite3_column_bytes(stmt, 0);
1102226031Sstas
1103226031Sstas	ret = decode_creds(context, data, len, &creds);
1104226031Sstas	if (ret)
1105226031Sstas	    break;
1106226031Sstas
1107226031Sstas	ret = krb5_compare_creds(context, which, mcreds, &creds);
1108226031Sstas	krb5_free_cred_contents(context, &creds);
1109226031Sstas	if (ret) {
1110226031Sstas	    credid = sqlite3_column_int64(stmt, 1);
1111226031Sstas	    ret = 0;
1112226031Sstas	    break;
1113226031Sstas	}
1114226031Sstas    }
1115226031Sstas
1116226031Sstas    sqlite3_finalize(stmt);
1117226031Sstas
1118226031Sstas    if (id) {
1119226031Sstas	ret = prepare_stmt(context, s->db, &stmt,
1120226031Sstas			   "DELETE FROM credentials WHERE oid=?");
1121226031Sstas	if (ret)
1122226031Sstas	    return ret;
1123226031Sstas	sqlite3_bind_int(stmt, 1, credid);
1124226031Sstas
1125226031Sstas	do {
1126226031Sstas	    ret = sqlite3_step(stmt);
1127226031Sstas	} while (ret == SQLITE_ROW);
1128226031Sstas	sqlite3_finalize(stmt);
1129226031Sstas	if (ret != SQLITE_DONE) {
1130226031Sstas	    ret = KRB5_CC_IO;
1131226031Sstas	    krb5_set_error_message(context, ret,
1132226031Sstas				   N_("failed to delete scache credental", ""));
1133226031Sstas	} else
1134226031Sstas	    ret = 0;
1135226031Sstas    }
1136226031Sstas
1137226031Sstas    return ret;
1138226031Sstas}
1139226031Sstas
1140226031Sstasstatic krb5_error_code KRB5_CALLCONV
1141226031Sstasscc_set_flags(krb5_context context,
1142226031Sstas	      krb5_ccache id,
1143226031Sstas	      krb5_flags flags)
1144226031Sstas{
1145226031Sstas    return 0; /* XXX */
1146226031Sstas}
1147226031Sstas
1148226031Sstasstruct cache_iter {
1149226031Sstas    char *drop;
1150226031Sstas    sqlite3 *db;
1151226031Sstas    sqlite3_stmt *stmt;
1152226031Sstas};
1153226031Sstas
1154226031Sstasstatic krb5_error_code KRB5_CALLCONV
1155226031Sstasscc_get_cache_first(krb5_context context, krb5_cc_cursor *cursor)
1156226031Sstas{
1157226031Sstas    struct cache_iter *ctx;
1158226031Sstas    krb5_error_code ret;
1159226031Sstas    char *name = NULL, *str = NULL;
1160226031Sstas
1161226031Sstas    *cursor = NULL;
1162226031Sstas
1163226031Sstas    ctx = calloc(1, sizeof(*ctx));
1164226031Sstas    if (ctx == NULL) {
1165226031Sstas	krb5_set_error_message(context, ENOMEM,
1166226031Sstas			       N_("malloc: out of memory", ""));
1167226031Sstas	return ENOMEM;
1168226031Sstas    }
1169226031Sstas
1170226031Sstas    ret = default_db(context, &ctx->db);
1171226031Sstas    if (ctx->db == NULL) {
1172226031Sstas	free(ctx);
1173226031Sstas	return ret;
1174226031Sstas    }
1175226031Sstas
1176226031Sstas    ret = asprintf(&name, "cacheIteration%pPid%d",
1177226031Sstas                   ctx, (int)getpid());
1178226031Sstas    if (ret < 0 || name == NULL) {
1179226031Sstas	krb5_set_error_message(context, ENOMEM,
1180226031Sstas			       N_("malloc: out of memory", ""));
1181226031Sstas	sqlite3_close(ctx->db);
1182226031Sstas	free(ctx);
1183226031Sstas	return ENOMEM;
1184226031Sstas    }
1185226031Sstas
1186226031Sstas    ret = asprintf(&ctx->drop, "DROP TABLE %s", name);
1187226031Sstas    if (ret < 0 || ctx->drop == NULL) {
1188226031Sstas	krb5_set_error_message(context, ENOMEM,
1189226031Sstas			       N_("malloc: out of memory", ""));
1190226031Sstas	sqlite3_close(ctx->db);
1191226031Sstas	free(name);
1192226031Sstas	free(ctx);
1193226031Sstas	return ENOMEM;
1194226031Sstas    }
1195226031Sstas
1196226031Sstas    ret = asprintf(&str, "CREATE TEMPORARY TABLE %s AS SELECT name FROM caches",
1197226031Sstas	     name);
1198226031Sstas    if (ret < 0 || str == NULL) {
1199226031Sstas	krb5_set_error_message(context, ENOMEM,
1200226031Sstas			       N_("malloc: out of memory", ""));
1201226031Sstas	sqlite3_close(ctx->db);
1202226031Sstas	free(name);
1203226031Sstas	free(ctx->drop);
1204226031Sstas	free(ctx);
1205226031Sstas	return ENOMEM;
1206226031Sstas    }
1207226031Sstas
1208226031Sstas    ret = exec_stmt(context, ctx->db, str, KRB5_CC_IO);
1209226031Sstas    free(str);
1210226031Sstas    str = NULL;
1211226031Sstas    if (ret) {
1212226031Sstas	sqlite3_close(ctx->db);
1213226031Sstas	free(name);
1214226031Sstas	free(ctx->drop);
1215226031Sstas	free(ctx);
1216226031Sstas	return ret;
1217226031Sstas    }
1218226031Sstas
1219226031Sstas    ret = asprintf(&str, "SELECT name FROM %s", name);
1220226031Sstas    free(name);
1221226031Sstas    if (ret < 0 || str == NULL) {
1222226031Sstas	exec_stmt(context, ctx->db, ctx->drop, 0);
1223226031Sstas	sqlite3_close(ctx->db);
1224226031Sstas	free(name);
1225226031Sstas	free(ctx->drop);
1226226031Sstas	free(ctx);
1227226031Sstas	return ENOMEM;
1228226031Sstas    }
1229226031Sstas
1230226031Sstas    ret = prepare_stmt(context, ctx->db, &ctx->stmt, str);
1231226031Sstas    free(str);
1232226031Sstas    if (ret) {
1233226031Sstas	exec_stmt(context, ctx->db, ctx->drop, 0);
1234226031Sstas	sqlite3_close(ctx->db);
1235226031Sstas	free(ctx->drop);
1236226031Sstas	free(ctx);
1237226031Sstas	return ret;
1238226031Sstas    }
1239226031Sstas
1240226031Sstas    *cursor = ctx;
1241226031Sstas
1242226031Sstas    return 0;
1243226031Sstas}
1244226031Sstas
1245226031Sstasstatic krb5_error_code KRB5_CALLCONV
1246226031Sstasscc_get_cache_next(krb5_context context,
1247226031Sstas		   krb5_cc_cursor cursor,
1248226031Sstas		   krb5_ccache *id)
1249226031Sstas{
1250226031Sstas    struct cache_iter *ctx = cursor;
1251226031Sstas    krb5_error_code ret;
1252226031Sstas    const char *name;
1253226031Sstas
1254226031Sstasagain:
1255226031Sstas    ret = sqlite3_step(ctx->stmt);
1256226031Sstas    if (ret == SQLITE_DONE) {
1257226031Sstas	krb5_clear_error_message(context);
1258226031Sstas        return KRB5_CC_END;
1259226031Sstas    } else if (ret != SQLITE_ROW) {
1260226031Sstas	krb5_set_error_message(context, KRB5_CC_IO,
1261226031Sstas			       N_("Database failed: %s", ""),
1262226031Sstas			       sqlite3_errmsg(ctx->db));
1263226031Sstas        return KRB5_CC_IO;
1264226031Sstas    }
1265226031Sstas
1266226031Sstas    if (sqlite3_column_type(ctx->stmt, 0) != SQLITE_TEXT)
1267226031Sstas	goto again;
1268226031Sstas
1269226031Sstas    name = (const char *)sqlite3_column_text(ctx->stmt, 0);
1270226031Sstas    if (name == NULL)
1271226031Sstas	goto again;
1272226031Sstas
1273226031Sstas    ret = _krb5_cc_allocate(context, &krb5_scc_ops, id);
1274226031Sstas    if (ret)
1275226031Sstas	return ret;
1276226031Sstas
1277226031Sstas    return scc_resolve(context, id, name);
1278226031Sstas}
1279226031Sstas
1280226031Sstasstatic krb5_error_code KRB5_CALLCONV
1281226031Sstasscc_end_cache_get(krb5_context context, krb5_cc_cursor cursor)
1282226031Sstas{
1283226031Sstas    struct cache_iter *ctx = cursor;
1284226031Sstas
1285226031Sstas    exec_stmt(context, ctx->db, ctx->drop, 0);
1286226031Sstas    sqlite3_finalize(ctx->stmt);
1287226031Sstas    sqlite3_close(ctx->db);
1288226031Sstas    free(ctx->drop);
1289226031Sstas    free(ctx);
1290226031Sstas    return 0;
1291226031Sstas}
1292226031Sstas
1293226031Sstasstatic krb5_error_code KRB5_CALLCONV
1294226031Sstasscc_move(krb5_context context, krb5_ccache from, krb5_ccache to)
1295226031Sstas{
1296226031Sstas    krb5_scache *sfrom = SCACHE(from);
1297226031Sstas    krb5_scache *sto = SCACHE(to);
1298226031Sstas    krb5_error_code ret;
1299226031Sstas
1300226031Sstas    if (strcmp(sfrom->file, sto->file) != 0) {
1301226031Sstas	krb5_set_error_message(context, KRB5_CC_BADNAME,
1302226031Sstas			       N_("Can't handle cross database "
1303226031Sstas				  "credential move: %s -> %s", ""),
1304226031Sstas			       sfrom->file, sto->file);
1305226031Sstas	return KRB5_CC_BADNAME;
1306226031Sstas    }
1307226031Sstas
1308226031Sstas    ret = make_database(context, sfrom);
1309226031Sstas    if (ret)
1310226031Sstas	return ret;
1311226031Sstas
1312226031Sstas    ret = exec_stmt(context, sfrom->db,
1313226031Sstas		    "BEGIN IMMEDIATE TRANSACTION", KRB5_CC_IO);
1314226031Sstas    if (ret) return ret;
1315226031Sstas
1316226031Sstas    if (sto->cid != SCACHE_INVALID_CID) {
1317226031Sstas	/* drop old cache entry */
1318226031Sstas
1319226031Sstas	sqlite3_bind_int(sfrom->dcache, 1, sto->cid);
1320226031Sstas	do {
1321226031Sstas	    ret = sqlite3_step(sfrom->dcache);
1322226031Sstas	} while (ret == SQLITE_ROW);
1323226031Sstas	sqlite3_reset(sfrom->dcache);
1324226031Sstas	if (ret != SQLITE_DONE) {
1325226031Sstas	    krb5_set_error_message(context, KRB5_CC_IO,
1326226031Sstas				   N_("Failed to delete old cache: %d", ""),
1327226031Sstas				   (int)ret);
1328226031Sstas	    goto rollback;
1329226031Sstas	}
1330226031Sstas    }
1331226031Sstas
1332226031Sstas    sqlite3_bind_text(sfrom->ucachen, 1, sto->name, -1, NULL);
1333226031Sstas    sqlite3_bind_int(sfrom->ucachen, 2, sfrom->cid);
1334226031Sstas
1335226031Sstas    do {
1336226031Sstas	ret = sqlite3_step(sfrom->ucachen);
1337226031Sstas    } while (ret == SQLITE_ROW);
1338226031Sstas    sqlite3_reset(sfrom->ucachen);
1339226031Sstas    if (ret != SQLITE_DONE) {
1340226031Sstas	krb5_set_error_message(context, KRB5_CC_IO,
1341226031Sstas			       N_("Failed to update new cache: %d", ""),
1342226031Sstas			       (int)ret);
1343226031Sstas	goto rollback;
1344226031Sstas    }
1345226031Sstas
1346226031Sstas    sto->cid = sfrom->cid;
1347226031Sstas
1348226031Sstas    ret = exec_stmt(context, sfrom->db, "COMMIT", KRB5_CC_IO);
1349226031Sstas    if (ret) return ret;
1350226031Sstas
1351226031Sstas    scc_free(sfrom);
1352226031Sstas
1353226031Sstas    return 0;
1354226031Sstas
1355226031Sstasrollback:
1356226031Sstas    exec_stmt(context, sfrom->db, "ROLLBACK", 0);
1357226031Sstas    scc_free(sfrom);
1358226031Sstas
1359226031Sstas    return KRB5_CC_IO;
1360226031Sstas}
1361226031Sstas
1362226031Sstasstatic krb5_error_code KRB5_CALLCONV
1363226031Sstasscc_get_default_name(krb5_context context, char **str)
1364226031Sstas{
1365226031Sstas    krb5_error_code ret;
1366226031Sstas    char *name;
1367226031Sstas
1368226031Sstas    *str = NULL;
1369226031Sstas
1370226031Sstas    ret = get_def_name(context, &name);
1371226031Sstas    if (ret)
1372226031Sstas	return _krb5_expand_default_cc_name(context, KRB5_SCACHE_NAME, str);
1373226031Sstas
1374226031Sstas    ret = asprintf(str, "SCC:%s", name);
1375226031Sstas    free(name);
1376226031Sstas    if (ret < 0 || *str == NULL) {
1377226031Sstas	krb5_set_error_message(context, ENOMEM,
1378226031Sstas			       N_("malloc: out of memory", ""));
1379226031Sstas	return ENOMEM;
1380226031Sstas    }
1381226031Sstas    return 0;
1382226031Sstas}
1383226031Sstas
1384226031Sstasstatic krb5_error_code KRB5_CALLCONV
1385226031Sstasscc_set_default(krb5_context context, krb5_ccache id)
1386226031Sstas{
1387226031Sstas    krb5_scache *s = SCACHE(id);
1388226031Sstas    krb5_error_code ret;
1389226031Sstas
1390226031Sstas    if (s->cid == SCACHE_INVALID_CID) {
1391226031Sstas	krb5_set_error_message(context, KRB5_CC_IO,
1392226031Sstas			       N_("Trying to set a invalid cache "
1393226031Sstas				  "as default %s", ""),
1394226031Sstas			       s->name);
1395226031Sstas	return KRB5_CC_IO;
1396226031Sstas    }
1397226031Sstas
1398226031Sstas    ret = sqlite3_bind_text(s->umaster, 1, s->name, -1, NULL);
1399226031Sstas    if (ret) {
1400226031Sstas	sqlite3_reset(s->umaster);
1401226031Sstas	krb5_set_error_message(context, KRB5_CC_IO,
1402226031Sstas			       N_("Failed to set name of default cache", ""));
1403226031Sstas	return KRB5_CC_IO;
1404226031Sstas    }
1405226031Sstas
1406226031Sstas    do {
1407226031Sstas	ret = sqlite3_step(s->umaster);
1408226031Sstas    } while (ret == SQLITE_ROW);
1409226031Sstas    sqlite3_reset(s->umaster);
1410226031Sstas    if (ret != SQLITE_DONE) {
1411226031Sstas	krb5_set_error_message(context, KRB5_CC_IO,
1412226031Sstas			       N_("Failed to update default cache", ""));
1413226031Sstas	return KRB5_CC_IO;
1414226031Sstas    }
1415226031Sstas
1416226031Sstas    return 0;
1417226031Sstas}
1418226031Sstas
1419226031Sstas/**
1420226031Sstas * Variable containing the SCC based credential cache implemention.
1421226031Sstas *
1422226031Sstas * @ingroup krb5_ccache
1423226031Sstas */
1424226031Sstas
1425226031SstasKRB5_LIB_VARIABLE const krb5_cc_ops krb5_scc_ops = {
1426226031Sstas    KRB5_CC_OPS_VERSION,
1427226031Sstas    "SCC",
1428226031Sstas    scc_get_name,
1429226031Sstas    scc_resolve,
1430226031Sstas    scc_gen_new,
1431226031Sstas    scc_initialize,
1432226031Sstas    scc_destroy,
1433226031Sstas    scc_close,
1434226031Sstas    scc_store_cred,
1435226031Sstas    NULL, /* scc_retrieve */
1436226031Sstas    scc_get_principal,
1437226031Sstas    scc_get_first,
1438226031Sstas    scc_get_next,
1439226031Sstas    scc_end_get,
1440226031Sstas    scc_remove_cred,
1441226031Sstas    scc_set_flags,
1442226031Sstas    NULL,
1443226031Sstas    scc_get_cache_first,
1444226031Sstas    scc_get_cache_next,
1445226031Sstas    scc_end_cache_get,
1446226031Sstas    scc_move,
1447226031Sstas    scc_get_default_name,
1448226031Sstas    scc_set_default
1449226031Sstas};
1450226031Sstas
1451226031Sstas#endif
1452