krb5_error_code verify_securid_data_2(krb5_context context, krb5_db_entry *client, krb5_sam_response_2 *sr2, krb5_enc_tkt_part *enc_tkt_reply, krb5_pa_data *pa, krb5_sam_challenge_2 **sc2_out) { krb5_error_code retval; int new_pin = 0; krb5_key_data *client_key_data = NULL; krb5_keyblock client_key; krb5_data scratch; krb5_enc_sam_response_enc_2 *esre2 = NULL; struct securid_track_data sid_track_data, *trackp = NULL; krb5_data tmp_data; SDI_HANDLE sd_handle = SDI_HANDLE_NONE; krb5_sam_challenge_2 *sc2p = NULL; char *cp, *user = NULL; char *securid_user = NULL; char passcode[LENPRNST+1]; char max_pin_len, min_pin_len, alpha_pin; memset(&client_key, 0, sizeof(client_key)); memset(&scratch, 0, sizeof(scratch)); *sc2_out = NULL; retval = krb5_unparse_name(context, client->princ, &user); if (retval != 0) { com_err("krb5kdc", retval, "while unparsing client name in verify_securid_data_2"); return retval; } if ((sr2->sam_enc_nonce_or_sad.ciphertext.data == NULL) || (sr2->sam_enc_nonce_or_sad.ciphertext.length <= 0)) { retval = KRB5KDC_ERR_PREAUTH_FAILED; krb5_set_error_message(context, retval, "No preauth data supplied in " "verify_securid_data_2 (%s)", user); goto cleanup; } retval = krb5_dbe_find_enctype(context, client, sr2->sam_enc_nonce_or_sad.enctype, KRB5_KDB_SALTTYPE_NORMAL, sr2->sam_enc_nonce_or_sad.kvno, &client_key_data); if (retval) { com_err("krb5kdc", retval, "while getting client key in verify_securid_data_2 (%s)", user); goto cleanup; } retval = krb5_dbe_decrypt_key_data(context, NULL, client_key_data, &client_key, NULL); if (retval != 0) { com_err("krb5kdc", retval, "while decrypting client key in verify_securid_data_2 (%s)", user); goto cleanup; } scratch.length = sr2->sam_enc_nonce_or_sad.ciphertext.length; scratch.data = k5alloc(scratch.length, &retval); if (retval) goto cleanup; retval = krb5_c_decrypt(context, &client_key, KRB5_KEYUSAGE_PA_SAM_RESPONSE, 0, &sr2->sam_enc_nonce_or_sad, &scratch); if (retval) { com_err("krb5kdc", retval, "while decrypting SAD in verify_securid_data_2 (%s)", user); goto cleanup; } retval = decode_krb5_enc_sam_response_enc_2(&scratch, &esre2); if (retval) { com_err("krb5kdc", retval, "while decoding SAD in verify_securid_data_2 (%s)", user); esre2 = NULL; goto cleanup; } if (sr2->sam_nonce != esre2->sam_nonce) { com_err("krb5kdc", KRB5KDC_ERR_PREAUTH_FAILED, "while checking nonce in verify_securid_data_2 (%s)", user); retval = KRB5KDC_ERR_PREAUTH_FAILED; goto cleanup; } if (esre2->sam_sad.length == 0 || esre2->sam_sad.data == NULL) { com_err("krb5kdc", KRB5KDC_ERR_PREAUTH_FAILED, "No SecurID passcode in verify_securid_data_2 (%s)", user); retval = KRB5KDC_ERR_PREAUTH_FAILED; goto cleanup; } /* Copy out SAD to null-terminated buffer */ memset(passcode, 0, sizeof(passcode)); if (esre2->sam_sad.length > (sizeof(passcode) - 1)) { retval = KRB5KDC_ERR_PREAUTH_FAILED; com_err("krb5kdc", retval, "SecurID passcode/PIN too long (%d bytes) in " "verify_securid_data_2 (%s)", esre2->sam_sad.length, user); goto cleanup; } memcpy(passcode, esre2->sam_sad.data, esre2->sam_sad.length); securid_user = strdup(user); if (!securid_user) { retval = ENOMEM; com_err("krb5kdc", ENOMEM, "while copying user name in verify_securid_data_2 (%s)", user); goto cleanup; } cp = strchr(securid_user, '@'); if (cp != NULL) *cp = '\0'; /* Check for any track_id data that may have state from a previous attempt * at SecurID authentication. */ if (sr2->sam_track_id.data && (sr2->sam_track_id.length > 0)) { krb5_data track_id_data; memset(&track_id_data, 0, sizeof(track_id_data)); retval = securid_decrypt_track_data_2(context, client, &sr2->sam_track_id, &track_id_data); if (retval) { com_err("krb5kdc", retval, "while decrypting SecurID trackID in " "verify_securid_data_2 (%s)", user); goto cleanup; } if (track_id_data.length < sizeof (struct securid_track_data)) { retval = KRB5KDC_ERR_PREAUTH_FAILED; com_err("krb5kdc", retval, "Length of track data incorrect"); goto cleanup; } trackp = (struct securid_track_data *)track_id_data.data; if(trackp->hostid != gethostid()) { krb5_klog_syslog(LOG_INFO, "Unexpected challenge response"); retval = KRB5KDC_ERR_DISCARD; goto cleanup; } switch(trackp->state) { case SECURID_STATE_INITIAL: goto initial; break; case SECURID_STATE_NEW_PIN_AGAIN: { int pin1_len, pin2_len; trackp->handle = ntohl(trackp->handle); pin2_len = strlen(passcode); pin1_len = strlen(trackp->passcode); if ((pin1_len != pin2_len) || (memcmp(passcode, trackp->passcode, pin1_len) != 0)) { retval = KRB5KDC_ERR_PREAUTH_FAILED; krb5_klog_syslog(LOG_INFO, "New SecurID PIN Failed for user " "%s: PIN mis-match", user); break; } retval = SD_Pin(trackp->handle, passcode); SD_Close(trackp->handle); if (retval == ACM_NEW_PIN_ACCEPTED) { enc_tkt_reply->flags|= TKT_FLG_HW_AUTH; enc_tkt_reply->flags|= TKT_FLG_PRE_AUTH; krb5_klog_syslog(LOG_INFO, "SecurID PIN Accepted for %s in " "verify_securid_data_2", securid_user); retval = 0; } else { retval = KRB5KDC_ERR_PREAUTH_FAILED; krb5_klog_syslog(LOG_INFO, "SecurID PIN Failed for user %s (AceServer " "returns %d) in verify_securid_data_2", user, retval); } break; } case SECURID_STATE_NEW_PIN: { krb5_sam_challenge_2_body sc2b; sc2p = k5alloc(sizeof *sc2p, &retval); if (retval) goto cleanup; memset(sc2p, 0, sizeof(*sc2p)); memset(&sc2b, 0, sizeof(sc2b)); sc2b.sam_type = PA_SAM_TYPE_SECURID; sc2b.sam_response_prompt.data = NEW_PIN_AGAIN_message; sc2b.sam_response_prompt.length = strlen(sc2b.sam_response_prompt.data); sc2b.sam_flags = KRB5_SAM_SEND_ENCRYPTED_SAD; sc2b.sam_etype = client_key.enctype; tmp_data.data = (char *)&sc2b.sam_nonce; tmp_data.length = sizeof(sc2b.sam_nonce); if ((retval = krb5_c_random_make_octets(context, &tmp_data))) { com_err("krb5kdc", retval, "while making nonce for SecurID new " "PIN2 SAM_CHALLENGE_2 (%s)", user); goto cleanup; } sid_track_data.state = SECURID_STATE_NEW_PIN_AGAIN; sid_track_data.handle = trackp->handle; sid_track_data.hostid = gethostid(); /* Should we complain if sizes don't work ?? */ memcpy(sid_track_data.passcode, passcode, sizeof(sid_track_data.passcode)); tmp_data.data = (char *)&sid_track_data; tmp_data.length = sizeof(sid_track_data); if ((retval = securid_encrypt_track_data_2(context, client, &tmp_data, &sc2b.sam_track_id))) { com_err("krb5kdc", retval, "while encrypting NEW PIN2 SecurID " "track data for SAM_CHALLENGE_2 (%s)", securid_user); goto cleanup; } retval = securid_make_sam_challenge_2_and_cksum(context, sc2p, &sc2b, &client_key); if (retval) { com_err("krb5kdc", retval, "while making cksum for " "SAM_CHALLENGE_2 (new PIN2) (%s)", securid_user); goto cleanup; } krb5_klog_syslog(LOG_INFO, "Requesting verification of new PIN for user %s", securid_user); *sc2_out = sc2p; sc2p = NULL; /*sc2_out may be set even on error path*/ retval = KRB5KDC_ERR_PREAUTH_REQUIRED; goto cleanup; } case SECURID_STATE_NEXT_CODE: trackp->handle = ntohl(trackp->handle); retval = SD_Next(trackp->handle, passcode); SD_Close(trackp->handle); if (retval == ACM_OK) { enc_tkt_reply->flags |= TKT_FLG_HW_AUTH | TKT_FLG_PRE_AUTH; krb5_klog_syslog(LOG_INFO, "Next SecurID Code Accepted for " "user %s", securid_user); retval = 0; } else { krb5_klog_syslog(LOG_INFO, "Next SecurID Code Failed for user " "%s (AceServer returns %d) in " "verify_securid_data_2", user, retval); retval = KRB5KDC_ERR_PREAUTH_FAILED; } break; } } else { /* No track data, this is first of N attempts */ initial: retval = SD_Init(&sd_handle); if (retval) { com_err("krb5kdc", KRB5KDC_ERR_PREAUTH_FAILED, "SD_Init() returns error %d in verify_securid_data_2 (%s)", retval, securid_user); retval = KRB5KDC_ERR_PREAUTH_FAILED; goto cleanup; } retval = SD_Lock(sd_handle, securid_user); if (retval != ACM_OK) { SD_Close(sd_handle); retval = KRB5KDC_ERR_PREAUTH_FAILED; krb5_klog_syslog(LOG_INFO, "SD_Lock() failed (AceServer returns %d) for %s", retval, securid_user); goto cleanup; } retval = SD_Check(sd_handle, passcode, securid_user); switch (retval) { case ACM_OK: SD_Close(sd_handle); enc_tkt_reply->flags|= TKT_FLG_HW_AUTH; enc_tkt_reply->flags|= TKT_FLG_PRE_AUTH; krb5_klog_syslog(LOG_INFO, "SecurID passcode accepted for user %s", user); retval = 0; break; case ACM_ACCESS_DENIED: SD_Close(sd_handle); retval = KRB5KDC_ERR_PREAUTH_FAILED; krb5_klog_syslog(LOG_INFO, "AceServer returns Access Denied for " "user %s (SAM2)", user); goto cleanup; case ACM_NEW_PIN_REQUIRED: new_pin = 1; /*fall through*/ case ACM_NEXT_CODE_REQUIRED: { krb5_sam_challenge_2_body sc2b; sc2p = k5alloc(sizeof *sc2p, &retval); if (retval) goto cleanup; memset(sc2p, 0, sizeof(*sc2p)); memset(&sc2b, 0, sizeof(sc2b)); sc2b.sam_type = PA_SAM_TYPE_SECURID; sc2b.sam_response_prompt.data = NEXT_PASSCODE_message; sc2b.sam_response_prompt.length = strlen(sc2b.sam_response_prompt.data); sc2b.sam_flags = KRB5_SAM_SEND_ENCRYPTED_SAD; sc2b.sam_etype = client_key.enctype; if (new_pin) { if ((AceGetMaxPinLen(sd_handle, &max_pin_len) == ACE_SUCCESS) && (AceGetMinPinLen(sd_handle, &min_pin_len) == ACE_SUCCESS) && (AceGetAlphanumeric(sd_handle, &alpha_pin) == ACE_SUCCESS)) { sprintf(PIN_message, "New PIN must contain %d to %d %sdigits", min_pin_len, max_pin_len, (alpha_pin == 0) ? "" : "alphanumeric "); sc2b.sam_challenge_label.data = PIN_message; sc2b.sam_challenge_label.length = strlen(sc2b.sam_challenge_label.data); } else { sc2b.sam_challenge_label.length = 0; } } tmp_data.data = (char *)&sc2b.sam_nonce; tmp_data.length = sizeof(sc2b.sam_nonce); if ((retval = krb5_c_random_make_octets(context, &tmp_data))) { com_err("krb5kdc", retval, "while making nonce for SecurID SAM_CHALLENGE_2 (%s)", user); goto cleanup; } if (new_pin) sid_track_data.state = SECURID_STATE_NEW_PIN; else sid_track_data.state = SECURID_STATE_NEXT_CODE; sid_track_data.handle = htonl(sd_handle); sid_track_data.hostid = gethostid(); tmp_data.data = (char *)&sid_track_data; tmp_data.length = sizeof(sid_track_data); retval = securid_encrypt_track_data_2(context, client, &tmp_data, &sc2b.sam_track_id); if (retval) { com_err("krb5kdc", retval, "while encrypting SecurID track " "data for SAM_CHALLENGE_2 (%s)", securid_user); goto cleanup; } retval = securid_make_sam_challenge_2_and_cksum(context, sc2p, &sc2b, &client_key); if (retval) { com_err("krb5kdc", retval, "while making cksum for SAM_CHALLENGE_2 (%s)", securid_user); } if (new_pin) krb5_klog_syslog(LOG_INFO, "New SecurID PIN required for " "user %s", securid_user); else krb5_klog_syslog(LOG_INFO, "Next SecurID passcode required " "for user %s", securid_user); *sc2_out = sc2p; sc2p = NULL; retval = KRB5KDC_ERR_PREAUTH_REQUIRED; /*sc2_out is permitted as an output on error path*/ goto cleanup; } default: com_err("krb5kdc", KRB5KDC_ERR_PREAUTH_FAILED, "AceServer returns unknown error code %d " "in verify_securid_data_2\n", retval); retval = KRB5KDC_ERR_PREAUTH_FAILED; goto cleanup; } } /* no track_id data */ cleanup: krb5_free_keyblock_contents(context, &client_key); free(scratch.data); krb5_free_enc_sam_response_enc_2(context, esre2); free(user); free(securid_user); free(trackp); krb5_free_sam_challenge_2(context, sc2p); return retval; }
static void kdc_verify_preauth(krb5_context context, krb5_data *req_pkt, krb5_kdc_req *request, krb5_enc_tkt_part *enc_tkt_reply, krb5_pa_data *pa_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, saved_retval = 0; krb5_sam_response_2 *sr2 = NULL; krb5_data scratch, *scratch2, *e_data = NULL; char *client_name = NULL; krb5_sam_challenge_2 *out_sc2 = NULL; krb5_db_entry *client = cb->client_entry(context, rock); scratch.data = (char *) pa_data->contents; scratch.length = pa_data->length; retval = krb5_unparse_name(context, client->princ, &client_name); if (retval) goto cleanup; retval = decode_krb5_sam_response_2(&scratch, &sr2); if (retval) { com_err("krb5kdc", retval, "while decoding SAM_RESPONSE_2 in verify_sam_response_2"); sr2 = NULL; goto cleanup; } switch (sr2->sam_type) { #ifdef ARL_SECURID_PREAUTH case PA_SAM_TYPE_SECURID: retval = verify_securid_data_2(context, client, sr2, enc_tkt_reply, pa_data, &out_sc2); if (retval) goto cleanup; break; #endif /* ARL_SECURID_PREAUTH */ #ifdef GRAIL_PREAUTH case PA_SAM_TYPE_GRAIL: retval = verify_grail_data(context, client, sr2, enc_tkt_reply, pa_data, &out_sc2); if (retval) goto cleanup; break; #endif /* GRAIL_PREAUTH */ default: retval = KRB5_PREAUTH_BAD_TYPE; com_err("krb5kdc", retval, "while verifying SAM 2 data"); break; } /* * It is up to the method-specific verify routine to set the * ticket flags to indicate TKT_FLG_HW_AUTH and/or * TKT_FLG_PRE_AUTH. Some methods may require more than one round * of dialog with the client and must return successfully from * their verify routine. If does not set the TGT flags, the * required_preauth conditions will not be met and it will try * again to get enough preauth data from the client. Do not set * TGT flags here. */ cleanup: /* * Note that e_data is an output even in error conditions. If we * successfully encode the output e_data, we return whatever error is * received above. Otherwise we return the encoding error. */ saved_retval = retval; if (out_sc2) { krb5_pa_data pa_out; krb5_pa_data *pa_array[2]; pa_array[0] = &pa_out; pa_array[1] = NULL; pa_out.pa_type = KRB5_PADATA_SAM_CHALLENGE_2; retval = encode_krb5_sam_challenge_2(out_sc2, &scratch2); krb5_free_sam_challenge_2(context, out_sc2); if (retval) goto encode_error; pa_out.contents = (krb5_octet *) scratch2->data; pa_out.length = scratch2->length; retval = encode_krb5_padata_sequence(pa_array, &e_data); krb5_free_data(context, scratch2); } encode_error: krb5_free_sam_response_2(context, sr2); free(client_name); if (retval == 0) retval = saved_retval; (*respond)(arg, retval, NULL, NULL, NULL); }
static krb5_error_code sam2_process(krb5_context context, krb5_clpreauth_moddata moddata, krb5_clpreauth_modreq modreq, krb5_get_init_creds_opt *opt, krb5_clpreauth_callbacks cb, krb5_clpreauth_rock rock, krb5_kdc_req *request, krb5_data *encoded_request_body, krb5_data *encoded_previous_request, krb5_pa_data *padata, krb5_prompter_fct prompter, void *prompter_data, krb5_pa_data ***out_padata) { krb5_error_code retval; krb5_sam_challenge_2 *sc2 = NULL; krb5_sam_challenge_2_body *sc2b = NULL; krb5_data tmp_data; krb5_data response_data; char name[100], banner[100], prompt[100], response[100]; krb5_prompt kprompt; krb5_prompt_type prompt_type; krb5_data defsalt, *salt; krb5_checksum **cksum; krb5_data *scratch = NULL; krb5_boolean valid_cksum = 0; krb5_enc_sam_response_enc_2 enc_sam_response_enc_2; krb5_sam_response_2 sr2; size_t ciph_len; krb5_pa_data **sam_padata; if (prompter == NULL) return KRB5_LIBOS_CANTREADPWD; tmp_data.length = padata->length; tmp_data.data = (char *)padata->contents; if ((retval = decode_krb5_sam_challenge_2(&tmp_data, &sc2))) return(retval); retval = decode_krb5_sam_challenge_2_body(&sc2->sam_challenge_2_body, &sc2b); if (retval) { krb5_free_sam_challenge_2(context, sc2); return(retval); } if (!sc2->sam_cksum || ! *sc2->sam_cksum) { krb5_free_sam_challenge_2(context, sc2); krb5_free_sam_challenge_2_body(context, sc2b); return(KRB5_SAM_NO_CHECKSUM); } if (sc2b->sam_flags & KRB5_SAM_MUST_PK_ENCRYPT_SAD) { krb5_free_sam_challenge_2(context, sc2); krb5_free_sam_challenge_2_body(context, sc2b); return(KRB5_SAM_UNSUPPORTED); } if (!krb5_c_valid_enctype(sc2b->sam_etype)) { krb5_free_sam_challenge_2(context, sc2); krb5_free_sam_challenge_2_body(context, sc2b); return(KRB5_SAM_INVALID_ETYPE); } /* All of the above error checks are KDC-specific, that is, they */ /* assume a failure in the KDC reply. By returning anything other */ /* than KRB5_KDC_UNREACH, KRB5_PREAUTH_FAILED, */ /* KRB5_LIBOS_PWDINTR, or KRB5_REALM_CANT_RESOLVE, the client will */ /* most likely go on to try the AS_REQ against master KDC */ if (!(sc2b->sam_flags & KRB5_SAM_USE_SAD_AS_KEY)) { /* We will need the password to obtain the key used for */ /* the checksum, and encryption of the sam_response. */ /* Go ahead and get it now, preserving the ordering of */ /* prompts for the user. */ retval = (*rock->gak_fct)(context, request->client, sc2b->sam_etype, prompter, prompter_data, rock->salt, rock->s2kparams, rock->as_key, *rock->gak_data); if (retval) { krb5_free_sam_challenge_2(context, sc2); krb5_free_sam_challenge_2_body(context, sc2b); return(retval); } } snprintf(name, sizeof(name), "%.*s", SAMDATA(sc2b->sam_type_name, _("SAM Authentication"), sizeof(name) - 1)); snprintf(banner, sizeof(banner), "%.*s", SAMDATA(sc2b->sam_challenge_label, sam_challenge_banner(sc2b->sam_type), sizeof(banner)-1)); snprintf(prompt, sizeof(prompt), "%s%.*s%s%.*s", sc2b->sam_challenge.length?"Challenge is [":"", SAMDATA(sc2b->sam_challenge, "", 20), sc2b->sam_challenge.length?"], ":"", SAMDATA(sc2b->sam_response_prompt, "passcode", 55)); response_data.data = response; response_data.length = sizeof(response); kprompt.prompt = prompt; kprompt.hidden = 1; kprompt.reply = &response_data; prompt_type = KRB5_PROMPT_TYPE_PREAUTH; krb5int_set_prompt_types(context, &prompt_type); if ((retval = ((*prompter)(context, prompter_data, name, banner, 1, &kprompt)))) { krb5_free_sam_challenge_2(context, sc2); krb5_free_sam_challenge_2_body(context, sc2b); krb5int_set_prompt_types(context, 0); return(retval); } krb5int_set_prompt_types(context, (krb5_prompt_type *)NULL); /* Generate salt used by string_to_key() */ salt = rock->salt; if (((int) salt->length == -1) && (salt->data == NULL)) { if ((retval = krb5_principal2salt(context, request->client, &defsalt))) { krb5_free_sam_challenge_2(context, sc2); krb5_free_sam_challenge_2_body(context, sc2b); return(retval); } salt = &defsalt; } else { defsalt.length = 0; } /* Get encryption key to be used for checksum and sam_response */ if (!(sc2b->sam_flags & KRB5_SAM_USE_SAD_AS_KEY)) { /* as_key = string_to_key(password) */ if (rock->as_key->length) { krb5_free_keyblock_contents(context, rock->as_key); rock->as_key->length = 0; } /* generate a key using the supplied password */ retval = krb5_c_string_to_key(context, sc2b->sam_etype, (krb5_data *)*rock->gak_data, salt, rock->as_key); if (retval) { krb5_free_sam_challenge_2(context, sc2); krb5_free_sam_challenge_2_body(context, sc2b); if (defsalt.length) free(defsalt.data); return(retval); } if (!(sc2b->sam_flags & KRB5_SAM_SEND_ENCRYPTED_SAD)) { /* as_key = combine_key (as_key, string_to_key(SAD)) */ krb5_keyblock tmp_kb; retval = krb5_c_string_to_key(context, sc2b->sam_etype, &response_data, salt, &tmp_kb); if (retval) { krb5_free_sam_challenge_2(context, sc2); krb5_free_sam_challenge_2_body(context, sc2b); if (defsalt.length) free(defsalt.data); return(retval); } /* This should be a call to the crypto library some day */ /* key types should already match the sam_etype */ retval = krb5int_c_combine_keys(context, rock->as_key, &tmp_kb, rock->as_key); if (retval) { krb5_free_sam_challenge_2(context, sc2); krb5_free_sam_challenge_2_body(context, sc2b); if (defsalt.length) free(defsalt.data); return(retval); } krb5_free_keyblock_contents(context, &tmp_kb); } if (defsalt.length) free(defsalt.data); } else { /* as_key = string_to_key(SAD) */ if (rock->as_key->length) { krb5_free_keyblock_contents(context, rock->as_key); rock->as_key->length = 0; } /* generate a key using the supplied password */ retval = krb5_c_string_to_key(context, sc2b->sam_etype, &response_data, salt, rock->as_key); if (defsalt.length) free(defsalt.data); if (retval) { krb5_free_sam_challenge_2(context, sc2); krb5_free_sam_challenge_2_body(context, sc2b); return(retval); } } /* Now we have a key, verify the checksum on the sam_challenge */ cksum = sc2->sam_cksum; for (; *cksum; cksum++) { if (!krb5_c_is_keyed_cksum((*cksum)->checksum_type)) continue; /* Check this cksum */ retval = krb5_c_verify_checksum(context, rock->as_key, KRB5_KEYUSAGE_PA_SAM_CHALLENGE_CKSUM, &sc2->sam_challenge_2_body, *cksum, &valid_cksum); if (retval) { krb5_free_data(context, scratch); krb5_free_sam_challenge_2(context, sc2); krb5_free_sam_challenge_2_body(context, sc2b); return(retval); } if (valid_cksum) break; } if (!valid_cksum) { krb5_free_sam_challenge_2(context, sc2); krb5_free_sam_challenge_2_body(context, sc2b); /* * Note: We return AP_ERR_BAD_INTEGRITY so upper-level applications * can interpret that as "password incorrect", which is probably * the best error we can return in this situation. */ return(KRB5KRB_AP_ERR_BAD_INTEGRITY); } /* fill in enc_sam_response_enc_2 */ enc_sam_response_enc_2.magic = KV5M_ENC_SAM_RESPONSE_ENC_2; enc_sam_response_enc_2.sam_nonce = sc2b->sam_nonce; if (sc2b->sam_flags & KRB5_SAM_SEND_ENCRYPTED_SAD) { enc_sam_response_enc_2.sam_sad = response_data; } else { enc_sam_response_enc_2.sam_sad.data = NULL; enc_sam_response_enc_2.sam_sad.length = 0; } /* encode and encrypt enc_sam_response_enc_2 with as_key */ retval = encode_krb5_enc_sam_response_enc_2(&enc_sam_response_enc_2, &scratch); if (retval) { krb5_free_sam_challenge_2(context, sc2); krb5_free_sam_challenge_2_body(context, sc2b); return(retval); } /* Fill in sam_response_2 */ memset(&sr2, 0, sizeof(sr2)); sr2.sam_type = sc2b->sam_type; sr2.sam_flags = sc2b->sam_flags; sr2.sam_track_id = sc2b->sam_track_id; sr2.sam_nonce = sc2b->sam_nonce; /* Now take care of sr2.sam_enc_nonce_or_sad by encrypting encoded */ /* enc_sam_response_enc_2 from above */ retval = krb5_c_encrypt_length(context, rock->as_key->enctype, scratch->length, &ciph_len); if (retval) { krb5_free_sam_challenge_2(context, sc2); krb5_free_sam_challenge_2_body(context, sc2b); krb5_free_data(context, scratch); return(retval); } sr2.sam_enc_nonce_or_sad.ciphertext.length = ciph_len; sr2.sam_enc_nonce_or_sad.ciphertext.data = (char *)malloc(sr2.sam_enc_nonce_or_sad.ciphertext.length); if (!sr2.sam_enc_nonce_or_sad.ciphertext.data) { krb5_free_sam_challenge_2(context, sc2); krb5_free_sam_challenge_2_body(context, sc2b); krb5_free_data(context, scratch); return(ENOMEM); } retval = krb5_c_encrypt(context, rock->as_key, KRB5_KEYUSAGE_PA_SAM_RESPONSE, NULL, scratch, &sr2.sam_enc_nonce_or_sad); if (retval) { krb5_free_sam_challenge_2(context, sc2); krb5_free_sam_challenge_2_body(context, sc2b); krb5_free_data(context, scratch); krb5_free_data_contents(context, &sr2.sam_enc_nonce_or_sad.ciphertext); return(retval); } krb5_free_data(context, scratch); scratch = NULL; /* Encode the sam_response_2 */ retval = encode_krb5_sam_response_2(&sr2, &scratch); krb5_free_sam_challenge_2(context, sc2); krb5_free_sam_challenge_2_body(context, sc2b); krb5_free_data_contents(context, &sr2.sam_enc_nonce_or_sad.ciphertext); if (retval) { return (retval); } /* Almost there, just need to make padata ! */ sam_padata = malloc(2 * sizeof(*sam_padata)); if (sam_padata == NULL) { krb5_free_data(context, scratch); return(ENOMEM); } sam_padata[0] = malloc(sizeof(krb5_pa_data)); if (sam_padata[0] == NULL) { krb5_free_data(context, scratch); free(sam_padata); return(ENOMEM); } sam_padata[0]->magic = KV5M_PA_DATA; sam_padata[0]->pa_type = KRB5_PADATA_SAM_RESPONSE_2; sam_padata[0]->length = scratch->length; sam_padata[0]->contents = (krb5_octet *) scratch->data; free(scratch); sam_padata[1] = NULL; *out_padata = sam_padata; return(0); }