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 47 krb5_data_zero(&data); 48 49 GSSAPI_KRB5_INIT (&context); 50 51 if (handle->usage != GSS_C_INITIATE && handle->usage != GSS_C_BOTH) { 52 *minor_status = GSS_KRB5_S_G_BAD_USAGE; 53 return GSS_S_FAILURE; 54 } 55 56 sp = krb5_storage_emem(); 57 if (sp == NULL) { 58 *minor_status = ENOMEM; 59 return GSS_S_FAILURE; 60 } 61 62 /* 63 type = krb5_cc_get_type(context, handle->ccache); 64 * 65 * XXX Always use reference keys since that makes it easier to 66 * transport between processing in seprate authentication domain. 67 * 68 * We should encrypt credentials in KCM though using the kcm 69 * session key. 70 */ 71 { 72 krb5_creds *creds; 73 74 if (handle->ccache == NULL) 75 goto out; 76 77 ret = krb5_store_uint32(sp, 0); 78 if (ret) { 79 krb5_storage_free(sp); 80 *minor_status = ret; 81 return GSS_S_FAILURE; 82 } 83 84 ret = _krb5_get_krbtgt(context, handle->ccache, 85 handle->principal->realm, 86 &creds); 87 if (ret) { 88 krb5_storage_free(sp); 89 *minor_status = ret; 90 return GSS_S_FAILURE; 91 } 92 93 ret = krb5_store_creds(sp, creds); 94 krb5_free_creds(context, creds); 95 if (ret) { 96 krb5_storage_free(sp); 97 *minor_status = ret; 98 return GSS_S_FAILURE; 99 } 100 101 } 102 ret = krb5_storage_to_data(sp, &data); 103 krb5_storage_free(sp); 104 if (ret) { 105 *minor_status = ret; 106 return GSS_S_FAILURE; 107 } 108 sp = krb5_storage_emem(); 109 if (sp == NULL) { 110 krb5_data_free(&data); 111 *minor_status = ENOMEM; 112 return GSS_S_FAILURE; 113 } 114 115 mech.data = GSS_KRB5_MECHANISM->elements; 116 mech.length = GSS_KRB5_MECHANISM->length; 117 118 ret = krb5_store_data(sp, mech); 119 if (ret) { 120 krb5_data_free(&data); 121 krb5_storage_free(sp); 122 *minor_status = ret; 123 return GSS_S_FAILURE; 124 } 125 126 ret = krb5_store_data(sp, data); 127 krb5_data_free(&data); 128 if (ret) { 129 krb5_storage_free(sp); 130 *minor_status = ret; 131 return GSS_S_FAILURE; 132 } 133 134 ret = krb5_storage_to_data(sp, &data); 135 krb5_storage_free(sp); 136 if (ret) { 137 *minor_status = ret; 138 return GSS_S_FAILURE; 139 } 140 141 out: 142 cred_token->value = data.data; 143 cred_token->length = data.length; 144 145 return GSS_S_COMPLETE; 146} 147 148OM_uint32 GSSAPI_CALLCONV 149_gsskrb5_import_cred(OM_uint32 * minor_status, 150 gss_buffer_t cred_token, 151 gss_cred_id_t * cred_handle) 152{ 153 krb5_context context; 154 krb5_error_code ret; 155 gsskrb5_cred handle; 156 krb5_ccache id; 157 krb5_storage *sp; 158 char *str; 159 uint32_t type; 160 int flags = 0; 161 162 *cred_handle = GSS_C_NO_CREDENTIAL; 163 164 GSSAPI_KRB5_INIT (&context); 165 166 sp = krb5_storage_from_mem(cred_token->value, cred_token->length); 167 if (sp == NULL) { 168 *minor_status = ENOMEM; 169 return GSS_S_FAILURE; 170 } 171 172 ret = krb5_ret_uint32(sp, &type); 173 if (ret) { 174 krb5_storage_free(sp); 175 *minor_status = ret; 176 return GSS_S_FAILURE; 177 } 178 switch (type) { 179 case 0: { 180 krb5_creds creds; 181 182 ret = krb5_ret_creds(sp, &creds); 183 krb5_storage_free(sp); 184 if (ret) { 185 *minor_status = ret; 186 return GSS_S_FAILURE; 187 } 188 189 ret = krb5_cc_new_unique(context, "API", NULL, &id); 190 if (ret) { 191 *minor_status = ret; 192 return GSS_S_FAILURE; 193 } 194 195 ret = krb5_cc_initialize(context, id, creds.client); 196 if (ret) { 197 krb5_cc_destroy(context, id); 198 *minor_status = ret; 199 return GSS_S_FAILURE; 200 } 201 202 ret = krb5_cc_store_cred(context, id, &creds); 203 krb5_free_cred_contents(context, &creds); 204 if (ret) { 205 krb5_cc_destroy(context, id); 206 *minor_status = ret; 207 return GSS_S_FAILURE; 208 } 209 210 flags |= GSS_CF_DESTROY_CRED_ON_RELEASE; 211 212 break; 213 } 214 case 1: 215 ret = krb5_ret_string(sp, &str); 216 krb5_storage_free(sp); 217 if (ret) { 218 *minor_status = ret; 219 return GSS_S_FAILURE; 220 } 221 222 ret = krb5_cc_resolve(context, str, &id); 223 krb5_xfree(str); 224 if (ret) { 225 *minor_status = ret; 226 return GSS_S_FAILURE; 227 } 228 break; 229 230 default: 231 krb5_storage_free(sp); 232 *minor_status = 0; 233 return GSS_S_NO_CRED; 234 } 235 236 handle = calloc(1, sizeof(*handle)); 237 if (handle == NULL) { 238 krb5_cc_close(context, id); 239 *minor_status = ENOMEM; 240 return GSS_S_FAILURE; 241 } 242 243 handle->usage = GSS_C_INITIATE; 244 krb5_cc_get_principal(context, id, &handle->principal); 245 handle->ccache = id; 246 handle->cred_flags = flags; 247 248 if (handle->principal) 249 __gsskrb5_ccache_lifetime(minor_status, context, 250 id, handle->principal, 251 &handle->endtime); 252 253 *cred_handle = (gss_cred_id_t)handle; 254 255 return GSS_S_COMPLETE; 256} 257 258OM_uint32 259_gsskrb5_destroy_cred(OM_uint32 *minor_status, 260 gss_cred_id_t *cred_handle) 261{ 262 gsskrb5_cred cred = (gsskrb5_cred)*cred_handle; 263 cred->cred_flags |= GSS_CF_DESTROY_CRED_ON_RELEASE; 264 return _gsskrb5_release_cred(minor_status, cred_handle); 265} 266 267static OM_uint32 268change_hold(OM_uint32 *minor_status, gsskrb5_cred cred, 269 krb5_error_code (*func)(krb5_context, krb5_ccache)) 270{ 271 krb5_error_code ret; 272 krb5_context context; 273 krb5_data data; 274 275 *minor_status = 0; 276 krb5_data_zero(&data); 277 278 GSSAPI_KRB5_INIT (&context); 279 280 if (cred == NULL) 281 return GSS_S_COMPLETE; 282 283 if (cred->usage != GSS_C_INITIATE && cred->usage != GSS_C_BOTH) { 284 *minor_status = GSS_KRB5_S_G_BAD_USAGE; 285 return GSS_S_FAILURE; 286 } 287 288 /* XXX only refcount nah-created credentials */ 289 ret = krb5_cc_get_config(context, cred->ccache, NULL, "nah-created", &data); 290 if (ret) { 291 *minor_status = ret; 292 return GSS_S_FAILURE; 293 } 294 krb5_data_free(&data); 295 296 ret = func(context, cred->ccache); 297 298 if (ret) { 299 *minor_status = ret; 300 return GSS_S_FAILURE; 301 } 302 303 return GSS_S_COMPLETE; 304} 305 306OM_uint32 307_gsskrb5_cred_hold(OM_uint32 *minor_status, gss_cred_id_t cred) 308{ 309 return change_hold(minor_status, (gsskrb5_cred)cred, krb5_cc_hold); 310} 311 312OM_uint32 313_gsskrb5_cred_unhold(OM_uint32 *minor_status, gss_cred_id_t cred) 314{ 315 return change_hold(minor_status, (gsskrb5_cred)cred, krb5_cc_unhold); 316} 317 318OM_uint32 319_gsskrb5_cred_label_get(OM_uint32 *minor_status, gss_cred_id_t cred_handle, 320 const char *label, gss_buffer_t value) 321{ 322 gsskrb5_cred cred = (gsskrb5_cred)cred_handle; 323 krb5_context context; 324 krb5_error_code ret; 325 krb5_data data; 326 327 GSSAPI_KRB5_INIT (&context); 328 329 if (cred == NULL) 330 return GSS_S_COMPLETE; 331 332 if (cred->ccache == NULL) { 333 *minor_status = GSS_KRB5_S_G_BAD_USAGE; 334 return GSS_S_FAILURE; 335 } 336 337 ret = krb5_cc_get_config(context, cred->ccache, NULL, label, &data); 338 if (ret) { 339 *minor_status = ret; 340 return GSS_S_FAILURE; 341 } 342 343 value->value = data.data; 344 value->length = data.length; 345 346 return GSS_S_COMPLETE; 347} 348 349OM_uint32 350_gsskrb5_cred_label_set(OM_uint32 *minor_status, gss_cred_id_t cred_handle, 351 const char *label, gss_buffer_t value) 352{ 353 gsskrb5_cred cred = (gsskrb5_cred)cred_handle; 354 krb5_context context; 355 krb5_error_code ret; 356 krb5_data data, *datap = NULL; 357 358 GSSAPI_KRB5_INIT (&context); 359 360 if (cred == NULL) 361 return GSS_S_COMPLETE; 362 363 if (cred->ccache == NULL) { 364 *minor_status = GSS_KRB5_S_G_BAD_USAGE; 365 return GSS_S_FAILURE; 366 } 367 368 if (value) { 369 data.data = value->value; 370 data.length = value->length; 371 datap = &data; 372 } 373 374 ret = krb5_cc_set_config(context, cred->ccache, NULL, label, datap); 375 if (ret) { 376 *minor_status = ret; 377 return GSS_S_FAILURE; 378 } 379 380 return GSS_S_COMPLETE; 381} 382 383 384OM_uint32 385_gsskrb5_appl_change_password(OM_uint32 *minor_status, 386 gss_name_t name, 387 const char *oldpw, 388 const char *newpw) 389{ 390 krb5_data result_code_string, result_string; 391 krb5_get_init_creds_opt *opt = NULL; 392 krb5_context context; 393 krb5_principal principal = (krb5_principal)name; 394 krb5_creds kcred; 395 krb5_error_code ret; 396 int result_code; 397 398 GSSAPI_KRB5_INIT (&context); 399 400 memset(&kcred, 0, sizeof(kcred)); 401 402 ret = krb5_get_init_creds_opt_alloc(context, &opt); 403 if (ret) 404 goto out; 405 406 krb5_get_init_creds_opt_set_tkt_life(opt, 300); 407 krb5_get_init_creds_opt_set_forwardable(opt, FALSE); 408 krb5_get_init_creds_opt_set_proxiable(opt, FALSE); 409 410 ret = krb5_get_init_creds_password(context, 411 &kcred, 412 principal, 413 oldpw, 414 NULL, 415 NULL, 416 0, 417 "kadmin/changepw", 418 opt); 419 if (ret) 420 goto out; 421 422 ret = krb5_set_password(context, &kcred, newpw, NULL, 423 &result_code, &result_code_string, &result_string); 424 if (ret) 425 goto out; 426 427 krb5_data_free(&result_string); 428 krb5_data_free(&result_code_string); 429 430 if (result_code) { 431 krb5_set_error_message(context, KRB5KRB_AP_ERR_BAD_INTEGRITY, 432 "Failed to change invalid password: %d", result_code); 433 ret = KRB5KRB_AP_ERR_BAD_INTEGRITY; 434 goto out; 435 } 436 437 out: 438 if (opt) 439 krb5_get_init_creds_opt_free(context, opt); 440 krb5_free_cred_contents(context, &kcred); 441 442 *minor_status = ret; 443 444 return ret ? GSS_S_FAILURE : GSS_S_COMPLETE; 445} 446 447 448