]> CyberLeo.Net >> Repos - FreeBSD/releng/10.0.git/blob - crypto/heimdal/lib/krb5/scache.c
- Copy stable/10 (r259064) to releng/10.0 as part of the
[FreeBSD/releng/10.0.git] / crypto / heimdal / lib / krb5 / scache.c
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