2N/A/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2N/A/*
2N/A * lib/krb5/krb/rd_priv.c
2N/A *
2N/A * Copyright 1990,1991,2007 by the Massachusetts Institute of Technology.
2N/A * All Rights Reserved.
2N/A *
2N/A * Export of this software from the United States of America may
2N/A * require a specific license from the United States Government.
2N/A * It is the responsibility of any person or organization contemplating
2N/A * export to obtain such a license before exporting.
2N/A *
2N/A * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
2N/A * distribute this software and its documentation for any purpose and
2N/A * without fee is hereby granted, provided that the above copyright
2N/A * notice appear in all copies and that both that copyright notice and
2N/A * this permission notice appear in supporting documentation, and that
2N/A * the name of M.I.T. not be used in advertising or publicity pertaining
2N/A * to distribution of the software without specific, written prior
2N/A * permission. Furthermore if you modify this software you must label
2N/A * your software as modified software and not distribute it in such a
2N/A * fashion that it might be confused with the original M.I.T. software.
2N/A * M.I.T. makes no representations about the suitability of
2N/A * this software for any purpose. It is provided "as is" without express
2N/A * or implied warranty.
2N/A */
2N/A/*
2N/A * Copyright (c) 1999, 2011, Oracle and/or its affiliates. All rights reserved.
2N/A */
2N/A/*
2N/A * krb5_rd_priv()
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/* Solaris Kerberos */
2N/A#include "kerberos_dtrace.h"
2N/A
2N/A/*
2N/A
2N/A Parses a KRB_PRIV message from inbuf, placing the confidential user
2N/A data in *outbuf.
2N/A
2N/A key specifies the key to be used for decryption of the message.
2N/A
2N/A remote_addr and local_addr specify the full
2N/A addresses (host and port) of the sender and receiver.
2N/A
2N/A outbuf points to allocated storage which the caller should
2N/A free when finished.
2N/A
2N/A i_vector is used as an initialization vector for the
2N/A encryption, and if non-NULL its contents are replaced with the last
2N/A block of the encrypted data upon exit.
2N/A
2N/A Returns system errors, integrity errors.
2N/A
2N/A*/
2N/A
2N/Astatic krb5_error_code
2N/Akrb5_rd_priv_basic(krb5_context context, const krb5_data *inbuf,
2N/A const krb5_key key, const krb5_address *local_addr,
2N/A const krb5_address *remote_addr, krb5_pointer i_vector,
2N/A krb5_replay_data *replaydata, krb5_data *outbuf)
2N/A{
2N/A krb5_error_code retval;
2N/A krb5_priv * privmsg;
2N/A krb5_data scratch;
2N/A krb5_priv_enc_part * privmsg_enc_part;
2N/A size_t blocksize;
2N/A krb5_data ivdata;
2N/A krb5_enctype enctype;
2N/A
2N/A if (!krb5_is_krb_priv(inbuf))
2N/A return KRB5KRB_AP_ERR_MSG_TYPE;
2N/A
2N/A /* decode private message */
2N/A if ((retval = decode_krb5_priv(inbuf, &privmsg)))
2N/A return retval;
2N/A
2N/A if (i_vector) {
2N/A enctype = krb5_k_key_enctype(context, key);
2N/A if ((retval = krb5_c_block_size(context, enctype, &blocksize)))
2N/A goto cleanup_privmsg;
2N/A
2N/A ivdata.length = blocksize;
2N/A ivdata.data = i_vector;
2N/A }
2N/A
2N/A scratch.length = privmsg->enc_part.ciphertext.length;
2N/A if (!(scratch.data = malloc(scratch.length))) {
2N/A retval = ENOMEM;
2N/A goto cleanup_privmsg;
2N/A }
2N/A
2N/A if ((retval = krb5_k_decrypt(context, key,
2N/A KRB5_KEYUSAGE_KRB_PRIV_ENCPART,
2N/A i_vector?&ivdata:0,
2N/A &privmsg->enc_part, &scratch))) {
2N/A /* Solaris Kerberos dtrace support */
2N/A KERBEROS_PROBE_KRB_PRIV(READ, inbuf, privmsg, NULL);
2N/A goto cleanup_scratch;
2N/A }
2N/A
2N/A /* now decode the decrypted stuff */
2N/A if ((retval = decode_krb5_enc_priv_part(&scratch, &privmsg_enc_part)))
2N/A goto cleanup_scratch;
2N/A
2N/A /* Solaris Kerberos dtrace support */
2N/A KERBEROS_PROBE_KRB_PRIV(READ, inbuf, privmsg, privmsg_enc_part);
2N/A
2N/A if (!krb5_address_compare(context,remote_addr,privmsg_enc_part->s_address)){
2N/A retval = KRB5KRB_AP_ERR_BADADDR;
2N/A goto cleanup_data;
2N/A }
2N/A
2N/A if (privmsg_enc_part->r_address) {
2N/A if (local_addr) {
2N/A if (!krb5_address_compare(context, local_addr,
2N/A privmsg_enc_part->r_address)) {
2N/A retval = KRB5KRB_AP_ERR_BADADDR;
2N/A goto cleanup_data;
2N/A }
2N/A } else {
2N/A krb5_address **our_addrs;
2N/A
2N/A if ((retval = krb5_os_localaddr(context, &our_addrs))) {
2N/A goto cleanup_data;
2N/A }
2N/A if (!krb5_address_search(context, privmsg_enc_part->r_address,
2N/A our_addrs)) {
2N/A krb5_free_addresses(context, our_addrs);
2N/A retval = KRB5KRB_AP_ERR_BADADDR;
2N/A goto cleanup_data;
2N/A }
2N/A krb5_free_addresses(context, our_addrs);
2N/A }
2N/A }
2N/A
2N/A replaydata->timestamp = privmsg_enc_part->timestamp;
2N/A replaydata->usec = privmsg_enc_part->usec;
2N/A replaydata->seq = privmsg_enc_part->seq_number;
2N/A
2N/A /* everything is ok - return data to the user */
2N/A *outbuf = privmsg_enc_part->user_data;
2N/A retval = 0;
2N/A
2N/Acleanup_data:;
2N/A if (retval == 0)
2N/A privmsg_enc_part->user_data.data = 0;
2N/A krb5_free_priv_enc_part(context, privmsg_enc_part);
2N/A
2N/Acleanup_scratch:;
2N/A memset(scratch.data, 0, scratch.length);
2N/A free(scratch.data);
2N/A
2N/Acleanup_privmsg:;
2N/A free(privmsg->enc_part.ciphertext.data);
2N/A free(privmsg);
2N/A
2N/A return retval;
2N/A}
2N/A
2N/Akrb5_error_code KRB5_CALLCONV
2N/Akrb5_rd_priv(krb5_context context, krb5_auth_context auth_context,
2N/A const krb5_data *inbuf, krb5_data *outbuf,
2N/A krb5_replay_data *outdata)
2N/A{
2N/A krb5_error_code retval;
2N/A krb5_key key;
2N/A krb5_replay_data replaydata;
2N/A
2N/A /* Get key */
2N/A if ((key = auth_context->recv_subkey) == NULL)
2N/A key = auth_context->key;
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 return KRB5_RC_REQUIRED;
2N/A
2N/A if (!auth_context->remote_addr)
2N/A return KRB5_REMOTE_ADDR_REQUIRED;
2N/A
2N/A if ((auth_context->auth_context_flags & KRB5_AUTH_CONTEXT_DO_TIME) &&
2N/A (auth_context->rcache == NULL))
2N/A return KRB5_RC_REQUIRED;
2N/A
2N/A {
2N/A krb5_address * premote_fulladdr;
2N/A krb5_address * plocal_fulladdr = NULL;
2N/A krb5_address remote_fulladdr;
2N/A krb5_address local_fulladdr;
2N/A CLEANUP_INIT(2);
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 CLEANUP_PUSH(local_fulladdr.contents, free);
2N/A plocal_fulladdr = &local_fulladdr;
2N/A } else {
2N/A return retval;
2N/A }
2N/A } else {
2N/A plocal_fulladdr = auth_context->local_addr;
2N/A }
2N/A }
2N/A
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 CLEANUP_PUSH(remote_fulladdr.contents, free);
2N/A premote_fulladdr = &remote_fulladdr;
2N/A } else {
2N/A CLEANUP_DONE();
2N/A return retval;
2N/A }
2N/A } else {
2N/A premote_fulladdr = auth_context->remote_addr;
2N/A }
2N/A
2N/A memset(&replaydata, 0, sizeof(replaydata));
2N/A if ((retval = krb5_rd_priv_basic(context, inbuf, key,
2N/A plocal_fulladdr,
2N/A premote_fulladdr,
2N/A auth_context->i_vector,
2N/A &replaydata, outbuf))) {
2N/A CLEANUP_DONE();
2N/A return retval;
2N/A }
2N/A
2N/A CLEANUP_DONE();
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 = krb5int_check_clockskew(context, replaydata.timestamp)))
2N/A goto error;
2N/A
2N/A if ((retval = krb5_gen_replay_name(context, auth_context->remote_addr,
2N/A "_priv", &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 free(replay.client);
2N/A goto error;
2N/A }
2N/A free(replay.client);
2N/A }
2N/A
2N/A if (auth_context->auth_context_flags & KRB5_AUTH_CONTEXT_DO_SEQUENCE) {
2N/A if (!krb5int_auth_con_chkseqnum(context, auth_context,
2N/A replaydata.seq)) {
2N/A retval = KRB5KRB_AP_ERR_BADORDER;
2N/A goto error;
2N/A }
2N/A auth_context->remote_seq_number++;
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->timestamp = replaydata.timestamp;
2N/A outdata->usec = replaydata.usec;
2N/A outdata->seq = replaydata.seq;
2N/A }
2N/A
2N/A /* everything is ok - return data to the user */
2N/A return 0;
2N/A
2N/Aerror:;
2N/A free(outbuf->data);
2N/A outbuf->length = 0;
2N/A outbuf->data = NULL;
2N/A
2N/A return retval;
2N/A}