Пример #1
0
krb5_error_code KRB5_CALLCONV
krb5_verify_checksum(krb5_context context, krb5_cksumtype ctype,
		     const krb5_checksum *cksum, krb5_const_pointer in,
		     size_t in_length, krb5_const_pointer seed,
		     size_t seed_length)
{
    krb5_data input;
    krb5_keyblock key;
    krb5_error_code ret;
    krb5_boolean valid;

    input.data = in;
    input.length = in_length;

    key.length = seed_length;
    key.contents = seed;

    if ((ret = krb5_c_verify_checksum(context, &key, 0, &input, cksum,
				      &valid)))
	return(ret);

    if (!valid)
	return(KRB5KRB_AP_ERR_BAD_INTEGRITY);

    return(0);
}
Пример #2
0
Файл: fast.c Проект: WeiY/krb5
krb5_error_code
krb5int_fast_process_response(krb5_context context,
                              struct krb5int_fast_request_state *state,
                              krb5_kdc_rep *resp,
                              krb5_keyblock **strengthen_key)
{
    krb5_error_code retval = 0;
    krb5_fast_response *fast_response = NULL;
    krb5_data *encoded_ticket = NULL;
    krb5_boolean cksum_valid;

    krb5_clear_error_message(context);
    *strengthen_key = NULL;
    if (state->armor_key == 0)
        return 0;
    retval = decrypt_fast_reply(context, state, resp->padata,
                                &fast_response);
    if (retval == 0) {
        if (fast_response->finished == 0) {
            retval = KRB5_KDCREP_MODIFIED;
            krb5_set_error_message(context, retval,
                                   _("FAST response missing finish message "
                                     "in KDC reply"));
        }
    }
    if (retval == 0)
        retval = encode_krb5_ticket(resp->ticket, &encoded_ticket);
    if (retval == 0)
        retval = krb5_c_verify_checksum(context, state->armor_key,
                                        KRB5_KEYUSAGE_FAST_FINISHED,
                                        encoded_ticket,
                                        &fast_response->finished->ticket_checksum,
                                        &cksum_valid);
    if (retval == 0 && cksum_valid == 0) {
        retval = KRB5_KDCREP_MODIFIED;
        krb5_set_error_message(context, retval,
                               _("Ticket modified in KDC reply"));
    }
    if (retval == 0) {
        krb5_free_principal(context, resp->client);
        resp->client = fast_response->finished->client;
        fast_response->finished->client = NULL;
        *strengthen_key = fast_response->strengthen_key;
        fast_response->strengthen_key = NULL;
        krb5_free_pa_data(context, resp->padata);
        resp->padata = fast_response->padata;
        fast_response->padata = NULL;
    }
    if (fast_response)
        krb5_free_fast_response(context, fast_response);
    if (encoded_ticket)
        krb5_free_data(context, encoded_ticket);
    return retval;
}
Пример #3
0
krb5_error_code check_pac_checksum(DATA_BLOB pac_data,
					  struct PAC_SIGNATURE_DATA *sig,
					  krb5_context context,
					  const krb5_keyblock *keyblock)
{
	krb5_error_code ret;
	krb5_checksum cksum;
	krb5_keyusage usage = 0;
	krb5_boolean checksum_valid = false;
	krb5_data input;

#ifdef HAVE_CHECKSUM_IN_KRB5_CHECKSUM /* Heimdal */
	cksum.cksumtype	= (krb5_cksumtype)sig->type;
	cksum.checksum.length	= sig->signature.length;
	cksum.checksum.data	= sig->signature.data;
#else /* MIT */
	cksum.checksum_type	= (krb5_cksumtype)sig->type;
	cksum.length		= sig->signature.length;
	cksum.contents		= sig->signature.data;
#endif

#ifdef HAVE_KRB5_KU_OTHER_CKSUM /* Heimdal */
	usage = KRB5_KU_OTHER_CKSUM;
#elif defined(HAVE_KRB5_KEYUSAGE_APP_DATA_CKSUM) /* MIT */
	usage = KRB5_KEYUSAGE_APP_DATA_CKSUM;
#else
#error UNKNOWN_KRB5_KEYUSAGE
#endif

	input.data = (char *)pac_data.data;
	input.length = pac_data.length;

	ret = krb5_c_verify_checksum(context,
				     keyblock,
				     usage,
				     &input,
				     &cksum,
				     &checksum_valid);
	if (!checksum_valid) {
		ret = KRB5KRB_AP_ERR_BAD_INTEGRITY;
	}
	if (ret){
		DEBUG(2,("check_pac_checksum: PAC Verification failed: %s (%d)\n",
			error_message(ret), ret));
		return ret;
	}

	return ret;
}
Пример #4
0
static krb5_error_code
k5_pac_verify_kdc_checksum(krb5_context context,
                           const krb5_pac pac,
                           const krb5_keyblock *privsvr)
{
    krb5_error_code ret;
    krb5_data server_checksum, privsvr_checksum;
    krb5_checksum checksum;
    krb5_boolean valid;
    krb5_octet *p;

    ret = k5_pac_locate_buffer(context, pac,
                               PAC_PRIVSVR_CHECKSUM, &privsvr_checksum);
    if (ret != 0)
        return ret;

    if (privsvr_checksum.length < PAC_SIGNATURE_DATA_LENGTH)
        return KRB5_BAD_MSIZE;

    ret = k5_pac_locate_buffer(context, pac,
                               PAC_SERVER_CHECKSUM, &server_checksum);
    if (ret != 0)
        return ret;

    if (server_checksum.length < PAC_SIGNATURE_DATA_LENGTH)
        return KRB5_BAD_MSIZE;

    p = (krb5_octet *)privsvr_checksum.data;
    checksum.checksum_type = load_32_le(p);
    checksum.length = privsvr_checksum.length - PAC_SIGNATURE_DATA_LENGTH;
    checksum.contents = p + PAC_SIGNATURE_DATA_LENGTH;
    if (!krb5_c_is_keyed_cksum(checksum.checksum_type))
        return KRB5KRB_AP_ERR_INAPP_CKSUM;

    server_checksum.data += PAC_SIGNATURE_DATA_LENGTH;
    server_checksum.length -= PAC_SIGNATURE_DATA_LENGTH;

    ret = krb5_c_verify_checksum(context, privsvr,
                                 KRB5_KEYUSAGE_APP_DATA_CKSUM,
                                 &server_checksum, &checksum, &valid);
    if (ret != 0)
        return ret;

    if (valid == FALSE)
        ret = KRB5KRB_AP_ERR_BAD_INTEGRITY;

    return ret;
}
Пример #5
0
krb5_error_code
krb5int_fast_verify_nego(krb5_context context,
                         struct krb5int_fast_request_state *state,
                         krb5_kdc_rep *rep, krb5_data *request,
                         krb5_keyblock *decrypting_key,
                         krb5_boolean *fast_avail)
{
    krb5_error_code retval = 0;
    krb5_checksum *checksum = NULL;
    krb5_pa_data *pa;
    krb5_data scratch;
    krb5_boolean valid;

    *fast_avail = FALSE;
    if (rep->enc_part2->flags& TKT_FLG_ENC_PA_REP) {
        pa = krb5int_find_pa_data(context, rep->enc_part2->enc_padata,
                                  KRB5_ENCPADATA_REQ_ENC_PA_REP);
        if (pa == NULL)
            retval = KRB5_KDCREP_MODIFIED;
        else {
            scratch.data = (char *) pa->contents;
            scratch.length = pa->length;
        }
        if (retval == 0)
            retval = decode_krb5_checksum(&scratch, &checksum);
        if (retval == 0)
            retval = krb5_c_verify_checksum(context, decrypting_key,
                                            KRB5_KEYUSAGE_AS_REQ,
                                            request, checksum, &valid);
        if (retval == 0 &&valid == 0)
            retval = KRB5_KDCREP_MODIFIED;
        if (retval == 0) {
            pa = krb5int_find_pa_data(context, rep->enc_part2->enc_padata,
                                      KRB5_PADATA_FX_FAST);
            *fast_avail = (pa != NULL);
        }
    }
    TRACE_FAST_NEGO(context, *fast_avail);
    if (checksum)
        krb5_free_checksum(context, checksum);
    return retval;
}
Пример #6
0
static krb5_error_code
verify_ad_signedpath_checksum(krb5_context context,
                              const krb5_db_entry *krbtgt,
                              krb5_keyblock *krbtgt_key,
                              krb5_enc_tkt_part *enc_tkt_part,
                              krb5_principal *deleg_path,
                              krb5_pa_data **method_data,
                              krb5_checksum *cksum,
                              krb5_boolean *valid)
{
    krb5_error_code                 code;
    krb5_data                      *data;

    *valid = FALSE;

    if (!krb5_c_is_keyed_cksum(cksum->checksum_type))
        return KRB5KRB_AP_ERR_INAPP_CKSUM;

    code = make_ad_signedpath_data(context,
                                   enc_tkt_part->client,
                                   enc_tkt_part->times.authtime,
                                   deleg_path,
                                   method_data,
                                   enc_tkt_part->authorization_data,
                                   &data);
    if (code != 0)
        return code;

    code = krb5_c_verify_checksum(context,
                                  krbtgt_key,
                                  KRB5_KEYUSAGE_AD_SIGNEDPATH,
                                  data,
                                  cksum,
                                  valid);

    krb5_free_data(context, data);
    return code;
}
Пример #7
0
/* Return true if cammac's KDC verifier is valid for enc_tkt, using krbtgt to
 * retrieve the TGT key indicated by the verifier. */
