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