cac0db2f8004ae88b9263dc3888a11a2d3d3d114Jakub Hrozek KCM Server - ccache JSON (un)marshalling for storing ccaches in
cac0db2f8004ae88b9263dc3888a11a2d3d3d114Jakub Hrozek sssd-secrets
cac0db2f8004ae88b9263dc3888a11a2d3d3d114Jakub Hrozek Copyright (C) Red Hat, 2017
cac0db2f8004ae88b9263dc3888a11a2d3d3d114Jakub Hrozek This program is free software; you can redistribute it and/or modify
cac0db2f8004ae88b9263dc3888a11a2d3d3d114Jakub Hrozek it under the terms of the GNU General Public License as published by
cac0db2f8004ae88b9263dc3888a11a2d3d3d114Jakub Hrozek the Free Software Foundation; either version 3 of the License, or
cac0db2f8004ae88b9263dc3888a11a2d3d3d114Jakub Hrozek (at your option) any later version.
cac0db2f8004ae88b9263dc3888a11a2d3d3d114Jakub Hrozek This program is distributed in the hope that it will be useful,
cac0db2f8004ae88b9263dc3888a11a2d3d3d114Jakub Hrozek but WITHOUT ANY WARRANTY; without even the implied warranty of
cac0db2f8004ae88b9263dc3888a11a2d3d3d114Jakub Hrozek MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
cac0db2f8004ae88b9263dc3888a11a2d3d3d114Jakub Hrozek GNU General Public License for more details.
cac0db2f8004ae88b9263dc3888a11a2d3d3d114Jakub Hrozek You should have received a copy of the GNU General Public License
cac0db2f8004ae88b9263dc3888a11a2d3d3d114Jakub Hrozek along with this program. If not, see <http://www.gnu.org/licenses/>.
cac0db2f8004ae88b9263dc3888a11a2d3d3d114Jakub Hrozek/* The base for storing secrets is:
cac0db2f8004ae88b9263dc3888a11a2d3d3d114Jakub Hrozek * Under $base, there are two containers:
cac0db2f8004ae88b9263dc3888a11a2d3d3d114Jakub Hrozek * /ccache - stores the ccaches
cac0db2f8004ae88b9263dc3888a11a2d3d3d114Jakub Hrozek * /ntlm - stores NTLM creds [Not implement yet]
cac0db2f8004ae88b9263dc3888a11a2d3d3d114Jakub Hrozek * There is also a special entry that contains the UUID of the default
cac0db2f8004ae88b9263dc3888a11a2d3d3d114Jakub Hrozek * cache for this UID:
cac0db2f8004ae88b9263dc3888a11a2d3d3d114Jakub Hrozek * /default - stores the UUID of the default ccache for this UID
cac0db2f8004ae88b9263dc3888a11a2d3d3d114Jakub Hrozek * Each ccache has a name and an UUID. On the secrets level, the 'secret'
cac0db2f8004ae88b9263dc3888a11a2d3d3d114Jakub Hrozek * is a concatenation of the stringified UUID and the name separated
cac0db2f8004ae88b9263dc3888a11a2d3d3d114Jakub Hrozek * by a plus-sign.
cac0db2f8004ae88b9263dc3888a11a2d3d3d114Jakub Hrozek#define KCM_SEC_URL "http://localhost/kcm/persistent"
cac0db2f8004ae88b9263dc3888a11a2d3d3d114Jakub Hrozek#define KCM_SEC_BASE_FMT KCM_SEC_URL"/%"SPRIuid"/"
cac0db2f8004ae88b9263dc3888a11a2d3d3d114Jakub Hrozek#define KCM_SEC_CCACHE_FMT KCM_SEC_BASE_FMT"ccache/"
cac0db2f8004ae88b9263dc3888a11a2d3d3d114Jakub Hrozek#define KCM_SEC_DFL_FMT KCM_SEC_BASE_FMT"default"
cac0db2f8004ae88b9263dc3888a11a2d3d3d114Jakub Hrozek * We keep the JSON representation of the ccache versioned to allow
cac0db2f8004ae88b9263dc3888a11a2d3d3d114Jakub Hrozek * us to modify the format in a future version
cac0db2f8004ae88b9263dc3888a11a2d3d3d114Jakub Hrozek * The secrets store is a key-value store at heart. We store the UUID
cac0db2f8004ae88b9263dc3888a11a2d3d3d114Jakub Hrozek * and the name in the key to allow easy lookups be either key
cac0db2f8004ae88b9263dc3888a11a2d3d3d114Jakub Hrozek/* Compat definition of json_array_foreach for older systems */
cac0db2f8004ae88b9263dc3888a11a2d3d3d114Jakub Hrozek idx < json_array_size(array) && (value = json_array_get(array, idx)); \
cac0db2f8004ae88b9263dc3888a11a2d3d3d114Jakub Hrozekconst char *sec_container_url_create(TALLOC_CTX *mem_ctx,
cac0db2f8004ae88b9263dc3888a11a2d3d3d114Jakub Hrozekconst char *sec_cc_url_create(TALLOC_CTX *mem_ctx,
cac0db2f8004ae88b9263dc3888a11a2d3d3d114Jakub Hrozekconst char *sec_dfl_url_create(TALLOC_CTX *mem_ctx,
cac0db2f8004ae88b9263dc3888a11a2d3d3d114Jakub Hrozekstatic const char *sec_key_create(TALLOC_CTX *mem_ctx,
cac0db2f8004ae88b9263dc3888a11a2d3d3d114Jakub Hrozek const char *name,
7d73049884e3a96ca3b00b5bd4104f4edd6287abJakub Hrozek return false;
7d73049884e3a96ca3b00b5bd4104f4edd6287abJakub Hrozek /* One char for separator (at UUID_STR_SIZE, because strlen doesn't
7d73049884e3a96ca3b00b5bd4104f4edd6287abJakub Hrozek * include the '\0', but UUID_STR_SIZE does) and at least one for
7d73049884e3a96ca3b00b5bd4104f4edd6287abJakub Hrozek * the name */
7d73049884e3a96ca3b00b5bd4104f4edd6287abJakub Hrozek DEBUG(SSSDBG_CRIT_FAILURE, "Key %s is too short\n", sec_key);
7d73049884e3a96ca3b00b5bd4104f4edd6287abJakub Hrozek return false;
7d73049884e3a96ca3b00b5bd4104f4edd6287abJakub Hrozek if (sec_key[UUID_STR_SIZE - 1] != SEC_KEY_SEPARATOR) {
7d73049884e3a96ca3b00b5bd4104f4edd6287abJakub Hrozek DEBUG(SSSDBG_CRIT_FAILURE, "Key doesn't contain the separator\n");
7d73049884e3a96ca3b00b5bd4104f4edd6287abJakub Hrozek return false;
7d73049884e3a96ca3b00b5bd4104f4edd6287abJakub Hrozek return true;
cac0db2f8004ae88b9263dc3888a11a2d3d3d114Jakub Hrozekstatic errno_t sec_key_parse(TALLOC_CTX *mem_ctx,
cac0db2f8004ae88b9263dc3888a11a2d3d3d114Jakub Hrozek const char **_name,
cac0db2f8004ae88b9263dc3888a11a2d3d3d114Jakub Hrozek strncpy(uuid_str, sec_key, sizeof(uuid_str)-1);
cac0db2f8004ae88b9263dc3888a11a2d3d3d114Jakub Hrozek if (sec_key[UUID_STR_SIZE - 1] != SEC_KEY_SEPARATOR) {
cac0db2f8004ae88b9263dc3888a11a2d3d3d114Jakub Hrozek DEBUG(SSSDBG_CRIT_FAILURE, "Key doesn't contain the separator\n");
cac0db2f8004ae88b9263dc3888a11a2d3d3d114Jakub Hrozek *_name = talloc_strdup(mem_ctx, sec_key + UUID_STR_SIZE);
cac0db2f8004ae88b9263dc3888a11a2d3d3d114Jakub Hrozekconst char *sec_key_get_name(const char *sec_key)
cac0db2f8004ae88b9263dc3888a11a2d3d3d114Jakub Hrozek const char *name)
cac0db2f8004ae88b9263dc3888a11a2d3d3d114Jakub Hrozek return false;
cac0db2f8004ae88b9263dc3888a11a2d3d3d114Jakub Hrozek return strcmp(sec_key + UUID_STR_SIZE, name) == 0;
cac0db2f8004ae88b9263dc3888a11a2d3d3d114Jakub Hrozek DEBUG(SSSDBG_MINOR_FAILURE, "Cannot convert key to UUID\n");
cac0db2f8004ae88b9263dc3888a11a2d3d3d114Jakub Hrozek return false;
cac0db2f8004ae88b9263dc3888a11a2d3d3d114Jakub Hrozek * Creates an array of principal elements that will be used later
cac0db2f8004ae88b9263dc3888a11a2d3d3d114Jakub Hrozek * in the form of:
a02a5ed51178b2cbede0396d66aed716b8898096René Genz * "components": [ "elem1", "elem2", ...]
cac0db2f8004ae88b9263dc3888a11a2d3d3d114Jakub Hrozekstatic json_t *princ_data_to_json(TALLOC_CTX *mem_ctx,
cac0db2f8004ae88b9263dc3888a11a2d3d3d114Jakub Hrozek /* FIXME - it might be cleaner to use stringn here, but the libjansson
cac0db2f8004ae88b9263dc3888a11a2d3d3d114Jakub Hrozek * version on RHEL-7 doesn't support that
cac0db2f8004ae88b9263dc3888a11a2d3d3d114Jakub Hrozek memcpy(str_princ_data, princ->data[i].data, princ->data[i].length);
cac0db2f8004ae88b9263dc3888a11a2d3d3d114Jakub Hrozek "Cannot convert principal data to string\n");
cac0db2f8004ae88b9263dc3888a11a2d3d3d114Jakub Hrozek ret = json_array_append_new(data_array, jdata);
cac0db2f8004ae88b9263dc3888a11a2d3d3d114Jakub Hrozek "Cannot append principal data to array\n");
cac0db2f8004ae88b9263dc3888a11a2d3d3d114Jakub Hrozek /* data_array now owns the reference to jdata */
cac0db2f8004ae88b9263dc3888a11a2d3d3d114Jakub Hrozek * "type": "number",
cac0db2f8004ae88b9263dc3888a11a2d3d3d114Jakub Hrozek * "realm": "string",
90503ff5aa1b2ba9f265ec5f9526539c9c377ca7Lukas Slebodnik * "components": [ "elem1", "elem2", ...]
cac0db2f8004ae88b9263dc3888a11a2d3d3d114Jakub Hrozekstatic json_t *princ_to_json(TALLOC_CTX *mem_ctx,
cac0db2f8004ae88b9263dc3888a11a2d3d3d114Jakub Hrozek components = princ_data_to_json(mem_ctx, princ);
cac0db2f8004ae88b9263dc3888a11a2d3d3d114Jakub Hrozek "Cannot convert principal data to JSON\n");
cac0db2f8004ae88b9263dc3888a11a2d3d3d114Jakub Hrozek /* FIXME - it might be cleaner to use the s% specifier here, but the libjansson
cac0db2f8004ae88b9263dc3888a11a2d3d3d114Jakub Hrozek * version on RHEL-7 doesn't support that
cac0db2f8004ae88b9263dc3888a11a2d3d3d114Jakub Hrozek memcpy(str_realm_data, princ->realm.data, princ->realm.length);
cac0db2f8004ae88b9263dc3888a11a2d3d3d114Jakub Hrozek "{s:i, s:s, s:o}",
cac0db2f8004ae88b9263dc3888a11a2d3d3d114Jakub Hrozek "Failed to pack JSON princ structure on line %d: %s\n",
cac0db2f8004ae88b9263dc3888a11a2d3d3d114Jakub Hrozek * "uuid": <data>,
cac0db2f8004ae88b9263dc3888a11a2d3d3d114Jakub Hrozek * "payload": <data>,
cac0db2f8004ae88b9263dc3888a11a2d3d3d114Jakub Hrozekstatic json_t *cred_to_json(struct kcm_cred *crd)
cac0db2f8004ae88b9263dc3888a11a2d3d3d114Jakub Hrozek cred_blob_data = sss_iobuf_get_data(crd->cred_blob);
cac0db2f8004ae88b9263dc3888a11a2d3d3d114Jakub Hrozek cred_blob_size = sss_iobuf_get_size(crd->cred_blob);
cac0db2f8004ae88b9263dc3888a11a2d3d3d114Jakub Hrozek base64_cred_blob = sss_base64_encode(crd, cred_blob_data, cred_blob_size);
cac0db2f8004ae88b9263dc3888a11a2d3d3d114Jakub Hrozek "Cannot base64 encode the certificate blob\n");
cac0db2f8004ae88b9263dc3888a11a2d3d3d114Jakub Hrozek "{s:s, s:s}",
cac0db2f8004ae88b9263dc3888a11a2d3d3d114Jakub Hrozek "Failed to pack JSON cred structure on line %d: %s\n",
cac0db2f8004ae88b9263dc3888a11a2d3d3d114Jakub Hrozek * "uuid": <data>,
cac0db2f8004ae88b9263dc3888a11a2d3d3d114Jakub Hrozek * "payload": <data>,
cac0db2f8004ae88b9263dc3888a11a2d3d3d114Jakub Hrozekstatic json_t *creds_to_json_array(struct kcm_cred *creds)
cac0db2f8004ae88b9263dc3888a11a2d3d3d114Jakub Hrozek "Cannot convert credentials to JSON\n");
cac0db2f8004ae88b9263dc3888a11a2d3d3d114Jakub Hrozek /* array now owns jcred */
cac0db2f8004ae88b9263dc3888a11a2d3d3d114Jakub Hrozek * The ccache is formatted in JSON as:
cac0db2f8004ae88b9263dc3888a11a2d3d3d114Jakub Hrozek * version: number
cac0db2f8004ae88b9263dc3888a11a2d3d3d114Jakub Hrozek * kdc_offset: number
cac0db2f8004ae88b9263dc3888a11a2d3d3d114Jakub Hrozek * principal : {
cac0db2f8004ae88b9263dc3888a11a2d3d3d114Jakub Hrozek * "type": "number",
cac0db2f8004ae88b9263dc3888a11a2d3d3d114Jakub Hrozek * "realm": "string",
90503ff5aa1b2ba9f265ec5f9526539c9c377ca7Lukas Slebodnik * "components": [ "elem1", "elem2", ...]
cac0db2f8004ae88b9263dc3888a11a2d3d3d114Jakub Hrozek * "uuid": <data>,
cac0db2f8004ae88b9263dc3888a11a2d3d3d114Jakub Hrozek * "payload": <data>,
cac0db2f8004ae88b9263dc3888a11a2d3d3d114Jakub Hrozekstatic json_t *ccache_to_json(struct kcm_ccache *cc)
cac0db2f8004ae88b9263dc3888a11a2d3d3d114Jakub Hrozek "Cannot convert princ to JSON\n");
cac0db2f8004ae88b9263dc3888a11a2d3d3d114Jakub Hrozek "Cannot convert creds to JSON array\n");
cac0db2f8004ae88b9263dc3888a11a2d3d3d114Jakub Hrozek "{s:i, s:i, s:o, s:o}",
cac0db2f8004ae88b9263dc3888a11a2d3d3d114Jakub Hrozek "Failed to pack JSON ccache structure on line %d: %s\n",
cac0db2f8004ae88b9263dc3888a11a2d3d3d114Jakub Hrozekstatic errno_t ccache_to_sec_kv(TALLOC_CTX *mem_ctx,
cac0db2f8004ae88b9263dc3888a11a2d3d3d114Jakub Hrozek const char **_sec_key,
cac0db2f8004ae88b9263dc3888a11a2d3d3d114Jakub Hrozek "Cannot convert ccache to JSON\n");
cac0db2f8004ae88b9263dc3888a11a2d3d3d114Jakub Hrozek /* it would be more efficient to learn the size with json_dumpb and
cac0db2f8004ae88b9263dc3888a11a2d3d3d114Jakub Hrozek * a NULL buffer, but that's only available since 2.10
cac0db2f8004ae88b9263dc3888a11a2d3d3d114Jakub Hrozek jdump = json_dumps(jcc, JSON_INDENT(4) | JSON_ENSURE_ASCII);
cac0db2f8004ae88b9263dc3888a11a2d3d3d114Jakub Hrozek "Cannot dump JSON\n");
cac0db2f8004ae88b9263dc3888a11a2d3d3d114Jakub Hrozek *_sec_key = sec_key_create(mem_ctx, cc->name, cc->uuid);
cac0db2f8004ae88b9263dc3888a11a2d3d3d114Jakub Hrozek if (*_sec_key == NULL || *_sec_value == NULL) {
cac0db2f8004ae88b9263dc3888a11a2d3d3d114Jakub Hrozekerrno_t kcm_ccache_to_sec_input(TALLOC_CTX *mem_ctx,
cac0db2f8004ae88b9263dc3888a11a2d3d3d114Jakub Hrozek const char **_url,
cac0db2f8004ae88b9263dc3888a11a2d3d3d114Jakub Hrozek ret = ccache_to_sec_kv(mem_ctx, cc, &key, &value);
cac0db2f8004ae88b9263dc3888a11a2d3d3d114Jakub Hrozek "Cannot convert cache %s to JSON [%d]: %s\n",
cac0db2f8004ae88b9263dc3888a11a2d3d3d114Jakub Hrozek "Cannot create payload buffer\n");
cac0db2f8004ae88b9263dc3888a11a2d3d3d114Jakub Hrozekstatic errno_t sec_value_to_json(const char *input,
cac0db2f8004ae88b9263dc3888a11a2d3d3d114Jakub Hrozek "Failed to parse JSON payload on line %d: %s\n",
cac0db2f8004ae88b9263dc3888a11a2d3d3d114Jakub Hrozek DEBUG(SSSDBG_CRIT_FAILURE, "Json data is not an object.\n");
cac0db2f8004ae88b9263dc3888a11a2d3d3d114Jakub Hrozek * ccache unmarshalling from JSON
cac0db2f8004ae88b9263dc3888a11a2d3d3d114Jakub Hrozekstatic errno_t json_element_to_krb5_data(TALLOC_CTX *mem_ctx,
cac0db2f8004ae88b9263dc3888a11a2d3d3d114Jakub Hrozek /* FIXME - it might be cleaner to use stringn here, but the libjansson
cac0db2f8004ae88b9263dc3888a11a2d3d3d114Jakub Hrozek * version on RHEL-7 doesn't support that
cac0db2f8004ae88b9263dc3888a11a2d3d3d114Jakub Hrozek DEBUG(SSSDBG_CRIT_FAILURE, "JSON element not a string\n");
cac0db2f8004ae88b9263dc3888a11a2d3d3d114Jakub Hrozek data->data = talloc_strndup(mem_ctx, str_value, str_len);
cac0db2f8004ae88b9263dc3888a11a2d3d3d114Jakub Hrozekstatic errno_t json_array_to_krb5_data(TALLOC_CTX *mem_ctx,
cac0db2f8004ae88b9263dc3888a11a2d3d3d114Jakub Hrozek DEBUG(SSSDBG_CRIT_FAILURE, "Json object is not an array.\n");
cac0db2f8004ae88b9263dc3888a11a2d3d3d114Jakub Hrozek data = talloc_zero_array(mem_ctx, krb5_data, len);
cac0db2f8004ae88b9263dc3888a11a2d3d3d114Jakub Hrozek ret = json_element_to_krb5_data(data, element, &data[idx]);
cac0db2f8004ae88b9263dc3888a11a2d3d3d114Jakub Hrozek "Cannot convert krb5 data element from JSON");
cac0db2f8004ae88b9263dc3888a11a2d3d3d114Jakub Hrozekstatic errno_t json_to_princ(TALLOC_CTX *mem_ctx,
cac0db2f8004ae88b9263dc3888a11a2d3d3d114Jakub Hrozek DEBUG(SSSDBG_CRIT_FAILURE, "Json principal is not an object.\n");
cac0db2f8004ae88b9263dc3888a11a2d3d3d114Jakub Hrozek princ = talloc_zero(tmp_ctx, struct krb5_principal_data);
cac0db2f8004ae88b9263dc3888a11a2d3d3d114Jakub Hrozek /* FIXME - it might be cleaner to use the s% specifier here, but the libjansson
cac0db2f8004ae88b9263dc3888a11a2d3d3d114Jakub Hrozek * version on RHEL-7 doesn't support that
cac0db2f8004ae88b9263dc3888a11a2d3d3d114Jakub Hrozek "{s:i, s:s, s:o}",
cac0db2f8004ae88b9263dc3888a11a2d3d3d114Jakub Hrozek "Failed to unpack JSON princ structure on line %d: %s\n",
cac0db2f8004ae88b9263dc3888a11a2d3d3d114Jakub Hrozek princ->realm.data = talloc_strndup(mem_ctx, realm_str, realm_size);
cac0db2f8004ae88b9263dc3888a11a2d3d3d114Jakub Hrozek ret = json_array_to_krb5_data(princ, components,
cac0db2f8004ae88b9263dc3888a11a2d3d3d114Jakub Hrozek "Cannot convert principal from JSON");
cac0db2f8004ae88b9263dc3888a11a2d3d3d114Jakub Hrozekstatic errno_t json_elem_to_cred(TALLOC_CTX *mem_ctx,
cac0db2f8004ae88b9263dc3888a11a2d3d3d114Jakub Hrozek "{s:s, s:s}",
cac0db2f8004ae88b9263dc3888a11a2d3d3d114Jakub Hrozek "Failed to unpack JSON cred structure on line %d: %s\n",
cac0db2f8004ae88b9263dc3888a11a2d3d3d114Jakub Hrozek outbuf = sss_base64_decode(tmp_ctx, base64_cred_blob, &outbuf_size);
cac0db2f8004ae88b9263dc3888a11a2d3d3d114Jakub Hrozek DEBUG(SSSDBG_CRIT_FAILURE, "Cannot decode cred blob\n");
cac0db2f8004ae88b9263dc3888a11a2d3d3d114Jakub Hrozek cred_blob = sss_iobuf_init_readonly(tmp_ctx, outbuf, outbuf_size);
cac0db2f8004ae88b9263dc3888a11a2d3d3d114Jakub Hrozekstatic errno_t json_to_creds(struct kcm_ccache *cc,
cac0db2f8004ae88b9263dc3888a11a2d3d3d114Jakub Hrozek DEBUG(SSSDBG_CRIT_FAILURE, "Json creds object is not an array.\n");
cac0db2f8004ae88b9263dc3888a11a2d3d3d114Jakub Hrozek "Cannot convert JSON cred element [%d]: %s\n",
cac0db2f8004ae88b9263dc3888a11a2d3d3d114Jakub Hrozek "Cannot store creds in ccache [%d]: %s\n",
cac0db2f8004ae88b9263dc3888a11a2d3d3d114Jakub Hrozekstatic errno_t sec_json_value_to_ccache(struct kcm_ccache *cc,
cac0db2f8004ae88b9263dc3888a11a2d3d3d114Jakub Hrozek "{s:i, s:i, s:o, s:o}",
cac0db2f8004ae88b9263dc3888a11a2d3d3d114Jakub Hrozek "Failed to unpack JSON creds structure on line %d: %s\n",
cac0db2f8004ae88b9263dc3888a11a2d3d3d114Jakub Hrozek "Expected version %d, received version %d\n",
cac0db2f8004ae88b9263dc3888a11a2d3d3d114Jakub Hrozek "Cannot store JSON to principal [%d]: %s\n",
cac0db2f8004ae88b9263dc3888a11a2d3d3d114Jakub Hrozek "Cannot store JSON to creds [%d]: %s\n",
cac0db2f8004ae88b9263dc3888a11a2d3d3d114Jakub Hrozek * sec_key is a concatenation of the ccache's UUID and name
cac0db2f8004ae88b9263dc3888a11a2d3d3d114Jakub Hrozek * sec_value is the JSON dump of the ccache contents
cac0db2f8004ae88b9263dc3888a11a2d3d3d114Jakub Hrozek "Cannot store secret to JSN [%d]: %s\n",
cac0db2f8004ae88b9263dc3888a11a2d3d3d114Jakub Hrozek /* We rely on sssd-secrets only searching the user's subtree so we
cac0db2f8004ae88b9263dc3888a11a2d3d3d114Jakub Hrozek * set the ownership to the client
cac0db2f8004ae88b9263dc3888a11a2d3d3d114Jakub Hrozek ret = sec_key_parse(cc, sec_key, &cc->name, cc->uuid);
cac0db2f8004ae88b9263dc3888a11a2d3d3d114Jakub Hrozek "Cannt parse secret key [%d]: %s\n",
cac0db2f8004ae88b9263dc3888a11a2d3d3d114Jakub Hrozek "Cannt parse secret value [%d]: %s\n",