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/send_tgs.c 9 * 10 * Copyright 1990,1991 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_send_tgs() 34 */ 35 36 #include "k5-int.h" 37 38 /* 39 Sends a request to the TGS and waits for a response. 40 options is used for the options in the KRB_TGS_REQ. 41 timestruct values are used for from, till, rtime " " " 42 enctype is used for enctype " " ", and to encrypt the authorization data, 43 sname is used for sname " " " 44 addrs, if non-NULL, is used for addresses " " " 45 authorization_dat, if non-NULL, is used for authorization_dat " " " 46 second_ticket, if required by options, is used for the 2nd ticket in the req. 47 in_cred is used for the ticket & session key in the KRB_AP_REQ header " " " 48 (the KDC realm is extracted from in_cred->server's realm) 49 50 The response is placed into *rep. 51 rep->response.data is set to point at allocated storage which should be 52 freed by the caller when finished. 53 54 returns system errors 55 */ 56 static krb5_error_code 57 krb5_send_tgs_basic(krb5_context context, krb5_data *in_data, krb5_creds *in_cred, krb5_data *outbuf) 58 { 59 krb5_error_code retval; 60 krb5_checksum checksum; 61 krb5_authenticator authent; 62 krb5_ap_req request; 63 krb5_data * scratch; 64 krb5_data * toutbuf; 65 66 /* Generate checksum */ 67 if ((retval = krb5_c_make_checksum(context, context->kdc_req_sumtype, 68 &in_cred->keyblock, 69 KRB5_KEYUSAGE_TGS_REQ_AUTH_CKSUM, 70 in_data, &checksum))) { 71 free(checksum.contents); 72 return(retval); 73 } 74 75 /* gen authenticator */ 76 authent.subkey = 0; 77 authent.seq_number = 0; 78 authent.checksum = &checksum; 79 authent.client = in_cred->client; 80 authent.authorization_data = in_cred->authdata; 81 if ((retval = krb5_us_timeofday(context, &authent.ctime, 82 &authent.cusec))) { 83 free(checksum.contents); 84 return(retval); 85 } 86 87 /* encode the authenticator */ 88 if ((retval = encode_krb5_authenticator(&authent, &scratch))) { 89 free(checksum.contents); 90 return(retval); 91 } 92 93 free(checksum.contents); 94 95 request.authenticator.ciphertext.data = 0; 96 request.authenticator.kvno = 0; 97 request.ap_options = 0; 98 request.ticket = 0; 99 100 if ((retval = decode_krb5_ticket(&(in_cred)->ticket, &request.ticket))) 101 /* Cleanup scratch and scratch data */ 102 goto cleanup_data; 103 104 /* call the encryption routine */ 105 if ((retval = krb5_encrypt_helper(context, &in_cred->keyblock, 106 KRB5_KEYUSAGE_TGS_REQ_AUTH, 107 scratch, &request.authenticator))) 108 goto cleanup_ticket; 109 110 retval = encode_krb5_ap_req(&request, &toutbuf); 111 *outbuf = *toutbuf; 112 krb5_xfree(toutbuf); 113 114 115 memset(request.authenticator.ciphertext.data, 0, 116 request.authenticator.ciphertext.length); 117 free(request.authenticator.ciphertext.data); 118 119 cleanup_ticket: 120 krb5_free_ticket(context, request.ticket); 121 122 cleanup_data: 123 memset(scratch->data, 0, scratch->length); 124 free(scratch->data); 125 126 free(scratch); 127 128 return retval; 129 } 130 131 krb5_error_code 132 krb5_send_tgs(krb5_context context, krb5_flags kdcoptions, 133 const krb5_ticket_times *timestruct, const krb5_enctype *ktypes, 134 krb5_const_principal sname, krb5_address *const *addrs, 135 krb5_authdata *const *authorization_data, 136 krb5_pa_data *const *padata, const krb5_data *second_ticket, 137 krb5_creds *in_cred, krb5_response *rep) 138 { 139 krb5_error_code retval; 140 krb5_kdc_req tgsreq; 141 krb5_data *scratch, scratch2; 142 krb5_ticket *sec_ticket = 0; 143 krb5_ticket *sec_ticket_arr[2]; 144 krb5_timestamp time_now; 145 krb5_pa_data **combined_padata; 146 krb5_pa_data ap_req_padata; 147 int tcp_only = 0, use_master; 148 149 /* 150 * in_creds MUST be a valid credential NOT just a partially filled in 151 * place holder for us to get credentials for the caller. 152 */ 153 if (!in_cred->ticket.length) 154 return(KRB5_NO_TKT_SUPPLIED); 155 156 memset((char *)&tgsreq, 0, sizeof(tgsreq)); 157 158 tgsreq.kdc_options = kdcoptions; 159 tgsreq.server = (krb5_principal) sname; 160 161 tgsreq.from = timestruct->starttime; 162 tgsreq.till = timestruct->endtime ? timestruct->endtime : 163 in_cred->times.endtime; 164 tgsreq.rtime = timestruct->renew_till; 165 if ((retval = krb5_timeofday(context, &time_now))) 166 return(retval); 167 /* XXX we know they are the same size... */ 168 rep->expected_nonce = tgsreq.nonce = (krb5_int32) time_now; 169 rep->request_time = time_now; 170 171 tgsreq.addresses = (krb5_address **) addrs; 172 173 if (authorization_data) { 174 /* need to encrypt it in the request */ 175 176 if ((retval = encode_krb5_authdata((const krb5_authdata**)authorization_data, 177 &scratch))) 178 return(retval); 179 180 if ((retval = krb5_encrypt_helper(context, &in_cred->keyblock, 181 KRB5_KEYUSAGE_TGS_REQ_AD_SESSKEY, 182 scratch, 183 &tgsreq.authorization_data))) { 184 krb5_xfree(tgsreq.authorization_data.ciphertext.data); 185 krb5_free_data(context, scratch); 186 return retval; 187 } 188 189 krb5_free_data(context, scratch); 190 } 191 192 /* Get the encryption types list */ 193 if (ktypes) { 194 /* Check passed ktypes and make sure they're valid. */ 195 for (tgsreq.nktypes = 0; ktypes[tgsreq.nktypes]; tgsreq.nktypes++) { 196 if (!krb5_c_valid_enctype(ktypes[tgsreq.nktypes])) 197 return KRB5_PROG_ETYPE_NOSUPP; 198 } 199 tgsreq.ktype = (krb5_enctype *)ktypes; 200 } else { 201 /* Get the default ktypes */ 202 krb5_get_tgs_ktypes(context, sname, &(tgsreq.ktype)); 203 for(tgsreq.nktypes = 0; tgsreq.ktype[tgsreq.nktypes]; tgsreq.nktypes++); 204 } 205 206 if (second_ticket) { 207 if ((retval = decode_krb5_ticket(second_ticket, &sec_ticket))) 208 goto send_tgs_error_1; 209 sec_ticket_arr[0] = sec_ticket; 210 sec_ticket_arr[1] = 0; 211 tgsreq.second_ticket = sec_ticket_arr; 212 } else 213 tgsreq.second_ticket = 0; 214 215 /* encode the body; then checksum it */ 216 if ((retval = encode_krb5_kdc_req_body(&tgsreq, &scratch))) 217 goto send_tgs_error_2; 218 219 /* 220 * Get an ap_req. 221 */ 222 if ((retval = krb5_send_tgs_basic(context, scratch, in_cred, &scratch2))) { 223 krb5_free_data(context, scratch); 224 goto send_tgs_error_2; 225 } 226 krb5_free_data(context, scratch); 227 228 ap_req_padata.pa_type = KRB5_PADATA_AP_REQ; 229 ap_req_padata.length = scratch2.length; 230 ap_req_padata.contents = (krb5_octet *)scratch2.data; 231 232 /* combine in any other supplied padata */ 233 if (padata) { 234 krb5_pa_data * const * counter; 235 register unsigned int i = 0; 236 for (counter = padata; *counter; counter++, i++); 237 combined_padata = malloc((i+2) * sizeof(*combined_padata)); 238 if (!combined_padata) { 239 krb5_xfree(ap_req_padata.contents); 240 retval = ENOMEM; 241 goto send_tgs_error_2; 242 } 243 combined_padata[0] = &ap_req_padata; 244 for (i = 1, counter = padata; *counter; counter++, i++) 245 combined_padata[i] = (krb5_pa_data *) *counter; 246 combined_padata[i] = 0; 247 } else { 248 combined_padata = (krb5_pa_data **)malloc(2*sizeof(*combined_padata)); 249 if (!combined_padata) { 250 krb5_xfree(ap_req_padata.contents); 251 retval = ENOMEM; 252 goto send_tgs_error_2; 253 } 254 combined_padata[0] = &ap_req_padata; 255 combined_padata[1] = 0; 256 } 257 tgsreq.padata = combined_padata; 258 259 /* the TGS_REQ is assembled in tgsreq, so encode it */ 260 if ((retval = encode_krb5_tgs_req(&tgsreq, &scratch))) { 261 krb5_xfree(ap_req_padata.contents); 262 krb5_xfree(combined_padata); 263 goto send_tgs_error_2; 264 } 265 krb5_xfree(ap_req_padata.contents); 266 krb5_xfree(combined_padata); 267 268 /* now send request & get response from KDC */ 269 send_again: 270 use_master = 0; 271 retval = krb5_sendto_kdc(context, scratch, 272 krb5_princ_realm(context, sname), 273 &rep->response, &use_master, tcp_only); 274 if (retval == 0) { 275 if (krb5_is_krb_error(&rep->response)) { 276 if (!tcp_only) { 277 krb5_error *err_reply; 278 retval = decode_krb5_error(&rep->response, &err_reply); 279 /* Solaris Kerberos */ 280 if (retval == 0) { 281 if (err_reply->error == KRB_ERR_RESPONSE_TOO_BIG) { 282 tcp_only = 1; 283 krb5_free_error(context, err_reply); 284 free(rep->response.data); 285 rep->response.data = 0; 286 goto send_again; 287 } 288 krb5_free_error(context, err_reply); 289 } 290 } 291 } else if (krb5_is_tgs_rep(&rep->response)) 292 rep->message_type = KRB5_TGS_REP; 293 else /* XXX: assume it's an error */ 294 rep->message_type = KRB5_ERROR; 295 } 296 297 krb5_free_data(context, scratch); 298 299 send_tgs_error_2:; 300 if (sec_ticket) 301 krb5_free_ticket(context, sec_ticket); 302 303 send_tgs_error_1:; 304 if (ktypes == NULL) 305 krb5_xfree(tgsreq.ktype); 306 if (tgsreq.authorization_data.ciphertext.data) { 307 memset(tgsreq.authorization_data.ciphertext.data, 0, 308 tgsreq.authorization_data.ciphertext.length); 309 krb5_xfree(tgsreq.authorization_data.ciphertext.data); 310 } 311 312 return retval; 313 } 314