2N/A/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2N/A/*
2N/A * NAME
2N/A * cred.c
2N/A *
2N/A * DESCRIPTION
2N/A * Provide an interface to assemble and disassemble krb5_cred
2N/A * structures.
2N/A *
2N/A */
2N/A
2N/A/*
2N/A * Copyright (c) 2002, 2011, Oracle and/or its affiliates. All rights reserved.
2N/A */
2N/A
2N/A#include "k5-int.h"
2N/A#include "cleanup.h"
2N/A#include "auth_con.h"
2N/A
2N/A#include <stddef.h> /* NULL */
2N/A#include <stdlib.h> /* malloc */
2N/A#include <errno.h> /* ENOMEM */
2N/A
2N/A/* Solaris Kerberos */
2N/A#include "kerberos_dtrace.h"
2N/A
2N/A/*-------------------- encrypt_credencpart --------------------*/
2N/A
2N/A/*
2N/A * encrypt the enc_part of krb5_cred
2N/A */
2N/Astatic krb5_error_code
2N/Aencrypt_credencpart(krb5_context context, krb5_cred_enc_part *pcredpart,
2N/A krb5_key pkey, krb5_enc_data *pencdata)
2N/A{
2N/A krb5_error_code retval;
2N/A krb5_data * scratch;
2N/A
2N/A /* start by encoding to-be-encrypted part of the message */
2N/A if ((retval = encode_krb5_enc_cred_part(pcredpart, &scratch)))
2N/A return retval;
2N/A
2N/A /*
2N/A * If the keyblock is NULL, just copy the data from the encoded
2N/A * data to the ciphertext area.
2N/A */
2N/A if (pkey == NULL) {
2N/A pencdata->ciphertext.data = scratch->data;
2N/A pencdata->ciphertext.length = scratch->length;
2N/A free(scratch);
2N/A return 0;
2N/A }
2N/A
2N/A /* call the encryption routine */
2N/A retval = krb5_encrypt_keyhelper(context, pkey,
2N/A KRB5_KEYUSAGE_KRB_CRED_ENCPART,
2N/A scratch, pencdata);
2N/A
2N/A if (retval) {
2N/A memset(pencdata->ciphertext.data, 0, pencdata->ciphertext.length);
2N/A free(pencdata->ciphertext.data);
2N/A pencdata->ciphertext.length = 0;
2N/A pencdata->ciphertext.data = 0;
2N/A }
2N/A
2N/A memset(scratch->data, 0, scratch->length);
2N/A krb5_free_data(context, scratch);
2N/A
2N/A return retval;
2N/A}
2N/A
2N/A/*----------------------- krb5_mk_ncred_basic -----------------------*/
2N/A
2N/Astatic krb5_error_code
2N/Akrb5_mk_ncred_basic(krb5_context context,
2N/A krb5_creds **ppcreds, krb5_int32 nppcreds,
2N/A krb5_key key, krb5_replay_data *replaydata,
2N/A krb5_address *local_addr, krb5_address *remote_addr,
2N/A krb5_cred *pcred,
2N/A krb5_cred_enc_part **ret_credenc)
2N/A{
2N/A krb5_cred_enc_part credenc;
2N/A krb5_error_code retval;
2N/A size_t size;
2N/A int i;
2N/A
2N/A credenc.magic = KV5M_CRED_ENC_PART;
2N/A
2N/A credenc.s_address = 0;
2N/A credenc.r_address = 0;
2N/A if (local_addr) krb5_copy_addr(context, local_addr, &credenc.s_address);
2N/A if (remote_addr) krb5_copy_addr(context, remote_addr, &credenc.r_address);
2N/A
2N/A credenc.nonce = replaydata->seq;
2N/A credenc.usec = replaydata->usec;
2N/A credenc.timestamp = replaydata->timestamp;
2N/A
2N/A /* Get memory for creds and initialize it */
2N/A size = sizeof(krb5_cred_info *) * (nppcreds + 1);
2N/A credenc.ticket_info = (krb5_cred_info **) calloc(1, size);
2N/A if (credenc.ticket_info == NULL)
2N/A return ENOMEM;
2N/A
2N/A /*
2N/A * For each credential in the list, initialize a cred info
2N/A * structure and copy the ticket into the ticket list.
2N/A */
2N/A for (i = 0; i < nppcreds; i++) {
2N/A credenc.ticket_info[i] = malloc(sizeof(krb5_cred_info));
2N/A if (credenc.ticket_info[i] == NULL) {
2N/A retval = ENOMEM;
2N/A goto cleanup;
2N/A }
2N/A credenc.ticket_info[i+1] = NULL;
2N/A
2N/A credenc.ticket_info[i]->magic = KV5M_CRED_INFO;
2N/A credenc.ticket_info[i]->times = ppcreds[i]->times;
2N/A credenc.ticket_info[i]->flags = ppcreds[i]->ticket_flags;
2N/A
2N/A if ((retval = decode_krb5_ticket(&ppcreds[i]->ticket,
2N/A &pcred->tickets[i])))
2N/A goto cleanup;
2N/A
2N/A if ((retval = krb5_copy_keyblock(context, &ppcreds[i]->keyblock,
2N/A &credenc.ticket_info[i]->session)))
2N/A goto cleanup;
2N/A
2N/A if ((retval = krb5_copy_principal(context, ppcreds[i]->client,
2N/A &credenc.ticket_info[i]->client)))
2N/A goto cleanup;
2N/A
2N/A if ((retval = krb5_copy_principal(context, ppcreds[i]->server,
2N/A &credenc.ticket_info[i]->server)))
2N/A goto cleanup;
2N/A
2N/A if ((retval = krb5_copy_addresses(context, ppcreds[i]->addresses,
2N/A &credenc.ticket_info[i]->caddrs)))
2N/A goto cleanup;
2N/A }
2N/A
2N/A /*
2N/A * NULL terminate the lists.
2N/A */
2N/A pcred->tickets[i] = NULL;
2N/A
2N/A /* encrypt the credential encrypted part */
2N/A retval = encrypt_credencpart(context, &credenc, key, &pcred->enc_part);
2N/A
2N/Acleanup:
2N/A /* Solaris Kerberos begin */
2N/A if (retval == 0 && ret_credenc != NULL) {
2N/A *ret_credenc = malloc (sizeof (krb5_cred_enc_part));
2N/A if (*ret_credenc != NULL)
2N/A **ret_credenc = credenc;
2N/A } else
2N/A krb5_free_cred_enc_part(context, &credenc);
2N/A /* Solaris Kerberos end */
2N/A return retval;
2N/A}
2N/A
2N/A/*----------------------- krb5_mk_ncred -----------------------*/
2N/A
2N/A/*
2N/A * This functions takes as input an array of krb5_credentials, and
2N/A * outputs an encoded KRB_CRED message suitable for krb5_rd_cred
2N/A */
2N/Akrb5_error_code KRB5_CALLCONV
2N/Akrb5_mk_ncred(krb5_context context, krb5_auth_context auth_context,
2N/A krb5_creds **ppcreds, krb5_data **ppdata,
2N/A krb5_replay_data *outdata)
2N/A{
2N/A krb5_address * premote_fulladdr = NULL;
2N/A krb5_address * plocal_fulladdr = NULL;
2N/A krb5_address remote_fulladdr;
2N/A krb5_address local_fulladdr;
2N/A krb5_error_code retval;
2N/A krb5_key key;
2N/A krb5_replay_data replaydata;
2N/A krb5_cred * pcred;
2N/A krb5_int32 ncred;
2N/A krb5_boolean increased_sequence = FALSE;
2N/A /* Solaris Kerberos */
2N/A krb5_cred_enc_part *credenc = NULL;
2N/A
2N/A local_fulladdr.contents = 0;
2N/A remote_fulladdr.contents = 0;
2N/A memset(&replaydata, 0, sizeof(krb5_replay_data));
2N/A
2N/A if (ppcreds == NULL)
2N/A return KRB5KRB_AP_ERR_BADADDR;
2N/A
2N/A /*
2N/A * Allocate memory for a NULL terminated list of tickets.
2N/A */
2N/A for (ncred = 0; ppcreds[ncred]; ncred++)
2N/A ;
2N/A
2N/A if ((pcred = (krb5_cred *)calloc(1, sizeof(krb5_cred))) == NULL)
2N/A return ENOMEM;
2N/A
2N/A if ((pcred->tickets
2N/A = (krb5_ticket **)calloc((size_t)ncred+1,
2N/A sizeof(krb5_ticket *))) == NULL) {
2N/A retval = ENOMEM;
2N/A goto error;
2N/A }
2N/A
2N/A /* Get keyblock */
2N/A if ((key = auth_context->send_subkey) == NULL)
2N/A key = auth_context->key;
2N/A
2N/A /* Get replay info */
2N/A if ((auth_context->auth_context_flags & KRB5_AUTH_CONTEXT_DO_TIME) &&
2N/A (auth_context->rcache == NULL)) {
2N/A retval = KRB5_RC_REQUIRED;
2N/A goto error;
2N/A }
2N/A
2N/A if (((auth_context->auth_context_flags & KRB5_AUTH_CONTEXT_RET_TIME) ||
2N/A (auth_context->auth_context_flags & KRB5_AUTH_CONTEXT_RET_SEQUENCE))
2N/A && (outdata == NULL)) {
2N/A /* Need a better error */
2N/A retval = KRB5_RC_REQUIRED;
2N/A goto error;
2N/A }
2N/A
2N/A if ((retval = krb5_us_timeofday(context, &replaydata.timestamp,
2N/A &replaydata.usec)))
2N/A goto error;
2N/A if (auth_context->auth_context_flags & KRB5_AUTH_CONTEXT_RET_TIME) {
2N/A outdata->timestamp = replaydata.timestamp;
2N/A outdata->usec = replaydata.usec;
2N/A }
2N/A if ((auth_context->auth_context_flags & KRB5_AUTH_CONTEXT_DO_SEQUENCE) ||
2N/A (auth_context->auth_context_flags & KRB5_AUTH_CONTEXT_RET_SEQUENCE)) {
2N/A replaydata.seq = auth_context->local_seq_number++;
2N/A increased_sequence = TRUE;
2N/A if (auth_context->auth_context_flags & KRB5_AUTH_CONTEXT_RET_SEQUENCE)
2N/A outdata->seq = replaydata.seq;
2N/A }
2N/A
2N/A if (auth_context->local_addr) {
2N/A if (auth_context->local_port) {
2N/A if ((retval = krb5_make_fulladdr(context, auth_context->local_addr,
2N/A auth_context->local_port,
2N/A &local_fulladdr)))
2N/A goto error;
2N/A plocal_fulladdr = &local_fulladdr;
2N/A } else {
2N/A plocal_fulladdr = auth_context->local_addr;
2N/A }
2N/A }
2N/A
2N/A if (auth_context->remote_addr) {
2N/A if (auth_context->remote_port) {
2N/A if ((retval = krb5_make_fulladdr(context,auth_context->remote_addr,
2N/A auth_context->remote_port,
2N/A &remote_fulladdr)))
2N/A goto error;
2N/A premote_fulladdr = &remote_fulladdr;
2N/A } else {
2N/A premote_fulladdr = auth_context->remote_addr;
2N/A }
2N/A }
2N/A
2N/A /* Setup creds structure */
2N/A /* Solaris Kerberos */
2N/A if ((retval = krb5_mk_ncred_basic(context, ppcreds, ncred, key,
2N/A &replaydata, plocal_fulladdr,
2N/A premote_fulladdr, pcred, &credenc))) {
2N/A goto error;
2N/A }
2N/A
2N/A if (auth_context->auth_context_flags & KRB5_AUTH_CONTEXT_DO_TIME) {
2N/A krb5_donot_replay replay;
2N/A
2N/A if ((retval = krb5_gen_replay_name(context, auth_context->local_addr,
2N/A "_forw", &replay.client)))
2N/A goto error;
2N/A
2N/A replay.server = ""; /* XXX */
2N/A replay.msghash = NULL;
2N/A replay.cusec = replaydata.usec;
2N/A replay.ctime = replaydata.timestamp;
2N/A if ((retval = krb5_rc_store(context, auth_context->rcache, &replay))) {
2N/A /* should we really error out here? XXX */
2N/A free(replay.client);
2N/A goto error;
2N/A }
2N/A free(replay.client);
2N/A }
2N/A
2N/A /* Encode creds structure */
2N/A retval = encode_krb5_cred(pcred, ppdata);
2N/A
2N/A /* Solaris Kerberos dtrace */
2N/A if (retval == 0)
2N/A KERBEROS_PROBE_KRB_CRED(MAKE, *ppdata, pcred, credenc);
2N/A
2N/Aerror:
2N/A /* Solaris Kerberos begin */
2N/A if (credenc != NULL) {
2N/A krb5_free_cred_enc_part(context, credenc);
2N/A free(credenc);
2N/A }
2N/A /* Solaris Kerberos end */
2N/A free(local_fulladdr.contents);
2N/A free(remote_fulladdr.contents);
2N/A krb5_free_cred(context, pcred);
2N/A
2N/A if (retval) {
2N/A if (increased_sequence)
2N/A auth_context->local_seq_number--;
2N/A }
2N/A return retval;
2N/A}
2N/A
2N/A/*----------------------- krb5_mk_1cred -----------------------*/
2N/A
2N/A/*
2N/A * A convenience function that calls krb5_mk_ncred.
2N/A */
2N/Akrb5_error_code KRB5_CALLCONV
2N/Akrb5_mk_1cred(krb5_context context, krb5_auth_context auth_context,
2N/A krb5_creds *pcreds, krb5_data **ppdata,
2N/A krb5_replay_data *outdata)
2N/A{
2N/A krb5_error_code retval;
2N/A krb5_creds **ppcreds;
2N/A
2N/A if ((ppcreds = (krb5_creds **)malloc(sizeof(*ppcreds) * 2)) == NULL) {
2N/A return ENOMEM;
2N/A }
2N/A
2N/A ppcreds[0] = pcreds;
2N/A ppcreds[1] = NULL;
2N/A
2N/A retval = krb5_mk_ncred(context, auth_context, ppcreds,
2N/A ppdata, outdata);
2N/A
2N/A free(ppcreds);
2N/A return retval;
2N/A}