Home | History | Annotate | Download | only in mech
      1 /*
      2  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
      3  * Use is subject to license terms.
      4  */
      5 
      6 #pragma ident	"%Z%%M%	%I%	%E% SMI"
      7 
      8 /*
      9  * Copyright 2000 by the Massachusetts Institute of Technology.
     10  * All Rights Reserved.
     11  *
     12  * Export of this software from the United States of America may
     13  *   require a specific license from the United States Government.
     14  *   It is the responsibility of any person or organization contemplating
     15  *   export to obtain such a license before exporting.
     16  *
     17  * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
     18  * distribute this software and its documentation for any purpose and
     19  * without fee is hereby granted, provided that the above copyright
     20  * notice appear in all copies and that both that copyright notice and
     21  * this permission notice appear in supporting documentation, and that
     22  * the name of M.I.T. not be used in advertising or publicity pertaining
     23  * to distribution of the software without specific, written prior
     24  * permission.  Furthermore if you modify this software you must label
     25  * your software as modified software and not distribute it in such a
     26  * fashion that it might be confused with the original M.I.T. software.
     27  * M.I.T. makes no representations about the suitability of
     28  * this software for any purpose.  It is provided "as is" without express
     29  * or implied warranty.
     30  *
     31  */
     32 
     33 /*
     34  * Copyright 1993 by OpenVision Technologies, Inc.
     35  *
     36  * Permission to use, copy, modify, distribute, and sell this software
     37  * and its documentation for any purpose is hereby granted without fee,
     38  * provided that the above copyright notice appears in all copies and
     39  * that both that copyright notice and this permission notice appear in
     40  * supporting documentation, and that the name of OpenVision not be used
     41  * in advertising or publicity pertaining to distribution of the software
     42  * without specific, written prior permission. OpenVision makes no
     43  * representations about the suitability of this software for any
     44  * purpose.  It is provided "as is" without express or implied warranty.
     45  *
     46  * OPENVISION DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
     47  * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
     48  * EVENT SHALL OPENVISION BE LIABLE FOR ANY SPECIAL, INDIRECT OR
     49  * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF
     50  * USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
     51  * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
     52  * PERFORMANCE OF THIS SOFTWARE.
     53  */
     54 
     55 /*
     56  * Copyright (C) 1998 by the FundsXpress, INC.
     57  *
     58  * All rights reserved.
     59  *
     60  * Export of this software from the United States of America may require
     61  * a specific license from the United States Government.  It is the
     62  * responsibility of any person or organization contemplating export to
     63  * obtain such a license before exporting.
     64  *
     65  * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
     66  * distribute this software and its documentation for any purpose and
     67  * without fee is hereby granted, provided that the above copyright
     68  * notice appear in all copies and that both that copyright notice and
     69  * this permission notice appear in supporting documentation, and that
     70  * the name of FundsXpress. not be used in advertising or publicity pertaining
     71  * to distribution of the software without specific, written prior
     72  * permission.  FundsXpress makes no representations about the suitability of
     73  * this software for any purpose.  It is provided "as is" without express
     74  * or implied warranty.
     75  *
     76  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
     77  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
     78  * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
     79  */
     80 
     81 #include "gss_libinit.h"
     82 #include <gssapiP_krb5.h>
     83 #include <k5-int.h>
     84 
     85 #ifdef HAVE_STRING_H
     86 #include <string.h>
     87 #else
     88 #include <strings.h>
     89 #endif
     90 
     91 /*
     92  * $Id: acquire_cred.c,v 1.25.6.2 2000/05/22 20:41:32 meeroh Exp $
     93  */
     94 
     95 /* ARGSUSED */
     96 static OM_uint32
     97 acquire_accept_cred_with_pw(context, minor_status, desired_name, password, cred)
     98 krb5_context context;
     99 OM_uint32 *minor_status;
    100 krb5_principal desired_name;
    101 const gss_buffer_t password;
    102 krb5_gss_cred_id_rec *cred;
    103 {
    104 	/*
    105 	 * We could add support for this, but we'd need a "memory" based
    106 	 * keytab, which we lack support for.
    107 	 */
    108 	return (GSS_S_UNAVAILABLE);
    109 }
    110 
    111 static OM_uint32
    112 acquire_init_cred_with_pw(context, minor_status, desired_name, password, cred)
    113 krb5_context context;
    114 OM_uint32 *minor_status;
    115 krb5_principal desired_name;
    116 const gss_buffer_t password;
    117 krb5_gss_cred_id_rec *cred;
    118 {
    119 	krb5_error_code code = 0;
    120 	krb5_ccache ccache1 = NULL;
    121 	krb5_ccache ccache2 = NULL;
    122 	krb5_creds creds;
    123 	char *pw;
    124 
    125 	cred->ccache = NULL;
    126 
    127 	if (password == NULL || password->length == NULL ||
    128 	    password->value == NULL)
    129 		pw = strdup("");
    130 	else if (*((char *)password->value + (password->length - 1)) == '\0')
    131 		pw = strdup(password->value);
    132 	else {
    133 		pw = malloc(password->length + 1);
    134 		if (pw == NULL) {
    135 			code = ENOMEM;
    136 			goto out;
    137 		}
    138 		*pw = '\0';
    139 		(void) strlcat(pw, password->value, password->length + 1);
    140 	}
    141 
    142 	if (pw == NULL) {
    143 		code = ENOMEM;
    144 		goto out;
    145 	}
    146 
    147 	(void) memset(&creds, 0, sizeof (creds));
    148 
    149 	code = krb5_get_init_creds_password(context, &creds, desired_name, pw,
    150 			NULL,	/* no prompter callback */
    151 			NULL,	/* no prompter callback data */
    152 			0,	/* start time (now) */
    153 			NULL,	/* target princ; NULL -> TGS */
    154 			NULL);	/* no options; use defaults/config */
    155 
    156 	if (code)
    157 		goto out;
    158 
    159 	/* Got a TGT, now make a MEMORY ccache, stuff in the TGT */
    160 
    161 	if ((code = krb5_cc_resolve(context, "MEMORY:GSSAPI", &ccache1)))
    162 		goto out;
    163 
    164 	/*
    165 	 * Weirdness: there's no way to gen a new ccache without first
    166 	 * opening another of well-known name.  A bug in the krb5 API,
    167 	 * really which will have to be fixed in coordination with MIT.
    168 	 *
    169 	 * So we first krb5_cc_resolve() "MEMORY:GSSAPI", then we
    170 	 * krb5_cc_gen_new(), which is a macro that finds the memory
    171 	 * ccache ops from the first ccache but generates a new one.  If
    172 	 * we don't close that first ccache it will leak.
    173 	 */
    174 	ccache2 = ccache1;
    175 	if ((code = krb5_cc_gen_new(context, &ccache2)) != 0)
    176 		goto out;
    177 
    178 	(void) krb5_cc_close(context, ccache1);	    /* avoid leak; see above */
    179 
    180 	if ((code = krb5_cc_initialize(context, ccache2, creds.client)) != 0)
    181 		goto out;
    182 
    183 	if ((code = krb5_cc_store_cred(context, ccache2, &creds)) != 0)
    184 		goto out;
    185 
    186 	krb5_free_cred_contents(context, &creds);
    187 
    188 	cred->ccache = ccache2;
    189 
    190 out:
    191 	if (pw)
    192 		free(pw);
    193 
    194 	*minor_status = code;
    195 
    196 	if (code == 0)
    197 		return (GSS_S_COMPLETE);
    198 
    199 	if (ccache2 != NULL)
    200 		(void) krb5_cc_close(context, ccache2);
    201 
    202 	return (GSS_S_FAILURE);
    203 }
    204 
    205 /*ARGSUSED*/
    206 OM_uint32
    207 krb5_gss_acquire_cred_with_password(minor_status,
    208 				desired_name, password, time_req,
    209 				desired_mechs, cred_usage,
    210 				output_cred_handle, actual_mechs,
    211 				time_rec)
    212 OM_uint32 *minor_status;
    213 gss_name_t desired_name;
    214 const gss_buffer_t password;
    215 OM_uint32 time_req;
    216 gss_OID_set desired_mechs;
    217 gss_cred_usage_t cred_usage;
    218 gss_cred_id_t *output_cred_handle;
    219 gss_OID_set *actual_mechs;
    220 OM_uint32 *time_rec;
    221 {
    222 	krb5_context context;
    223 	size_t i;
    224 	krb5_gss_cred_id_t cred;
    225 	gss_OID_set ret_mechs = GSS_C_NULL_OID_SET;
    226 	const gss_OID_set_desc  * valid_mechs;
    227 	int req_old, req_new;
    228 	OM_uint32 ret;
    229 	krb5_error_code code;
    230 
    231 	if (desired_name == GSS_C_NO_NAME)
    232 		return (GSS_S_BAD_NAME);
    233 
    234 	code = gssint_initialize_library();
    235 	if (code) {
    236 		*minor_status = code;
    237 		return (GSS_S_FAILURE);
    238 	}
    239 
    240 	code = krb5_gss_init_context(&context);
    241 	if (code) {
    242 		*minor_status = code;
    243 		return (GSS_S_FAILURE);
    244 	}
    245 
    246 	/* make sure all outputs are valid */
    247 
    248 	*output_cred_handle = NULL;
    249 	if (actual_mechs)
    250 		*actual_mechs = NULL;
    251 	if (time_rec)
    252 		*time_rec = 0;
    253 
    254 	/* validate the name */
    255 	if (!kg_validate_name(desired_name)) {
    256 		*minor_status = (OM_uint32) G_VALIDATE_FAILED;
    257 		krb5_free_context(context);
    258 		return (GSS_S_CALL_BAD_STRUCTURE|GSS_S_BAD_NAME);
    259 	}
    260 
    261 	/*
    262 	 * verify that the requested mechanism set is the default, or
    263 	 * contains krb5
    264 	 */
    265 
    266 	if (desired_mechs == GSS_C_NULL_OID_SET) {
    267 		valid_mechs = gss_mech_set_krb5_both;
    268 		req_old = 1;
    269 		req_new = 1;
    270 	} else {
    271 		req_old = 0;
    272 		req_new = 0;
    273 
    274 		for (i = 0; i < desired_mechs->count; i++) {
    275 			if (g_OID_equal(gss_mech_krb5_old,
    276 				    &(desired_mechs->elements[i])))
    277 				req_old++;
    278 			if (g_OID_equal(gss_mech_krb5,
    279 				    &(desired_mechs->elements[i])))
    280 				req_new++;
    281 		}
    282 
    283 		if (!req_old && !req_new) {
    284 			*minor_status = 0;
    285 			krb5_free_context(context);
    286 			return (GSS_S_BAD_MECH);
    287 		}
    288 	}
    289 
    290 	/* create the gss cred structure */
    291 	if ((cred = (krb5_gss_cred_id_t)
    292 		    xmalloc(sizeof (krb5_gss_cred_id_rec))) == NULL) {
    293 		*minor_status = ENOMEM;
    294 		krb5_free_context(context);
    295 		return (GSS_S_FAILURE);
    296 	}
    297 	memset(cred, 0, sizeof (krb5_gss_cred_id_rec));
    298 
    299 	cred->usage = cred_usage;
    300 	cred->princ = NULL;
    301 	cred->prerfc_mech = req_old;
    302 	cred->rfc_mech = req_new;
    303 
    304 	cred->keytab = NULL;
    305 	cred->ccache = NULL;
    306 
    307 	if ((cred_usage != GSS_C_INITIATE) &&
    308 			(cred_usage != GSS_C_ACCEPT) &&
    309 			(cred_usage != GSS_C_BOTH)) {
    310 		xfree(cred);
    311 		*minor_status = (OM_uint32) G_BAD_USAGE;
    312 		krb5_free_context(context);
    313 		return (GSS_S_FAILURE);
    314 	}
    315 
    316 	/*
    317 	 * If requested, acquire credentials for accepting.  This will
    318 	 * fill in cred->princ if the desired_name is not specified.
    319 	 */
    320 
    321 	if ((cred_usage == GSS_C_ACCEPT) ||
    322 			(cred_usage == GSS_C_BOTH))
    323 		if ((ret = acquire_accept_cred_with_pw(context, minor_status,
    324 						(krb5_principal) desired_name,
    325 						password, cred))
    326 				!= GSS_S_COMPLETE) {
    327 			if (cred->princ)
    328 				krb5_free_principal(context, cred->princ);
    329 			xfree(cred);
    330 			krb5_free_context(context);
    331 			/* minor_status set by acquire_accept_cred() */
    332 			return (ret);
    333 		}
    334 
    335 	/*
    336 	 * If requested, acquire credentials for initiation.  This will
    337 	 * fill in cred->princ if it wasn't set above, and the
    338 	 * desired_name is not specified.
    339 	 */
    340 
    341 	if ((cred_usage == GSS_C_INITIATE) ||
    342 			(cred_usage == GSS_C_BOTH))
    343 		if ((ret = acquire_init_cred_with_pw(context, minor_status,
    344 				cred->princ ? cred->princ : (krb5_principal)
    345 				desired_name, password, cred))
    346 				!= GSS_S_COMPLETE) {
    347 			if (cred->keytab)
    348 				(void) krb5_kt_close(context, cred->keytab);
    349 			if (cred->princ)
    350 				krb5_free_principal(context, cred->princ);
    351 			xfree(cred);
    352 			krb5_free_context(context);
    353 			/* minor_status set by acquire_init_cred() */
    354 			return (ret);
    355 		}
    356 
    357 	/* if the princ wasn't filled in already, fill it in now */
    358 
    359 	if (!cred->princ)
    360 		if ((code = krb5_copy_principal(context, (krb5_principal)
    361 				desired_name, &(cred->princ)))) {
    362 			if (cred->ccache)
    363 				(void) krb5_cc_close(context, cred->ccache);
    364 			if (cred->keytab)
    365 				(void) krb5_kt_close(context, cred->keytab);
    366 			xfree(cred);
    367 			*minor_status = code;
    368 			krb5_free_context(context);
    369 			return (GSS_S_FAILURE);
    370 		}
    371 
    372 	/* at this point, the cred structure has been completely created */
    373 
    374 	/* compute time_rec */
    375 
    376 	if (cred_usage == GSS_C_ACCEPT) {
    377 		if (time_rec)
    378 			*time_rec = GSS_C_INDEFINITE;
    379 	} else {
    380 		krb5_timestamp now;
    381 
    382 		if ((code = krb5_timeofday(context, &now))) {
    383 			if (cred->ccache)
    384 				(void) krb5_cc_close(context, cred->ccache);
    385 			if (cred->keytab)
    386 				(void) krb5_kt_close(context, cred->keytab);
    387 			if (cred->princ)
    388 				krb5_free_principal(context, cred->princ);
    389 			xfree(cred);
    390 			*minor_status = code;
    391 			krb5_free_context(context);
    392 			return (GSS_S_FAILURE);
    393 		}
    394 
    395 		if (time_rec)
    396 			*time_rec = (cred->tgt_expire > now) ?
    397 				(cred->tgt_expire - now) : 0;
    398 	}
    399 
    400 	/* create mechs */
    401 
    402 	if (actual_mechs) {
    403 		if (GSS_ERROR(ret = gss_create_empty_oid_set(minor_status,
    404 						&ret_mechs)) ||
    405 		    (cred->prerfc_mech && GSS_ERROR(ret =
    406 					gss_add_oid_set_member(minor_status,
    407 					    (gss_OID) gss_mech_krb5_old,
    408 					    &ret_mechs))) ||
    409 		    (cred->rfc_mech && GSS_ERROR(ret =
    410 					gss_add_oid_set_member(minor_status,
    411 					    (gss_OID) gss_mech_krb5,
    412 					    &ret_mechs)))) {
    413 			if (cred->ccache)
    414 				(void) krb5_cc_close(context, cred->ccache);
    415 			if (cred->keytab)
    416 				(void) krb5_kt_close(context, cred->keytab);
    417 			if (cred->princ)
    418 				krb5_free_principal(context, cred->princ);
    419 			xfree(cred);
    420 			krb5_free_context(context);
    421 			/* (*minor_status) set above */
    422 			return (ret);
    423 		}
    424 	}
    425 
    426 	/* intern the credential handle */
    427 
    428 	if (! kg_save_cred_id((gss_cred_id_t)cred)) {
    429 		(void) gss_release_oid_set(NULL, &ret_mechs);
    430 		free(ret_mechs->elements);
    431 		free(ret_mechs);
    432 		if (cred->ccache)
    433 			(void) krb5_cc_close(context, cred->ccache);
    434 		if (cred->keytab)
    435 			(void) krb5_kt_close(context, cred->keytab);
    436 		if (cred->princ)
    437 			krb5_free_principal(context, cred->princ);
    438 		xfree(cred);
    439 		krb5_free_context(context);
    440 		*minor_status = (OM_uint32) G_VALIDATE_FAILED;
    441 		return (GSS_S_FAILURE);
    442 	}
    443 
    444 	krb5_free_context(context);
    445 
    446 	/* return success */
    447 	*minor_status = 0;
    448 	*output_cred_handle = (gss_cred_id_t)cred;
    449 	if (actual_mechs)
    450 		*actual_mechs = ret_mechs;
    451 	return (GSS_S_COMPLETE);
    452 }
    453 
    454 /*ARGSUSED*/
    455 OM_uint32
    456 gssspi_acquire_cred_with_password(ctx, minor_status, desired_name,
    457 		password, time_req, desired_mechs, cred_usage,
    458 		output_cred_handle, actual_mechs, time_rec)
    459 void *ctx;
    460 OM_uint32 *minor_status;
    461 gss_name_t desired_name;
    462 const gss_buffer_t password;
    463 OM_uint32 time_req;
    464 gss_OID_set desired_mechs;
    465 gss_cred_usage_t cred_usage;
    466 gss_cred_id_t *output_cred_handle;
    467 gss_OID_set *actual_mechs;
    468 OM_uint32 *time_rec;
    469 {
    470 	OM_uint32 ret;
    471 
    472 	ret = krb5_gss_acquire_cred_with_password(minor_status,
    473 			desired_name, password, time_req, desired_mechs,
    474 			cred_usage, output_cred_handle, actual_mechs, time_rec);
    475 	return (ret);
    476 }
    477