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 krb5_error_code retval; 141 krb5_kdc_req tgsreq; 142 krb5_data *scratch, scratch2; 143 krb5_ticket *sec_ticket = 0; 144 krb5_ticket *sec_ticket_arr[2]; 145 krb5_timestamp time_now; 146 krb5_pa_data **combined_padata; 147 krb5_pa_data ap_req_padata; 148 int tcp_only = 0, use_master; 149 150 /* 151 * in_creds MUST be a valid credential NOT just a partially filled in 152 * place holder for us to get credentials for the caller. 153 */ 154 if (!in_cred->ticket.length) 155 return(KRB5_NO_TKT_SUPPLIED); 156 157 memset((char *)&tgsreq, 0, sizeof(tgsreq)); 158 159 tgsreq.kdc_options = kdcoptions; 160 tgsreq.server = (krb5_principal) sname; 161 162 tgsreq.from = timestruct->starttime; 163 tgsreq.till = timestruct->endtime ? timestruct->endtime : 164 in_cred->times.endtime; 165 tgsreq.rtime = timestruct->renew_till; 166 if ((retval = krb5_timeofday(context, &time_now))) 167 return(retval); 168 /* XXX we know they are the same size... */ 169 rep->expected_nonce = tgsreq.nonce = (krb5_int32) time_now; 170 rep->request_time = time_now; 171 172 tgsreq.addresses = (krb5_address **) addrs; 173 174 if (authorization_data) { 175 /* need to encrypt it in the request */ 176 177 if ((retval = encode_krb5_authdata(authorization_data, 178 &scratch))) 179 return(retval); 180 181 if ((retval = krb5_encrypt_helper(context, &in_cred->keyblock, 182 KRB5_KEYUSAGE_TGS_REQ_AD_SESSKEY, 183 scratch, 184 &tgsreq.authorization_data))) { 185 krb5_xfree(tgsreq.authorization_data.ciphertext.data); 186 krb5_free_data(context, scratch); 187 return retval; 188 } 189 190 krb5_free_data(context, scratch); 191 } 192 193 /* Get the encryption types list */ 194 if (ktypes) { 195 /* Check passed ktypes and make sure they're valid. */ 196 for (tgsreq.nktypes = 0; ktypes[tgsreq.nktypes]; tgsreq.nktypes++) { 197 if (!krb5_c_valid_enctype(ktypes[tgsreq.nktypes])) 198 return KRB5_PROG_ETYPE_NOSUPP; 199 } 200 tgsreq.ktype = (krb5_enctype *)ktypes; 201 } else { 202 /* Get the default ktypes */ 203 /* Solaris Kerberos */ 204 if ((retval = krb5_get_tgs_ktypes(context, sname, &(tgsreq.ktype)))) 205 goto send_tgs_error_2; 206 for(tgsreq.nktypes = 0; tgsreq.ktype[tgsreq.nktypes]; tgsreq.nktypes++); 207 } 208 209 if (second_ticket) { 210 if ((retval = decode_krb5_ticket(second_ticket, &sec_ticket))) 211 goto send_tgs_error_1; 212 sec_ticket_arr[0] = sec_ticket; 213 sec_ticket_arr[1] = 0; 214 tgsreq.second_ticket = sec_ticket_arr; 215 } else 216 tgsreq.second_ticket = 0; 217 218 /* encode the body; then checksum it */ 219 if ((retval = encode_krb5_kdc_req_body(&tgsreq, &scratch))) 220 goto send_tgs_error_2; 221 222 /* 223 * Get an ap_req. 224 */ 225 if ((retval = krb5_send_tgs_basic(context, scratch, in_cred, &scratch2))) { 226 krb5_free_data(context, scratch); 227 goto send_tgs_error_2; 228 } 229 krb5_free_data(context, scratch); 230 231 ap_req_padata.pa_type = KRB5_PADATA_AP_REQ; 232 ap_req_padata.length = scratch2.length; 233 ap_req_padata.contents = (krb5_octet *)scratch2.data; 234 235 /* combine in any other supplied padata */ 236 if (padata) { 237 krb5_pa_data * const * counter; 238 register unsigned int i = 0; 239 for (counter = padata; *counter; counter++, i++); 240 combined_padata = malloc((i+2) * sizeof(*combined_padata)); 241 if (!combined_padata) { 242 krb5_xfree(ap_req_padata.contents); 243 retval = ENOMEM; 244 goto send_tgs_error_2; 245 } 246 combined_padata[0] = &ap_req_padata; 247 for (i = 1, counter = padata; *counter; counter++, i++) 248 combined_padata[i] = (krb5_pa_data *) *counter; 249 combined_padata[i] = 0; 250 } else { 251 combined_padata = (krb5_pa_data **)malloc(2*sizeof(*combined_padata)); 252 if (!combined_padata) { 253 krb5_xfree(ap_req_padata.contents); 254 retval = ENOMEM; 255 goto send_tgs_error_2; 256 } 257 combined_padata[0] = &ap_req_padata; 258 combined_padata[1] = 0; 259 } 260 tgsreq.padata = combined_padata; 261 262 /* the TGS_REQ is assembled in tgsreq, so encode it */ 263 if ((retval = encode_krb5_tgs_req(&tgsreq, &scratch))) { 264 krb5_xfree(ap_req_padata.contents); 265 krb5_xfree(combined_padata); 266 goto send_tgs_error_2; 267 } 268 krb5_xfree(ap_req_padata.contents); 269 krb5_xfree(combined_padata); 270 271 /* now send request & get response from KDC */ 272 send_again: 273 use_master = 0; 274 retval = krb5_sendto_kdc(context, scratch, 275 krb5_princ_realm(context, sname), 276 &rep->response, &use_master, tcp_only); 277 if (retval == 0) { 278 if (krb5_is_krb_error(&rep->response)) { 279 if (!tcp_only) { 280 krb5_error *err_reply; 281 retval = decode_krb5_error(&rep->response, &err_reply); 282 /* Solaris Kerberos */ 283 if (retval == 0) { 284 if (err_reply->error == KRB_ERR_RESPONSE_TOO_BIG) { 285 tcp_only = 1; 286 krb5_free_error(context, err_reply); 287 free(rep->response.data); 288 rep->response.data = 0; 289 goto send_again; 290 } 291 krb5_free_error(context, err_reply); 292 } 293 } 294 } else if (krb5_is_tgs_rep(&rep->response)) 295 rep->message_type = KRB5_TGS_REP; 296 else /* XXX: assume it's an error */ 297 rep->message_type = KRB5_ERROR; 298 } 299 300 krb5_free_data(context, scratch); 301 302 send_tgs_error_2:; 303 if (sec_ticket) 304 krb5_free_ticket(context, sec_ticket); 305 306 send_tgs_error_1:; 307 if (ktypes == NULL) 308 krb5_xfree(tgsreq.ktype); 309 if (tgsreq.authorization_data.ciphertext.data) { 310 memset(tgsreq.authorization_data.ciphertext.data, 0, 311 tgsreq.authorization_data.ciphertext.length); 312 krb5_xfree(tgsreq.authorization_data.ciphertext.data); 313 } 314 315 return retval; 316 } 317