static OM_uint32 gsskrb5_acceptor_start(OM_uint32 * minor_status, gsskrb5_ctx ctx, krb5_context context, const gss_cred_id_t acceptor_cred_handle, const gss_buffer_t input_token_buffer, const gss_channel_bindings_t input_chan_bindings, gss_name_t * src_name, gss_OID * mech_type, gss_buffer_t output_token, OM_uint32 * ret_flags, OM_uint32 * time_rec, gss_cred_id_t * delegated_cred_handle) { krb5_error_code kret; OM_uint32 ret = GSS_S_COMPLETE; krb5_data indata; krb5_flags ap_options; krb5_keytab keytab = NULL; int is_cfx = 0; const gsskrb5_cred acceptor_cred = (gsskrb5_cred)acceptor_cred_handle; /* * We may, or may not, have an escapsulation. */ ret = _gsskrb5_decapsulate (minor_status, input_token_buffer, &indata, "\x01\x00", GSS_KRB5_MECHANISM); if (ret) { /* Assume that there is no OID wrapping. */ indata.length = input_token_buffer->length; indata.data = input_token_buffer->value; } /* * We need to get our keytab */ if (acceptor_cred == NULL) { if (_gsskrb5_keytab != NULL) keytab = _gsskrb5_keytab; } else if (acceptor_cred->keytab != NULL) { keytab = acceptor_cred->keytab; } /* * We need to check the ticket and create the AP-REP packet */ { krb5_rd_req_in_ctx in = NULL; krb5_rd_req_out_ctx out = NULL; krb5_principal server = NULL; if (acceptor_cred) server = acceptor_cred->principal; kret = krb5_rd_req_in_ctx_alloc(context, &in); if (kret == 0) kret = krb5_rd_req_in_set_keytab(context, in, keytab); if (kret) { if (in) krb5_rd_req_in_ctx_free(context, in); *minor_status = kret; return GSS_S_FAILURE; } kret = krb5_rd_req_ctx(context, &ctx->auth_context, &indata, server, in, &out); krb5_rd_req_in_ctx_free(context, in); if (kret == KRB5KRB_AP_ERR_SKEW || kret == KRB5KRB_AP_ERR_TKT_NYV) { /* * No reply in non-MUTUAL mode, but we don't know that its * non-MUTUAL mode yet, thats inside the 8003 checksum, so * lets only send the error token on clock skew, that * limit when send error token for non-MUTUAL. */ return send_error_token(minor_status, context, kret, server, &indata, output_token); } else if (kret) { *minor_status = kret; return GSS_S_FAILURE; } /* * we need to remember some data on the context_handle. */ kret = krb5_rd_req_out_get_ap_req_options(context, out, &ap_options); if (kret == 0) kret = krb5_rd_req_out_get_ticket(context, out, &ctx->ticket); if (kret == 0) kret = krb5_rd_req_out_get_keyblock(context, out, &ctx->service_keyblock); ctx->lifetime = ctx->ticket->ticket.endtime; krb5_rd_req_out_ctx_free(context, out); if (kret) { ret = GSS_S_FAILURE; *minor_status = kret; return ret; } } /* * We need to copy the principal names to the context and the * calling layer. */ kret = krb5_copy_principal(context, ctx->ticket->client, &ctx->source); if (kret) { ret = GSS_S_FAILURE; *minor_status = kret; } kret = krb5_copy_principal(context, ctx->ticket->server, &ctx->target); if (kret) { ret = GSS_S_FAILURE; *minor_status = kret; return ret; } /* * We need to setup some compat stuff, this assumes that * context_handle->target is already set. */ ret = _gss_DES3_get_mic_compat(minor_status, ctx, context); if (ret) return ret; if (src_name != NULL) { kret = krb5_copy_principal (context, ctx->ticket->client, (gsskrb5_name*)src_name); if (kret) { ret = GSS_S_FAILURE; *minor_status = kret; return ret; } } /* * We need to get the flags out of the 8003 checksum. */ { krb5_authenticator authenticator; kret = krb5_auth_con_getauthenticator(context, ctx->auth_context, &authenticator); if(kret) { ret = GSS_S_FAILURE; *minor_status = kret; return ret; } if (authenticator->cksum == NULL) { krb5_free_authenticator(context, &authenticator); *minor_status = 0; return GSS_S_BAD_BINDINGS; } if (authenticator->cksum->cksumtype == CKSUMTYPE_GSSAPI) { ret = _gsskrb5_verify_8003_checksum(minor_status, input_chan_bindings, authenticator->cksum, &ctx->flags, &ctx->fwd_data); krb5_free_authenticator(context, &authenticator); if (ret) { return ret; } } else { krb5_crypto crypto; kret = krb5_crypto_init(context, ctx->auth_context->keyblock, 0, &crypto); if(kret) { krb5_free_authenticator(context, &authenticator); ret = GSS_S_FAILURE; *minor_status = kret; return ret; } /* * Windows accepts Samba3's use of a kerberos, rather than * GSSAPI checksum here */ kret = krb5_verify_checksum(context, crypto, KRB5_KU_AP_REQ_AUTH_CKSUM, NULL, 0, authenticator->cksum); krb5_free_authenticator(context, &authenticator); krb5_crypto_destroy(context, crypto); if(kret) { ret = GSS_S_BAD_SIG; *minor_status = kret; return ret; } /* * Samba style get some flags (but not DCE-STYLE), use * ap_options to guess the mutual flag. */ ctx->flags = GSS_C_REPLAY_FLAG | GSS_C_SEQUENCE_FLAG; if (ap_options & AP_OPTS_MUTUAL_REQUIRED) ctx->flags |= GSS_C_MUTUAL_FLAG; } } if(ctx->flags & GSS_C_MUTUAL_FLAG) { krb5_data outbuf; int use_subkey = 0; _gsskrb5i_is_cfx(context, ctx, 1); is_cfx = (ctx->more_flags & IS_CFX); if (is_cfx || (ap_options & AP_OPTS_USE_SUBKEY)) { use_subkey = 1; } else { krb5_keyblock *rkey; /* * If there is a initiator subkey, copy that to acceptor * subkey to match Windows behavior */ kret = krb5_auth_con_getremotesubkey(context, ctx->auth_context, &rkey); if (kret == 0) { kret = krb5_auth_con_setlocalsubkey(context, ctx->auth_context, rkey); if (kret == 0) use_subkey = 1; krb5_free_keyblock(context, rkey); } } if (use_subkey) { ctx->more_flags |= ACCEPTOR_SUBKEY; krb5_auth_con_addflags(context, ctx->auth_context, KRB5_AUTH_CONTEXT_USE_SUBKEY, NULL); } kret = krb5_mk_rep(context, ctx->auth_context, &outbuf); if (kret) { *minor_status = kret; return GSS_S_FAILURE; } if (IS_DCE_STYLE(ctx)) { output_token->length = outbuf.length; output_token->value = outbuf.data; } else { ret = _gsskrb5_encapsulate(minor_status, &outbuf, output_token, "\x02\x00", GSS_KRB5_MECHANISM); krb5_data_free (&outbuf); if (ret) return ret; } } ctx->flags |= GSS_C_TRANS_FLAG; /* Remember the flags */ ctx->lifetime = ctx->ticket->ticket.endtime; ctx->more_flags |= OPEN; if (mech_type) *mech_type = GSS_KRB5_MECHANISM; if (time_rec) { ret = _gsskrb5_lifetime_left(minor_status, context, ctx->lifetime, time_rec); if (ret) { return ret; } } /* * When GSS_C_DCE_STYLE is in use, we need ask for a AP-REP from * the client. */ if (IS_DCE_STYLE(ctx)) { /* * Return flags to caller, but we haven't processed * delgations yet */ if (ret_flags) *ret_flags = (ctx->flags & ~GSS_C_DELEG_FLAG); ctx->state = ACCEPTOR_WAIT_FOR_DCESTYLE; return GSS_S_CONTINUE_NEEDED; } ret = gsskrb5_acceptor_ready(minor_status, ctx, context, delegated_cred_handle); if (ret_flags) *ret_flags = ctx->flags; return ret; }
/* * Construct a TGS request and return its ASN.1 encoding as well as the * timestamp, nonce, and subkey used. The pacb_fn callback allows the caller * to amend the request padata after the nonce and subkey are determined. */ krb5_error_code k5_make_tgs_req(krb5_context context, struct krb5int_fast_request_state *fast_state, krb5_creds *tgt, krb5_flags kdcoptions, krb5_address *const *addrs, krb5_pa_data **in_padata, krb5_creds *desired, k5_pacb_fn pacb_fn, void *pacb_data, krb5_data *req_asn1_out, krb5_timestamp *timestamp_out, krb5_int32 *nonce_out, krb5_keyblock **subkey_out) { krb5_error_code ret; krb5_kdc_req req; krb5_data *authdata_asn1 = NULL, *req_body_asn1 = NULL; krb5_data *ap_req_asn1 = NULL, *tgs_req_asn1 = NULL; krb5_ticket *sec_ticket = NULL; krb5_ticket *sec_ticket_arr[2]; krb5_timestamp time_now; krb5_pa_data **padata = NULL, *pa; krb5_keyblock *subkey = NULL; krb5_enc_data authdata_enc; krb5_enctype enctypes[2], *defenctypes = NULL; size_t count, i; *req_asn1_out = empty_data(); *timestamp_out = 0; *nonce_out = 0; *subkey_out = NULL; memset(&req, 0, sizeof(req)); memset(&authdata_enc, 0, sizeof(authdata_enc)); /* tgt's client principal must match the desired client principal. */ if (!krb5_principal_compare(context, tgt->client, desired->client)) return KRB5_PRINC_NOMATCH; /* tgt must be an actual credential, not a template. */ if (!tgt->ticket.length) return KRB5_NO_TKT_SUPPLIED; req.kdc_options = kdcoptions; req.server = desired->server; req.from = desired->times.starttime; req.till = desired->times.endtime ? desired->times.endtime : tgt->times.endtime; req.rtime = desired->times.renew_till; ret = krb5_timeofday(context, &time_now); if (ret) return ret; *nonce_out = req.nonce = (krb5_int32)time_now; *timestamp_out = time_now; req.addresses = (krb5_address **)addrs; /* Generate subkey. */ ret = krb5_generate_subkey(context, &tgt->keyblock, &subkey); if (ret) return ret; TRACE_SEND_TGS_SUBKEY(context, subkey); ret = krb5int_fast_tgs_armor(context, fast_state, subkey, &tgt->keyblock, NULL, NULL); if (ret) goto cleanup; if (desired->authdata != NULL) { ret = encode_krb5_authdata(desired->authdata, &authdata_asn1); if (ret) goto cleanup; ret = krb5_encrypt_helper(context, subkey, KRB5_KEYUSAGE_TGS_REQ_AD_SUBKEY, authdata_asn1, &authdata_enc); if (ret) goto cleanup; req.authorization_data = authdata_enc; } if (desired->keyblock.enctype != ENCTYPE_NULL) { if (!krb5_c_valid_enctype(desired->keyblock.enctype)) { ret = KRB5_PROG_ETYPE_NOSUPP; goto cleanup; } enctypes[0] = desired->keyblock.enctype; enctypes[1] = ENCTYPE_NULL; req.ktype = enctypes; req.nktypes = 1; } else { /* Get the default TGS enctypes. */ ret = krb5_get_tgs_ktypes(context, desired->server, &defenctypes); if (ret) goto cleanup; for (count = 0; defenctypes[count]; count++); req.ktype = defenctypes; req.nktypes = count; } TRACE_SEND_TGS_ETYPES(context, req.ktype); if (kdcoptions & (KDC_OPT_ENC_TKT_IN_SKEY | KDC_OPT_CNAME_IN_ADDL_TKT)) { if (desired->second_ticket.length == 0) { ret = KRB5_NO_2ND_TKT; goto cleanup; } ret = decode_krb5_ticket(&desired->second_ticket, &sec_ticket); if (ret) goto cleanup; sec_ticket_arr[0] = sec_ticket; sec_ticket_arr[1] = NULL; req.second_ticket = sec_ticket_arr; } /* Encode the request body. */ ret = krb5int_fast_prep_req_body(context, fast_state, &req, &req_body_asn1); if (ret) goto cleanup; ret = tgs_construct_ap_req(context, req_body_asn1, tgt, subkey, &ap_req_asn1); if (ret) goto cleanup; for (count = 0; in_padata != NULL && in_padata[count] != NULL; count++); /* Construct a padata array for the request, beginning with the ap-req. */ padata = k5calloc(count + 2, sizeof(krb5_pa_data *), &ret); if (padata == NULL) goto cleanup; padata[0] = k5alloc(sizeof(krb5_pa_data), &ret); if (padata[0] == NULL) goto cleanup; padata[0]->pa_type = KRB5_PADATA_AP_REQ; padata[0]->contents = k5memdup(ap_req_asn1->data, ap_req_asn1->length, &ret); if (padata[0] == NULL) goto cleanup; padata[0]->length = ap_req_asn1->length; /* Append copies of any other supplied padata. */ for (i = 0; in_padata != NULL && in_padata[i] != NULL; i++) { pa = k5alloc(sizeof(krb5_pa_data), &ret); if (pa == NULL) goto cleanup; pa->pa_type = in_padata[i]->pa_type; pa->length = in_padata[i]->length; pa->contents = k5memdup(in_padata[i]->contents, in_padata[i]->length, &ret); if (pa->contents == NULL) goto cleanup; padata[i + 1] = pa; } req.padata = padata; if (pacb_fn != NULL) { ret = (*pacb_fn)(context, subkey, &req, pacb_data); if (ret) goto cleanup; } /* Encode the TGS-REQ. Discard the krb5_data container. */ ret = krb5int_fast_prep_req(context, fast_state, &req, ap_req_asn1, encode_krb5_tgs_req, &tgs_req_asn1); if (ret) goto cleanup; *req_asn1_out = *tgs_req_asn1; free(tgs_req_asn1); tgs_req_asn1 = NULL; *subkey_out = subkey; subkey = NULL; cleanup: krb5_free_data(context, authdata_asn1); krb5_free_data(context, req_body_asn1); krb5_free_data(context, ap_req_asn1); krb5_free_pa_data(context, req.padata); krb5_free_ticket(context, sec_ticket); krb5_free_data_contents(context, &authdata_enc.ciphertext); krb5_free_keyblock(context, subkey); free(defenctypes); return ret; }
/* * Request: * NameZ * ServerPrincipalPresent * ServerPrincipal OPTIONAL * Key * * Repsonse: * */ static krb5_error_code kcm_op_get_initial_ticket(krb5_context context, kcm_client *client, kcm_operation opcode, krb5_storage *request, krb5_storage *response) { krb5_error_code ret; kcm_ccache ccache; char *name; int8_t not_tgt = 0; krb5_principal server = NULL; krb5_keyblock key; krb5_keyblock_zero(&key); ret = krb5_ret_stringz(request, &name); if (ret) return ret; KCM_LOG_REQUEST_NAME(context, client, opcode, name); ret = krb5_ret_int8(request, ¬_tgt); if (ret) { free(name); return ret; } if (not_tgt) { ret = krb5_ret_principal(request, &server); if (ret) { free(name); return ret; } } ret = krb5_ret_keyblock(request, &key); if (ret) { free(name); if (server != NULL) krb5_free_principal(context, server); return ret; } ret = kcm_ccache_resolve_client(context, client, opcode, name, &ccache); if (ret == 0) { HEIMDAL_MUTEX_lock(&ccache->mutex); if (ccache->server != NULL) { krb5_free_principal(context, ccache->server); ccache->server = NULL; } krb5_free_keyblock(context, &ccache->key.keyblock); ccache->server = server; ccache->key.keyblock = key; ccache->flags |= KCM_FLAGS_USE_CACHED_KEY; ret = kcm_ccache_enqueue_default(context, ccache, NULL); if (ret) { ccache->server = NULL; krb5_keyblock_zero(&ccache->key.keyblock); ccache->flags &= ~(KCM_FLAGS_USE_CACHED_KEY); } HEIMDAL_MUTEX_unlock(&ccache->mutex); } free(name); if (ret != 0) { krb5_free_principal(context, server); krb5_free_keyblock(context, &key); } kcm_release_ccache(context, ccache); return ret; }
int main(int argc, char *argv[]) { struct afsconf_dir *tdir; long code; const char *confdir; if (argc == 1) { fprintf(stderr, "%s: usage is '%s <opcode> options, e.g.\n", argv[0], argv[0]); fprintf(stderr, "\t%s add <kvno> <keyfile> <princ>\n", argv[0]); fprintf(stderr, "\tOR\n\t%s add <kvno> <key>\n", argv[0]); fprintf(stderr, "\t\tEx: %s add 0 \"80b6a7cd7a9dadb6\"\n", argv[0]); fprintf(stderr, "\t%s delete <kvno>\n", argv[0]); fprintf(stderr, "\t%s list\n", argv[0]); exit(1); } confdir = AFSDIR_SERVER_ETC_DIRPATH; tdir = afsconf_Open(confdir); if (!tdir) { fprintf(stderr, "%s: can't initialize conf dir '%s'\n", argv[0], confdir); exit(1); } if (strcmp(argv[1], "add")==0) { krb5_context context; krb5_principal principal; krb5_keyblock *key; krb5_error_code retval; int kvno, keymode = 0; if (argc != 5) { if (argc == 4) keymode = 1; else { fprintf(stderr, "%s add: usage is '%s add <kvno> <keyfile> " "<princ>\n", argv[0], argv[0]); fprintf(stderr, "\tOR\n\t%s add <kvno> <key>\n", argv[0]); fprintf(stderr, "\t\tEx: %s add 0 \"80b6a7cd7a9dadb6\"\n", argv[0]); exit(1); } } kvno = atoi(argv[2]); if (keymode) { char tkey[8]; int i; char *cp; if (strlen(argv[3]) != 16) { printf("key %s is not in right format\n", argv[3]); printf(" <key> should be an 8byte hex representation \n"); printf(" Ex: setkey add 0 \"80b6a7cd7a9dadb6\"\n"); exit(1); } memset(tkey, 0, sizeof(tkey)); for (i = 7, cp = argv[3] + 15; i >= 0; i--, cp -= 2) tkey[i] = char2hex(*cp) + char2hex(*(cp - 1)) * 16; code = afsconf_AddKey(tdir, kvno, tkey, 1); } else { krb5_init_context(&context); retval = krb5_parse_name(context, argv[4], &principal); if (retval != 0) { afs_com_err(argv[0], retval, "while parsing AFS principal"); exit(1); } retval = krb5_kt_read_service_key(context, argv[3], principal, kvno, ENCTYPE_DES_CBC_CRC, &key); if (retval == KRB5_KT_NOTFOUND) retval = krb5_kt_read_service_key(context, argv[3], principal, kvno, ENCTYPE_DES_CBC_MD5, &key); if (retval == KRB5_KT_NOTFOUND) retval = krb5_kt_read_service_key(context, argv[3], principal, kvno, ENCTYPE_DES_CBC_MD4, &key); if (retval == KRB5_KT_NOTFOUND) { char * princname = NULL; krb5_unparse_name(context, principal, &princname); afs_com_err(argv[0], retval, "for keytab entry with Principal %s, kvno %u, DES-CBC-CRC/MD5/MD4", princname ? princname : argv[4], kvno); exit(1); } else if (retval != 0) { afs_com_err(argv[0], retval, "while extracting AFS service key"); exit(1); } #ifdef USING_HEIMDAL #define deref_key_length(key) \ key->keyvalue.length #define deref_key_contents(key) \ key->keyvalue.data #else #define deref_key_length(key) \ key->length #define deref_key_contents(key) \ key->contents #endif if (deref_key_length(key) != 8) { fprintf(stderr, "Key length should be 8, but is really %u!\n", (unsigned int)deref_key_length(key)); exit(1); } code = afsconf_AddKey(tdir, kvno, (char *) deref_key_contents(key), 1); } if (code) { fprintf(stderr, "%s: failed to set key, code %ld.\n", argv[0], code); exit(1); } if (keymode == 0) { krb5_free_principal(context, principal); krb5_free_keyblock(context, key); } } else if (strcmp(argv[1], "delete")==0) { long kvno; if (argc != 3) { fprintf(stderr, "%s delete: usage is '%s delete <kvno>\n", argv[0], argv[0]); exit(1); } kvno = atoi(argv[2]); code = afsconf_DeleteKey(tdir, kvno); if (code) { fprintf(stderr, "%s: failed to delete key %ld, (code %ld)\n", argv[0], kvno, code); exit(1); } } else if (strcmp(argv[1], "list") == 0) { struct afsconf_keys tkeys; int i, j; code = afsconf_GetKeys(tdir, &tkeys); if (code) { fprintf(stderr, "%s: failed to get keys, code %ld\n", argv[0], code); exit(1); } for(i=0;i<tkeys.nkeys;i++) { if (tkeys.key[i].kvno != -1) { printf("kvno %4d: key is: ", tkeys.key[i].kvno); for (j = 0; j < 8; j++) printf("%02x", (unsigned char) tkeys.key[i].key[j]); printf("\n"); } } printf("All done.\n"); } else { fprintf(stderr, "%s: unknown operation '%s', type '%s' for " "assistance\n", argv[0], argv[1], argv[0]); exit(1); } exit(0); }
static krb5_error_code process_preauth(krb5_context context, void *plugin_context, void *request_context, krb5_get_init_creds_opt *opt, preauth_get_client_data_proc get_data_proc, struct _krb5_preauth_client_rock *rock, krb5_kdc_req *request, krb5_data *encoded_request_body, krb5_data *encoded_previous_request, krb5_pa_data *padata, krb5_prompter_fct prompter, void *prompter_data, preauth_get_as_key_proc gak_fct, void *gak_data, krb5_data *salt, krb5_data *s2kparams, krb5_keyblock *as_key, krb5_pa_data ***out_padata) { krb5_error_code retval = 0; krb5_enctype enctype = 0; krb5_keyblock *challenge_key = NULL, *armor_key = NULL; krb5_data *etype_data = NULL; krb5int_access kaccess; if (krb5int_accessor(&kaccess, KRB5INT_ACCESS_VERSION) != 0) return 0; retval = fast_get_armor_key(context, get_data_proc, rock, &armor_key); if (retval || armor_key == NULL) return 0; retval = get_data_proc(context, rock, krb5plugin_preauth_client_get_etype, &etype_data); if (retval == 0) { enctype = *((krb5_enctype *)etype_data->data); if (as_key->length == 0 ||as_key->enctype != enctype) retval = gak_fct(context, request->client, enctype, prompter, prompter_data, salt, s2kparams, as_key, gak_data); } if (retval == 0 && padata->length) { krb5_enc_data *enc = NULL; krb5_data scratch; scratch.length = padata->length; scratch.data = (char *) padata->contents; retval = krb5_c_fx_cf2_simple(context,armor_key, "kdcchallengearmor", as_key, "challengelongterm", &challenge_key); if (retval == 0) retval =kaccess.decode_enc_data(&scratch, &enc); scratch.data = NULL; if (retval == 0) { scratch.data = malloc(enc->ciphertext.length); scratch.length = enc->ciphertext.length; if (scratch.data == NULL) retval = ENOMEM; } if (retval == 0) retval = krb5_c_decrypt(context, challenge_key, KRB5_KEYUSAGE_ENC_CHALLENGE_KDC, NULL, enc, &scratch); /* * Per draft 11 of the preauth framework, the client MAY but is not * required to actually check the timestamp from the KDC other than to * confirm it decrypts. This code does not perform that check. */ if (scratch.data) krb5_free_data_contents(context, &scratch); if (retval == 0) fast_set_kdc_verified(context, get_data_proc, rock); if (enc) kaccess.free_enc_data(context, enc); } else if (retval == 0) { /*No padata; we send*/ krb5_enc_data enc; krb5_pa_data *pa = NULL; krb5_pa_data **pa_array = NULL; krb5_data *encoded_ts = NULL; krb5_pa_enc_ts ts; enc.ciphertext.data = NULL; retval = krb5_us_timeofday(context, &ts.patimestamp, &ts.pausec); if (retval == 0) retval = kaccess.encode_enc_ts(&ts, &encoded_ts); if (retval == 0) retval = krb5_c_fx_cf2_simple(context, armor_key, "clientchallengearmor", as_key, "challengelongterm", &challenge_key); if (retval == 0) retval = kaccess.encrypt_helper(context, challenge_key, KRB5_KEYUSAGE_ENC_CHALLENGE_CLIENT, encoded_ts, &enc); if (encoded_ts) krb5_free_data(context, encoded_ts); encoded_ts = NULL; if (retval == 0) { retval = kaccess.encode_enc_data(&enc, &encoded_ts); krb5_free_data_contents(context, &enc.ciphertext); } if (retval == 0) { pa = calloc(1, sizeof(krb5_pa_data)); if (pa == NULL) retval = ENOMEM; } if (retval == 0) { pa_array = calloc(2, sizeof(krb5_pa_data *)); if (pa_array == NULL) retval = ENOMEM; } if (retval == 0) { pa->length = encoded_ts->length; pa->contents = (unsigned char *) encoded_ts->data; pa->pa_type = KRB5_PADATA_ENCRYPTED_CHALLENGE; free(encoded_ts); encoded_ts = NULL; pa_array[0] = pa; pa = NULL; *out_padata = pa_array; pa_array = NULL; } if (pa) free(pa); if (encoded_ts) krb5_free_data(context, encoded_ts); if (pa_array) free(pa_array); } if (challenge_key) krb5_free_keyblock(context, challenge_key); if (armor_key) krb5_free_keyblock(context, armor_key); if (etype_data != NULL) get_data_proc(context, rock, krb5plugin_preauth_client_free_etype, &etype_data); return retval; }
static krb5_error_code make_seal_token_v1_iov(krb5_context context, krb5_gss_ctx_id_rec *ctx, int conf_req_flag, int *conf_state, gss_iov_buffer_desc *iov, int iov_count, int toktype) { krb5_error_code code = 0; gss_iov_buffer_t header; gss_iov_buffer_t padding; gss_iov_buffer_t trailer; krb5_checksum md5cksum; krb5_checksum cksum; size_t k5_headerlen = 0, k5_trailerlen = 0; size_t data_length = 0, assoc_data_length = 0; size_t tmsglen = 0, tlen; unsigned char *ptr; krb5_keyusage sign_usage = KG_USAGE_SIGN; assert(toktype == KG_TOK_WRAP_MSG); md5cksum.length = cksum.length = 0; md5cksum.contents = cksum.contents = NULL; header = kg_locate_iov(iov, iov_count, GSS_IOV_BUFFER_TYPE_HEADER); if (header == NULL) return EINVAL; padding = kg_locate_iov(iov, iov_count, GSS_IOV_BUFFER_TYPE_PADDING); if (padding == NULL && (ctx->gss_flags & GSS_C_DCE_STYLE) == 0) return EINVAL; trailer = kg_locate_iov(iov, iov_count, GSS_IOV_BUFFER_TYPE_TRAILER); if (trailer != NULL) trailer->buffer.length = 0; /* Determine confounder length */ if (toktype == KG_TOK_WRAP_MSG || conf_req_flag) k5_headerlen = kg_confounder_size(context, ctx->enc); /* Check padding length */ if (toktype == KG_TOK_WRAP_MSG) { size_t k5_padlen = (ctx->sealalg == SEAL_ALG_MICROSOFT_RC4) ? 1 : 8; size_t gss_padlen; size_t conf_data_length; kg_iov_msglen(iov, iov_count, &data_length, &assoc_data_length); conf_data_length = k5_headerlen + data_length - assoc_data_length; if (k5_padlen == 1) gss_padlen = 1; /* one byte to indicate one byte of padding */ else gss_padlen = k5_padlen - (conf_data_length % k5_padlen); if (ctx->gss_flags & GSS_C_DCE_STYLE) { /* DCE will pad the actual data itself; padding buffer optional and will be zeroed */ gss_padlen = 0; if (conf_data_length % k5_padlen) code = KRB5_BAD_MSIZE; } else if (padding->type & GSS_IOV_BUFFER_FLAG_ALLOCATE) { code = kg_allocate_iov(padding, gss_padlen); } else if (padding->buffer.length < gss_padlen) { code = KRB5_BAD_MSIZE; } if (code != 0) goto cleanup; /* Initialize padding buffer to pad itself */ if (padding != NULL) { padding->buffer.length = gss_padlen; memset(padding->buffer.value, (int)gss_padlen, gss_padlen); } if (ctx->gss_flags & GSS_C_DCE_STYLE) tmsglen = k5_headerlen; /* confounder length */ else tmsglen = conf_data_length + padding->buffer.length + assoc_data_length; } /* Determine token size */ tlen = g_token_size(ctx->mech_used, 14 + ctx->cksum_size + tmsglen); k5_headerlen += tlen - tmsglen; if (header->type & GSS_IOV_BUFFER_FLAG_ALLOCATE) code = kg_allocate_iov(header, k5_headerlen); else if (header->buffer.length < k5_headerlen) code = KRB5_BAD_MSIZE; if (code != 0) goto cleanup; header->buffer.length = k5_headerlen; ptr = (unsigned char *)header->buffer.value; g_make_token_header(ctx->mech_used, 14 + ctx->cksum_size + tmsglen, &ptr, toktype); /* 0..1 SIGN_ALG */ store_16_le(ctx->signalg, &ptr[0]); /* 2..3 SEAL_ALG or Filler */ if (toktype == KG_TOK_WRAP_MSG && conf_req_flag) { store_16_le(ctx->sealalg, &ptr[2]); } else { /* No seal */ ptr[2] = 0xFF; ptr[3] = 0xFF; } /* 4..5 Filler */ ptr[4] = 0xFF; ptr[5] = 0xFF; /* pad the plaintext, encrypt if needed, and stick it in the token */ /* initialize the checksum */ switch (ctx->signalg) { case SGN_ALG_DES_MAC_MD5: case SGN_ALG_MD2_5: md5cksum.checksum_type = CKSUMTYPE_RSA_MD5; break; case SGN_ALG_HMAC_SHA1_DES3_KD: md5cksum.checksum_type = CKSUMTYPE_HMAC_SHA1_DES3; break; case SGN_ALG_HMAC_MD5: md5cksum.checksum_type = CKSUMTYPE_HMAC_MD5_ARCFOUR; if (toktype != KG_TOK_WRAP_MSG) sign_usage = 15; break; default: case SGN_ALG_DES_MAC: abort (); } code = krb5_c_checksum_length(context, md5cksum.checksum_type, &k5_trailerlen); if (code != 0) goto cleanup; md5cksum.length = k5_trailerlen; if (k5_headerlen != 0) { code = kg_make_confounder(context, ctx->enc, ptr + 14 + ctx->cksum_size); if (code != 0) goto cleanup; } /* compute the checksum */ code = kg_make_checksum_iov_v1(context, md5cksum.checksum_type, ctx->cksum_size, ctx->seq, ctx->enc, sign_usage, iov, iov_count, toktype, &md5cksum); if (code != 0) goto cleanup; switch (ctx->signalg) { case SGN_ALG_DES_MAC_MD5: case SGN_ALG_3: code = kg_encrypt(context, ctx->seq, KG_USAGE_SEAL, (g_OID_equal(ctx->mech_used, gss_mech_krb5_old) ? ctx->seq->contents : NULL), md5cksum.contents, md5cksum.contents, 16); if (code != 0) goto cleanup; cksum.length = ctx->cksum_size; cksum.contents = md5cksum.contents + 16 - cksum.length; memcpy(ptr + 14, cksum.contents, cksum.length); break; case SGN_ALG_HMAC_SHA1_DES3_KD: assert(md5cksum.length == ctx->cksum_size); memcpy(ptr + 14, md5cksum.contents, md5cksum.length); break; case SGN_ALG_HMAC_MD5: memcpy(ptr + 14, md5cksum.contents, ctx->cksum_size); break; } /* create the seq_num */ code = kg_make_seq_num(context, ctx->seq, ctx->initiate ? 0 : 0xFF, (OM_uint32)ctx->seq_send, ptr + 14, ptr + 6); if (code != 0) goto cleanup; if (conf_req_flag) { if (ctx->sealalg == SEAL_ALG_MICROSOFT_RC4) { unsigned char bigend_seqnum[4]; krb5_keyblock *enc_key; size_t i; store_32_be(ctx->seq_send, bigend_seqnum); code = krb5_copy_keyblock(context, ctx->enc, &enc_key); if (code != 0) goto cleanup; assert(enc_key->length == 16); for (i = 0; i < enc_key->length; i++) ((char *)enc_key->contents)[i] ^= 0xF0; code = kg_arcfour_docrypt_iov(context, enc_key, 0, bigend_seqnum, 4, iov, iov_count); krb5_free_keyblock(context, enc_key); } else { code = kg_encrypt_iov(context, ctx->proto, ((ctx->gss_flags & GSS_C_DCE_STYLE) != 0), 0 /*EC*/, 0 /*RRC*/, ctx->enc, KG_USAGE_SEAL, NULL, iov, iov_count); } if (code != 0) goto cleanup; } ctx->seq_send++; ctx->seq_send &= 0xFFFFFFFFL; code = 0; if (conf_state != NULL) *conf_state = conf_req_flag; cleanup: if (code != 0) kg_release_iov(iov, iov_count); krb5_free_checksum_contents(context, &md5cksum); return code; }
krb5_error_code KRB5_LIB_FUNCTION krb5_verify_ap_req2(krb5_context context, krb5_auth_context *auth_context, krb5_ap_req *ap_req, krb5_const_principal server, krb5_keyblock *keyblock, krb5_flags flags, krb5_flags *ap_req_options, krb5_ticket **ticket, krb5_key_usage usage) { krb5_ticket *t; krb5_auth_context ac; krb5_error_code ret; EtypeList etypes; if (ticket) *ticket = NULL; if (auth_context && *auth_context) { ac = *auth_context; } else { ret = krb5_auth_con_init (context, &ac); if (ret) return ret; } t = calloc(1, sizeof(*t)); if (t == NULL) { ret = ENOMEM; krb5_clear_error_string (context); goto out; } if (ap_req->ap_options.use_session_key && ac->keyblock){ ret = krb5_decrypt_ticket(context, &ap_req->ticket, ac->keyblock, &t->ticket, flags); krb5_free_keyblock(context, ac->keyblock); ac->keyblock = NULL; }else ret = krb5_decrypt_ticket(context, &ap_req->ticket, keyblock, &t->ticket, flags); if(ret) goto out; ret = _krb5_principalname2krb5_principal(context, &t->server, ap_req->ticket.sname, ap_req->ticket.realm); if (ret) goto out; ret = _krb5_principalname2krb5_principal(context, &t->client, t->ticket.cname, t->ticket.crealm); if (ret) goto out; /* save key */ ret = krb5_copy_keyblock(context, &t->ticket.key, &ac->keyblock); if (ret) goto out; ret = decrypt_authenticator (context, &t->ticket.key, &ap_req->authenticator, ac->authenticator, usage); if (ret) goto out; { krb5_principal p1, p2; krb5_boolean res; _krb5_principalname2krb5_principal(context, &p1, ac->authenticator->cname, ac->authenticator->crealm); _krb5_principalname2krb5_principal(context, &p2, t->ticket.cname, t->ticket.crealm); res = krb5_principal_compare (context, p1, p2); krb5_free_principal (context, p1); krb5_free_principal (context, p2); if (!res) { ret = KRB5KRB_AP_ERR_BADMATCH; krb5_clear_error_string (context); goto out; } } /* check addresses */ if (t->ticket.caddr && ac->remote_address && !krb5_address_search (context, ac->remote_address, t->ticket.caddr)) { ret = KRB5KRB_AP_ERR_BADADDR; krb5_clear_error_string (context); goto out; } /* check timestamp in authenticator */ { krb5_timestamp now; krb5_timeofday (context, &now); if (abs(ac->authenticator->ctime - now) > context->max_skew) { ret = KRB5KRB_AP_ERR_SKEW; krb5_clear_error_string (context); goto out; } } if (ac->authenticator->seq_number) krb5_auth_con_setremoteseqnumber(context, ac, *ac->authenticator->seq_number); /* XXX - Xor sequence numbers */ if (ac->authenticator->subkey) { ret = krb5_auth_con_setremotesubkey(context, ac, ac->authenticator->subkey); if (ret) goto out; } ret = find_etypelist(context, ac, &etypes); if (ret) goto out; ac->keytype = ETYPE_NULL; if (etypes.val) { int i; for (i = 0; i < etypes.len; i++) { if (krb5_enctype_valid(context, etypes.val[i]) == 0) { ac->keytype = etypes.val[i]; break; } } } if (ap_req_options) { *ap_req_options = 0; if (ac->keytype != ETYPE_NULL) *ap_req_options |= AP_OPTS_USE_SUBKEY; if (ap_req->ap_options.use_session_key) *ap_req_options |= AP_OPTS_USE_SESSION_KEY; if (ap_req->ap_options.mutual_required) *ap_req_options |= AP_OPTS_MUTUAL_REQUIRED; } if(ticket) *ticket = t; else krb5_free_ticket (context, t); if (auth_context) { if (*auth_context == NULL) *auth_context = ac; } else krb5_auth_con_free (context, ac); free_EtypeList(&etypes); return 0; out: if (t) krb5_free_ticket (context, t); if (auth_context == NULL || *auth_context == NULL) krb5_auth_con_free (context, ac); return ret; }
static OM_uint32 inquire_sec_context_get_subkey (OM_uint32 *minor_status, const gsskrb5_ctx context_handle, krb5_context context, enum keytype keytype, gss_buffer_set_t *data_set) { krb5_keyblock *key = NULL; krb5_storage *sp = NULL; krb5_data data; OM_uint32 maj_stat = GSS_S_COMPLETE; krb5_error_code ret; krb5_data_zero(&data); sp = krb5_storage_emem(); if (sp == NULL) { _gsskrb5_clear_status(); ret = ENOMEM; goto out; } HEIMDAL_MUTEX_lock(&context_handle->ctx_id_mutex); switch(keytype) { case ACCEPTOR_KEY: ret = _gsskrb5i_get_acceptor_subkey(context_handle, context, &key); break; case INITIATOR_KEY: ret = _gsskrb5i_get_initiator_subkey(context_handle, context, &key); break; case TOKEN_KEY: ret = _gsskrb5i_get_token_key(context_handle, context, &key); break; default: _gsskrb5_set_status(EINVAL, "%d is not a valid subkey type", keytype); ret = EINVAL; break; } HEIMDAL_MUTEX_unlock(&context_handle->ctx_id_mutex); if (ret) goto out; if (key == NULL) { _gsskrb5_set_status(EINVAL, "have no subkey of type %d", keytype); ret = EINVAL; goto out; } ret = krb5_store_keyblock(sp, *key); krb5_free_keyblock (context, key); if (ret) goto out; ret = krb5_storage_to_data(sp, &data); if (ret) goto out; { gss_buffer_desc value; value.length = data.length; value.value = data.data; maj_stat = gss_add_buffer_set_member(minor_status, &value, data_set); } out: krb5_data_free(&data); if (sp) krb5_storage_free(sp); if (ret) { *minor_status = ret; maj_stat = GSS_S_FAILURE; } return maj_stat; }
static OM_uint32 export_lucid_sec_context_v1(OM_uint32 *minor_status, gsskrb5_ctx context_handle, krb5_context context, gss_buffer_set_t *data_set) { krb5_storage *sp = NULL; OM_uint32 major_status = GSS_S_COMPLETE; krb5_error_code ret; krb5_keyblock *key = NULL; int32_t number; int is_cfx; krb5_data data; *minor_status = 0; HEIMDAL_MUTEX_lock(&context_handle->ctx_id_mutex); _gsskrb5i_is_cfx(context_handle, &is_cfx); sp = krb5_storage_emem(); if (sp == NULL) { _gsskrb5_clear_status(); ret = ENOMEM; goto out; } ret = krb5_store_int32(sp, 1); if (ret) goto out; ret = krb5_store_int32(sp, (context_handle->more_flags & LOCAL) ? 1 : 0); if (ret) goto out; ret = krb5_store_int32(sp, context_handle->lifetime); if (ret) goto out; krb5_auth_con_getlocalseqnumber (context, context_handle->auth_context, &number); ret = krb5_store_uint32(sp, (uint32_t)0); /* store top half as zero */ if (ret) goto out; ret = krb5_store_uint32(sp, (uint32_t)number); if (ret) goto out; krb5_auth_getremoteseqnumber (context, context_handle->auth_context, &number); ret = krb5_store_uint32(sp, (uint32_t)0); /* store top half as zero */ if (ret) goto out; ret = krb5_store_uint32(sp, (uint32_t)number); if (ret) goto out; ret = krb5_store_int32(sp, (is_cfx) ? 1 : 0); if (ret) goto out; ret = _gsskrb5i_get_token_key(context_handle, context, &key); if (ret) goto out; if (is_cfx == 0) { int sign_alg, seal_alg; switch (key->keytype) { case ETYPE_DES_CBC_CRC: case ETYPE_DES_CBC_MD4: case ETYPE_DES_CBC_MD5: sign_alg = 0; seal_alg = 0; break; case ETYPE_DES3_CBC_MD5: case ETYPE_DES3_CBC_SHA1: sign_alg = 4; seal_alg = 2; break; case ETYPE_ARCFOUR_HMAC_MD5: case ETYPE_ARCFOUR_HMAC_MD5_56: sign_alg = 17; seal_alg = 16; break; default: sign_alg = -1; seal_alg = -1; break; } ret = krb5_store_int32(sp, sign_alg); if (ret) goto out; ret = krb5_store_int32(sp, seal_alg); if (ret) goto out; /* ctx_key */ ret = krb5_store_keyblock(sp, *key); if (ret) goto out; } else { int subkey_p = (context_handle->more_flags & ACCEPTOR_SUBKEY) ? 1 : 0; /* have_acceptor_subkey */ ret = krb5_store_int32(sp, subkey_p); if (ret) goto out; /* ctx_key */ ret = krb5_store_keyblock(sp, *key); if (ret) goto out; /* acceptor_subkey */ if (subkey_p) { ret = krb5_store_keyblock(sp, *key); if (ret) goto out; } } ret = krb5_storage_to_data(sp, &data); if (ret) goto out; { gss_buffer_desc ad_data; ad_data.value = data.data; ad_data.length = data.length; ret = gss_add_buffer_set_member(minor_status, &ad_data, data_set); krb5_data_free(&data); if (ret) goto out; } out: if (key) krb5_free_keyblock (context, key); if (sp) krb5_storage_free(sp); if (ret) { *minor_status = ret; major_status = GSS_S_FAILURE; } HEIMDAL_MUTEX_unlock(&context_handle->ctx_id_mutex); return major_status; }
static OM_uint32 gsskrb5_extract_key(OM_uint32 *minor_status, gss_ctx_id_t context_handle, const gss_OID oid, krb5_keyblock **keyblock) { krb5_error_code ret; gss_buffer_set_t data_set = GSS_C_NO_BUFFER_SET; OM_uint32 major_status; krb5_context context = NULL; krb5_storage *sp = NULL; if (context_handle == GSS_C_NO_CONTEXT) { ret = EINVAL; return GSS_S_FAILURE; } ret = krb5_init_context(&context); if(ret) { *minor_status = ret; return GSS_S_FAILURE; } major_status = gss_inquire_sec_context_by_oid (minor_status, context_handle, oid, &data_set); if (major_status) return major_status; if (data_set == GSS_C_NO_BUFFER_SET || data_set->count != 1) { gss_release_buffer_set(minor_status, &data_set); *minor_status = EINVAL; return GSS_S_FAILURE; } sp = krb5_storage_from_mem(data_set->elements[0].value, data_set->elements[0].length); if (sp == NULL) { ret = ENOMEM; goto out; } *keyblock = calloc(1, sizeof(**keyblock)); if (keyblock == NULL) { ret = ENOMEM; goto out; } ret = krb5_ret_keyblock(sp, *keyblock); out: gss_release_buffer_set(minor_status, &data_set); if (sp) krb5_storage_free(sp); if (ret && keyblock) { krb5_free_keyblock(context, *keyblock); *keyblock = NULL; } if (context) krb5_free_context(context); *minor_status = ret; if (ret) return GSS_S_FAILURE; return GSS_S_COMPLETE; }
int main () { krb5_context context = 0; krb5_data in, in2, out, out2, check, check2, state, signdata; krb5_crypto_iov iov[5]; int i, j, pos; unsigned int dummy; size_t len; krb5_enc_data enc_out, enc_out2; krb5_keyblock *keyblock; krb5_key key; memset(iov, 0, sizeof(iov)); in.data = "This is a test.\n"; in.length = strlen (in.data); in2.data = "This is another test.\n"; in2.length = strlen (in2.data); test ("Seeding random number generator", krb5_c_random_seed (context, &in)); /* Set up output buffers. */ out.data = malloc(2048); out2.data = malloc(2048); check.data = malloc(2048); check2.data = malloc(2048); if (out.data == NULL || out2.data == NULL || check.data == NULL || check2.data == NULL) abort(); out.magic = KV5M_DATA; out.length = 2048; out2.magic = KV5M_DATA; out2.length = 2048; check.length = 2048; check2.length = 2048; for (i = 0; interesting_enctypes[i]; i++) { krb5_enctype enctype = interesting_enctypes [i]; printf ("Testing enctype %d\n", enctype); test ("Initializing a keyblock", krb5_init_keyblock (context, enctype, 0, &keyblock)); test ("Generating random keyblock", krb5_c_make_random_key (context, enctype, keyblock)); test ("Creating opaque key from keyblock", krb5_k_create_key (context, keyblock, &key)); enc_out.ciphertext = out; enc_out2.ciphertext = out2; /* We use an intermediate `len' because size_t may be different size than `int' */ krb5_c_encrypt_length (context, keyblock->enctype, in.length, &len); enc_out.ciphertext.length = len; /* Encrypt, decrypt, and see if we got the plaintext back again. */ test ("Encrypting (c)", krb5_c_encrypt (context, keyblock, 7, 0, &in, &enc_out)); display ("Enc output", &enc_out.ciphertext); test ("Decrypting", krb5_c_decrypt (context, keyblock, 7, 0, &enc_out, &check)); test ("Comparing", compare_results (&in, &check)); /* Try again with the opaque-key-using variants. */ memset(out.data, 0, out.length); test ("Encrypting (k)", krb5_k_encrypt (context, key, 7, 0, &in, &enc_out)); display ("Enc output", &enc_out.ciphertext); test ("Decrypting", krb5_k_decrypt (context, key, 7, 0, &enc_out, &check)); test ("Comparing", compare_results (&in, &check)); /* Check if this enctype supports IOV encryption. */ if ( krb5_c_crypto_length(context, keyblock->enctype, KRB5_CRYPTO_TYPE_HEADER, &dummy) == 0 ){ /* Set up iovecs for stream decryption. */ memcpy(out2.data, enc_out.ciphertext.data, enc_out.ciphertext.length); iov[0].flags= KRB5_CRYPTO_TYPE_STREAM; iov[0].data.data = out2.data; iov[0].data.length = enc_out.ciphertext.length; iov[1].flags = KRB5_CRYPTO_TYPE_DATA; /* Decrypt the encrypted data from above and check it. */ test("IOV stream decrypting (c)", krb5_c_decrypt_iov( context, keyblock, 7, 0, iov, 2)); test("Comparing results", compare_results(&in, &iov[1].data)); /* Try again with the opaque-key-using variant. */ memcpy(out2.data, enc_out.ciphertext.data, enc_out.ciphertext.length); test("IOV stream decrypting (k)", krb5_k_decrypt_iov( context, key, 7, 0, iov, 2)); test("Comparing results", compare_results(&in, &iov[1].data)); /* Set up iovecs for AEAD encryption. */ signdata.magic = KV5M_DATA; signdata.data = (char *) "This should be signed"; signdata.length = strlen(signdata.data); iov[0].flags = KRB5_CRYPTO_TYPE_HEADER; iov[1].flags = KRB5_CRYPTO_TYPE_DATA; iov[1].data = in; /*We'll need to copy memory before encrypt*/ iov[2].flags = KRB5_CRYPTO_TYPE_SIGN_ONLY; iov[2].data = signdata; iov[3].flags = KRB5_CRYPTO_TYPE_PADDING; iov[4].flags = KRB5_CRYPTO_TYPE_TRAILER; /* "Allocate" data for the iovec buffers from the "out" buffer. */ test("Setting up iov lengths", krb5_c_crypto_length_iov(context, keyblock->enctype, iov, 5)); for (j=0,pos=0; j <= 4; j++ ){ if (iov[j].flags == KRB5_CRYPTO_TYPE_SIGN_ONLY) continue; iov[j].data.data = &out.data[pos]; pos += iov[j].data.length; } assert (iov[1].data.length == in.length); memcpy(iov[1].data.data, in.data, in.length); /* Encrypt and decrypt in place, and check the result. */ test("iov encrypting (c)", krb5_c_encrypt_iov(context, keyblock, 7, 0, iov, 5)); assert(iov[1].data.length == in.length); display("Header", &iov[0].data); display("Data", &iov[1].data); display("Padding", &iov[3].data); display("Trailer", &iov[4].data); test("iov decrypting", krb5_c_decrypt_iov(context, keyblock, 7, 0, iov, 5)); test("Comparing results", compare_results(&in, &iov[1].data)); /* Try again with opaque-key-using variants. */ test("iov encrypting (k)", krb5_k_encrypt_iov(context, key, 7, 0, iov, 5)); assert(iov[1].data.length == in.length); display("Header", &iov[0].data); display("Data", &iov[1].data); display("Padding", &iov[3].data); display("Trailer", &iov[4].data); test("iov decrypting", krb5_k_decrypt_iov(context, key, 7, 0, iov, 5)); test("Comparing results", compare_results(&in, &iov[1].data)); } enc_out.ciphertext.length = out.length; check.length = 2048; test ("init_state", krb5_c_init_state (context, keyblock, 7, &state)); test ("Encrypting with state", krb5_c_encrypt (context, keyblock, 7, &state, &in, &enc_out)); display ("Enc output", &enc_out.ciphertext); test ("Encrypting again with state", krb5_c_encrypt (context, keyblock, 7, &state, &in2, &enc_out2)); display ("Enc output", &enc_out2.ciphertext); test ("free_state", krb5_c_free_state (context, keyblock, &state)); test ("init_state", krb5_c_init_state (context, keyblock, 7, &state)); test ("Decrypting with state", krb5_c_decrypt (context, keyblock, 7, &state, &enc_out, &check)); test ("Decrypting again with state", krb5_c_decrypt (context, keyblock, 7, &state, &enc_out2, &check2)); test ("free_state", krb5_c_free_state (context, keyblock, &state)); test ("Comparing", compare_results (&in, &check)); test ("Comparing", compare_results (&in2, &check2)); krb5_free_keyblock (context, keyblock); krb5_k_free_key (context, key); } /* Test the RC4 decrypt fallback from key usage 9 to 8. */ test ("Initializing an RC4 keyblock", krb5_init_keyblock (context, ENCTYPE_ARCFOUR_HMAC, 0, &keyblock)); test ("Generating random RC4 key", krb5_c_make_random_key (context, ENCTYPE_ARCFOUR_HMAC, keyblock)); enc_out.ciphertext = out; krb5_c_encrypt_length (context, keyblock->enctype, in.length, &len); enc_out.ciphertext.length = len; check.length = 2048; test ("Encrypting with RC4 key usage 8", krb5_c_encrypt (context, keyblock, 8, 0, &in, &enc_out)); display ("Enc output", &enc_out.ciphertext); test ("Decrypting with RC4 key usage 9", krb5_c_decrypt (context, keyblock, 9, 0, &enc_out, &check)); test ("Comparing", compare_results (&in, &check)); krb5_free_keyblock (context, keyblock); free(out.data); free(out2.data); free(check.data); free(check2.data); return 0; }
static krb5_error_code init_tgs_req (krb5_context context, krb5_ccache ccache, krb5_addresses *addresses, krb5_kdc_flags flags, Ticket *second_ticket, krb5_creds *in_creds, krb5_creds *krbtgt, unsigned nonce, krb5_keyblock **subkey, TGS_REQ *t, krb5_key_usage usage) { krb5_error_code ret = 0; memset(t, 0, sizeof(*t)); t->pvno = 5; t->msg_type = krb_tgs_req; if (in_creds->session.keytype) { ALLOC_SEQ(&t->req_body.etype, 1); if(t->req_body.etype.val == NULL) { ret = ENOMEM; krb5_set_error_string(context, "malloc: out of memory"); goto fail; } t->req_body.etype.val[0] = in_creds->session.keytype; } else { ret = krb5_init_etype(context, &t->req_body.etype.len, &t->req_body.etype.val, NULL); } if (ret) goto fail; t->req_body.addresses = addresses; t->req_body.kdc_options = flags.b; ret = copy_Realm(&in_creds->server->realm, &t->req_body.realm); if (ret) goto fail; ALLOC(t->req_body.sname, 1); if (t->req_body.sname == NULL) { ret = ENOMEM; krb5_set_error_string(context, "malloc: out of memory"); goto fail; } /* some versions of some code might require that the client be present in TGS-REQs, but this is clearly against the spec */ ret = copy_PrincipalName(&in_creds->server->name, t->req_body.sname); if (ret) goto fail; /* req_body.till should be NULL if there is no endtime specified, but old MIT code (like DCE secd) doesn't like that */ ALLOC(t->req_body.till, 1); if(t->req_body.till == NULL){ ret = ENOMEM; krb5_set_error_string(context, "malloc: out of memory"); goto fail; } *t->req_body.till = in_creds->times.endtime; t->req_body.nonce = nonce; if(second_ticket){ ALLOC(t->req_body.additional_tickets, 1); if (t->req_body.additional_tickets == NULL) { ret = ENOMEM; krb5_set_error_string(context, "malloc: out of memory"); goto fail; } ALLOC_SEQ(t->req_body.additional_tickets, 1); if (t->req_body.additional_tickets->val == NULL) { ret = ENOMEM; krb5_set_error_string(context, "malloc: out of memory"); goto fail; } ret = copy_Ticket(second_ticket, t->req_body.additional_tickets->val); if (ret) goto fail; } ALLOC(t->padata, 1); if (t->padata == NULL) { ret = ENOMEM; krb5_set_error_string(context, "malloc: out of memory"); goto fail; } ALLOC_SEQ(t->padata, 1); if (t->padata->val == NULL) { ret = ENOMEM; krb5_set_error_string(context, "malloc: out of memory"); goto fail; } { krb5_auth_context ac; krb5_keyblock *key = NULL; ret = krb5_auth_con_init(context, &ac); if(ret) goto fail; if (krb5_config_get_bool_default(context, NULL, FALSE, "realms", krbtgt->server->realm, "tgs_require_subkey", NULL)) { ret = krb5_generate_subkey (context, &krbtgt->session, &key); if (ret) { krb5_auth_con_free (context, ac); goto fail; } ret = krb5_auth_con_setlocalsubkey(context, ac, key); if (ret) { if (key) krb5_free_keyblock (context, key); krb5_auth_con_free (context, ac); goto fail; } } ret = set_auth_data (context, &t->req_body, &in_creds->authdata, key ? key : &krbtgt->session); if (ret) { if (key) krb5_free_keyblock (context, key); krb5_auth_con_free (context, ac); goto fail; } ret = make_pa_tgs_req(context, ac, &t->req_body, t->padata->val, krbtgt, usage); if(ret) { if (key) krb5_free_keyblock (context, key); krb5_auth_con_free(context, ac); goto fail; } *subkey = key; krb5_auth_con_free(context, ac); } fail: if (ret) { t->req_body.addresses = NULL; free_TGS_REQ (t); } return ret; }
/*ARGSUSED*/ krb5_error_code process_tgs_req(krb5_data *pkt, const krb5_fulladdr *from, krb5_data **response) { krb5_keyblock * subkey = 0; krb5_kdc_req *request = 0; krb5_db_entry server; krb5_kdc_rep reply; krb5_enc_kdc_rep_part reply_encpart; krb5_ticket ticket_reply, *header_ticket = 0; int st_idx = 0; krb5_enc_tkt_part enc_tkt_reply; krb5_transited enc_tkt_transited; int newtransited = 0; krb5_error_code retval = 0; int nprincs = 0; krb5_boolean more; krb5_timestamp kdc_time, authtime=0; krb5_keyblock session_key; krb5_timestamp until, rtime; krb5_keyblock encrypting_key; krb5_key_data *server_key; char *cname = 0, *sname = 0, *tmp = 0; const char *fromstring = 0; krb5_last_req_entry *nolrarray[2], nolrentry; /* krb5_address *noaddrarray[1]; */ krb5_enctype useenctype; int errcode, errcode2; register int i; int firstpass = 1; const char *status = 0; char ktypestr[128]; char rep_etypestr[128]; char fromstringbuf[70]; session_key.contents = 0; retval = decode_krb5_tgs_req(pkt, &request); if (retval) return retval; if (request->msg_type != KRB5_TGS_REQ) return KRB5_BADMSGTYPE; ktypes2str(ktypestr, sizeof(ktypestr), request->nktypes, request->ktype); /* * setup_server_realm() sets up the global realm-specific data pointer. */ if ((retval = setup_server_realm(request->server))) { krb5_free_kdc_req(kdc_context, request); return retval; } fromstring = inet_ntop(ADDRTYPE2FAMILY(from->address->addrtype), from->address->contents, fromstringbuf, sizeof(fromstringbuf)); if (!fromstring) fromstring = "<unknown>"; if ((errcode = krb5_unparse_name(kdc_context, request->server, &sname))) { status = "UNPARSING SERVER"; goto cleanup; } limit_string(sname); /* errcode = kdc_process_tgs_req(request, from, pkt, &req_authdat); */ errcode = kdc_process_tgs_req(request, from, pkt, &header_ticket, &subkey); if (header_ticket && header_ticket->enc_part2 && (errcode2 = krb5_unparse_name(kdc_context, header_ticket->enc_part2->client, &cname))) { status = "UNPARSING CLIENT"; errcode = errcode2; goto cleanup; } limit_string(cname); if (errcode) { status = "PROCESS_TGS"; goto cleanup; } if (!header_ticket) { errcode = KRB5_NO_TKT_SUPPLIED; /* XXX? */ status="UNEXPECTED NULL in header_ticket"; goto cleanup; } /* * We've already dealt with the AP_REQ authentication, so we can * use header_ticket freely. The encrypted part (if any) has been * decrypted with the session key. */ authtime = header_ticket->enc_part2->times.authtime; /* XXX make sure server here has the proper realm...taken from AP_REQ header? */ nprincs = 1; if ((errcode = get_principal(kdc_context, request->server, &server, &nprincs, &more))) { status = "LOOKING_UP_SERVER"; nprincs = 0; goto cleanup; } tgt_again: if (more) { status = "NON_UNIQUE_PRINCIPAL"; errcode = KRB5KDC_ERR_PRINCIPAL_NOT_UNIQUE; goto cleanup; } else if (nprincs != 1) { /* * might be a request for a TGT for some other realm; we * should do our best to find such a TGS in this db */ if (firstpass && krb5_is_tgs_principal(request->server) == TRUE) { if (krb5_princ_size(kdc_context, request->server) == 2) { krb5_data *server_1 = krb5_princ_component(kdc_context, request->server, 1); krb5_data *tgs_1 = krb5_princ_component(kdc_context, tgs_server, 1); if (!tgs_1 || !data_eq(*server_1, *tgs_1)) { krb5_db_free_principal(kdc_context, &server, nprincs); find_alternate_tgs(request, &server, &more, &nprincs); firstpass = 0; goto tgt_again; } } } krb5_db_free_principal(kdc_context, &server, nprincs); status = "UNKNOWN_SERVER"; errcode = KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN; goto cleanup; } if ((errcode = krb5_timeofday(kdc_context, &kdc_time))) { status = "TIME_OF_DAY"; goto cleanup; } if ((retval = validate_tgs_request(request, server, header_ticket, kdc_time, &status))) { if (!status) status = "UNKNOWN_REASON"; errcode = retval + ERROR_TABLE_BASE_krb5; goto cleanup; } /* * We pick the session keytype here.... * * Some special care needs to be taken in the user-to-user * case, since we don't know what keytypes the application server * which is doing user-to-user authentication can support. We * know that it at least must be able to support the encryption * type of the session key in the TGT, since otherwise it won't be * able to decrypt the U2U ticket! So we use that in preference * to anything else. */ useenctype = 0; if (isflagset(request->kdc_options, KDC_OPT_ENC_TKT_IN_SKEY)) { krb5_keyblock * st_sealing_key; krb5_kvno st_srv_kvno; krb5_enctype etype; /* * Get the key for the second ticket, and decrypt it. */ if ((errcode = kdc_get_server_key(request->second_ticket[st_idx], &st_sealing_key, &st_srv_kvno))) { status = "2ND_TKT_SERVER"; goto cleanup; } errcode = krb5_decrypt_tkt_part(kdc_context, st_sealing_key, request->second_ticket[st_idx]); krb5_free_keyblock(kdc_context, st_sealing_key); if (errcode) { status = "2ND_TKT_DECRYPT"; goto cleanup; } etype = request->second_ticket[st_idx]->enc_part2->session->enctype; if (!krb5_c_valid_enctype(etype)) { status = "BAD_ETYPE_IN_2ND_TKT"; errcode = KRB5KDC_ERR_ETYPE_NOSUPP; goto cleanup; } for (i = 0; i < request->nktypes; i++) { if (request->ktype[i] == etype) { useenctype = etype; break; } } } /* * Select the keytype for the ticket session key. */ if ((useenctype == 0) && (useenctype = select_session_keytype(kdc_context, &server, request->nktypes, request->ktype)) == 0) { /* unsupported ktype */ status = "BAD_ENCRYPTION_TYPE"; errcode = KRB5KDC_ERR_ETYPE_NOSUPP; goto cleanup; } errcode = krb5_c_make_random_key(kdc_context, useenctype, &session_key); if (errcode) { /* random key failed */ status = "RANDOM_KEY_FAILED"; goto cleanup; } ticket_reply.server = request->server; /* XXX careful for realm... */ enc_tkt_reply.flags = 0; enc_tkt_reply.times.starttime = 0; /* * Fix header_ticket's starttime; if it's zero, fill in the * authtime's value. */ if (!(header_ticket->enc_part2->times.starttime)) header_ticket->enc_part2->times.starttime = header_ticket->enc_part2->times.authtime; /* don't use new addresses unless forwarded, see below */ enc_tkt_reply.caddrs = header_ticket->enc_part2->caddrs; /* noaddrarray[0] = 0; */ reply_encpart.caddrs = 0; /* optional...don't put it in */ /* It should be noted that local policy may affect the */ /* processing of any of these flags. For example, some */ /* realms may refuse to issue renewable tickets */ if (isflagset(request->kdc_options, KDC_OPT_FORWARDABLE)) setflag(enc_tkt_reply.flags, TKT_FLG_FORWARDABLE); if (isflagset(request->kdc_options, KDC_OPT_FORWARDED)) { setflag(enc_tkt_reply.flags, TKT_FLG_FORWARDED); /* include new addresses in ticket & reply */ enc_tkt_reply.caddrs = request->addresses; reply_encpart.caddrs = request->addresses; } if (isflagset(header_ticket->enc_part2->flags, TKT_FLG_FORWARDED)) setflag(enc_tkt_reply.flags, TKT_FLG_FORWARDED); if (isflagset(request->kdc_options, KDC_OPT_PROXIABLE)) setflag(enc_tkt_reply.flags, TKT_FLG_PROXIABLE); if (isflagset(request->kdc_options, KDC_OPT_PROXY)) { setflag(enc_tkt_reply.flags, TKT_FLG_PROXY); /* include new addresses in ticket & reply */ enc_tkt_reply.caddrs = request->addresses; reply_encpart.caddrs = request->addresses; } if (isflagset(request->kdc_options, KDC_OPT_ALLOW_POSTDATE)) setflag(enc_tkt_reply.flags, TKT_FLG_MAY_POSTDATE); if (isflagset(request->kdc_options, KDC_OPT_POSTDATED)) { setflag(enc_tkt_reply.flags, TKT_FLG_POSTDATED); setflag(enc_tkt_reply.flags, TKT_FLG_INVALID); enc_tkt_reply.times.starttime = request->from; } else enc_tkt_reply.times.starttime = kdc_time; if (isflagset(request->kdc_options, KDC_OPT_VALIDATE)) { /* BEWARE of allocation hanging off of ticket & enc_part2, it belongs to the caller */ ticket_reply = *(header_ticket); enc_tkt_reply = *(header_ticket->enc_part2); enc_tkt_reply.authorization_data = NULL; clear(enc_tkt_reply.flags, TKT_FLG_INVALID); } if (isflagset(request->kdc_options, KDC_OPT_RENEW)) { krb5_deltat old_life; /* BEWARE of allocation hanging off of ticket & enc_part2, it belongs to the caller */ ticket_reply = *(header_ticket); enc_tkt_reply = *(header_ticket->enc_part2); enc_tkt_reply.authorization_data = NULL; old_life = enc_tkt_reply.times.endtime - enc_tkt_reply.times.starttime; enc_tkt_reply.times.starttime = kdc_time; enc_tkt_reply.times.endtime = min(header_ticket->enc_part2->times.renew_till, kdc_time + old_life); } else { /* not a renew request */ enc_tkt_reply.times.starttime = kdc_time; until = (request->till == 0) ? kdc_infinity : request->till; enc_tkt_reply.times.endtime = min(until, min(enc_tkt_reply.times.starttime + server.max_life, min(enc_tkt_reply.times.starttime + max_life_for_realm, header_ticket->enc_part2->times.endtime))); if (isflagset(request->kdc_options, KDC_OPT_RENEWABLE_OK) && (enc_tkt_reply.times.endtime < request->till) && isflagset(header_ticket->enc_part2->flags, TKT_FLG_RENEWABLE)) { setflag(request->kdc_options, KDC_OPT_RENEWABLE); request->rtime = min(request->till, header_ticket->enc_part2->times.renew_till); } } rtime = (request->rtime == 0) ? kdc_infinity : request->rtime; if (isflagset(request->kdc_options, KDC_OPT_RENEWABLE)) { /* already checked above in policy check to reject request for a renewable ticket using a non-renewable ticket */ setflag(enc_tkt_reply.flags, TKT_FLG_RENEWABLE); enc_tkt_reply.times.renew_till = min(rtime, min(header_ticket->enc_part2->times.renew_till, enc_tkt_reply.times.starttime + min(server.max_renewable_life, max_renewable_life_for_realm))); } else { enc_tkt_reply.times.renew_till = 0; } /* * Set authtime to be the same as header_ticket's */ enc_tkt_reply.times.authtime = header_ticket->enc_part2->times.authtime; /* * Propagate the preauthentication flags through to the returned ticket. */ if (isflagset(header_ticket->enc_part2->flags, TKT_FLG_PRE_AUTH)) setflag(enc_tkt_reply.flags, TKT_FLG_PRE_AUTH); if (isflagset(header_ticket->enc_part2->flags, TKT_FLG_HW_AUTH)) setflag(enc_tkt_reply.flags, TKT_FLG_HW_AUTH); /* starttime is optional, and treated as authtime if not present. so we can nuke it if it matches */ if (enc_tkt_reply.times.starttime == enc_tkt_reply.times.authtime) enc_tkt_reply.times.starttime = 0; /* assemble any authorization data */ if (request->authorization_data.ciphertext.data) { krb5_data scratch; scratch.length = request->authorization_data.ciphertext.length; if (!(scratch.data = malloc(request->authorization_data.ciphertext.length))) { status = "AUTH_NOMEM"; errcode = ENOMEM; goto cleanup; } if ((errcode = krb5_c_decrypt(kdc_context, header_ticket->enc_part2->session, KRB5_KEYUSAGE_TGS_REQ_AD_SESSKEY, 0, &request->authorization_data, &scratch))) { status = "AUTH_ENCRYPT_FAIL"; free(scratch.data); goto cleanup; } /* scratch now has the authorization data, so we decode it */ errcode = decode_krb5_authdata(&scratch, &(request->unenc_authdata)); free(scratch.data); if (errcode) { status = "AUTH_DECODE"; goto cleanup; } if ((errcode = concat_authorization_data(request->unenc_authdata, header_ticket->enc_part2->authorization_data, &enc_tkt_reply.authorization_data))) { status = "CONCAT_AUTH"; goto cleanup; } } else enc_tkt_reply.authorization_data = header_ticket->enc_part2->authorization_data; enc_tkt_reply.session = &session_key; enc_tkt_reply.client = header_ticket->enc_part2->client; enc_tkt_reply.transited.tr_type = KRB5_DOMAIN_X500_COMPRESS; enc_tkt_reply.transited.tr_contents = empty_string; /* equivalent of "" */ /* * Only add the realm of the presented tgt to the transited list if * it is different than the local realm (cross-realm) and it is different * than the realm of the client (since the realm of the client is already * implicitly part of the transited list and should not be explicitly * listed). */ /* realm compare is like strcmp, but knows how to deal with these args */ if (realm_compare(header_ticket->server, tgs_server) || realm_compare(header_ticket->server, enc_tkt_reply.client)) { /* tgt issued by local realm or issued by realm of client */ enc_tkt_reply.transited = header_ticket->enc_part2->transited; } else { /* tgt issued by some other realm and not the realm of the client */ /* assemble new transited field into allocated storage */ if (header_ticket->enc_part2->transited.tr_type != KRB5_DOMAIN_X500_COMPRESS) { status = "BAD_TRTYPE"; errcode = KRB5KDC_ERR_TRTYPE_NOSUPP; goto cleanup; } enc_tkt_transited.tr_type = KRB5_DOMAIN_X500_COMPRESS; enc_tkt_transited.magic = 0; enc_tkt_transited.tr_contents.magic = 0; enc_tkt_transited.tr_contents.data = 0; enc_tkt_transited.tr_contents.length = 0; enc_tkt_reply.transited = enc_tkt_transited; if ((errcode = add_to_transited(&header_ticket->enc_part2->transited.tr_contents, &enc_tkt_reply.transited.tr_contents, header_ticket->server, enc_tkt_reply.client, request->server))) { status = "ADD_TR_FAIL"; goto cleanup; } newtransited = 1; } if (!isflagset (request->kdc_options, KDC_OPT_DISABLE_TRANSITED_CHECK)) { unsigned int tlen; char *tdots; errcode = krb5_check_transited_list (kdc_context, &enc_tkt_reply.transited.tr_contents, krb5_princ_realm (kdc_context, header_ticket->enc_part2->client), krb5_princ_realm (kdc_context, request->server)); tlen = enc_tkt_reply.transited.tr_contents.length; tdots = tlen > 125 ? "..." : ""; tlen = tlen > 125 ? 125 : tlen; if (errcode == 0) { setflag (enc_tkt_reply.flags, TKT_FLG_TRANSIT_POLICY_CHECKED); } else if (errcode == KRB5KRB_AP_ERR_ILL_CR_TKT) krb5_klog_syslog (LOG_INFO, "bad realm transit path from '%s' to '%s' " "via '%.*s%s'", cname ? cname : "<unknown client>", sname ? sname : "<unknown server>", tlen, enc_tkt_reply.transited.tr_contents.data, tdots); else { const char *emsg = krb5_get_error_message(kdc_context, errcode); krb5_klog_syslog (LOG_ERR, "unexpected error checking transit from " "'%s' to '%s' via '%.*s%s': %s", cname ? cname : "<unknown client>", sname ? sname : "<unknown server>", tlen, enc_tkt_reply.transited.tr_contents.data, tdots, emsg); krb5_free_error_message(kdc_context, emsg); } } else krb5_klog_syslog (LOG_INFO, "not checking transit path"); if (reject_bad_transit && !isflagset (enc_tkt_reply.flags, TKT_FLG_TRANSIT_POLICY_CHECKED)) { errcode = KRB5KDC_ERR_POLICY; status = "BAD_TRANSIT"; goto cleanup; } ticket_reply.enc_part2 = &enc_tkt_reply; /* * If we are doing user-to-user authentication, then make sure * that the client for the second ticket matches the request * server, and then encrypt the ticket using the session key of * the second ticket. */ if (isflagset(request->kdc_options, KDC_OPT_ENC_TKT_IN_SKEY)) { /* * Make sure the client for the second ticket matches * requested server. */ krb5_enc_tkt_part *t2enc = request->second_ticket[st_idx]->enc_part2; krb5_principal client2 = t2enc->client; if (!krb5_principal_compare(kdc_context, request->server, client2)) { if ((errcode = krb5_unparse_name(kdc_context, client2, &tmp))) tmp = 0; if (tmp != NULL) limit_string(tmp); krb5_klog_syslog(LOG_INFO, "TGS_REQ %s: 2ND_TKT_MISMATCH: " "authtime %d, %s for %s, 2nd tkt client %s", fromstring, authtime, cname ? cname : "<unknown client>", sname ? sname : "<unknown server>", tmp ? tmp : "<unknown>"); errcode = KRB5KDC_ERR_SERVER_NOMATCH; goto cleanup; } ticket_reply.enc_part.kvno = 0; ticket_reply.enc_part.enctype = t2enc->session->enctype; if ((errcode = krb5_encrypt_tkt_part(kdc_context, t2enc->session, &ticket_reply))) { status = "2ND_TKT_ENCRYPT"; goto cleanup; } st_idx++; } else { /* * Find the server key */ if ((errcode = krb5_dbe_find_enctype(kdc_context, &server, -1, /* ignore keytype */ -1, /* Ignore salttype */ 0, /* Get highest kvno */ &server_key))) { status = "FINDING_SERVER_KEY"; goto cleanup; } /* convert server.key into a real key (it may be encrypted * in the database) */ if ((errcode = krb5_dbekd_decrypt_key_data(kdc_context, &master_keyblock, server_key, &encrypting_key, NULL))) { status = "DECRYPT_SERVER_KEY"; goto cleanup; } errcode = krb5_encrypt_tkt_part(kdc_context, &encrypting_key, &ticket_reply); krb5_free_keyblock_contents(kdc_context, &encrypting_key); if (errcode) { status = "TKT_ENCRYPT"; goto cleanup; } ticket_reply.enc_part.kvno = server_key->key_data_kvno; } /* Start assembling the response */ reply.msg_type = KRB5_TGS_REP; reply.padata = 0; /* always */ reply.client = header_ticket->enc_part2->client; reply.enc_part.kvno = 0; /* We are using the session key */ reply.ticket = &ticket_reply; reply_encpart.session = &session_key; reply_encpart.nonce = request->nonce; /* copy the time fields EXCEPT for authtime; its location is used for ktime */ reply_encpart.times = enc_tkt_reply.times; reply_encpart.times.authtime = header_ticket->enc_part2->times.authtime; /* starttime is optional, and treated as authtime if not present. so we can nuke it if it matches */ if (enc_tkt_reply.times.starttime == enc_tkt_reply.times.authtime) enc_tkt_reply.times.starttime = 0; nolrentry.lr_type = KRB5_LRQ_NONE; nolrentry.value = 0; nolrarray[0] = &nolrentry; nolrarray[1] = 0; reply_encpart.last_req = nolrarray; /* not available for TGS reqs */ reply_encpart.key_exp = 0; /* ditto */ reply_encpart.flags = enc_tkt_reply.flags; reply_encpart.server = ticket_reply.server; /* use the session key in the ticket, unless there's a subsession key in the AP_REQ */ reply.enc_part.enctype = subkey ? subkey->enctype : header_ticket->enc_part2->session->enctype; errcode = krb5_encode_kdc_rep(kdc_context, KRB5_TGS_REP, &reply_encpart, subkey ? 1 : 0, subkey ? subkey : header_ticket->enc_part2->session, &reply, response); if (errcode) { status = "ENCODE_KDC_REP"; } else { status = "ISSUE"; } memset(ticket_reply.enc_part.ciphertext.data, 0, ticket_reply.enc_part.ciphertext.length); free(ticket_reply.enc_part.ciphertext.data); /* these parts are left on as a courtesy from krb5_encode_kdc_rep so we can use them in raw form if needed. But, we don't... */ memset(reply.enc_part.ciphertext.data, 0, reply.enc_part.ciphertext.length); free(reply.enc_part.ciphertext.data); cleanup: if (status) { const char * emsg = NULL; if (!errcode) rep_etypes2str(rep_etypestr, sizeof(rep_etypestr), &reply); if (errcode) emsg = krb5_get_error_message (kdc_context, errcode); krb5_klog_syslog(LOG_INFO, "TGS_REQ (%s) %s: %s: authtime %d, " "%s%s %s for %s%s%s", ktypestr, fromstring, status, authtime, !errcode ? rep_etypestr : "", !errcode ? "," : "", cname ? cname : "<unknown client>", sname ? sname : "<unknown server>", errcode ? ", " : "", errcode ? emsg : ""); if (errcode) krb5_free_error_message (kdc_context, emsg); } if (errcode) { int got_err = 0; if (status == 0) { status = krb5_get_error_message (kdc_context, errcode); got_err = 1; } errcode -= ERROR_TABLE_BASE_krb5; if (errcode < 0 || errcode > 128) errcode = KRB_ERR_GENERIC; retval = prepare_error_tgs(request, header_ticket, errcode, fromstring, response, status); if (got_err) { krb5_free_error_message (kdc_context, status); status = 0; } } if (header_ticket) krb5_free_ticket(kdc_context, header_ticket); if (request) krb5_free_kdc_req(kdc_context, request); if (cname) free(cname); if (sname) free(sname); if (nprincs) krb5_db_free_principal(kdc_context, &server, 1); if (session_key.contents) krb5_free_keyblock_contents(kdc_context, &session_key); if (newtransited) free(enc_tkt_reply.transited.tr_contents.data); if (subkey) krb5_free_keyblock(kdc_context, subkey); return retval; }
/* returns 0 for failure, 1 for success */ static int k5support_verify_tgt(krb5_context context, krb5_ccache ccache) { krb5_principal server; krb5_data packet; krb5_keyblock *keyblock = NULL; krb5_auth_context auth_context = NULL; krb5_error_code k5_retcode; krb5_keytab kt = NULL; char thishost[BUFSIZ]; int result = 0; memset(&packet, 0, sizeof(packet)); if ((k5_retcode = krb5_sname_to_principal(context, NULL, verify_principal, KRB5_NT_SRV_HST, &server))) { k5support_log_err(context, k5_retcode, "krb5_sname_to_principal()"); return 0; } if (keytabname) { if ((k5_retcode = krb5_kt_resolve(context, keytabname, &kt))) { k5support_log_err(context, k5_retcode, "krb5_kt_resolve()"); goto fini; } } if ((k5_retcode = krb5_kt_read_service_key(context, kt, server, 0, 0, &keyblock))) { k5support_log_err(context, k5_retcode, "krb5_kt_read_service_key()"); goto fini; } if (keyblock) { krb5_free_keyblock(context, keyblock); } /* this duplicates work done in krb5_sname_to_principal * oh well. */ if (gethostname(thishost, BUFSIZ) < 0) { goto fini; } thishost[BUFSIZ-1] = '\0'; if ((k5_retcode = krb5_mk_req(context, &auth_context, 0, verify_principal, thishost, NULL, ccache, &packet))) { k5support_log_err(context, k5_retcode, "krb5_mk_req()"); } if (auth_context) { krb5_auth_con_free(context, auth_context); auth_context = NULL; } if (k5_retcode) { goto fini; } if ((k5_retcode = krb5_rd_req(context, &auth_context, &packet, server, NULL, NULL, NULL))) { k5support_log_err(context, k5_retcode, "krb5_rd_req()"); goto fini; } if (auth_context) { krb5_auth_con_free(context, auth_context); auth_context = NULL; } /* all is good now */ result = 1; fini: if (!k5_retcode) { krb5_free_data_contents(context, &packet); } krb5_free_principal(context, server); return result; }
NTSTATUS ads_verify_ticket(TALLOC_CTX *mem_ctx, const char *realm, time_t time_offset, const DATA_BLOB *ticket, char **principal, struct PAC_DATA **pac_data, DATA_BLOB *ap_rep, DATA_BLOB *session_key, bool use_replay_cache) { NTSTATUS sret = NT_STATUS_LOGON_FAILURE; NTSTATUS pac_ret; DATA_BLOB auth_data; krb5_context context = NULL; krb5_auth_context auth_context = NULL; krb5_data packet; krb5_ticket *tkt = NULL; krb5_rcache rcache = NULL; krb5_keyblock *keyblock = NULL; time_t authtime; krb5_error_code ret = 0; int flags = 0; krb5_principal host_princ = NULL; krb5_const_principal client_principal = NULL; char *host_princ_s = NULL; bool auth_ok = False; bool got_auth_data = False; struct named_mutex *mutex = NULL; ZERO_STRUCT(packet); ZERO_STRUCT(auth_data); *principal = NULL; *pac_data = NULL; *ap_rep = data_blob_null; *session_key = data_blob_null; initialize_krb5_error_table(); ret = krb5_init_context(&context); if (ret) { DEBUG(1,("ads_verify_ticket: krb5_init_context failed (%s)\n", error_message(ret))); return NT_STATUS_LOGON_FAILURE; } if (time_offset != 0) { krb5_set_real_time(context, time(NULL) + time_offset, 0); } ret = krb5_set_default_realm(context, realm); if (ret) { DEBUG(1,("ads_verify_ticket: krb5_set_default_realm failed (%s)\n", error_message(ret))); goto out; } /* This whole process is far more complex than I would like. We have to go through all this to allow us to store the secret internally, instead of using /etc/krb5.keytab */ ret = krb5_auth_con_init(context, &auth_context); if (ret) { DEBUG(1,("ads_verify_ticket: krb5_auth_con_init failed (%s)\n", error_message(ret))); goto out; } krb5_auth_con_getflags( context, auth_context, &flags ); if ( !use_replay_cache ) { /* Disable default use of a replay cache */ flags &= ~KRB5_AUTH_CONTEXT_DO_TIME; krb5_auth_con_setflags( context, auth_context, flags ); } if (asprintf(&host_princ_s, "%s$", global_myname()) == -1) { goto out; } strlower_m(host_princ_s); ret = smb_krb5_parse_name(context, host_princ_s, &host_princ); if (ret) { DEBUG(1,("ads_verify_ticket: smb_krb5_parse_name(%s) failed (%s)\n", host_princ_s, error_message(ret))); goto out; } if ( use_replay_cache ) { /* Lock a mutex surrounding the replay as there is no locking in the MIT krb5 code surrounding the replay cache... */ mutex = grab_named_mutex(talloc_tos(), "replay cache mutex", 10); if (mutex == NULL) { DEBUG(1,("ads_verify_ticket: unable to protect " "replay cache with mutex.\n")); ret = KRB5_CC_IO; goto out; } /* JRA. We must set the rcache here. This will prevent replay attacks. */ ret = krb5_get_server_rcache(context, krb5_princ_component(context, host_princ, 0), &rcache); if (ret) { DEBUG(1,("ads_verify_ticket: krb5_get_server_rcache " "failed (%s)\n", error_message(ret))); goto out; } ret = krb5_auth_con_setrcache(context, auth_context, rcache); if (ret) { DEBUG(1,("ads_verify_ticket: krb5_auth_con_setrcache " "failed (%s)\n", error_message(ret))); goto out; } } /* Try secrets.tdb first and fallback to the krb5.keytab if necessary */ auth_ok = ads_secrets_verify_ticket(context, auth_context, host_princ, ticket, &tkt, &keyblock, &ret); if (!auth_ok && (ret == KRB5KRB_AP_ERR_TKT_NYV || ret == KRB5KRB_AP_ERR_TKT_EXPIRED || ret == KRB5KRB_AP_ERR_SKEW)) { goto auth_failed; } if (!auth_ok && lp_use_kerberos_keytab()) { auth_ok = ads_keytab_verify_ticket(context, auth_context, ticket, &tkt, &keyblock, &ret); } if ( use_replay_cache ) { TALLOC_FREE(mutex); #if 0 /* Heimdal leaks here, if we fix the leak, MIT crashes */ if (rcache) { krb5_rc_close(context, rcache); } #endif } auth_failed: if (!auth_ok) { DEBUG(3,("ads_verify_ticket: krb5_rd_req with auth failed (%s)\n", error_message(ret))); /* Try map the error return in case it's something like * a clock skew error. */ sret = krb5_to_nt_status(ret); if (NT_STATUS_IS_OK(sret) || NT_STATUS_EQUAL(sret,NT_STATUS_UNSUCCESSFUL)) { sret = NT_STATUS_LOGON_FAILURE; } DEBUG(10,("ads_verify_ticket: returning error %s\n", nt_errstr(sret) )); goto out; } authtime = get_authtime_from_tkt(tkt); client_principal = get_principal_from_tkt(tkt); ret = krb5_mk_rep(context, auth_context, &packet); if (ret) { DEBUG(3,("ads_verify_ticket: Failed to generate mutual authentication reply (%s)\n", error_message(ret))); goto out; } *ap_rep = data_blob(packet.data, packet.length); if (packet.data) { kerberos_free_data_contents(context, &packet); ZERO_STRUCT(packet); } get_krb5_smb_session_key(context, auth_context, session_key, True); dump_data_pw("SMB session key (from ticket)\n", session_key->data, session_key->length); #if 0 file_save("/tmp/ticket.dat", ticket->data, ticket->length); #endif /* continue when no PAC is retrieved or we couldn't decode the PAC (like accounts that have the UF_NO_AUTH_DATA_REQUIRED flag set, or Kerberos tickets encrypted using a DES key) - Guenther */ got_auth_data = get_auth_data_from_tkt(mem_ctx, &auth_data, tkt); if (!got_auth_data) { DEBUG(3,("ads_verify_ticket: did not retrieve auth data. continuing without PAC\n")); } if (got_auth_data) { pac_ret = decode_pac_data(mem_ctx, &auth_data, context, keyblock, client_principal, authtime, pac_data); if (!NT_STATUS_IS_OK(pac_ret)) { DEBUG(3,("ads_verify_ticket: failed to decode PAC_DATA: %s\n", nt_errstr(pac_ret))); *pac_data = NULL; } data_blob_free(&auth_data); } #if 0 #if defined(HAVE_KRB5_TKT_ENC_PART2) /* MIT */ if (tkt->enc_part2) { file_save("/tmp/authdata.dat", tkt->enc_part2->authorization_data[0]->contents, tkt->enc_part2->authorization_data[0]->length); } #else /* Heimdal */ if (tkt->ticket.authorization_data) { file_save("/tmp/authdata.dat", tkt->ticket.authorization_data->val->ad_data.data, tkt->ticket.authorization_data->val->ad_data.length); } #endif #endif if ((ret = smb_krb5_unparse_name(context, client_principal, principal))) { DEBUG(3,("ads_verify_ticket: smb_krb5_unparse_name failed (%s)\n", error_message(ret))); sret = NT_STATUS_LOGON_FAILURE; goto out; } sret = NT_STATUS_OK; out: TALLOC_FREE(mutex); if (!NT_STATUS_IS_OK(sret)) { data_blob_free(&auth_data); } if (!NT_STATUS_IS_OK(sret)) { data_blob_free(ap_rep); } if (host_princ) { krb5_free_principal(context, host_princ); } if (keyblock) { krb5_free_keyblock(context, keyblock); } if (tkt != NULL) { krb5_free_ticket(context, tkt); } SAFE_FREE(host_princ_s); if (auth_context) { krb5_auth_con_free(context, auth_context); } if (context) { krb5_free_context(context); } return sret; }
/* Create the response for a client. */ static krb5_error_code server_return(krb5_context kcontext, krb5_pa_data *padata, struct _krb5_db_entry_new *client, krb5_data *req_pkt, krb5_kdc_req *request, krb5_kdc_rep *reply, struct _krb5_key_data *client_key, krb5_keyblock *encrypting_key, krb5_pa_data **send_pa, preauth_get_entry_data_proc server_get_entry_data, void *pa_module_context, void **pa_request_context) { /* This module does a couple of dumb things. It tags its reply with * the same type as the initial challenge (expecting the client to sort * out whether there's anything useful in there). Oh, and it replaces * the AS reply key with one which is sent in the clear. */ krb5_keyblock *kb; krb5_int32 enctype; int i; *send_pa = NULL; /* We'll want a key with the first supported enctype. */ for (i = 0; i < request->nktypes; i++) { kb = NULL; if (krb5_init_keyblock(kcontext, request->ktype[i], 0, &kb) == 0) { break; } } if (i >= request->nktypes) { /* No matching cipher type found. */ return 0; } /* Randomize a key and save it for the client. */ if (krb5_c_make_random_key(kcontext, request->ktype[i], kb) != 0) { krb5_free_keyblock(kcontext, kb); return 0; } #ifdef DEBUG fprintf(stderr, "Generated random key, type=%d, length=%d.\n", kb->enctype, kb->length); #endif *send_pa = malloc(sizeof(krb5_pa_data)); if (*send_pa == NULL) { krb5_free_keyblock(kcontext, kb); return ENOMEM; } (*send_pa)->pa_type = KRB5_PADATA_WPSE_REQ; (*send_pa)->length = 4 + kb->length; (*send_pa)->contents = malloc(4 + kb->length); if ((*send_pa)->contents == NULL) { free(*send_pa); *send_pa = NULL; krb5_free_keyblock(kcontext, kb); return ENOMEM; } /* Store the preauth data. */ enctype = htonl(kb->enctype); memcpy((*send_pa)->contents, &enctype, 4); memcpy((*send_pa)->contents + 4, kb->contents, kb->length); krb5_free_keyblock_contents(kcontext, encrypting_key); krb5_copy_keyblock_contents(kcontext, kb, encrypting_key); /* Clean up. */ krb5_free_keyblock(kcontext, kb); return 0; }
krb5_error_code _krb5_mk_req_internal(krb5_context context, krb5_auth_context *auth_context, const krb5_flags ap_req_options, krb5_data *in_data, krb5_creds *in_creds, krb5_data *outbuf, krb5_key_usage checksum_usage, krb5_key_usage encrypt_usage) { krb5_error_code ret; krb5_data authenticator; Checksum c; Checksum *c_opt; krb5_auth_context ac; if(auth_context) { if(*auth_context == NULL) ret = krb5_auth_con_init(context, auth_context); else ret = 0; ac = *auth_context; } else ret = krb5_auth_con_init(context, &ac); if(ret) return ret; if(ac->local_subkey == NULL && (ap_req_options & AP_OPTS_USE_SUBKEY)) { ret = krb5_auth_con_generatelocalsubkey(context, ac, &in_creds->session); if(ret) goto out; } krb5_free_keyblock(context, ac->keyblock); ret = krb5_copy_keyblock(context, &in_creds->session, &ac->keyblock); if (ret) goto out; /* it's unclear what type of checksum we can use. try the best one, except: * a) if it's configured differently for the current realm, or * b) if the session key is des-cbc-crc */ if (in_data) { if(ac->keyblock->keytype == ETYPE_DES_CBC_CRC) { /* this is to make DCE secd (and older MIT kdcs?) happy */ ret = krb5_create_checksum(context, NULL, 0, CKSUMTYPE_RSA_MD4, in_data->data, in_data->length, &c); } else if(ac->keyblock->keytype == ETYPE_ARCFOUR_HMAC_MD5 || ac->keyblock->keytype == ETYPE_ARCFOUR_HMAC_MD5_56 || ac->keyblock->keytype == ETYPE_DES_CBC_MD4 || ac->keyblock->keytype == ETYPE_DES_CBC_MD5) { /* this is to make MS kdc happy */ ret = krb5_create_checksum(context, NULL, 0, CKSUMTYPE_RSA_MD5, in_data->data, in_data->length, &c); } else { krb5_crypto crypto; ret = krb5_crypto_init(context, ac->keyblock, 0, &crypto); if (ret) goto out; ret = krb5_create_checksum(context, crypto, checksum_usage, 0, in_data->data, in_data->length, &c); krb5_crypto_destroy(context, crypto); } c_opt = &c; } else { c_opt = NULL; } if (ret) goto out; ret = _krb5_build_authenticator(context, ac, ac->keyblock->keytype, in_creds, c_opt, &authenticator, encrypt_usage); if (c_opt) free_Checksum (c_opt); if (ret) goto out; ret = krb5_build_ap_req (context, ac->keyblock->keytype, in_creds, ap_req_options, authenticator, outbuf); out: if(auth_context == NULL) krb5_auth_con_free(context, ac); return ret; }
static krb5_error_code client_process(krb5_context kcontext, void *plugin_context, void *request_context, krb5_get_init_creds_opt *opt, preauth_get_client_data_proc client_get_data_proc, struct _krb5_preauth_client_rock *rock, krb5_kdc_req *request, krb5_data *encoded_request_body, krb5_data *encoded_previous_request, krb5_pa_data *pa_data, krb5_prompter_fct prompter, void *prompter_data, preauth_get_as_key_proc gak_fct, void *gak_data, krb5_data *salt, krb5_data *s2kparams, krb5_keyblock *as_key, krb5_pa_data **out_pa_data) { krb5_pa_data *send_pa; krb5_int32 nnonce, enctype; krb5_keyblock *kb; krb5_error_code status; int *pctx; #ifdef DEBUG fprintf(stderr, "%d bytes of preauthentication data (type %d)\n", pa_data->length, pa_data->pa_type); #endif pctx = plugin_context; if (pctx) { (*pctx)++; } if (pa_data->length == 0) { /* Create preauth data. */ send_pa = malloc(sizeof(krb5_pa_data)); if (send_pa == NULL) return ENOMEM; send_pa->pa_type = KRB5_PADATA_WPSE_REQ; send_pa->length = 4; send_pa->contents = malloc(4); if (send_pa->contents == NULL) { free(send_pa); return ENOMEM; } /* Store the preauth data. */ nnonce = htonl(request->nonce); memcpy(send_pa->contents, &nnonce, 4); *out_pa_data = send_pa; } else { /* A reply from the KDC. Conventionally this would be * indicated by a different preauthentication type, but this * mechanism/implementation doesn't do that. */ if (pa_data->length > 4) { memcpy(&enctype, pa_data->contents, 4); kb = NULL; status = krb5_init_keyblock(kcontext, ntohl(enctype), pa_data->length - 4, &kb); if (status != 0) return status; memcpy(kb->contents, pa_data->contents + 4, pa_data->length - 4); #ifdef DEBUG fprintf(stderr, "Recovered key type=%d, length=%d.\n", kb->enctype, kb->length); #endif status = krb5_copy_keyblock_contents(kcontext, kb, as_key); krb5_free_keyblock(kcontext, kb); return status; } return KRB5KRB_ERR_GENERIC; } return 0; }
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL krb5_string_to_key_derived(krb5_context context, const void *str, size_t len, krb5_enctype etype, krb5_keyblock *key) { struct _krb5_encryption_type *et = _krb5_find_enctype(etype); krb5_error_code ret; struct _krb5_key_data kd; size_t keylen; u_char *tmp; if(et == NULL) { krb5_set_error_message (context, KRB5_PROG_ETYPE_NOSUPP, N_("encryption type %d not supported", ""), etype); return KRB5_PROG_ETYPE_NOSUPP; } keylen = et->keytype->bits / 8; ALLOC(kd.key, 1); if(kd.key == NULL) { krb5_set_error_message (context, ENOMEM, N_("malloc: out of memory", "")); return ENOMEM; } ret = krb5_data_alloc(&kd.key->keyvalue, et->keytype->size); if(ret) { free(kd.key); return ret; } kd.key->keytype = etype; tmp = malloc (keylen); if(tmp == NULL) { krb5_free_keyblock(context, kd.key); krb5_set_error_message (context, ENOMEM, N_("malloc: out of memory", "")); return ENOMEM; } ret = _krb5_n_fold(str, len, tmp, keylen); if (ret) { free(tmp); krb5_set_error_message (context, ENOMEM, N_("malloc: out of memory", "")); return ret; } kd.schedule = NULL; _krb5_DES3_random_to_key(context, kd.key, tmp, keylen); memset(tmp, 0, keylen); free(tmp); ret = _krb5_derive_key(context, et, &kd, "kerberos", /* XXX well known constant */ strlen("kerberos")); if (ret) { _krb5_free_key_data(context, &kd, et); return ret; } ret = krb5_copy_keyblock_contents(context, kd.key, key); _krb5_free_key_data(context, &kd, et); return ret; }
static krb5_error_code digest_request(krb5_context context, krb5_realm realm, krb5_ccache ccache, krb5_key_usage usage, const DigestReqInner *ireq, DigestRepInner *irep) { DigestREQ req; DigestREP rep; krb5_error_code ret; krb5_data data, data2; size_t size = 0; krb5_crypto crypto = NULL; krb5_auth_context ac = NULL; krb5_principal principal = NULL; krb5_ccache id = NULL; krb5_realm r = NULL; krb5_data_zero(&data); krb5_data_zero(&data2); memset(&req, 0, sizeof(req)); memset(&rep, 0, sizeof(rep)); if (ccache == NULL) { ret = krb5_cc_default(context, &id); if (ret) goto out; } else id = ccache; if (realm == NULL) { ret = krb5_get_default_realm(context, &r); if (ret) goto out; } else r = realm; /* * */ ret = krb5_make_principal(context, &principal, r, KRB5_DIGEST_NAME, r, NULL); if (ret) goto out; ASN1_MALLOC_ENCODE(DigestReqInner, data.data, data.length, ireq, &size, ret); if (ret) { krb5_set_error_message(context, ret, N_("Failed to encode digest inner request", "")); goto out; } if (size != data.length) krb5_abortx(context, "ASN.1 internal encoder error"); ret = krb5_mk_req_exact(context, &ac, AP_OPTS_USE_SUBKEY|AP_OPTS_MUTUAL_REQUIRED, principal, NULL, id, &req.apReq); if (ret) goto out; { krb5_keyblock *key; ret = krb5_auth_con_getlocalsubkey(context, ac, &key); if (ret) goto out; if (key == NULL) { ret = EINVAL; krb5_set_error_message(context, ret, N_("Digest failed to get local subkey", "")); goto out; } ret = krb5_crypto_init(context, key, 0, &crypto); krb5_free_keyblock (context, key); if (ret) goto out; } ret = krb5_encrypt_EncryptedData(context, crypto, usage, data.data, data.length, 0, &req.innerReq); if (ret) goto out; krb5_data_free(&data); ASN1_MALLOC_ENCODE(DigestREQ, data.data, data.length, &req, &size, ret); if (ret) { krb5_set_error_message(context, ret, N_("Failed to encode DigestREQest", "")); goto out; } if (size != data.length) krb5_abortx(context, "ASN.1 internal encoder error"); ret = krb5_sendto_kdc(context, &data, &r, &data2); if (ret) goto out; ret = decode_DigestREP(data2.data, data2.length, &rep, NULL); if (ret) { krb5_set_error_message(context, ret, N_("Failed to parse digest response", "")); goto out; } { krb5_ap_rep_enc_part *repl; ret = krb5_rd_rep(context, ac, &rep.apRep, &repl); if (ret) goto out; krb5_free_ap_rep_enc_part(context, repl); } { krb5_keyblock *key; ret = krb5_auth_con_getremotesubkey(context, ac, &key); if (ret) goto out; if (key == NULL) { ret = EINVAL; krb5_set_error_message(context, ret, N_("Digest reply have no remote subkey", "")); goto out; } krb5_crypto_destroy(context, crypto); ret = krb5_crypto_init(context, key, 0, &crypto); krb5_free_keyblock (context, key); if (ret) goto out; } krb5_data_free(&data); ret = krb5_decrypt_EncryptedData(context, crypto, usage, &rep.innerRep, &data); if (ret) goto out; ret = decode_DigestRepInner(data.data, data.length, irep, NULL); if (ret) { krb5_set_error_message(context, ret, N_("Failed to decode digest inner reply", "")); goto out; } out: if (ccache == NULL && id) krb5_cc_close(context, id); if (realm == NULL && r) free(r); if (crypto) krb5_crypto_destroy(context, crypto); if (ac) krb5_auth_con_free(context, ac); if (principal) krb5_free_principal(context, principal); krb5_data_free(&data); krb5_data_free(&data2); free_DigestREQ(&req); free_DigestREP(&rep); return ret; }
void KRB5_LIB_FUNCTION krb5_rd_req_out_ctx_free(krb5_context context, krb5_rd_req_out_ctx ctx) { krb5_free_keyblock(context, ctx->keyblock); free(ctx); }
static void ec_verify(krb5_context context, krb5_data *req_pkt, krb5_kdc_req *request, krb5_enc_tkt_part *enc_tkt_reply, krb5_pa_data *data, krb5_kdcpreauth_callbacks cb, krb5_kdcpreauth_rock rock, krb5_kdcpreauth_moddata moddata, krb5_kdcpreauth_verify_respond_fn respond, void *arg) { krb5_error_code retval = 0; krb5_timestamp now; krb5_enc_data *enc = NULL; krb5_data scratch, plain; krb5_keyblock *armor_key = cb->fast_armor(context, rock); krb5_pa_enc_ts *ts = NULL; krb5_keyblock *client_keys = NULL; krb5_keyblock *challenge_key = NULL; krb5_keyblock *kdc_challenge_key; krb5_kdcpreauth_modreq modreq = NULL; int i = 0; plain.data = NULL; if (armor_key == NULL) { retval = ENOENT; krb5_set_error_message(context, ENOENT, _("Encrypted Challenge used outside of FAST " "tunnel")); } scratch.data = (char *) data->contents; scratch.length = data->length; if (retval == 0) retval = decode_krb5_enc_data(&scratch, &enc); if (retval == 0) { plain.data = malloc(enc->ciphertext.length); plain.length = enc->ciphertext.length; if (plain.data == NULL) retval = ENOMEM; } if (retval == 0) retval = cb->client_keys(context, rock, &client_keys); if (retval == 0) { for (i = 0; client_keys[i].enctype&& (retval == 0); i++ ) { retval = krb5_c_fx_cf2_simple(context, armor_key, "clientchallengearmor", &client_keys[i], "challengelongterm", &challenge_key); if (retval == 0) retval = krb5_c_decrypt(context, challenge_key, KRB5_KEYUSAGE_ENC_CHALLENGE_CLIENT, NULL, enc, &plain); if (challenge_key) krb5_free_keyblock(context, challenge_key); challenge_key = NULL; if (retval == 0) break; /*We failed to decrypt. Try next key*/ retval = 0; } if (client_keys[i].enctype == 0) { retval = KRB5KDC_ERR_PREAUTH_FAILED; krb5_set_error_message(context, retval, _("Incorrect password in encrypted " "challenge")); } } if (retval == 0) retval = decode_krb5_pa_enc_ts(&plain, &ts); if (retval == 0) retval = krb5_timeofday(context, &now); if (retval == 0) { if (labs(now-ts->patimestamp) < context->clockskew) { enc_tkt_reply->flags |= TKT_FLG_PRE_AUTH; /* * If this fails, we won't generate a reply to the client. That * may cause the client to fail, but at this point the KDC has * considered this a success, so the return value is ignored. */ if (krb5_c_fx_cf2_simple(context, armor_key, "kdcchallengearmor", &client_keys[i], "challengelongterm", &kdc_challenge_key) == 0) modreq = (krb5_kdcpreauth_modreq)kdc_challenge_key; } else { /*skew*/ retval = KRB5KRB_AP_ERR_SKEW; } } cb->free_keys(context, rock, client_keys); if (plain.data) free(plain.data); if (enc) krb5_free_enc_data(context, enc); if (ts) krb5_free_pa_enc_ts(context, ts); (*respond)(arg, retval, modreq, NULL, NULL); }
static krb5_error_code kdc_verify_preauth(krb5_context context, 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 get_entry_proc, void *pa_module_context, void **pa_request_context, krb5_data **e_data, krb5_authdata ***authz_data) { krb5_error_code retval = 0; krb5_timestamp now; krb5_enc_data *enc = NULL; krb5_data scratch, plain; krb5_keyblock *armor_key = NULL; krb5_pa_enc_ts *ts = NULL; krb5int_access kaccess; krb5_keyblock *client_keys = NULL; krb5_data *client_data = NULL; krb5_keyblock *challenge_key = NULL; int i = 0; plain.data = NULL; if (krb5int_accessor(&kaccess, KRB5INT_ACCESS_VERSION) != 0) return 0; retval = fast_kdc_get_armor_key(context, get_entry_proc, request, client, &armor_key); if (retval == 0 &&armor_key == NULL) { retval = ENOENT; krb5_set_error_message(context, ENOENT, "Encrypted Challenge used outside of FAST tunnel"); } scratch.data = (char *) data->contents; scratch.length = data->length; if (retval == 0) retval = kaccess.decode_enc_data(&scratch, &enc); if (retval == 0) { plain.data = malloc(enc->ciphertext.length); plain.length = enc->ciphertext.length; if (plain.data == NULL) retval = ENOMEM; } if (retval == 0) retval = get_entry_proc(context, request, client, krb5plugin_preauth_keys, &client_data); if (retval == 0) { client_keys = (krb5_keyblock *) client_data->data; for (i = 0; client_keys[i].enctype&& (retval == 0); i++ ) { retval = krb5_c_fx_cf2_simple(context, armor_key, "clientchallengearmor", &client_keys[i], "challengelongterm", &challenge_key); if (retval == 0) retval = krb5_c_decrypt(context, challenge_key, KRB5_KEYUSAGE_ENC_CHALLENGE_CLIENT, NULL, enc, &plain); if (challenge_key) krb5_free_keyblock(context, challenge_key); challenge_key = NULL; if (retval == 0) break; /*We failed to decrypt. Try next key*/ retval = 0; krb5_free_keyblock_contents(context, &client_keys[i]); } if (client_keys[i].enctype == 0) { retval = KRB5KDC_ERR_PREAUTH_FAILED; krb5_set_error_message(context, retval, "Incorrect password in encrypted challenge"); } else { /*not run out of keys*/ int j; assert (retval == 0); for (j = i+1; client_keys[j].enctype; j++) krb5_free_keyblock_contents(context, &client_keys[j]); } } if (retval == 0) retval = kaccess.decode_enc_ts(&plain, &ts); if (retval == 0) retval = krb5_timeofday(context, &now); if (retval == 0) { if (labs(now-ts->patimestamp) < context->clockskew) { enc_tkt_reply->flags |= TKT_FLG_PRE_AUTH; /* * If this fails, we won't generate a reply to the client. That * may cause the client to fail, but at this point the KDC has * considered this a success, so the return value is ignored. */ fast_kdc_replace_reply_key(context, get_entry_proc, request); krb5_c_fx_cf2_simple(context, armor_key, "kdcchallengearmor", &client_keys[i], "challengelongterm", (krb5_keyblock **) pa_request_context); } else { /*skew*/ retval = KRB5KRB_AP_ERR_SKEW; } } if (client_keys) { if (client_keys[i].enctype) krb5_free_keyblock_contents(context, &client_keys[i]); krb5_free_data(context, client_data); } if (armor_key) krb5_free_keyblock(context, armor_key); if (plain.data) free(plain.data); if (enc) kaccess.free_enc_data(context, enc); if (ts) kaccess.free_enc_ts(context, ts); return retval; }
krb5_error_code kdc_fast_response_handle_padata(struct kdc_request_state *state, krb5_kdc_req *request, krb5_kdc_rep *rep, krb5_enctype enctype) { krb5_error_code retval = 0; krb5_fast_finished finish; krb5_fast_response fast_response; krb5_data *encoded_ticket = NULL; krb5_data *encrypted_reply = NULL; krb5_pa_data *pa = NULL, **pa_array = NULL; krb5_cksumtype cksumtype = CKSUMTYPE_RSA_MD5; krb5_pa_data *empty_padata[] = {NULL}; krb5_keyblock *strengthen_key = NULL; kdc_realm_t *kdc_active_realm = state->realm_data; if (!state->armor_key) return 0; memset(&finish, 0, sizeof(finish)); retval = krb5_init_keyblock(kdc_context, enctype, 0, &strengthen_key); if (retval == 0) retval = krb5_c_make_random_key(kdc_context, enctype, strengthen_key); if (retval == 0) { state->strengthen_key = strengthen_key; strengthen_key = NULL; } fast_response.padata = rep->padata; if (fast_response.padata == NULL) fast_response.padata = &empty_padata[0]; fast_response.strengthen_key = state->strengthen_key; fast_response.nonce = request->nonce; fast_response.finished = &finish; finish.client = rep->client; pa_array = calloc(3, sizeof(*pa_array)); if (pa_array == NULL) retval = ENOMEM; pa = calloc(1, sizeof(krb5_pa_data)); if (retval == 0 && pa == NULL) retval = ENOMEM; if (retval == 0) retval = krb5_us_timeofday(kdc_context, &finish.timestamp, &finish.usec); if (retval == 0) retval = encode_krb5_ticket(rep->ticket, &encoded_ticket); if (retval == 0) retval = krb5int_c_mandatory_cksumtype(kdc_context, state->armor_key->enctype, &cksumtype); if (retval == 0) retval = krb5_c_make_checksum(kdc_context, cksumtype, state->armor_key, KRB5_KEYUSAGE_FAST_FINISHED, encoded_ticket, &finish.ticket_checksum); if (retval == 0) retval = encrypt_fast_reply(state, &fast_response, &encrypted_reply); if (retval == 0) { pa[0].pa_type = KRB5_PADATA_FX_FAST; pa[0].length = encrypted_reply->length; pa[0].contents = (unsigned char *) encrypted_reply->data; pa_array[0] = &pa[0]; krb5_free_pa_data(kdc_context, rep->padata); rep->padata = pa_array; pa_array = NULL; free(encrypted_reply); encrypted_reply = NULL; pa = NULL; } if (pa) free(pa); if (pa_array) free(pa_array); if (encrypted_reply) krb5_free_data(kdc_context, encrypted_reply); if (encoded_ticket) krb5_free_data(kdc_context, encoded_ticket); if (strengthen_key != NULL) krb5_free_keyblock(kdc_context, strengthen_key); if (finish.ticket_checksum.contents) krb5_free_checksum_contents(kdc_context, &finish.ticket_checksum); return retval; }
static OM_uint32 gsskrb5_acceptor_start(OM_uint32 * minor_status, gsskrb5_ctx ctx, krb5_context context, const gss_cred_id_t acceptor_cred_handle, const gss_buffer_t input_token_buffer, const gss_channel_bindings_t input_chan_bindings, gss_name_t * src_name, gss_OID * mech_type, gss_buffer_t output_token, OM_uint32 * ret_flags, OM_uint32 * time_rec, gss_cred_id_t * delegated_cred_handle) { krb5_error_code kret; OM_uint32 ret = GSS_S_COMPLETE; krb5_data indata; krb5_flags ap_options; krb5_keytab keytab = NULL; int is_cfx = 0; const gsskrb5_cred acceptor_cred = (gsskrb5_cred)acceptor_cred_handle; krb5_boolean is_hostbased_service = FALSE; /* * We may, or may not, have an escapsulation. */ ret = _gsskrb5_decapsulate (minor_status, input_token_buffer, &indata, "\x01\x00", ctx->mech); if (ret) { /* Assume that there is no OID wrapping. */ indata.length = input_token_buffer->length; indata.data = input_token_buffer->value; } /* * We need to get our keytab */ if (acceptor_cred == NULL) { if (_gsskrb5_keytab != NULL) keytab = _gsskrb5_keytab; } else if (acceptor_cred->keytab != NULL) { keytab = acceptor_cred->keytab; } is_hostbased_service = (acceptor_cred && acceptor_cred->principal && krb5_principal_is_gss_hostbased_service(context, acceptor_cred->principal)); /* * We need to check the ticket and create the AP-REP packet */ { krb5_rd_req_in_ctx in = NULL; krb5_rd_req_out_ctx out = NULL; krb5_principal server = NULL; if (acceptor_cred && !is_hostbased_service) server = acceptor_cred->principal; kret = krb5_rd_req_in_ctx_alloc(context, &in); if (kret == 0) kret = krb5_rd_req_in_set_keytab(context, in, keytab); if (kret) { if (in) krb5_rd_req_in_ctx_free(context, in); *minor_status = kret; return GSS_S_FAILURE; } kret = krb5_rd_req_ctx(context, &ctx->auth_context, &indata, server, in, &out); krb5_rd_req_in_ctx_free(context, in); if (ret && _gss_mg_log_level(5)) { const char *e = krb5_get_error_message(context, ret); char *s = NULL; if (server) (void)krb5_unparse_name(context, server, &s); _gss_mg_log(5, "gss-asc: rd_req (server: %s) failed with: %d: %s", s ? s : "<not specified>", ret, e); krb5_free_error_message(context, e); if (s) krb5_xfree(s); } switch (kret) { case 0: break; case KRB5KRB_AP_ERR_SKEW: case KRB5KRB_AP_ERR_TKT_NYV: /* * No reply in non-MUTUAL mode, but we don't know that its * non-MUTUAL mode yet, thats inside the 8003 checksum, so * lets only send the error token on clock skew, that * limit when send error token for non-MUTUAL. */ return send_error_token(minor_status, context, kret, server, &indata, ctx->mech, output_token); case KRB5KRB_AP_ERR_MODIFIED: case KRB5_KT_NOTFOUND: case KRB5_KT_END: /* * If the error is on the keytab entry missing or bad * decryption, lets assume that the keytab version was * wrong and tell the client that. */ return send_error_token(minor_status, context, KRB5KRB_AP_ERR_MODIFIED, server, NULL, ctx->mech, output_token); default: *minor_status = kret; return GSS_S_FAILURE; } /* * we need to remember some data on the context_handle. */ kret = krb5_rd_req_out_get_ap_req_options(context, out, &ap_options); if (kret == 0) kret = krb5_rd_req_out_get_ticket(context, out, &ctx->ticket); if (kret == 0) kret = krb5_rd_req_out_get_keyblock(context, out, &ctx->service_keyblock); if (kret == 0) { int flags; flags = krb5_rd_req_out_get_flags(context, out); if (flags & KRB5_RD_REQ_OUT_PAC_VALID) ctx->more_flags |= PAC_VALID; } if (kret == 0 && is_hostbased_service) { krb5_principal sp = ctx->ticket->server; if (sp->name.name_string.len < 1 || strcmp(sp->name.name_string.val[0], acceptor_cred->principal->name.name_string.val[0]) != 0) { kret = KRB5KRB_AP_WRONG_PRINC; krb5_set_error_message(context, ret, "Expecting service %s but got %s", acceptor_cred->principal->name.name_string.val[0], sp->name.name_string.val[0]); } } ctx->endtime = ctx->ticket->ticket.endtime; krb5_rd_req_out_ctx_free(context, out); if (kret) { ret = GSS_S_FAILURE; *minor_status = kret; return ret; } } /* * We need to copy the principal names to the context and the * calling layer. */ kret = krb5_copy_principal(context, ctx->ticket->client, &ctx->source); if (kret) { *minor_status = kret; return GSS_S_FAILURE; } kret = krb5_copy_principal(context, ctx->ticket->server, &ctx->target); if (kret) { ret = GSS_S_FAILURE; *minor_status = kret; return ret; } /* * We need to setup some compat stuff, this assumes that * context_handle->target is already set. */ ret = _gss_DES3_get_mic_compat(minor_status, ctx, context); if (ret) return ret; if (src_name != NULL) { kret = krb5_copy_principal (context, ctx->ticket->client, (gsskrb5_name*)src_name); if (kret) { ret = GSS_S_FAILURE; *minor_status = kret; return ret; } } /* * We need to get the flags out of the 8003 checksum. */ { krb5_authenticator authenticator; kret = krb5_auth_con_getauthenticator(context, ctx->auth_context, &authenticator); if(kret) { ret = GSS_S_FAILURE; *minor_status = kret; return ret; } if (authenticator->cksum == NULL) { krb5_free_authenticator(context, &authenticator); *minor_status = 0; return GSS_S_BAD_BINDINGS; } if (authenticator->cksum->cksumtype == CKSUMTYPE_GSSAPI) { krb5_data finished_data; krb5_crypto crypto = NULL; if (ctx->auth_context->remote_subkey) { kret = krb5_crypto_init(context, ctx->auth_context->remote_subkey, 0, &crypto); if (kret) { *minor_status = kret; return GSS_S_FAILURE; } } krb5_data_zero(&finished_data); ret = _gsskrb5_verify_8003_checksum(minor_status, context, crypto, input_chan_bindings, authenticator->cksum, &ctx->flags, &ctx->fwd_data, &finished_data); krb5_free_authenticator(context, &authenticator); if (ret) { krb5_crypto_destroy(context, crypto); return ret; } if (finished_data.length) { GSS_KRB5_FINISHED finished; krb5_data pkt; memset(&finished, 0, sizeof(finished)); if (ctx->messages == NULL) { krb5_crypto_destroy(context, crypto); krb5_data_free(&finished_data); *minor_status = 0; return GSS_S_BAD_SIG; } kret = krb5_storage_to_data(ctx->messages, &pkt); if (kret) { krb5_crypto_destroy(context, crypto); krb5_data_free(&finished_data); *minor_status = kret; return GSS_S_FAILURE; } if (ctx->auth_context->remote_subkey == NULL) { krb5_crypto_destroy(context, crypto); krb5_data_free(&finished_data); krb5_data_free(&pkt); *minor_status = 0; return GSS_S_BAD_SIG; } kret = decode_GSS_KRB5_FINISHED(finished_data.data, finished_data.length, &finished, NULL); krb5_data_free(&finished_data); if (kret) { krb5_crypto_destroy(context, crypto); krb5_data_free(&pkt); *minor_status = kret; return GSS_S_FAILURE; } kret = krb5_verify_checksum(context, crypto, KRB5_KU_FINISHED, pkt.data, pkt.length, &finished.gss_mic); free_GSS_KRB5_FINISHED(&finished); krb5_data_free(&pkt); if (kret) { krb5_crypto_destroy(context, crypto); *minor_status = kret; return GSS_S_FAILURE; } } krb5_crypto_destroy(context, crypto); } else { krb5_crypto crypto; kret = krb5_crypto_init(context, ctx->auth_context->keyblock, 0, &crypto); if(kret) { krb5_free_authenticator(context, &authenticator); ret = GSS_S_FAILURE; *minor_status = kret; return ret; } /* * Windows accepts Samba3's use of a kerberos, rather than * GSSAPI checksum here */ kret = krb5_verify_checksum(context, crypto, KRB5_KU_AP_REQ_AUTH_CKSUM, NULL, 0, authenticator->cksum); krb5_free_authenticator(context, &authenticator); krb5_crypto_destroy(context, crypto); if(kret) { ret = GSS_S_BAD_SIG; *minor_status = kret; return ret; } /* * Samba style get some flags (but not DCE-STYLE), use * ap_options to guess the mutual flag. */ ctx->flags = GSS_C_REPLAY_FLAG | GSS_C_SEQUENCE_FLAG; if (ap_options & AP_OPTS_MUTUAL_REQUIRED) ctx->flags |= GSS_C_MUTUAL_FLAG; } } if(ctx->flags & GSS_C_MUTUAL_FLAG) { krb5_data outbuf; int use_subkey = 0; _gsskrb5i_is_cfx(context, ctx, 1); is_cfx = (ctx->more_flags & IS_CFX); if (is_cfx || (ap_options & AP_OPTS_USE_SUBKEY)) { use_subkey = 1; } else { krb5_keyblock *rkey; /* * If there is a initiator subkey, copy that to acceptor * subkey to match Windows behavior */ kret = krb5_auth_con_getremotesubkey(context, ctx->auth_context, &rkey); if (kret == 0) { kret = krb5_auth_con_setlocalsubkey(context, ctx->auth_context, rkey); if (kret == 0) use_subkey = 1; krb5_free_keyblock(context, rkey); } } if (use_subkey) { ctx->gk5c.flags |= GK5C_ACCEPTOR_SUBKEY; krb5_auth_con_addflags(context, ctx->auth_context, KRB5_AUTH_CONTEXT_USE_SUBKEY, NULL); } kret = krb5_mk_rep(context, ctx->auth_context, &outbuf); if (kret) { *minor_status = kret; return GSS_S_FAILURE; } if (IS_DCE_STYLE(ctx)) { output_token->length = outbuf.length; output_token->value = outbuf.data; } else { ret = _gsskrb5_encapsulate(minor_status, &outbuf, output_token, "\x02\x00", ctx->mech); krb5_data_free (&outbuf); if (ret) return ret; } } ctx->flags |= GSS_C_TRANS_FLAG; /* Remember the flags */ ctx->endtime = ctx->ticket->ticket.endtime; ctx->more_flags |= OPEN; if (mech_type) *mech_type = ctx->mech; if (time_rec) { ret = _gsskrb5_lifetime_left(minor_status, context, ctx->endtime, time_rec); if (ret) { return ret; } } /* * When GSS_C_DCE_STYLE is in use, we need ask for a AP-REP from * the client. */ if (IS_DCE_STYLE(ctx)) { /* * Return flags to caller, but we haven't processed * delgations yet */ if (ret_flags) *ret_flags = (ctx->flags & ~GSS_C_DELEG_FLAG); ctx->acceptor_state = acceptor_wait_for_dcestyle; return GSS_S_CONTINUE_NEEDED; } ret = gsskrb5_acceptor_ready(minor_status, ctx, context, delegated_cred_handle); if (ret_flags) *ret_flags = ctx->flags; return ret; }
static OM_uint32 kg_unseal_v1_iov(krb5_context context, OM_uint32 *minor_status, krb5_gss_ctx_id_rec *ctx, gss_iov_buffer_desc *iov, int iov_count, size_t token_wrapper_len, int *conf_state, gss_qop_t *qop_state, int toktype) { OM_uint32 code; gss_iov_buffer_t header; gss_iov_buffer_t trailer; unsigned char *ptr; int sealalg; int signalg; krb5_checksum cksum; krb5_checksum md5cksum; size_t cksum_len = 0; size_t conflen = 0; int direction; krb5_ui_4 seqnum; OM_uint32 retval; size_t sumlen; krb5_keyusage sign_usage = KG_USAGE_SIGN; md5cksum.length = cksum.length = 0; md5cksum.contents = cksum.contents = NULL; header = kg_locate_header_iov(iov, iov_count, toktype); assert(header != NULL); trailer = kg_locate_iov(iov, iov_count, GSS_IOV_BUFFER_TYPE_TRAILER); if (trailer != NULL && trailer->buffer.length != 0) { *minor_status = (OM_uint32)KRB5_BAD_MSIZE; return GSS_S_DEFECTIVE_TOKEN; } if (header->buffer.length < token_wrapper_len + 14) { *minor_status = 0; return GSS_S_DEFECTIVE_TOKEN; } ptr = (unsigned char *)header->buffer.value + token_wrapper_len; signalg = ptr[0]; signalg |= ptr[1] << 8; sealalg = ptr[2]; sealalg |= ptr[3] << 8; if (ptr[4] != 0xFF || ptr[5] != 0xFF) { *minor_status = 0; return GSS_S_DEFECTIVE_TOKEN; } if (toktype != KG_TOK_WRAP_MSG && sealalg != 0xFFFF) { *minor_status = 0; return GSS_S_DEFECTIVE_TOKEN; } if (toktype == KG_TOK_WRAP_MSG && !(sealalg == 0xFFFF || sealalg == ctx->sealalg)) { *minor_status = 0; return GSS_S_DEFECTIVE_TOKEN; } if ((ctx->sealalg == SEAL_ALG_NONE && signalg > 1) || (ctx->sealalg == SEAL_ALG_1 && signalg != SGN_ALG_3) || (ctx->sealalg == SEAL_ALG_DES3KD && signalg != SGN_ALG_HMAC_SHA1_DES3_KD)|| (ctx->sealalg == SEAL_ALG_MICROSOFT_RC4 && signalg != SGN_ALG_HMAC_MD5)) { *minor_status = 0; return GSS_S_DEFECTIVE_TOKEN; } switch (signalg) { case SGN_ALG_DES_MAC_MD5: case SGN_ALG_MD2_5: case SGN_ALG_HMAC_MD5: cksum_len = 8; if (toktype != KG_TOK_WRAP_MSG) sign_usage = 15; break; case SGN_ALG_3: cksum_len = 16; break; case SGN_ALG_HMAC_SHA1_DES3_KD: cksum_len = 20; break; default: *minor_status = 0; return GSS_S_DEFECTIVE_TOKEN; } /* get the token parameters */ code = kg_get_seq_num(context, ctx->seq, ptr + 14, ptr + 6, &direction, &seqnum); if (code != 0) { *minor_status = code; return GSS_S_BAD_SIG; } /* decode the message, if SEAL */ if (toktype == KG_TOK_WRAP_MSG) { if (sealalg != 0xFFFF) { if (ctx->sealalg == SEAL_ALG_MICROSOFT_RC4) { unsigned char bigend_seqnum[4]; krb5_keyblock *enc_key; size_t i; store_32_be(seqnum, bigend_seqnum); code = krb5_k_key_keyblock(context, ctx->enc, &enc_key); if (code != 0) { retval = GSS_S_FAILURE; goto cleanup; } assert(enc_key->length == 16); for (i = 0; i < enc_key->length; i++) ((char *)enc_key->contents)[i] ^= 0xF0; code = kg_arcfour_docrypt_iov(context, enc_key, 0, &bigend_seqnum[0], 4, iov, iov_count); krb5_free_keyblock(context, enc_key); } else { code = kg_decrypt_iov(context, 0, ((ctx->gss_flags & GSS_C_DCE_STYLE) != 0), 0 /*EC*/, 0 /*RRC*/, ctx->enc, KG_USAGE_SEAL, NULL, iov, iov_count); } if (code != 0) { retval = GSS_S_FAILURE; goto cleanup; } } conflen = kg_confounder_size(context, ctx->enc->keyblock.enctype); } if (header->buffer.length != token_wrapper_len + 14 + cksum_len + conflen) { retval = GSS_S_DEFECTIVE_TOKEN; goto cleanup; } /* compute the checksum of the message */ /* initialize the checksum */ switch (signalg) { case SGN_ALG_DES_MAC_MD5: case SGN_ALG_MD2_5: case SGN_ALG_DES_MAC: case SGN_ALG_3: md5cksum.checksum_type = CKSUMTYPE_RSA_MD5; break; case SGN_ALG_HMAC_MD5: md5cksum.checksum_type = CKSUMTYPE_HMAC_MD5_ARCFOUR; break; case SGN_ALG_HMAC_SHA1_DES3_KD: md5cksum.checksum_type = CKSUMTYPE_HMAC_SHA1_DES3; break; default: abort(); } code = krb5_c_checksum_length(context, md5cksum.checksum_type, &sumlen); if (code != 0) { retval = GSS_S_FAILURE; goto cleanup; } md5cksum.length = sumlen; /* compute the checksum of the message */ code = kg_make_checksum_iov_v1(context, md5cksum.checksum_type, cksum_len, ctx->seq, ctx->enc, sign_usage, iov, iov_count, toktype, &md5cksum); if (code != 0) { retval = GSS_S_FAILURE; goto cleanup; } switch (signalg) { case SGN_ALG_DES_MAC_MD5: case SGN_ALG_3: code = kg_encrypt_inplace(context, ctx->seq, KG_USAGE_SEAL, (g_OID_equal(ctx->mech_used, gss_mech_krb5_old) ? ctx->seq->keyblock.contents : NULL), md5cksum.contents, 16); if (code != 0) { retval = GSS_S_FAILURE; goto cleanup; } cksum.length = cksum_len; cksum.contents = md5cksum.contents + 16 - cksum.length; code = k5_bcmp(cksum.contents, ptr + 14, cksum.length); break; case SGN_ALG_HMAC_SHA1_DES3_KD: case SGN_ALG_HMAC_MD5: code = k5_bcmp(md5cksum.contents, ptr + 14, cksum_len); break; default: code = 0; retval = GSS_S_DEFECTIVE_TOKEN; goto cleanup; break; } if (code != 0) { code = 0; retval = GSS_S_BAD_SIG; goto cleanup; } /* * For GSS_C_DCE_STYLE, the caller manages the padding, because the * pad length is in the RPC PDU. The value of the padding may be * uninitialized. For normal GSS, the last bytes of the decrypted * data contain the pad length. kg_fixup_padding_iov() will find * this and fixup the last data IOV appropriately. */ if (toktype == KG_TOK_WRAP_MSG && (ctx->gss_flags & GSS_C_DCE_STYLE) == 0) { retval = kg_fixup_padding_iov(&code, iov, iov_count); if (retval != GSS_S_COMPLETE) goto cleanup; } if (conf_state != NULL) *conf_state = (sealalg != 0xFFFF); if (qop_state != NULL) *qop_state = GSS_C_QOP_DEFAULT; if ((ctx->initiate && direction != 0xff) || (!ctx->initiate && direction != 0)) { *minor_status = (OM_uint32)G_BAD_DIRECTION; retval = GSS_S_BAD_SIG; } code = 0; retval = g_order_check(&ctx->seqstate, (gssint_uint64)seqnum); cleanup: krb5_free_checksum_contents(context, &md5cksum); *minor_status = code; return retval; }
static void finish_process_as_req(struct as_req_state *state, krb5_error_code errcode) { krb5_key_data *server_key; krb5_key_data *client_key; krb5_keyblock *as_encrypting_key = NULL; krb5_data *response = NULL; const char *emsg = 0; int did_log = 0; register int i; krb5_enctype useenctype; loop_respond_fn oldrespond; void *oldarg; kdc_realm_t *kdc_active_realm = state->active_realm; assert(state); oldrespond = state->respond; oldarg = state->arg; if (errcode) goto egress; if ((errcode = validate_forwardable(state->request, *state->client, *state->server, state->kdc_time, &state->status))) { errcode += ERROR_TABLE_BASE_krb5; goto egress; } state->ticket_reply.enc_part2 = &state->enc_tkt_reply; /* * Find the server key */ if ((errcode = krb5_dbe_find_enctype(kdc_context, state->server, -1, /* ignore keytype */ -1, /* Ignore salttype */ 0, /* Get highest kvno */ &server_key))) { state->status = "FINDING_SERVER_KEY"; goto egress; } /* * Convert server->key into a real key * (it may be encrypted in the database) * * server_keyblock is later used to generate auth data signatures */ if ((errcode = krb5_dbe_decrypt_key_data(kdc_context, NULL, server_key, &state->server_keyblock, NULL))) { state->status = "DECRYPT_SERVER_KEY"; goto egress; } /* * Find the appropriate client key. We search in the order specified * by request keytype list. */ client_key = NULL; useenctype = 0; for (i = 0; i < state->request->nktypes; i++) { useenctype = state->request->ktype[i]; if (!krb5_c_valid_enctype(useenctype)) continue; if (!krb5_dbe_find_enctype(kdc_context, state->client, useenctype, -1, 0, &client_key)) break; } if (!(client_key)) { /* Cannot find an appropriate key */ state->status = "CANT_FIND_CLIENT_KEY"; errcode = KRB5KDC_ERR_ETYPE_NOSUPP; goto egress; } state->rock.client_key = client_key; /* convert client.key_data into a real key */ if ((errcode = krb5_dbe_decrypt_key_data(kdc_context, NULL, client_key, &state->client_keyblock, NULL))) { state->status = "DECRYPT_CLIENT_KEY"; goto egress; } state->client_keyblock.enctype = useenctype; /* Start assembling the response */ state->reply.msg_type = KRB5_AS_REP; state->reply.client = state->enc_tkt_reply.client; /* post canonization */ state->reply.ticket = &state->ticket_reply; state->reply_encpart.session = &state->session_key; if ((errcode = fetch_last_req_info(state->client, &state->reply_encpart.last_req))) { state->status = "FETCH_LAST_REQ"; goto egress; } state->reply_encpart.nonce = state->request->nonce; state->reply_encpart.key_exp = get_key_exp(state->client); state->reply_encpart.flags = state->enc_tkt_reply.flags; state->reply_encpart.server = state->ticket_reply.server; /* copy the time fields EXCEPT for authtime; it's location * is used for ktime */ state->reply_encpart.times = state->enc_tkt_reply.times; state->reply_encpart.times.authtime = state->authtime = state->kdc_time; state->reply_encpart.caddrs = state->enc_tkt_reply.caddrs; state->reply_encpart.enc_padata = NULL; /* Fetch the padata info to be returned (do this before * authdata to handle possible replacement of reply key */ errcode = return_padata(kdc_context, &state->rock, state->req_pkt, state->request, &state->reply, &state->client_keyblock, &state->pa_context); if (errcode) { state->status = "KDC_RETURN_PADATA"; goto egress; } errcode = handle_authdata(kdc_context, state->c_flags, state->client, state->server, state->server, &state->client_keyblock, &state->server_keyblock, &state->server_keyblock, state->req_pkt, state->request, NULL, /* for_user_princ */ NULL, /* enc_tkt_request */ &state->enc_tkt_reply); if (errcode) { krb5_klog_syslog(LOG_INFO, _("AS_REQ : handle_authdata (%d)"), errcode); state->status = "HANDLE_AUTHDATA"; goto egress; } errcode = krb5_encrypt_tkt_part(kdc_context, &state->server_keyblock, &state->ticket_reply); if (errcode) { state->status = "ENCRYPTING_TICKET"; goto egress; } state->ticket_reply.enc_part.kvno = server_key->key_data_kvno; errcode = kdc_fast_response_handle_padata(state->rstate, state->request, &state->reply, state->client_keyblock.enctype); if (errcode) { state->status = "fast response handling"; goto egress; } /* now encode/encrypt the response */ state->reply.enc_part.enctype = state->client_keyblock.enctype; errcode = kdc_fast_handle_reply_key(state->rstate, &state->client_keyblock, &as_encrypting_key); if (errcode) { state->status = "generating reply key"; goto egress; } errcode = return_enc_padata(kdc_context, state->req_pkt, state->request, as_encrypting_key, state->server, &state->reply_encpart, FALSE); if (errcode) { state->status = "KDC_RETURN_ENC_PADATA"; goto egress; } errcode = krb5_encode_kdc_rep(kdc_context, KRB5_AS_REP, &state->reply_encpart, 0, as_encrypting_key, &state->reply, &response); state->reply.enc_part.kvno = client_key->key_data_kvno; if (errcode) { state->status = "ENCODE_KDC_REP"; goto egress; } /* these parts are left on as a courtesy from krb5_encode_kdc_rep so we can use them in raw form if needed. But, we don't... */ memset(state->reply.enc_part.ciphertext.data, 0, state->reply.enc_part.ciphertext.length); free(state->reply.enc_part.ciphertext.data); log_as_req(kdc_context, state->from, state->request, &state->reply, state->client, state->cname, state->server, state->sname, state->authtime, 0, 0, 0); did_log = 1; egress: if (errcode != 0) assert (state->status != 0); free_padata_context(kdc_context, state->pa_context); if (as_encrypting_key) krb5_free_keyblock(kdc_context, as_encrypting_key); if (errcode) emsg = krb5_get_error_message(kdc_context, errcode); if (state->status) { log_as_req(kdc_context, state->from, state->request, &state->reply, state->client, state->cname, state->server, state->sname, state->authtime, state->status, errcode, emsg); did_log = 1; } if (errcode) { if (state->status == 0) { state->status = emsg; } if (errcode != KRB5KDC_ERR_DISCARD) { errcode -= ERROR_TABLE_BASE_krb5; if (errcode < 0 || errcode > 128) errcode = KRB_ERR_GENERIC; errcode = prepare_error_as(state->rstate, state->request, errcode, state->e_data, state->typed_e_data, ((state->client != NULL) ? state->client->princ : NULL), &response, state->status); state->status = 0; } } if (emsg) krb5_free_error_message(kdc_context, emsg); if (state->enc_tkt_reply.authorization_data != NULL) krb5_free_authdata(kdc_context, state->enc_tkt_reply.authorization_data); if (state->server_keyblock.contents != NULL) krb5_free_keyblock_contents(kdc_context, &state->server_keyblock); if (state->client_keyblock.contents != NULL) krb5_free_keyblock_contents(kdc_context, &state->client_keyblock); if (state->reply.padata != NULL) krb5_free_pa_data(kdc_context, state->reply.padata); if (state->reply_encpart.enc_padata) krb5_free_pa_data(kdc_context, state->reply_encpart.enc_padata); if (state->cname != NULL) free(state->cname); if (state->sname != NULL) free(state->sname); krb5_db_free_principal(kdc_context, state->client); krb5_db_free_principal(kdc_context, state->server); if (state->session_key.contents != NULL) krb5_free_keyblock_contents(kdc_context, &state->session_key); if (state->ticket_reply.enc_part.ciphertext.data != NULL) { memset(state->ticket_reply.enc_part.ciphertext.data , 0, state->ticket_reply.enc_part.ciphertext.length); free(state->ticket_reply.enc_part.ciphertext.data); } krb5_free_pa_data(kdc_context, state->e_data); krb5_free_data(kdc_context, state->inner_body); kdc_free_rstate(state->rstate); krb5_free_kdc_req(kdc_context, state->request); assert(did_log != 0); free(state); (*oldrespond)(oldarg, errcode, response); }
static krb5_error_code ads_secrets_verify_ticket(krb5_context context, krb5_auth_context auth_context, krb5_principal host_princ, const DATA_BLOB *ticket, krb5_ticket **pp_tkt, krb5_keyblock **keyblock, krb5_error_code *perr) { krb5_error_code ret = 0; bool auth_ok = False; char *password_s = NULL; krb5_data password; krb5_enctype enctypes[] = { #if defined(ENCTYPE_ARCFOUR_HMAC) ENCTYPE_ARCFOUR_HMAC, #endif ENCTYPE_DES_CBC_CRC, ENCTYPE_DES_CBC_MD5, ENCTYPE_NULL }; krb5_data packet; int i; *pp_tkt = NULL; *keyblock = NULL; *perr = 0; if (!secrets_init()) { DEBUG(1,("ads_secrets_verify_ticket: secrets_init failed\n")); *perr = KRB5_CONFIG_CANTOPEN; return False; } password_s = secrets_fetch_machine_password(lp_workgroup(), NULL, NULL); if (!password_s) { DEBUG(1,("ads_secrets_verify_ticket: failed to fetch machine password\n")); *perr = KRB5_LIBOS_CANTREADPWD; return False; } password.data = password_s; password.length = strlen(password_s); /* CIFS doesn't use addresses in tickets. This would break NAT. JRA */ packet.length = ticket->length; packet.data = (char *)ticket->data; /* We need to setup a auth context with each possible encoding type in turn. */ for (i=0;enctypes[i];i++) { krb5_keyblock *key = NULL; if (!(key = SMB_MALLOC_P(krb5_keyblock))) { ret = ENOMEM; goto out; } if (create_kerberos_key_from_string(context, host_princ, &password, key, enctypes[i], false)) { SAFE_FREE(key); continue; } krb5_auth_con_setuseruserkey(context, auth_context, key); if (!(ret = krb5_rd_req(context, &auth_context, &packet, NULL, NULL, NULL, pp_tkt))) { DEBUG(10,("ads_secrets_verify_ticket: enc type [%u] decrypted message !\n", (unsigned int)enctypes[i] )); auth_ok = True; krb5_copy_keyblock(context, key, keyblock); krb5_free_keyblock(context, key); break; } DEBUG((ret != KRB5_BAD_ENCTYPE) ? 3 : 10, ("ads_secrets_verify_ticket: enc type [%u] failed to decrypt with error %s\n", (unsigned int)enctypes[i], error_message(ret))); /* successfully decrypted but ticket is just not valid at the moment */ if (ret == KRB5KRB_AP_ERR_TKT_NYV || ret == KRB5KRB_AP_ERR_TKT_EXPIRED || ret == KRB5KRB_AP_ERR_SKEW) { krb5_free_keyblock(context, key); break; } krb5_free_keyblock(context, key); } out: SAFE_FREE(password_s); *perr = ret; return auth_ok; }
krb5_error_code KRB5_CALLCONV krb5_rd_rep(krb5_context context, krb5_auth_context auth_context, const krb5_data *inbuf, krb5_ap_rep_enc_part **repl) { krb5_error_code retval; krb5_ap_rep * reply; krb5_data scratch; if (!krb5_is_ap_rep(inbuf)) return KRB5KRB_AP_ERR_MSG_TYPE; /* decode it */ if ((retval = decode_krb5_ap_rep(inbuf, &reply))) return retval; /* put together an eblock for this encryption */ scratch.length = reply->enc_part.ciphertext.length; if (!(scratch.data = malloc(scratch.length))) { krb5_free_ap_rep(context, reply); return(ENOMEM); } if ((retval = krb5_c_decrypt(context, auth_context->keyblock, KRB5_KEYUSAGE_AP_REP_ENCPART, 0, &reply->enc_part, &scratch))) goto clean_scratch; /* now decode the decrypted stuff */ retval = decode_krb5_ap_rep_enc_part(&scratch, repl); if (retval) goto clean_scratch; /* Check reply fields */ if (((*repl)->ctime != auth_context->authentp->ctime) || ((*repl)->cusec != auth_context->authentp->cusec)) { retval = KRB5_MUTUAL_FAILED; goto clean_scratch; } /* Set auth subkey */ if ((*repl)->subkey) { if (auth_context->recv_subkey) { krb5_free_keyblock(context, auth_context->recv_subkey); auth_context->recv_subkey = NULL; } retval = krb5_copy_keyblock(context, (*repl)->subkey, &auth_context->recv_subkey); if (retval) goto clean_scratch; if (auth_context->send_subkey) { krb5_free_keyblock(context, auth_context->send_subkey); auth_context->send_subkey = NULL; } retval = krb5_copy_keyblock(context, (*repl)->subkey, &auth_context->send_subkey); if (retval) { krb5_free_keyblock(context, auth_context->send_subkey); auth_context->send_subkey = NULL; } /* not used for anything yet */ auth_context->negotiated_etype = (*repl)->subkey->enctype; } /* Get remote sequence number */ auth_context->remote_seq_number = (*repl)->seq_number; clean_scratch: memset(scratch.data, 0, scratch.length); krb5_free_ap_rep(context, reply); free(scratch.data); return retval; }
/* Construct a cookie pa-data item using the cookie values from state, or a * trivial "MIT" cookie if no values are set. */ krb5_error_code kdc_fast_make_cookie(krb5_context context, struct kdc_request_state *state, krb5_db_entry *local_tgt, krb5_const_principal client_princ, krb5_pa_data **cookie_out) { krb5_error_code ret; krb5_secure_cookie cookie; krb5_pa_data **contents = state->out_cookie_padata, *pa; krb5_keyblock *key = NULL; krb5_timestamp now; krb5_enc_data enc; krb5_data *der_cookie = NULL; krb5_kvno kvno; size_t ctlen; *cookie_out = NULL; memset(&enc, 0, sizeof(enc)); /* Make a trivial cookie if there are no contents to marshal or we don't * have a TGT entry to encrypt them. */ if (contents == NULL || *contents == NULL || local_tgt == NULL) return make_padata(KRB5_PADATA_FX_COOKIE, "MIT", 3, cookie_out); ret = get_cookie_key(context, local_tgt, 0, client_princ, &key, &kvno); if (ret) goto cleanup; /* Encode the cookie. */ ret = krb5_timeofday(context, &now); if (ret) goto cleanup; cookie.time = now; cookie.data = contents; ret = encode_krb5_secure_cookie(&cookie, &der_cookie); if (ret) goto cleanup; /* Encrypt the cookie in key. */ ret = krb5_c_encrypt_length(context, key->enctype, der_cookie->length, &ctlen); if (ret) goto cleanup; ret = alloc_data(&enc.ciphertext, ctlen); if (ret) goto cleanup; ret = krb5_c_encrypt(context, key, KRB5_KEYUSAGE_PA_FX_COOKIE, NULL, der_cookie, &enc); if (ret) goto cleanup; /* Construct the cookie pa-data entry. */ ret = alloc_padata(KRB5_PADATA_FX_COOKIE, 8 + enc.ciphertext.length, &pa); memcpy(pa->contents, "MIT1", 4); store_32_be(kvno, pa->contents + 4); memcpy(pa->contents + 8, enc.ciphertext.data, enc.ciphertext.length); *cookie_out = pa; cleanup: krb5_free_keyblock(context, key); if (der_cookie != NULL) { zapfree(der_cookie->data, der_cookie->length); free(der_cookie); } krb5_free_data_contents(context, &enc.ciphertext); return ret; }