Beispiel #1
0
/* 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;
}
Beispiel #2
0
/* Converts a krb5_pa_otp_challenge into a JSON object. */
static krb5_error_code
codec_encode_challenge(krb5_context ctx, krb5_pa_otp_challenge *chl,
                       char **json)
{
    k5_json_object obj = NULL, tmp = NULL;
    k5_json_string str = NULL;
    k5_json_array arr = NULL;
    krb5_error_code retval;
    int i;

    retval = k5_json_object_create(&obj);
    if (retval != 0)
        goto cleanup;

    if (chl->service.data) {
        retval = k5_json_string_create_len(chl->service.data,
                                           chl->service.length, &str);
        if (retval != 0)
            goto cleanup;
        retval = k5_json_object_set(obj, "service", str);
        k5_json_release(str);
        if (retval != 0)
            goto cleanup;
    }

    retval = k5_json_array_create(&arr);
    if (retval != 0)
        goto cleanup;

    for (i = 0; chl->tokeninfo[i] != NULL ; i++) {
        retval = codec_encode_tokeninfo(chl->tokeninfo[i], &tmp);
        if (retval != 0)
            goto cleanup;

        retval = k5_json_array_add(arr, tmp);
        k5_json_release(tmp);
        if (retval != 0)
            goto cleanup;
    }

    retval = k5_json_object_set(obj, "tokenInfo", arr);
    if (retval != 0)
        goto cleanup;

    retval = k5_json_encode(obj, json);
    if (retval)
        goto cleanup;

cleanup:
    k5_json_release(arr);
    k5_json_release(obj);
    return retval;
}
Beispiel #3
0
/* Converts a krb5_otp_tokeninfo into a JSON object. */
static krb5_error_code
codec_encode_tokeninfo(krb5_otp_tokeninfo *ti, k5_json_object *out)
{
    krb5_error_code retval;
    k5_json_object obj;
    krb5_flags flags;

    retval = k5_json_object_create(&obj);
    if (retval != 0)
        goto error;

    flags = KRB5_RESPONDER_OTP_FLAGS_COLLECT_TOKEN;
    if (ti->flags & KRB5_OTP_FLAG_COLLECT_PIN) {
        flags |= KRB5_RESPONDER_OTP_FLAGS_COLLECT_PIN;
        if (ti->flags & KRB5_OTP_FLAG_SEPARATE_PIN)
            flags |= KRB5_RESPONDER_OTP_FLAGS_NEXTOTP;
    }
    if (ti->flags & KRB5_OTP_FLAG_NEXTOTP)
        flags |= KRB5_RESPONDER_OTP_FLAGS_NEXTOTP;

    retval = codec_int32_to_value(flags, obj, "flags");
    if (retval != 0)
        goto error;

    retval = codec_data_to_value(&ti->vendor, obj, "vendor");
    if (retval != 0)
        goto error;

    retval = codec_data_to_value(&ti->challenge, obj, "challenge");
    if (retval != 0)
        goto error;

    retval = codec_int32_to_value(ti->length, obj, "length");
    if (retval != 0)
        goto error;

    if (ti->format != KRB5_OTP_FORMAT_BASE64 &&
        ti->format != KRB5_OTP_FORMAT_BINARY) {
        retval = codec_int32_to_value(ti->format, obj, "format");
        if (retval != 0)
            goto error;
    }

    retval = codec_data_to_value(&ti->token_id, obj, "tokenID");
    if (retval != 0)
        goto error;

    retval = codec_data_to_value(&ti->alg_id, obj, "algID");
    if (retval != 0)
        goto error;

    *out = obj;
    return 0;

error:
    k5_json_release(obj);
    return retval;
}
Beispiel #4
0
static void
array_dealloc(void *ptr)
{
    k5_json_array array = ptr;
    size_t i;

    for (i = 0; i < array->len; i++)
        k5_json_release(array->values[i]);
    free(array->values);
}
Beispiel #5
0
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;
}
Beispiel #6
0
/* Decode the responder answer into a tokeninfo, a value and a pin. */
static krb5_error_code
codec_decode_answer(krb5_context context, const char *answer,
                    krb5_otp_tokeninfo **tis, krb5_otp_tokeninfo **ti,
                    krb5_data *value, krb5_data *pin)
{
    krb5_error_code retval;
    k5_json_value val = NULL;
    krb5_int32 indx, i;
    krb5_data tmp;

    if (answer == NULL)
        return EBADMSG;

    retval = k5_json_decode(answer, &val);
    if (retval != 0)
        goto cleanup;

    if (k5_json_get_tid(val) != K5_JSON_TID_OBJECT)
        goto cleanup;

    retval = codec_value_to_int32(val, "tokeninfo", &indx);
    if (retval != 0)
        goto cleanup;

    for (i = 0; tis[i] != NULL; i++) {
        if (i == indx) {
            retval = codec_value_to_data(val, "value", &tmp);
            if (retval != 0 && retval != ENOENT)
                goto cleanup;

            retval = codec_value_to_data(val, "pin", pin);
            if (retval != 0 && retval != ENOENT) {
                krb5_free_data_contents(context, &tmp);
                goto cleanup;
            }

            *value = tmp;
            *ti = tis[i];
            retval = 0;
            goto cleanup;
        }
    }
    retval = EINVAL;

cleanup:
    k5_json_release(val);
    return retval;
}
Beispiel #7
0
/* Converts a krb5_data struct into a property of a JSON object. */
static krb5_error_code
codec_data_to_value(krb5_data *data, k5_json_object obj, const char *key)
{
    krb5_error_code retval;
    k5_json_string str;

    if (data->data == NULL)
        return 0;

    retval = k5_json_string_create_len(data->data, data->length, &str);
    if (retval)
        return retval;

    retval = k5_json_object_set(obj, key, str);
    k5_json_release(str);
    return retval;
}
Beispiel #8
0
/* Converts a krb5_int32 into a property of a JSON object. */
static krb5_error_code
codec_int32_to_value(krb5_int32 int32, k5_json_object obj, const char *key)
{
    krb5_error_code retval;
    k5_json_number num;

    if (int32 == -1)
        return 0;

    retval = k5_json_number_create(int32, &num);
    if (retval)
        return retval;

    retval = k5_json_object_set(obj, key, num);
    k5_json_release(num);
    return retval;
}
Beispiel #9
0
static krb5_error_code
responder(krb5_context ctx, void *rawdata, krb5_responder_context rctx)
{
    krb5_error_code err;
    char *key, *value, *pin, *encoded1, *encoded2;
    const char *challenge;
    k5_json_value decoded1, decoded2;
    k5_json_object ids;
    k5_json_number val;
    krb5_int32 token_flags;
    struct responder_data *data = rawdata;
    krb5_responder_pkinit_challenge *chl;
    krb5_responder_otp_challenge *ochl;
    unsigned int i, n;

    data->called = TRUE;

    /* Check that a particular challenge has the specified expected value. */
    if (data->challenge != NULL) {
        /* Separate the challenge name and its expected value. */
        key = strdup(data->challenge);
        if (key == NULL)
            exit(ENOMEM);
        value = key + strcspn(key, "=");
        if (*value != '\0')
            *value++ = '\0';
        /* Read the challenge. */
        challenge = krb5_responder_get_challenge(ctx, rctx, key);
        err = k5_json_decode(value, &decoded1);
        /* Check for "no challenge". */
        if (challenge == NULL && *value == '\0') {
            fprintf(stderr, "OK: (no challenge) == (no challenge)\n");
        } else if (err != 0) {
            /* It's not JSON, so assume we're just after a string compare. */
            if (strcmp(challenge, value) == 0) {
                fprintf(stderr, "OK: \"%s\" == \"%s\"\n", challenge, value);
            } else {
                fprintf(stderr, "ERROR: \"%s\" != \"%s\"\n", challenge, value);
                exit(1);
            }
        } else {
            /* Assume we're after a JSON compare - decode the actual value. */
            err = k5_json_decode(challenge, &decoded2);
            if (err != 0) {
                fprintf(stderr, "error decoding \"%s\"\n", challenge);
                exit(1);
            }
            /* Re-encode the expected challenge and the actual challenge... */
            err = k5_json_encode(decoded1, &encoded1);
            if (err != 0) {
                fprintf(stderr, "error encoding json data\n");
                exit(1);
            }
            err = k5_json_encode(decoded2, &encoded2);
            if (err != 0) {
                fprintf(stderr, "error encoding json data\n");
                exit(1);
            }
            k5_json_release(decoded1);
            k5_json_release(decoded2);
            /* ... and see if they look the same. */
            if (strcmp(encoded1, encoded2) == 0) {
                fprintf(stderr, "OK: \"%s\" == \"%s\"\n", encoded1, encoded2);
            } else {
                fprintf(stderr, "ERROR: \"%s\" != \"%s\"\n", encoded1,
                        encoded2);
                exit(1);
            }
            free(encoded1);
            free(encoded2);
        }
        free(key);
    }

    /* Provide a particular response for a challenge. */
    if (data->response != NULL) {
        /* Separate the challenge and its data content... */
        key = strdup(data->response);
        if (key == NULL)
            exit(ENOMEM);
        value = key + strcspn(key, "=");
        if (*value != '\0')
            *value++ = '\0';
        /* ... and pass it in. */
        err = krb5_responder_set_answer(ctx, rctx, key, value);
        if (err != 0) {
            fprintf(stderr, "error setting response\n");
            exit(1);
        }
        free(key);
    }

    if (data->print_pkinit_challenge) {
        /* Read the PKINIT challenge, formatted as a structure. */
        err = krb5_responder_pkinit_get_challenge(ctx, rctx, &chl);
        if (err != 0) {
            fprintf(stderr, "error getting pkinit challenge\n");
            exit(1);
        }
        if (chl != NULL) {
            for (n = 0; chl->identities[n] != NULL; n++)
                continue;
            for (i = 0; chl->identities[i] != NULL; i++) {
                if (chl->identities[i]->token_flags != -1) {
                    printf("identity %u/%u: %s (flags=0x%lx)\n", i + 1, n,
                           chl->identities[i]->identity,
                           (long)chl->identities[i]->token_flags);
                } else {
                    printf("identity %u/%u: %s\n", i + 1, n,
                           chl->identities[i]->identity);
                }
            }
        }
        krb5_responder_pkinit_challenge_free(ctx, rctx, chl);
    }

    /* Provide a particular response for the PKINIT challenge. */
    if (data->pkinit_answer != NULL) {
        /* Read the PKINIT challenge, formatted as a structure. */
        err = krb5_responder_pkinit_get_challenge(ctx, rctx, &chl);
        if (err != 0) {
            fprintf(stderr, "error getting pkinit challenge\n");
            exit(1);
        }
        /*
         * In case order matters, if the identity starts with "FILE:", exercise
         * the set_answer function, with the real answer second.
         */
        if (chl != NULL &&
            chl->identities != NULL &&
            chl->identities[0] != NULL) {
            if (strncmp(chl->identities[0]->identity, "FILE:", 5) == 0)
                krb5_responder_pkinit_set_answer(ctx, rctx, "foo", "bar");
        }
        /* Provide the real answer. */
        key = strdup(data->pkinit_answer);
        if (key == NULL)
            exit(ENOMEM);
        value = strrchr(key, '=');
        if (value != NULL)
            *value++ = '\0';
        else
            value = "";
        err = krb5_responder_pkinit_set_answer(ctx, rctx, key, value);
        if (err != 0) {
            fprintf(stderr, "error setting response\n");
            exit(1);
        }
        free(key);
        /*
         * In case order matters, if the identity starts with "PKCS12:",
         * exercise the set_answer function, with the real answer first.
         */
        if (chl != NULL &&
            chl->identities != NULL &&
            chl->identities[0] != NULL) {
            if (strncmp(chl->identities[0]->identity, "PKCS12:", 5) == 0)
                krb5_responder_pkinit_set_answer(ctx, rctx, "foo", "bar");
        }
        krb5_responder_pkinit_challenge_free(ctx, rctx, chl);
    }

    /*
     * Something we always check: read the PKINIT challenge, both as a
     * structure and in JSON form, reconstruct the JSON form from the
     * structure's contents, and check that they're the same.
     */
    challenge = krb5_responder_get_challenge(ctx, rctx,
                                             KRB5_RESPONDER_QUESTION_PKINIT);
    if (challenge != NULL) {
        krb5_responder_pkinit_get_challenge(ctx, rctx, &chl);
        if (chl == NULL) {
            fprintf(stderr, "pkinit raw challenge set, "
                    "but structure is NULL\n");
            exit(1);
        }
        if (k5_json_object_create(&ids) != 0) {
            fprintf(stderr, "error creating json objects\n");
            exit(1);
        }
        for (i = 0; chl->identities[i] != NULL; i++) {
            token_flags = chl->identities[i]->token_flags;
            if (k5_json_number_create(token_flags, &val) != 0) {
                fprintf(stderr, "error creating json number\n");
                exit(1);
            }
            if (k5_json_object_set(ids, chl->identities[i]->identity,
                                   val) != 0) {
                fprintf(stderr, "error adding json number to object\n");
                exit(1);
            }
            k5_json_release(val);
        }
        /* Encode the structure... */
        err = k5_json_encode(ids, &encoded1);
        if (err != 0) {
            fprintf(stderr, "error encoding json data\n");
            exit(1);
        }
        k5_json_release(ids);
        /* ... and see if they look the same. */
        if (strcmp(encoded1, challenge) != 0) {
            fprintf(stderr, "\"%s\" != \"%s\"\n", encoded1, challenge);
            exit(1);
        }
        krb5_responder_pkinit_challenge_free(ctx, rctx, chl);
        free(encoded1);
    }

    /* Provide a particular response for an OTP challenge. */
    if (data->otp_answer != NULL) {
        if (krb5_responder_otp_get_challenge(ctx, rctx, &ochl) == 0) {
            key = strchr(data->otp_answer, '=');
            if (key != NULL) {
                /* Make a copy of the answer that we can chop up. */
                key = strdup(data->otp_answer);
                if (key == NULL)
                    return ENOMEM;
                /* Isolate the ti value. */
                value = strchr(key, '=');
                *value++ = '\0';
                n = atoi(key);
                /* Break the value and PIN apart. */
                pin = strchr(value, ':');
                if (pin != NULL)
                    *pin++ = '\0';
                err = krb5_responder_otp_set_answer(ctx, rctx, n, value, pin);
                if (err != 0) {
                    fprintf(stderr, "error setting response\n");
                    exit(1);
                }
                free(key);
            }
            krb5_responder_otp_challenge_free(ctx, rctx, ochl);
        }
    }

    return 0;
}