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