/* Convert a JSON value to a ccache handle or to NULL. Set *new_out to true if * the ccache handle is a newly created memory ccache, false otherwise. */ static int json_to_ccache(krb5_context context, k5_json_value v, krb5_ccache *ccache_out, krb5_boolean *new_out) { krb5_error_code ret; krb5_ccache ccache = NULL; krb5_principal princ; krb5_creds creds; k5_json_array array; size_t i, len; *ccache_out = NULL; *new_out = FALSE; if (k5_json_get_tid(v) == K5_JSON_TID_NULL) return 0; if (k5_json_get_tid(v) == K5_JSON_TID_STRING) { /* We got a reference to an external ccache; just resolve it. */ return krb5_cc_resolve(context, k5_json_string_utf8(v), ccache_out) ? -1 : 0; } /* We got the contents of a memory ccache. */ if (k5_json_get_tid(v) != K5_JSON_TID_ARRAY) return -1; array = v; len = k5_json_array_length(array); if (len < 1) return -1; /* Initialize a new memory ccache using the principal in the first array * entry.*/ if (krb5_cc_new_unique(context, "MEMORY", NULL, &ccache)) return -1; if (json_to_principal(context, k5_json_array_get(array, 0), &princ)) goto invalid; ret = krb5_cc_initialize(context, ccache, princ); krb5_free_principal(context, princ); if (ret) goto invalid; /* Add remaining array entries to the ccache as credentials. */ for (i = 1; i < len; i++) { if (json_to_creds(context, k5_json_array_get(array, 1), &creds)) goto invalid; ret = krb5_cc_store_cred(context, ccache, &creds); krb5_free_cred_contents(context, &creds); if (ret) goto invalid; } *ccache_out = ccache; *new_out = TRUE; return 0; invalid: (void)krb5_cc_destroy(context, ccache); return -1; }
/* Convert a JSON value to a null-terminated authdata list or to NULL. */ static int json_to_authdata(krb5_context context, k5_json_value v, krb5_authdata ***authdata_out) { k5_json_array array; krb5_authdata **authdata = NULL; size_t len, i; *authdata_out = NULL; if (k5_json_get_tid(v) == K5_JSON_TID_NULL) return 0; if (k5_json_get_tid(v) != K5_JSON_TID_ARRAY) return -1; array = v; len = k5_json_array_length(array); authdata = calloc(len + 1, sizeof(*authdata)); for (i = 0; i < len; i++) { if (json_to_authdata_element(k5_json_array_get(array, i), &authdata[i])) goto invalid; } authdata[i] = NULL; *authdata_out = authdata; return 0; invalid: krb5_free_authdata(context, authdata); return -1; }
/* Convert a JSON value to a null-terminated list of krb5 addresses or to * NULL. */ static int json_to_addresses(krb5_context context, k5_json_value v, krb5_address ***addresses_out) { k5_json_array array; krb5_address **addrs = NULL; size_t len, i; *addresses_out = NULL; if (k5_json_get_tid(v) == K5_JSON_TID_NULL) return 0; if (k5_json_get_tid(v) != K5_JSON_TID_ARRAY) return -1; array = v; len = k5_json_array_length(array); addrs = calloc(len + 1, sizeof(*addrs)); for (i = 0; i < len; i++) { if (json_to_address(k5_json_array_get(array, i), &addrs[i])) goto invalid; } addrs[i] = NULL; *addresses_out = addrs; return 0; invalid: krb5_free_addresses(context, addrs); return -1; }
/* Return the idx element of array if it is of type tid; otherwise return * NULL. The caller is responsible for checking the array length. */ static k5_json_value check_element(k5_json_array array, size_t idx, k5_json_tid tid) { k5_json_value v; v = k5_json_array_get(array, idx); return (k5_json_get_tid(v) == tid) ? v : NULL; }
/* Converts a JSON object into a krb5_responder_otp_challenge. */ static krb5_responder_otp_challenge * codec_decode_challenge(krb5_context ctx, const char *json) { krb5_responder_otp_challenge *chl = NULL; k5_json_value obj = NULL, arr = NULL, tmp = NULL; krb5_error_code retval; size_t i; retval = k5_json_decode(json, &obj); if (retval != 0) goto error; if (k5_json_get_tid(obj) != K5_JSON_TID_OBJECT) goto error; arr = k5_json_object_get(obj, "tokenInfo"); if (arr == NULL) goto error; if (k5_json_get_tid(arr) != K5_JSON_TID_ARRAY) goto error; chl = calloc(1, sizeof(krb5_responder_otp_challenge)); if (chl == NULL) goto error; chl->tokeninfo = calloc(k5_json_array_length(arr) + 1, sizeof(krb5_responder_otp_tokeninfo*)); if (chl->tokeninfo == NULL) goto error; retval = codec_value_to_string(obj, "service", &chl->service); if (retval != 0 && retval != ENOENT) goto error; for (i = 0; i < k5_json_array_length(arr); i++) { tmp = k5_json_array_get(arr, i); if (k5_json_get_tid(tmp) != K5_JSON_TID_OBJECT) goto error; chl->tokeninfo[i] = codec_decode_tokeninfo(tmp); if (chl->tokeninfo[i] == NULL) goto error; } k5_json_release(obj); return chl; error: if (chl != NULL) { for (i = 0; chl->tokeninfo != NULL && chl->tokeninfo[i] != NULL; i++) free_tokeninfo(chl->tokeninfo[i]); free(chl->tokeninfo); free(chl); } k5_json_release(obj); return NULL; }
OM_uint32 KRB5_CALLCONV krb5_gss_import_cred(OM_uint32 *minor_status, gss_buffer_t token, gss_cred_id_t *cred_handle) { OM_uint32 status = GSS_S_COMPLETE; krb5_context context; krb5_error_code ret; krb5_gss_cred_id_t cred; k5_json_value v = NULL; k5_json_array array; k5_json_string str; char *copy = NULL; ret = krb5_gss_init_context(&context); if (ret) { *minor_status = ret; return GSS_S_FAILURE; } /* Decode token. */ copy = k5memdup0(token->value, token->length, &ret); if (copy == NULL) { status = GSS_S_FAILURE; *minor_status = ret; goto cleanup; } if (k5_json_decode(copy, &v)) goto invalid; /* Decode the CRED_EXPORT_MAGIC array wrapper. */ if (k5_json_get_tid(v) != K5_JSON_TID_ARRAY) goto invalid; array = v; if (k5_json_array_length(array) != 2) goto invalid; str = check_element(array, 0, K5_JSON_TID_STRING); if (str == NULL || strcmp(k5_json_string_utf8(str), CRED_EXPORT_MAGIC) != 0) goto invalid; if (json_to_kgcred(context, k5_json_array_get(array, 1), &cred)) goto invalid; *cred_handle = (gss_cred_id_t)cred; cleanup: free(copy); k5_json_release(v); krb5_free_context(context); return status; invalid: status = GSS_S_DEFECTIVE_TOKEN; goto cleanup; }
/* Convert a JSON value to a krb5 GSS name or to NULL. */ static int json_to_kgname(krb5_context context, k5_json_value v, krb5_gss_name_t *name_out) { k5_json_array array; krb5_gss_name_t name = NULL; *name_out = NULL; if (k5_json_get_tid(v) == K5_JSON_TID_NULL) return 0; if (k5_json_get_tid(v) != K5_JSON_TID_ARRAY) return -1; array = v; if (k5_json_array_length(array) != 3) return -1; name = calloc(1, sizeof(*name)); if (name == NULL) return -1; if (k5_mutex_init(&name->lock)) { free(name); return -1; } if (json_to_principal(context, k5_json_array_get(array, 0), &name->princ)) goto invalid; if (json_to_optional_string(k5_json_array_get(array, 1), &name->service)) goto invalid; if (json_to_optional_string(k5_json_array_get(array, 2), &name->host)) goto invalid; *name_out = name; return 0; invalid: kg_release_name(context, &name); return -1; }
/* Convert a JSON array value to a krb5 GSS credential. */ static int json_to_kgcred(krb5_context context, k5_json_array array, krb5_gss_cred_id_t *cred_out) { krb5_gss_cred_id_t cred; k5_json_number n; k5_json_bool b; krb5_boolean is_new; OM_uint32 tmp; *cred_out = NULL; if (k5_json_array_length(array) != 14) return -1; cred = calloc(1, sizeof(*cred)); if (cred == NULL) return -1; if (k5_mutex_init(&cred->lock)) { free(cred); return -1; } n = check_element(array, 0, K5_JSON_TID_NUMBER); if (n == NULL) goto invalid; cred->usage = k5_json_number_value(n); if (json_to_kgname(context, k5_json_array_get(array, 1), &cred->name)) goto invalid; if (json_to_principal(context, k5_json_array_get(array, 2), &cred->impersonator)) goto invalid; b = check_element(array, 3, K5_JSON_TID_BOOL); if (b == NULL) goto invalid; cred->default_identity = k5_json_bool_value(b); b = check_element(array, 4, K5_JSON_TID_BOOL); if (b == NULL) goto invalid; cred->iakerb_mech = k5_json_bool_value(b); if (json_to_keytab(context, k5_json_array_get(array, 5), &cred->keytab)) goto invalid; if (json_to_rcache(context, k5_json_array_get(array, 6), &cred->rcache)) goto invalid; if (json_to_ccache(context, k5_json_array_get(array, 7), &cred->ccache, &is_new)) goto invalid; cred->destroy_ccache = is_new; if (json_to_keytab(context, k5_json_array_get(array, 8), &cred->client_keytab)) goto invalid; b = check_element(array, 9, K5_JSON_TID_BOOL); if (b == NULL) goto invalid; cred->have_tgt = k5_json_bool_value(b); n = check_element(array, 10, K5_JSON_TID_NUMBER); if (n == NULL) goto invalid; cred->expire = k5_json_number_value(n); n = check_element(array, 11, K5_JSON_TID_NUMBER); if (n == NULL) goto invalid; cred->refresh_time = k5_json_number_value(n); if (json_to_etypes(k5_json_array_get(array, 12), &cred->req_enctypes)) goto invalid; if (json_to_optional_string(k5_json_array_get(array, 13), &cred->password)) goto invalid; *cred_out = cred; return 0; invalid: (void)krb5_gss_release_cred(&tmp, (gss_cred_id_t *)&cred); return -1; }
/* Convert a JSON value to a krb5 credential structure, filling in creds. */ static int json_to_creds(krb5_context context, k5_json_value v, krb5_creds *creds) { k5_json_array array; k5_json_number n; k5_json_bool b; k5_json_string s; unsigned char *data; size_t len; memset(creds, 0, sizeof(*creds)); if (k5_json_get_tid(v) != K5_JSON_TID_ARRAY) return -1; array = v; if (k5_json_array_length(array) != 13) return -1; if (json_to_principal(context, k5_json_array_get(array, 0), &creds->client)) goto invalid; if (json_to_principal(context, k5_json_array_get(array, 1), &creds->server)) goto invalid; if (json_to_keyblock(k5_json_array_get(array, 2), &creds->keyblock)) goto invalid; n = check_element(array, 3, K5_JSON_TID_NUMBER); if (n == NULL) goto invalid; creds->times.authtime = k5_json_number_value(n); n = check_element(array, 4, K5_JSON_TID_NUMBER); if (n == NULL) goto invalid; creds->times.starttime = k5_json_number_value(n); n = check_element(array, 5, K5_JSON_TID_NUMBER); if (n == NULL) goto invalid; creds->times.endtime = k5_json_number_value(n); n = check_element(array, 6, K5_JSON_TID_NUMBER); if (n == NULL) goto invalid; creds->times.renew_till = k5_json_number_value(n); b = check_element(array, 7, K5_JSON_TID_BOOL); if (b == NULL) goto invalid; creds->is_skey = k5_json_bool_value(b); n = check_element(array, 8, K5_JSON_TID_NUMBER); if (n == NULL) goto invalid; creds->ticket_flags = k5_json_number_value(n); if (json_to_addresses(context, k5_json_array_get(array, 9), &creds->addresses)) goto invalid; s = check_element(array, 10, K5_JSON_TID_STRING); if (s == NULL) goto invalid; if (k5_json_string_unbase64(s, &data, &len)) goto invalid; creds->ticket.data = (char *)data; creds->ticket.length = len; s = check_element(array, 11, K5_JSON_TID_STRING); if (s == NULL) goto invalid; if (k5_json_string_unbase64(s, &data, &len)) goto invalid; creds->second_ticket.data = (char *)data; creds->second_ticket.length = len; if (json_to_authdata(context, k5_json_array_get(array, 12), &creds->authdata)) goto invalid; creds->magic = KV5M_CREDS; return 0; invalid: krb5_free_cred_contents(context, creds); memset(creds, 0, sizeof(*creds)); return -1; }