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