1 /* 2 * Copyright 2005 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 <gssapiP_krb5.h> 82 #include <k5-int.h> 83 84 #ifdef HAVE_STRING_H 85 #include <string.h> 86 #else 87 #include <strings.h> 88 #endif 89 90 /* 91 * $Id: acquire_cred.c,v 1.25.6.2 2000/05/22 20:41:32 meeroh Exp $ 92 */ 93 94 /* ARGSUSED */ 95 static OM_uint32 96 acquire_accept_cred_with_pw(context, minor_status, desired_name, password, cred) 97 krb5_context context; 98 OM_uint32 *minor_status; 99 krb5_principal desired_name; 100 const gss_buffer_t password; 101 krb5_gss_cred_id_rec *cred; 102 { 103 /* 104 * We could add support for this, but we'd need a "memory" based 105 * keytab, which we lack support for. 106 */ 107 return (GSS_S_UNAVAILABLE); 108 } 109 110 static OM_uint32 111 acquire_init_cred_with_pw(context, minor_status, desired_name, password, cred) 112 krb5_context context; 113 OM_uint32 *minor_status; 114 krb5_principal desired_name; 115 const gss_buffer_t password; 116 krb5_gss_cred_id_rec *cred; 117 { 118 krb5_error_code code = 0; 119 krb5_ccache ccache1 = NULL; 120 krb5_ccache ccache2 = NULL; 121 krb5_creds creds; 122 char *pw; 123 124 cred->ccache = NULL; 125 126 if (password == NULL || password->length == NULL || 127 password->value == NULL) 128 pw = strdup(""); 129 else if (*((char *)password->value + (password->length - 1)) == '\0') 130 pw = strdup(password->value); 131 else { 132 pw = malloc(password->length + 1); 133 if (pw == NULL) { 134 code = ENOMEM; 135 goto out; 136 } 137 *pw = '\0'; 138 (void) strlcat(pw, password->value, password->length + 1); 139 } 140 141 if (pw == NULL) { 142 code = ENOMEM; 143 goto out; 144 } 145 146 (void) memset(&creds, 0, sizeof (creds)); 147 148 code = krb5_get_init_creds_password(context, &creds, desired_name, pw, 149 NULL, /* no prompter callback */ 150 NULL, /* no prompter callback data */ 151 0, /* start time (now) */ 152 NULL, /* target princ; NULL -> TGS */ 153 NULL); /* no options; use defaults/config */ 154 155 if (code) 156 goto out; 157 158 /* Got a TGT, now make a MEMORY ccache, stuff in the TGT */ 159 160 if ((code = krb5_cc_resolve(context, "MEMORY:GSSAPI", &ccache1))) 161 goto out; 162 163 /* 164 * Weirdness: there's no way to gen a new ccache without first 165 * opening another of well-known name. A bug in the krb5 API, 166 * really which will have to be fixed in coordination with MIT. 167 * 168 * So we first krb5_cc_resolve() "MEMORY:GSSAPI", then we 169 * krb5_cc_gen_new(), which is a macro that finds the memory 170 * ccache ops from the first ccache but generates a new one. If 171 * we don't close that first ccache it will leak. 172 */ 173 ccache2 = ccache1; 174 if ((code = krb5_cc_gen_new(context, &ccache2)) != 0) 175 goto out; 176 177 (void) krb5_cc_close(context, ccache1); /* avoid leak; see above */ 178 179 if ((code = krb5_cc_initialize(context, ccache2, creds.client)) != 0) 180 goto out; 181 182 if ((code = krb5_cc_store_cred(context, ccache2, &creds)) != 0) 183 goto out; 184 185 krb5_free_cred_contents(context, &creds); 186 187 cred->ccache = ccache2; 188 189 out: 190 if (pw) 191 free(pw); 192 193 *minor_status = code; 194 195 if (code == 0) 196 return (GSS_S_COMPLETE); 197 198 if (ccache2 != NULL) 199 (void) krb5_cc_close(context, ccache2); 200 201 return (GSS_S_FAILURE); 202 } 203 204 /*ARGSUSED*/ 205 OM_uint32 206 krb5_gss_acquire_cred_with_password_no_lock(ctx, minor_status, 207 desired_name, password, time_req, 208 desired_mechs, cred_usage, 209 output_cred_handle, actual_mechs, 210 time_rec) 211 void *ctx; 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 0 232 if (GSS_ERROR(kg_get_context(minor_status, &context))) 233 return (GSS_S_FAILURE); 234 #endif 235 236 context = ctx; 237 238 if (desired_name == GSS_C_NO_NAME) 239 return (GSS_S_BAD_NAME); 240 241 /* make sure all outputs are valid */ 242 243 *output_cred_handle = NULL; 244 if (actual_mechs) 245 *actual_mechs = NULL; 246 if (time_rec) 247 *time_rec = 0; 248 249 /* validate the name */ 250 if (!kg_validate_name(desired_name)) { 251 *minor_status = (OM_uint32) G_VALIDATE_FAILED; 252 return (GSS_S_CALL_BAD_STRUCTURE|GSS_S_BAD_NAME); 253 } 254 255 /* 256 * verify that the requested mechanism set is the default, or 257 * contains krb5 258 */ 259 260 if (desired_mechs == GSS_C_NULL_OID_SET) { 261 valid_mechs = gss_mech_set_krb5_both; 262 req_old = 1; 263 req_new = 1; 264 } else { 265 req_old = 0; 266 req_new = 0; 267 268 for (i = 0; i < desired_mechs->count; i++) { 269 if (g_OID_equal(gss_mech_krb5_old, 270 &(desired_mechs->elements[i]))) 271 req_old++; 272 if (g_OID_equal(gss_mech_krb5, 273 &(desired_mechs->elements[i]))) 274 req_new++; 275 } 276 277 if (!req_old && !req_new) { 278 *minor_status = 0; 279 return (GSS_S_BAD_MECH); 280 } 281 } 282 283 /* create the gss cred structure */ 284 if ((cred = (krb5_gss_cred_id_t) 285 xmalloc(sizeof (krb5_gss_cred_id_rec))) == NULL) { 286 *minor_status = ENOMEM; 287 return (GSS_S_FAILURE); 288 } 289 memset(cred, 0, sizeof (krb5_gss_cred_id_rec)); 290 291 cred->usage = cred_usage; 292 cred->princ = NULL; 293 cred->actual_mechs = valid_mechs; 294 cred->prerfc_mech = req_old; 295 cred->rfc_mech = req_new; 296 297 cred->keytab = NULL; 298 cred->ccache = NULL; 299 300 if ((cred_usage != GSS_C_INITIATE) && 301 (cred_usage != GSS_C_ACCEPT) && 302 (cred_usage != GSS_C_BOTH)) { 303 xfree(cred); 304 *minor_status = (OM_uint32) G_BAD_USAGE; 305 return (GSS_S_FAILURE); 306 } 307 308 /* 309 * If requested, acquire credentials for accepting. This will 310 * fill in cred->princ if the desired_name is not specified. 311 */ 312 313 if ((cred_usage == GSS_C_ACCEPT) || 314 (cred_usage == GSS_C_BOTH)) 315 if ((ret = acquire_accept_cred_with_pw(context, minor_status, 316 (krb5_principal) desired_name, 317 password, cred)) 318 != GSS_S_COMPLETE) { 319 if (cred->princ) 320 krb5_free_principal(context, cred->princ); 321 xfree(cred); 322 /* minor_status set by acquire_accept_cred() */ 323 return (ret); 324 } 325 326 /* 327 * If requested, acquire credentials for initiation. This will 328 * fill in cred->princ if it wasn't set above, and the 329 * desired_name is not specified. 330 */ 331 332 if ((cred_usage == GSS_C_INITIATE) || 333 (cred_usage == GSS_C_BOTH)) 334 if ((ret = acquire_init_cred_with_pw(context, minor_status, 335 cred->princ ? cred->princ : (krb5_principal) 336 desired_name, password, cred)) 337 != GSS_S_COMPLETE) { 338 if (cred->keytab) 339 (void) krb5_kt_close(context, cred->keytab); 340 if (cred->princ) 341 krb5_free_principal(context, cred->princ); 342 xfree(cred); 343 /* minor_status set by acquire_init_cred() */ 344 return (ret); 345 } 346 347 /* if the princ wasn't filled in already, fill it in now */ 348 349 if (!cred->princ) 350 if ((code = krb5_copy_principal(context, (krb5_principal) 351 desired_name, &(cred->princ)))) { 352 if (cred->ccache) 353 (void) krb5_cc_close(context, cred->ccache); 354 if (cred->keytab) 355 (void) krb5_kt_close(context, cred->keytab); 356 xfree(cred); 357 *minor_status = code; 358 return (GSS_S_FAILURE); 359 } 360 361 /* at this point, the cred structure has been completely created */ 362 363 /* compute time_rec */ 364 365 if (cred_usage == GSS_C_ACCEPT) { 366 if (time_rec) 367 *time_rec = GSS_C_INDEFINITE; 368 } else { 369 krb5_timestamp now; 370 371 if ((code = krb5_timeofday(context, &now))) { 372 if (cred->ccache) 373 (void) krb5_cc_close(context, cred->ccache); 374 if (cred->keytab) 375 (void) krb5_kt_close(context, cred->keytab); 376 if (cred->princ) 377 krb5_free_principal(context, cred->princ); 378 xfree(cred); 379 *minor_status = code; 380 return (GSS_S_FAILURE); 381 } 382 383 if (time_rec) 384 *time_rec = (cred->tgt_expire > now) ? 385 (cred->tgt_expire - now) : 0; 386 } 387 388 /* create mechs */ 389 390 if (actual_mechs) { 391 if (GSS_ERROR(ret = gss_create_empty_oid_set(minor_status, 392 &ret_mechs)) || 393 (cred->prerfc_mech && GSS_ERROR(ret = 394 gss_add_oid_set_member(minor_status, 395 (gss_OID) gss_mech_krb5_old, 396 &ret_mechs))) || 397 (cred->rfc_mech && GSS_ERROR(ret = 398 gss_add_oid_set_member(minor_status, 399 (gss_OID) gss_mech_krb5, 400 &ret_mechs)))) { 401 if (cred->ccache) 402 (void) krb5_cc_close(context, cred->ccache); 403 if (cred->keytab) 404 (void) krb5_kt_close(context, cred->keytab); 405 if (cred->princ) 406 krb5_free_principal(context, cred->princ); 407 xfree(cred); 408 /* (*minor_status) set above */ 409 return (ret); 410 } 411 } 412 413 /* intern the credential handle */ 414 415 if (! kg_save_cred_id((gss_cred_id_t)cred)) { 416 (void) gss_release_oid_set(NULL, &ret_mechs); 417 free(ret_mechs->elements); 418 free(ret_mechs); 419 if (cred->ccache) 420 (void) krb5_cc_close(context, cred->ccache); 421 if (cred->keytab) 422 (void) krb5_kt_close(context, cred->keytab); 423 if (cred->princ) 424 krb5_free_principal(context, cred->princ); 425 xfree(cred); 426 *minor_status = (OM_uint32) G_VALIDATE_FAILED; 427 return (GSS_S_FAILURE); 428 } 429 430 /* return success */ 431 432 *minor_status = 0; 433 *output_cred_handle = (gss_cred_id_t)cred; 434 if (actual_mechs) 435 *actual_mechs = ret_mechs; 436 return (GSS_S_COMPLETE); 437 } 438 439 OM_uint32 440 gssspi_acquire_cred_with_password(ctx, minor_status, desired_name, 441 password, time_req, desired_mechs, cred_usage, 442 output_cred_handle, actual_mechs, time_rec) 443 void *ctx; 444 OM_uint32 *minor_status; 445 gss_name_t desired_name; 446 const gss_buffer_t password; 447 OM_uint32 time_req; 448 gss_OID_set desired_mechs; 449 gss_cred_usage_t cred_usage; 450 gss_cred_id_t *output_cred_handle; 451 gss_OID_set *actual_mechs; 452 OM_uint32 *time_rec; 453 { 454 OM_uint32 ret; 455 456 mutex_lock(&krb5_mutex); 457 ret = krb5_gss_acquire_cred_with_password_no_lock(ctx, minor_status, 458 desired_name, password, time_req, desired_mechs, 459 cred_usage, output_cred_handle, actual_mechs, time_rec); 460 mutex_unlock(&krb5_mutex); 461 return (ret); 462 } 463