Home | History | Annotate | Download | only in mech
      1 /*
      2  * Copyright 2000 by the Massachusetts Institute of Technology.
      3  * All Rights Reserved.
      4  *
      5  * Export of this software from the United States of America may
      6  *   require a specific license from the United States Government.
      7  *   It is the responsibility of any person or organization contemplating
      8  *   export to obtain such a license before exporting.
      9  *
     10  * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
     11  * distribute this software and its documentation for any purpose and
     12  * without fee is hereby granted, provided that the above copyright
     13  * notice appear in all copies and that both that copyright notice and
     14  * this permission notice appear in supporting documentation, and that
     15  * the name of M.I.T. not be used in advertising or publicity pertaining
     16  * to distribution of the software without specific, written prior
     17  * permission.  Furthermore if you modify this software you must label
     18  * your software as modified software and not distribute it in such a
     19  * fashion that it might be confused with the original M.I.T. software.
     20  * M.I.T. makes no representations about the suitability of
     21  * this software for any purpose.  It is provided "as is" without express
     22  * or implied warranty.
     23  *
     24  */
     25 /*
     26  * Copyright (C) 1998 by the FundsXpress, INC.
     27  *
     28  * All rights reserved.
     29  *
     30  * Export of this software from the United States of America may require
     31  * a specific license from the United States Government.  It is the
     32  * responsibility of any person or organization contemplating export to
     33  * obtain such a license before exporting.
     34  *
     35  * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
     36  * distribute this software and its documentation for any purpose and
     37  * without fee is hereby granted, provided that the above copyright
     38  * notice appear in all copies and that both that copyright notice and
     39  * this permission notice appear in supporting documentation, and that
     40  * the name of FundsXpress. not be used in advertising or publicity pertaining
     41  * to distribution of the software without specific, written prior
     42  * permission.  FundsXpress makes no representations about the suitability of
     43  * this software for any purpose.  It is provided "as is" without express
     44  * or implied warranty.
     45  *
     46  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
     47  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
     48  * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
     49  */
     50 
     51 #include "gssapiP_krb5.h"
     52 #ifdef HAVE_STRING_H
     53 #include <string.h>
     54 #else
     55 #include <strings.h>
     56 #endif
     57 
     58 /*
     59  * $Id: add_cred.c 18396 2006-07-25 20:29:43Z lxs $
     60  */
     61 
     62 /* V2 interface */
     63 OM_uint32
     64 krb5_gss_add_cred(minor_status, input_cred_handle,
     65 		  desired_name, desired_mech, cred_usage,
     66 		  initiator_time_req, acceptor_time_req,
     67 		  output_cred_handle, actual_mechs,
     68 		  initiator_time_rec, acceptor_time_rec)
     69     OM_uint32		*minor_status;
     70     gss_cred_id_t	input_cred_handle;
     71     gss_name_t		desired_name;
     72     gss_OID		desired_mech;
     73     gss_cred_usage_t	cred_usage;
     74     OM_uint32		initiator_time_req;
     75     OM_uint32		acceptor_time_req;
     76     gss_cred_id_t	*output_cred_handle;
     77     gss_OID_set		*actual_mechs;
     78     OM_uint32		*initiator_time_rec;
     79     OM_uint32		*acceptor_time_rec;
     80 {
     81     krb5_context	context;
     82     OM_uint32		major_status, lifetime;
     83     krb5_gss_cred_id_t	cred;
     84     krb5_error_code	code;
     85 
     86     /* this is pretty simple, since there's not really any difference
     87        between the underlying mechanisms.  The main hair is in copying
     88        a mechanism if requested. */
     89 
     90     /* check if the desired_mech is bogus */
     91 
     92     if (!g_OID_equal(desired_mech, gss_mech_krb5) &&
     93 	!g_OID_equal(desired_mech, gss_mech_krb5_old)) {
     94 	*minor_status = 0;
     95 	return(GSS_S_BAD_MECH);
     96     }
     97 
     98     /* check if the desired_mech is bogus */
     99 
    100     if ((cred_usage != GSS_C_INITIATE) &&
    101 	(cred_usage != GSS_C_ACCEPT) &&
    102 	(cred_usage != GSS_C_BOTH)) {
    103 	*minor_status = (OM_uint32) G_BAD_USAGE;
    104 	return(GSS_S_FAILURE);
    105     }
    106 
    107     /* since the default credential includes all the mechanisms,
    108        return an error for that case. */
    109 
    110     /*SUPPRESS 29*/
    111     if (input_cred_handle == GSS_C_NO_CREDENTIAL) {
    112 	*minor_status = 0;
    113 	return(GSS_S_DUPLICATE_ELEMENT);
    114     }
    115 
    116     code = krb5_gss_init_context(&context);
    117     if (code) {
    118 	*minor_status = code;
    119 	return GSS_S_FAILURE;
    120     }
    121 
    122     major_status = krb5_gss_validate_cred_1(minor_status, input_cred_handle,
    123 					    context);
    124     if (GSS_ERROR(major_status)) {
    125 	krb5_free_context(context);
    126 	return major_status;
    127     }
    128 
    129     cred = (krb5_gss_cred_id_t) input_cred_handle;
    130     k5_mutex_assert_locked(&cred->lock);
    131 
    132     /* check if the cred_usage is equal or "less" than the passed-in cred
    133        if copying */
    134 
    135     if (!((cred->usage == cred_usage) ||
    136 	  ((cred->usage == GSS_C_BOTH) &&
    137 	   (output_cred_handle != NULL)))) {
    138       *minor_status = (OM_uint32) G_BAD_USAGE;
    139       krb5_free_context(context);
    140       return(GSS_S_FAILURE);
    141     }
    142 
    143     /* check that desired_mech isn't already in the credential */
    144 
    145     if ((g_OID_equal(desired_mech, gss_mech_krb5_old) && cred->prerfc_mech) ||
    146 	(g_OID_equal(desired_mech, gss_mech_krb5) && cred->rfc_mech)) {
    147 	*minor_status = 0;
    148 	krb5_free_context(context);
    149 	return(GSS_S_DUPLICATE_ELEMENT);
    150     }
    151 
    152     if (GSS_ERROR(kg_sync_ccache_name(context, minor_status))) {
    153 	krb5_free_context(context);
    154 	return GSS_S_FAILURE;
    155     }
    156 
    157     /* verify the desired_name */
    158 
    159     /*SUPPRESS 29*/
    160     if ((desired_name != (gss_name_t) NULL) &&
    161 	(! kg_validate_name(desired_name))) {
    162 	*minor_status = (OM_uint32) G_VALIDATE_FAILED;
    163 	krb5_free_context(context);
    164 	return(GSS_S_CALL_BAD_STRUCTURE|GSS_S_BAD_NAME);
    165     }
    166 
    167     /* make sure the desired_name is the same as the existing one */
    168 
    169     if (desired_name &&
    170 	!krb5_principal_compare(context, (krb5_principal) desired_name,
    171 				cred->princ)) {
    172 	*minor_status = 0;
    173 	krb5_free_context(context);
    174 	return(GSS_S_BAD_NAME);
    175     }
    176 
    177     /* copy the cred if necessary */
    178 
    179     if (output_cred_handle) {
    180 	/* make a copy */
    181 	krb5_gss_cred_id_t new_cred;
    182 	char *kttype, ktboth[1024];
    183 	const char *cctype, *ccname;
    184 	char ccboth[1024];
    185 
    186 	if ((new_cred =
    187 	     (krb5_gss_cred_id_t) xmalloc(sizeof(krb5_gss_cred_id_rec)))
    188 	    == NULL) {
    189 	    *minor_status = ENOMEM;
    190 	    krb5_free_context(context);
    191 	    return(GSS_S_FAILURE);
    192 	}
    193 	memset(new_cred, 0, sizeof(krb5_gss_cred_id_rec));
    194 
    195 	new_cred->usage = cred_usage;
    196 	new_cred->prerfc_mech = cred->prerfc_mech;
    197 	new_cred->rfc_mech = cred->rfc_mech;
    198 	new_cred->tgt_expire = cred->tgt_expire;
    199 
    200 	if (cred->princ)
    201 	    code = krb5_copy_principal(context, cred->princ, &new_cred->princ);
    202 	if (code) {
    203 	    xfree(new_cred);
    204 
    205 	    *minor_status = code;
    206 	    krb5_free_context(context);
    207 	    return(GSS_S_FAILURE);
    208 	}
    209 
    210 	if (cred->keytab) {
    211 	    kttype = krb5_kt_get_type(context, cred->keytab);
    212 	    if ((strlen(kttype)+2) > sizeof(ktboth)) {
    213 		if (new_cred->princ)
    214 		    krb5_free_principal(context, new_cred->princ);
    215 		xfree(new_cred);
    216 
    217 		*minor_status = ENOMEM;
    218 		krb5_free_context(context);
    219 		return(GSS_S_FAILURE);
    220 	    }
    221 
    222 	    strncpy(ktboth, kttype, sizeof(ktboth) - 1);
    223 	    ktboth[sizeof(ktboth) - 1] = '\0';
    224 	    strncat(ktboth, ":", sizeof(ktboth) - 1 - strlen(ktboth));
    225 
    226 	    code = krb5_kt_get_name(context, cred->keytab,
    227 				    ktboth+strlen(ktboth),
    228 				    sizeof(ktboth)-strlen(ktboth));
    229 	    if (code) {
    230 		if(new_cred->princ)
    231 		    krb5_free_principal(context, new_cred->princ);
    232 		xfree(new_cred);
    233 
    234 		*minor_status = code;
    235 		krb5_free_context(context);
    236 		return(GSS_S_FAILURE);
    237 	    }
    238 
    239 	    code = krb5_kt_resolve(context, ktboth, &new_cred->keytab);
    240 	    if (code) {
    241 		if (new_cred->princ)
    242 		krb5_free_principal(context, new_cred->princ);
    243 		xfree(new_cred);
    244 
    245 		*minor_status = code;
    246 		krb5_free_context(context);
    247 		return(GSS_S_FAILURE);
    248 	    }
    249 	} else {
    250 	    new_cred->keytab = NULL;
    251 	}
    252 
    253 	if (cred->rcache) {
    254 	    /* Open the replay cache for this principal. */
    255 	    if ((code = krb5_get_server_rcache(context,
    256 					       krb5_princ_component(context, cred->princ, 0),
    257 					       &new_cred->rcache))) {
    258 		if (new_cred->keytab)
    259 		    krb5_kt_close(context, new_cred->keytab);
    260 		if (new_cred->princ)
    261 		    krb5_free_principal(context, new_cred->princ);
    262 		xfree(new_cred);
    263 
    264 		krb5_free_context(context);
    265 		*minor_status = code;
    266 		return(GSS_S_FAILURE);
    267 	    }
    268 	} else {
    269 	    new_cred->rcache = NULL;
    270 	}
    271 
    272 	if (cred->ccache) {
    273 	    cctype = krb5_cc_get_type(context, cred->ccache);
    274 	    ccname = krb5_cc_get_name(context, cred->ccache);
    275 
    276 	    if ((strlen(cctype)+strlen(ccname)+2) > sizeof(ccboth)) {
    277 		if (new_cred->rcache)
    278 		    krb5_rc_close(context, new_cred->rcache);
    279 		if (new_cred->keytab)
    280 		    krb5_kt_close(context, new_cred->keytab);
    281 		if (new_cred->princ)
    282 		krb5_free_principal(context, new_cred->princ);
    283 		xfree(new_cred);
    284 
    285 		krb5_free_context(context);
    286 		*minor_status = ENOMEM;
    287 		return(GSS_S_FAILURE);
    288 	    }
    289 
    290 	    strncpy(ccboth, cctype, sizeof(ccboth) - 1);
    291 	    ccboth[sizeof(ccboth) - 1] = '\0';
    292 	    strncat(ccboth, ":", sizeof(ccboth) - 1 - strlen(ccboth));
    293 	    strncat(ccboth, ccname, sizeof(ccboth) - 1 - strlen(ccboth));
    294 
    295 	    code = krb5_cc_resolve(context, ccboth, &new_cred->ccache);
    296 	    if (code) {
    297 		if (new_cred->rcache)
    298 		    krb5_rc_close(context, new_cred->rcache);
    299 		if (new_cred->keytab)
    300 		    krb5_kt_close(context, new_cred->keytab);
    301 		if (new_cred->princ)
    302 		    krb5_free_principal(context, new_cred->princ);
    303 		xfree(new_cred);
    304 		krb5_free_context(context);
    305 
    306 		*minor_status = code;
    307 		return(GSS_S_FAILURE);
    308 	    }
    309 	} else {
    310 	    new_cred->ccache = NULL;
    311 	}
    312 
    313 	/* intern the credential handle */
    314 
    315 	if (! kg_save_cred_id((gss_cred_id_t) new_cred)) {
    316 	    if (new_cred->ccache)
    317 		krb5_cc_close(context, new_cred->ccache);
    318 	    if (new_cred->rcache)
    319 		krb5_rc_close(context, new_cred->rcache);
    320 	    if (new_cred->keytab)
    321 		krb5_kt_close(context, new_cred->keytab);
    322 	    if (new_cred->princ)
    323 	    krb5_free_principal(context, new_cred->princ);
    324 	    xfree(new_cred);
    325 	    krb5_free_context(context);
    326 
    327 	    *minor_status = (OM_uint32) G_VALIDATE_FAILED;
    328 	    return(GSS_S_FAILURE);
    329 	}
    330 
    331 	/* modify new_cred */
    332 
    333 	cred = new_cred;
    334     }
    335 
    336     /* set the flag for the new mechanism */
    337 
    338     if (g_OID_equal(desired_mech, gss_mech_krb5_old))
    339 	cred->prerfc_mech = 1;
    340     else if (g_OID_equal(desired_mech, gss_mech_krb5))
    341 	cred->rfc_mech = 1;
    342 
    343     /* set the outputs */
    344 
    345     if (GSS_ERROR(major_status = krb5_gss_inquire_cred(minor_status,
    346 						       (gss_cred_id_t)cred,
    347 						       NULL, &lifetime,
    348 						       NULL, actual_mechs))) {
    349 	OM_uint32 dummy;
    350 
    351 	if (output_cred_handle)
    352 	    (void) krb5_gss_release_cred(&dummy, (gss_cred_id_t *) &cred);
    353 	krb5_free_context(context);
    354 
    355 	return(major_status);
    356     }
    357 
    358     if (initiator_time_rec)
    359 	*initiator_time_rec = lifetime;
    360     if (acceptor_time_rec)
    361 	*acceptor_time_rec = lifetime;
    362 
    363     if (output_cred_handle)
    364 	*output_cred_handle = (gss_cred_id_t)cred;
    365 
    366     krb5_free_context(context);
    367     *minor_status = 0;
    368     return(GSS_S_COMPLETE);
    369 }
    370