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