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