krb5: Improve cccol sub naming; add gss_store_cred_into2()

- Formalize the TYPE:collection_name:subsidiary_name naming scheme for
   ccaches in ccache collections
    - KEYRING: ccaches are weird because they have one more optional field: the
      "anchor", so rather than just assume a naming convention everywhere, we
      add new functions as well
 - Add krb5_cc_{resolve,default}_sub() that allows one to specify a
   "subsidiary" ccache name in a collection separately from the
   collection name
 - Add krb5_cc_{resolve,default}_for() which take a principal name,
   unparse it, and use it as the subsidiary ccache name (with colons
   replaced)
 - Make kinit use the new interfaces
 - Add missing DIR ccache iteration functionality
 - Revamps test_cc
 - Add krb5_cc_get_collection() and krb5_cc_get_subsidiary()
 - Bump the ccops SPI version number
 - Add gss_store_cred_into2()
 - Make MEMORY:anonymous not linked into the global MEMORY ccache
   collection, and uses this for delegated cred handles

TBD:

 - Split this up into a krb5 change and gss mech_krb5 change?
 - Add krb5_cc_init_and_store() utility, per Greg's suggestion?
This commit is contained in:
Nicolas Williams
2020-01-22 19:18:14 -06:00
parent a7359d6898
commit 7bf4d76e75
33 changed files with 1749 additions and 715 deletions

View File

