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/mk_req_ext.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_mk_req_extended() 35 */ 36 37 38 #include <k5-int.h> 39 #include <auth_con.h> 40 41 /* 42 Formats a KRB_AP_REQ message into outbuf, with more complete options than 43 krb_mk_req. 44 45 outbuf, ap_req_options, checksum, and ccache are used in the 46 same fashion as for krb5_mk_req. 47 48 creds is used to supply the credentials (ticket and session key) needed 49 to form the request. 50 51 if creds->ticket has no data (length == 0), then a ticket is obtained 52 from either the cache or the TGS, passing creds to krb5_get_credentials(). 53 kdc_options specifies the options requested for the ticket to be used. 54 If a ticket with appropriate flags is not found in the cache, then these 55 options are passed on in a request to an appropriate KDC. 56 57 ap_req_options specifies the KRB_AP_REQ options desired. 58 59 if ap_req_options specifies AP_OPTS_USE_SESSION_KEY, then creds->ticket 60 must contain the appropriate ENC-TKT-IN-SKEY ticket. 61 62 checksum specifies the checksum to be used in the authenticator. 63 64 The outbuf buffer storage is allocated, and should be freed by the 65 caller when finished. 66 67 On an error return, the credentials pointed to by creds might have been 68 augmented with additional fields from the obtained credentials; the entire 69 credentials should be released by calling krb5_free_creds(). 70 71 returns system errors 72 */ 73 74 static krb5_error_code 75 krb5_generate_authenticator (krb5_context, 76 krb5_authenticator *, krb5_principal, 77 krb5_checksum *, krb5_keyblock *, 78 krb5_ui_4, krb5_authdata ** ); 79 80 krb5_error_code 81 krb5int_generate_and_save_subkey (krb5_context context, 82 krb5_auth_context auth_context, 83 krb5_keyblock *keyblock) 84 { 85 #if 0 86 /* 87 * Solaris Kerberos: Don't bother with this PRNG stuff, 88 * we have /dev/random and PKCS#11 to handle Random Numbers. 89 */ 90 /* Provide some more fodder for random number code. 91 This isn't strong cryptographically; the point here is not 92 to guarantee randomness, but to make it less likely that multiple 93 sessions could pick the same subkey. */ 94 struct { 95 krb5_int32 sec, usec; 96 } rnd_data; 97 krb5_data d; 98 99 krb5_crypto_us_timeofday (&rnd_data.sec, &rnd_data.usec); 100 d.length = sizeof (rnd_data); 101 d.data = (char *) &rnd_data; 102 (void) krb5_c_random_add_entropy (context, KRB5_C_RANDSOURCE_TIMING, &d); 103 #endif 104 krb5_error_code retval; 105 106 if (auth_context->send_subkey != NULL) { 107 krb5_free_keyblock(context, auth_context->send_subkey); 108 auth_context->send_subkey = NULL; 109 } 110 111 if ((retval = krb5_generate_subkey(context, keyblock, &auth_context->send_subkey))) 112 return retval; 113 114 if (auth_context->recv_subkey != NULL) { 115 krb5_free_keyblock(context, auth_context->recv_subkey); 116 auth_context->recv_subkey = NULL; 117 } 118 retval = krb5_copy_keyblock(context, auth_context->send_subkey, 119 &auth_context->recv_subkey); 120 if (retval) { 121 krb5_free_keyblock(context, auth_context->send_subkey); 122 auth_context->send_subkey = NULL; 123 return retval; 124 } 125 return 0; 126 } 127 128 krb5_error_code KRB5_CALLCONV 129 krb5_mk_req_extended( 130 krb5_context context, 131 krb5_auth_context * auth_context, 132 const krb5_flags ap_req_options, 133 krb5_data * in_data, 134 krb5_creds * in_creds, 135 krb5_data * outbuf) 136 { 137 krb5_error_code retval; 138 krb5_checksum checksum; 139 krb5_checksum *checksump = 0; 140 krb5_auth_context new_auth_context; 141 142 krb5_ap_req request; 143 krb5_data *scratch = 0; 144 krb5_data *toutbuf; 145 146 request.ap_options = ap_req_options & AP_OPTS_WIRE_MASK; 147 request.authenticator.ciphertext.data = 0; 148 request.ticket = 0; 149 150 if (!in_creds->ticket.length) 151 return(KRB5_NO_TKT_SUPPLIED); 152 153 /* we need a native ticket */ 154 if ((retval = decode_krb5_ticket(&(in_creds)->ticket, &request.ticket))) 155 return(retval); 156 157 /* verify that the ticket is not expired */ 158 if ((retval = krb5_validate_times(context, &in_creds->times))) 159 goto cleanup; 160 161 /* generate auth_context if needed */ 162 if (*auth_context == NULL) { 163 if ((retval = krb5_auth_con_init(context, &new_auth_context))) 164 goto cleanup; 165 *auth_context = new_auth_context; 166 } 167 168 /* set auth context keyblock */ 169 if ((*auth_context)->keyblock != NULL) { 170 krb5_free_keyblock(context, (*auth_context)->keyblock); 171 (*auth_context)->keyblock = NULL; 172 } 173 if ((retval = krb5_copy_keyblock(context, &in_creds->keyblock, 174 &((*auth_context)->keyblock)))) 175 goto cleanup; 176 177 /* generate seq number if needed */ 178 if ((((*auth_context)->auth_context_flags & KRB5_AUTH_CONTEXT_DO_SEQUENCE) 179 || ((*auth_context)->auth_context_flags & KRB5_AUTH_CONTEXT_RET_SEQUENCE)) 180 && ((*auth_context)->local_seq_number == 0)) 181 if ((retval = krb5_generate_seq_number(context, &in_creds->keyblock, 182 &(*auth_context)->local_seq_number))) 183 goto cleanup; 184 185 186 /* generate subkey if needed */ 187 if (!in_data &&(*auth_context)->checksum_func) { 188 retval = (*auth_context)->checksum_func(context, 189 *auth_context, 190 (*auth_context)->checksum_func_data, 191 &in_data); 192 if (retval) 193 goto cleanup; 194 } 195 196 if ((ap_req_options & AP_OPTS_USE_SUBKEY)&&(!(*auth_context)->send_subkey)) { 197 retval = krb5int_generate_and_save_subkey (context, *auth_context, 198 &in_creds->keyblock); 199 if (retval) 200 goto cleanup; 201 } 202 203 if (in_data) { 204 if ((*auth_context)->req_cksumtype == 0x8003) { 205 /* XXX Special hack for GSSAPI */ 206 checksum.checksum_type = 0x8003; 207 checksum.length = in_data->length; 208 checksum.contents = (krb5_octet *) in_data->data; 209 } else { 210 retval = krb5_c_make_checksum(context, 211 (*auth_context)->req_cksumtype, 212 (*auth_context)->keyblock, 213 KRB5_KEYUSAGE_AP_REQ_AUTH_CKSUM, 214 in_data, &checksum); 215 if (retval) 216 goto cleanup_cksum; 217 } 218 checksump = &checksum; 219 } 220 221 /* Generate authenticator */ 222 if (((*auth_context)->authentp = (krb5_authenticator *)malloc(sizeof( 223 krb5_authenticator))) == NULL) { 224 retval = ENOMEM; 225 goto cleanup_cksum; 226 } 227 228 if ((retval = krb5_generate_authenticator(context, 229 (*auth_context)->authentp, 230 (in_creds)->client, checksump, 231 (*auth_context)->send_subkey, 232 (*auth_context)->local_seq_number, 233 (in_creds)->authdata))) 234 goto cleanup_cksum; 235 236 /* encode the authenticator */ 237 if ((retval = encode_krb5_authenticator((*auth_context)->authentp, 238 &scratch))) 239 goto cleanup_cksum; 240 241 /* Null out these fields, to prevent pointer sharing problems; 242 * they were supplied by the caller 243 */ 244 (*auth_context)->authentp->client = NULL; 245 (*auth_context)->authentp->checksum = NULL; 246 (*auth_context)->authentp->authorization_data = NULL; 247 248 /* call the encryption routine */ 249 retval = krb5_encrypt_helper(context, &in_creds->keyblock, 250 KRB5_KEYUSAGE_AP_REQ_AUTH, 251 scratch, &request.authenticator); 252 if (retval) 253 goto cleanup_cksum; 254 255 if ((retval = encode_krb5_ap_req(&request, &toutbuf))) 256 goto cleanup_cksum; 257 #ifdef HAVE_C_STRUCTURE_ASSIGNMENT 258 *outbuf = *toutbuf; 259 #else 260 memcpy(outbuf, toutbuf, sizeof(krb5_data)); 261 #endif 262 263 krb5_xfree(toutbuf); 264 265 cleanup_cksum: 266 if (checksump && checksump->checksum_type != 0x8003) 267 free(checksump->contents); 268 269 cleanup: 270 if (request.ticket) 271 krb5_free_ticket(context, request.ticket); 272 if (request.authenticator.ciphertext.data) { 273 (void) memset(request.authenticator.ciphertext.data, 0, 274 request.authenticator.ciphertext.length); 275 free(request.authenticator.ciphertext.data); 276 } 277 if (scratch) { 278 memset(scratch->data, 0, scratch->length); 279 krb5_xfree(scratch->data); 280 krb5_xfree(scratch); 281 } 282 return retval; 283 } 284 285 static krb5_error_code 286 krb5_generate_authenticator( 287 krb5_context context, 288 krb5_authenticator *authent, 289 krb5_principal client, 290 krb5_checksum *cksum, 291 krb5_keyblock *key, 292 krb5_ui_4 seq_number, 293 krb5_authdata **authorization) 294 { 295 krb5_error_code retval; 296 297 authent->client = client; 298 authent->checksum = cksum; 299 if (key) { 300 retval = krb5_copy_keyblock(context, key, &authent->subkey); 301 if (retval) 302 return retval; 303 } else 304 authent->subkey = 0; 305 authent->seq_number = seq_number; 306 authent->authorization_data = authorization; 307 308 return(krb5_us_timeofday(context, &authent->ctime, &authent->cusec)); 309 } 310