static krb5_error_code krb5_rd_cred_basic(krb5_context context, krb5_data *pcreddata, krb5_key pkey, krb5_replay_data *replaydata, krb5_creds ***pppcreds) { krb5_error_code retval = 0; krb5_cred * pcred = NULL; krb5_int32 ncreds = 0; krb5_int32 i = 0; krb5_cred_enc_part encpart; /* decode cred message */ if ((retval = decode_krb5_cred(pcreddata, &pcred))) return retval; memset(&encpart, 0, sizeof(encpart)); if ((retval = decrypt_credencdata(context, pcred, pkey, &encpart))) goto cleanup_cred; replaydata->timestamp = encpart.timestamp; replaydata->usec = encpart.usec; replaydata->seq = encpart.nonce; /* * Allocate the list of creds. The memory is allocated so that * krb5_free_tgt_creds can be used to free the list. */ for (ncreds = 0; pcred->tickets[ncreds]; ncreds++); if ((*pppcreds = (krb5_creds **)malloc((size_t)(sizeof(krb5_creds *) * (ncreds + 1)))) == NULL) { retval = ENOMEM; goto cleanup_cred; } (*pppcreds)[0] = NULL; /* * For each credential, create a strcture in the list of * credentials and copy the information. */ while (i < ncreds) { krb5_cred_info * pinfo; krb5_creds * pcur; krb5_data * pdata; if ((pcur = (krb5_creds *)calloc(1, sizeof(krb5_creds))) == NULL) { retval = ENOMEM; goto cleanup; } (*pppcreds)[i] = pcur; (*pppcreds)[i+1] = 0; pinfo = encpart.ticket_info[i++]; if ((retval = krb5_copy_principal(context, pinfo->client, &pcur->client))) goto cleanup; if ((retval = krb5_copy_principal(context, pinfo->server, &pcur->server))) goto cleanup; if ((retval = krb5_copy_keyblock_contents(context, pinfo->session, &pcur->keyblock))) goto cleanup; if ((retval = krb5_copy_addresses(context, pinfo->caddrs, &pcur->addresses))) goto cleanup; if ((retval = encode_krb5_ticket(pcred->tickets[i - 1], &pdata))) goto cleanup; pcur->ticket = *pdata; free(pdata); pcur->is_skey = FALSE; pcur->magic = KV5M_CREDS; pcur->times = pinfo->times; pcur->ticket_flags = pinfo->flags; pcur->authdata = NULL; /* not used */ memset(&pcur->second_ticket, 0, sizeof(pcur->second_ticket)); } /* * NULL terminate the list */ (*pppcreds)[i] = NULL; cleanup: if (retval) { krb5_free_tgt_creds(context, *pppcreds); *pppcreds = NULL; } cleanup_cred: krb5_free_cred(context, pcred); krb5_free_cred_enc_part(context, &encpart); return retval; }
/* * This functions takes as input an array of krb5_credentials, and * outputs an encoded KRB_CRED message suitable for krb5_rd_cred */ krb5_error_code KRB5_CALLCONV krb5_mk_ncred(krb5_context context, krb5_auth_context auth_context, krb5_creds **ppcreds, krb5_data **ppdata, krb5_replay_data *outdata) { krb5_address * premote_fulladdr = NULL; krb5_address * plocal_fulladdr = NULL; krb5_address remote_fulladdr; krb5_address local_fulladdr; krb5_error_code retval; krb5_key key; krb5_replay_data replaydata; krb5_cred * pcred; krb5_int32 ncred; krb5_boolean increased_sequence = FALSE; local_fulladdr.contents = 0; remote_fulladdr.contents = 0; memset(&replaydata, 0, sizeof(krb5_replay_data)); if (ppcreds == NULL) return KRB5KRB_AP_ERR_BADADDR; /* * Allocate memory for a NULL terminated list of tickets. */ for (ncred = 0; ppcreds[ncred]; ncred++) ; if ((pcred = (krb5_cred *)calloc(1, sizeof(krb5_cred))) == NULL) return ENOMEM; if ((pcred->tickets = (krb5_ticket **)calloc((size_t)ncred+1, sizeof(krb5_ticket *))) == NULL) { retval = ENOMEM; goto error; } /* Get keyblock */ if ((key = auth_context->send_subkey) == NULL) key = auth_context->key; /* Get replay info */ if ((auth_context->auth_context_flags & KRB5_AUTH_CONTEXT_DO_TIME) && (auth_context->rcache == NULL)) { retval = KRB5_RC_REQUIRED; goto error; } if (((auth_context->auth_context_flags & KRB5_AUTH_CONTEXT_RET_TIME) || (auth_context->auth_context_flags & KRB5_AUTH_CONTEXT_RET_SEQUENCE)) && (outdata == NULL)) { /* Need a better error */ retval = KRB5_RC_REQUIRED; goto error; } if ((retval = krb5_us_timeofday(context, &replaydata.timestamp, &replaydata.usec))) goto error; if (auth_context->auth_context_flags & KRB5_AUTH_CONTEXT_RET_TIME) { outdata->timestamp = replaydata.timestamp; outdata->usec = replaydata.usec; } if ((auth_context->auth_context_flags & KRB5_AUTH_CONTEXT_DO_SEQUENCE) || (auth_context->auth_context_flags & KRB5_AUTH_CONTEXT_RET_SEQUENCE)) { replaydata.seq = auth_context->local_seq_number++; increased_sequence = TRUE; if (auth_context->auth_context_flags & KRB5_AUTH_CONTEXT_RET_SEQUENCE) outdata->seq = replaydata.seq; } if (auth_context->local_addr) { if (auth_context->local_port) { if ((retval = krb5_make_fulladdr(context, auth_context->local_addr, auth_context->local_port, &local_fulladdr))) goto error; plocal_fulladdr = &local_fulladdr; } else { plocal_fulladdr = auth_context->local_addr; } } if (auth_context->remote_addr) { if (auth_context->remote_port) { if ((retval = krb5_make_fulladdr(context,auth_context->remote_addr, auth_context->remote_port, &remote_fulladdr))) goto error; premote_fulladdr = &remote_fulladdr; } else { premote_fulladdr = auth_context->remote_addr; } } /* Setup creds structure */ if ((retval = krb5_mk_ncred_basic(context, ppcreds, ncred, key, &replaydata, plocal_fulladdr, premote_fulladdr, pcred))) { goto error; } if (auth_context->auth_context_flags & KRB5_AUTH_CONTEXT_DO_TIME) { krb5_donot_replay replay; if ((retval = krb5_gen_replay_name(context, auth_context->local_addr, "_forw", &replay.client))) goto error; replay.server = ""; /* XXX */ replay.msghash = NULL; replay.cusec = replaydata.usec; replay.ctime = replaydata.timestamp; if ((retval = krb5_rc_store(context, auth_context->rcache, &replay))) { /* should we really error out here? XXX */ free(replay.client); goto error; } free(replay.client); } /* Encode creds structure */ retval = encode_krb5_cred(pcred, ppdata); error: free(local_fulladdr.contents); free(remote_fulladdr.contents); krb5_free_cred(context, pcred); if (retval) { if (increased_sequence) auth_context->local_seq_number--; } return retval; }
/* Validate a KRB-CRED message in creddata, and return a list of forwarded * credentials along with replay cache information. */ krb5_error_code KRB5_CALLCONV krb5_rd_cred(krb5_context context, krb5_auth_context authcon, krb5_data *creddata, krb5_creds ***creds_out, krb5_replay_data *replaydata_out) { krb5_error_code ret = 0; krb5_creds **credlist = NULL; krb5_cred *krbcred = NULL; krb5_cred_enc_part *encpart = NULL; krb5_replay_data rdata; const krb5_int32 flags = authcon->auth_context_flags; *creds_out = NULL; if (((flags & KRB5_AUTH_CONTEXT_RET_TIME) || (flags & KRB5_AUTH_CONTEXT_RET_SEQUENCE)) && replaydata_out == NULL) return KRB5_RC_REQUIRED; ret = decode_krb5_cred(creddata, &krbcred); if (ret) goto cleanup; ret = decrypt_encpart(context, &krbcred->enc_part, authcon, &encpart); if (ret) goto cleanup; ret = make_cred_list(context, krbcred, encpart, &credlist); if (ret) goto cleanup; if (authcon->recv_subkey != NULL || authcon->key != NULL) { rdata.timestamp = encpart->timestamp; ret = k5_privsafe_check_replay(context, authcon, &rdata, &krbcred->enc_part, NULL); if (ret) goto cleanup; } if (flags & KRB5_AUTH_CONTEXT_DO_SEQUENCE) { if (authcon->remote_seq_number != (uint32_t)encpart->nonce) { ret = KRB5KRB_AP_ERR_BADORDER; goto cleanup; } authcon->remote_seq_number++; } *creds_out = credlist; credlist = NULL; if ((flags & KRB5_AUTH_CONTEXT_RET_TIME) || (flags & KRB5_AUTH_CONTEXT_RET_SEQUENCE)) { replaydata_out->timestamp = encpart->timestamp; replaydata_out->usec = encpart->usec; replaydata_out->seq = encpart->nonce; } cleanup: krb5_free_tgt_creds(context, credlist); krb5_free_cred(context, krbcred); krb5_free_cred_enc_part(context, encpart); free(encpart); /* krb5_free_cred_enc_part doesn't do this */ return ret; }