1 /* 2 * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 3 * Use is subject to license terms. 4 */ 5 6 7 /* 8 * lib/krb5/krb/get_creds.c 9 * 10 * Copyright 1990 by the Massachusetts Institute of Technology. 11 * All Rights Reserved. 12 * 13 * Export of this software from the United States of America may 14 * require a specific license from the United States Government. 15 * It is the responsibility of any person or organization contemplating 16 * export to obtain such a license before exporting. 17 * 18 * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and 19 * distribute this software and its documentation for any purpose and 20 * without fee is hereby granted, provided that the above copyright 21 * notice appear in all copies and that both that copyright notice and 22 * this permission notice appear in supporting documentation, and that 23 * the name of M.I.T. not be used in advertising or publicity pertaining 24 * to distribution of the software without specific, written prior 25 * permission. Furthermore if you modify this software you must label 26 * your software as modified software and not distribute it in such a 27 * fashion that it might be confused with the original M.I.T. software. 28 * M.I.T. makes no representations about the suitability of 29 * this software for any purpose. It is provided "as is" without express 30 * or implied warranty. 31 * 32 * 33 * krb5_get_credentials() 34 */ 35 36 37 38 /* 39 Attempts to use the credentials cache or TGS exchange to get an additional 40 ticket for the 41 client identified by in_creds->client, the server identified by 42 in_creds->server, with options options, expiration date specified in 43 in_creds->times.endtime (0 means as long as possible), session key type 44 specified in in_creds->keyblock.enctype (if non-zero) 45 46 Any returned ticket and intermediate ticket-granting tickets are 47 stored in ccache. 48 49 returns errors from encryption routines, system errors 50 */ 51 52 #include "k5-int.h" 53 54 /*ARGSUSED*/ 55 static krb5_error_code 56 krb5_get_credentials_core(krb5_context context, krb5_flags options, 57 krb5_creds *in_creds, krb5_creds *mcreds, 58 krb5_flags *fields) 59 { 60 /* Solaris Kerberos */ 61 krb5_error_code ret = 0; 62 63 if (!in_creds || !in_creds->server || !in_creds->client) 64 return EINVAL; 65 66 memset((char *)mcreds, 0, sizeof(krb5_creds)); 67 mcreds->magic = KV5M_CREDS; 68 /* 69 * Solaris Kerberos: 70 * Set endtime appropriately to make sure we do not rope in 71 * expired creds. If endtime is set to 0 (which it almost always 72 * is, courtesy memset/calloc) the krb5_cc_retrieve_cred() call in 73 * krb5_get_credentials() with KRB5_TC_MATCH_TIMES will 74 * succeed and return the expired cred. 75 * 76 * Hence, endtime below is set to "now" if in_creds->times.endtime 77 * is 0, so that krb5_cc_retrieve_cred fails and we get fresh creds, 78 * if necessary. But, if in_creds has a non-zero endtime, we honor it. 79 */ 80 if (in_creds->times.endtime != 0) 81 mcreds->times.endtime = in_creds->times.endtime; 82 else 83 if ((ret = krb5_timeofday(context, &mcreds->times.endtime)) != 0) 84 return (ret); 85 86 ret = krb5_copy_keyblock_data(context, &in_creds->keyblock, 87 &mcreds->keyblock); 88 if (ret) 89 return (ret); 90 91 mcreds->authdata = in_creds->authdata; 92 mcreds->server = in_creds->server; 93 mcreds->client = in_creds->client; 94 95 *fields = KRB5_TC_MATCH_TIMES /*XXX |KRB5_TC_MATCH_SKEY_TYPE */ 96 | KRB5_TC_MATCH_AUTHDATA 97 | KRB5_TC_SUPPORTED_KTYPES; 98 if (mcreds->keyblock.enctype) { 99 krb5_enctype *ktypes; 100 int i; 101 102 *fields |= KRB5_TC_MATCH_KTYPE; 103 ret = krb5_get_tgs_ktypes (context, mcreds->server, &ktypes); 104 for (i = 0; ktypes[i]; i++) 105 if (ktypes[i] == mcreds->keyblock.enctype) 106 break; 107 if (ktypes[i] == 0) 108 ret = KRB5_CC_NOT_KTYPE; 109 free (ktypes); 110 if (ret) 111 return ret; 112 } 113 if (options & KRB5_GC_USER_USER) { 114 /* also match on identical 2nd tkt and tkt encrypted in a 115 session key */ 116 *fields |= KRB5_TC_MATCH_2ND_TKT|KRB5_TC_MATCH_IS_SKEY; 117 mcreds->is_skey = TRUE; 118 mcreds->second_ticket = in_creds->second_ticket; 119 if (!in_creds->second_ticket.length) 120 return KRB5_NO_2ND_TKT; 121 } 122 123 return 0; 124 } 125 126 krb5_error_code KRB5_CALLCONV 127 krb5_get_credentials(krb5_context context, krb5_flags options, 128 krb5_ccache ccache, krb5_creds *in_creds, 129 krb5_creds **out_creds) 130 { 131 krb5_error_code retval; 132 krb5_creds mcreds; 133 krb5_creds *ncreds; 134 krb5_creds **tgts; 135 krb5_flags fields; 136 int not_ktype; 137 138 retval = krb5_get_credentials_core(context, options, 139 in_creds, 140 &mcreds, &fields); 141 142 if (retval) return retval; 143 144 if ((ncreds = (krb5_creds *)malloc(sizeof(krb5_creds))) == NULL) 145 return ENOMEM; 146 147 memset((char *)ncreds, 0, sizeof(krb5_creds)); 148 ncreds->magic = KV5M_CREDS; 149 150 /* The caller is now responsible for cleaning up in_creds */ 151 /* Solaris Kerberos */ 152 if ((retval = krb5_cc_retrieve_cred(context, ccache, fields, &mcreds, 153 ncreds)) !=0) { 154 krb5_xfree(ncreds); 155 ncreds = in_creds; 156 } else { 157 *out_creds = ncreds; 158 } 159 160 if ((retval != KRB5_CC_NOTFOUND && retval != KRB5_CC_NOT_KTYPE) 161 || options & KRB5_GC_CACHED) 162 return retval; 163 164 if (retval == KRB5_CC_NOT_KTYPE) 165 not_ktype = 1; 166 else 167 not_ktype = 0; 168 169 retval = krb5_get_cred_from_kdc(context, ccache, ncreds, out_creds, &tgts); 170 if (tgts) { 171 register int i = 0; 172 krb5_error_code rv2; 173 while (tgts[i]) { 174 /* Solaris Kerberos */ 175 if ((rv2 = krb5_cc_store_cred(context, ccache, tgts[i])) != 0) { 176 retval = rv2; 177 break; 178 } 179 i++; 180 } 181 krb5_free_tgt_creds(context, tgts); 182 } 183 /* 184 * Translate KRB5_CC_NOTFOUND if we previously got 185 * KRB5_CC_NOT_KTYPE from krb5_cc_retrieve_cred(), in order to 186 * handle the case where there is no TGT in the ccache and the 187 * input enctype didn't match. This handling is necessary because 188 * some callers, such as GSSAPI, iterate through enctypes and 189 * KRB5_CC_NOTFOUND passed through from the 190 * krb5_get_cred_from_kdc() is semantically incorrect, since the 191 * actual failure was the non-existence of a ticket of the correct 192 * enctype rather than the missing TGT. 193 */ 194 if ((retval == KRB5_CC_NOTFOUND || retval == KRB5_CC_NOT_KTYPE) 195 && not_ktype) 196 retval = KRB5_CC_NOT_KTYPE; 197 198 if (!retval) { 199 /* the purpose of the krb5_get_credentials call is to 200 * obtain a set of credentials for the caller. the 201 * krb5_cc_store_cred() call is to optimize performance 202 * for future calls. Ignore any errors, since the credentials 203 * are still valid even if we fail to store them in the cache. 204 */ 205 /* Solaris Kerberos */ 206 retval = krb5_cc_store_cred(context, ccache, *out_creds); 207 } 208 return retval; 209 } 210 211 #define INT_GC_VALIDATE 1 212 #define INT_GC_RENEW 2 213 214 /*ARGSUSED*/ 215 static krb5_error_code 216 krb5_get_credentials_val_renew_core(krb5_context context, krb5_flags options, 217 krb5_ccache ccache, krb5_creds *in_creds, 218 krb5_creds **out_creds, int which) 219 { 220 krb5_error_code retval; 221 krb5_principal tmp; 222 krb5_creds **tgts = 0; 223 224 switch(which) { 225 case INT_GC_VALIDATE: 226 retval = krb5_get_cred_from_kdc_validate(context, ccache, 227 in_creds, out_creds, &tgts); 228 break; 229 case INT_GC_RENEW: 230 retval = krb5_get_cred_from_kdc_renew(context, ccache, 231 in_creds, out_creds, &tgts); 232 break; 233 default: 234 /* Should never happen */ 235 retval = 255; 236 break; 237 } 238 if (retval) return retval; 239 if (tgts) krb5_free_tgt_creds(context, tgts); 240 241 retval = krb5_cc_get_principal(context, ccache, &tmp); 242 if (retval) return retval; 243 244 retval = krb5_cc_initialize(context, ccache, tmp); 245 /* Solaris Kerberos */ 246 if (retval) { 247 krb5_free_principal(context, tmp); 248 return retval; 249 } 250 251 retval = krb5_cc_store_cred(context, ccache, *out_creds); 252 krb5_free_principal(context, tmp); 253 return retval; 254 } 255 256 krb5_error_code KRB5_CALLCONV 257 krb5_get_credentials_validate(krb5_context context, krb5_flags options, 258 krb5_ccache ccache, krb5_creds *in_creds, 259 krb5_creds **out_creds) 260 { 261 return(krb5_get_credentials_val_renew_core(context, options, ccache, 262 in_creds, out_creds, 263 INT_GC_VALIDATE)); 264 } 265 266 krb5_error_code KRB5_CALLCONV 267 krb5_get_credentials_renew(krb5_context context, krb5_flags options, 268 krb5_ccache ccache, krb5_creds *in_creds, 269 krb5_creds **out_creds) 270 { 271 272 return(krb5_get_credentials_val_renew_core(context, options, ccache, 273 in_creds, out_creds, 274 INT_GC_RENEW)); 275 } 276 277 static krb5_error_code 278 krb5_validate_or_renew_creds(krb5_context context, krb5_creds *creds, 279 krb5_principal client, krb5_ccache ccache, 280 char *in_tkt_service, int validate) 281 { 282 krb5_error_code ret; 283 krb5_creds in_creds; /* only client and server need to be filled in */ 284 krb5_creds *out_creds = 0; /* for check before dereferencing below */ 285 krb5_creds **tgts; 286 287 memset((char *)&in_creds, 0, sizeof(krb5_creds)); 288 289 in_creds.server = NULL; 290 tgts = NULL; 291 292 in_creds.client = client; 293 294 if (in_tkt_service) { 295 /* this is ugly, because so are the data structures involved. I'm 296 in the library, so I'm going to manipulate the data structures 297 directly, otherwise, it will be worse. */ 298 299 if ((ret = krb5_parse_name(context, in_tkt_service, &in_creds.server))) 300 goto cleanup; 301 302 /* stuff the client realm into the server principal. 303 realloc if necessary */ 304 if (in_creds.server->realm.length < in_creds.client->realm.length) 305 if ((in_creds.server->realm.data = 306 (char *) realloc(in_creds.server->realm.data, 307 in_creds.client->realm.length)) == NULL) { 308 ret = ENOMEM; 309 goto cleanup; 310 } 311 312 in_creds.server->realm.length = in_creds.client->realm.length; 313 memcpy(in_creds.server->realm.data, in_creds.client->realm.data, 314 in_creds.client->realm.length); 315 } else { 316 if ((ret = krb5_build_principal_ext(context, &in_creds.server, 317 in_creds.client->realm.length, 318 in_creds.client->realm.data, 319 KRB5_TGS_NAME_SIZE, 320 KRB5_TGS_NAME, 321 in_creds.client->realm.length, 322 in_creds.client->realm.data, 323 0))) 324 goto cleanup; 325 } 326 327 if (validate) 328 ret = krb5_get_cred_from_kdc_validate(context, ccache, 329 &in_creds, &out_creds, &tgts); 330 else 331 ret = krb5_get_cred_from_kdc_renew(context, ccache, 332 &in_creds, &out_creds, &tgts); 333 334 /* ick. copy the struct contents, free the container */ 335 if (out_creds) { 336 *creds = *out_creds; 337 krb5_xfree(out_creds); 338 } 339 340 cleanup: 341 342 if (in_creds.server) 343 krb5_free_principal(context, in_creds.server); 344 if (tgts) 345 krb5_free_tgt_creds(context, tgts); 346 347 return(ret); 348 } 349 350 krb5_error_code KRB5_CALLCONV 351 krb5_get_validated_creds(krb5_context context, krb5_creds *creds, krb5_principal client, krb5_ccache ccache, char *in_tkt_service) 352 { 353 return(krb5_validate_or_renew_creds(context, creds, client, ccache, 354 in_tkt_service, 1)); 355 } 356 357 krb5_error_code KRB5_CALLCONV 358 krb5_get_renewed_creds(krb5_context context, krb5_creds *creds, krb5_principal client, krb5_ccache ccache, char *in_tkt_service) 359 { 360 return(krb5_validate_or_renew_creds(context, creds, client, ccache, 361 in_tkt_service, 0)); 362 } 363