krb5_boolean
cammac_check_kdcver(krb5_context context, krb5_cammac *cammac,
                    krb5_enc_tkt_part *enc_tkt, krb5_db_entry *krbtgt)
{
    krb5_verifier_mac *ver = cammac->kdc_verifier;
    krb5_key_data *kd;
    krb5_keyblock tgtkey;
    krb5_boolean valid = FALSE;
    krb5_data *der_enctkt = NULL;

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

    if (ver == NULL)
        goto cleanup;

    /* Fetch the krbtgt key indicated by the KDC verifier.  Only allow the
     * first krbtgt key of the specified kvno. */
    if (krb5_dbe_find_enctype(context, krbtgt, -1, -1, ver->kvno, &kd) != 0)
        goto cleanup;
    if (krb5_dbe_decrypt_key_data(context, NULL, kd, &tgtkey, NULL) != 0)
        goto cleanup;
    if (ver->enctype != ENCTYPE_NULL && tgtkey.enctype != ver->enctype)
        goto cleanup;

    /* Verify the checksum over the DER-encoded enc_tkt with the CAMMAC
     * elements as authdata. */
    if (encode_kdcver_encpart(enc_tkt, cammac->elements, &der_enctkt) != 0)
        goto cleanup;
    (void)krb5_c_verify_checksum(context, &tgtkey, KRB5_KEYUSAGE_CAMMAC,
                                 der_enctkt, &ver->checksum, &valid);

cleanup:
    krb5_free_keyblock_contents(context, &tgtkey);
    krb5_free_data(context, der_enctkt);
    return valid;
}
Пример #8
0
static krb5_error_code
sam2_process(krb5_context context, krb5_clpreauth_moddata moddata,
	     krb5_clpreauth_modreq modreq, krb5_get_init_creds_opt *opt,
	     krb5_clpreauth_callbacks cb, krb5_clpreauth_rock rock,
	     krb5_kdc_req *request, krb5_data *encoded_request_body,
	     krb5_data *encoded_previous_request, krb5_pa_data *padata,
	     krb5_prompter_fct prompter, void *prompter_data,
	     krb5_pa_data ***out_padata)
{
    krb5_error_code retval;
    krb5_sam_challenge_2 *sc2 = NULL;
    krb5_sam_challenge_2_body *sc2b = NULL;
    krb5_data tmp_data;
    krb5_data response_data;
    char name[100], banner[100], prompt[100], response[100];
    krb5_prompt kprompt;
    krb5_prompt_type prompt_type;
    krb5_data defsalt, *salt;
    krb5_checksum **cksum;
    krb5_data *scratch = NULL;
    krb5_boolean valid_cksum = 0;
    krb5_enc_sam_response_enc_2 enc_sam_response_enc_2;
    krb5_sam_response_2 sr2;
    size_t ciph_len;
    krb5_pa_data **sam_padata;

    if (prompter == NULL)
        return KRB5_LIBOS_CANTREADPWD;

    tmp_data.length = padata->length;
    tmp_data.data = (char *)padata->contents;

    if ((retval = decode_krb5_sam_challenge_2(&tmp_data, &sc2)))
        return(retval);

    retval = decode_krb5_sam_challenge_2_body(&sc2->sam_challenge_2_body, &sc2b);

    if (retval) {
        krb5_free_sam_challenge_2(context, sc2);
        return(retval);
    }

    if (!sc2->sam_cksum || ! *sc2->sam_cksum) {
        krb5_free_sam_challenge_2(context, sc2);
        krb5_free_sam_challenge_2_body(context, sc2b);
        return(KRB5_SAM_NO_CHECKSUM);
    }

    if (sc2b->sam_flags & KRB5_SAM_MUST_PK_ENCRYPT_SAD) {
        krb5_free_sam_challenge_2(context, sc2);
        krb5_free_sam_challenge_2_body(context, sc2b);
        return(KRB5_SAM_UNSUPPORTED);
    }

    if (!krb5_c_valid_enctype(sc2b->sam_etype)) {
        krb5_free_sam_challenge_2(context, sc2);
        krb5_free_sam_challenge_2_body(context, sc2b);
        return(KRB5_SAM_INVALID_ETYPE);
    }

    /* All of the above error checks are KDC-specific, that is, they     */
    /* assume a failure in the KDC reply.  By returning anything other   */
    /* than KRB5_KDC_UNREACH, KRB5_PREAUTH_FAILED,               */
    /* KRB5_LIBOS_PWDINTR, or KRB5_REALM_CANT_RESOLVE, the client will   */
    /* most likely go on to try the AS_REQ against master KDC            */

    if (!(sc2b->sam_flags & KRB5_SAM_USE_SAD_AS_KEY)) {
        /* We will need the password to obtain the key used for */
        /* the checksum, and encryption of the sam_response.    */
        /* Go ahead and get it now, preserving the ordering of  */
        /* prompts for the user.                                */

        retval = (*rock->gak_fct)(context, request->client, sc2b->sam_etype,
				  prompter, prompter_data, rock->salt,
				  rock->s2kparams, rock->as_key,
				  *rock->gak_data);
        if (retval) {
            krb5_free_sam_challenge_2(context, sc2);
            krb5_free_sam_challenge_2_body(context, sc2b);
            return(retval);
        }
    }

    snprintf(name, sizeof(name), "%.*s",
             SAMDATA(sc2b->sam_type_name, _("SAM Authentication"),
                     sizeof(name) - 1));

    snprintf(banner, sizeof(banner), "%.*s",
             SAMDATA(sc2b->sam_challenge_label,
                     sam_challenge_banner(sc2b->sam_type),
                     sizeof(banner)-1));

    snprintf(prompt, sizeof(prompt), "%s%.*s%s%.*s",
             sc2b->sam_challenge.length?"Challenge is [":"",
             SAMDATA(sc2b->sam_challenge, "", 20),
             sc2b->sam_challenge.length?"], ":"",
             SAMDATA(sc2b->sam_response_prompt, "passcode", 55));

    response_data.data = response;
    response_data.length = sizeof(response);
    kprompt.prompt = prompt;
    kprompt.hidden = 1;
    kprompt.reply = &response_data;

    prompt_type = KRB5_PROMPT_TYPE_PREAUTH;
    krb5int_set_prompt_types(context, &prompt_type);

    if ((retval = ((*prompter)(context, prompter_data, name,
                               banner, 1, &kprompt)))) {
        krb5_free_sam_challenge_2(context, sc2);
        krb5_free_sam_challenge_2_body(context, sc2b);
        krb5int_set_prompt_types(context, 0);
        return(retval);
    }

    krb5int_set_prompt_types(context, (krb5_prompt_type *)NULL);

    /* Generate salt used by string_to_key() */
    salt = rock->salt;
    if (((int) salt->length == -1) && (salt->data == NULL)) {
        if ((retval =
             krb5_principal2salt(context, request->client, &defsalt))) {
            krb5_free_sam_challenge_2(context, sc2);
            krb5_free_sam_challenge_2_body(context, sc2b);
            return(retval);
        }
        salt = &defsalt;
    } else {
        defsalt.length = 0;
    }

    /* Get encryption key to be used for checksum and sam_response */
    if (!(sc2b->sam_flags & KRB5_SAM_USE_SAD_AS_KEY)) {
        /* as_key = string_to_key(password) */

        if (rock->as_key->length) {
            krb5_free_keyblock_contents(context, rock->as_key);
            rock->as_key->length = 0;
        }

        /* generate a key using the supplied password */
        retval = krb5_c_string_to_key(context, sc2b->sam_etype,
				      (krb5_data *)*rock->gak_data, salt,
				      rock->as_key);

        if (retval) {
            krb5_free_sam_challenge_2(context, sc2);
            krb5_free_sam_challenge_2_body(context, sc2b);
            if (defsalt.length) free(defsalt.data);
            return(retval);
        }

        if (!(sc2b->sam_flags & KRB5_SAM_SEND_ENCRYPTED_SAD)) {
            /* as_key = combine_key (as_key, string_to_key(SAD)) */
            krb5_keyblock tmp_kb;

            retval = krb5_c_string_to_key(context, sc2b->sam_etype,
                                          &response_data, salt, &tmp_kb);

            if (retval) {
                krb5_free_sam_challenge_2(context, sc2);
                krb5_free_sam_challenge_2_body(context, sc2b);
                if (defsalt.length) free(defsalt.data);
                return(retval);
            }

            /* This should be a call to the crypto library some day */
            /* key types should already match the sam_etype */
            retval = krb5int_c_combine_keys(context, rock->as_key, &tmp_kb,
					    rock->as_key);

            if (retval) {
                krb5_free_sam_challenge_2(context, sc2);
                krb5_free_sam_challenge_2_body(context, sc2b);
                if (defsalt.length) free(defsalt.data);
                return(retval);
            }
            krb5_free_keyblock_contents(context, &tmp_kb);
        }

        if (defsalt.length)
            free(defsalt.data);

    } else {
        /* as_key = string_to_key(SAD) */

        if (rock->as_key->length) {
            krb5_free_keyblock_contents(context, rock->as_key);
            rock->as_key->length = 0;
        }

        /* generate a key using the supplied password */
        retval = krb5_c_string_to_key(context, sc2b->sam_etype,
                                      &response_data, salt, rock->as_key);

        if (defsalt.length)
            free(defsalt.data);

        if (retval) {
            krb5_free_sam_challenge_2(context, sc2);
            krb5_free_sam_challenge_2_body(context, sc2b);
            return(retval);
        }
    }

    /* Now we have a key, verify the checksum on the sam_challenge */

    cksum = sc2->sam_cksum;

    for (; *cksum; cksum++) {
        if (!krb5_c_is_keyed_cksum((*cksum)->checksum_type))
            continue;
        /* Check this cksum */
        retval = krb5_c_verify_checksum(context, rock->as_key,
                                        KRB5_KEYUSAGE_PA_SAM_CHALLENGE_CKSUM,
                                        &sc2->sam_challenge_2_body,
                                        *cksum, &valid_cksum);
        if (retval) {
            krb5_free_data(context, scratch);
            krb5_free_sam_challenge_2(context, sc2);
            krb5_free_sam_challenge_2_body(context, sc2b);
            return(retval);
        }
        if (valid_cksum)
            break;
    }

    if (!valid_cksum) {
        krb5_free_sam_challenge_2(context, sc2);
        krb5_free_sam_challenge_2_body(context, sc2b);
        /*
         * Note: We return AP_ERR_BAD_INTEGRITY so upper-level applications
         * can interpret that as "password incorrect", which is probably
         * the best error we can return in this situation.
         */
        return(KRB5KRB_AP_ERR_BAD_INTEGRITY);
    }

    /* fill in enc_sam_response_enc_2 */
    enc_sam_response_enc_2.magic = KV5M_ENC_SAM_RESPONSE_ENC_2;
    enc_sam_response_enc_2.sam_nonce = sc2b->sam_nonce;
    if (sc2b->sam_flags & KRB5_SAM_SEND_ENCRYPTED_SAD) {
        enc_sam_response_enc_2.sam_sad = response_data;
    } else {
        enc_sam_response_enc_2.sam_sad.data = NULL;
        enc_sam_response_enc_2.sam_sad.length = 0;
    }

    /* encode and encrypt enc_sam_response_enc_2 with as_key */
    retval = encode_krb5_enc_sam_response_enc_2(&enc_sam_response_enc_2,
                                                &scratch);
    if (retval) {
        krb5_free_sam_challenge_2(context, sc2);
        krb5_free_sam_challenge_2_body(context, sc2b);
        return(retval);
    }

    /* Fill in sam_response_2 */
    memset(&sr2, 0, sizeof(sr2));
    sr2.sam_type = sc2b->sam_type;
    sr2.sam_flags = sc2b->sam_flags;
    sr2.sam_track_id = sc2b->sam_track_id;
    sr2.sam_nonce = sc2b->sam_nonce;

    /* Now take care of sr2.sam_enc_nonce_or_sad by encrypting encoded   */
    /* enc_sam_response_enc_2 from above */

    retval = krb5_c_encrypt_length(context, rock->as_key->enctype,
				   scratch->length, &ciph_len);
    if (retval) {
        krb5_free_sam_challenge_2(context, sc2);
        krb5_free_sam_challenge_2_body(context, sc2b);
        krb5_free_data(context, scratch);
        return(retval);
    }
    sr2.sam_enc_nonce_or_sad.ciphertext.length = ciph_len;

    sr2.sam_enc_nonce_or_sad.ciphertext.data =
        (char *)malloc(sr2.sam_enc_nonce_or_sad.ciphertext.length);

    if (!sr2.sam_enc_nonce_or_sad.ciphertext.data) {
        krb5_free_sam_challenge_2(context, sc2);
        krb5_free_sam_challenge_2_body(context, sc2b);
        krb5_free_data(context, scratch);
        return(ENOMEM);
    }

    retval = krb5_c_encrypt(context, rock->as_key,
			    KRB5_KEYUSAGE_PA_SAM_RESPONSE, NULL, scratch,
			    &sr2.sam_enc_nonce_or_sad);
    if (retval) {
        krb5_free_sam_challenge_2(context, sc2);
        krb5_free_sam_challenge_2_body(context, sc2b);
        krb5_free_data(context, scratch);
        krb5_free_data_contents(context, &sr2.sam_enc_nonce_or_sad.ciphertext);
        return(retval);
    }
    krb5_free_data(context, scratch);
    scratch = NULL;

    /* Encode the sam_response_2 */
    retval = encode_krb5_sam_response_2(&sr2, &scratch);
    krb5_free_sam_challenge_2(context, sc2);
    krb5_free_sam_challenge_2_body(context, sc2b);
    krb5_free_data_contents(context, &sr2.sam_enc_nonce_or_sad.ciphertext);

    if (retval) {
        return (retval);
    }

    /* Almost there, just need to make padata !  */
    sam_padata = malloc(2 * sizeof(*sam_padata));
    if (sam_padata == NULL) {
        krb5_free_data(context, scratch);
        return(ENOMEM);
    }
    sam_padata[0] = malloc(sizeof(krb5_pa_data));
    if (sam_padata[0] == NULL) {
        krb5_free_data(context, scratch);
	free(sam_padata);
        return(ENOMEM);
    }

    sam_padata[0]->magic = KV5M_PA_DATA;
    sam_padata[0]->pa_type = KRB5_PADATA_SAM_RESPONSE_2;
    sam_padata[0]->length = scratch->length;
    sam_padata[0]->contents = (krb5_octet *) scratch->data;
    free(scratch);
    sam_padata[1] = NULL;

    *out_padata = sam_padata;

    return(0);
}
Пример #9
0
static int
find_type_in_ad(krb5_context context,
		int type,
		krb5_data *data,
		krb5_boolean *found,
		krb5_boolean failp,
		krb5_keyblock *sessionkey,
		const AuthorizationData *ad,
		int level)
{
    krb5_error_code ret = 0;
    size_t i;

    if (level > 9) {
	ret = ENOENT; /* XXX */
	krb5_set_error_message(context, ret,
			       N_("Authorization data nested deeper "
				  "then %d levels, stop searching", ""),
			       level);
	goto out;
    }

    /*
     * Only copy out the element the first time we get to it, we need
     * to run over the whole authorization data fields to check if
     * there are any container clases we need to care about.
     */
    for (i = 0; i < ad->len; i++) {
	if (!*found && ad->val[i].ad_type == type) {
	    ret = der_copy_octet_string(&ad->val[i].ad_data, data);
	    if (ret) {
		krb5_set_error_message(context, ret,
				       N_("malloc: out of memory", ""));
		goto out;
	    }
	    *found = TRUE;
	    continue;
	}
	switch (ad->val[i].ad_type) {
	case KRB5_AUTHDATA_IF_RELEVANT: {
	    AuthorizationData child;
	    ret = decode_AuthorizationData(ad->val[i].ad_data.data,
					   ad->val[i].ad_data.length,
					   &child,
					   NULL);
	    if (ret) {
		krb5_set_error_message(context, ret,
				       N_("Failed to decode "
					  "IF_RELEVANT with %d", ""),
				       (int)ret);
		goto out;
	    }
	    ret = find_type_in_ad(context, type, data, found, FALSE,
				  sessionkey, &child, level + 1);
	    free_AuthorizationData(&child);
	    if (ret)
		goto out;
	    break;
	}
#if 0 /* XXX test */
	case KRB5_AUTHDATA_KDC_ISSUED: {
	    AD_KDCIssued child;

	    ret = decode_AD_KDCIssued(ad->val[i].ad_data.data,
				      ad->val[i].ad_data.length,
				      &child,
				      NULL);
	    if (ret) {
		krb5_set_error_message(context, ret,
				       N_("Failed to decode "
					  "AD_KDCIssued with %d", ""),
				       ret);
		goto out;
	    }
	    if (failp) {
		krb5_boolean valid;
		krb5_data buf;
		size_t len;

		ASN1_MALLOC_ENCODE(AuthorizationData, buf.data, buf.length,
				   &child.elements, &len, ret);
		if (ret) {
		    free_AD_KDCIssued(&child);
		    krb5_clear_error_message(context);
		    goto out;
		}
		if(buf.length != len)
		    krb5_abortx(context, "internal error in ASN.1 encoder");

		ret = krb5_c_verify_checksum(context, sessionkey, 19, &buf,
					     &child.ad_checksum, &valid);
		krb5_data_free(&buf);
		if (ret) {
		    free_AD_KDCIssued(&child);
		    goto out;
		}
		if (!valid) {
		    krb5_clear_error_message(context);
		    ret = ENOENT;
		    free_AD_KDCIssued(&child);
		    goto out;
		}
	    }
	    ret = find_type_in_ad(context, type, data, found, failp, sessionkey,
				  &child.elements, level + 1);
	    free_AD_KDCIssued(&child);
	    if (ret)
		goto out;
	    break;
	}
#endif
	case KRB5_AUTHDATA_AND_OR:
	    if (!failp)
		break;
	    ret = ENOENT; /* XXX */
	    krb5_set_error_message(context, ret,
				   N_("Authorization data contains "
				      "AND-OR element that is unknown to the "
				      "application", ""));
	    goto out;
	default:
	    if (!failp)
		break;
	    ret = ENOENT; /* XXX */
	    krb5_set_error_message(context, ret,
				   N_("Authorization data contains "
				      "unknown type (%d) ", ""),
				   ad->val[i].ad_type);
	    goto out;
	}
    }
out:
    if (ret) {
	if (*found) {
	    krb5_data_free(data);
	    *found = 0;
	}
    }
    return ret;
}
Пример #10
0
krb5_error_code
kdc_find_fast(krb5_kdc_req **requestptr,
              krb5_data *checksummed_data,
              krb5_keyblock *tgs_subkey,
              krb5_keyblock *tgs_session,
              struct kdc_request_state *state,
              krb5_data **inner_body_out)
{
    krb5_error_code retval = 0;
    krb5_pa_data *fast_padata, *cookie_padata = NULL;
    krb5_data scratch, *inner_body = NULL;
    krb5_fast_req * fast_req = NULL;
    krb5_kdc_req *request = *requestptr;
    krb5_fast_armored_req *fast_armored_req = NULL;
    krb5_checksum *cksum;
    krb5_boolean cksum_valid;
    krb5_keyblock empty_keyblock;
    kdc_realm_t *kdc_active_realm = state->realm_data;

    if (inner_body_out != NULL)
        *inner_body_out = NULL;
    scratch.data = NULL;
    krb5_clear_error_message(kdc_context);
    memset(&empty_keyblock, 0, sizeof(krb5_keyblock));
    fast_padata = krb5int_find_pa_data(kdc_context,
                                       request->padata, KRB5_PADATA_FX_FAST);
    if (fast_padata !=  NULL){
        scratch.length = fast_padata->length;
        scratch.data = (char *) fast_padata->contents;
        retval = decode_krb5_pa_fx_fast_request(&scratch, &fast_armored_req);
        if (retval == 0 &&fast_armored_req->armor) {
            switch (fast_armored_req->armor->armor_type) {
            case KRB5_FAST_ARMOR_AP_REQUEST:
                if (tgs_subkey) {
                    retval = KRB5KDC_ERR_PREAUTH_FAILED;
                    krb5_set_error_message(kdc_context, retval,
                                           _("Ap-request armor not permitted "
                                             "with TGS"));
                    break;
                }
                retval = armor_ap_request(state, fast_armored_req->armor);
                break;
            default:
                krb5_set_error_message(kdc_context, KRB5KDC_ERR_PREAUTH_FAILED,
                                       _("Unknown FAST armor type %d"),
                                       fast_armored_req->armor->armor_type);
                retval = KRB5KDC_ERR_PREAUTH_FAILED;
            }
        }
        if (retval == 0 && !state->armor_key) {
            if (tgs_subkey)
                retval = krb5_c_fx_cf2_simple(kdc_context,
                                              tgs_subkey, "subkeyarmor",
                                              tgs_session, "ticketarmor",
                                              &state->armor_key);
            else {
                retval = KRB5KDC_ERR_PREAUTH_FAILED;
                krb5_set_error_message(kdc_context, retval,
                                       _("No armor key but FAST armored "
                                         "request present"));
            }
        }
        if (retval == 0) {
            krb5_data plaintext;
            plaintext.length = fast_armored_req->enc_part.ciphertext.length;
            plaintext.data = malloc(plaintext.length);
            if (plaintext.data == NULL)
                retval = ENOMEM;
            retval = krb5_c_decrypt(kdc_context,
                                    state->armor_key,
                                    KRB5_KEYUSAGE_FAST_ENC, NULL,
                                    &fast_armored_req->enc_part,
                                    &plaintext);
            if (retval == 0)
                retval = decode_krb5_fast_req(&plaintext, &fast_req);
            if (retval == 0 && inner_body_out != NULL) {
                retval = fetch_asn1_field((unsigned char *)plaintext.data,
                                          1, 2, &scratch);
                if (retval == 0) {
                    retval = krb5_copy_data(kdc_context, &scratch,
                                            &inner_body);
                }
            }
            if (plaintext.data)
                free(plaintext.data);
        }
        cksum = &fast_armored_req->req_checksum;
        if (retval == 0)
            retval = krb5_c_verify_checksum(kdc_context, state->armor_key,
                                            KRB5_KEYUSAGE_FAST_REQ_CHKSUM,
                                            checksummed_data, cksum,
                                            &cksum_valid);
        if (retval == 0 && !cksum_valid) {
            retval = KRB5KRB_AP_ERR_MODIFIED;
            krb5_set_error_message(kdc_context, retval,
                                   _("FAST req_checksum invalid; request "
                                     "modified"));
        }
        if (retval == 0) {
            if (!krb5_c_is_keyed_cksum(cksum->checksum_type)) {
                retval = KRB5KDC_ERR_POLICY;
                krb5_set_error_message(kdc_context, retval,
                                       _("Unkeyed checksum used in fast_req"));
            }
        }
        if (retval == 0) {
            if ((fast_req->fast_options & UNSUPPORTED_CRITICAL_FAST_OPTIONS) != 0)
                retval = KRB5KDC_ERR_UNKNOWN_CRITICAL_FAST_OPTION;
        }
        if (retval == 0)
            cookie_padata = krb5int_find_pa_data(kdc_context,
                                                 fast_req->req_body->padata,
                                                 KRB5_PADATA_FX_COOKIE);
        if (retval == 0) {
            state->fast_options = fast_req->fast_options;
            krb5_free_kdc_req( kdc_context, request);
            *requestptr = fast_req->req_body;
            fast_req->req_body = NULL;
        }
    }
    else {
        cookie_padata = krb5int_find_pa_data(kdc_context,
                                             request->padata,
                                             KRB5_PADATA_FX_COOKIE);
    }
    if (retval == 0 && cookie_padata != NULL) {
        krb5_pa_data *new_padata = malloc(sizeof (krb5_pa_data));
        if (new_padata == NULL) {
            retval = ENOMEM;
        } else {
            new_padata->pa_type = KRB5_PADATA_FX_COOKIE;
            new_padata->length = cookie_padata->length;
            new_padata->contents = malloc(new_padata->length);
            if (new_padata->contents == NULL) {
                retval = ENOMEM;
                free(new_padata);
            } else {
                memcpy(new_padata->contents, cookie_padata->contents,
                       new_padata->length);
                state->cookie = new_padata;
            }
        }
    }
    if (retval == 0 && inner_body_out != NULL) {
        *inner_body_out = inner_body;
        inner_body = NULL;
    }
    krb5_free_data(kdc_context, inner_body);
    if (fast_req)
        krb5_free_fast_req( kdc_context, fast_req);
    if (fast_armored_req)
        krb5_free_fast_armored_req(kdc_context, fast_armored_req);
    return retval;
}
Пример #11
0
static krb5_error_code
recvauth(int f,
	krb5_context krb_context,
	unsigned int *valid_checksum,
	krb5_ticket **ticket,
	int *auth_type,
	krb5_principal *client,
	int encr_flag,
	krb5_keytab keytab)
{
	krb5_error_code status = 0;
	krb5_auth_context auth_context = NULL;
	krb5_rcache rcache;
	krb5_authenticator *authenticator;
	krb5_data inbuf;
	krb5_data auth_version;

	*valid_checksum = 0;

	if ((status = krb5_auth_con_init(krb_context, &auth_context)))
		return (status);

	/* Only need remote address for rd_cred() to verify client */
	if ((status = krb5_auth_con_genaddrs(krb_context, auth_context, f,
			KRB5_AUTH_CONTEXT_GENERATE_REMOTE_FULL_ADDR)))
		return (status);

	status = krb5_auth_con_getrcache(krb_context, auth_context, &rcache);
	if (status)
		return (status);

	if (!rcache) {
		krb5_principal server;

		status = krb5_sname_to_principal(krb_context, 0, 0,
						KRB5_NT_SRV_HST, &server);
		if (status)
			return (status);

		status = krb5_get_server_rcache(krb_context,
				krb5_princ_component(krb_context, server, 0),
				&rcache);
		krb5_free_principal(krb_context, server);
		if (status)
			return (status);

		status = krb5_auth_con_setrcache(krb_context, auth_context,
						rcache);
		if (status)
			return (status);
	}
	if ((status = krb5_compat_recvauth(krb_context,
					&auth_context,
					&f,
					NULL,	/* Specify daemon principal */
					0,	/* no flags */
					keytab,	/* NULL to use v5srvtab */
					ticket,	/* return ticket */
					auth_type, /* authentication system */
					&auth_version))) {
		if (*auth_type == KRB5_RECVAUTH_V5) {
			/*
			 * clean up before exiting
			 */
			getstr(f, rusername, sizeof (rusername), "remuser");
			getstr(f, lusername, sizeof (lusername), "locuser");
			getstr(f, term, sizeof (term), "Terminal type");
		}
		return (status);
	}

	getstr(f, lusername, sizeof (lusername), "locuser");
	getstr(f, term, sizeof (term), "Terminal type");

	kcmd_protocol = KCMD_UNKNOWN_PROTOCOL;
	if (auth_version.length != 9 || auth_version.data == NULL) {
		syslog(LOG_ERR, "Bad application protocol version length in "
		    "KRB5 exchange, exiting");
		fatal(f, "Bad application version length, exiting.");
	}
	/*
	 * Determine which Kerberos CMD protocol was used.
	 */
	if (strncmp(auth_version.data, "KCMDV0.1", 9) == 0) {
		kcmd_protocol = KCMD_OLD_PROTOCOL;
	} else if (strncmp(auth_version.data, "KCMDV0.2", 9) == 0) {
		kcmd_protocol = KCMD_NEW_PROTOCOL;
	} else {
		syslog(LOG_ERR, "Unrecognized KCMD protocol (%s), exiting",
			(char *)auth_version.data);
		fatal(f, "Unrecognized KCMD protocol, exiting");
	}

	if ((*auth_type == KRB5_RECVAUTH_V5) && chksum_flag &&
		kcmd_protocol == KCMD_OLD_PROTOCOL) {
		if ((status = krb5_auth_con_getauthenticator(krb_context,
							    auth_context,
							    &authenticator)))
			return (status);
		if (authenticator->checksum) {
			struct sockaddr_storage adr;
			int adr_length = sizeof (adr);
			int buflen;
			krb5_data input;
			krb5_keyblock key;
			char *chksumbuf;

			/*
			 * Define the lenght of the chksum buffer.
			 * chksum string = "[portnum]:termstr:username"
			 * The extra 32 is to hold a integer string for
			 * the portnumber.
			 */
			buflen = strlen(term) + strlen(lusername) + 32;
			chksumbuf = (char *)malloc(buflen);
			if (chksumbuf == 0) {
				krb5_free_authenticator(krb_context,
							authenticator);
				fatal(f, "Out of memory error");
			}

			if (getsockname(f, (struct sockaddr *)&adr,
							&adr_length) != 0) {
				krb5_free_authenticator(krb_context,
							authenticator);
				fatal(f, "getsockname error");
			}

			(void) snprintf(chksumbuf, buflen,
					"%u:%s%s",
					ntohs(SOCK_PORT(adr)),
					term, lusername);

			input.data = chksumbuf;
			input.length = strlen(chksumbuf);
			key.contents = (*ticket)->enc_part2->session->contents;
			key.length = (*ticket)->enc_part2->session->length;
			status = krb5_c_verify_checksum(krb_context,
						&key, 0,
						&input,
						authenticator->checksum,
						valid_checksum);

			if (status == 0 && *valid_checksum == 0)
				status = KRB5KRB_AP_ERR_BAD_INTEGRITY;

			if (chksumbuf)
				krb5_xfree(chksumbuf);
			if (status) {
				krb5_free_authenticator(krb_context,
							authenticator);
				return (status);
			}
		}
		krb5_free_authenticator(krb_context, authenticator);
	}

	if ((status = krb5_copy_principal(krb_context,
					(*ticket)->enc_part2->client,
					client)))
		return (status);

	/* Get the Unix username of the remote user */
	getstr(f, rusername, sizeof (rusername), "remuser");

	/* Get the Kerberos principal name string of the remote user */
	if ((status = krb5_unparse_name(krb_context, *client, &krusername)))
		return (status);

#ifdef DEBUG
	syslog(LOG_DEBUG | LOG_AUTH, "rlogind: got krb5 credentials for %s",
	    (krusername != NULL ? krusername : "******"));
#endif

	if (encr_flag) {
		status = krb5_auth_con_getremotesubkey(krb_context,
						    auth_context,
						    &session_key);
		if (status) {
			syslog(LOG_ERR, "Error getting KRB5 session "
			    "subkey, exiting");
			fatal(f, "Error getting KRB5 session subkey, exiting");
		}
		/*
		 * The "new" protocol requires that a subkey be sent.
		 */
		if (session_key == NULL &&
		    kcmd_protocol == KCMD_NEW_PROTOCOL) {
			syslog(LOG_ERR, "No KRB5 session subkey sent, exiting");
			fatal(f, "No KRB5 session subkey sent, exiting");
		}
		/*
		 * The "old" protocol does not permit an authenticator subkey.
		 * The key is taken from the ticket instead (see below).
		 */
		if (session_key != NULL &&
		    kcmd_protocol == KCMD_OLD_PROTOCOL) {
			syslog(LOG_ERR, "KRB5 session subkey not permitted "
			    "with old KCMD protocol, exiting");

			fatal(f, "KRB5 session subkey not permitted "
			    "with old KCMD protocol, exiting");
		}
		/*
		 * If no key at this point, use the session key from
		 * the ticket.
		 */
		if (session_key == NULL) {
			/*
			 * Save the session key so we can configure the crypto
			 * module later.
			 */
			status = krb5_copy_keyblock(krb_context,
					    (*ticket)->enc_part2->session,
					    &session_key);
			if (status) {
				syslog(LOG_ERR, "krb5_copy_keyblock failed");
				fatal(f, "krb5_copy_keyblock failed");
			}
		}
		/*
		 * If session key still cannot be found, we must
		 * exit because encryption is required here
		 * when encr_flag (-x) is set.
		 */
		if (session_key == NULL) {
			syslog(LOG_ERR, "Could not find an encryption key,"
				    "exiting");
			fatal(f, "Encryption required but key not found, "
			    "exiting");
		}
	}
	/*
	 * Use krb5_read_message to read the principal stuff.
	 */
	if ((status = krb5_read_message(krb_context, (krb5_pointer)&f,
					&inbuf)))
		fatal(f, "Error reading krb5 message");

	if (inbuf.length) { /* Forwarding being done, read creds */
		krb5_creds **creds = NULL;

		if (status = krb5_rd_cred(krb_context, auth_context, &inbuf,
					    &creds, NULL)) {
			if (rcache)
				(void) krb5_rc_close(krb_context, rcache);
			krb5_free_creds(krb_context, *creds);
			fatal(f, "Can't get forwarded credentials");
		}

		/* Store the forwarded creds in the ccache */
		if (status = store_forw_creds(krb_context,
					    creds, *ticket, lusername,
					    &ccache)) {
			if (rcache)
				(void) krb5_rc_close(krb_context, rcache);
			krb5_free_creds(krb_context, *creds);
			fatal(f, "Can't store forwarded credentials");
		}
		krb5_free_creds(krb_context, *creds);
	}

	if (rcache)
		(void) krb5_rc_close(krb_context, rcache);

	return (status);
}
Пример #12
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;
}
Пример #13
0
int
main(int argc, char **argv)
{
    krb5_error_code ret;
    krb5_context context = NULL;
    size_t i;
    struct test *test;
    krb5_keyblock kb, *kbp;
    krb5_checksum cksum;
    krb5_cksumtype mtype;
    krb5_boolean valid, verbose = FALSE;
    int status = 0;

    if (argc >= 2 && strcmp(argv[1], "-v") == 0)
        verbose = TRUE;
    for (i = 0; i < sizeof(test_cases) / sizeof(*test_cases); i++) {
        test = &test_cases[i];
        if (test->enctype != 0) {
            kb.magic = KV5M_KEYBLOCK;
            kb.enctype = test->enctype;
            kb.length = test->keybits.length;
            kb.contents = (unsigned char *)test->keybits.data;
            kbp = &kb;
        } else
            kbp = NULL;
        ret = krb5_c_make_checksum(context, test->sumtype, kbp, test->usage,
                                   &test->plaintext, &cksum);
        assert(!ret);
        if (verbose) {
            char buf[64];
            krb5_cksumtype_to_string(test->sumtype, buf, sizeof(buf));
            printf("\nTest %d:\n", (int)i);
            printf("Plaintext: %.*s\n", (int)test->plaintext.length,
                   test->plaintext.data);
            printf("Checksum type: %s\n", buf);
            if (test->enctype != 0) {
                krb5_enctype_to_name(test->enctype, FALSE, buf, sizeof(buf));
                printf("Enctype: %s\n", buf);
                printhex("Key: ", test->keybits.data, test->keybits.length);
                printf("Key usage: %d\n", (int)test->usage);
            }
            printhex("Checksum: ", cksum.contents, cksum.length);
        }
        if (test->cksum.length != cksum.length ||
            memcmp(test->cksum.data, cksum.contents, cksum.length) != 0) {
            printf("derive test %d failed\n", (int)i);
            status = 1;
            if (!verbose)
                break;
        }

        /* Test that the checksum verifies successfully. */
        ret = krb5_c_verify_checksum(context, kbp, test->usage,
                                     &test->plaintext, &cksum, &valid);
        assert(!ret);
        if (!valid) {
            printf("test %d verify failed\n", (int)i);
            status = 1;
            if (!verbose)
                break;
        }

        if (kbp != NULL) {
            ret = krb5int_c_mandatory_cksumtype(context, kbp->enctype, &mtype);
            assert(!ret);
            if (test->sumtype == mtype) {
                /* Test that a checksum type of 0 uses the mandatory checksum
                 * type for the key. */
                cksum.checksum_type = 0;
                ret = krb5_c_verify_checksum(context, kbp, test->usage,
                                             &test->plaintext, &cksum, &valid);
                assert(!ret && valid);
            }
        }

        krb5_free_checksum_contents(context, &cksum);
    }
    return status;
}
Пример #14
0
DWORD
LwKrb5VerifyPac(
    krb5_context ctx,
    const krb5_ticket *pTgsTicket,
    const struct berval *pPacBerVal,
    const krb5_keyblock *serviceKey,
    char** ppchLogonInfo,
    size_t* psLogonInfo
    )
{
    krb5_error_code ret = 0;
    PAC_DATA *pPacData = NULL;
    DWORD i;
    char *pchPacCopy = NULL;
    //Do not free
    krb5_data krbPacData = {0};
    //Do not free
    krb5_checksum checksum = {0};
    //Do not free
    PAC_SIGNATURE_DATA *pServerSig = NULL;
    PAC_LOGON_NAME *pLogonName = NULL;
    size_t sServerSig = 0;
    //Do not free
    char *pchLogonInfoStart = NULL;
    size_t sLogonInfoLen = 0;
    krb5_boolean bHasGoodChecksum = FALSE;
    uint64_t qwNtAuthTime;
    DWORD dwError = LW_ERROR_SUCCESS;
    //Free with krb5_free_unparsed_name
    PSTR pszClientPrincipal = NULL;
    PSTR pszLogonName = NULL;
    char* pchLogonInfo = NULL;

    #if defined(WORDS_BIGENDIAN)
    WORD * pwNameLocal = NULL;
    DWORD dwCount = 0;
    #endif

    dwError = LwAllocateMemory(
                pPacBerVal->bv_len,
                OUT_PPVOID(&pPacData));
    BAIL_ON_LW_ERROR(dwError);

    memcpy(pPacData, pPacBerVal->bv_val, pPacBerVal->bv_len);

    #if defined(WORDS_BIGENDIAN)
        pPacData->dwBufferCount = LW_ENDIAN_SWAP32(pPacData->dwBufferCount);
        pPacData->dwVersion = LW_ENDIAN_SWAP32(pPacData->dwVersion);
    #endif

    // We only know about version 0
    if (pPacData->dwVersion != 0)
    {
        dwError = LW_ERROR_INVALID_MESSAGE;
        BAIL_ON_LW_ERROR(dwError);
    }
    // Make sure that the last buffer in the pac data doesn't go out of bounds
    // of the parent buffer
    if ((void *)&pPacData->buffers[pPacData->dwBufferCount] -
            (void *)pPacData > pPacBerVal->bv_len)
    {
        dwError = LW_ERROR_INVALID_MESSAGE;
        BAIL_ON_LW_ERROR(dwError);
    }

    // Make sure the data associated with each buffer doesn't go out of
    // bounds
    for (i = 0; i < pPacData->dwBufferCount; i++)
    {
        #if defined(WORDS_BIGENDIAN)
            pPacData->buffers[i].dwType = LW_ENDIAN_SWAP32(pPacData->buffers[i].dwType);
            pPacData->buffers[i].dwSize = LW_ENDIAN_SWAP32(pPacData->buffers[i].dwSize);
            pPacData->buffers[i].qwOffset = LW_ENDIAN_SWAP64(pPacData->buffers[i].qwOffset);
        #endif

        if (pPacData->buffers[i].qwOffset + pPacData->buffers[i].dwSize <
                pPacData->buffers[i].qwOffset)
        {
            dwError = LW_ERROR_INVALID_MESSAGE;
            BAIL_ON_LW_ERROR(dwError);
        }
        if (pPacData->buffers[i].qwOffset + pPacData->buffers[i].dwSize >
                pPacBerVal->bv_len)
        {
            dwError = LW_ERROR_INVALID_MESSAGE;
            BAIL_ON_LW_ERROR(dwError);
        }
    }

    dwError = LwAllocateMemory(
                pPacBerVal->bv_len,
                OUT_PPVOID(&pchPacCopy));
    BAIL_ON_LW_ERROR(dwError);

    memcpy(pchPacCopy, pPacBerVal->bv_val, pPacBerVal->bv_len);

    krbPacData.magic = KV5M_DATA;
    krbPacData.length = pPacBerVal->bv_len;
    krbPacData.data = pchPacCopy;

    for (i = 0; i < pPacData->dwBufferCount; i++)
    {
    	switch (pPacData->buffers[i].dwType)
    	{
    	    case PAC_TYPE_LOGON_INFO:
    	        pchLogonInfoStart = (char *)pPacData + pPacData->buffers[i].qwOffset;
                sLogonInfoLen = pPacData->buffers[i].dwSize;
                break;
            case PAC_TYPE_SRV_CHECKSUM:
                pServerSig = (PAC_SIGNATURE_DATA *)((char *)pPacData +
                             pPacData->buffers[i].qwOffset);

                #if defined(WORDS_BIGENDIAN)
                    pServerSig->dwType = LW_ENDIAN_SWAP32(pServerSig->dwType);
                #endif

                sServerSig = pPacData->buffers[i].dwSize -
                        (size_t)&((PAC_SIGNATURE_DATA *)0)->pchSignature;
                /* The checksum is calculated with the signatures zeroed out. */
                memset(pchPacCopy + pPacData->buffers[i].qwOffset +
                       (size_t)&((PAC_SIGNATURE_DATA *)0)->pchSignature,
                       0,
                       pPacData->buffers[i].dwSize -
                           (size_t)&((PAC_SIGNATURE_DATA *)0)->pchSignature);
                break;
            case PAC_TYPE_KDC_CHECKSUM:
                /* The checksum is calculated with the signatures zeroed out. */
    		memset(pchPacCopy + pPacData->buffers[i].qwOffset +
    	               (size_t)&((PAC_SIGNATURE_DATA *)0)->pchSignature,
    		       0,
    		       pPacData->buffers[i].dwSize -
    		           (size_t)&((PAC_SIGNATURE_DATA *)0)->pchSignature);
    		break;
            case PAC_TYPE_LOGON_NAME:
                pLogonName = (PAC_LOGON_NAME *)((char *)pPacData +
                             pPacData->buffers[i].qwOffset);

                #if defined(WORDS_BIGENDIAN)
                    pLogonName->ticketTime = LW_ENDIAN_SWAP64(pLogonName->ticketTime);
                    pLogonName->wAccountNameLen = LW_ENDIAN_SWAP16(pLogonName->wAccountNameLen);
                    pwNameLocal = pLogonName->pwszName;

                    for ( dwCount = 0 ;
                          dwCount < pLogonName->wAccountNameLen / 2 ;
                          dwCount++ )
                    {
                        pwNameLocal[dwCount] = LW_ENDIAN_SWAP16(pwNameLocal[dwCount]);
                    }
                #endif

                if ((char *)&pLogonName->pwszName +
                    pLogonName->wAccountNameLen >
                    (char *)pPacData + pPacData->buffers[i].qwOffset +
                    pPacData->buffers[i].dwSize)
                {
                    // The message is invalid because the terminating null
                    // of the name lands outside of the buffer.
                    dwError = LW_ERROR_INVALID_MESSAGE;
                    BAIL_ON_LW_ERROR(dwError);
                }
                break;
            default:
                break;
    	}
    }

    if (pServerSig == NULL)
    {
        dwError = LW_ERROR_INVALID_MESSAGE;
        BAIL_ON_LW_ERROR(dwError);
    }

    if (pLogonName == NULL)
    {
        //We need the logon name to verify the pac is for the right user
        dwError = LW_ERROR_INVALID_MESSAGE;
        BAIL_ON_LW_ERROR(dwError);
    }

    if (pchLogonInfoStart == NULL)
    {
        /* The buffer we really care about isn't in the pac. */
        dwError = LW_ERROR_INVALID_MESSAGE;
        BAIL_ON_LW_ERROR(dwError);
    }

    checksum.magic = KV5M_CHECKSUM;
    checksum.checksum_type = pServerSig->dwType;
    checksum.length = sServerSig;
    checksum.contents = (unsigned char *)pServerSig->pchSignature;

    ret = krb5_c_verify_checksum(
                    ctx,
                    serviceKey,
                    KRB5_KEYUSAGE_APP_DATA_CKSUM,
                    &krbPacData,
                    &checksum,
                    &bHasGoodChecksum);
    BAIL_ON_KRB_ERROR(ctx, ret);

    if (!bHasGoodChecksum)
    {
        dwError = LW_ERROR_INVALID_MESSAGE;
        BAIL_ON_LW_ERROR(dwError);
    }

    // Make sure the pac was issued with this ticket, not an old ticket
    qwNtAuthTime = pTgsTicket->enc_part2->times.authtime;
    qwNtAuthTime += 11644473600LL;
    qwNtAuthTime *= 1000*1000*10;
    if (pLogonName->ticketTime != qwNtAuthTime)
    {
        dwError = LW_ERROR_CLOCK_SKEW;
        BAIL_ON_LW_ERROR(dwError);
    }
    ret = krb5_unparse_name(
                    ctx,
                    pTgsTicket->enc_part2->client,
                    &pszClientPrincipal);
    BAIL_ON_KRB_ERROR(ctx, ret);

    // Strip off the domain name
    if (strchr(pszClientPrincipal, '@') != NULL)
    {
        strchr(pszClientPrincipal, '@')[0] = '\0'; 
    }

    dwError = LwWc16snToMbs(
        pLogonName->pwszName,
        &pszLogonName,
        pLogonName->wAccountNameLen / 2);
    BAIL_ON_LW_ERROR(dwError);    

    if (strcasecmp(pszClientPrincipal, pszLogonName))
    {
        // The pac belongs to a different user
        dwError = LW_ERROR_INVALID_LOGIN_ID;
        BAIL_ON_LW_ERROR(dwError);    
    }

    dwError = LwAllocateMemory(
                sLogonInfoLen,
                OUT_PPVOID(&pchLogonInfo));
    BAIL_ON_LW_ERROR(dwError);

    memcpy(pchLogonInfo, pchLogonInfoStart, sLogonInfoLen);
    *ppchLogonInfo = pchLogonInfo;
    *psLogonInfo = sLogonInfoLen;

cleanup:
    LW_SAFE_FREE_STRING(pszLogonName);
    LW_SAFE_FREE_MEMORY(pPacData);
    LW_SAFE_FREE_MEMORY(pchPacCopy);
    if (pszClientPrincipal != NULL)
    {
        krb5_free_unparsed_name(ctx, pszClientPrincipal);
    }
    return dwError;

error:
    LW_SAFE_FREE_MEMORY(pchLogonInfo);
    *ppchLogonInfo = NULL;
    goto cleanup;
}
Пример #15
0
int
ksm_process_in_msg(struct snmp_secmod_incoming_params *parms)
{
    long            temp;
    krb5_cksumtype  cksumtype;
    krb5_auth_context auth_context = NULL;
    krb5_error_code retcode;
    krb5_checksum   checksum;
    krb5_data       ap_req, ivector;
    krb5_flags      flags;
    krb5_keyblock  *subkey = NULL;
#ifdef MIT_NEW_CRYPTO
    krb5_data       input, output;
    krb5_boolean    valid;
    krb5_enc_data   in_crypt;
#else                           /* MIT_NEW_CRYPTO */
    krb5_encrypt_block eblock;
#endif                          /* MIT_NEW_CRYPTO */
    krb5_ticket    *ticket = NULL;
    int             retval = SNMPERR_SUCCESS, response = 0;
    size_t          length =
        parms->wholeMsgLen - (u_int) (parms->secParams - parms->wholeMsg);
    u_char         *current = parms->secParams, type;
    size_t          cksumlength, blocksize;
    long            hint;
    char           *cname;
    struct ksm_secStateRef *ksm_state;
    struct ksm_cache_entry *entry;

    DEBUGMSGTL(("ksm", "Processing has begun\n"));

    checksum.contents = NULL;
    ap_req.data = NULL;
    ivector.length = 0;
    ivector.data = NULL;

    /*
     * First, parse the security parameters (because we need the subkey inside
     * of the ticket to do anything
     */

    if ((current = asn_parse_sequence(current, &length, &type,
                                      (ASN_UNIVERSAL | ASN_PRIMITIVE |
                                       ASN_OCTET_STR),
                                      "ksm first octet")) == NULL) {
        DEBUGMSGTL(("ksm", "Initial security paramter parsing failed\n"));

        retval = SNMPERR_ASN_PARSE_ERR;
        goto error;
    }

    if ((current = asn_parse_sequence(current, &length, &type,
                                      (ASN_SEQUENCE | ASN_CONSTRUCTOR),
                                      "ksm sequence")) == NULL) {
        DEBUGMSGTL(("ksm",
                    "Security parameter sequence parsing failed\n"));

        retval = SNMPERR_ASN_PARSE_ERR;
        goto error;
    }

    if ((current = asn_parse_int(current, &length, &type, &temp,
                                 sizeof(temp))) == NULL) {
        DEBUGMSGTL(("ksm", "Security parameter checksum type parsing"
                    "failed\n"));

        retval = SNMPERR_ASN_PARSE_ERR;
        goto error;
    }

    cksumtype = temp;

#ifdef MIT_NEW_CRYPTO
    if (!krb5_c_valid_cksumtype(cksumtype)) {
        DEBUGMSGTL(("ksm", "Invalid checksum type (%d)\n", cksumtype));

        retval = SNMPERR_KRB5;
        snmp_set_detail("Invalid checksum type");
        goto error;
    }

    if (!krb5_c_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 (!krb5_c_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;
    }
#else /* ! MIT_NEW_CRYPTO */
    if (!valid_cksumtype(cksumtype)) {
        DEBUGMSGTL(("ksm", "Invalid checksum type (%d)\n", cksumtype));

        retval = SNMPERR_KRB5;
        snmp_set_detail("Invalid checksum type");
        goto error;
    }

    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;
    }
#endif /* MIT_NEW_CRYPTO */

    checksum.checksum_type = cksumtype;

    cksumlength = length;

    if ((current = asn_parse_sequence(current, &cksumlength, &type,
                                      (ASN_UNIVERSAL | ASN_PRIMITIVE |
                                       ASN_OCTET_STR), "ksm checksum")) ==
        NULL) {
        DEBUGMSGTL(("ksm",
                    "Security parameter checksum parsing failed\n"));

        retval = SNMPERR_ASN_PARSE_ERR;
        goto error;
    }

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

    memcpy(checksum.contents, current, cksumlength);

    checksum.length = cksumlength;
    checksum.checksum_type = cksumtype;

    /*
     * Zero out the checksum so the validation works correctly
     */

    memset(current, 0, cksumlength);

    current += cksumlength;
    length = parms->wholeMsgLen - (u_int) (current - parms->wholeMsg);

    if ((current = asn_parse_sequence(current, &length, &type,
                                      (ASN_UNIVERSAL | ASN_PRIMITIVE |
                                       ASN_OCTET_STR), "ksm ap_req")) ==
        NULL) {
        DEBUGMSGTL(("ksm", "KSM security parameter AP_REQ/REP parsing "
                    "failed\n"));

        retval = SNMPERR_ASN_PARSE_ERR;
        goto error;
    }

    ap_req.length = length;
    ap_req.data = malloc(length);
    if (!ap_req.data) {
        DEBUGMSGTL(("ksm",
                    "KSM unable to malloc %d bytes for AP_REQ/REP.\n",
                    length));
        retval = SNMPERR_MALLOC;
        goto error;
    }

    memcpy(ap_req.data, current, length);

    current += length;
    length = parms->wholeMsgLen - (u_int) (current - parms->wholeMsg);

    if ((current = asn_parse_int(current, &length, &type, &hint,
                                 sizeof(hint))) == NULL) {
        DEBUGMSGTL(("ksm",
                    "KSM security parameter hint parsing failed\n"));

        retval = SNMPERR_ASN_PARSE_ERR;
        goto error;
    }

    /*
     * Okay!  We've got it all!  Now try decoding the damn ticket.
     *
     * But of course there's a WRINKLE!  We need to figure out if we're
     * processing a AP_REQ or an AP_REP.  How do we do that?  We're going
     * to cheat, and look at the first couple of bytes (which is what
     * the Kerberos library routines do anyway).
     *
     * If there are ever new Kerberos message formats, we'll need to fix
     * this here.
     *
     * If it's a _response_, then we need to get the auth_context
     * from our cache.
     */

    if (ap_req.length
        && (ap_req.data[0] == 0x6e || ap_req.data[0] == 0x4e)) {

        /*
         * We need to initalize the authorization context, and set the
         * replay cache in it (and initialize the replay cache if we
         * haven't already
         */

        retcode = krb5_auth_con_init(kcontext, &auth_context);

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

        if (!rcache) {
            krb5_data       server;
            server.data = "host";
            server.length = strlen(server.data);

            retcode = krb5_get_server_rcache(kcontext, &server, &rcache);

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

        retcode = krb5_auth_con_setrcache(kcontext, auth_context, rcache);

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

        retcode = krb5_rd_req(kcontext, &auth_context, &ap_req, NULL,
                              keytab, &flags, &ticket);

        krb5_auth_con_setrcache(kcontext, auth_context, NULL);

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

        retcode =
            krb5_unparse_name(kcontext, ticket->enc_part2->client, &cname);

        if (retcode == 0) {
            DEBUGMSGTL(("ksm", "KSM authenticated principal name: %s\n",
                        cname));
            free(cname);
        }

        /*
         * Check to make sure AP_OPTS_MUTUAL_REQUIRED was set
         */

        if (!(flags & AP_OPTS_MUTUAL_REQUIRED)) {
            DEBUGMSGTL(("ksm",
                        "KSM MUTUAL_REQUIRED not set in request!\n"));
            retval = SNMPERR_KRB5;
            snmp_set_detail("MUTUAL_REQUIRED not set in message");
            goto error;
        }

        retcode =
            krb5_auth_con_getremotesubkey(kcontext, auth_context, &subkey);

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

    } else if (ap_req.length && (ap_req.data[0] == 0x6f ||
                                 ap_req.data[0] == 0x4f)) {
        /*
         * Looks like a response; let's see if we've got that auth_context
         * in our cache.
         */

        krb5_ap_rep_enc_part *repl = NULL;

        response = 1;

        entry = ksm_get_cache(parms->pdu->msgid);

        if (!entry) {
            DEBUGMSGTL(("ksm",
                        "KSM: Unable to find auth_context for PDU with "
                        "message ID of %ld\n", parms->pdu->msgid));
            retval = SNMPERR_KRB5;
            goto error;
        }

        auth_context = entry->auth_context;

        /*
         * In that case, let's call the rd_rep function
         */

        retcode = krb5_rd_rep(kcontext, auth_context, &ap_req, &repl);

        if (repl)
            krb5_free_ap_rep_enc_part(kcontext, repl);

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

        DEBUGMSGTL(("ksm", "KSM: krb5_rd_rep() decoded successfully.\n"));

        retcode =
            krb5_auth_con_getlocalsubkey(kcontext, auth_context, &subkey);

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

    } else {
        DEBUGMSGTL(("ksm", "Unknown Kerberos message type (%02x)\n",
                    ap_req.data[0]));
        retval = SNMPERR_KRB5;
        snmp_set_detail("Unknown Kerberos message type");
        goto error;
    }

#ifdef MIT_NEW_CRYPTO
    input.data = (char *) parms->wholeMsg;
    input.length = parms->wholeMsgLen;

    retcode =
        krb5_c_verify_checksum(kcontext, subkey, KSM_KEY_USAGE_CHECKSUM,
                               &input, &checksum, &valid);
#else                           /* MIT_NEW_CRYPTO */
    retcode = krb5_verify_checksum(kcontext, cksumtype, &checksum,
                                   parms->wholeMsg, parms->wholeMsgLen,
                                   (krb5_pointer) subkey->contents,
                                   subkey->length);
#endif                          /* MIT_NEW_CRYPTO */

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

    /*
     * Don't ask me why they didn't simply return an error, but we have
     * to check to see if "valid" is false.
     */

#ifdef MIT_NEW_CRYPTO
    if (!valid) {
        DEBUGMSGTL(("ksm", "Computed checksum did not match supplied "
                    "checksum!\n"));
        retval = SNMPERR_KRB5;
        snmp_set_detail
            ("Computed checksum did not match supplied checksum");
        goto error;
    }
#endif                          /* MIT_NEW_CRYPTO */

    /*
     * Handle an encrypted PDU.  Note that it's an OCTET_STRING of the
     * output of whatever Kerberos cryptosystem you're using (defined by
     * the encryption type).  Note that this is NOT the EncryptedData
     * sequence - it's what goes in the "cipher" field of EncryptedData.
     */

    if (parms->secLevel == SNMP_SEC_LEVEL_AUTHPRIV) {

        if ((current = asn_parse_sequence(current, &length, &type,
                                          (ASN_UNIVERSAL | ASN_PRIMITIVE |
                                           ASN_OCTET_STR), "ksm pdu")) ==
            NULL) {
            DEBUGMSGTL(("ksm", "KSM sPDU octet decoding failed\n"));
            retval = SNMPERR_ASN_PARSE_ERR;
            goto error;
        }

        /*
         * The PDU is now pointed at by "current", and the length is in
         * "length".
         */

        DEBUGMSGTL(("ksm", "KSM starting sPDU decode\n"));

        /*
         * We need to set up a blank initialization vector for the decryption.
         * 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);

#ifndef MIT_NEW_CRYPTO

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

        retcode = krb5_process_key(kcontext, &eblock, subkey);

        if (retcode) {
            DEBUGMSGTL(("ksm", "KSM key post-processing failed: %s\n",
                        error_message(retcode)));
            snmp_set_detail(error_message(retcode));
            retval = SNMPERR_KRB5;
            goto error;
        }
#endif                          /* !MIT_NEW_CRYPTO */

        if (length > *parms->scopedPduLen) {
            DEBUGMSGTL(("ksm", "KSM not enough room - have %d bytes to "
                        "decrypt but only %d bytes available\n", length,
                        *parms->scopedPduLen));
            retval = SNMPERR_TOO_LONG;
#ifndef MIT_NEW_CRYPTO
            krb5_finish_key(kcontext, &eblock);
#endif                          /* ! MIT_NEW_CRYPTO */
            goto error;
        }
#ifdef MIT_NEW_CRYPTO
        in_crypt.ciphertext.data = (char *) current;
        in_crypt.ciphertext.length = length;
        in_crypt.enctype = subkey->enctype;
        output.data = (char *) *parms->scopedPdu;
        output.length = *parms->scopedPduLen;

        retcode =
            krb5_c_decrypt(kcontext, subkey, KSM_KEY_USAGE_ENCRYPTION,
                           &ivector, &in_crypt, &output);
#else                           /* MIT_NEW_CRYPTO */

        retcode = krb5_decrypt(kcontext, (krb5_pointer) current,
                               *parms->scopedPdu, length, &eblock,
                               ivector.data);

        krb5_finish_key(kcontext, &eblock);

#endif                          /* MIT_NEW_CRYPTO */

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

        *parms->scopedPduLen = length;

    } else {
        /*
         * Clear PDU
         */

        *parms->scopedPdu = current;
        *parms->scopedPduLen =
            parms->wholeMsgLen - (current - parms->wholeMsg);
    }

    /*
     * A HUGE GROSS HACK
     */

    *parms->maxSizeResponse = parms->maxMsgSize - 200;

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

    /*
     * Set the secName to the right value (a hack for now).  But that's
     * only used for when we're processing a request, not a response.
     */

    if (!response) {

        retcode = krb5_unparse_name(kcontext, ticket->enc_part2->client,
                                    &cname);

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

        if (strlen(cname) > *parms->secNameLen + 1) {
            DEBUGMSGTL(("ksm",
                        "KSM: Principal length (%d) is too long (%d)\n",
                        strlen(cname), parms->secNameLen));
            retval = SNMPERR_TOO_LONG;
            free(cname);
            goto error;
        }

        strcpy(parms->secName, cname);
        *parms->secNameLen = strlen(cname);

        free(cname);

        /*
         * Also, if we're not a response, keep around our auth_context so we
         * can encode the reply message correctly
         */

        ksm_state = SNMP_MALLOC_STRUCT(ksm_secStateRef);

        if (!ksm_state) {
            DEBUGMSGTL(("ksm", "KSM unable to malloc memory for "
                        "ksm_secStateRef\n"));
            retval = SNMPERR_MALLOC;
            goto error;
        }

        ksm_state->auth_context = auth_context;
        auth_context = NULL;
        ksm_state->cksumtype = cksumtype;

        *parms->secStateRef = ksm_state;
    } else {

        /*
         * We _still_ have to set the secName in process_in_msg().  Do
         * that now with what we were passed in before (we cached it,
         * remember?)
         */

        memcpy(parms->secName, entry->secName, entry->secNameLen);
        *parms->secNameLen = entry->secNameLen;
    }

    /*
     * Just in case
     */

    parms->secEngineID = (u_char *) "";
    *parms->secEngineIDLen = 0;

    auth_context = NULL;        /* So we don't try to free it on success */

  error:
    if (retval == SNMPERR_ASN_PARSE_ERR &&
        snmp_increment_statistic(STAT_SNMPINASNPARSEERRS) == 0)
        DEBUGMSGTL(("ksm", "Failed to increment statistics.\n"));

    if (subkey)
        krb5_free_keyblock(kcontext, subkey);

    if (checksum.contents)
        free(checksum.contents);

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

    if (ticket)
        krb5_free_ticket(kcontext, ticket);

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

    if (ap_req.data)
        free(ap_req.data);

    return retval;
}
Пример #16
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;
}
Пример #17
0
static krb5_error_code
k5_pac_verify_server_checksum(krb5_context context,
                              const krb5_pac pac,
                              const krb5_keyblock *server)
{
    krb5_error_code ret;
    krb5_data pac_data; /* PAC with zeroed checksums */
    krb5_checksum checksum;
    krb5_data checksum_data;
    krb5_boolean valid;
    krb5_octet *p;

    ret = k5_pac_locate_buffer(context, pac,
                               PAC_SERVER_CHECKSUM, &checksum_data);
    if (ret != 0)
        return ret;

    if (checksum_data.length < PAC_SIGNATURE_DATA_LENGTH)
        return KRB5_BAD_MSIZE;

    p = (krb5_octet *)checksum_data.data;
    checksum.checksum_type = load_32_le(p);
    checksum.length = checksum_data.length - PAC_SIGNATURE_DATA_LENGTH;
    checksum.contents = p + PAC_SIGNATURE_DATA_LENGTH;
    if (!krb5_c_is_keyed_cksum(checksum.checksum_type))
        return KRB5KRB_AP_ERR_INAPP_CKSUM;

    pac_data.length = pac->data.length;
    pac_data.data = malloc(pac->data.length);
    if (pac_data.data == NULL)
        return ENOMEM;

    memcpy(pac_data.data, pac->data.data, pac->data.length);

    /* Zero out both checksum buffers */
    ret = k5_pac_zero_signature(context, pac,
                                PAC_SERVER_CHECKSUM, &pac_data);
    if (ret != 0) {
        free(pac_data.data);
        return ret;
    }

    ret = k5_pac_zero_signature(context, pac,
                                PAC_PRIVSVR_CHECKSUM, &pac_data);
    if (ret != 0) {
        free(pac_data.data);
        return ret;
    }

    ret = krb5_c_verify_checksum(context, server,
                                 KRB5_KEYUSAGE_APP_DATA_CKSUM,
                                 &pac_data, &checksum, &valid);

    free(pac_data.data);

    if (ret != 0) {
        return ret;
    }

    if (valid == FALSE)
        ret = KRB5KRB_AP_ERR_BAD_INTEGRITY;

    return ret;
}
Пример #18
0
OM_uint32
gss_krb5int_unseal_token_v3(krb5_context *contextptr,
			    OM_uint32 *minor_status,
			    krb5_gss_ctx_id_rec *ctx,
			    unsigned char *ptr, int bodysize,
			    gss_buffer_t message_buffer,
			    int *conf_state, int *qop_state, int toktype)
{
    krb5_context context = *contextptr;
    krb5_data plain;
    gssint_uint64 seqnum;
    size_t ec, rrc;
    int key_usage;
    unsigned char acceptor_flag;
    krb5_checksum sum;
    krb5_error_code err;
    krb5_boolean valid;
    krb5_keyblock *key;

    ASSERT(toktype != KG_TOK_SEAL_MSG || ctx->enc != 0);
    ASSERT(ctx->big_endian == 0);
    ASSERT(ctx->proto == 1);

    if (qop_state)
	*qop_state = GSS_C_QOP_DEFAULT;

    acceptor_flag = ctx->initiate ? FLAG_SENDER_IS_ACCEPTOR : 0;
    key_usage = (toktype == KG_TOK_WRAP_MSG
		 ? (!ctx->initiate
		    ? KG_USAGE_INITIATOR_SEAL
		    : KG_USAGE_ACCEPTOR_SEAL)
		 : (!ctx->initiate
		    ? KG_USAGE_INITIATOR_SIGN
		    : KG_USAGE_ACCEPTOR_SIGN));

    /* Oops.  I wrote this code assuming ptr would be at the start of
       the token header.  */
    ptr -= 2;
    bodysize += 2;

    if (bodysize < 16) {
    defective:
	*minor_status = 0;
	return GSS_S_DEFECTIVE_TOKEN;
    }
    if ((ptr[2] & FLAG_SENDER_IS_ACCEPTOR) != acceptor_flag) {
	*minor_status = (OM_uint32)G_BAD_DIRECTION;
	return GSS_S_BAD_SIG;
    }

    /* Two things to note here.

       First, we can't really enforce the use of the acceptor's subkey,
       if we're the acceptor; the initiator may have sent messages
       before getting the subkey.  We could probably enforce it if
       we're the initiator.

       Second, if someone tweaks the code to not set the flag telling
       the krb5 library to generate a new subkey in the AP-REP
       message, the MIT library may include a subkey anyways --
       namely, a copy of the AP-REQ subkey, if it was provided.  So
       the initiator may think we wanted a subkey, and set the flag,
       even though we weren't trying to set the subkey.  The "other"
       key, the one not asserted by the acceptor, will have the same
       value in that case, though, so we can just ignore the flag.  */
    if (ctx->have_acceptor_subkey && (ptr[2] & FLAG_ACCEPTOR_SUBKEY)) {
	key = ctx->acceptor_subkey;
    } else {
	key = ctx->enc;
    }

#ifdef _KERNEL
    context->kef_cipher_mt = get_cipher_mech_type(context, key);
    context->kef_hash_mt = get_hash_mech_type(context, key);

    if ((err = init_key_kef(context->kef_cipher_mt, key))) {
	return (GSS_S_FAILURE);
    }
#endif /* _KERNEL */

    if (toktype == KG_TOK_WRAP_MSG) {
	if (load_16_be(ptr) != 0x0504)
	    goto defective;
	if (ptr[3] != 0xff)
	    goto defective;
	ec = load_16_be(ptr+4);
	rrc = load_16_be(ptr+6);
	seqnum = load_64_be(ptr+8);
	if (!rotate_left(ptr+16, bodysize-16, rrc)) {
	no_mem:
	    *minor_status = ENOMEM;
	    return GSS_S_FAILURE;
	}
	if (ptr[2] & FLAG_WRAP_CONFIDENTIAL) {
	    /* confidentiality */
	    krb5_enc_data cipher;
	    unsigned char *althdr;
            size_t plainlen;

	    if (conf_state)
		*conf_state = 1;
	    /* Do we have no decrypt_size function?

	       For all current cryptosystems, the ciphertext size will
	       be larger than the plaintext size.  */
	    cipher.enctype = key->enctype;
	    cipher.ciphertext.length = bodysize - 16;
	    cipher.ciphertext.data = (char *)ptr + 16;
	    plain.length = plainlen = bodysize - 16;
	    plain.data = MALLOC(plain.length);
	    if (plain.data == NULL)
		goto no_mem;
	    err = krb5_c_decrypt(context, key, key_usage, 0,
				 &cipher, &plain);
	    if (err) {
		goto error;
	    }
	    /* Don't use bodysize here!  Use the fact that
	       plain.length has been adjusted to the
	       correct length.  */
	    althdr = (uchar_t *)plain.data + plain.length - 16;
	    if (load_16_be(althdr) != 0x0504
		|| althdr[2] != ptr[2]
		|| althdr[3] != ptr[3]
		|| memcmp(althdr+8, ptr+8, 8)) {
		FREE(plain.data, plainlen);
		goto defective;
	    }
	    message_buffer->length = plain.length - ec - 16;
	    message_buffer->value = MALLOC(message_buffer->length);
	    if (message_buffer->value == NULL) {
		FREE(plain.data, plainlen);
		goto no_mem;
	    }
	    (void) memcpy(message_buffer->value, plain.data,
			message_buffer->length);
	    FREE(plain.data, plainlen);
	} else {
	    /* no confidentiality */
	    if (conf_state)
		*conf_state = 0;
	    if (ec + 16 < ec)
		/* overflow check */
		goto defective;
	    if (ec + 16 > bodysize)
		goto defective;
	    /* We have: header | msg | cksum.
	       We need cksum(msg | header).
	       Rotate the first two.  */
	    store_16_be(0, ptr+4);
	    store_16_be(0, ptr+6);
	    plain.length = bodysize-ec;
	    plain.data = (char *)ptr;
	    if (!rotate_left(ptr, bodysize-ec, 16))
		goto no_mem;
	    sum.length = ec;
	    if (sum.length != ctx->cksum_size) {
		*minor_status = 0;
		return GSS_S_BAD_SIG;
	    }
	    sum.contents = ptr+bodysize-ec;
	    sum.checksum_type = ctx->cksumtype;
	    err = krb5_c_verify_checksum(context, key, key_usage,
					 &plain, &sum, &valid);
	    if (err) {
		*minor_status = err;
		return GSS_S_BAD_SIG;
	    }
	    if (!valid) {
		*minor_status = 0;
		return GSS_S_BAD_SIG;
	    }
	    message_buffer->length = plain.length - 16;
	    message_buffer->value = MALLOC(message_buffer->length);
	    if (message_buffer->value == NULL)
		goto no_mem;
	    (void) memcpy(message_buffer->value,
		plain.data, message_buffer->length);

		/*
		 * Solaris Kerberos: Restore the original token.
		 * This allows the token to be detected as a duplicate if it
		 * is passed in to gss_unwrap() again.
		 */
		if (!rotate_left(ptr, bodysize-ec, bodysize - ec - 16))
			goto no_mem;
		store_16_be(ec, ptr+4);
		store_16_be(rrc, ptr+6);
	}
	err = g_order_check(&ctx->seqstate, seqnum);
	*minor_status = 0;
	return err;
    } else if (toktype == KG_TOK_MIC_MSG) {
	/* wrap token, no confidentiality */
	if (load_16_be(ptr) != 0x0404)
	    goto defective;
    verify_mic_1:
	if (ptr[3] != 0xff)
	    goto defective;
	if (load_32_be(ptr+4) != (ulong_t)0xffffffffU)
	    goto defective;
	seqnum = load_64_be(ptr+8);
	plain.length = message_buffer->length + 16;
	plain.data = MALLOC(plain.length);
	if (plain.data == NULL)
	    goto no_mem;
	if (message_buffer->length)
	    (void) memcpy(plain.data,
		message_buffer->value, message_buffer->length);
	(void) memcpy(plain.data + message_buffer->length, ptr, 16);
	sum.length = bodysize - 16;
	sum.contents = ptr + 16;
	sum.checksum_type = ctx->cksumtype;
	err = krb5_c_verify_checksum(context, key, key_usage,
				     &plain, &sum, &valid);
	if (err) {
	error:
	    FREE(plain.data, plain.length);
	    *minor_status = err;
	    save_error_info(*minor_status, context);
	    return GSS_S_BAD_SIG; /* XXX */
	}
	FREE(plain.data, plain.length);
	if (!valid) {
	    *minor_status = 0;
	    return GSS_S_BAD_SIG;
	}
	err = g_order_check(&ctx->seqstate, seqnum);
	*minor_status = 0;
	return err;
    } else if (toktype == KG_TOK_DEL_CTX) {
	if (load_16_be(ptr) != 0x0405)
	    goto defective;
	message_buffer = (gss_buffer_t)&empty_message;
	goto verify_mic_1;
    } else {
	goto defective;
    }
}
Пример #19
0
 krb5_error_code smb_krb5_verify_checksum(krb5_context context,
					 krb5_keyblock *keyblock,
					 krb5_keyusage usage,
					 krb5_checksum *cksum,
					 uint8 *data,
					 size_t length)
{
	krb5_error_code ret;

	/* verify the checksum */

	/* welcome to the wonderful world of samba's kerberos abstraction layer:
	 * 
	 * function			heimdal 0.6.1rc3	heimdal 0.7	MIT krb 1.4.2
	 * -----------------------------------------------------------------------------
	 * krb5_c_verify_checksum	-			works		works
	 * krb5_verify_checksum		works (6 args)		works (6 args)	broken (7 args) 
	 */

#if defined(HAVE_KRB5_C_VERIFY_CHECKSUM)
	{
		krb5_boolean checksum_valid = False;
		krb5_data input;

		input.data = (char *)data;
		input.length = length;

		ret = krb5_c_verify_checksum(context, 
					     keyblock, 
					     usage,
					     &input, 
					     cksum,
					     &checksum_valid);
		if (ret) {
			DEBUG(3,("smb_krb5_verify_checksum: krb5_c_verify_checksum() failed: %s\n", 
				error_message(ret)));
			return ret;
		}

		if (!checksum_valid)
			ret = KRB5KRB_AP_ERR_BAD_INTEGRITY;
	}

#elif KRB5_VERIFY_CHECKSUM_ARGS == 6 && defined(HAVE_KRB5_CRYPTO_INIT) && defined(HAVE_KRB5_CRYPTO) && defined(HAVE_KRB5_CRYPTO_DESTROY)

	/* Warning: MIT's krb5_verify_checksum cannot be used as it will use a key
	 * without enctype and it ignores any key_usage types - Guenther */

	{

		krb5_crypto crypto;
		ret = krb5_crypto_init(context,
				       keyblock,
				       0,
				       &crypto);
		if (ret) {
			DEBUG(0,("smb_krb5_verify_checksum: krb5_crypto_init() failed: %s\n", 
				error_message(ret)));
			return ret;
		}

		ret = krb5_verify_checksum(context,
					   crypto,
					   usage,
					   data,
					   length,
					   cksum);

		krb5_crypto_destroy(context, crypto);
	}

#else
#error UNKNOWN_KRB5_VERIFY_CHECKSUM_FUNCTION
#endif

	return ret;
}