krb5_error_code krb5_obtain_padata(krb5_context context, krb5_pa_data **preauth_to_use, git_key_proc key_proc, krb5_const_pointer key_seed, krb5_creds *creds, krb5_kdc_req *request) { krb5_error_code retval; krb5_etype_info etype_info = 0; krb5_pa_data ** pa; krb5_pa_data ** send_pa_list; krb5_pa_data ** send_pa; const krb5_preauth_ops *ops; krb5_keyblock * def_enc_key = 0; krb5_enctype enctype; krb5_data salt; krb5_data scratch; int size; int f_salt = 0; if (preauth_to_use == NULL) return 0; for (pa = preauth_to_use, size=0; *pa; pa++, size++) { if ((*pa)->pa_type == KRB5_PADATA_ETYPE_INFO) { /* XXX use the first one. Is there another way to disambiguate? */ if (etype_info) continue; scratch.length = (*pa)->length; scratch.data = (char *) (*pa)->contents; retval = decode_krb5_etype_info(&scratch, &etype_info); if (retval) return retval; if (etype_info[0] == NULL) { krb5_free_etype_info(context, etype_info); etype_info = NULL; } } } if ((send_pa_list = malloc((size+1) * sizeof(krb5_pa_data *))) == NULL) return ENOMEM; send_pa = send_pa_list; *send_pa = 0; enctype = request->ktype[0]; salt.data = 0; salt.length = SALT_TYPE_NO_LENGTH; if (etype_info) { enctype = etype_info[0]->etype; salt.data = (char *) etype_info[0]->salt; if(etype_info[0]->length == KRB5_ETYPE_NO_SALT) salt.length = SALT_TYPE_NO_LENGTH; /* XXX */ else salt.length = etype_info[0]->length; } if (salt.length == SALT_TYPE_NO_LENGTH) { /* * This will set the salt length */ if ((retval = krb5_principal2salt(context, request->client, &salt))) return(retval); f_salt = 1; } if ((retval = (*key_proc)(context, enctype, &salt, key_seed, &def_enc_key))) goto cleanup; for (pa = preauth_to_use; *pa; pa++) { if (find_pa_system((*pa)->pa_type, &ops)) continue; if (ops->obtain == 0) continue; retval = ((ops)->obtain)(context, *pa, etype_info, def_enc_key, key_proc, key_seed, creds, request, send_pa); if (retval) goto cleanup; if (*send_pa) send_pa++; *send_pa = 0; } retval = 0; if (send_pa_list[0]) { request->padata = send_pa_list; send_pa_list = 0; } cleanup: if (etype_info) krb5_free_etype_info(context, etype_info); if (f_salt) free(salt.data); if (send_pa_list) krb5_free_pa_data(context, send_pa_list); if (def_enc_key) krb5_free_keyblock(context, def_enc_key); return retval; }
/* Set etype info parameters in rock based on padata. */ static krb5_error_code get_etype_info(krb5_context context, krb5_pa_data **padata, krb5_kdc_req *request, krb5_clpreauth_rock rock) { krb5_error_code ret = 0; krb5_pa_data *pa; krb5_data d; krb5_etype_info etype_info = NULL, e; krb5_etype_info_entry *entry; krb5_boolean valid_found; int i; /* Find an etype-info2 or etype-info element in padata. */ pa = krb5int_find_pa_data(context, padata, KRB5_PADATA_ETYPE_INFO2); if (pa != NULL) { d = padata2data(*pa); (void)decode_krb5_etype_info2(&d, &etype_info); } else { pa = krb5int_find_pa_data(context, padata, KRB5_PADATA_ETYPE_INFO); if (pa != NULL) { d = padata2data(*pa); (void)decode_krb5_etype_info(&d, &etype_info); } } /* Fall back to pw-salt/afs3-salt if no etype-info element is present. */ if (etype_info == NULL) return get_salt(context, padata, request, rock); /* Search entries in order of the request's enctype preference. */ entry = NULL; valid_found = FALSE; for (i = 0; i < request->nktypes && entry == NULL; i++) { for (e = etype_info; *e != NULL && entry == NULL; e++) { if ((*e)->etype == request->ktype[i]) entry = *e; if (krb5_c_valid_enctype((*e)->etype)) valid_found = TRUE; } } if (entry == NULL) { ret = (valid_found) ? KRB5_CONFIG_ETYPE_NOSUPP : KRB5_PROG_ETYPE_NOSUPP; goto cleanup; } /* Set rock fields based on the entry we selected. */ *rock->etype = entry->etype; krb5_free_data_contents(context, rock->salt); if (entry->length != KRB5_ETYPE_NO_SALT) { *rock->salt = make_data(entry->salt, entry->length); entry->salt = NULL; *rock->default_salt = FALSE; } else { *rock->salt = empty_data(); *rock->default_salt = TRUE; } krb5_free_data_contents(context, rock->s2kparams); *rock->s2kparams = entry->s2kparams; entry->s2kparams = empty_data(); TRACE_PREAUTH_ETYPE_INFO(context, *rock->etype, rock->salt, rock->s2kparams); cleanup: krb5_free_etype_info(context, etype_info); return ret; }
krb5_error_code krb5_do_preauth(krb5_context context, krb5_kdc_req *request, krb5_pa_data **in_padata, krb5_pa_data ***out_padata, krb5_data *salt, krb5_data *s2kparams, krb5_enctype *etype, krb5_keyblock *as_key, krb5_prompter_fct prompter, void *prompter_data, krb5_gic_get_as_key_fct gak_fct, void *gak_data) { int h, i, j, out_pa_list_size; int seen_etype_info2 = 0; krb5_pa_data *out_pa = NULL, **out_pa_list = NULL; krb5_data scratch; krb5_etype_info etype_info = NULL; krb5_error_code ret; static const int paorder[] = { PA_INFO, PA_REAL }; int realdone; KRB5_LOG0(KRB5_INFO, "krb5_do_preauth() start"); if (in_padata == NULL) { *out_padata = NULL; return(0); } #ifdef DEBUG if (salt && salt->data && salt->length > 0) { fprintf (stderr, "salt len=%d", salt->length); if (salt->length > 0) fprintf (stderr, " '%*s'", salt->length, salt->data); fprintf (stderr, "; preauth data types:"); for (i = 0; in_padata[i]; i++) { fprintf (stderr, " %d", in_padata[i]->pa_type); } fprintf (stderr, "\n"); } #endif out_pa_list = NULL; out_pa_list_size = 0; /* first do all the informational preauths, then the first real one */ for (h=0; h<(sizeof(paorder)/sizeof(paorder[0])); h++) { realdone = 0; for (i=0; in_padata[i] && !realdone; i++) { int k, l, etype_found, valid_etype_found; /* * This is really gross, but is necessary to prevent * lossge when talking to a 1.0.x KDC, which returns an * erroneous PA-PW-SALT when it returns a KRB-ERROR * requiring additional preauth. */ switch (in_padata[i]->pa_type) { case KRB5_PADATA_ETYPE_INFO: case KRB5_PADATA_ETYPE_INFO2: { krb5_preauthtype pa_type = in_padata[i]->pa_type; if (etype_info) { if (seen_etype_info2 || pa_type != KRB5_PADATA_ETYPE_INFO2) continue; if (pa_type == KRB5_PADATA_ETYPE_INFO2) { krb5_free_etype_info( context, etype_info); etype_info = NULL; } } scratch.length = in_padata[i]->length; scratch.data = (char *) in_padata[i]->contents; if (pa_type == KRB5_PADATA_ETYPE_INFO2) { seen_etype_info2++; ret = decode_krb5_etype_info2(&scratch, &etype_info); } else ret = decode_krb5_etype_info(&scratch, &etype_info); if (ret) { ret = 0; /*Ignore error and etype_info element*/ krb5_free_etype_info( context, etype_info); etype_info = NULL; continue; } if (etype_info[0] == NULL) { krb5_free_etype_info(context, etype_info); etype_info = NULL; break; } /* * Select first etype in our request which is also in * etype-info (preferring client request ktype order). */ for (etype_found = 0, valid_etype_found = 0, k = 0; !etype_found && k < request->nktypes; k++) { for (l = 0; etype_info[l]; l++) { if (etype_info[l]->etype == request->ktype[k]) { etype_found++; break; } /* check if program has support for this etype for more * precise error reporting. */ if (valid_enctype(etype_info[l]->etype)) valid_etype_found++; } } if (!etype_found) { KRB5_LOG(KRB5_ERR, "error !etype_found, " "valid_etype_found = %d", valid_etype_found); if (valid_etype_found) { /* supported enctype but not requested */ ret = KRB5_CONFIG_ETYPE_NOSUPP; goto cleanup; } else { /* unsupported enctype */ ret = KRB5_PROG_ETYPE_NOSUPP; goto cleanup; } } scratch.data = (char *) etype_info[l]->salt; scratch.length = etype_info[l]->length; krb5_free_data_contents(context, salt); if (scratch.length == KRB5_ETYPE_NO_SALT) salt->data = NULL; else if ((ret = krb5int_copy_data_contents( context, &scratch, salt)) != 0) goto cleanup; *etype = etype_info[l]->etype; krb5_free_data_contents(context, s2kparams); if ((ret = krb5int_copy_data_contents(context, &etype_info[l]->s2kparams, s2kparams)) != 0) goto cleanup; break; } case KRB5_PADATA_PW_SALT: case KRB5_PADATA_AFS3_SALT: if (etype_info) continue; break; default: ; } for (j=0; pa_types[j].type >= 0; j++) { if ((in_padata[i]->pa_type == pa_types[j].type) && (pa_types[j].flags & paorder[h])) { out_pa = NULL; if ((ret = ((*pa_types[j].fct)(context, request, in_padata[i], &out_pa, salt, s2kparams, etype, as_key, prompter, prompter_data, gak_fct, gak_data)))) { goto cleanup; } if (out_pa) { if (out_pa_list == NULL) { if ((out_pa_list = (krb5_pa_data **) malloc(2*sizeof(krb5_pa_data *))) == NULL) { ret = ENOMEM; goto cleanup; } } else { if ((out_pa_list = (krb5_pa_data **) realloc(out_pa_list, (out_pa_list_size+2)* sizeof(krb5_pa_data *))) == NULL) { /* XXX this will leak the pointers which have already been allocated. oh well. */ ret = ENOMEM; goto cleanup; } } out_pa_list[out_pa_list_size++] = out_pa; } if (paorder[h] == PA_REAL) realdone = 1; } } } } if (out_pa_list) out_pa_list[out_pa_list_size++] = NULL; *out_padata = out_pa_list; if (etype_info) krb5_free_etype_info(context, etype_info); KRB5_LOG0(KRB5_INFO, "krb5_do_preauth() end"); return(0); cleanup: if (out_pa_list) { out_pa_list[out_pa_list_size++] = NULL; krb5_free_pa_data(context, out_pa_list); } if (etype_info) krb5_free_etype_info(context, etype_info); KRB5_LOG0(KRB5_INFO, "krb5_do_preauth() end"); return (ret); }