/*
SSSD
KCM Server - ccache JSON (un)marshalling for storing ccaches in
sssd-secrets
Copyright (C) Red Hat, 2017
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "config.h"
#include <stdio.h>
#include <talloc.h>
#include <jansson.h>
#include "util/util_creds.h"
#include "util/crypto/sss_crypto.h"
#include "responder/kcm/kcmsrv_ccache_pvt.h"
/* The base for storing secrets is:
*
* Under $base, there are two containers:
* /ccache - stores the ccaches
* /ntlm - stores NTLM creds [Not implement yet]
*
* There is also a special entry that contains the UUID of the default
* cache for this UID:
* /default - stores the UUID of the default ccache for this UID
*
* Each ccache has a name and an UUID. On the secrets level, the 'secret'
* is a concatenation of the stringified UUID and the name separated
* by a plus-sign.
*/
/*
* We keep the JSON representation of the ccache versioned to allow
* us to modify the format in a future version
*/
/*
* The secrets store is a key-value store at heart. We store the UUID
* and the name in the key to allow easy lookups be either key
*/
/* Compat definition of json_array_foreach for older systems */
#ifndef json_array_foreach
for(idx = 0; \
idx++)
#endif
{
return talloc_asprintf(mem_ctx,
}
const char *sec_key)
{
return talloc_asprintf(mem_ctx,
KCM_SEC_CCACHE_FMT"%s",
sec_key);
}
{
return talloc_asprintf(mem_ctx,
}
const char *name,
{
return talloc_asprintf(mem_ctx,
}
{
return false;
}
/* One char for separator (at UUID_STR_SIZE, because strlen doesn't
* include the '\0', but UUID_STR_SIZE does) and at least one for
* the name */
return false;
}
return false;
}
return true;
}
const char *sec_key,
const char **_name,
{
if (!sec_key_valid(sec_key)) {
return EINVAL;
}
return EINVAL;
}
return ENOMEM;
}
return EOK;
}
{
if (!sec_key_valid(sec_key)) {
return EINVAL;
}
return EOK;
}
{
if (!sec_key_valid(sec_key)) {
return NULL;
}
return sec_key + UUID_STR_SIZE;
}
const char *name)
{
return false;
}
}
{
return false;
}
}
/*
* Creates an array of principal elements that will be used later
* in the form of:
* "components": [ "elem1", "elem2", ...]
*/
{
int ret;
char *str_princ_data;
data_array = json_array();
if (data_array == NULL) {
return NULL;
}
/* FIXME - it might be cleaner to use stringn here, but the libjansson
* version on RHEL-7 doesn't support that
*/
char,
if (str_princ_data == NULL) {
return NULL;
}
"Cannot convert principal data to string\n");
return NULL;
}
if (ret != 0) {
"Cannot append principal data to array\n");
return NULL;
}
/* data_array now owns the reference to jdata */
}
return data_array;
}
/* Creates:
* {
* "type": "number",
* "realm": "string",
* "components": [ "elem1", "elem2", ...]
* }
*/
{
char *str_realm_data;
if (components == NULL) {
"Cannot convert principal data to JSON\n");
return NULL;
}
/* FIXME - it might be cleaner to use the s% specifier here, but the libjansson
* version on RHEL-7 doesn't support that
*/
char,
if (str_realm_data == NULL) {
return NULL;
}
"{s:i, s:s, s:o}",
"realm", str_realm_data,
"components", components);
"Failed to pack JSON princ structure on line %d: %s\n",
return NULL;
}
return jprinc;
}
/* Creates:
* {
* "uuid": <data>,
* "payload": <data>,
* },
*/
{
char *base64_cred_blob;
if (base64_cred_blob == NULL) {
"Cannot base64 encode the certificate blob\n");
return NULL;
}
"{s:s, s:s}",
"uuid", uuid_str,
"payload", base64_cred_blob);
"Failed to pack JSON cred structure on line %d: %s\n",
return NULL;
}
return jcred;
}
/*
* Creates:
* [
* {
* "uuid": <data>,
* "payload": <data>,
* },
* ...
* ]
*/
{
array = json_array();
return NULL;
}
"Cannot convert credentials to JSON\n");
return NULL;
}
/* array now owns jcred */
}
return array;
}
/*
* The ccache is formatted in JSON as:
* {
* version: number
* kdc_offset: number
* principal : {
* "type": "number",
* "realm": "string",
* "components": [ "elem1", "elem2", ...]
* }
* creds : [
* {
* "uuid": <data>,
* "payload": <data>,
* },
* {
* ...
* }
* ]
* }
* }
*/
{
"Cannot convert princ to JSON\n");
return NULL;
}
"Cannot convert creds to JSON array\n");
return NULL;
}
"{s:i, s:i, s:o, s:o}",
"version", KS_JSON_VERSION,
"principal", princ,
"creds", creds);
"Failed to pack JSON ccache structure on line %d: %s\n",
return NULL;
}
return jcc;
}
struct kcm_ccache *cc,
const char **_sec_key,
const char **_sec_value)
{
char *jdump;
"Cannot convert ccache to JSON\n");
return ERR_JSON_ENCODING;
}
/* it would be more efficient to learn the size with json_dumpb and
* a NULL buffer, but that's only available since 2.10
*/
"Cannot dump JSON\n");
return ERR_JSON_ENCODING;
}
return ENOMEM;
}
return EOK;
}
struct kcm_ccache *cc,
const char **_url,
{
const char *key;
const char *value;
const char *url;
return ENOMEM;
}
"Cannot convert cache %s to JSON [%d]: %s\n",
goto done;
}
goto done;
}
"Cannot create payload buffer\n");
goto done;
}
done:
return ret;
}
{
int ok;
"Failed to parse JSON payload on line %d: %s\n",
return ERR_JSON_DECODING;
}
if (!ok) {
return ERR_JSON_DECODING;
}
return EOK;
}
/*
* ccache unmarshalling from JSON
*/
{
const char *str_value;
/* FIXME - it might be cleaner to use stringn here, but the libjansson
* version on RHEL-7 doesn't support that
*/
return EINVAL;
}
return ENOMEM;
}
return EOK;
}
{
int ok;
if (!ok) {
return ERR_JSON_DECODING;
}
if (len == 0) {
*_len = 0;
return EOK;
}
return ENOMEM;
}
"Cannot convert krb5 data element from JSON");
return ret;
}
}
return EOK;
}
{
int ok;
char *realm_str;
if (!ok) {
goto done;
}
goto done;
}
return ENOMEM;
}
/* FIXME - it might be cleaner to use the s% specifier here, but the libjansson
* version on RHEL-7 doesn't support that
*/
&error,
"{s:i, s:s, s:o}",
"realm", &realm_str,
"components", &components);
if (ret != 0) {
"Failed to unpack JSON princ structure on line %d: %s\n",
goto done;
}
return ENOMEM;
}
"Cannot convert principal from JSON");
goto done;
}
done:
return ret;
}
{
char *uuid_str;
const char *base64_cred_blob;
&error,
"{s:s, s:s}",
"uuid", &uuid_str,
"payload", &base64_cred_blob);
if (ret != 0) {
"Failed to unpack JSON cred structure on line %d: %s\n",
return EINVAL;
}
goto done;
}
goto done;
}
goto done;
}
goto done;
}
done:
return ret;
}
{
int ok;
if (!ok) {
return ERR_JSON_DECODING;
}
"Cannot convert JSON cred element [%d]: %s\n",
return ret;
}
"Cannot store creds in ccache [%d]: %s\n",
return ret;
}
}
return EOK;
}
{
int version;
&error,
"{s:i, s:i, s:o, s:o}",
"version", &version,
"principal", &princ,
"creds", &creds);
if (ret != 0) {
"Failed to unpack JSON creds structure on line %d: %s\n",
return EINVAL;
}
if (version != KS_JSON_VERSION) {
"Expected version %d, received version %d\n",
return EINVAL;
}
"Cannot store JSON to principal [%d]: %s\n",
return ret;
}
"Cannot store JSON to creds [%d]: %s\n",
return EOK;
}
return EOK;
}
/*
* sec_key is a concatenation of the ccache's UUID and name
* sec_value is the JSON dump of the ccache contents
*/
const char *sec_key,
const char *sec_value,
struct kcm_ccache **_cc)
{
"Cannot store secret to JSN [%d]: %s\n",
goto done;
}
goto done;
}
goto done;
}
/* We rely on sssd-secrets only searching the user's subtree so we
* set the ownership to the client
*/
"Cannt parse secret key [%d]: %s\n",
goto done;
}
"Cannt parse secret value [%d]: %s\n",
goto done;
}
done:
return ret;
}