krb5_error_code KRB5_LIB_FUNCTION krb5_get_forwarded_creds (krb5_context context, krb5_auth_context auth_context, krb5_ccache ccache, krb5_flags flags, const char *hostname, krb5_creds *in_creds, krb5_data *out_data) { krb5_error_code ret; krb5_creds *out_creds; krb5_addresses addrs, *paddrs; KRB_CRED cred; KrbCredInfo *krb_cred_info; EncKrbCredPart enc_krb_cred_part; size_t len; unsigned char *buf; size_t buf_size; krb5_kdc_flags kdc_flags; krb5_crypto crypto; struct addrinfo *ai; int save_errno; krb5_creds *ticket; char *realm; realm = in_creds->client->realm; addrs.len = 0; addrs.val = NULL; paddrs = &addrs; { krb5_boolean noaddr; krb5_appdefault_boolean(context, NULL, realm, "no-addresses", KRB5_ADDRESSLESS_DEFAULT, &noaddr); if (noaddr) paddrs = NULL; } /* * If tickets are address-less, forward address-less tickets. */ if (paddrs) { ret = _krb5_get_krbtgt (context, ccache, realm, &ticket); if(ret == 0) { if (ticket->addresses.len == 0) paddrs = NULL; krb5_free_creds (context, ticket); } } if (paddrs != NULL) { ret = getaddrinfo (hostname, NULL, NULL, &ai); if (ret) { save_errno = errno; krb5_set_error_string(context, "resolving %s: %s", hostname, gai_strerror(ret)); return krb5_eai_to_heim_errno(ret, save_errno); } ret = add_addrs (context, &addrs, ai); freeaddrinfo (ai); if (ret) return ret; } kdc_flags.b = int2KDCOptions(flags); ret = krb5_get_kdc_cred (context, ccache, kdc_flags, paddrs, NULL, in_creds, &out_creds); krb5_free_addresses (context, &addrs); if (ret) { return ret; } memset (&cred, 0, sizeof(cred)); cred.pvno = 5; cred.msg_type = krb_cred; ALLOC_SEQ(&cred.tickets, 1); if (cred.tickets.val == NULL) { ret = ENOMEM; krb5_set_error_string(context, "malloc: out of memory"); goto out2; } ret = decode_Ticket(out_creds->ticket.data, out_creds->ticket.length, cred.tickets.val, &len); if (ret) goto out3; memset (&enc_krb_cred_part, 0, sizeof(enc_krb_cred_part)); ALLOC_SEQ(&enc_krb_cred_part.ticket_info, 1); if (enc_krb_cred_part.ticket_info.val == NULL) { ret = ENOMEM; krb5_set_error_string(context, "malloc: out of memory"); goto out4; } if (auth_context->flags & KRB5_AUTH_CONTEXT_DO_TIME) { krb5_timestamp sec; int32_t usec; krb5_us_timeofday (context, &sec, &usec); ALLOC(enc_krb_cred_part.timestamp, 1); if (enc_krb_cred_part.timestamp == NULL) { ret = ENOMEM; krb5_set_error_string(context, "malloc: out of memory"); goto out4; } *enc_krb_cred_part.timestamp = sec; ALLOC(enc_krb_cred_part.usec, 1); if (enc_krb_cred_part.usec == NULL) { ret = ENOMEM; krb5_set_error_string(context, "malloc: out of memory"); goto out4; } *enc_krb_cred_part.usec = usec; } else { enc_krb_cred_part.timestamp = NULL; enc_krb_cred_part.usec = NULL; } if (auth_context->local_address && auth_context->local_port && paddrs) { ret = krb5_make_addrport (context, &enc_krb_cred_part.s_address, auth_context->local_address, auth_context->local_port); if (ret) goto out4; } if (auth_context->remote_address) { if (auth_context->remote_port) { krb5_boolean noaddr; krb5_const_realm srealm; srealm = krb5_principal_get_realm(context, out_creds->server); /* Is this correct, and should we use the paddrs == NULL trick here as well? Having an address-less ticket may indicate that we don't know our own global address, but it does not necessary mean that we don't know the server's. */ krb5_appdefault_boolean(context, NULL, srealm, "no-addresses", FALSE, &noaddr); if (!noaddr) { ret = krb5_make_addrport (context, &enc_krb_cred_part.r_address, auth_context->remote_address, auth_context->remote_port); if (ret) goto out4; } } else { ALLOC(enc_krb_cred_part.r_address, 1); if (enc_krb_cred_part.r_address == NULL) { ret = ENOMEM; krb5_set_error_string(context, "malloc: out of memory"); goto out4; } ret = krb5_copy_address (context, auth_context->remote_address, enc_krb_cred_part.r_address); if (ret) goto out4; } } /* fill ticket_info.val[0] */ enc_krb_cred_part.ticket_info.len = 1; krb_cred_info = enc_krb_cred_part.ticket_info.val; copy_EncryptionKey (&out_creds->session, &krb_cred_info->key); ALLOC(krb_cred_info->prealm, 1); copy_Realm (&out_creds->client->realm, krb_cred_info->prealm); ALLOC(krb_cred_info->pname, 1); copy_PrincipalName(&out_creds->client->name, krb_cred_info->pname); ALLOC(krb_cred_info->flags, 1); *krb_cred_info->flags = out_creds->flags.b; ALLOC(krb_cred_info->authtime, 1); *krb_cred_info->authtime = out_creds->times.authtime; ALLOC(krb_cred_info->starttime, 1); *krb_cred_info->starttime = out_creds->times.starttime; ALLOC(krb_cred_info->endtime, 1); *krb_cred_info->endtime = out_creds->times.endtime; ALLOC(krb_cred_info->renew_till, 1); *krb_cred_info->renew_till = out_creds->times.renew_till; ALLOC(krb_cred_info->srealm, 1); copy_Realm (&out_creds->server->realm, krb_cred_info->srealm); ALLOC(krb_cred_info->sname, 1); copy_PrincipalName (&out_creds->server->name, krb_cred_info->sname); ALLOC(krb_cred_info->caddr, 1); copy_HostAddresses (&out_creds->addresses, krb_cred_info->caddr); krb5_free_creds (context, out_creds); /* encode EncKrbCredPart */ ASN1_MALLOC_ENCODE(EncKrbCredPart, buf, buf_size, &enc_krb_cred_part, &len, ret); free_EncKrbCredPart (&enc_krb_cred_part); if (ret) { free_KRB_CRED(&cred); return ret; } if(buf_size != len) krb5_abortx(context, "internal error in ASN.1 encoder"); if (auth_context->flags & KRB5_AUTH_CONTEXT_CLEAR_FORWARDED_CRED) { cred.enc_part.etype = ENCTYPE_NULL; cred.enc_part.kvno = NULL; cred.enc_part.cipher.data = buf; cred.enc_part.cipher.length = buf_size; } else { /* * Here older versions then 0.7.2 of Heimdal used the local or * remote subkey. That is wrong, the session key should be * used. Heimdal 0.7.2 and newer have code to try both in the * receiving end. */ ret = krb5_crypto_init(context, auth_context->keyblock, 0, &crypto); if (ret) { free(buf); free_KRB_CRED(&cred); return ret; } ret = krb5_encrypt_EncryptedData (context, crypto, KRB5_KU_KRB_CRED, buf, len, 0, &cred.enc_part); free(buf); krb5_crypto_destroy(context, crypto); if (ret) { free_KRB_CRED(&cred); return ret; } } ASN1_MALLOC_ENCODE(KRB_CRED, buf, buf_size, &cred, &len, ret); free_KRB_CRED (&cred); if (ret) return ret; if(buf_size != len) krb5_abortx(context, "internal error in ASN.1 encoder"); out_data->length = len; out_data->data = buf; return 0; out4: free_EncKrbCredPart(&enc_krb_cred_part); out3: free_KRB_CRED(&cred); out2: krb5_free_creds (context, out_creds); return ret; }
static krb5_error_code init_tgs_req (krb5_context context, krb5_ccache ccache, krb5_addresses *addresses, krb5_kdc_flags flags, Ticket *second_ticket, krb5_creds *in_creds, krb5_creds *krbtgt, unsigned nonce, const METHOD_DATA *padata, krb5_keyblock **subkey, TGS_REQ *t, krb5_key_usage usage) { krb5_error_code ret = 0; memset(t, 0, sizeof(*t)); t->pvno = 5; t->msg_type = krb_tgs_req; if (in_creds->session.keytype) { ALLOC_SEQ(&t->req_body.etype, 1); if(t->req_body.etype.val == NULL) { ret = ENOMEM; krb5_set_error_string(context, "malloc: out of memory"); goto fail; } t->req_body.etype.val[0] = in_creds->session.keytype; } else { ret = krb5_init_etype(context, &t->req_body.etype.len, &t->req_body.etype.val, NULL); } if (ret) goto fail; t->req_body.addresses = addresses; t->req_body.kdc_options = flags.b; ret = copy_Realm(&in_creds->server->realm, &t->req_body.realm); if (ret) goto fail; ALLOC(t->req_body.sname, 1); if (t->req_body.sname == NULL) { ret = ENOMEM; krb5_set_error_string(context, "malloc: out of memory"); goto fail; } /* some versions of some code might require that the client be present in TGS-REQs, but this is clearly against the spec */ ret = copy_PrincipalName(&in_creds->server->name, t->req_body.sname); if (ret) goto fail; /* req_body.till should be NULL if there is no endtime specified, but old MIT code (like DCE secd) doesn't like that */ ALLOC(t->req_body.till, 1); if(t->req_body.till == NULL){ ret = ENOMEM; krb5_set_error_string(context, "malloc: out of memory"); goto fail; } *t->req_body.till = in_creds->times.endtime; t->req_body.nonce = nonce; if(second_ticket){ ALLOC(t->req_body.additional_tickets, 1); if (t->req_body.additional_tickets == NULL) { ret = ENOMEM; krb5_set_error_string(context, "malloc: out of memory"); goto fail; } ALLOC_SEQ(t->req_body.additional_tickets, 1); if (t->req_body.additional_tickets->val == NULL) { ret = ENOMEM; krb5_set_error_string(context, "malloc: out of memory"); goto fail; } ret = copy_Ticket(second_ticket, t->req_body.additional_tickets->val); if (ret) goto fail; } ALLOC(t->padata, 1); if (t->padata == NULL) { ret = ENOMEM; krb5_set_error_string(context, "malloc: out of memory"); goto fail; } ALLOC_SEQ(t->padata, 1 + padata->len); if (t->padata->val == NULL) { ret = ENOMEM; krb5_set_error_string(context, "malloc: out of memory"); goto fail; } { int i; for (i = 0; i < padata->len; i++) { ret = copy_PA_DATA(&padata->val[i], &t->padata->val[i + 1]); if (ret) { krb5_set_error_string(context, "malloc: out of memory"); goto fail; } } } { krb5_auth_context ac; krb5_keyblock *key = NULL; ret = krb5_auth_con_init(context, &ac); if(ret) goto fail; if (krb5_config_get_bool_default(context, NULL, FALSE, "realms", krbtgt->server->realm, "tgs_require_subkey", NULL)) { ret = krb5_generate_subkey (context, &krbtgt->session, &key); if (ret) { krb5_auth_con_free (context, ac); goto fail; } ret = krb5_auth_con_setlocalsubkey(context, ac, key); if (ret) { if (key) krb5_free_keyblock (context, key); krb5_auth_con_free (context, ac); goto fail; } } ret = set_auth_data (context, &t->req_body, &in_creds->authdata, key ? key : &krbtgt->session); if (ret) { if (key) krb5_free_keyblock (context, key); krb5_auth_con_free (context, ac); goto fail; } ret = make_pa_tgs_req(context, ac, &t->req_body, &t->padata->val[0], krbtgt, usage); if(ret) { if (key) krb5_free_keyblock (context, key); krb5_auth_con_free(context, ac); goto fail; } *subkey = key; krb5_auth_con_free(context, ac); } fail: if (ret) { t->req_body.addresses = NULL; free_TGS_REQ (t); } return ret; }
static krb5_error_code init_as_req (krb5_context context, KDCOptions opts, const krb5_creds *creds, const krb5_addresses *addrs, const krb5_enctype *etypes, AS_REQ *a) { krb5_error_code ret; memset(a, 0, sizeof(*a)); a->pvno = 5; a->msg_type = krb_as_req; a->req_body.kdc_options = opts; a->req_body.cname = malloc(sizeof(*a->req_body.cname)); if (a->req_body.cname == NULL) { ret = ENOMEM; krb5_set_error_message(context, ret, N_("malloc: out of memory", "")); goto fail; } a->req_body.sname = malloc(sizeof(*a->req_body.sname)); if (a->req_body.sname == NULL) { ret = ENOMEM; krb5_set_error_message(context, ret, N_("malloc: out of memory", "")); goto fail; } ret = _krb5_principal2principalname (a->req_body.cname, creds->client); if (ret) goto fail; ret = copy_Realm(&creds->client->realm, &a->req_body.realm); if (ret) goto fail; ret = _krb5_principal2principalname (a->req_body.sname, creds->server); if (ret) goto fail; if(creds->times.starttime) { a->req_body.from = malloc(sizeof(*a->req_body.from)); if (a->req_body.from == NULL) { ret = ENOMEM; krb5_set_error_message(context, ret, N_("malloc: out of memory", "")); goto fail; } *a->req_body.from = creds->times.starttime; } if(creds->times.endtime){ ALLOC(a->req_body.till, 1); *a->req_body.till = creds->times.endtime; } if(creds->times.renew_till){ a->req_body.rtime = malloc(sizeof(*a->req_body.rtime)); if (a->req_body.rtime == NULL) { ret = ENOMEM; krb5_set_error_message(context, ret, N_("malloc: out of memory", "")); goto fail; } *a->req_body.rtime = creds->times.renew_till; } a->req_body.nonce = 0; ret = krb5_init_etype (context, &a->req_body.etype.len, &a->req_body.etype.val, etypes); if (ret) goto fail; /* * This means no addresses */ if (addrs && addrs->len == 0) { a->req_body.addresses = NULL; } else { a->req_body.addresses = malloc(sizeof(*a->req_body.addresses)); if (a->req_body.addresses == NULL) { ret = ENOMEM; krb5_set_error_message(context, ret, N_("malloc: out of memory", "")); goto fail; } if (addrs) ret = krb5_copy_addresses(context, addrs, a->req_body.addresses); else { ret = krb5_get_all_client_addrs (context, a->req_body.addresses); if(ret == 0 && a->req_body.addresses->len == 0) { free(a->req_body.addresses); a->req_body.addresses = NULL; } } if (ret) goto fail; } a->req_body.enc_authorization_data = NULL; a->req_body.additional_tickets = NULL; a->padata = NULL; return 0; fail: free_AS_REQ(a); memset(a, 0, sizeof(*a)); return ret; }
static krb5_error_code init_as_req (krb5_context context, KDCOptions opts, krb5_creds *creds, const krb5_addresses *addrs, const krb5_enctype *etypes, const krb5_preauthtype *ptypes, const krb5_preauthdata *preauth, krb5_key_proc key_proc, krb5_const_pointer keyseed, unsigned nonce, AS_REQ *a) { krb5_error_code ret; krb5_salt salt; memset(a, 0, sizeof(*a)); a->pvno = 5; a->msg_type = krb_as_req; a->req_body.kdc_options = opts; a->req_body.cname = malloc(sizeof(*a->req_body.cname)); if (a->req_body.cname == NULL) { ret = ENOMEM; krb5_set_error_string(context, "malloc: out of memory"); goto fail; } a->req_body.sname = malloc(sizeof(*a->req_body.sname)); if (a->req_body.sname == NULL) { ret = ENOMEM; krb5_set_error_string(context, "malloc: out of memory"); goto fail; } ret = _krb5_principal2principalname (a->req_body.cname, creds->client); if (ret) goto fail; ret = _krb5_principal2principalname (a->req_body.sname, creds->server); if (ret) goto fail; ret = copy_Realm(&creds->client->realm, &a->req_body.realm); if (ret) goto fail; if(creds->times.starttime) { a->req_body.from = malloc(sizeof(*a->req_body.from)); if (a->req_body.from == NULL) { ret = ENOMEM; krb5_set_error_string(context, "malloc: out of memory"); goto fail; } *a->req_body.from = creds->times.starttime; } if(creds->times.endtime){ ALLOC(a->req_body.till, 1); *a->req_body.till = creds->times.endtime; } if(creds->times.renew_till){ a->req_body.rtime = malloc(sizeof(*a->req_body.rtime)); if (a->req_body.rtime == NULL) { ret = ENOMEM; krb5_set_error_string(context, "malloc: out of memory"); goto fail; } *a->req_body.rtime = creds->times.renew_till; } a->req_body.nonce = nonce; ret = krb5_init_etype (context, &a->req_body.etype.len, &a->req_body.etype.val, etypes); if (ret) goto fail; /* * This means no addresses */ if (addrs && addrs->len == 0) { a->req_body.addresses = NULL; } else { a->req_body.addresses = malloc(sizeof(*a->req_body.addresses)); if (a->req_body.addresses == NULL) { ret = ENOMEM; krb5_set_error_string(context, "malloc: out of memory"); goto fail; } if (addrs) ret = krb5_copy_addresses(context, addrs, a->req_body.addresses); else { ret = krb5_get_all_client_addrs (context, a->req_body.addresses); if(ret == 0 && a->req_body.addresses->len == 0) { free(a->req_body.addresses); a->req_body.addresses = NULL; } } if (ret) return ret; } a->req_body.enc_authorization_data = NULL; a->req_body.additional_tickets = NULL; if(preauth != NULL) { int i; ALLOC(a->padata, 1); if(a->padata == NULL) { ret = ENOMEM; krb5_set_error_string(context, "malloc: out of memory"); goto fail; } a->padata->val = NULL; a->padata->len = 0; for(i = 0; i < preauth->len; i++) { if(preauth->val[i].type == KRB5_PADATA_ENC_TIMESTAMP){ int j; for(j = 0; j < preauth->val[i].info.len; j++) { krb5_salt *sp = &salt; if(preauth->val[i].info.val[j].salttype) salt.salttype = *preauth->val[i].info.val[j].salttype; else salt.salttype = KRB5_PW_SALT; if(preauth->val[i].info.val[j].salt) salt.saltvalue = *preauth->val[i].info.val[j].salt; else if(salt.salttype == KRB5_PW_SALT) sp = NULL; else krb5_data_zero(&salt.saltvalue); ret = add_padata(context, a->padata, creds->client, key_proc, keyseed, &preauth->val[i].info.val[j].etype, 1, sp); if (ret == 0) break; } } } } else /* not sure this is the way to use `ptypes' */ if (ptypes == NULL || *ptypes == KRB5_PADATA_NONE) a->padata = NULL; else if (*ptypes == KRB5_PADATA_ENC_TIMESTAMP) { ALLOC(a->padata, 1); if (a->padata == NULL) { ret = ENOMEM; krb5_set_error_string(context, "malloc: out of memory"); goto fail; } a->padata->len = 0; a->padata->val = NULL; /* make a v5 salted pa-data */ add_padata(context, a->padata, creds->client, key_proc, keyseed, a->req_body.etype.val, a->req_body.etype.len, NULL); /* make a v4 salted pa-data */ salt.salttype = KRB5_PW_SALT; krb5_data_zero(&salt.saltvalue); add_padata(context, a->padata, creds->client, key_proc, keyseed, a->req_body.etype.val, a->req_body.etype.len, &salt); } else { krb5_set_error_string (context, "pre-auth type %d not supported", *ptypes); ret = KRB5_PREAUTH_BAD_TYPE; goto fail; } return 0; fail: free_AS_REQ(a); return ret; }