@@ -40,6 +40,7 @@
typedef struct krb5_scache {
char *name;
char *file;
char *sub;
sqlite3 *db;
sqlite_uint64 cid;
@@ -66,7 +67,7 @@ typedef struct krb5_scache {
#else
#define KRB5_SCACHE_DB "/tmp/krb5scc_%{uid}"
#endif
#define KRB5_SCACHE_NAME "SCC:" SCACHE_DEF_NAME ":" KRB5_SCACHE_DB
#define KRB5_SCACHE_NAME "SCC:" KRB5_SCACHE_DB ":" SCACHE_DEF_NAME
#define SCACHE_INVALID_CID ((sqlite_uint64)-1)
@@ -103,7 +104,8 @@ typedef struct krb5_scache {
#define SQL_UCACHE_PRINCIPAL "UPDATE caches SET principal=? WHERE OID=?"
#define SQL_DCACHE "DELETE FROM caches WHERE OID=?"
#define SQL_SCACHE "SELECT principal,name FROM caches WHERE OID=?"
#define SQL_SCACHE_NAME "SELECT oid FROM caches WHERE NAME=?"
#define SQL_SCACHE_NAME "SELECT oid FROM caches WHERE NAME=? OR " \
"(PRINCIPAL IS NOT NULL AND PRINCIPAL=?)"
#define SQL_CCREDS "" \
"CREATE TABLE credentials (" \
@@ -153,8 +155,12 @@ free_krb5(void *str)
static void
scc_free(krb5_scache *s)
{
if (!s)
return;
if (s->file)
free(s->file);
if (s->sub)
free(s->sub);
if (s->name)
free(s->name);
@@ -225,22 +231,53 @@ exec_stmt(krb5_context context, sqlite3 *db, const char *str,
}
static krb5_error_code
default_db(krb5_context context, sqlite3 **db)
default_db(krb5_context context, const char *name, sqlite3 **db, char **file)
{
char *name;
char *s = NULL;
char *f = NULL;
int ret;
ret = _krb5_expand_default_cc_name(context, KRB5_SCACHE_DB, &name);
if (ret)
return ret;
if (file)
*file = NULL;
ret = sqlite3_open_v2(name, db, SQLITE_OPEN_READWRITE, NULL);
free(name);
if (name == NULL) {
if ((name = krb5_cc_default_name(context))) {
if (strncmp(name, "SCC:", sizeof("SCC:") - 1) == 0)
name += sizeof("SCC:") - 1;
}
if (name == NULL) {
ret = _krb5_expand_default_cc_name(context, KRB5_SCACHE_DB, &s);
if (ret)
return ret;
name = s;
}
}
if (strncmp(name, "SCC:", sizeof("SCC:") - 1) == 0)
name += sizeof("SCC:") - 1;
if ((f = strdup(name)) == NULL) {
free(s);
return krb5_enomem(context);
}
free(s);
if ((s = strrchr(f, ':')))
*s = '\0';
ret = sqlite3_open_v2(f, db, SQLITE_OPEN_READWRITE, NULL);
if (ret != SQLITE_OK) {
sqlite3_close_v2(*db);
krb5_clear_error_message(context);
free(f);
return ENOENT;
}
if (file)
*file = f;
else
free(f);
#ifdef TRACEME
sqlite3_trace(*db, trace, NULL);
#endif
@@ -249,14 +286,14 @@ default_db(krb5_context context, sqlite3 **db)
}
static krb5_error_code
get_def_name(krb5_context context, char **str)
get_def_name(krb5_context context, char *filein, char **str, char **file)
{
krb5_error_code ret;
sqlite3_stmt *stmt;
const char *name;
sqlite3 *db;
ret = default_db(context, &db);
ret = default_db(context, filein, &db, file);
if (ret)
return ret;
@@ -294,10 +331,15 @@ out:
static krb5_scache * KRB5_CALLCONV
scc_alloc(krb5_context context, const char *name)
scc_alloc(krb5_context context,
const char *name,
const char *sub,
int new_unique)
{
krb5_error_code ret;
krb5_error_code ret = 0;
krb5_scache *s;
char *freeme = NULL;
char *subsidiary;
ALLOC(s, 1);
if(s == NULL)
@@ -305,41 +347,87 @@ scc_alloc(krb5_context context, const char *name)
s->cid = SCACHE_INVALID_CID;
if (name) {
char *file;
if (*name == '\0') {
ret = get_def_name(context, &s->name);
if (ret)
s->name = strdup(SCACHE_DEF_NAME);
} else
s->name = strdup(name);
file = strrchr(s->name, ':');
if (file) {
*file++ = '\0';
s->file = strdup(file);
ret = 0;
} else {
ret = _krb5_expand_default_cc_name(context, KRB5_SCACHE_DB, &s->file);
}
if (name && *name && sub && *sub) {
if ((s->sub = strdup(sub)) == NULL ||
(s->file = strdup(name)) == NULL) {
free(s->file);
free(s);
(void) krb5_enomem(context);
return NULL;
}
} else {
_krb5_expand_default_cc_name(context, KRB5_SCACHE_DB, &s->file);
ret = asprintf(&s->name, "unique-%p", s);
s->sub = NULL;
s->file = NULL;
s->name = NULL;
if (name == NULL)
name = krb5_cc_default_name(context);
if (name == NULL) {
ret = _krb5_expand_default_cc_name(context, KRB5_SCACHE_DB,
&freeme);
if (ret) {
free(s);
return NULL;
}
name = freeme;
}
if (strncmp(name, "SCC:", sizeof("SCC:") - 1) == 0)
name += sizeof("SCC:") - 1;
if ((s->file = strdup(name)) == NULL) {
(void) krb5_enomem(context);
scc_free(s);
free(freeme);
return NULL;
}
if ((subsidiary = strrchr(s->file, ':'))) {
#ifdef WIN32
if (subsidiary == &s->file + 1)
subsidiary = NULL;
else
#endif
*(subsidiary++) = '\0';
}
if (new_unique) {
ret = asprintf(&s->sub, "unique-%p", s) < 0 || s->sub == NULL ?
krb5_enomem(context) : 0;
} else if (subsidiary == NULL || *subsidiary == '\0') {
ret = get_def_name(context, s->file, &s->sub, NULL);
if (ret) {
if ((s->sub = strdup(SCACHE_DEF_NAME)) == NULL)
ret = krb5_enomem(context);
else
ret = 0;
}
} else if ((s->sub = strdup(subsidiary)) == NULL) {
ret = krb5_enomem(context);
}
}
if (ret < 0 || s->file == NULL || s->name == NULL) {
if (ret == 0 && s->file && s->sub &&
(asprintf(&s->name, "%s:%s", s->file, s->sub) < 0 || s->name == NULL))
ret = krb5_enomem(context);
if (ret || s->file == NULL || s->sub == NULL || s->name == NULL) {
scc_free(s);
free(freeme);
return NULL;
}
return s;
}
static krb5_error_code
open_database(krb5_context context, krb5_scache *s, int flags)
{
struct stat st;
int ret;
if (!(flags & SQLITE_OPEN_CREATE) && stat(s->file, &st) == 0 &&
st.st_size == 0)
return ENOENT;
ret = sqlite3_open_v2(s->file, &s->db, SQLITE_OPEN_READWRITE|flags, NULL);
if (ret) {
if (s->db) {
@@ -361,7 +449,7 @@ create_cache(krb5_context context, krb5_scache *s)
{
int ret;
sqlite3_bind_text(s->icache, 1, s->name, -1, NULL);
sqlite3_bind_text(s->icache, 1, s->sub, -1, NULL);
do {
ret = sqlite3_step(s->icache);
} while (ret == SQLITE_ROW);
@@ -477,20 +565,32 @@ bind_principal(krb5_context context,
*
*/
static const char* KRB5_CALLCONV
static krb5_error_code KRB5_CALLCONV
scc_get_name(krb5_context context,
krb5_ccache id)
krb5_ccache id,
const char **name,
const char **file,
const char **sub)
{
return SCACHE(id)->name;
if (name)
*name = SCACHE(id)->name;
if (file)
*file = SCACHE(id)->file;
if (sub)
*sub = SCACHE(id)->sub;
return 0;
}
static krb5_error_code KRB5_CALLCONV
scc_resolve(krb5_context context, krb5_ccache *id, const char *res)
scc_resolve(krb5_context context,
krb5_ccache *id,
const char *res,
const char *sub)
{
krb5_error_code ret;
krb5_scache *s;
int ret;
s = scc_alloc(context, res);
s = scc_alloc(context, res, sub, 0);
if (s == NULL) {
krb5_set_error_message(context, KRB5_CC_NOMEM,
N_("malloc: out of memory", ""));
@@ -503,12 +603,12 @@ scc_resolve(krb5_context context, krb5_ccache *id, const char *res)
return ret;
}
ret = sqlite3_bind_text(s->scache_name, 1, s->name, -1, NULL);
ret = sqlite3_bind_text(s->scache_name, 1, s->sub, -1, NULL);
if (ret != SQLITE_OK) {
krb5_set_error_message(context, ENOMEM,
"bind name: %s", sqlite3_errmsg(s->db));
scc_free(s);
return ENOMEM;
krb5_set_error_message(context, ENOMEM,
"bind principal: %s", sqlite3_errmsg(s->db));
scc_free(s);
return ENOMEM;
}
if (sqlite3_step(s->scache_name) == SQLITE_ROW) {
@@ -540,7 +640,7 @@ scc_gen_new(krb5_context context, krb5_ccache *id)
{
krb5_scache *s;
s = scc_alloc(context, NULL);
s = scc_alloc(context, NULL, NULL, 1);
if (s == NULL) {
krb5_set_error_message(context, KRB5_CC_NOMEM,
@@ -557,7 +657,7 @@ scc_gen_new(krb5_context context, krb5_ccache *id)
static krb5_error_code KRB5_CALLCONV
scc_initialize(krb5_context context,
krb5_ccache id,
krb5_principal primary_principal)
krb5_principal principal)
{
krb5_scache *s = SCACHE(id);
krb5_error_code ret;
@@ -589,7 +689,7 @@ scc_initialize(krb5_context context,
}
}
ret = bind_principal(context, s->db, s->ucachep, 1, primary_principal);
ret = bind_principal(context, s->db, s->ucachep, 1, principal);
if (ret)
goto rollback;
sqlite3_bind_int(s->ucachep, 2, s->cid);
@@ -827,8 +927,8 @@ scc_get_principal(krb5_context context,
if (sqlite3_step(s->scache) != SQLITE_ROW) {
sqlite3_reset(s->scache);
krb5_set_error_message(context, KRB5_CC_END,
N_("No principal for cache SCC:%s:%s", ""),
s->name, s->file);
N_("No principal for cache SCC:%s", ""),
s->name);
return KRB5_CC_END;
}
@@ -836,8 +936,8 @@ scc_get_principal(krb5_context context,
sqlite3_reset(s->scache);
krb5_set_error_message(context, KRB5_CC_END,
N_("Principal data of wrong type "
"for SCC:%s:%s", ""),
s->name, s->file);
"for SCC:%s", ""),
s->name);
return KRB5_CC_END;
}
@@ -845,8 +945,8 @@ scc_get_principal(krb5_context context,
if (str == NULL) {
sqlite3_reset(s->scache);
krb5_set_error_message(context, KRB5_CC_END,
N_("Principal not set for SCC:%s:%s", ""),
s->name, s->file);
N_("Principal not set for SCC:%s", ""),
s->name);
return KRB5_CC_END;
}
@@ -1001,8 +1101,8 @@ next:
if (sqlite3_column_type(ctx->credstmt, 0) != SQLITE_BLOB) {
krb5_set_error_message(context, KRB5_CC_END,
N_("credential of wrong type for SCC:%s:%s", ""),
s->name, s->file);
N_("credential of wrong type for SCC:%s", ""),
s->name);
sqlite3_reset(ctx->credstmt);
return KRB5_CC_END;
}
@@ -1079,8 +1179,8 @@ scc_remove_cred(krb5_context context,
ret = KRB5_CC_END;
krb5_set_error_message(context, ret,
N_("Credential of wrong type "
"for SCC:%s:%s", ""),
s->name, s->file);
"for SCC:%s", ""),
s->name);
break;
}
@@ -1134,6 +1234,7 @@ scc_set_flags(krb5_context context,
struct cache_iter {
char *drop;
char *file;
sqlite3 *db;
sqlite3_stmt *stmt;
};
@@ -1151,8 +1252,8 @@ scc_get_cache_first(krb5_context context, krb5_cc_cursor *cursor)
if (ctx == NULL)
return krb5_enomem(context);
ret = default_db(context, &ctx->db);
if (ctx->db == NULL) {
ret = default_db(context, NULL, &ctx->db, &ctx->file);
if (ret) {
free(ctx);
return ret;
}
@@ -1160,48 +1261,48 @@ scc_get_cache_first(krb5_context context, krb5_cc_cursor *cursor)
ret = asprintf(&name, "cacheIteration%pPid%d",
ctx, (int)getpid());
if (ret < 0 || name == NULL) {
sqlite3_close(ctx->db);
free(ctx);
return krb5_enomem(context);
sqlite3_close(ctx->db);
free(ctx);
return krb5_enomem(context);
}
ret = asprintf(&ctx->drop, "DROP TABLE %s", name);
if (ret < 0 || ctx->drop == NULL) {
sqlite3_close(ctx->db);
free(name);
free(ctx);
return krb5_enomem(context);
sqlite3_close(ctx->db);
free(name);
free(ctx);
return krb5_enomem(context);
}
ret = asprintf(&str, "CREATE TEMPORARY TABLE %s AS SELECT name FROM caches",
name);
name);
if (ret < 0 || str == NULL) {
sqlite3_close(ctx->db);
free(name);
free(ctx->drop);
free(ctx);
return krb5_enomem(context);
sqlite3_close(ctx->db);
free(name);
free(ctx->drop);
free(ctx);
return krb5_enomem(context);
}
ret = exec_stmt(context, ctx->db, str, KRB5_CC_IO);
free(str);
str = NULL;
if (ret) {
sqlite3_close(ctx->db);
free(name);
free(ctx->drop);
free(ctx);
return ret;
sqlite3_close(ctx->db);
free(name);
free(ctx->drop);
free(ctx);
return ret;
}
ret = asprintf(&str, "SELECT name FROM %s", name);
if (ret < 0 || str == NULL) {
exec_stmt(context, ctx->db, ctx->drop, 0);
sqlite3_close(ctx->db);
free(name);
free(ctx->drop);
free(ctx);
return krb5_enomem(context);
exec_stmt(context, ctx->db, ctx->drop, 0);
sqlite3_close(ctx->db);
free(name);
free(ctx->drop);
free(ctx);
return krb5_enomem(context);
}
free(name);
@@ -1249,10 +1350,13 @@ again:
goto again;
ret = _krb5_cc_allocate(context, &krb5_scc_ops, id);
if (ret)
return ret;
return scc_resolve(context, id, name);
if (ret == 0)
ret = scc_resolve(context, id, ctx->file, name);
if (ret) {
free(*id);
*id = NULL;
}
return ret;
}
static krb5_error_code KRB5_CALLCONV
@@ -1263,6 +1367,7 @@ scc_end_cache_get(krb5_context context, krb5_cc_cursor cursor)
exec_stmt(context, ctx->db, ctx->drop, 0);
sqlite3_finalize(ctx->stmt);
sqlite3_close(ctx->db);
free(ctx->file);
free(ctx->drop);
free(ctx);
return 0;
@@ -1304,7 +1409,7 @@ scc_move(krb5_context context, krb5_ccache from, krb5_ccache to)
}
}
sqlite3_bind_text(sfrom->ucachen, 1, sto->name, -1, NULL);
sqlite3_bind_text(sfrom->ucachen, 1, sto->sub, -1, NULL);
sqlite3_bind_int(sfrom->ucachen, 2, sfrom->cid);
do {
@@ -1334,20 +1439,8 @@ rollback:
static krb5_error_code KRB5_CALLCONV
scc_get_default_name(krb5_context context, char **str)
{
krb5_error_code ret;
char *name;
*str = NULL;
ret = get_def_name(context, &name);
if (ret)
return _krb5_expand_default_cc_name(context, KRB5_SCACHE_NAME, str);
ret = asprintf(str, "SCC:%s", name);
free(name);
if (ret < 0 || *str == NULL)
return krb5_enomem(context);
return 0;
return _krb5_expand_default_cc_name(context, KRB5_SCACHE_NAME, str);
}
static krb5_error_code KRB5_CALLCONV
@@ -1364,7 +1457,7 @@ scc_set_default(krb5_context context, krb5_ccache id)
return KRB5_CC_IO;
}
ret = sqlite3_bind_text(s->umaster, 1, s->name, -1, NULL);
ret = sqlite3_bind_text(s->umaster, 1, s->sub, -1, NULL);
if (ret) {
sqlite3_reset(s->umaster);
krb5_set_error_message(context, KRB5_CC_IO,