Esempio n. 1
0
krb5_error_code KRB5_CALLCONV
krb5_mk_safe(krb5_context context, krb5_auth_context auth_context,
             const krb5_data *userdata, krb5_data *outbuf,
             krb5_replay_data *outdata)
{
    krb5_error_code       retval;
    krb5_key              key;
    krb5_replay_data      replaydata;

    /* Clear replaydata block */
    memset(&replaydata, 0, sizeof(krb5_replay_data));

    /* Get key */
    if ((key = auth_context->send_subkey) == NULL)
        key = auth_context->key;

    /* Get replay info */
    if ((auth_context->auth_context_flags & KRB5_AUTH_CONTEXT_DO_TIME) &&
        (auth_context->rcache == NULL))
        return KRB5_RC_REQUIRED;

    if (((auth_context->auth_context_flags & KRB5_AUTH_CONTEXT_RET_TIME) ||
         (auth_context->auth_context_flags & KRB5_AUTH_CONTEXT_RET_SEQUENCE)) &&
        (outdata == NULL))
        /* Need a better error */
        return KRB5_RC_REQUIRED;

    if (!auth_context->local_addr)
        return KRB5_LOCAL_ADDR_REQUIRED;

    if ((auth_context->auth_context_flags & KRB5_AUTH_CONTEXT_DO_TIME) ||
        (auth_context->auth_context_flags & KRB5_AUTH_CONTEXT_RET_TIME)) {
        if ((retval = krb5_us_timeofday(context, &replaydata.timestamp,
                                        &replaydata.usec)))
            return retval;
        if (auth_context->auth_context_flags & KRB5_AUTH_CONTEXT_RET_TIME) {
            outdata->timestamp = replaydata.timestamp;
            outdata->usec = replaydata.usec;
        }
    }
    if ((auth_context->auth_context_flags & KRB5_AUTH_CONTEXT_DO_SEQUENCE) ||
        (auth_context->auth_context_flags & KRB5_AUTH_CONTEXT_RET_SEQUENCE)) {
        replaydata.seq = auth_context->local_seq_number++;
        if (auth_context->auth_context_flags & KRB5_AUTH_CONTEXT_RET_SEQUENCE)
            outdata->seq = replaydata.seq;
    }

    {
        krb5_address * premote_fulladdr = NULL;
        krb5_address * plocal_fulladdr;
        krb5_address remote_fulladdr;
        krb5_address local_fulladdr;
        krb5_cksumtype sumtype;

        CLEANUP_INIT(2);

        if (auth_context->local_port) {
            if (!(retval = krb5_make_fulladdr(context, auth_context->local_addr,
                                              auth_context->local_port,
                                              &local_fulladdr))){
                CLEANUP_PUSH(local_fulladdr.contents, free);
                plocal_fulladdr = &local_fulladdr;
            } else {
                goto error;
            }
        } else {
            plocal_fulladdr = auth_context->local_addr;
        }

        if (auth_context->remote_addr) {
            if (auth_context->remote_port) {
                if (!(retval = krb5_make_fulladdr(context,auth_context->remote_addr,
                                                  auth_context->remote_port,
                                                  &remote_fulladdr))){
                    CLEANUP_PUSH(remote_fulladdr.contents, free);
                    premote_fulladdr = &remote_fulladdr;
                } else {
                    CLEANUP_DONE();
                    goto error;
                }
            } else {
                premote_fulladdr = auth_context->remote_addr;
            }
        }

        {
            krb5_enctype enctype = krb5_k_key_enctype(context, key);
            unsigned int nsumtypes;
            unsigned int i;
            krb5_cksumtype *sumtypes;
            retval = krb5_c_keyed_checksum_types (context, enctype,
                                                  &nsumtypes, &sumtypes);
            if (retval) {
                CLEANUP_DONE ();
                goto error;
            }
            if (nsumtypes == 0) {
                retval = KRB5_BAD_ENCTYPE;
                krb5_free_cksumtypes (context, sumtypes);
                CLEANUP_DONE ();
                goto error;
            }
            for (i = 0; i < nsumtypes; i++)
                if (auth_context->safe_cksumtype == sumtypes[i])
                    break;
            krb5_free_cksumtypes (context, sumtypes);
            if (i < nsumtypes)
                sumtype = auth_context->safe_cksumtype;
            else {
                switch (enctype) {
                case ENCTYPE_DES_CBC_MD4:
                    sumtype = CKSUMTYPE_RSA_MD4_DES;
                    break;
                case ENCTYPE_DES_CBC_MD5:
                case ENCTYPE_DES_CBC_CRC:
                    sumtype = CKSUMTYPE_RSA_MD5_DES;
                    break;
                default:
                    retval = krb5int_c_mandatory_cksumtype(context, enctype,
                                                           &sumtype);
                    if (retval) {
                        CLEANUP_DONE();
                        goto error;
                    }
                    break;
                }
            }
        }
        if ((retval = krb5_mk_safe_basic(context, userdata, key, &replaydata,
                                         plocal_fulladdr, premote_fulladdr,
                                         sumtype, outbuf))) {
            CLEANUP_DONE();
            goto error;
        }

        CLEANUP_DONE();
    }

    if (auth_context->auth_context_flags & KRB5_AUTH_CONTEXT_DO_TIME) {
        krb5_donot_replay replay;

        if ((retval = krb5_gen_replay_name(context, auth_context->local_addr,
                                           "_safe", &replay.client))) {
            free(outbuf);
            goto error;
        }

        replay.server = "";             /* XXX */
        replay.msghash = NULL;
        replay.cusec = replaydata.usec;
        replay.ctime = replaydata.timestamp;
        if ((retval = krb5_rc_store(context, auth_context->rcache, &replay))) {
            /* should we really error out here? XXX */
            free(outbuf);
            goto error;
        }
        free(replay.client);
    }

    return 0;

error:
    if ((auth_context->auth_context_flags & KRB5_AUTH_CONTEXT_DO_SEQUENCE) ||
        (auth_context->auth_context_flags & KRB5_AUTH_CONTEXT_RET_SEQUENCE))
        auth_context->local_seq_number--;

    return retval;
}
Esempio n. 2
0
static krb5_error_code
client_process(krb5_context kcontext,
	       void *client_plugin_context,
	       void *client_request_context,
	       krb5_get_init_creds_opt *opt,
	       preauth_get_client_data_proc client_get_data_proc,
	       struct _krb5_preauth_client_rock *rock,
	       krb5_kdc_req *request,
	       krb5_data *encoded_request_body,
	       krb5_data *encoded_previous_request,
	       krb5_pa_data *pa_data,
	       krb5_prompter_fct prompter,
	       void *prompter_data,
	       preauth_get_as_key_proc gak_fct,
	       void *gak_data,
	       krb5_data *salt, krb5_data *s2kparams,
	       krb5_keyblock *as_key,
	       krb5_pa_data **out_pa_data)
{
    krb5_pa_data *send_pa;
    krb5_checksum checksum;
    krb5_enctype enctype;
    krb5_cksumtype *cksumtypes;
    krb5_error_code status = 0;
    krb5_int32 cksumtype, *enctypes;
    unsigned int i, n_enctypes, cksumtype_count;
    int num_gic_info = 0;
    krb5_gic_opt_pa_data *gic_info;

    status = krb5_get_init_creds_opt_get_pa(kcontext, opt,
					    &num_gic_info, &gic_info);
    if (status && status != ENOENT) {
#ifdef DEBUG
	fprintf(stderr, "Error from krb5_get_init_creds_opt_get_pa: %s\n",
		error_message(status));
#endif
	return status;
    }
#ifdef DEBUG
    fprintf(stderr, "(cksum_body) Got the following gic options:\n");
#endif
    for (i = 0; i < num_gic_info; i++) {
#ifdef DEBUG
	fprintf(stderr, "  '%s' = '%s'\n", gic_info[i].attr, gic_info[i].value);
#endif
    }
    krb5_get_init_creds_opt_free_pa(kcontext, num_gic_info, gic_info);

    memset(&checksum, 0, sizeof(checksum));

    /* Get the user's long-term key if we haven't asked for it yet.  Try
     * all of the encryption types which the server supports. */
    if (as_key->length == 0) {
	if ((pa_data != NULL) && (pa_data->length >= 4)) {
#ifdef DEBUG
	    fprintf(stderr, "%d bytes of preauth data.\n", pa_data->length);
#endif
	    n_enctypes = pa_data->length / 4;
	    enctypes = (krb5_int32*) pa_data->contents;
	} else {
	    n_enctypes = request->nktypes;
	}
	for (i = 0; i < n_enctypes; i++) {
	    if ((pa_data != NULL) && (pa_data->length >= 4)) {
		memcpy(&enctype, pa_data->contents + 4 * i, 4);
		enctype = ntohl(enctype);
	    } else {
		enctype = request->ktype[i];
	    }
#ifdef DEBUG
	    fprintf(stderr, "Asking for AS key (type = %d).\n", enctype);
#endif
	    status = (*gak_fct)(kcontext, request->client, enctype,
				prompter, prompter_data,
				salt, s2kparams, as_key, gak_data);
	    if (status == 0)
		break;
	}
	if (status != 0)
	    return status;
    }
#ifdef DEBUG
    fprintf(stderr, "Got AS key (type = %d).\n", as_key->enctype);
#endif

    /* Determine an appropriate checksum type for this key. */
    cksumtype_count = 0;
    cksumtypes = NULL;
    status = krb5_c_keyed_checksum_types(kcontext, as_key->enctype,
					 &cksumtype_count, &cksumtypes);
    if (status != 0)
	return status;

    /* Generate the checksum. */
    for (i = 0; i < cksumtype_count; i++) {
	status = krb5_c_make_checksum(kcontext, cksumtypes[i], as_key,
				      KRB5_KEYUSAGE_TGS_REQ_AUTH_CKSUM,
				      encoded_request_body,
				      &checksum);
	if (status == 0) {
#ifdef DEBUG
	    fprintf(stderr, "Made checksum (type = %d, %d bytes).\n",
		    checksum.checksum_type, encoded_request_body->length);
#endif
	    break;
	}
    }
    cksumtype = htonl(cksumtypes[i]);
    krb5_free_cksumtypes(kcontext, cksumtypes);
    if (status != 0) {
	if (checksum.length > 0)
	    krb5_free_checksum_contents(kcontext, &checksum);
	return status;
    }

    /* Allocate the preauth data structure. */
    send_pa = malloc(sizeof(krb5_pa_data));
    if (send_pa == NULL) {
	krb5_free_checksum_contents(kcontext, &checksum);
	return ENOMEM;
    }
    send_pa->pa_type = KRB5_PADATA_CKSUM_BODY_REQ;
    send_pa->length = 4 + checksum.length;
    send_pa->contents = malloc(4 + checksum.length);
    if (send_pa->contents == NULL) {
	krb5_free_checksum_contents(kcontext, &checksum);
	free(send_pa);
	return ENOMEM;
    }

    /* Store the checksum. */
    memcpy(send_pa->contents, &cksumtype, 4);
    memcpy(send_pa->contents + 4, checksum.contents, checksum.length);
    *out_pa_data = send_pa;

    /* Clean up. */
    krb5_free_checksum_contents(kcontext, &checksum);

    return 0;
}
Esempio n. 3
0
/* Verify a request from a client. */
static krb5_error_code
server_verify(krb5_context kcontext,
              struct _krb5_db_entry_new *client,
              krb5_data *req_pkt,
              krb5_kdc_req *request,
              krb5_enc_tkt_part *enc_tkt_reply,
              krb5_pa_data *data,
              preauth_get_entry_data_proc server_get_entry_data,
              void *pa_module_context,
              void **pa_request_context,
              krb5_data **e_data,
              krb5_authdata ***authz_data)
{
    krb5_int32 cksumtype;
    krb5_checksum checksum;
    krb5_boolean valid;
    krb5_data *key_data, *req_body;
    krb5_keyblock *keys, *key;
    size_t length;
    int i;
    unsigned int j, cksumtypes_count;
    krb5_cksumtype *cksumtypes;
    krb5_error_code status;
    struct server_stats *stats;
    krb5_data *test_edata;
    test_svr_req_ctx *svr_req_ctx;
    krb5_authdata **my_authz_data = NULL;

    stats = pa_module_context;

#ifdef DEBUG
    fprintf(stderr, "cksum_body: server_verify\n");
#endif
    /* Verify the preauth data.  Start with the checksum type. */
    if (data->length < 4) {
        stats->failures++;
        return KRB5KDC_ERR_PREAUTH_FAILED;
    }
    memcpy(&cksumtype, data->contents, 4);
    memset(&checksum, 0, sizeof(checksum));
    checksum.checksum_type = ntohl(cksumtype);

    /* Verify that the amount of data we have left is what we expect. */
    if (krb5_c_checksum_length(kcontext, checksum.checksum_type,
                               &length) != 0) {
#ifdef DEBUG
        fprintf(stderr, "Error determining checksum size (type = %d). "
                "Is it supported?\n", checksum.checksum_type);
#endif
        stats->failures++;
        return KRB5KDC_ERR_SUMTYPE_NOSUPP;
    }
    if (data->length - 4 != length) {
#ifdef DEBUG
        fprintf(stderr, "Checksum size doesn't match client packet size.\n");
#endif
        stats->failures++;
        return KRB5KDC_ERR_PREAUTH_FAILED;
    }
    checksum.length = length;

    /* Pull up the client's keys. */
    key_data = NULL;
    if ((*server_get_entry_data)(kcontext, request, client,
                                 krb5plugin_preauth_keys, &key_data) != 0) {
#ifdef DEBUG
        fprintf(stderr, "Error retrieving client keys.\n");
#endif
        stats->failures++;
        return KRB5KDC_ERR_PREAUTH_FAILED;
    }

    /* Find the key which would have been used to generate the checksum. */
    keys = (krb5_keyblock *) key_data->data;
    key = NULL;
    for (i = 0; keys[i].enctype != 0; i++) {
        key = &keys[i];
        cksumtypes_count = 0;
        cksumtypes = NULL;
        if (krb5_c_keyed_checksum_types(kcontext, key->enctype,
                                        &cksumtypes_count, &cksumtypes) != 0)
            continue;
        for (j = 0; j < cksumtypes_count; j++) {
            if (cksumtypes[j] == checksum.checksum_type)
                break;
        }
        if (cksumtypes != NULL)
            krb5_free_cksumtypes(kcontext, cksumtypes);
        if (j < cksumtypes_count) {
#ifdef DEBUG
            fprintf(stderr, "Found checksum key.\n");
#endif
            break;
        }
    }
    if ((key == NULL) || (key->enctype == 0)) {
        for (i = 0; keys[i].enctype != 0; i++)
            krb5_free_keyblock_contents(kcontext, &keys[i]);
        krb5_free_data(kcontext, key_data);
        stats->failures++;
        return KRB5KDC_ERR_SUMTYPE_NOSUPP;
    }

    /* Save a copy of the key. */
    if (krb5_copy_keyblock(kcontext, &keys[i], &key) != 0) {
        for (i = 0; keys[i].enctype != 0; i++)
            krb5_free_keyblock_contents(kcontext, &keys[i]);
        krb5_free_data(kcontext, key_data);
        stats->failures++;
        return KRB5KDC_ERR_SUMTYPE_NOSUPP;
    }
    for (i = 0; keys[i].enctype != 0; i++)
        krb5_free_keyblock_contents(kcontext, &keys[i]);
    krb5_free_data(kcontext, key_data);

    /* Rebuild a copy of the client's request-body.  If we were serious
     * about doing this with any chance of working interoperability, we'd
     * extract the structure directly from the req_pkt structure.  This
     * will probably work if it's us on both ends, though. */
    req_body = NULL;
    if ((*server_get_entry_data)(kcontext, request, client,
                                 krb5plugin_preauth_request_body,
                                 &req_body) != 0) {
        krb5_free_keyblock(kcontext, key);
        stats->failures++;
        return KRB5KDC_ERR_PREAUTH_FAILED;
    }

#ifdef DEBUG
    fprintf(stderr, "AS key type %d, checksum type %d, %d bytes.\n",
            key->enctype, checksum.checksum_type, req_body->length);
#endif

    /* Verify the checksum itself. */
    checksum.contents = data->contents + 4;
    valid = FALSE;
    status = krb5_c_verify_checksum(kcontext, key,
                                    KRB5_KEYUSAGE_TGS_REQ_AUTH_CKSUM,
                                    req_body, &checksum, &valid);

    /* Clean up. */
    krb5_free_data(kcontext, req_body);
    krb5_free_keyblock(kcontext, key);

    /* Evaluate our results. */
    if ((status != 0) || (!valid)) {
#ifdef DEBUG
        if (status != 0) {
            fprintf(stderr, "Error in checksum verification.\n");
        } else {
            fprintf(stderr, "Checksum mismatch.\n");
        }
#endif
        /* Return edata to exercise code that handles edata... */
        test_edata = malloc(sizeof(*test_edata));
        if (test_edata != NULL) {
            test_edata->data = malloc(20);
            if (test_edata->data == NULL) {
                free(test_edata);
            } else {
                test_edata->length = 20;
                memset(test_edata->data, 'F', 20); /* fill it with junk */
                *e_data = test_edata;
            }
        }
        stats->failures++;
        return KRB5KDC_ERR_PREAUTH_FAILED;
    }

    /*
     * Return some junk authorization data just to exercise the
     * code path handling the returned authorization data.
     *
     * NOTE that this is NOT VALID authorization data!
     */
#ifdef DEBUG
    fprintf(stderr, "cksum_body: doing authorization data!\n");
#endif
    my_authz_data = malloc(2 * sizeof(*my_authz_data));
    if (my_authz_data != NULL) {
#if 1 /* USE_5000_AD */
#define AD_ALLOC_SIZE 5000
        /* ad_header consists of a sequence tag (0x30) and length
         * (0x82 0x1384) followed by octet string tag (0x04) and
         * length (0x82 0x1380) */
        krb5_octet ad_header[] = {0x30, 0x82, 0x13, 0x84, 0x04, 0x82, 0x13, 0x80};
#else
#define AD_ALLOC_SIZE 100
        /* ad_header consists of a sequence tag (0x30) and length
         * (0x62) followed by octet string tag (0x04) and length
         * (0x60) */
        krb5_octet ad_header[] = {0x30, 0x62, 0x04, 0x60};
#endif

        my_authz_data[1] = NULL;
        my_authz_data[0] = malloc(sizeof(krb5_authdata));
        if (my_authz_data[0] == NULL) {
            free(my_authz_data);
            return ENOMEM;
        }
        my_authz_data[0]->contents = malloc(AD_ALLOC_SIZE);
        if (my_authz_data[0]->contents == NULL) {
            free(my_authz_data[0]);
            free(my_authz_data);
            return ENOMEM;
        }
        memset(my_authz_data[0]->contents, '\0', AD_ALLOC_SIZE);
        my_authz_data[0]->magic = KV5M_AUTHDATA;
        my_authz_data[0]->ad_type = 1;
        my_authz_data[0]->length = AD_ALLOC_SIZE;
        memcpy(my_authz_data[0]->contents, ad_header, sizeof(ad_header));
        snprintf(my_authz_data[0]->contents + sizeof(ad_header),
                 AD_ALLOC_SIZE - sizeof(ad_header),
                 "cksum authorization data: %d bytes worth!\n", AD_ALLOC_SIZE);
        *authz_data = my_authz_data;
#ifdef DEBUG
        fprintf(stderr, "Returning %d bytes of authorization data\n",
                AD_ALLOC_SIZE);
#endif
    }

    /* Return edata to exercise code that handles edata... */
    test_edata = malloc(sizeof(*test_edata));
    if (test_edata != NULL) {
        test_edata->data = malloc(20);
        if (test_edata->data == NULL) {
            free(test_edata);
        } else {
            test_edata->length = 20;
            memset(test_edata->data, 'S', 20); /* fill it with junk */
            *e_data = test_edata;
        }
    }

    /* Return a request context to exercise code that handles it */
    svr_req_ctx = malloc(sizeof(*svr_req_ctx));
    if (svr_req_ctx != NULL) {
        svr_req_ctx->value1 = 111111;
        svr_req_ctx->value2 = 222222;
#ifdef DEBUG
        fprintf(stderr, "server_verify: returning context at %p\n",
                svr_req_ctx);
#endif
    }
    *pa_request_context = svr_req_ctx;

    /* Note that preauthentication succeeded. */
    enc_tkt_reply->flags |= TKT_FLG_PRE_AUTH;
    stats->successes++;
    return 0;
}
Esempio n. 4
0
/* Verify a request from a client. */
static krb5_error_code
server_verify(krb5_context kcontext,
	      struct _krb5_db_entry_new *client,
	      krb5_data *req_pkt,
	      krb5_kdc_req *request,
	      krb5_enc_tkt_part *enc_tkt_reply,
	      krb5_pa_data *data,
	      preauth_get_entry_data_proc server_get_entry_data,
	      void *pa_module_context,
	      void **pa_request_context,
	      krb5_data **e_data)
{
    krb5_int32 cksumtype;
    krb5_checksum checksum;
    krb5_boolean valid;
    krb5_data *key_data, *req_body;
    krb5_keyblock *keys, *key;
    size_t length;
    int i;
    unsigned int j, cksumtypes_count;
    krb5_cksumtype *cksumtypes;
    krb5_error_code status;
    struct server_stats *stats;
    krb5_data *test_edata;

    stats = pa_module_context;

    /* Verify the preauth data.  Start with the checksum type. */
    if (data->length < 4) {
	stats->failures++;
	return KRB5KDC_ERR_PREAUTH_FAILED;
    }
    memcpy(&cksumtype, data->contents, 4);
    memset(&checksum, 0, sizeof(checksum));
    checksum.checksum_type = ntohl(cksumtype);

    /* Verify that the amount of data we have left is what we expect. */
    if (krb5_c_checksum_length(kcontext, checksum.checksum_type,
			       &length) != 0) {
#ifdef DEBUG
	fprintf(stderr, "Error determining checksum size (type = %d). "
		"Is it supported?\n", checksum.checksum_type);
#endif
	stats->failures++;
	return KRB5KDC_ERR_SUMTYPE_NOSUPP;
    }
    if (data->length - 4 != length) {
#ifdef DEBUG
	fprintf(stderr, "Checksum size doesn't match client packet size.\n");
#endif
	stats->failures++;
	return KRB5KDC_ERR_PREAUTH_FAILED;
    }
    checksum.length = length;

    /* Pull up the client's keys. */
    key_data = NULL;
    if ((*server_get_entry_data)(kcontext, request, client,
				 krb5plugin_preauth_keys, &key_data) != 0) {
#ifdef DEBUG
	fprintf(stderr, "Error retrieving client keys.\n");
#endif
	stats->failures++;
	return KRB5KDC_ERR_PREAUTH_FAILED;
    }

    /* Find the key which would have been used to generate the checksum. */
    keys = (krb5_keyblock *) key_data->data;
    key = NULL;
    for (i = 0; keys[i].enctype != 0; i++) {
	key = &keys[i];
	cksumtypes_count = 0;
	cksumtypes = NULL;
	if (krb5_c_keyed_checksum_types(kcontext, key->enctype,
					&cksumtypes_count, &cksumtypes) != 0)
	    continue;
	for (j = 0; j < cksumtypes_count; j++) {
	    if (cksumtypes[j] == checksum.checksum_type)
		break;
	}
	if (cksumtypes != NULL)
	    krb5_free_cksumtypes(kcontext, cksumtypes);
	if (j < cksumtypes_count) {
#ifdef DEBUG
	    fprintf(stderr, "Found checksum key.\n");
#endif
	    break;
	}
    }
    if ((key == NULL) || (key->enctype == 0)) {
	for (i = 0; keys[i].enctype != 0; i++)
	    krb5_free_keyblock_contents(kcontext, &keys[i]);
	krb5_free_data(kcontext, key_data);
	stats->failures++;
	return KRB5KDC_ERR_SUMTYPE_NOSUPP;
    }

    /* Save a copy of the key. */
    if (krb5_copy_keyblock(kcontext, &keys[i], &key) != 0) {
	for (i = 0; keys[i].enctype != 0; i++)
	    krb5_free_keyblock_contents(kcontext, &keys[i]);
	krb5_free_data(kcontext, key_data);
	stats->failures++;
	return KRB5KDC_ERR_SUMTYPE_NOSUPP;
    }
    for (i = 0; keys[i].enctype != 0; i++)
	krb5_free_keyblock_contents(kcontext, &keys[i]);
    krb5_free_data(kcontext, key_data);

    /* Rebuild a copy of the client's request-body.  If we were serious
     * about doing this with any chance of working interoperability, we'd
     * extract the structure directly from the req_pkt structure.  This
     * will probably work if it's us on both ends, though. */
    req_body = NULL;
    if ((*server_get_entry_data)(kcontext, request, client,
				 krb5plugin_preauth_request_body,
				 &req_body) != 0) {
	krb5_free_keyblock(kcontext, key);
	stats->failures++;
	return KRB5KDC_ERR_PREAUTH_FAILED;
    }

#ifdef DEBUG
    fprintf(stderr, "AS key type %d, checksum type %d, %d bytes.\n",
	    key->enctype, checksum.checksum_type, req_body->length);
#endif

    /* Verify the checksum itself. */
    checksum.contents = data->contents + 4;
    valid = FALSE;
    status = krb5_c_verify_checksum(kcontext, key,
				    KRB5_KEYUSAGE_TGS_REQ_AUTH_CKSUM,
				    req_body, &checksum, &valid);

    /* Clean up. */
    krb5_free_data(kcontext, req_body);
    krb5_free_keyblock(kcontext, key);

    /* Evaluate our results. */
    if ((status != 0) || (!valid)) {
#ifdef DEBUG
	if (status != 0) {
	    fprintf(stderr, "Error in checksum verification.\n");
	} else {
	    fprintf(stderr, "Checksum mismatch.\n");
	}
#endif
	/* Return edata to exercise code that handles edata... */
	test_edata = malloc(sizeof(*test_edata));
	if (test_edata != NULL) {
	    test_edata->data = malloc(20);
	    if (test_edata->data == NULL) {
		free(test_edata);
	    } else {
		test_edata->length = 20;
		memset(test_edata->data, 'F', 20); /* fill it with junk */
		*e_data = test_edata;
	    }
	}
	stats->failures++;
	return KRB5KDC_ERR_PREAUTH_FAILED;
    }

    /* Return edata to exercise code that handles edata... */
    test_edata = malloc(sizeof(*test_edata));
    if (test_edata != NULL) {
	test_edata->data = malloc(20);
	if (test_edata->data == NULL) {
	    free(test_edata);
	} else {
	    test_edata->length = 20;
	    memset(test_edata->data, 'S', 20); /* fill it with junk */
	    *e_data = test_edata;
	}
    }

    /* Note that preauthentication succeeded. */
    enc_tkt_reply->flags |= TKT_FLG_PRE_AUTH;
    stats->successes++;
    return 0;
}
Esempio n. 5
0
int
ksm_rgenerate_out_msg(struct snmp_secmod_outgoing_params *parms)
{
    krb5_auth_context auth_context = NULL;
    krb5_error_code retcode;
    krb5_ccache     cc = NULL;
    int             retval = SNMPERR_SUCCESS;
    krb5_data       outdata, ivector;
    krb5_keyblock  *subkey = NULL;
#ifdef MIT_NEW_CRYPTO
    krb5_data       input;
    krb5_enc_data   output;
    unsigned int    numcksumtypes;
    krb5_cksumtype  *cksumtype_array;
#else                           /* MIT_NEW_CRYPTO */
    krb5_encrypt_block eblock;
#endif                          /* MIT_NEW_CRYPTO */
    size_t          blocksize, encrypted_length;
    unsigned char  *encrypted_data = NULL;
    int             zero = 0, i;
    u_char         *cksum_pointer, *endp = *parms->wholeMsg;
    krb5_cksumtype  cksumtype;
    krb5_checksum   pdu_checksum;
    u_char         **wholeMsg = parms->wholeMsg;
    size_t	   *offset = parms->wholeMsgOffset, seq_offset;
    struct ksm_secStateRef *ksm_state = (struct ksm_secStateRef *)
        parms->secStateRef;
    int rc;

    DEBUGMSGTL(("ksm", "Starting KSM processing\n"));

    outdata.length = 0;
    outdata.data = NULL;
    ivector.length = 0;
    ivector.data = NULL;
    pdu_checksum.contents = NULL;

    if (!ksm_state) {
        /*
         * If we don't have a ksm_state, then we're a request.  Get a
         * credential cache and build a ap_req.
         */
        retcode = krb5_cc_default(kcontext, &cc);

        if (retcode) {
            DEBUGMSGTL(("ksm", "KSM: krb5_cc_default failed: %s\n",
                        error_message(retcode)));
            snmp_set_detail(error_message(retcode));
            retval = SNMPERR_KRB5;
            goto error;
        }

        DEBUGMSGTL(("ksm", "KSM: Set credential cache successfully\n"));

        /*
         * This seems odd, since we don't need this until later (or earlier,
         * depending on how you look at it), but because the most likely
         * errors are Kerberos at this point, I'll get this now to save
         * time not encoding the rest of the packet.
         *
         * Also, we need the subkey to encrypt the PDU (if required).
         */

        retcode =
            krb5_mk_req(kcontext, &auth_context,
                        AP_OPTS_MUTUAL_REQUIRED | AP_OPTS_USE_SUBKEY,
                        (char *) service_name, parms->session->peername, NULL,
                        cc, &outdata);

        if (retcode) {
            DEBUGMSGTL(("ksm", "KSM: krb5_mk_req failed: %s\n",
                        error_message(retcode)));
            snmp_set_detail(error_message(retcode));
            retval = SNMPERR_KRB5;
            goto error;
        }

	DEBUGMSGTL(("ksm", "KSM: ticket retrieved successfully for \"%s/%s\" "
		    "(may not be actual ticket sname)\n", service_name,
		    parms->session->peername));

    } else {

        /*
         * Grab the auth_context from our security state reference
         */

        auth_context = ksm_state->auth_context;

        /*
         * Bundle up an AP_REP.  Note that we do this only when we
         * have a security state reference (which means we're in an agent
         * and we're sending a response).
         */

        DEBUGMSGTL(("ksm", "KSM: Starting reply processing.\n"));

        retcode = krb5_mk_rep(kcontext, auth_context, &outdata);

        if (retcode) {
            DEBUGMSGTL(("ksm", "KSM: krb5_mk_rep failed: %s\n",
                        error_message(retcode)));
            snmp_set_detail(error_message(retcode));
            retval = SNMPERR_KRB5;
            goto error;
        }

        DEBUGMSGTL(("ksm", "KSM: Finished with krb5_mk_rep()\n"));
    }

    /*
     * If we have to encrypt the PDU, do that now
     */

    if (parms->secLevel == SNMP_SEC_LEVEL_AUTHPRIV) {

        DEBUGMSGTL(("ksm", "KSM: Starting PDU encryption.\n"));

        /*
         * It's weird -
         *
         * If we're on the manager, it's a local subkey (because that's in
         * our AP_REQ)
         *
         * If we're on the agent, it's a remote subkey (because that comes
         * FROM the received AP_REQ).
         */

        if (ksm_state)
            retcode = krb5_auth_con_getremotesubkey(kcontext, auth_context,
                                                    &subkey);
        else
            retcode = krb5_auth_con_getlocalsubkey(kcontext, auth_context,
                                                   &subkey);

        if (retcode) {
            DEBUGMSGTL(("ksm",
                        "KSM: krb5_auth_con_getlocalsubkey failed: %s\n",
                        error_message(retcode)));
            snmp_set_detail(error_message(retcode));
            retval = SNMPERR_KRB5;
            goto error;
        }

        /*
         * Note that here we need to handle different things between the
         * old and new crypto APIs.  First, we need to get the final encrypted
         * length of the PDU.
         */

#ifdef MIT_NEW_CRYPTO
        retcode = krb5_c_encrypt_length(kcontext, subkey->enctype,
                                        parms->scopedPduLen,
                                        &encrypted_length);

        if (retcode) {
            DEBUGMSGTL(("ksm",
                        "Encryption length calculation failed: %s\n",
                        error_message(retcode)));
            snmp_set_detail(error_message(retcode));
            retval = SNMPERR_KRB5;
            goto error;
        }
#else                           /* MIT_NEW_CRYPTO */

        krb5_use_enctype(kcontext, &eblock, subkey->enctype);
        retcode = krb5_process_key(kcontext, &eblock, subkey);

        if (retcode) {
            DEBUGMSGTL(("ksm", "krb5_process_key failed: %s\n",
                        error_message(retcode)));
            snmp_set_detail(error_message(retcode));
            retval = SNMPERR_KRB5;
            goto error;
        }

        encrypted_length = krb5_encrypt_size(parms->scopedPduLen,
                                             eblock.crypto_entry);
#endif                          /* MIT_NEW_CRYPTO */

        encrypted_data = malloc(encrypted_length);

        if (!encrypted_data) {
            DEBUGMSGTL(("ksm",
                        "KSM: Unable to malloc %d bytes for encrypt "
                        "buffer: %s\n", parms->scopedPduLen,
                        strerror(errno)));
            retval = SNMPERR_MALLOC;
#ifndef MIT_NEW_CRYPTO
            krb5_finish_key(kcontext, &eblock);
#endif                          /* ! MIT_NEW_CRYPTO */

            goto error;
        }

        /*
         * We need to set up a blank initialization vector for the encryption.
         * Use a block of all zero's (which is dependent on the block size
         * of the encryption method).
         */

#ifdef MIT_NEW_CRYPTO

        retcode = krb5_c_block_size(kcontext, subkey->enctype, &blocksize);

        if (retcode) {
            DEBUGMSGTL(("ksm",
                        "Unable to determine crypto block size: %s\n",
                        error_message(retcode)));
            snmp_set_detail(error_message(retcode));
            retval = SNMPERR_KRB5;
            goto error;
        }
#else                           /* MIT_NEW_CRYPTO */

        blocksize =
            krb5_enctype_array[subkey->enctype]->system->block_length;

#endif                          /* MIT_NEW_CRYPTO */

        ivector.data = malloc(blocksize);

        if (!ivector.data) {
            DEBUGMSGTL(("ksm", "Unable to allocate %d bytes for ivector\n",
                        blocksize));
            retval = SNMPERR_MALLOC;
            goto error;
        }

        ivector.length = blocksize;
        memset(ivector.data, 0, blocksize);

        /*
         * Finally!  Do the encryption!
         */

#ifdef MIT_NEW_CRYPTO

        input.data = (char *) parms->scopedPdu;
        input.length = parms->scopedPduLen;
        output.ciphertext.data = (char *) encrypted_data;
        output.ciphertext.length = encrypted_length;

        retcode =
            krb5_c_encrypt(kcontext, subkey, KSM_KEY_USAGE_ENCRYPTION,
                           &ivector, &input, &output);

#else                           /* MIT_NEW_CRYPTO */

        retcode = krb5_encrypt(kcontext, (krb5_pointer) parms->scopedPdu,
                               (krb5_pointer) encrypted_data,
                               parms->scopedPduLen, &eblock, ivector.data);

        krb5_finish_key(kcontext, &eblock);

#endif                          /* MIT_NEW_CRYPTO */

        if (retcode) {
            DEBUGMSGTL(("ksm", "KSM: krb5_encrypt failed: %s\n",
                        error_message(retcode)));
            retval = SNMPERR_KRB5;
            snmp_set_detail(error_message(retcode));
            goto error;
        }

	*offset = 0;

        rc = asn_realloc_rbuild_string(wholeMsg, parms->wholeMsgLen,
                                             offset, 1,
                                             (u_char) (ASN_UNIVERSAL |
                                                       ASN_PRIMITIVE |
                                                       ASN_OCTET_STR),
                                             encrypted_data,
                                             encrypted_length);

        if (rc == 0) {
            DEBUGMSGTL(("ksm", "Building encrypted payload failed.\n"));
            retval = SNMPERR_TOO_LONG;
            goto error;
        }

        DEBUGMSGTL(("ksm", "KSM: Encryption complete.\n"));

    } else {
        /*
         * Plaintext PDU (not encrypted)
         */

        if (*parms->wholeMsgLen < parms->scopedPduLen) {
            DEBUGMSGTL(("ksm", "Not enough room for plaintext PDU.\n"));
            retval = SNMPERR_TOO_LONG;
            goto error;
        }
    }

    /*
     * Start encoding the msgSecurityParameters
     *
     * For now, use 0 for the response hint
     */

    DEBUGMSGTL(("ksm", "KSM: scopedPdu added to payload\n"));

    seq_offset = *offset;

    rc = asn_realloc_rbuild_int(wholeMsg, parms->wholeMsgLen,
                                      offset, 1,
                                      (u_char) (ASN_UNIVERSAL |
                                                ASN_PRIMITIVE |
                                                ASN_INTEGER),
                                      (long *) &zero, sizeof(zero));

    if (rc == 0) {
        DEBUGMSGTL(("ksm", "Building ksm security parameters failed.\n"));
        retval = SNMPERR_TOO_LONG;
        goto error;
    }

    rc = asn_realloc_rbuild_string(wholeMsg, parms->wholeMsgLen,
                                         offset, 1,
                                         (u_char) (ASN_UNIVERSAL |
                                                   ASN_PRIMITIVE |
                                                   ASN_OCTET_STR),
                                         (u_char *) outdata.data,
                                         outdata.length);

    if (rc == 0) {
        DEBUGMSGTL(("ksm", "Building ksm AP_REQ failed.\n"));
        retval = SNMPERR_TOO_LONG;
        goto error;
    }

    /*
     * Now, we need to pick the "right" checksum algorithm.  For old
     * crypto, just pick CKSUMTYPE_RSA_MD5_DES; for new crypto, pick
     * one of the "approved" ones.
     */

#ifdef MIT_NEW_CRYPTO
    retcode = krb5_c_keyed_checksum_types(kcontext, subkey->enctype,
                                          &numcksumtypes, &cksumtype_array);

    if (retcode) {
	DEBUGMSGTL(("ksm", "Unable to find appropriate keyed checksum: %s\n",
		    error_message(retcode)));
	snmp_set_detail(error_message(retcode));
        retval = SNMPERR_KRB5;
        goto error;
    }

    if (numcksumtypes <= 0) {
	DEBUGMSGTL(("ksm", "We received a list of zero cksumtypes for this "
		    "enctype (%d)\n", subkey->enctype));
	snmp_set_detail("No valid checksum type for this encryption type");
	retval = SNMPERR_KRB5;
	goto error;
    }

    /*
     * It's not clear to me from the API which checksum you're supposed
     * to support, so I'm taking a guess at the first one
     */

    cksumtype = cksumtype_array[0];

    krb5_free_cksumtypes(kcontext, cksumtype_array);

    DEBUGMSGTL(("ksm", "KSM: Choosing checksum type of %d (subkey type "
		"of %d)\n", cksumtype, subkey->enctype));

    retcode = krb5_c_checksum_length(kcontext, cksumtype, &blocksize);

    if (retcode) {
        DEBUGMSGTL(("ksm", "Unable to determine checksum length: %s\n",
                    error_message(retcode)));
        snmp_set_detail(error_message(retcode));
        retval = SNMPERR_KRB5;
        goto error;
    }

    pdu_checksum.length = blocksize;

#else /* MIT_NEW_CRYPTO */
    if (ksm_state)
        cksumtype = ksm_state->cksumtype;
    else
	cksumtype = CKSUMTYPE_RSA_MD5_DES;

    if (!is_keyed_cksum(cksumtype)) {
        DEBUGMSGTL(("ksm", "Checksum type %d is not a keyed checksum\n",
                    cksumtype));
        snmp_set_detail("Checksum is not a keyed checksum");
        retval = SNMPERR_KRB5;
        goto error;
    }

    if (!is_coll_proof_cksum(cksumtype)) {
        DEBUGMSGTL(("ksm", "Checksum type %d is not a collision-proof "
                    "checksum\n", cksumtype));
        snmp_set_detail("Checksum is not a collision-proof checksum");
        retval = SNMPERR_KRB5;
        goto error;
    }

    pdu_checksum.length = krb5_checksum_size(kcontext, cksumtype);
    pdu_checksum.checksum_type = cksumtype;

#endif /* MIT_NEW_CRYPTO */

    /*
     * Note that here, we're just leaving blank space for the checksum;
     * we remember where that is, and we'll fill it in later.
     */

    *offset += pdu_checksum.length;
    memset(*wholeMsg + *parms->wholeMsgLen - *offset, 0, pdu_checksum.length);

    cksum_pointer = *wholeMsg + *parms->wholeMsgLen - *offset;

    rc = asn_realloc_rbuild_header(wholeMsg, parms->wholeMsgLen,
                                         parms->wholeMsgOffset, 1,
                                         (u_char) (ASN_UNIVERSAL |
                                                   ASN_PRIMITIVE |
                                                   ASN_OCTET_STR),
                                         pdu_checksum.length);

    if (rc == 0) {
        DEBUGMSGTL(("ksm", "Building ksm security parameters failed.\n"));
        retval = SNMPERR_TOO_LONG;
        goto error;
    }

    rc = asn_realloc_rbuild_int(wholeMsg, parms->wholeMsgLen,
                                      parms->wholeMsgOffset, 1,
                                      (u_char) (ASN_UNIVERSAL |
                                                ASN_PRIMITIVE |
                                                ASN_OCTET_STR),
                                      (long *) &cksumtype,
                                      sizeof(cksumtype));

    if (rc == 0) {
        DEBUGMSGTL(("ksm", "Building ksm security parameters failed.\n"));
        retval = SNMPERR_TOO_LONG;
        goto error;
    }

    rc = asn_realloc_rbuild_sequence(wholeMsg, parms->wholeMsgLen,
                                           parms->wholeMsgOffset, 1,
                                           (u_char) (ASN_SEQUENCE |
                                                     ASN_CONSTRUCTOR),
                                           *offset - seq_offset);

    if (rc == 0) {
        DEBUGMSGTL(("ksm", "Building ksm security parameters failed.\n"));
        retval = SNMPERR_TOO_LONG;
        goto error;
    }

    rc = asn_realloc_rbuild_header(wholeMsg, parms->wholeMsgLen,
                                         parms->wholeMsgOffset, 1,
                                         (u_char) (ASN_UNIVERSAL |
                                                   ASN_PRIMITIVE |
                                                   ASN_OCTET_STR),
                                         *offset - seq_offset);

    if (rc == 0) {
        DEBUGMSGTL(("ksm", "Building ksm security parameters failed.\n"));
        retval = SNMPERR_TOO_LONG;
        goto error;
    }

    DEBUGMSGTL(("ksm", "KSM: Security parameter encoding completed\n"));

    /*
     * We're done with the KSM security parameters - now we do the global
     * header and wrap up the whole PDU.
     */

    if (*parms->wholeMsgLen < parms->globalDataLen) {
        DEBUGMSGTL(("ksm", "Building global data failed.\n"));
        retval = SNMPERR_TOO_LONG;
        goto error;
    }

    *offset += parms->globalDataLen;
    memcpy(*wholeMsg + *parms->wholeMsgLen - *offset,
	   parms->globalData, parms->globalDataLen);

    rc = asn_realloc_rbuild_sequence(wholeMsg, parms->wholeMsgLen,
                                           offset, 1,
                                           (u_char) (ASN_SEQUENCE |
                                                     ASN_CONSTRUCTOR),
                                           *offset);

    if (rc == 0) {
        DEBUGMSGTL(("ksm", "Building master packet sequence.\n"));
        retval = SNMPERR_TOO_LONG;
        goto error;
    }

    DEBUGMSGTL(("ksm", "KSM: PDU master packet encoding complete.\n"));

    /*
     * Now we need to checksum the entire PDU (since it's built).
     */

    pdu_checksum.contents = malloc(pdu_checksum.length);

    if (!pdu_checksum.contents) {
        DEBUGMSGTL(("ksm", "Unable to malloc %d bytes for checksum\n",
                    pdu_checksum.length));
        retval = SNMPERR_MALLOC;
        goto error;
    }

    /*
     * If we didn't encrypt the packet, we haven't yet got the subkey.
     * Get that now.
     */

    if (!subkey) {
        if (ksm_state)
            retcode = krb5_auth_con_getremotesubkey(kcontext, auth_context,
                                                    &subkey);
        else
            retcode = krb5_auth_con_getlocalsubkey(kcontext, auth_context,
                                                   &subkey);
        if (retcode) {
            DEBUGMSGTL(("ksm", "krb5_auth_con_getlocalsubkey failed: %s\n",
                        error_message(retcode)));
            snmp_set_detail(error_message(retcode));
            retval = SNMPERR_KRB5;
            goto error;
        }
    }
#ifdef MIT_NEW_CRYPTO

    input.data = (char *) (*wholeMsg + *parms->wholeMsgLen - *offset);
    input.length = *offset;
        retcode = krb5_c_make_checksum(kcontext, cksumtype, subkey,
                                       KSM_KEY_USAGE_CHECKSUM, &input,
                                       &pdu_checksum);

#else                           /* MIT_NEW_CRYPTO */

    retcode = krb5_calculate_checksum(kcontext, cksumtype, *wholeMsg +
				      *parms->wholeMsgLen - *offset,
                                      *offset,
                                      (krb5_pointer) subkey->contents,
                                      subkey->length, &pdu_checksum);

#endif                          /* MIT_NEW_CRYPTO */

    if (retcode) {
        DEBUGMSGTL(("ksm", "Calculate checksum failed: %s\n",
                    error_message(retcode)));
        retval = SNMPERR_KRB5;
        snmp_set_detail(error_message(retcode));
        goto error;
    }

    DEBUGMSGTL(("ksm", "KSM: Checksum calculation complete.\n"));

    memcpy(cksum_pointer, pdu_checksum.contents, pdu_checksum.length);

    DEBUGMSGTL(("ksm", "KSM: Writing checksum of %d bytes at offset %d\n",
                pdu_checksum.length, cksum_pointer - (*wholeMsg + 1)));

    DEBUGMSGTL(("ksm", "KSM: Checksum:"));

    for (i = 0; i < pdu_checksum.length; i++)
        DEBUGMSG(("ksm", " %02x",
                  (unsigned int) pdu_checksum.contents[i]));

    DEBUGMSG(("ksm", "\n"));

    /*
     * If we're _not_ called as part of a response (null ksm_state),
     * then save the auth_context for later using our cache routines.
     */

    if (!ksm_state) {
        if ((retval = ksm_insert_cache(parms->pdu->msgid, auth_context,
                                       (u_char *) parms->secName,
                                       parms->secNameLen)) !=
            SNMPERR_SUCCESS)
            goto error;
        auth_context = NULL;
    }

    DEBUGMSGTL(("ksm", "KSM processing complete!\n"));

  error:

    if (pdu_checksum.contents)
#ifdef MIT_NEW_CRYPTO
        krb5_free_checksum_contents(kcontext, &pdu_checksum);
#else                           /* MIT_NEW_CRYPTO */
        free(pdu_checksum.contents);
#endif                          /* MIT_NEW_CRYPTO */

    if (ivector.data)
        free(ivector.data);

    if (subkey)
        krb5_free_keyblock(kcontext, subkey);

    if (encrypted_data)
        free(encrypted_data);

    if (cc)
        krb5_cc_close(kcontext, cc);

    if (auth_context && !ksm_state)
        krb5_auth_con_free(kcontext, auth_context);

    return retval;
}