1/* 2 * Copyright (c) 2009 Kungliga Tekniska Högskolan 3 * (Royal Institute of Technology, Stockholm, Sweden). 4 * All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 17 * 3. Neither the name of the Institute nor the names of its contributors 18 * may be used to endorse or promote products derived from this software 19 * without specific prior written permission. 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND 22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24 * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE 25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31 * SUCH DAMAGE. 32 */ 33 34#include "gsskrb5_locl.h" 35 36OM_uint32 GSSAPI_CALLCONV 37_gsskrb5_export_cred(OM_uint32 *minor_status, 38 gss_cred_id_t cred_handle, 39 gss_buffer_t cred_token) 40{ 41 gsskrb5_cred handle = (gsskrb5_cred)cred_handle; 42 krb5_context context; 43 krb5_error_code ret; 44 krb5_storage *sp; 45 krb5_data data, mech; 46 char *str; 47 48 GSSAPI_KRB5_INIT (&context); 49 50 if (handle->usage != GSS_C_INITIATE && handle->usage != GSS_C_BOTH) { 51 *minor_status = GSS_KRB5_S_G_BAD_USAGE; 52 return GSS_S_FAILURE; 53 } 54 55 sp = krb5_storage_emem(); 56 if (sp == NULL) { 57 *minor_status = ENOMEM; 58 return GSS_S_FAILURE; 59 } 60 61 /* 62 type = krb5_cc_get_type(context, handle->ccache); 63 * 64 * XXX Always use reference keys since that makes it easier to 65 * transport between processing in seprate authentication domain. 66 * 67 * We should encrypt credentials in KCM though using the kcm 68 * session key. 69 */ 70 if (1 /*strcmp(type, "MEMORY") == 0 */) { 71 krb5_creds *creds; 72 ret = krb5_store_uint32(sp, 0); 73 if (ret) { 74 krb5_storage_free(sp); 75 *minor_status = ret; 76 return GSS_S_FAILURE; 77 } 78 79 ret = _krb5_get_krbtgt(context, handle->ccache, 80 handle->principal->realm, 81 &creds); 82 if (ret) { 83 krb5_storage_free(sp); 84 *minor_status = ret; 85 return GSS_S_FAILURE; 86 } 87 88 ret = krb5_store_creds(sp, creds); 89 krb5_free_creds(context, creds); 90 if (ret) { 91 krb5_storage_free(sp); 92 *minor_status = ret; 93 return GSS_S_FAILURE; 94 } 95 96 } else { 97 ret = krb5_store_uint32(sp, 1); 98 if (ret) { 99 krb5_storage_free(sp); 100 *minor_status = ret; 101 return GSS_S_FAILURE; 102 } 103 104 ret = krb5_cc_get_full_name(context, handle->ccache, &str); 105 if (ret) { 106 krb5_storage_free(sp); 107 *minor_status = ret; 108 return GSS_S_FAILURE; 109 } 110 111 ret = krb5_store_string(sp, str); 112 free(str); 113 if (ret) { 114 krb5_storage_free(sp); 115 *minor_status = ret; 116 return GSS_S_FAILURE; 117 } 118 } 119 ret = krb5_storage_to_data(sp, &data); 120 krb5_storage_free(sp); 121 if (ret) { 122 *minor_status = ret; 123 return GSS_S_FAILURE; 124 } 125 sp = krb5_storage_emem(); 126 if (sp == NULL) { 127 krb5_data_free(&data); 128 *minor_status = ENOMEM; 129 return GSS_S_FAILURE; 130 } 131 132 mech.data = GSS_KRB5_MECHANISM->elements; 133 mech.length = GSS_KRB5_MECHANISM->length; 134 135 ret = krb5_store_data(sp, mech); 136 if (ret) { 137 krb5_data_free(&data); 138 krb5_storage_free(sp); 139 *minor_status = ret; 140 return GSS_S_FAILURE; 141 } 142 143 ret = krb5_store_data(sp, data); 144 krb5_data_free(&data); 145 if (ret) { 146 krb5_storage_free(sp); 147 *minor_status = ret; 148 return GSS_S_FAILURE; 149 } 150 151 ret = krb5_storage_to_data(sp, &data); 152 krb5_storage_free(sp); 153 if (ret) { 154 *minor_status = ret; 155 return GSS_S_FAILURE; 156 } 157 158 cred_token->value = data.data; 159 cred_token->length = data.length; 160 161 return GSS_S_COMPLETE; 162} 163 164OM_uint32 GSSAPI_CALLCONV 165_gsskrb5_import_cred(OM_uint32 * minor_status, 166 gss_buffer_t cred_token, 167 gss_cred_id_t * cred_handle) 168{ 169 krb5_context context; 170 krb5_error_code ret; 171 gsskrb5_cred handle; 172 krb5_ccache id; 173 krb5_storage *sp; 174 char *str; 175 uint32_t type; 176 int flags = 0; 177 178 *cred_handle = GSS_C_NO_CREDENTIAL; 179 180 GSSAPI_KRB5_INIT (&context); 181 182 sp = krb5_storage_from_mem(cred_token->value, cred_token->length); 183 if (sp == NULL) { 184 *minor_status = ENOMEM; 185 return GSS_S_FAILURE; 186 } 187 188 ret = krb5_ret_uint32(sp, &type); 189 if (ret) { 190 krb5_storage_free(sp); 191 *minor_status = ret; 192 return GSS_S_FAILURE; 193 } 194 switch (type) { 195 case 0: { 196 krb5_creds creds; 197 198 ret = krb5_ret_creds(sp, &creds); 199 krb5_storage_free(sp); 200 if (ret) { 201 *minor_status = ret; 202 return GSS_S_FAILURE; 203 } 204 205 ret = krb5_cc_new_unique(context, "API", NULL, &id); 206 if (ret) { 207 *minor_status = ret; 208 return GSS_S_FAILURE; 209 } 210 211 ret = krb5_cc_initialize(context, id, creds.client); 212 if (ret) { 213 krb5_cc_destroy(context, id); 214 *minor_status = ret; 215 return GSS_S_FAILURE; 216 } 217 218 ret = krb5_cc_store_cred(context, id, &creds); 219 krb5_free_cred_contents(context, &creds); 220 if (ret) { 221 krb5_cc_destroy(context, id); 222 *minor_status = ret; 223 return GSS_S_FAILURE; 224 } 225 226 flags |= GSS_CF_DESTROY_CRED_ON_RELEASE; 227 228 break; 229 } 230 case 1: 231 ret = krb5_ret_string(sp, &str); 232 krb5_storage_free(sp); 233 if (ret) { 234 *minor_status = ret; 235 return GSS_S_FAILURE; 236 } 237 238 ret = krb5_cc_resolve(context, str, &id); 239 krb5_xfree(str); 240 if (ret) { 241 *minor_status = ret; 242 return GSS_S_FAILURE; 243 } 244 break; 245 246 default: 247 krb5_storage_free(sp); 248 *minor_status = 0; 249 return GSS_S_NO_CRED; 250 } 251 252 handle = calloc(1, sizeof(*handle)); 253 if (handle == NULL) { 254 krb5_cc_close(context, id); 255 *minor_status = ENOMEM; 256 return GSS_S_FAILURE; 257 } 258 259 handle->usage = GSS_C_INITIATE; 260 krb5_cc_get_principal(context, id, &handle->principal); 261 handle->ccache = id; 262 handle->cred_flags = flags; 263 264 if (handle->principal) 265 __gsskrb5_ccache_lifetime(minor_status, context, 266 id, handle->principal, 267 &handle->endtime); 268 269 *cred_handle = (gss_cred_id_t)handle; 270 271 return GSS_S_COMPLETE; 272} 273 274OM_uint32 275_gsskrb5_destroy_cred(OM_uint32 *minor_status, 276 gss_cred_id_t *cred_handle) 277{ 278 gsskrb5_cred cred = (gsskrb5_cred)*cred_handle; 279 cred->cred_flags |= GSS_CF_DESTROY_CRED_ON_RELEASE; 280 return _gsskrb5_release_cred(minor_status, cred_handle); 281} 282 283static OM_uint32 284change_hold(OM_uint32 *minor_status, gsskrb5_cred cred, 285 krb5_error_code (*func)(krb5_context, krb5_ccache)) 286{ 287 krb5_error_code ret; 288 krb5_context context; 289 krb5_data data; 290 291 *minor_status = 0; 292 krb5_data_zero(&data); 293 294 GSSAPI_KRB5_INIT (&context); 295 296 if (cred == NULL) 297 return GSS_S_COMPLETE; 298 299 if (cred->usage != GSS_C_INITIATE && cred->usage != GSS_C_BOTH) { 300 *minor_status = GSS_KRB5_S_G_BAD_USAGE; 301 return GSS_S_FAILURE; 302 } 303 304 /* XXX only refcount nah-created credentials */ 305 ret = krb5_cc_get_config(context, cred->ccache, NULL, "nah-created", &data); 306 if (ret) { 307 *minor_status = ret; 308 return GSS_S_FAILURE; 309 } 310 krb5_data_free(&data); 311 312 ret = func(context, cred->ccache); 313 314 if (ret) { 315 *minor_status = ret; 316 return GSS_S_FAILURE; 317 } 318 319 return GSS_S_COMPLETE; 320} 321 322OM_uint32 323_gsskrb5_cred_hold(OM_uint32 *minor_status, gss_cred_id_t cred) 324{ 325 return change_hold(minor_status, (gsskrb5_cred)cred, krb5_cc_hold); 326} 327 328OM_uint32 329_gsskrb5_cred_unhold(OM_uint32 *minor_status, gss_cred_id_t cred) 330{ 331 return change_hold(minor_status, (gsskrb5_cred)cred, krb5_cc_unhold); 332} 333 334OM_uint32 335_gsskrb5_cred_label_get(OM_uint32 *minor_status, gss_cred_id_t cred_handle, 336 const char *label, gss_buffer_t value) 337{ 338 gsskrb5_cred cred = (gsskrb5_cred)cred_handle; 339 krb5_context context; 340 krb5_error_code ret; 341 krb5_data data; 342 343 GSSAPI_KRB5_INIT (&context); 344 345 if (cred == NULL) 346 return GSS_S_COMPLETE; 347 348 if (cred->ccache == NULL) { 349 *minor_status = GSS_KRB5_S_G_BAD_USAGE; 350 return GSS_S_FAILURE; 351 } 352 353 ret = krb5_cc_get_config(context, cred->ccache, NULL, label, &data); 354 if (ret) { 355 *minor_status = ret; 356 return GSS_S_FAILURE; 357 } 358 359 value->value = data.data; 360 value->length = data.length; 361 362 return GSS_S_COMPLETE; 363} 364 365OM_uint32 366_gsskrb5_cred_label_set(OM_uint32 *minor_status, gss_cred_id_t cred_handle, 367 const char *label, gss_buffer_t value) 368{ 369 gsskrb5_cred cred = (gsskrb5_cred)cred_handle; 370 krb5_context context; 371 krb5_error_code ret; 372 krb5_data data, *datap = NULL; 373 374 GSSAPI_KRB5_INIT (&context); 375 376 if (cred == NULL) 377 return GSS_S_COMPLETE; 378 379 if (cred->ccache == NULL) { 380 *minor_status = GSS_KRB5_S_G_BAD_USAGE; 381 return GSS_S_FAILURE; 382 } 383 384 if (value) { 385 data.data = value->value; 386 data.length = value->length; 387 datap = &data; 388 } 389 390 ret = krb5_cc_set_config(context, cred->ccache, NULL, label, datap); 391 if (ret) { 392 *minor_status = ret; 393 return GSS_S_FAILURE; 394 } 395 396 return GSS_S_COMPLETE; 397} 398 399 400OM_uint32 401_gsskrb5_appl_change_password(OM_uint32 *minor_status, 402 gss_name_t name, 403 const char *oldpw, 404 const char *newpw) 405{ 406 krb5_data result_code_string, result_string; 407 krb5_get_init_creds_opt *opt = NULL; 408 krb5_context context; 409 krb5_principal principal = (krb5_principal)name; 410 krb5_creds kcred; 411 krb5_error_code ret; 412 int result_code; 413 414 GSSAPI_KRB5_INIT (&context); 415 416 memset(&kcred, 0, sizeof(kcred)); 417 418 ret = krb5_get_init_creds_opt_alloc(context, &opt); 419 if (ret) 420 goto out; 421 422 krb5_get_init_creds_opt_set_tkt_life(opt, 300); 423 krb5_get_init_creds_opt_set_forwardable(opt, FALSE); 424 krb5_get_init_creds_opt_set_proxiable(opt, FALSE); 425 426 ret = krb5_get_init_creds_password(context, 427 &kcred, 428 principal, 429 oldpw, 430 NULL, 431 NULL, 432 0, 433 "kadmin/changepw", 434 opt); 435 if (ret) 436 goto out; 437 438 ret = krb5_set_password(context, &kcred, newpw, NULL, 439 &result_code, &result_code_string, &result_string); 440 if (ret) 441 goto out; 442 443 krb5_data_free(&result_string); 444 krb5_data_free(&result_code_string); 445 446 if (result_code) { 447 krb5_set_error_message(context, KRB5KRB_AP_ERR_BAD_INTEGRITY, 448 "Failed to change invalid password: %d", result_code); 449 ret = KRB5KRB_AP_ERR_BAD_INTEGRITY; 450 goto out; 451 } 452 453 out: 454 if (opt) 455 krb5_get_init_creds_opt_free(context, opt); 456 krb5_free_cred_contents(context, &kcred); 457 458 *minor_status = ret; 459 460 return ret ? GSS_S_FAILURE : GSS_S_COMPLETE; 461} 462 463 464