etc/services - sync with NetBSD-8
[minix.git] / crypto / external / bsd / heimdal / dist / lib / krb5 / scache.c
blobf91e4d4412d5b92245c51420682b5a68a5c545c2
1 /* $NetBSD: scache.c,v 1.1.1.2 2014/04/24 12:45:51 pettai Exp $ */
3 /*
4 * Copyright (c) 2008 Kungliga Tekniska Högskolan
5 * (Royal Institute of Technology, Stockholm, Sweden).
6 * All rights reserved.
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
12 * 1. Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
19 * 3. Neither the name of the Institute nor the names of its contributors
20 * may be used to endorse or promote products derived from this software
21 * without specific prior written permission.
23 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33 * SUCH DAMAGE.
36 #include "krb5_locl.h"
38 #ifdef HAVE_SCC
40 #include <sqlite3.h>
42 typedef struct krb5_scache {
43 char *name;
44 char *file;
45 sqlite3 *db;
47 sqlite_uint64 cid;
49 sqlite3_stmt *icred;
50 sqlite3_stmt *dcred;
51 sqlite3_stmt *iprincipal;
53 sqlite3_stmt *icache;
54 sqlite3_stmt *ucachen;
55 sqlite3_stmt *ucachep;
56 sqlite3_stmt *dcache;
57 sqlite3_stmt *scache;
58 sqlite3_stmt *scache_name;
59 sqlite3_stmt *umaster;
61 } krb5_scache;
63 #define SCACHE(X) ((krb5_scache *)(X)->data.data)
65 #define SCACHE_DEF_NAME "Default-cache"
66 #ifdef KRB5_USE_PATH_TOKENS
67 #define KRB5_SCACHE_DB "%{TEMP}/krb5scc_%{uid}"
68 #else
69 #define KRB5_SCACHE_DB "/tmp/krb5scc_%{uid}"
70 #endif
71 #define KRB5_SCACHE_NAME "SCC:" SCACHE_DEF_NAME ":" KRB5_SCACHE_DB
73 #define SCACHE_INVALID_CID ((sqlite_uint64)-1)
79 #define SQL_CMASTER "" \
80 "CREATE TABLE master (" \
81 "oid INTEGER PRIMARY KEY," \
82 "version INTEGER NOT NULL," \
83 "defaultcache TEXT NOT NULL" \
84 ")"
86 #define SQL_SETUP_MASTER \
87 "INSERT INTO master (version,defaultcache) VALUES(2, \"" SCACHE_DEF_NAME "\")"
88 #define SQL_UMASTER "UPDATE master SET defaultcache=? WHERE version=2"
90 #define SQL_CCACHE "" \
91 "CREATE TABLE caches (" \
92 "oid INTEGER PRIMARY KEY," \
93 "principal TEXT," \
94 "name TEXT NOT NULL" \
95 ")"
97 #define SQL_TCACHE "" \
98 "CREATE TRIGGER CacheDropCreds AFTER DELETE ON caches " \
99 "FOR EACH ROW BEGIN " \
100 "DELETE FROM credentials WHERE cid=old.oid;" \
101 "END"
103 #define SQL_ICACHE "INSERT INTO caches (name) VALUES(?)"
104 #define SQL_UCACHE_NAME "UPDATE caches SET name=? WHERE OID=?"
105 #define SQL_UCACHE_PRINCIPAL "UPDATE caches SET principal=? WHERE OID=?"
106 #define SQL_DCACHE "DELETE FROM caches WHERE OID=?"
107 #define SQL_SCACHE "SELECT principal,name FROM caches WHERE OID=?"
108 #define SQL_SCACHE_NAME "SELECT oid FROM caches WHERE NAME=?"
110 #define SQL_CCREDS "" \
111 "CREATE TABLE credentials (" \
112 "oid INTEGER PRIMARY KEY," \
113 "cid INTEGER NOT NULL," \
114 "kvno INTEGER NOT NULL," \
115 "etype INTEGER NOT NULL," \
116 "created_at INTEGER NOT NULL," \
117 "cred BLOB NOT NULL" \
120 #define SQL_TCRED "" \
121 "CREATE TRIGGER credDropPrincipal AFTER DELETE ON credentials " \
122 "FOR EACH ROW BEGIN " \
123 "DELETE FROM principals WHERE credential_id=old.oid;" \
124 "END"
126 #define SQL_ICRED "INSERT INTO credentials (cid, kvno, etype, cred, created_at) VALUES (?,?,?,?,?)"
127 #define SQL_DCRED "DELETE FROM credentials WHERE cid=?"
129 #define SQL_CPRINCIPALS "" \
130 "CREATE TABLE principals (" \
131 "oid INTEGER PRIMARY KEY," \
132 "principal TEXT NOT NULL," \
133 "type INTEGER NOT NULL," \
134 "credential_id INTEGER NOT NULL" \
137 #define SQL_IPRINCIPAL "INSERT INTO principals (principal, type, credential_id) VALUES (?,?,?)"
140 * sqlite destructors
143 static void
144 free_data(void *data)
146 free(data);
149 static void
150 free_krb5(void *str)
152 krb5_xfree(str);
155 static void
156 scc_free(krb5_scache *s)
158 if (s->file)
159 free(s->file);
160 if (s->name)
161 free(s->name);
163 if (s->icred)
164 sqlite3_finalize(s->icred);
165 if (s->dcred)
166 sqlite3_finalize(s->dcred);
167 if (s->iprincipal)
168 sqlite3_finalize(s->iprincipal);
169 if (s->icache)
170 sqlite3_finalize(s->icache);
171 if (s->ucachen)
172 sqlite3_finalize(s->ucachen);
173 if (s->ucachep)
174 sqlite3_finalize(s->ucachep);
175 if (s->dcache)
176 sqlite3_finalize(s->dcache);
177 if (s->scache)
178 sqlite3_finalize(s->scache);
179 if (s->scache_name)
180 sqlite3_finalize(s->scache_name);
181 if (s->umaster)
182 sqlite3_finalize(s->umaster);
184 if (s->db)
185 sqlite3_close(s->db);
186 free(s);
189 #ifdef TRACEME
190 static void
191 trace(void* ptr, const char * str)
193 printf("SQL: %s\n", str);
195 #endif
197 static krb5_error_code
198 prepare_stmt(krb5_context context, sqlite3 *db,
199 sqlite3_stmt **stmt, const char *str)
201 int ret;
203 ret = sqlite3_prepare_v2(db, str, -1, stmt, NULL);
204 if (ret != SQLITE_OK) {
205 krb5_set_error_message(context, ENOENT,
206 N_("Failed to prepare stmt %s: %s", ""),
207 str, sqlite3_errmsg(db));
208 return ENOENT;
210 return 0;
213 static krb5_error_code
214 exec_stmt(krb5_context context, sqlite3 *db, const char *str,
215 krb5_error_code code)
217 int ret;
219 ret = sqlite3_exec(db, str, NULL, NULL, NULL);
220 if (ret != SQLITE_OK && code) {
221 krb5_set_error_message(context, code,
222 N_("scache execute %s: %s", ""), str,
223 sqlite3_errmsg(db));
224 return code;
226 return 0;
229 static krb5_error_code
230 default_db(krb5_context context, sqlite3 **db)
232 char *name;
233 int ret;
235 ret = _krb5_expand_default_cc_name(context, KRB5_SCACHE_DB, &name);
236 if (ret)
237 return ret;
239 ret = sqlite3_open_v2(name, db, SQLITE_OPEN_READWRITE, NULL);
240 free(name);
241 if (ret != SQLITE_OK) {
242 krb5_clear_error_message(context);
243 return ENOENT;
246 #ifdef TRACEME
247 sqlite3_trace(*db, trace, NULL);
248 #endif
250 return 0;
253 static krb5_error_code
254 get_def_name(krb5_context context, char **str)
256 krb5_error_code ret;
257 sqlite3_stmt *stmt;
258 const char *name;
259 sqlite3 *db;
261 ret = default_db(context, &db);
262 if (ret)
263 return ret;
265 ret = prepare_stmt(context, db, &stmt, "SELECT defaultcache FROM master");
266 if (ret) {
267 sqlite3_close(db);
268 return ret;
271 ret = sqlite3_step(stmt);
272 if (ret != SQLITE_ROW)
273 goto out;
275 if (sqlite3_column_type(stmt, 0) != SQLITE_TEXT)
276 goto out;
278 name = (const char *)sqlite3_column_text(stmt, 0);
279 if (name == NULL)
280 goto out;
282 *str = strdup(name);
283 if (*str == NULL)
284 goto out;
286 sqlite3_finalize(stmt);
287 sqlite3_close(db);
288 return 0;
289 out:
290 sqlite3_finalize(stmt);
291 sqlite3_close(db);
292 krb5_clear_error_message(context);
293 return ENOENT;
298 static krb5_scache * KRB5_CALLCONV
299 scc_alloc(krb5_context context, const char *name)
301 krb5_error_code ret;
302 krb5_scache *s;
304 ALLOC(s, 1);
305 if(s == NULL)
306 return NULL;
308 s->cid = SCACHE_INVALID_CID;
310 if (name) {
311 char *file;
313 if (*name == '\0') {
314 krb5_error_code ret;
315 ret = get_def_name(context, &s->name);
316 if (ret)
317 s->name = strdup(SCACHE_DEF_NAME);
318 } else
319 s->name = strdup(name);
321 file = strrchr(s->name, ':');
322 if (file) {
323 *file++ = '\0';
324 s->file = strdup(file);
325 ret = 0;
326 } else {
327 ret = _krb5_expand_default_cc_name(context, KRB5_SCACHE_DB, &s->file);
329 } else {
330 _krb5_expand_default_cc_name(context, KRB5_SCACHE_DB, &s->file);
331 ret = asprintf(&s->name, "unique-%p", s);
333 if (ret < 0 || s->file == NULL || s->name == NULL) {
334 scc_free(s);
335 return NULL;
338 return s;
341 static krb5_error_code
342 open_database(krb5_context context, krb5_scache *s, int flags)
344 int ret;
346 ret = sqlite3_open_v2(s->file, &s->db, SQLITE_OPEN_READWRITE|flags, NULL);
347 if (ret) {
348 if (s->db) {
349 krb5_set_error_message(context, ENOENT,
350 N_("Error opening scache file %s: %s", ""),
351 s->file, sqlite3_errmsg(s->db));
352 sqlite3_close(s->db);
353 s->db = NULL;
354 } else
355 krb5_set_error_message(context, ENOENT,
356 N_("malloc: out of memory", ""));
357 return ENOENT;
359 return 0;
362 static krb5_error_code
363 create_cache(krb5_context context, krb5_scache *s)
365 int ret;
367 sqlite3_bind_text(s->icache, 1, s->name, -1, NULL);
368 do {
369 ret = sqlite3_step(s->icache);
370 } while (ret == SQLITE_ROW);
371 if (ret != SQLITE_DONE) {
372 krb5_set_error_message(context, KRB5_CC_IO,
373 N_("Failed to add scache: %d", ""), ret);
374 return KRB5_CC_IO;
376 sqlite3_reset(s->icache);
378 s->cid = sqlite3_last_insert_rowid(s->db);
380 return 0;
383 static krb5_error_code
384 make_database(krb5_context context, krb5_scache *s)
386 int created_file = 0;
387 int ret;
389 if (s->db)
390 return 0;
392 ret = open_database(context, s, 0);
393 if (ret) {
394 mode_t oldumask = umask(077);
395 ret = open_database(context, s, SQLITE_OPEN_CREATE);
396 umask(oldumask);
397 if (ret) goto out;
399 created_file = 1;
401 ret = exec_stmt(context, s->db, SQL_CMASTER, KRB5_CC_IO);
402 if (ret) goto out;
403 ret = exec_stmt(context, s->db, SQL_CCACHE, KRB5_CC_IO);
404 if (ret) goto out;
405 ret = exec_stmt(context, s->db, SQL_CCREDS, KRB5_CC_IO);
406 if (ret) goto out;
407 ret = exec_stmt(context, s->db, SQL_CPRINCIPALS, KRB5_CC_IO);
408 if (ret) goto out;
409 ret = exec_stmt(context, s->db, SQL_SETUP_MASTER, KRB5_CC_IO);
410 if (ret) goto out;
412 ret = exec_stmt(context, s->db, SQL_TCACHE, KRB5_CC_IO);
413 if (ret) goto out;
414 ret = exec_stmt(context, s->db, SQL_TCRED, KRB5_CC_IO);
415 if (ret) goto out;
418 #ifdef TRACEME
419 sqlite3_trace(s->db, trace, NULL);
420 #endif
422 ret = prepare_stmt(context, s->db, &s->icred, SQL_ICRED);
423 if (ret) goto out;
424 ret = prepare_stmt(context, s->db, &s->dcred, SQL_DCRED);
425 if (ret) goto out;
426 ret = prepare_stmt(context, s->db, &s->iprincipal, SQL_IPRINCIPAL);
427 if (ret) goto out;
428 ret = prepare_stmt(context, s->db, &s->icache, SQL_ICACHE);
429 if (ret) goto out;
430 ret = prepare_stmt(context, s->db, &s->ucachen, SQL_UCACHE_NAME);
431 if (ret) goto out;
432 ret = prepare_stmt(context, s->db, &s->ucachep, SQL_UCACHE_PRINCIPAL);
433 if (ret) goto out;
434 ret = prepare_stmt(context, s->db, &s->dcache, SQL_DCACHE);
435 if (ret) goto out;
436 ret = prepare_stmt(context, s->db, &s->scache, SQL_SCACHE);
437 if (ret) goto out;
438 ret = prepare_stmt(context, s->db, &s->scache_name, SQL_SCACHE_NAME);
439 if (ret) goto out;
440 ret = prepare_stmt(context, s->db, &s->umaster, SQL_UMASTER);
441 if (ret) goto out;
443 return 0;
445 out:
446 if (s->db)
447 sqlite3_close(s->db);
448 if (created_file)
449 unlink(s->file);
451 return ret;
454 static krb5_error_code
455 bind_principal(krb5_context context,
456 sqlite3 *db,
457 sqlite3_stmt *stmt,
458 int col,
459 krb5_const_principal principal)
461 krb5_error_code ret;
462 char *str;
464 ret = krb5_unparse_name(context, principal, &str);
465 if (ret)
466 return ret;
468 ret = sqlite3_bind_text(stmt, col, str, -1, free_krb5);
469 if (ret != SQLITE_OK) {
470 krb5_xfree(str);
471 krb5_set_error_message(context, ENOMEM,
472 N_("scache bind principal: %s", ""),
473 sqlite3_errmsg(db));
474 return ENOMEM;
476 return 0;
483 static const char* KRB5_CALLCONV
484 scc_get_name(krb5_context context,
485 krb5_ccache id)
487 return SCACHE(id)->name;
490 static krb5_error_code KRB5_CALLCONV
491 scc_resolve(krb5_context context, krb5_ccache *id, const char *res)
493 krb5_scache *s;
494 int ret;
496 s = scc_alloc(context, res);
497 if (s == NULL) {
498 krb5_set_error_message(context, KRB5_CC_NOMEM,
499 N_("malloc: out of memory", ""));
500 return KRB5_CC_NOMEM;
503 ret = make_database(context, s);
504 if (ret) {
505 scc_free(s);
506 return ret;
509 ret = sqlite3_bind_text(s->scache_name, 1, s->name, -1, NULL);
510 if (ret != SQLITE_OK) {
511 krb5_set_error_message(context, ENOMEM,
512 "bind name: %s", sqlite3_errmsg(s->db));
513 scc_free(s);
514 return ENOMEM;
517 if (sqlite3_step(s->scache_name) == SQLITE_ROW) {
519 if (sqlite3_column_type(s->scache_name, 0) != SQLITE_INTEGER) {
520 sqlite3_reset(s->scache_name);
521 krb5_set_error_message(context, KRB5_CC_END,
522 N_("Cache name of wrong type "
523 "for scache %s", ""),
524 s->name);
525 scc_free(s);
526 return KRB5_CC_END;
529 s->cid = sqlite3_column_int(s->scache_name, 0);
530 } else {
531 s->cid = SCACHE_INVALID_CID;
533 sqlite3_reset(s->scache_name);
535 (*id)->data.data = s;
536 (*id)->data.length = sizeof(*s);
538 return 0;
541 static krb5_error_code KRB5_CALLCONV
542 scc_gen_new(krb5_context context, krb5_ccache *id)
544 krb5_scache *s;
546 s = scc_alloc(context, NULL);
548 if (s == NULL) {
549 krb5_set_error_message(context, KRB5_CC_NOMEM,
550 N_("malloc: out of memory", ""));
551 return KRB5_CC_NOMEM;
554 (*id)->data.data = s;
555 (*id)->data.length = sizeof(*s);
557 return 0;
560 static krb5_error_code KRB5_CALLCONV
561 scc_initialize(krb5_context context,
562 krb5_ccache id,
563 krb5_principal primary_principal)
565 krb5_scache *s = SCACHE(id);
566 krb5_error_code ret;
568 ret = make_database(context, s);
569 if (ret)
570 return ret;
572 ret = exec_stmt(context, s->db, "BEGIN IMMEDIATE TRANSACTION", KRB5_CC_IO);
573 if (ret) return ret;
575 if (s->cid == SCACHE_INVALID_CID) {
576 ret = create_cache(context, s);
577 if (ret)
578 goto rollback;
579 } else {
580 sqlite3_bind_int(s->dcred, 1, s->cid);
581 do {
582 ret = sqlite3_step(s->dcred);
583 } while (ret == SQLITE_ROW);
584 sqlite3_reset(s->dcred);
585 if (ret != SQLITE_DONE) {
586 ret = KRB5_CC_IO;
587 krb5_set_error_message(context, ret,
588 N_("Failed to delete old "
589 "credentials: %s", ""),
590 sqlite3_errmsg(s->db));
591 goto rollback;
595 ret = bind_principal(context, s->db, s->ucachep, 1, primary_principal);
596 if (ret)
597 goto rollback;
598 sqlite3_bind_int(s->ucachep, 2, s->cid);
600 do {
601 ret = sqlite3_step(s->ucachep);
602 } while (ret == SQLITE_ROW);
603 sqlite3_reset(s->ucachep);
604 if (ret != SQLITE_DONE) {
605 ret = KRB5_CC_IO;
606 krb5_set_error_message(context, ret,
607 N_("Failed to bind principal to cache %s", ""),
608 sqlite3_errmsg(s->db));
609 goto rollback;
612 ret = exec_stmt(context, s->db, "COMMIT", KRB5_CC_IO);
613 if (ret) return ret;
615 return 0;
617 rollback:
618 exec_stmt(context, s->db, "ROLLBACK", 0);
620 return ret;
624 static krb5_error_code KRB5_CALLCONV
625 scc_close(krb5_context context,
626 krb5_ccache id)
628 scc_free(SCACHE(id));
629 return 0;
632 static krb5_error_code KRB5_CALLCONV
633 scc_destroy(krb5_context context,
634 krb5_ccache id)
636 krb5_scache *s = SCACHE(id);
637 int ret;
639 if (s->cid == SCACHE_INVALID_CID)
640 return 0;
642 sqlite3_bind_int(s->dcache, 1, s->cid);
643 do {
644 ret = sqlite3_step(s->dcache);
645 } while (ret == SQLITE_ROW);
646 sqlite3_reset(s->dcache);
647 if (ret != SQLITE_DONE) {
648 krb5_set_error_message(context, KRB5_CC_IO,
649 N_("Failed to destroy cache %s: %s", ""),
650 s->name, sqlite3_errmsg(s->db));
651 return KRB5_CC_IO;
653 return 0;
656 static krb5_error_code
657 encode_creds(krb5_context context, krb5_creds *creds, krb5_data *data)
659 krb5_error_code ret;
660 krb5_storage *sp;
662 sp = krb5_storage_emem();
663 if (sp == NULL) {
664 krb5_set_error_message(context, ENOMEM,
665 N_("malloc: out of memory", ""));
666 return ENOMEM;
669 ret = krb5_store_creds(sp, creds);
670 if (ret) {
671 krb5_set_error_message(context, ret,
672 N_("Failed to store credential in scache", ""));
673 krb5_storage_free(sp);
674 return ret;
677 ret = krb5_storage_to_data(sp, data);
678 krb5_storage_free(sp);
679 if (ret)
680 krb5_set_error_message(context, ret,
681 N_("Failed to encode credential in scache", ""));
682 return ret;
685 static krb5_error_code
686 decode_creds(krb5_context context, const void *data, size_t length,
687 krb5_creds *creds)
689 krb5_error_code ret;
690 krb5_storage *sp;
692 sp = krb5_storage_from_readonly_mem(data, length);
693 if (sp == NULL) {
694 krb5_set_error_message(context, ENOMEM,
695 N_("malloc: out of memory", ""));
696 return ENOMEM;
699 ret = krb5_ret_creds(sp, creds);
700 krb5_storage_free(sp);
701 if (ret) {
702 krb5_set_error_message(context, ret,
703 N_("Failed to read credential in scache", ""));
704 return ret;
706 return 0;
710 static krb5_error_code KRB5_CALLCONV
711 scc_store_cred(krb5_context context,
712 krb5_ccache id,
713 krb5_creds *creds)
715 sqlite_uint64 credid;
716 krb5_scache *s = SCACHE(id);
717 krb5_error_code ret;
718 krb5_data data;
720 ret = make_database(context, s);
721 if (ret)
722 return ret;
724 ret = encode_creds(context, creds, &data);
725 if (ret)
726 return ret;
728 sqlite3_bind_int(s->icred, 1, s->cid);
730 krb5_enctype etype = 0;
731 int kvno = 0;
732 Ticket t;
733 size_t len;
735 ret = decode_Ticket(creds->ticket.data,
736 creds->ticket.length, &t, &len);
737 if (ret == 0) {
738 if(t.enc_part.kvno)
739 kvno = *t.enc_part.kvno;
741 etype = t.enc_part.etype;
743 free_Ticket(&t);
746 sqlite3_bind_int(s->icred, 2, kvno);
747 sqlite3_bind_int(s->icred, 3, etype);
751 sqlite3_bind_blob(s->icred, 4, data.data, data.length, free_data);
752 sqlite3_bind_int(s->icred, 5, time(NULL));
754 ret = exec_stmt(context, s->db, "BEGIN IMMEDIATE TRANSACTION", KRB5_CC_IO);
755 if (ret) return ret;
757 do {
758 ret = sqlite3_step(s->icred);
759 } while (ret == SQLITE_ROW);
760 sqlite3_reset(s->icred);
761 if (ret != SQLITE_DONE) {
762 ret = KRB5_CC_IO;
763 krb5_set_error_message(context, ret,
764 N_("Failed to add credential: %s", ""),
765 sqlite3_errmsg(s->db));
766 goto rollback;
769 credid = sqlite3_last_insert_rowid(s->db);
772 bind_principal(context, s->db, s->iprincipal, 1, creds->server);
773 sqlite3_bind_int(s->iprincipal, 2, 1);
774 sqlite3_bind_int(s->iprincipal, 3, credid);
776 do {
777 ret = sqlite3_step(s->iprincipal);
778 } while (ret == SQLITE_ROW);
779 sqlite3_reset(s->iprincipal);
780 if (ret != SQLITE_DONE) {
781 ret = KRB5_CC_IO;
782 krb5_set_error_message(context, ret,
783 N_("Failed to add principal: %s", ""),
784 sqlite3_errmsg(s->db));
785 goto rollback;
790 bind_principal(context, s->db, s->iprincipal, 1, creds->client);
791 sqlite3_bind_int(s->iprincipal, 2, 0);
792 sqlite3_bind_int(s->iprincipal, 3, credid);
794 do {
795 ret = sqlite3_step(s->iprincipal);
796 } while (ret == SQLITE_ROW);
797 sqlite3_reset(s->iprincipal);
798 if (ret != SQLITE_DONE) {
799 ret = KRB5_CC_IO;
800 krb5_set_error_message(context, ret,
801 N_("Failed to add principal: %s", ""),
802 sqlite3_errmsg(s->db));
803 goto rollback;
807 ret = exec_stmt(context, s->db, "COMMIT", KRB5_CC_IO);
808 if (ret) return ret;
810 return 0;
812 rollback:
813 exec_stmt(context, s->db, "ROLLBACK", 0);
815 return ret;
818 static krb5_error_code KRB5_CALLCONV
819 scc_get_principal(krb5_context context,
820 krb5_ccache id,
821 krb5_principal *principal)
823 krb5_scache *s = SCACHE(id);
824 krb5_error_code ret;
825 const char *str;
827 *principal = NULL;
829 ret = make_database(context, s);
830 if (ret)
831 return ret;
833 sqlite3_bind_int(s->scache, 1, s->cid);
835 if (sqlite3_step(s->scache) != SQLITE_ROW) {
836 sqlite3_reset(s->scache);
837 krb5_set_error_message(context, KRB5_CC_END,
838 N_("No principal for cache SCC:%s:%s", ""),
839 s->name, s->file);
840 return KRB5_CC_END;
843 if (sqlite3_column_type(s->scache, 0) != SQLITE_TEXT) {
844 sqlite3_reset(s->scache);
845 krb5_set_error_message(context, KRB5_CC_END,
846 N_("Principal data of wrong type "
847 "for SCC:%s:%s", ""),
848 s->name, s->file);
849 return KRB5_CC_END;
852 str = (const char *)sqlite3_column_text(s->scache, 0);
853 if (str == NULL) {
854 sqlite3_reset(s->scache);
855 krb5_set_error_message(context, KRB5_CC_END,
856 N_("Principal not set for SCC:%s:%s", ""),
857 s->name, s->file);
858 return KRB5_CC_END;
861 ret = krb5_parse_name(context, str, principal);
863 sqlite3_reset(s->scache);
865 return ret;
868 struct cred_ctx {
869 char *drop;
870 sqlite3_stmt *stmt;
871 sqlite3_stmt *credstmt;
874 static krb5_error_code KRB5_CALLCONV
875 scc_get_first (krb5_context context,
876 krb5_ccache id,
877 krb5_cc_cursor *cursor)
879 krb5_scache *s = SCACHE(id);
880 krb5_error_code ret;
881 struct cred_ctx *ctx;
882 char *str = NULL, *name = NULL;
884 *cursor = NULL;
886 ctx = calloc(1, sizeof(*ctx));
887 if (ctx == NULL) {
888 krb5_set_error_message(context, ENOMEM,
889 N_("malloc: out of memory", ""));
890 return ENOMEM;
893 ret = make_database(context, s);
894 if (ret) {
895 free(ctx);
896 return ret;
899 if (s->cid == SCACHE_INVALID_CID) {
900 krb5_set_error_message(context, KRB5_CC_END,
901 N_("Iterating a invalid scache %s", ""),
902 s->name);
903 free(ctx);
904 return KRB5_CC_END;
907 ret = asprintf(&name, "credIteration%pPid%d",
908 ctx, (int)getpid());
909 if (ret < 0 || name == NULL) {
910 krb5_set_error_message(context, ENOMEM,
911 N_("malloc: out of memory", ""));
912 free(ctx);
913 return ENOMEM;
916 ret = asprintf(&ctx->drop, "DROP TABLE %s", name);
917 if (ret < 0 || ctx->drop == NULL) {
918 krb5_set_error_message(context, ENOMEM,
919 N_("malloc: out of memory", ""));
920 free(name);
921 free(ctx);
922 return ENOMEM;
925 ret = asprintf(&str, "CREATE TEMPORARY TABLE %s "
926 "AS SELECT oid,created_at FROM credentials WHERE cid = %lu",
927 name, (unsigned long)s->cid);
928 if (ret < 0 || str == NULL) {
929 free(ctx->drop);
930 free(name);
931 free(ctx);
932 return ENOMEM;
935 ret = exec_stmt(context, s->db, str, KRB5_CC_IO);
936 free(str);
937 str = NULL;
938 if (ret) {
939 free(ctx->drop);
940 free(name);
941 free(ctx);
942 return ret;
945 ret = asprintf(&str, "SELECT oid FROM %s ORDER BY created_at", name);
946 if (ret < 0 || str == NULL) {
947 exec_stmt(context, s->db, ctx->drop, 0);
948 free(ctx->drop);
949 free(name);
950 free(ctx);
951 return ret;
954 ret = prepare_stmt(context, s->db, &ctx->stmt, str);
955 free(str);
956 str = NULL;
957 free(name);
958 if (ret) {
959 exec_stmt(context, s->db, ctx->drop, 0);
960 free(ctx->drop);
961 free(ctx);
962 return ret;
965 ret = prepare_stmt(context, s->db, &ctx->credstmt,
966 "SELECT cred FROM credentials WHERE oid = ?");
967 if (ret) {
968 sqlite3_finalize(ctx->stmt);
969 exec_stmt(context, s->db, ctx->drop, 0);
970 free(ctx->drop);
971 free(ctx);
972 return ret;
975 *cursor = ctx;
977 return 0;
980 static krb5_error_code KRB5_CALLCONV
981 scc_get_next (krb5_context context,
982 krb5_ccache id,
983 krb5_cc_cursor *cursor,
984 krb5_creds *creds)
986 struct cred_ctx *ctx = *cursor;
987 krb5_scache *s = SCACHE(id);
988 krb5_error_code ret;
989 sqlite_uint64 oid;
990 const void *data = NULL;
991 size_t len = 0;
993 next:
994 ret = sqlite3_step(ctx->stmt);
995 if (ret == SQLITE_DONE) {
996 krb5_clear_error_message(context);
997 return KRB5_CC_END;
998 } else if (ret != SQLITE_ROW) {
999 krb5_set_error_message(context, KRB5_CC_IO,
1000 N_("scache Database failed: %s", ""),
1001 sqlite3_errmsg(s->db));
1002 return KRB5_CC_IO;
1005 oid = sqlite3_column_int64(ctx->stmt, 0);
1007 /* read cred from credentials table */
1009 sqlite3_bind_int(ctx->credstmt, 1, oid);
1011 ret = sqlite3_step(ctx->credstmt);
1012 if (ret != SQLITE_ROW) {
1013 sqlite3_reset(ctx->credstmt);
1014 goto next;
1017 if (sqlite3_column_type(ctx->credstmt, 0) != SQLITE_BLOB) {
1018 krb5_set_error_message(context, KRB5_CC_END,
1019 N_("credential of wrong type for SCC:%s:%s", ""),
1020 s->name, s->file);
1021 sqlite3_reset(ctx->credstmt);
1022 return KRB5_CC_END;
1025 data = sqlite3_column_blob(ctx->credstmt, 0);
1026 len = sqlite3_column_bytes(ctx->credstmt, 0);
1028 ret = decode_creds(context, data, len, creds);
1029 sqlite3_reset(ctx->credstmt);
1030 return ret;
1033 static krb5_error_code KRB5_CALLCONV
1034 scc_end_get (krb5_context context,
1035 krb5_ccache id,
1036 krb5_cc_cursor *cursor)
1038 struct cred_ctx *ctx = *cursor;
1039 krb5_scache *s = SCACHE(id);
1041 sqlite3_finalize(ctx->stmt);
1042 sqlite3_finalize(ctx->credstmt);
1044 exec_stmt(context, s->db, ctx->drop, 0);
1046 free(ctx->drop);
1047 free(ctx);
1049 return 0;
1052 static krb5_error_code KRB5_CALLCONV
1053 scc_remove_cred(krb5_context context,
1054 krb5_ccache id,
1055 krb5_flags which,
1056 krb5_creds *mcreds)
1058 krb5_scache *s = SCACHE(id);
1059 krb5_error_code ret;
1060 sqlite3_stmt *stmt;
1061 sqlite_uint64 credid = 0;
1062 const void *data = NULL;
1063 size_t len = 0;
1065 ret = make_database(context, s);
1066 if (ret)
1067 return ret;
1069 ret = prepare_stmt(context, s->db, &stmt,
1070 "SELECT cred,oid FROM credentials "
1071 "WHERE cid = ?");
1072 if (ret)
1073 return ret;
1075 sqlite3_bind_int(stmt, 1, s->cid);
1077 /* find credential... */
1078 while (1) {
1079 krb5_creds creds;
1081 ret = sqlite3_step(stmt);
1082 if (ret == SQLITE_DONE) {
1083 ret = 0;
1084 break;
1085 } else if (ret != SQLITE_ROW) {
1086 ret = KRB5_CC_IO;
1087 krb5_set_error_message(context, ret,
1088 N_("scache Database failed: %s", ""),
1089 sqlite3_errmsg(s->db));
1090 break;
1093 if (sqlite3_column_type(stmt, 0) != SQLITE_BLOB) {
1094 ret = KRB5_CC_END;
1095 krb5_set_error_message(context, ret,
1096 N_("Credential of wrong type "
1097 "for SCC:%s:%s", ""),
1098 s->name, s->file);
1099 break;
1102 data = sqlite3_column_blob(stmt, 0);
1103 len = sqlite3_column_bytes(stmt, 0);
1105 ret = decode_creds(context, data, len, &creds);
1106 if (ret)
1107 break;
1109 ret = krb5_compare_creds(context, which, mcreds, &creds);
1110 krb5_free_cred_contents(context, &creds);
1111 if (ret) {
1112 credid = sqlite3_column_int64(stmt, 1);
1113 ret = 0;
1114 break;
1118 sqlite3_finalize(stmt);
1120 if (id) {
1121 ret = prepare_stmt(context, s->db, &stmt,
1122 "DELETE FROM credentials WHERE oid=?");
1123 if (ret)
1124 return ret;
1125 sqlite3_bind_int(stmt, 1, credid);
1127 do {
1128 ret = sqlite3_step(stmt);
1129 } while (ret == SQLITE_ROW);
1130 sqlite3_finalize(stmt);
1131 if (ret != SQLITE_DONE) {
1132 ret = KRB5_CC_IO;
1133 krb5_set_error_message(context, ret,
1134 N_("failed to delete scache credental", ""));
1135 } else
1136 ret = 0;
1139 return ret;
1142 static krb5_error_code KRB5_CALLCONV
1143 scc_set_flags(krb5_context context,
1144 krb5_ccache id,
1145 krb5_flags flags)
1147 return 0; /* XXX */
1150 struct cache_iter {
1151 char *drop;
1152 sqlite3 *db;
1153 sqlite3_stmt *stmt;
1156 static krb5_error_code KRB5_CALLCONV
1157 scc_get_cache_first(krb5_context context, krb5_cc_cursor *cursor)
1159 struct cache_iter *ctx;
1160 krb5_error_code ret;
1161 char *name = NULL, *str = NULL;
1163 *cursor = NULL;
1165 ctx = calloc(1, sizeof(*ctx));
1166 if (ctx == NULL) {
1167 krb5_set_error_message(context, ENOMEM,
1168 N_("malloc: out of memory", ""));
1169 return ENOMEM;
1172 ret = default_db(context, &ctx->db);
1173 if (ctx->db == NULL) {
1174 free(ctx);
1175 return ret;
1178 ret = asprintf(&name, "cacheIteration%pPid%d",
1179 ctx, (int)getpid());
1180 if (ret < 0 || name == NULL) {
1181 krb5_set_error_message(context, ENOMEM,
1182 N_("malloc: out of memory", ""));
1183 sqlite3_close(ctx->db);
1184 free(ctx);
1185 return ENOMEM;
1188 ret = asprintf(&ctx->drop, "DROP TABLE %s", name);
1189 if (ret < 0 || ctx->drop == NULL) {
1190 krb5_set_error_message(context, ENOMEM,
1191 N_("malloc: out of memory", ""));
1192 sqlite3_close(ctx->db);
1193 free(name);
1194 free(ctx);
1195 return ENOMEM;
1198 ret = asprintf(&str, "CREATE TEMPORARY TABLE %s AS SELECT name FROM caches",
1199 name);
1200 if (ret < 0 || str == NULL) {
1201 krb5_set_error_message(context, ENOMEM,
1202 N_("malloc: out of memory", ""));
1203 sqlite3_close(ctx->db);
1204 free(name);
1205 free(ctx->drop);
1206 free(ctx);
1207 return ENOMEM;
1210 ret = exec_stmt(context, ctx->db, str, KRB5_CC_IO);
1211 free(str);
1212 str = NULL;
1213 if (ret) {
1214 sqlite3_close(ctx->db);
1215 free(name);
1216 free(ctx->drop);
1217 free(ctx);
1218 return ret;
1221 ret = asprintf(&str, "SELECT name FROM %s", name);
1222 free(name);
1223 if (ret < 0 || str == NULL) {
1224 exec_stmt(context, ctx->db, ctx->drop, 0);
1225 sqlite3_close(ctx->db);
1226 free(name);
1227 free(ctx->drop);
1228 free(ctx);
1229 return ENOMEM;
1232 ret = prepare_stmt(context, ctx->db, &ctx->stmt, str);
1233 free(str);
1234 if (ret) {
1235 exec_stmt(context, ctx->db, ctx->drop, 0);
1236 sqlite3_close(ctx->db);
1237 free(ctx->drop);
1238 free(ctx);
1239 return ret;
1242 *cursor = ctx;
1244 return 0;
1247 static krb5_error_code KRB5_CALLCONV
1248 scc_get_cache_next(krb5_context context,
1249 krb5_cc_cursor cursor,
1250 krb5_ccache *id)
1252 struct cache_iter *ctx = cursor;
1253 krb5_error_code ret;
1254 const char *name;
1256 again:
1257 ret = sqlite3_step(ctx->stmt);
1258 if (ret == SQLITE_DONE) {
1259 krb5_clear_error_message(context);
1260 return KRB5_CC_END;
1261 } else if (ret != SQLITE_ROW) {
1262 krb5_set_error_message(context, KRB5_CC_IO,
1263 N_("Database failed: %s", ""),
1264 sqlite3_errmsg(ctx->db));
1265 return KRB5_CC_IO;
1268 if (sqlite3_column_type(ctx->stmt, 0) != SQLITE_TEXT)
1269 goto again;
1271 name = (const char *)sqlite3_column_text(ctx->stmt, 0);
1272 if (name == NULL)
1273 goto again;
1275 ret = _krb5_cc_allocate(context, &krb5_scc_ops, id);
1276 if (ret)
1277 return ret;
1279 return scc_resolve(context, id, name);
1282 static krb5_error_code KRB5_CALLCONV
1283 scc_end_cache_get(krb5_context context, krb5_cc_cursor cursor)
1285 struct cache_iter *ctx = cursor;
1287 exec_stmt(context, ctx->db, ctx->drop, 0);
1288 sqlite3_finalize(ctx->stmt);
1289 sqlite3_close(ctx->db);
1290 free(ctx->drop);
1291 free(ctx);
1292 return 0;
1295 static krb5_error_code KRB5_CALLCONV
1296 scc_move(krb5_context context, krb5_ccache from, krb5_ccache to)
1298 krb5_scache *sfrom = SCACHE(from);
1299 krb5_scache *sto = SCACHE(to);
1300 krb5_error_code ret;
1302 if (strcmp(sfrom->file, sto->file) != 0) {
1303 krb5_set_error_message(context, KRB5_CC_BADNAME,
1304 N_("Can't handle cross database "
1305 "credential move: %s -> %s", ""),
1306 sfrom->file, sto->file);
1307 return KRB5_CC_BADNAME;
1310 ret = make_database(context, sfrom);
1311 if (ret)
1312 return ret;
1314 ret = exec_stmt(context, sfrom->db,
1315 "BEGIN IMMEDIATE TRANSACTION", KRB5_CC_IO);
1316 if (ret) return ret;
1318 if (sto->cid != SCACHE_INVALID_CID) {
1319 /* drop old cache entry */
1321 sqlite3_bind_int(sfrom->dcache, 1, sto->cid);
1322 do {
1323 ret = sqlite3_step(sfrom->dcache);
1324 } while (ret == SQLITE_ROW);
1325 sqlite3_reset(sfrom->dcache);
1326 if (ret != SQLITE_DONE) {
1327 krb5_set_error_message(context, KRB5_CC_IO,
1328 N_("Failed to delete old cache: %d", ""),
1329 (int)ret);
1330 goto rollback;
1334 sqlite3_bind_text(sfrom->ucachen, 1, sto->name, -1, NULL);
1335 sqlite3_bind_int(sfrom->ucachen, 2, sfrom->cid);
1337 do {
1338 ret = sqlite3_step(sfrom->ucachen);
1339 } while (ret == SQLITE_ROW);
1340 sqlite3_reset(sfrom->ucachen);
1341 if (ret != SQLITE_DONE) {
1342 krb5_set_error_message(context, KRB5_CC_IO,
1343 N_("Failed to update new cache: %d", ""),
1344 (int)ret);
1345 goto rollback;
1348 sto->cid = sfrom->cid;
1350 ret = exec_stmt(context, sfrom->db, "COMMIT", KRB5_CC_IO);
1351 if (ret) return ret;
1353 scc_free(sfrom);
1355 return 0;
1357 rollback:
1358 exec_stmt(context, sfrom->db, "ROLLBACK", 0);
1359 scc_free(sfrom);
1361 return KRB5_CC_IO;
1364 static krb5_error_code KRB5_CALLCONV
1365 scc_get_default_name(krb5_context context, char **str)
1367 krb5_error_code ret;
1368 char *name;
1370 *str = NULL;
1372 ret = get_def_name(context, &name);
1373 if (ret)
1374 return _krb5_expand_default_cc_name(context, KRB5_SCACHE_NAME, str);
1376 ret = asprintf(str, "SCC:%s", name);
1377 free(name);
1378 if (ret < 0 || *str == NULL) {
1379 krb5_set_error_message(context, ENOMEM,
1380 N_("malloc: out of memory", ""));
1381 return ENOMEM;
1383 return 0;
1386 static krb5_error_code KRB5_CALLCONV
1387 scc_set_default(krb5_context context, krb5_ccache id)
1389 krb5_scache *s = SCACHE(id);
1390 krb5_error_code ret;
1392 if (s->cid == SCACHE_INVALID_CID) {
1393 krb5_set_error_message(context, KRB5_CC_IO,
1394 N_("Trying to set a invalid cache "
1395 "as default %s", ""),
1396 s->name);
1397 return KRB5_CC_IO;
1400 ret = sqlite3_bind_text(s->umaster, 1, s->name, -1, NULL);
1401 if (ret) {
1402 sqlite3_reset(s->umaster);
1403 krb5_set_error_message(context, KRB5_CC_IO,
1404 N_("Failed to set name of default cache", ""));
1405 return KRB5_CC_IO;
1408 do {
1409 ret = sqlite3_step(s->umaster);
1410 } while (ret == SQLITE_ROW);
1411 sqlite3_reset(s->umaster);
1412 if (ret != SQLITE_DONE) {
1413 krb5_set_error_message(context, KRB5_CC_IO,
1414 N_("Failed to update default cache", ""));
1415 return KRB5_CC_IO;
1418 return 0;
1422 * Variable containing the SCC based credential cache implemention.
1424 * @ingroup krb5_ccache
1427 KRB5_LIB_VARIABLE const krb5_cc_ops krb5_scc_ops = {
1428 KRB5_CC_OPS_VERSION,
1429 "SCC",
1430 scc_get_name,
1431 scc_resolve,
1432 scc_gen_new,
1433 scc_initialize,
1434 scc_destroy,
1435 scc_close,
1436 scc_store_cred,
1437 NULL, /* scc_retrieve */
1438 scc_get_principal,
1439 scc_get_first,
1440 scc_get_next,
1441 scc_end_get,
1442 scc_remove_cred,
1443 scc_set_flags,
1444 NULL,
1445 scc_get_cache_first,
1446 scc_get_cache_next,
1447 scc_end_cache_get,
1448 scc_move,
1449 scc_get_default_name,
1450 scc_set_default
1453 #endif