1 /* 2 * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 3 * Use is subject to license terms. 4 */ 5 6 7 /* 8 * NAME 9 * cred.c 10 * 11 * DESCRIPTION 12 * Provide an interface to assemble and disassemble krb5_cred 13 * structures. 14 * 15 */ 16 #include "k5-int.h" 17 #include "cleanup.h" 18 #include "auth_con.h" 19 20 #include <stddef.h> /* NULL */ 21 #include <stdlib.h> /* malloc */ 22 #include <errno.h> /* ENOMEM */ 23 24 /*-------------------- encrypt_credencpart --------------------*/ 25 26 /*ARGSUSED*/ 27 /* 28 * encrypt the enc_part of krb5_cred 29 */ 30 static krb5_error_code 31 encrypt_credencpart(krb5_context context, krb5_cred_enc_part *pcredpart, krb5_keyblock *pkeyblock, krb5_enc_data *pencdata) 32 { 33 krb5_error_code retval; 34 krb5_data * scratch; 35 36 /* start by encoding to-be-encrypted part of the message */ 37 if ((retval = encode_krb5_enc_cred_part(pcredpart, &scratch))) 38 return retval; 39 40 /* 41 * If the keyblock is NULL, just copy the data from the encoded 42 * data to the ciphertext area. 43 */ 44 if (pkeyblock == NULL) { 45 pencdata->ciphertext.data = scratch->data; 46 pencdata->ciphertext.length = scratch->length; 47 krb5_xfree(scratch); 48 return 0; 49 } 50 51 /* call the encryption routine */ 52 retval = krb5_encrypt_helper(context, pkeyblock, 53 KRB5_KEYUSAGE_KRB_CRED_ENCPART, 54 scratch, pencdata); 55 56 if (retval) { 57 memset(pencdata->ciphertext.data, 0, pencdata->ciphertext.length); 58 free(pencdata->ciphertext.data); 59 pencdata->ciphertext.length = 0; 60 pencdata->ciphertext.data = 0; 61 } 62 63 memset(scratch->data, 0, scratch->length); 64 krb5_free_data(context, scratch); 65 66 return retval; 67 } 68 69 /*----------------------- krb5_mk_ncred_basic -----------------------*/ 70 71 static krb5_error_code 72 krb5_mk_ncred_basic(krb5_context context, krb5_creds **ppcreds, krb5_int32 nppcreds, krb5_keyblock *keyblock, krb5_replay_data *replaydata, krb5_address *local_addr, krb5_address *remote_addr, krb5_cred *pcred) 73 { 74 krb5_cred_enc_part credenc; 75 krb5_error_code retval; 76 size_t size; 77 int i; 78 79 credenc.magic = KV5M_CRED_ENC_PART; 80 81 credenc.s_address = 0; 82 credenc.r_address = 0; 83 if (local_addr) krb5_copy_addr(context, local_addr, &credenc.s_address); 84 if (remote_addr) krb5_copy_addr(context, remote_addr, &credenc.r_address); 85 86 credenc.nonce = replaydata->seq; 87 credenc.usec = replaydata->usec; 88 credenc.timestamp = replaydata->timestamp; 89 90 /* Get memory for creds and initialize it */ 91 size = sizeof(krb5_cred_info *) * (nppcreds + 1); 92 credenc.ticket_info = (krb5_cred_info **) malloc(size); 93 if (credenc.ticket_info == NULL) 94 return ENOMEM; 95 memset(credenc.ticket_info, 0, size); 96 97 /* 98 * For each credential in the list, initialize a cred info 99 * structure and copy the ticket into the ticket list. 100 */ 101 for (i = 0; i < nppcreds; i++) { 102 credenc.ticket_info[i] = malloc(sizeof(krb5_cred_info)); 103 if (credenc.ticket_info[i] == NULL) { 104 retval = ENOMEM; 105 goto cleanup; 106 } 107 credenc.ticket_info[i+1] = NULL; 108 109 credenc.ticket_info[i]->magic = KV5M_CRED_INFO; 110 credenc.ticket_info[i]->times = ppcreds[i]->times; 111 credenc.ticket_info[i]->flags = ppcreds[i]->ticket_flags; 112 113 if ((retval = decode_krb5_ticket(&ppcreds[i]->ticket, 114 &pcred->tickets[i]))) 115 goto cleanup; 116 117 if ((retval = krb5_copy_keyblock(context, &ppcreds[i]->keyblock, 118 &credenc.ticket_info[i]->session))) 119 goto cleanup; 120 121 if ((retval = krb5_copy_principal(context, ppcreds[i]->client, 122 &credenc.ticket_info[i]->client))) 123 goto cleanup; 124 125 if ((retval = krb5_copy_principal(context, ppcreds[i]->server, 126 &credenc.ticket_info[i]->server))) 127 goto cleanup; 128 129 if ((retval = krb5_copy_addresses(context, ppcreds[i]->addresses, 130 &credenc.ticket_info[i]->caddrs))) 131 goto cleanup; 132 } 133 134 /* 135 * NULL terminate the lists. 136 */ 137 pcred->tickets[i] = NULL; 138 139 /* encrypt the credential encrypted part */ 140 retval = encrypt_credencpart(context, &credenc, keyblock, 141 &pcred->enc_part); 142 143 cleanup: 144 krb5_free_cred_enc_part(context, &credenc); 145 return retval; 146 } 147 148 /*----------------------- krb5_mk_ncred -----------------------*/ 149 150 /* 151 * This functions takes as input an array of krb5_credentials, and 152 * outputs an encoded KRB_CRED message suitable for krb5_rd_cred 153 */ 154 krb5_error_code KRB5_CALLCONV 155 krb5_mk_ncred(krb5_context context, krb5_auth_context auth_context, krb5_creds **ppcreds, krb5_data **ppdata, krb5_replay_data *outdata) 156 { 157 krb5_address * premote_fulladdr = NULL; 158 krb5_address * plocal_fulladdr = NULL; 159 krb5_address remote_fulladdr; 160 krb5_address local_fulladdr; 161 krb5_error_code retval; 162 krb5_keyblock * keyblock; 163 krb5_replay_data replaydata; 164 krb5_cred * pcred; 165 krb5_int32 ncred; 166 167 local_fulladdr.contents = 0; 168 remote_fulladdr.contents = 0; 169 memset(&replaydata, 0, sizeof(krb5_replay_data)); 170 171 if (ppcreds == NULL) { 172 return KRB5KRB_AP_ERR_BADADDR; 173 } 174 175 /* 176 * Allocate memory for a NULL terminated list of tickets. 177 */ 178 for (ncred = 0; ppcreds[ncred]; ncred++); 179 180 if ((pcred = (krb5_cred *)malloc(sizeof(krb5_cred))) == NULL) 181 return ENOMEM; 182 memset(pcred, 0, sizeof(krb5_cred)); 183 184 if ((pcred->tickets 185 = (krb5_ticket **)malloc(sizeof(krb5_ticket *) * (ncred + 1))) == NULL) { 186 retval = ENOMEM; 187 free(pcred); 188 } 189 memset(pcred->tickets, 0, sizeof(krb5_ticket *) * (ncred +1)); 190 191 /* Get keyblock */ 192 if ((keyblock = auth_context->send_subkey) == NULL) 193 keyblock = auth_context->keyblock; 194 195 /* Get replay info */ 196 if ((auth_context->auth_context_flags & KRB5_AUTH_CONTEXT_DO_TIME) && 197 (auth_context->rcache == NULL)) 198 return KRB5_RC_REQUIRED; 199 200 if (((auth_context->auth_context_flags & KRB5_AUTH_CONTEXT_RET_TIME) || 201 (auth_context->auth_context_flags & KRB5_AUTH_CONTEXT_RET_SEQUENCE)) && 202 (outdata == NULL)) 203 /* Need a better error */ 204 return KRB5_RC_REQUIRED; 205 206 if ((retval = krb5_us_timeofday(context, &replaydata.timestamp, 207 &replaydata.usec))) 208 return retval; 209 if (auth_context->auth_context_flags & KRB5_AUTH_CONTEXT_RET_TIME) { 210 outdata->timestamp = replaydata.timestamp; 211 outdata->usec = replaydata.usec; 212 } 213 if ((auth_context->auth_context_flags & KRB5_AUTH_CONTEXT_DO_SEQUENCE) || 214 (auth_context->auth_context_flags & KRB5_AUTH_CONTEXT_RET_SEQUENCE)) { 215 replaydata.seq = auth_context->local_seq_number; 216 if (auth_context->auth_context_flags & KRB5_AUTH_CONTEXT_DO_SEQUENCE) { 217 auth_context->local_seq_number++; 218 } else { 219 outdata->seq = replaydata.seq; 220 } 221 } 222 223 if (auth_context->local_addr) { 224 if (auth_context->local_port) { 225 if ((retval = krb5_make_fulladdr(context, auth_context->local_addr, 226 auth_context->local_port, 227 &local_fulladdr))) 228 goto error; 229 plocal_fulladdr = &local_fulladdr; 230 } else { 231 plocal_fulladdr = auth_context->local_addr; 232 } 233 } 234 235 if (auth_context->remote_addr) { 236 if (auth_context->remote_port) { 237 if ((retval = krb5_make_fulladdr(context,auth_context->remote_addr, 238 auth_context->remote_port, 239 &remote_fulladdr))) 240 goto error; 241 premote_fulladdr = &remote_fulladdr; 242 } else { 243 premote_fulladdr = auth_context->remote_addr; 244 } 245 } 246 247 /* Setup creds structure */ 248 if ((retval = krb5_mk_ncred_basic(context, ppcreds, ncred, keyblock, 249 &replaydata, plocal_fulladdr, 250 premote_fulladdr, pcred))) { 251 goto error; 252 } 253 254 if (auth_context->auth_context_flags & KRB5_AUTH_CONTEXT_DO_TIME) { 255 krb5_donot_replay replay; 256 257 if ((retval = krb5_gen_replay_name(context, auth_context->local_addr, 258 "_forw", &replay.client))) 259 goto error; 260 261 replay.server = ""; /* XXX */ 262 replay.cusec = replaydata.usec; 263 replay.ctime = replaydata.timestamp; 264 if ((retval = krb5_rc_store(context, auth_context->rcache, &replay))) { 265 /* should we really error out here? XXX */ 266 krb5_xfree(replay.client); 267 goto error; 268 } 269 krb5_xfree(replay.client); 270 } 271 272 /* Encode creds structure */ 273 retval = encode_krb5_cred(pcred, ppdata); 274 275 error: 276 if (local_fulladdr.contents) 277 free(local_fulladdr.contents); 278 if (remote_fulladdr.contents) 279 free(remote_fulladdr.contents); 280 krb5_free_cred(context, pcred); 281 282 if (retval) { 283 if ((auth_context->auth_context_flags & KRB5_AUTH_CONTEXT_DO_SEQUENCE) 284 || (auth_context->auth_context_flags & KRB5_AUTH_CONTEXT_RET_SEQUENCE)) 285 auth_context->local_seq_number--; 286 } 287 return retval; 288 } 289 290 /*----------------------- krb5_mk_1cred -----------------------*/ 291 292 /* 293 * A convenience function that calls krb5_mk_ncred. 294 */ 295 krb5_error_code KRB5_CALLCONV 296 krb5_mk_1cred(krb5_context context, krb5_auth_context auth_context, krb5_creds *pcreds, krb5_data **ppdata, krb5_replay_data *outdata) 297 { 298 krb5_error_code retval; 299 krb5_creds **ppcreds; 300 301 if ((ppcreds = (krb5_creds **)malloc(sizeof(*ppcreds) * 2)) == NULL) { 302 return ENOMEM; 303 } 304 305 ppcreds[0] = pcreds; 306 ppcreds[1] = NULL; 307 308 retval = krb5_mk_ncred(context, auth_context, ppcreds, 309 ppdata, outdata); 310 311 free(ppcreds); 312 return retval; 313 } 314 315