1 /* 2 * Copyright (c) 2002, 2010, Oracle and/or its affiliates. All rights reserved. 3 */ 4 5 /* 6 * lib/krb5/krb/send_tgs.c 7 * 8 * Copyright 1990,1991 by the Massachusetts Institute of Technology. 9 * All Rights Reserved. 10 * 11 * Export of this software from the United States of America may 12 * require a specific license from the United States Government. 13 * It is the responsibility of any person or organization contemplating 14 * export to obtain such a license before exporting. 15 * 16 * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and 17 * distribute this software and its documentation for any purpose and 18 * without fee is hereby granted, provided that the above copyright 19 * notice appear in all copies and that both that copyright notice and 20 * this permission notice appear in supporting documentation, and that 21 * the name of M.I.T. not be used in advertising or publicity pertaining 22 * to distribution of the software without specific, written prior 23 * permission. Furthermore if you modify this software you must label 24 * your software as modified software and not distribute it in such a 25 * fashion that it might be confused with the original M.I.T. software. 26 * M.I.T. makes no representations about the suitability of 27 * this software for any purpose. It is provided "as is" without express 28 * or implied warranty. 29 * 30 * 31 * krb5_send_tgs() 32 */ 33 34 #include "k5-int.h" 35 36 /* 37 Sends a request to the TGS and waits for a response. 38 options is used for the options in the KRB_TGS_REQ. 39 timestruct values are used for from, till, rtime " " " 40 enctype is used for enctype " " ", and to encrypt the authorization data, 41 sname is used for sname " " " 42 addrs, if non-NULL, is used for addresses " " " 43 authorization_dat, if non-NULL, is used for authorization_dat " " " 44 second_ticket, if required by options, is used for the 2nd ticket in the req. 45 in_cred is used for the ticket & session key in the KRB_AP_REQ header " " " 46 (the KDC realm is extracted from in_cred->server's realm) 47 48 The response is placed into *rep. 49 rep->response.data is set to point at allocated storage which should be 50 freed by the caller when finished. 51 52 returns system errors 53 */ 54 static krb5_error_code 55 krb5_send_tgs_basic(krb5_context context, krb5_data *in_data, krb5_creds *in_cred, krb5_data *outbuf) 56 { 57 krb5_error_code retval; 58 krb5_checksum checksum; 59 krb5_authenticator authent; 60 krb5_ap_req request; 61 krb5_data * scratch; 62 krb5_data * toutbuf; 63 64 /* Generate checksum */ 65 if ((retval = krb5_c_make_checksum(context, context->kdc_req_sumtype, 66 &in_cred->keyblock, 67 KRB5_KEYUSAGE_TGS_REQ_AUTH_CKSUM, 68 in_data, &checksum))) { 69 free(checksum.contents); 70 return(retval); 71 } 72 73 /* gen authenticator */ 74 authent.subkey = 0; 75 authent.seq_number = 0; 76 authent.checksum = &checksum; 77 authent.client = in_cred->client; 78 authent.authorization_data = in_cred->authdata; 79 if ((retval = krb5_us_timeofday(context, &authent.ctime, 80 &authent.cusec))) { 81 free(checksum.contents); 82 return(retval); 83 } 84 85 /* encode the authenticator */ 86 if ((retval = encode_krb5_authenticator(&authent, &scratch))) { 87 free(checksum.contents); 88 return(retval); 89 } 90 91 free(checksum.contents); 92 93 request.authenticator.ciphertext.data = 0; 94 request.authenticator.kvno = 0; 95 request.ap_options = 0; 96 request.ticket = 0; 97 98 if ((retval = decode_krb5_ticket(&(in_cred)->ticket, &request.ticket))) 99 /* Cleanup scratch and scratch data */ 100 goto cleanup_data; 101 102 /* call the encryption routine */ 103 if ((retval = krb5_encrypt_helper(context, &in_cred->keyblock, 104 KRB5_KEYUSAGE_TGS_REQ_AUTH, 105 scratch, &request.authenticator))) 106 goto cleanup_ticket; 107 108 retval = encode_krb5_ap_req(&request, &toutbuf); 109 /* Solaris Kerberos */ 110 if (retval == 0) { 111 *outbuf = *toutbuf; 112 krb5_xfree(toutbuf); 113 } 114 115 116 memset(request.authenticator.ciphertext.data, 0, 117 request.authenticator.ciphertext.length); 118 free(request.authenticator.ciphertext.data); 119 120 cleanup_ticket: 121 krb5_free_ticket(context, request.ticket); 122 123 cleanup_data: 124 memset(scratch->data, 0, scratch->length); 125 free(scratch->data); 126 127 free(scratch); 128 129 return retval; 130 } 131 132 krb5_error_code 133 krb5_send_tgs(krb5_context context, krb5_flags kdcoptions, 134 const krb5_ticket_times *timestruct, const krb5_enctype *ktypes, 135 krb5_const_principal sname, krb5_address *const *addrs, 136 krb5_authdata *const *authorization_data, 137 krb5_pa_data *const *padata, const krb5_data *second_ticket, 138 krb5_creds *in_cred, krb5_response *rep) 139 { 140 return (krb5_send_tgs2(context, kdcoptions, 141 timestruct, ktypes, 142 sname, addrs, 143 authorization_data, 144 padata, second_ticket, 145 in_cred, rep, 146 NULL)); 147 } 148 149 /* 150 * Solaris Kerberos 151 * Same as krb5_send_tgs plus an extra arg to return the FQDN 152 * of the KDC sent the request. 153 */ 154 krb5_error_code 155 krb5_send_tgs2(krb5_context context, krb5_flags kdcoptions, 156 const krb5_ticket_times *timestruct, const krb5_enctype *ktypes, 157 krb5_const_principal sname, krb5_address *const *addrs, 158 krb5_authdata *const *authorization_data, 159 krb5_pa_data *const *padata, const krb5_data *second_ticket, 160 krb5_creds *in_cred, krb5_response *rep, char **hostname_used) 161 { 162 krb5_error_code retval; 163 krb5_kdc_req tgsreq; 164 krb5_data *scratch, scratch2; 165 krb5_ticket *sec_ticket = 0; 166 krb5_ticket *sec_ticket_arr[2]; 167 krb5_timestamp time_now; 168 krb5_pa_data **combined_padata; 169 krb5_pa_data ap_req_padata; 170 int tcp_only = 0, use_master; 171 172 /* 173 * in_creds MUST be a valid credential NOT just a partially filled in 174 * place holder for us to get credentials for the caller. 175 */ 176 if (!in_cred->ticket.length) 177 return(KRB5_NO_TKT_SUPPLIED); 178 179 memset((char *)&tgsreq, 0, sizeof(tgsreq)); 180 181 tgsreq.kdc_options = kdcoptions; 182 tgsreq.server = (krb5_principal) sname; 183 184 tgsreq.from = timestruct->starttime; 185 tgsreq.till = timestruct->endtime ? timestruct->endtime : 186 in_cred->times.endtime; 187 tgsreq.rtime = timestruct->renew_till; 188 if ((retval = krb5_timeofday(context, &time_now))) 189 return(retval); 190 /* XXX we know they are the same size... */ 191 rep->expected_nonce = tgsreq.nonce = (krb5_int32) time_now; 192 rep->request_time = time_now; 193 194 tgsreq.addresses = (krb5_address **) addrs; 195 196 if (authorization_data) { 197 /* need to encrypt it in the request */ 198 199 if ((retval = encode_krb5_authdata(authorization_data, 200 &scratch))) 201 return(retval); 202 203 if ((retval = krb5_encrypt_helper(context, &in_cred->keyblock, 204 KRB5_KEYUSAGE_TGS_REQ_AD_SESSKEY, 205 scratch, 206 &tgsreq.authorization_data))) { 207 krb5_xfree(tgsreq.authorization_data.ciphertext.data); 208 krb5_free_data(context, scratch); 209 return retval; 210 } 211 212 krb5_free_data(context, scratch); 213 } 214 215 /* Get the encryption types list */ 216 if (ktypes) { 217 /* Check passed ktypes and make sure they're valid. */ 218 for (tgsreq.nktypes = 0; ktypes[tgsreq.nktypes]; tgsreq.nktypes++) { 219 if (!krb5_c_valid_enctype(ktypes[tgsreq.nktypes])) 220 return KRB5_PROG_ETYPE_NOSUPP; 221 } 222 tgsreq.ktype = (krb5_enctype *)ktypes; 223 } else { 224 /* Get the default ktypes */ 225 /* Solaris Kerberos */ 226 if ((retval = krb5_get_tgs_ktypes(context, sname, &(tgsreq.ktype)))) 227 goto send_tgs_error_2; 228 for(tgsreq.nktypes = 0; tgsreq.ktype[tgsreq.nktypes]; tgsreq.nktypes++); 229 } 230 231 if (second_ticket) { 232 if ((retval = decode_krb5_ticket(second_ticket, &sec_ticket))) 233 goto send_tgs_error_1; 234 sec_ticket_arr[0] = sec_ticket; 235 sec_ticket_arr[1] = 0; 236 tgsreq.second_ticket = sec_ticket_arr; 237 } else 238 tgsreq.second_ticket = 0; 239 240 /* encode the body; then checksum it */ 241 if ((retval = encode_krb5_kdc_req_body(&tgsreq, &scratch))) 242 goto send_tgs_error_2; 243 244 /* 245 * Get an ap_req. 246 */ 247 if ((retval = krb5_send_tgs_basic(context, scratch, in_cred, &scratch2))) { 248 krb5_free_data(context, scratch); 249 goto send_tgs_error_2; 250 } 251 krb5_free_data(context, scratch); 252 253 ap_req_padata.pa_type = KRB5_PADATA_AP_REQ; 254 ap_req_padata.length = scratch2.length; 255 ap_req_padata.contents = (krb5_octet *)scratch2.data; 256 257 /* combine in any other supplied padata */ 258 if (padata) { 259 krb5_pa_data * const * counter; 260 register unsigned int i = 0; 261 for (counter = padata; *counter; counter++, i++); 262 combined_padata = malloc((i+2) * sizeof(*combined_padata)); 263 if (!combined_padata) { 264 krb5_xfree(ap_req_padata.contents); 265 retval = ENOMEM; 266 goto send_tgs_error_2; 267 } 268 combined_padata[0] = &ap_req_padata; 269 for (i = 1, counter = padata; *counter; counter++, i++) 270 combined_padata[i] = (krb5_pa_data *) *counter; 271 combined_padata[i] = 0; 272 } else { 273 combined_padata = (krb5_pa_data **)malloc(2*sizeof(*combined_padata)); 274 if (!combined_padata) { 275 krb5_xfree(ap_req_padata.contents); 276 retval = ENOMEM; 277 goto send_tgs_error_2; 278 } 279 combined_padata[0] = &ap_req_padata; 280 combined_padata[1] = 0; 281 } 282 tgsreq.padata = combined_padata; 283 284 /* the TGS_REQ is assembled in tgsreq, so encode it */ 285 if ((retval = encode_krb5_tgs_req(&tgsreq, &scratch))) { 286 krb5_xfree(ap_req_padata.contents); 287 krb5_xfree(combined_padata); 288 goto send_tgs_error_2; 289 } 290 krb5_xfree(ap_req_padata.contents); 291 krb5_xfree(combined_padata); 292 293 /* now send request & get response from KDC */ 294 send_again: 295 use_master = 0; 296 retval = krb5_sendto_kdc2(context, scratch, 297 krb5_princ_realm(context, sname), 298 &rep->response, &use_master, tcp_only, 299 hostname_used); 300 if (retval == 0) { 301 if (krb5_is_krb_error(&rep->response)) { 302 if (!tcp_only) { 303 krb5_error *err_reply; 304 retval = decode_krb5_error(&rep->response, &err_reply); 305 /* Solaris Kerberos */ 306 if (retval == 0) { 307 if (err_reply->error == KRB_ERR_RESPONSE_TOO_BIG) { 308 tcp_only = 1; 309 krb5_free_error(context, err_reply); 310 free(rep->response.data); 311 rep->response.data = 0; 312 goto send_again; 313 } 314 krb5_free_error(context, err_reply); 315 } 316 } 317 } else if (krb5_is_tgs_rep(&rep->response)) 318 rep->message_type = KRB5_TGS_REP; 319 else /* XXX: assume it's an error */ 320 rep->message_type = KRB5_ERROR; 321 } 322 323 krb5_free_data(context, scratch); 324 325 send_tgs_error_2:; 326 if (sec_ticket) 327 krb5_free_ticket(context, sec_ticket); 328 329 send_tgs_error_1:; 330 if (ktypes == NULL) 331 krb5_xfree(tgsreq.ktype); 332 if (tgsreq.authorization_data.ciphertext.data) { 333 memset(tgsreq.authorization_data.ciphertext.data, 0, 334 tgsreq.authorization_data.ciphertext.length); 335 krb5_xfree(tgsreq.authorization_data.ciphertext.data); 336 } 337 338 return retval; 339 } 340