/*
* Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved.
*/
/*
* Copyright 2000 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 (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.
*/
#include "gssapiP_krb5.h"
#ifdef HAVE_STRING_H
#include <string.h>
#else
#include <strings.h>
#endif
#include <locale.h> /* Solaris Kerberos */
/*
* $Id: add_cred.c 18396 2006-07-25 20:29:43Z lxs $
*/
/* V2 interface */
OM_uint32
krb5_gss_add_cred(minor_status, input_cred_handle,
desired_name, desired_mech, cred_usage,
initiator_time_req, acceptor_time_req,
output_cred_handle, actual_mechs,
initiator_time_rec, acceptor_time_rec)
OM_uint32 *minor_status;
gss_cred_id_t input_cred_handle;
gss_name_t desired_name;
gss_OID desired_mech;
gss_cred_usage_t cred_usage;
OM_uint32 initiator_time_req;
OM_uint32 acceptor_time_req;
gss_cred_id_t *output_cred_handle;
gss_OID_set *actual_mechs;
OM_uint32 *initiator_time_rec;
OM_uint32 *acceptor_time_rec;
{
krb5_context context;
OM_uint32 major_status, lifetime;
krb5_gss_cred_id_t cred;
krb5_error_code code;
/* this is pretty simple, since there's not really any difference
between the underlying mechanisms. The main hair is in copying
a mechanism if requested. */
/* check if the desired_mech is bogus */
if (!g_OID_equal(desired_mech, gss_mech_krb5) &&
!g_OID_equal(desired_mech, gss_mech_krb5_old)) {
*minor_status = 0;
return(GSS_S_BAD_MECH);
}
/* check if the desired_mech is bogus */
if ((cred_usage != GSS_C_INITIATE) &&
(cred_usage != GSS_C_ACCEPT) &&
(cred_usage != GSS_C_BOTH)) {
*minor_status = (OM_uint32) G_BAD_USAGE;
return(GSS_S_FAILURE);
}
/* since the default credential includes all the mechanisms,
return an error for that case. */
/*SUPPRESS 29*/
if (input_cred_handle == GSS_C_NO_CREDENTIAL) {
*minor_status = 0;
return(GSS_S_DUPLICATE_ELEMENT);
}
code = krb5_gss_init_context(&context);
if (code) {
*minor_status = code;
return GSS_S_FAILURE;
}
major_status = krb5_gss_validate_cred_1(minor_status, input_cred_handle,
context);
if (GSS_ERROR(major_status)) {
save_error_info(*minor_status, context);
krb5_free_context(context);
return major_status;
}
cred = (krb5_gss_cred_id_t) input_cred_handle;
k5_mutex_assert_locked(&cred->lock);
/* check if the cred_usage is equal or "less" than the passed-in cred
if copying */
if (!((cred->usage == cred_usage) ||
((cred->usage == GSS_C_BOTH) &&
(output_cred_handle != NULL)))) {
*minor_status = (OM_uint32) G_BAD_USAGE;
krb5_free_context(context);
return(GSS_S_FAILURE);
}
/* check that desired_mech isn't already in the credential */
if ((g_OID_equal(desired_mech, gss_mech_krb5_old) && cred->prerfc_mech) ||
(g_OID_equal(desired_mech, gss_mech_krb5) && cred->rfc_mech)) {
*minor_status = 0;
krb5_free_context(context);
return(GSS_S_DUPLICATE_ELEMENT);
}
if (GSS_ERROR(kg_sync_ccache_name(context, minor_status))) {
save_error_info(*minor_status, context);
krb5_free_context(context);
return GSS_S_FAILURE;
}
/* verify the desired_name */
/*SUPPRESS 29*/
if ((desired_name != (gss_name_t) NULL) &&
(! kg_validate_name(desired_name))) {
*minor_status = (OM_uint32) G_VALIDATE_FAILED;
krb5_free_context(context);
return(GSS_S_CALL_BAD_STRUCTURE|GSS_S_BAD_NAME);
}
/* make sure the desired_name is the same as the existing one */
if (desired_name &&
!krb5_principal_compare(context, (krb5_principal) desired_name,
cred->princ)) {
/* Solaris Kerberos: spruce-up the err msg */
krb5_principal dname = (krb5_principal) desired_name;
char *s_name = NULL, *s_princ= NULL;
int kret = krb5_unparse_name(context, dname, &s_name);
int kret1 = krb5_unparse_name(context, cred->princ, &s_princ);
*minor_status = (OM_uint32) G_BAD_USAGE;
if (kret == 0 && kret1 == 0) {
krb5_set_error_message(context, *minor_status,
dgettext(TEXT_DOMAIN,
"Desired name principal '%s' does not match '%s'"),
s_name, s_princ);
save_error_info(*minor_status, context);
}
if (s_name)
krb5_free_unparsed_name(context, s_name);
if (s_princ)
krb5_free_unparsed_name(context, s_princ);
krb5_free_context(context);
return(GSS_S_BAD_NAME);
}
/* copy the cred if necessary */
if (output_cred_handle) {
/* make a copy */
krb5_gss_cred_id_t new_cred;
char *kttype, ktboth[1024];
const char *cctype, *ccname;
char ccboth[1024];
if ((new_cred =
(krb5_gss_cred_id_t) xmalloc(sizeof(krb5_gss_cred_id_rec)))
== NULL) {
*minor_status = ENOMEM;
krb5_free_context(context);
return(GSS_S_FAILURE);
}
memset(new_cred, 0, sizeof(krb5_gss_cred_id_rec));
new_cred->usage = cred_usage;
new_cred->prerfc_mech = cred->prerfc_mech;
new_cred->rfc_mech = cred->rfc_mech;
new_cred->tgt_expire = cred->tgt_expire;
if (cred->princ)
code = krb5_copy_principal(context, cred->princ, &new_cred->princ);
if (code) {
xfree(new_cred);
*minor_status = code;
save_error_info(*minor_status, context);
krb5_free_context(context);
return(GSS_S_FAILURE);
}
if (cred->keytab) {
kttype = krb5_kt_get_type(context, cred->keytab);
if ((strlen(kttype)+2) > sizeof(ktboth)) {
if (new_cred->princ)
krb5_free_principal(context, new_cred->princ);
xfree(new_cred);
*minor_status = ENOMEM;
krb5_free_context(context);
return(GSS_S_FAILURE);
}
strncpy(ktboth, kttype, sizeof(ktboth) - 1);
ktboth[sizeof(ktboth) - 1] = '\0';
strncat(ktboth, ":", sizeof(ktboth) - 1 - strlen(ktboth));
code = krb5_kt_get_name(context, cred->keytab,
ktboth+strlen(ktboth),
sizeof(ktboth)-strlen(ktboth));
if (code) {
if(new_cred->princ)
krb5_free_principal(context, new_cred->princ);
xfree(new_cred);
*minor_status = code;
save_error_info(*minor_status, context);
krb5_free_context(context);
return(GSS_S_FAILURE);
}
code = krb5_kt_resolve(context, ktboth, &new_cred->keytab);
if (code) {
if (new_cred->princ)
krb5_free_principal(context, new_cred->princ);
xfree(new_cred);
*minor_status = code;
save_error_info(*minor_status, context);
krb5_free_context(context);
return(GSS_S_FAILURE);
}
} else {
new_cred->keytab = NULL;
}
if (cred->rcache) {
/* Open the replay cache for this principal. */
if ((code = krb5_get_server_rcache(context,
krb5_princ_component(context, cred->princ, 0),
&new_cred->rcache))) {
if (new_cred->keytab)
krb5_kt_close(context, new_cred->keytab);
if (new_cred->princ)
krb5_free_principal(context, new_cred->princ);
xfree(new_cred);
*minor_status = code;
save_error_info(*minor_status, context);
krb5_free_context(context);
return(GSS_S_FAILURE);
}
} else {
new_cred->rcache = NULL;
}
if (cred->ccache) {
cctype = krb5_cc_get_type(context, cred->ccache);
ccname = krb5_cc_get_name(context, cred->ccache);
if ((strlen(cctype)+strlen(ccname)+2) > sizeof(ccboth)) {
if (new_cred->rcache)
krb5_rc_close(context, new_cred->rcache);
if (new_cred->keytab)
krb5_kt_close(context, new_cred->keytab);
if (new_cred->princ)
krb5_free_principal(context, new_cred->princ);
xfree(new_cred);
krb5_free_context(context);
*minor_status = ENOMEM;
return(GSS_S_FAILURE);
}
strncpy(ccboth, cctype, sizeof(ccboth) - 1);
ccboth[sizeof(ccboth) - 1] = '\0';
strncat(ccboth, ":", sizeof(ccboth) - 1 - strlen(ccboth));
strncat(ccboth, ccname, sizeof(ccboth) - 1 - strlen(ccboth));
code = krb5_cc_resolve(context, ccboth, &new_cred->ccache);
if (code) {
if (new_cred->rcache)
krb5_rc_close(context, new_cred->rcache);
if (new_cred->keytab)
krb5_kt_close(context, new_cred->keytab);
if (new_cred->princ)
krb5_free_principal(context, new_cred->princ);
xfree(new_cred);
*minor_status = code;
save_error_info(*minor_status, context);
krb5_free_context(context);
return(GSS_S_FAILURE);
}
} else {
new_cred->ccache = NULL;
}
/* intern the credential handle */
if (! kg_save_cred_id((gss_cred_id_t) new_cred)) {
if (new_cred->ccache)
krb5_cc_close(context, new_cred->ccache);
if (new_cred->rcache)
krb5_rc_close(context, new_cred->rcache);
if (new_cred->keytab)
krb5_kt_close(context, new_cred->keytab);
if (new_cred->princ)
krb5_free_principal(context, new_cred->princ);
xfree(new_cred);
krb5_free_context(context);
*minor_status = (OM_uint32) G_VALIDATE_FAILED;
return(GSS_S_FAILURE);
}
/* modify new_cred */
cred = new_cred;
}
/* set the flag for the new mechanism */
if (g_OID_equal(desired_mech, gss_mech_krb5_old))
cred->prerfc_mech = 1;
else if (g_OID_equal(desired_mech, gss_mech_krb5))
cred->rfc_mech = 1;
/* set the outputs */
if (GSS_ERROR(major_status = krb5_gss_inquire_cred(minor_status,
(gss_cred_id_t)cred,
NULL, &lifetime,
NULL, actual_mechs))) {
OM_uint32 dummy;
if (output_cred_handle)
(void) krb5_gss_release_cred(&dummy, (gss_cred_id_t *) &cred);
krb5_free_context(context);
return(major_status);
}
if (initiator_time_rec)
*initiator_time_rec = lifetime;
if (acceptor_time_rec)
*acceptor_time_rec = lifetime;
if (output_cred_handle)
*output_cred_handle = (gss_cred_id_t)cred;
krb5_free_context(context);
*minor_status = 0;
return(GSS_S_COMPLETE);
}