init_sec_context.c revision 5e01956f3000408c2a2c5a08c8d0acf2c2a9d8ee
/*
*/
/*
* Copyright 2000,2002, 2003 by the Massachusetts Institute of Technology.
* All Rights Reserved.
*
* Export of this software from the United States of America may
* require a specific license from the United States Government.
* It is the responsibility of any person or organization contemplating
* export to obtain such a license before exporting.
*
* WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
* distribute this software and its documentation for any purpose and
* without fee is hereby granted, provided that the above copyright
* notice appear in all copies and that both that copyright notice and
* this permission notice appear in supporting documentation, and that
* the name of M.I.T. not be used in advertising or publicity pertaining
* to distribution of the software without specific, written prior
* permission. Furthermore if you modify this software you must label
* your software as modified software and not distribute it in such a
* fashion that it might be confused with the original M.I.T. software.
* M.I.T. makes no representations about the suitability of
* this software for any purpose. It is provided "as is" without express
* or implied warranty.
*
*/
/*
* Copyright 1993 by OpenVision Technologies, Inc.
*
* Permission to use, copy, modify, distribute, and sell this software
* and its documentation for any purpose is hereby granted without fee,
* provided that the above copyright notice appears in all copies and
* that both that copyright notice and this permission notice appear in
* supporting documentation, and that the name of OpenVision not be used
* in advertising or publicity pertaining to distribution of the software
* without specific, written prior permission. OpenVision makes no
* representations about the suitability of this software for any
* purpose. It is provided "as is" without express or implied warranty.
*
* OPENVISION DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
* INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
* EVENT SHALL OPENVISION BE LIABLE FOR ANY SPECIAL, INDIRECT OR
* CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF
* USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
/*
* Copyright (C) 1998 by the FundsXpress, INC.
*
* All rights reserved.
*
* Export of this software from the United States of America may require
* a specific license from the United States Government. It is the
* responsibility of any person or organization contemplating export to
* obtain such a license before exporting.
*
* WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
* distribute this software and its documentation for any purpose and
* without fee is hereby granted, provided that the above copyright
* notice appear in all copies and that both that copyright notice and
* this permission notice appear in supporting documentation, and that
* the name of FundsXpress. not be used in advertising or publicity pertaining
* to distribution of the software without specific, written prior
* permission. FundsXpress makes no representations about the suitability of
* this software for any purpose. It is provided "as is" without express
* or implied warranty.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
* WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
*/
/* Solaris Kerberos */
#include <libintl.h>
#include <locale.h>
#include "k5-int.h"
#include "gss_libinit.h"
#include "gssapiP_krb5.h"
#include "mglueP.h"
#ifdef HAVE_MEMORY_H
#include <memory.h>
#endif
#include <stdlib.h>
#include <assert.h>
/* Solaris Kerberos start */
/* Solaris Kerberos end */
/*
* $Id: init_sec_context.c 18721 2006-10-16 16:18:29Z epeisach $
*/
/* XXX This is for debugging only!!! Should become a real bitfield
at some point */
int krb5_gss_dbg_client_expcreds = 0;
/*
* Common code which fetches the correct krb5 credentials from the
* ccache.
*/
{
goto cleanup;
goto cleanup;
if (code)
goto cleanup;
/*
* Enforce a stricter limit (without timeskew forgiveness at the
* boundaries) because accept_sec_context code is also similarly
* non-forgiving.
*/
goto cleanup;
}
return code;
}
struct gss_checksum_data {
};
#ifdef CFX_EXERCISE
#include "../../krb5/krb/auth_con.h"
#endif
static krb5_error_code KRB5_CALLCONV
{
unsigned char *ptr;
unsigned int junk;
/* build the checksum field */
/* first get KRB_CRED message, so we know its length */
/* clear the time check flag that was set in krb5_auth_con_init() */
&credmsg);
/* turn KRB5_AUTH_CONTEXT_DO_TIME back on */
if (code) {
request */
} else {
return(KRB5KRB_ERR_FIELD_TOOLONG);
}
}
} else {
}
#ifdef CFX_EXERCISE
/* Our ftp client code stupidly assumes a base64-encoded
version of the token will fit in 10K, so don't make this
too big. */
} else
junk = 0;
#else
junk = 0;
#endif
/* now allocate a buffer to hold the checksum data and
(maybe) KRB_CRED msg */
return(ENOMEM);
}
/* Solaris Kerberos */
/* done with this, free it */
/* free credmsg data */
}
if (junk)
return 0;
}
static krb5_error_code
{
krb5_flags mk_req_flags = 0;
struct gss_checksum_data cksum_struct;
unsigned char *ptr;
unsigned char *t;
unsigned int tlen;
/* compute the hash of the channel bindings */
return(code);
case ENCTYPE_DES_CBC_CRC:
case ENCTYPE_DES_CBC_MD4:
case ENCTYPE_DES_CBC_MD5:
case ENCTYPE_DES3_CBC_SHA1:
if (code)
goto cleanup;
break;
default:
break;
}
/* call mk_req. subkey and ap_req need to be used or destroyed */
if (code)
goto cleanup;
/* store the interesting stuff from creds and authent */
/* build up the token */
/* allocate space for the token */
goto cleanup;
}
/* fill in the buffer */
ptr = t;
&ptr, KG_TOK_CTX_AP_REQ);
/* pass it back */
code = 0;
return (code);
}
/*
* setup_enc
*
* Fill in the encryption descriptors. Called after AP-REQ is made.
*/
static OM_uint32
{
int i;
if (code)
goto fail;
ctx->have_acceptor_subkey = 0;
case ENCTYPE_DES_CBC_MD5:
case ENCTYPE_DES_CBC_MD4:
case ENCTYPE_DES_CBC_CRC:
/* The encryption key is the session key XOR
0xf0f0f0f0f0f0f0f0. */
goto fail;
goto copy_subkey_to_seq;
case ENCTYPE_DES3_CBC_SHA1:
/* MIT extension */
if (code)
goto fail;
if (code) {
goto fail;
}
goto success;
case ENCTYPE_ARCFOUR_HMAC:
/* Microsoft extension */
goto copy_subkey;
default:
/* Fill some fields we shouldn't be using on this path
with garbage. */
if (code)
goto fail;
&ctx->cksum_size);
if (code)
goto fail;
goto copy_subkey;
}
fail:
/* SUNW15resync - (as in prev snv code) add if-code and success label fix */
if (code) {
*minor_status = code;
return GSS_S_FAILURE;
}
return (GSS_S_COMPLETE);
}
/*
* new_connection
*
* Do the grunt work of setting up a new context.
*/
static OM_uint32
int default_mech)
{
/* make sure the cred is usable for init */
*minor_status = 0;
return(GSS_S_NO_CRED);
}
/* complain if the input token is non-null */
*minor_status = 0;
return(GSS_S_DEFECTIVE_TOKEN);
}
/* create the ctx */
== NULL) {
*minor_status = ENOMEM;
return(GSS_S_FAILURE);
}
/* fill in the ctx */
goto fail;
/* limit the encryption types negotiated (if requested) */
if (cred->req_enctypes) {
cred->req_enctypes))) {
goto fail;
}
}
goto fail;
} else {
}
goto fail;
goto fail;
if (code)
goto fail;
if (default_mech) {
}
!= GSS_S_COMPLETE) {
code = *minor_status;
goto fail;
}
/*
* Now try to make it static if at all possible....
*/
{
/* gsskrb5 v1 */
(code == KG_EMPTY_CCACHE))
if (code == KRB5KRB_AP_ERR_TKT_EXPIRED)
goto fail;
}
}
if (k_cred) {
k_cred = 0;
}
/* at this point, the context is constructed and valid,
hence, releaseable */
/* intern the context handle */
goto fail;
}
ctx_free = 0;
/* compute time_rec */
if (time_rec) {
goto fail;
}
/* set the other returns */
*output_token = token;
if (ret_flags)
if (actual_mech_type)
/* return successfully */
*minor_status = 0;
ctx->established = 0;
return(GSS_S_CONTINUE_NEEDED);
} else {
return(GSS_S_COMPLETE);
}
fail:
if (ctx_free) {
if (ctx_free->auth_context)
} else
*minor_status = code;
return (major_status);
}
/*
* mutual_auth
*
* Handle the reply from the acceptor, if we're doing mutual auth.
*/
static OM_uint32
{
unsigned char *ptr;
char *sptr;
if (code)
goto fail;
/* validate the context handle */
/*SUPPRESS 29*/
if (! kg_validate_ctx_id(*context_handle)) {
return(GSS_S_NO_CONTEXT);
}
/* make sure the context is non-established, and that certain
arguments are unchanged */
if ((ctx->established) ||
goto fail;
}
(krb5_principal) target_name)) {
/* Solaris Kerberos: spruce-up the err msg */
"Target name principal '%s' does not match '%s'"),
}
if (s_name)
if (s_princ)
goto fail;
}
/* verify the token and leave the AP_REP message in ap_rep */
if (input_token == GSS_C_NO_BUFFER) {
code = 0;
goto fail;
}
/* Handle a KRB_ERROR message from the server */
if (code)
goto fail;
else
code = 0;
goto fail;
} else {
*minor_status = 0;
return(GSS_S_DEFECTIVE_TOKEN);
}
}
/* decode the ap_rep */
&ap_rep_data))) {
/*
* XXX A hack for backwards compatiblity.
* To be removed in 1999 -- proven
*/
&ap_rep_data)))
goto fail;
}
/* store away the sequence number */
/* Keep acceptor's subkey. */
&ctx->acceptor_subkey);
if (code)
goto fail;
if (code)
goto fail;
}
/* free the ap_rep_data */
/* set established */
/* set returns */
if (time_rec) {
goto fail;
}
if (ret_flags)
if (actual_mech_type)
/* success */
*minor_status = 0;
return GSS_S_COMPLETE;
fail:
*minor_status = code;
return (major_status);
}
{
int err;
int default_mech = 0;
if (*context_handle == GSS_C_NO_CONTEXT) {
if (kerr) {
*minor_status = kerr;
return GSS_S_FAILURE;
}
return GSS_S_FAILURE;
}
} else {
}
/* set up return values so they can be "freed" successfully */
output_token->length = 0;
if (actual_mech_type)
*actual_mech_type = NULL;
/* verify that the target_name is valid and usable */
if (! kg_validate_name(target_name)) {
/* Solaris Kerberos: spruce-up the err msg */
if (kret == 0) {
"Target name principal '%s' is invalid"),
s_name);
}
if (*context_handle == GSS_C_NO_CONTEXT)
return(GSS_S_CALL_BAD_STRUCTURE|GSS_S_BAD_NAME);
}
/* verify the credential, or use the default */
/*SUPPRESS 29*/
if (claimant_cred_handle == GSS_C_NO_CREDENTIAL) {
/*
* Solaris Kerberos: here we are using the Solaris specific
* function get_default_cred() to handle the special case of a
* root principal
*/
(gss_cred_id_t *)&cred);
if (*context_handle == GSS_C_NO_CONTEXT)
return(major_status);
}
} else {
if (GSS_ERROR(major_status)) {
if (*context_handle == GSS_C_NO_CONTEXT)
return(major_status);
}
}
if (kerr) {
*minor_status = kerr;
return GSS_S_FAILURE;
}
/* verify the mech_type */
err = 0;
if (mech_type == GSS_C_NULL_OID) {
default_mech = 1;
} else if (cred->prerfc_mech) {
} else {
err = 1;
}
err = 1;
if (!cred->prerfc_mech)
err = 1;
err = 1;
} else {
err = 1;
}
if (err) {
*minor_status = 0;
if (*context_handle == GSS_C_NO_CONTEXT)
return(GSS_S_BAD_MECH);
}
/* is this a new connection or not? */
/*SUPPRESS 29*/
if (*context_handle == GSS_C_NO_CONTEXT) {
if (*context_handle == GSS_C_NO_CONTEXT) {
} else
} else {
/* mutual_auth doesn't care about the credentials */
context);
/* If context_handle is now NO_CONTEXT, mutual_auth called
delete_sec_context, which would've zapped the krb5 context
too. */
}
return(major_status);
}
#ifndef _WIN32
static int kdc_flag = 0;
#endif
{
#ifndef _WIN32
int is_kdc;
#endif
if (err)
return err;
#ifndef _WIN32
if (err)
return err;
if (is_kdc)
return krb5int_init_context_kdc(ctxp);
#endif
return krb5_init_context(ctxp);
}
#ifndef _WIN32
{
if (err)
return err;
if (err)
return err;
kdc_flag = 1;
return 0;
}
#endif
/* Solaris Kerberos specific routines start */
#define ROOT_UID 0
#define CACHE_FILENAME_LEN 35
extern int
static krb5_boolean
{
return FALSE;
/*
* Solaris Kerberos:
* Don't bother to compare the realms as princ1 will always have a
* referral realm set.
*/
/*
* Solaris Kerberos
*/
if (nelem == 2) {
if (p->length == 1) {
const char *s = p->data;
if (s[0] == '*') {
return FALSE;
return TRUE;
}
}
}
return FALSE;
}
/*
* Solaris Kerberos
* This is a dup of krb5_ktfile_get_entry (sigh) but is necessary to
* to get a custom princ compare above (principal_ignore_inst_compare)
* api (krb5_principal_compare)
*/
static krb5_error_code KRB5_CALLCONV
{
krb5_error_code kerror = 0;
int found_wrong_kvno = 0;
int kvno_offset = 0;
/* Open the keyfile for reading */
"kerror= %d\n", kerror);
return(kerror);
}
/*
* For efficiency and simplicity, we'll use a while true that
* is exited with a break statement.
*/
/*CONSTCOND*/
while (TRUE) {
break;
/*
* by the time this loop exits, it must either free cur_entry,
* and copy new_entry there, or free new_entry. Otherwise, it
* leaks.
*/
/*
* if the principal isn't the one requested, free new_entry
* and continue to the next.
*/
continue;
}
/*
* if the enctype is not ignored and doesn't match, free new_entry
* and continue to the next
*/
if (enctype != IGNORE_ENCTYPE) {
&similar))) {
break;
}
if (!similar) {
continue;
}
/*
* Coerce the enctype of the output keyblock in case we
* got an inexact match on the enctype.
*/
}
if (kvno == IGNORE_VNO) {
/*
* if this is the first match, or if the new vno is
* bigger, free the current and keep the new. Otherwise,
* free the new.
*/
/*
* A 1.2.x keytab contains only the low 8 bits of the key
* version number. Since it can be much bigger, and thus
* the 8-bit value can wrap, we need some heuristics to
* figure out the "highest" numbered key if some numbers
* close to 255 and some near 0 are used.
*
* The heuristic here:
* If we have any keys with versions over 240, then assume
* that all version numbers 0-127 refer to 256+N instead.
* Not perfect, but maybe good enough?
*/
kvno_offset = 128;
} else {
}
} else {
/*
* if this kvno matches, free the current (will there ever
* be one?), keep the new, and break out. Otherwise, remember
* that we were here so we can return the right error, and
* free the new
*/
/*
* Yuck. The krb5-1.2.x keytab format only stores one byte
* for the kvno, so we're toast if the kvno requested is
* higher than that. Short-term workaround: only compare
* the low 8 bits.
*/
break;
} else {
}
}
}
if (kerror == KRB5_KT_END) {
kerror = 0;
else if (found_wrong_kvno)
else
}
if (kerror) {
"%d\n", kerror);
return kerror;
}
"kerror= %d\n", kerror);
return kerror;
}
/* Let us close the file before we leave */
return 0;
}
/*
* Solaris Kerberos
* for a match of name and LOCALREALM and if found, return instance
* as a string.
*
* Caller must free returned string.
*/
static krb5_error_code
const char *sname,
char **instance) /* out */
{
register const krb5_data *p;
if (!keytab)
return EINVAL;
return (ENOMEM);
(char *)0);
if (ret)
goto out;
0, /* don't have vno available */
0, &kt_ent);
if (ret)
goto out;
free_kt_ent++; /* kt_ent is not a ptr */
if (nelem != 2) {
goto out;
}
if (!s) {
goto out;
}
out:
if (client)
if (free_kt_ent)
if (ret == 0)
*instance = s;
return ret;
}
static OM_uint32
const char *sname,
int use_nodename)
{
0,
};
if (!sname)
return (GSS_S_FAILURE);
*minor_status = code;
return (GSS_S_FAILURE);
}
if (!use_nodename) {
if (code == 0) {
KRB5_NT_UNKNOWN, &me);
}
} else {
KRB5_NT_SRV_HST, &me);
}
/* Solaris Kerberos */
if (code == 0) {
} else {
/* Try to set a useful error message */
"Failed to find realm for %s in keytab"),
if (princ)
}
}
if (code) {
*minor_status = code;
return (GSS_S_FAILURE);
}
0))) {
*minor_status = code;
return (GSS_S_FAILURE);
}
* when request
* gets to KDC
*/
*minor_status = code;
return (GSS_S_FAILURE);
}
if (code != 0) {
*minor_status = code;
return (GSS_S_FAILURE);
}
/*
* Evidently (sigh), on success, krb5_get_init_creds_keytab
* changes the my_creds princ ptrs so we need to free those
* princs (me&server) as well as freeing all of my_creds contents.
*/
if (code) {
*minor_status = code;
return (GSS_S_FAILURE);
}
&ccache);
if (code != 0) {
*minor_status = code;
return (GSS_S_FAILURE);
}
if (code != 0) {
*minor_status = code;
return (GSS_S_FAILURE);
}
&my_creds);
if (code) {
*minor_status = code;
"code = %d\n", code);
return (GSS_S_FAILURE);
}
return (GSS_S_COMPLETE);
}
static OM_uint32
{
static char ccache_name_buf[CACHE_FILENAME_LEN];
int options = 0;
0,
};
*minor_status = code;
return (GSS_S_FAILURE);
}
*minor_status = code;
return (GSS_S_FAILURE);
}
0))) {
*minor_status = code;
return (GSS_S_FAILURE);
}
return (KDC_ERR_BADOPTION);
}
creds.ticket_flags = 0;
if (code) {
*minor_status = code;
return (GSS_S_FAILURE);
}
uid, -1);
if (code == -1) {
*minor_status = code;
return (GSS_S_FAILURE);
}
return (GSS_S_COMPLETE);
}
/*
* Solaris Kerberos:
* We enforce a minimum refresh time on the root cred. This avoids problems for
* the higher level communication protocol for having valid creds and
* setting up a valid context, only to have it expire before or while
* it is being used. For non root users we don't care since we do not refresh
* there creds, they get what they can get.
*/
#define MIN_REFRESH_TIME 300
#define MIN_RENEW_TIME 1500
/* get_default_cred() must be called with the krb5_mutex lock held */
static OM_uint32
{
/*
* Solaris Kerberos
* Use krb5_getuid() to select the mechanism to obtain the uid.
*/
/* Get the default cred for user */
/* If we're not root we're done */
return (major);
/*
* Try and get root's cred in the cache using keytab.
*
* First try "root" and then try "host" - this allows
* Secure NFS to use the host principal for mounting if
* there is no root principal.
*
* Then try "host/<anything>" to match any instance (needed
* for DHCP clients).
*/
if (major != GSS_S_COMPLETE)
if (major != GSS_S_COMPLETE)
context, "host", 0);
if (major != GSS_S_COMPLETE)
return (major);
/* We should have valid tgt now in the cache, so get it. */
return (major);
}
/* We've got a gss cred handle that is a kerberos cred handle. */
/* If we can't get the time, assume the worst. */
return (GSS_S_CREDENTIALS_EXPIRED);
}
/* If root's cred has expired re-get it */
if (major != GSS_S_COMPLETE)
if (major != GSS_S_COMPLETE)
context, "host", 0);
if (major != GSS_S_COMPLETE)
return (major);
if (major != GSS_S_COMPLETE)
return (major);
/* Any body else is SOL unless we can renew their credential cache */
if ((major != GSS_S_COMPLETE) &&
(major != KDC_ERR_BADOPTION))
return (major);
if (major != GSS_S_COMPLETE)
return (major);
}
/* Otherwise we got non expired creds */
return (GSS_S_COMPLETE);
}
/* Solaris Kerberos specific routines end */