1/* 2 * Copyright (c) 2004, PADL Software Pty Ltd. 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * 3. Neither the name of PADL Software nor the names of its contributors 17 * may be used to endorse or promote products derived from this software 18 * without specific prior written permission. 19 * 20 * THIS SOFTWARE IS PROVIDED BY PADL SOFTWARE AND CONTRIBUTORS ``AS IS'' AND 21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23 * ARE DISCLAIMED. IN NO EVENT SHALL PADL SOFTWARE OR CONTRIBUTORS BE LIABLE 24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 30 * SUCH DAMAGE. 31 */ 32 33#include "gsskrb5_locl.h" 34#include <hex.h> 35#include <rtbl.h> 36 37static char* 38printable_time(time_t t) 39{ 40 static char s[128]; 41 char *p; 42 43 if ((p = ctime(&t)) == NULL) 44 strlcpy(s, "?", sizeof(s)); 45 else 46 strlcpy(s, p + 4, sizeof(s)); 47 s[20] = 0; 48 return s; 49} 50 51 52OM_uint32 GSSAPI_CALLCONV _gsskrb5_inquire_cred_by_oid 53 (OM_uint32 * minor_status, 54 const gss_cred_id_t cred_handle, 55 const gss_OID desired_object, 56 gss_buffer_set_t *data_set) 57{ 58 krb5_context context; 59 gsskrb5_cred cred = (gsskrb5_cred)cred_handle; 60 krb5_error_code ret; 61 gss_buffer_desc buffer; 62 63 GSSAPI_KRB5_INIT (&context); 64 65 if (gss_oid_equal(desired_object, GSS_KRB5_COPY_CCACHE_X)) { 66 char *str; 67 68 HEIMDAL_MUTEX_lock(&cred->cred_id_mutex); 69 70 if (cred->ccache == NULL) { 71 HEIMDAL_MUTEX_unlock(&cred->cred_id_mutex); 72 *minor_status = EINVAL; 73 return GSS_S_FAILURE; 74 } 75 76 ret = krb5_cc_get_full_name(context, cred->ccache, &str); 77 HEIMDAL_MUTEX_unlock(&cred->cred_id_mutex); 78 if (ret) { 79 *minor_status = ret; 80 return GSS_S_FAILURE; 81 } 82 83 buffer.value = str; 84 buffer.length = strlen(str); 85 86 ret = gss_add_buffer_set_member(minor_status, &buffer, data_set); 87 if (ret != GSS_S_COMPLETE) 88 _gsskrb5_clear_status (); 89 90 free(str); 91 92 *minor_status = 0; 93 return GSS_S_COMPLETE; 94 95 } else if (gss_oid_equal(desired_object, GSS_C_CRED_VALIDATE)) { 96 krb5_verify_init_creds_opt vopt; 97 krb5_creds *kcred = NULL; 98 99 if (cred->ccache == NULL || cred->principal == NULL) 100 return GSS_S_FAILURE; 101 102 krb5_verify_init_creds_opt_init(&vopt); 103 krb5_verify_init_creds_opt_set_ap_req_nofail(&vopt, TRUE); 104 105 HEIMDAL_MUTEX_lock(&cred->cred_id_mutex); 106 107 ret = _krb5_get_krbtgt(context, cred->ccache, cred->principal->realm, &kcred); 108 if (ret == 0) { 109 ret = krb5_verify_init_creds(context, 110 kcred, 111 NULL, 112 NULL, 113 &cred->ccache, 114 &vopt); 115 krb5_free_creds(context, kcred); 116 } 117 HEIMDAL_MUTEX_unlock(&cred->cred_id_mutex); 118 119 if (ret) { 120 *minor_status = ret; 121 return GSS_S_FAILURE; 122 } 123 124 return GSS_S_COMPLETE; 125 126 } else if (gss_oid_equal(desired_object, GSS_C_NT_UUID)) { 127 krb5_uuid uuid; 128 char *str; 129 130 HEIMDAL_MUTEX_lock(&cred->cred_id_mutex); 131 132 if (cred->ccache == NULL) { 133 HEIMDAL_MUTEX_unlock(&cred->cred_id_mutex); 134 *minor_status = EINVAL; 135 return GSS_S_FAILURE; 136 } 137 138 ret = krb5_cc_get_uuid(context, cred->ccache, uuid); 139 HEIMDAL_MUTEX_unlock(&cred->cred_id_mutex); 140 if (ret) { 141 *minor_status = ret; 142 return GSS_S_FAILURE; 143 } 144 145 str = krb5_uuid_to_string(uuid); 146 if (str == NULL) { 147 *minor_status = ENOMEM; 148 return GSS_S_FAILURE; 149 } 150 151 buffer.value = str; 152 buffer.length = strlen(str); 153 154 ret = gss_add_buffer_set_member(minor_status, &buffer, data_set); 155 free(str); 156 if (ret != GSS_S_COMPLETE) 157 _gsskrb5_clear_status (); 158 159 return GSS_S_COMPLETE; 160 161 } else if (gss_oid_equal(desired_object, GSS_C_CRED_DIAG)) { 162 krb5_cc_cursor cursor; 163 krb5_creds creds; 164#ifdef HAVE_KCM 165 krb5_data data; 166#endif 167 rtbl_t ct = NULL; 168 char *str = NULL; 169 170 if (cred->ccache == NULL) { 171 *minor_status = EINVAL; 172 return GSS_S_FAILURE; 173 } 174 175 /* 176 * Cache name 177 */ 178 179 ret = krb5_cc_get_full_name(context, cred->ccache, &str); 180 HEIMDAL_MUTEX_unlock(&cred->cred_id_mutex); 181 if (ret) { 182 *minor_status = ret; 183 return GSS_S_FAILURE; 184 } 185 186 buffer.value = str; 187 buffer.length = strlen(str); 188 189 ret = gss_add_buffer_set_member(minor_status, &buffer, data_set); 190 free(str); 191 if (ret != GSS_S_COMPLETE) 192 _gsskrb5_clear_status (); 193 194 /* 195 * cache list 196 */ 197 198 ct = rtbl_create(); 199 if (ct == NULL) { 200 *minor_status = ENOMEM; 201 return GSS_S_FAILURE; 202 } 203 204#define COL_ISSUED "Issued" 205#define COL_EXPIRES "Expires" 206#define COL_PRINCIPAL "Principal" 207#define COL_ENCTYPE "Enctype" 208 209 rtbl_add_column(ct, COL_PRINCIPAL, 0); 210 rtbl_add_column(ct, COL_ISSUED, 0); 211 rtbl_add_column(ct, COL_EXPIRES, 0); 212 rtbl_add_column(ct, COL_ENCTYPE, 0); 213 rtbl_set_separator(ct, " "); 214 215 HEIMDAL_MUTEX_lock(&cred->cred_id_mutex); 216 217 ret = krb5_cc_start_seq_get (context, cred->ccache, &cursor); 218 if (ret) 219 goto out; 220 221 while ((ret = krb5_cc_next_cred (context, cred->ccache, &cursor, &creds)) == 0) { 222 223 ret = krb5_unparse_name(context, creds.server, &str); 224 if (ret) 225 goto next; 226 227 rtbl_add_column_entry(ct, COL_PRINCIPAL, str); 228 free(str); 229 230 rtbl_add_column_entry(ct, COL_ISSUED, 231 printable_time(creds.times.starttime)); 232 if (time(NULL) < creds.times.endtime) 233 rtbl_add_column_entry(ct, COL_EXPIRES, 234 printable_time(creds.times.endtime)); 235 else 236 rtbl_add_column_entry(ct, COL_EXPIRES, "Expired"); 237 238 ret = krb5_enctype_to_string(context, creds.session.keytype, &str); 239 if (ret) 240 goto next; 241 rtbl_add_column_entry(ct, COL_ENCTYPE, str); 242 free(str); 243 244 next: 245 krb5_free_cred_contents (context, &creds); 246 } 247 (void)krb5_cc_end_seq_get (context, cred->ccache, &cursor); 248 249 if (ret != KRB5_CC_END) 250 goto out; 251 252 253 buffer.value = rtbl_format_str(ct); 254 if (buffer.value == NULL) { 255 *minor_status = ENOMEM; 256 return GSS_S_FAILURE; 257 } 258 buffer.length = strlen((char *)buffer.value); 259 260 ret = gss_add_buffer_set_member(minor_status, &buffer, data_set); 261 free(buffer.value); 262 if (ret != GSS_S_COMPLETE) 263 _gsskrb5_clear_status (); 264 265 266#ifdef HAVE_KCM 267 /* 268 * kcm status 269 */ 270 271 ret = krb5_cc_get_config(context, cred->ccache, NULL, "kcm-status", &data); 272 if (ret == 0) { 273 uint32_t num; 274 int status = -1, kcmret = -1; 275 if (data.length >= 8) { 276 memcpy(&num, ((int8_t*)data.data) + 4, sizeof(num)); 277 status = (int)ntohl(num); 278 } 279 if (data.length >= 12) { 280 memcpy(&num, ((int8_t*)data.data) + 8, sizeof(num)); 281 kcmret = (int)ntohl(num); 282 } 283 ret = asprintf(&str, "kcm-status: %s ret: %d", 284 _krb5_kcm_get_status(status), kcmret); 285 krb5_data_free(&data); 286 if (ret < 0) 287 goto out; 288 buffer.value = str; 289 buffer.length = strlen(str); 290 291 ret = gss_add_buffer_set_member(minor_status, &buffer, data_set); 292 free(str); 293 if (ret != GSS_S_COMPLETE) 294 _gsskrb5_clear_status (); 295 } 296#else 297 buffer.value = NULL; 298 buffer.length = 0; 299 (void)gss_add_buffer_set_member(minor_status, &buffer, data_set); 300#endif 301 ret = 0; 302 303 out: 304 HEIMDAL_MUTEX_unlock(&cred->cred_id_mutex); 305 306 if (ct) 307 rtbl_destroy(ct); 308 309 if (ret) { 310 *minor_status = ret; 311 return GSS_S_FAILURE; 312 } 313 314 return GSS_S_COMPLETE; 315 316 } else if (gss_oid_equal(desired_object, GSS_C_CRED_SET_DEFAULT)) { 317 318 HEIMDAL_MUTEX_lock(&cred->cred_id_mutex); 319 320 if (cred->ccache) 321 ret = krb5_cc_switch(context, cred->ccache); 322 else 323 ret = EINVAL; 324 325 HEIMDAL_MUTEX_unlock(&cred->cred_id_mutex); 326 327 if (ret) { 328 *minor_status = ret; 329 return GSS_S_FAILURE; 330 } 331 332 buffer.value = (void *)"default"; 333 buffer.length = 7; 334 return gss_add_buffer_set_member(minor_status, &buffer, data_set); 335 336 } else if (gss_oid_equal(desired_object, GSS_C_CRED_GET_DEFAULT)) { 337 const char *defname; 338 char *fn = NULL; 339 340 HEIMDAL_MUTEX_lock(&cred->cred_id_mutex); 341 ret = krb5_cc_get_full_name(context, cred->ccache, &fn); 342 HEIMDAL_MUTEX_unlock(&cred->cred_id_mutex); 343 344 if (ret) { 345 *minor_status = ret; 346 return GSS_S_FAILURE; 347 } 348 349 defname = krb5_cc_default_name(context); 350 if (defname && strcmp(fn, defname) == 0) 351 ret = 0; 352 else 353 ret = EINVAL; 354 free(fn); 355 356 if (ret) { 357 *minor_status = ret; 358 return GSS_S_FAILURE; 359 } 360 361 buffer.value = (void *)"default"; 362 buffer.length = 7; 363 return gss_add_buffer_set_member(minor_status, &buffer, data_set); 364 365 } else if (gss_oid_equal(desired_object, GSS_C_CRED_RENEW)) { 366 krb5_creds in, *out = NULL; 367 krb5_kdc_flags flags; 368 const char *realm; 369 370 memset(&in, 0, sizeof(in)); 371 372 HEIMDAL_MUTEX_lock(&cred->cred_id_mutex); 373 374 ret = krb5_cc_get_principal(context, cred->ccache, &in.client); 375 if(ret) { 376 HEIMDAL_MUTEX_unlock(&cred->cred_id_mutex); 377 *minor_status = ret; 378 return GSS_S_FAILURE; 379 } 380 realm = krb5_principal_get_realm(context, in.client); 381 ret = krb5_make_principal(context, &in.server, realm, KRB5_TGS_NAME, realm, NULL); 382 if(ret) { 383 HEIMDAL_MUTEX_unlock(&cred->cred_id_mutex); 384 krb5_free_cred_contents(context, &in); 385 *minor_status = ret; 386 return GSS_S_FAILURE; 387 } 388 389 ret = krb5_get_credentials(context, KRB5_GC_CACHED, cred->ccache, &in, &out); 390 if (ret) { 391 HEIMDAL_MUTEX_unlock(&cred->cred_id_mutex); 392 krb5_free_cred_contents(context, &in); 393 *minor_status = ret; 394 return GSS_S_FAILURE; 395 } 396 397 if (out->flags.b.renewable == 0) { 398 HEIMDAL_MUTEX_unlock(&cred->cred_id_mutex); 399 krb5_free_cred_contents(context, &in); 400 krb5_free_creds(context, out); 401 krb5_set_error_message(context, GSS_KRB5_S_G_BAD_USAGE, "Credential is not renewable"); 402 *minor_status = GSS_KRB5_S_G_BAD_USAGE; 403 return GSS_S_FAILURE; 404 } 405 406 flags.i = 0; 407 flags.b.renewable = out->flags.b.renewable; 408 flags.b.forwardable = out->flags.b.forwardable; 409 flags.b.proxiable = out->flags.b.proxiable; 410 411 krb5_free_creds(context, out); 412 out = NULL; 413 414 ret = krb5_get_kdc_cred(context, 415 cred->ccache, 416 flags, 417 NULL, 418 NULL, 419 &in, 420 &out); 421 if(ret == 0) { 422 (void)krb5_cc_remove_cred(context, cred->ccache, 0, &in); 423 ret = krb5_cc_store_cred(context, cred->ccache, out); 424 krb5_free_creds (context, out); 425 } 426 krb5_free_cred_contents(context, &in); 427 HEIMDAL_MUTEX_unlock(&cred->cred_id_mutex); 428 429 if (ret) { 430 *minor_status = ret; 431 return GSS_S_FAILURE; 432 } 433 434 buffer.value = (void *)"renewed"; 435 buffer.length = 7; 436 return gss_add_buffer_set_member(minor_status, &buffer, data_set); 437 438 } else { 439 *minor_status = EINVAL; 440 return GSS_S_FAILURE; 441 } 442} 443 444