Path: blob/main/crypto/krb5/src/plugins/preauth/securid_sam2/grail.c
34889 views
/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */1/* plugins/preauth/securid_sam2/grail.c - Test method for SAM-2 preauth */2/*3* Copyright (C) 2012 by the Massachusetts Institute of Technology.4* All rights reserved.5*6* Redistribution and use in source and binary forms, with or without7* modification, are permitted provided that the following conditions8* are met:9*10* * Redistributions of source code must retain the above copyright11* notice, this list of conditions and the following disclaimer.12*13* * Redistributions in binary form must reproduce the above copyright14* notice, this list of conditions and the following disclaimer in15* the documentation and/or other materials provided with the16* distribution.17*18* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS19* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT20* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS21* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE22* COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,23* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES24* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR25* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)26* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,27* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)28* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED29* OF THE POSSIBILITY OF SUCH DAMAGE.30*/3132/*33* This test method exists to exercise the client SAM-2 code and some of the34* KDC SAM-2 code. We make up a weakly random number and presents it to the35* client in the prompt (in plain text), as well as encrypted in the track ID.36* To verify, we compare the decrypted track ID to the entered value.37*38* Do not use this method in production; it is not secure.39*/4041#ifdef GRAIL_PREAUTH4243#include "k5-int.h"44#include <kdb.h>45#include <adm_proto.h>46#include <ctype.h>47#include "extern.h"4849static krb5_error_code50get_grail_key(krb5_context context, krb5_db_entry *client,51krb5_keyblock *key_out)52{53krb5_db_entry *grail_entry = NULL;54krb5_key_data *kd;55int sam_type = PA_SAM_TYPE_GRAIL;56krb5_error_code ret = 0;5758ret = sam_get_db_entry(context, client->princ, &sam_type, &grail_entry);59if (ret)60return KRB5_PREAUTH_NO_KEY;61ret = krb5_dbe_find_enctype(context, grail_entry, -1, -1, -1, &kd);62if (ret)63goto cleanup;64ret = krb5_dbe_decrypt_key_data(context, NULL, kd, key_out, NULL);65if (ret)66goto cleanup;6768cleanup:69if (grail_entry)70krb5_db_free_principal(context, grail_entry);71return ret;72}7374static krb5_error_code75decrypt_track_data(krb5_context context, krb5_db_entry *client,76krb5_data *enc_track_data, krb5_data *output)77{78krb5_error_code ret;79krb5_keyblock sam_key;80krb5_enc_data enc;81krb5_data result = empty_data();8283sam_key.contents = NULL;84*output = empty_data();8586ret = get_grail_key(context, client, &sam_key);87if (ret != 0)88return ret;89enc.ciphertext = *enc_track_data;90enc.enctype = ENCTYPE_UNKNOWN;91enc.kvno = 0;92ret = alloc_data(&result, enc_track_data->length);93if (ret)94goto cleanup;95ret = krb5_c_decrypt(context, &sam_key,96KRB5_KEYUSAGE_PA_SAM_CHALLENGE_TRACKID, 0, &enc,97&result);98if (ret)99goto cleanup;100101*output = result;102result = empty_data();103104cleanup:105krb5_free_keyblock_contents(context, &sam_key);106krb5_free_data_contents(context, &result);107return ret;108}109110static krb5_error_code111encrypt_track_data(krb5_context context, krb5_db_entry *client,112krb5_data *track_data, krb5_data *output)113{114krb5_error_code ret;115size_t olen;116krb5_keyblock sam_key;117krb5_enc_data enc;118119*output = empty_data();120enc.ciphertext = empty_data();121sam_key.contents = NULL;122123ret = get_grail_key(context, client, &sam_key);124if (ret != 0)125return ret;126127ret = krb5_c_encrypt_length(context, sam_key.enctype,128track_data->length, &olen);129if (ret != 0)130goto cleanup;131assert(olen <= 65536);132ret = alloc_data(&enc.ciphertext, olen);133if (ret)134goto cleanup;135enc.enctype = sam_key.enctype;136enc.kvno = 0;137138ret = krb5_c_encrypt(context, &sam_key,139KRB5_KEYUSAGE_PA_SAM_CHALLENGE_TRACKID, 0,140track_data, &enc);141if (ret)142goto cleanup;143144*output = enc.ciphertext;145enc.ciphertext = empty_data();146147cleanup:148krb5_free_keyblock_contents(context, &sam_key);149krb5_free_data_contents(context, &enc.ciphertext);150return ret;151}152153krb5_error_code154get_grail_edata(krb5_context context, krb5_db_entry *client,155krb5_keyblock *client_key, krb5_sam_challenge_2 *sc2_out)156{157krb5_error_code ret;158krb5_data tmp_data, track_id = empty_data();159int tval = time(NULL) % 77777;160krb5_sam_challenge_2_body sc2b;161char tval_string[256], prompt[256];162163snprintf(tval_string, sizeof(tval_string), "%d", tval);164snprintf(prompt, sizeof(prompt), "Enter %d", tval);165166memset(&sc2b, 0, sizeof(sc2b));167sc2b.magic = KV5M_SAM_CHALLENGE_2;168sc2b.sam_track_id = empty_data();169sc2b.sam_flags = KRB5_SAM_SEND_ENCRYPTED_SAD;170sc2b.sam_type_name = empty_data();171sc2b.sam_challenge_label = empty_data();172sc2b.sam_challenge = empty_data();173sc2b.sam_response_prompt = string2data(prompt);174sc2b.sam_pk_for_sad = empty_data();175sc2b.sam_type = PA_SAM_TYPE_GRAIL;176sc2b.sam_etype = client_key->enctype;177178tmp_data = string2data(tval_string);179ret = encrypt_track_data(context, client, &tmp_data, &track_id);180if (ret)181goto cleanup;182sc2b.sam_track_id = track_id;183184tmp_data = make_data(&sc2b.sam_nonce, sizeof(sc2b.sam_nonce));185ret = krb5_c_random_make_octets(context, &tmp_data);186if (ret)187goto cleanup;188189ret = sam_make_challenge(context, &sc2b, client_key, sc2_out);190191cleanup:192krb5_free_data_contents(context, &track_id);193return ret;194}195196krb5_error_code197verify_grail_data(krb5_context context, krb5_db_entry *client,198krb5_sam_response_2 *sr2, krb5_enc_tkt_part *enc_tkt_reply,199krb5_pa_data *pa, krb5_sam_challenge_2 **sc2_out)200{201krb5_error_code ret;202krb5_key_data *client_key_data = NULL;203krb5_keyblock client_key;204krb5_data scratch = empty_data(), track_id_data = empty_data();205krb5_enc_sam_response_enc_2 *esre2 = NULL;206207*sc2_out = NULL;208memset(&client_key, 0, sizeof(client_key));209210if ((sr2->sam_enc_nonce_or_sad.ciphertext.data == NULL) ||211(sr2->sam_enc_nonce_or_sad.ciphertext.length <= 0))212return KRB5KDC_ERR_PREAUTH_FAILED;213214ret = krb5_dbe_find_enctype(context, client,215sr2->sam_enc_nonce_or_sad.enctype, -1,216sr2->sam_enc_nonce_or_sad.kvno,217&client_key_data);218if (ret)219goto cleanup;220221ret = krb5_dbe_decrypt_key_data(context, NULL, client_key_data,222&client_key, NULL);223if (ret)224goto cleanup;225ret = alloc_data(&scratch, sr2->sam_enc_nonce_or_sad.ciphertext.length);226if (ret)227goto cleanup;228ret = krb5_c_decrypt(context, &client_key, KRB5_KEYUSAGE_PA_SAM_RESPONSE,229NULL, &sr2->sam_enc_nonce_or_sad, &scratch);230if (ret)231goto cleanup;232233ret = decode_krb5_enc_sam_response_enc_2(&scratch, &esre2);234if (ret)235goto cleanup;236237if (sr2->sam_nonce != esre2->sam_nonce) {238ret = KRB5KDC_ERR_PREAUTH_FAILED;239goto cleanup;240}241242if (esre2->sam_sad.length == 0 || esre2->sam_sad.data == NULL) {243ret = KRB5KDC_ERR_PREAUTH_FAILED;244goto cleanup;245}246247ret = decrypt_track_data(context, client, &sr2->sam_track_id,248&track_id_data);249if (ret)250goto cleanup;251252/* Some enctypes aren't length-preserving; try to work anyway. */253while (track_id_data.length > 0 &&254!isdigit(track_id_data.data[track_id_data.length - 1]))255track_id_data.length--;256257if (!data_eq(track_id_data, esre2->sam_sad)) {258ret = KRB5KDC_ERR_PREAUTH_FAILED;259goto cleanup;260}261262enc_tkt_reply->flags |= (TKT_FLG_HW_AUTH | TKT_FLG_PRE_AUTH);263264cleanup:265krb5_free_keyblock_contents(context, &client_key);266krb5_free_data_contents(context, &scratch);267krb5_free_enc_sam_response_enc_2(context, esre2);268return ret;269}270271#endif /* GRAIL_PREAUTH */272273274