krb5_error_code KRB5_CALLCONV krb5_copy_creds(krb5_context context, const krb5_creds *incred, krb5_creds **outcred) { krb5_creds *tempcred; krb5_error_code retval; krb5_data *scratch; if (!(tempcred = (krb5_creds *)malloc(sizeof(*tempcred)))) return ENOMEM; *tempcred = *incred; retval = krb5_copy_principal(context, incred->client, &tempcred->client); if (retval) goto cleanlast; retval = krb5_copy_principal(context, incred->server, &tempcred->server); if (retval) goto cleanclient; retval = krb5_copy_keyblock_contents(context, &incred->keyblock, &tempcred->keyblock); if (retval) goto cleanserver; retval = krb5_copy_addresses(context, incred->addresses, &tempcred->addresses); if (retval) goto cleanblock; retval = krb5_copy_data(context, &incred->ticket, &scratch); if (retval) goto cleanaddrs; tempcred->ticket = *scratch; krb5_xfree(scratch); retval = krb5_copy_data(context, &incred->second_ticket, &scratch); if (retval) goto cleanticket; tempcred->second_ticket = *scratch; krb5_xfree(scratch); retval = krb5_copy_authdata(context, incred->authdata,&tempcred->authdata); if (retval) goto clearticket; *outcred = tempcred; return 0; clearticket: memset(tempcred->ticket.data,0,tempcred->ticket.length); cleanticket: free(tempcred->ticket.data); cleanaddrs: krb5_free_addresses(context, tempcred->addresses); cleanblock: krb5_xfree(tempcred->keyblock.contents); cleanserver: krb5_free_principal(context, tempcred->server); cleanclient: krb5_free_principal(context, tempcred->client); cleanlast: krb5_xfree(tempcred); return retval; }
/* * Copy contents of input credentials structure to supplied * destination, allocating storage for indirect fields as needed. On * success, the output is a deep copy of the input. On error, the * output structure is garbage and its contents should be ignored. */ krb5_error_code k5_copy_creds_contents(krb5_context context, const krb5_creds *incred, krb5_creds *tempcred) { krb5_error_code retval; krb5_data *scratch; *tempcred = *incred; retval = krb5_copy_principal(context, incred->client, &tempcred->client); if (retval) goto cleanlast; retval = krb5_copy_principal(context, incred->server, &tempcred->server); if (retval) goto cleanclient; retval = krb5_copy_keyblock_contents(context, &incred->keyblock, &tempcred->keyblock); if (retval) goto cleanserver; retval = krb5_copy_addresses(context, incred->addresses, &tempcred->addresses); if (retval) goto cleanblock; retval = krb5_copy_data(context, &incred->ticket, &scratch); if (retval) goto cleanaddrs; tempcred->ticket = *scratch; free(scratch); retval = krb5_copy_data(context, &incred->second_ticket, &scratch); if (retval) goto clearticket; tempcred->second_ticket = *scratch; free(scratch); retval = krb5_copy_authdata(context, incred->authdata,&tempcred->authdata); if (retval) goto clearsecondticket; return 0; clearsecondticket: memset(tempcred->second_ticket.data,0,tempcred->second_ticket.length); free(tempcred->second_ticket.data); clearticket: memset(tempcred->ticket.data,0,tempcred->ticket.length); free(tempcred->ticket.data); cleanaddrs: krb5_free_addresses(context, tempcred->addresses); cleanblock: free(tempcred->keyblock.contents); cleanserver: krb5_free_principal(context, tempcred->server); cleanclient: krb5_free_principal(context, tempcred->client); cleanlast: /* Do not free tempcred - we did not allocate it - its contents are garbage - but we should not free it */ return retval; }
/* Decode error_packet as a KRB-ERROR message and retrieve its e-data into * *edata_out. */ static krb5_error_code get_error_edata(krb5_context context, const krb5_data *error_packet, krb5_data **edata_out) { krb5_error_code ret; krb5_error *krberror = NULL; *edata_out = NULL; ret = krb5_rd_error(context, error_packet, &krberror); if (ret) return ret; if (krberror->e_data.data == NULL) { /* Return a krb5 error code based on the error number. */ ret = ERROR_TABLE_BASE_krb5 + (krb5_error_code)krberror->error; goto cleanup; } ret = krb5_copy_data(context, &krberror->e_data, edata_out); cleanup: krb5_free_error(context, krberror); return ret; }
static krb5_error_code set_paid(struct pa_info_data *paid, krb5_context context, krb5_enctype etype, krb5_salttype salttype, void *salt_string, size_t salt_len, krb5_data *s2kparams) { paid->etype = etype; paid->salt.salttype = salttype; paid->salt.saltvalue.data = malloc(salt_len + 1); if (paid->salt.saltvalue.data == NULL) { krb5_clear_error_message(context); return ENOMEM; } memcpy(paid->salt.saltvalue.data, salt_string, salt_len); ((char *)paid->salt.saltvalue.data)[salt_len] = '\0'; paid->salt.saltvalue.length = salt_len; if (s2kparams) { krb5_error_code ret; ret = krb5_copy_data(context, s2kparams, &paid->s2kparams); if (ret) { krb5_clear_error_message(context); krb5_free_salt(context, paid->salt); return ret; } } else paid->s2kparams = NULL; return 0; }
static krb5_error_code greet_hello(krb5_context context, krb5_data **ret) { krb5_data tmp; tmp.data = "Hello, KDC issued acceptor world!"; tmp.length = strlen(tmp.data); return krb5_copy_data(context, &tmp, ret); }
void kdc_insert_lookaside(krb5_data *inpkt, krb5_data *outpkt) { register krb5_kdc_replay_ent *eptr; krb5_int32 timenow; time_t db_age; if (krb5_timeofday(kdc_context, &timenow) || krb5_db_get_age(kdc_context, 0, &db_age)) return; /* this is a new entry */ eptr = (krb5_kdc_replay_ent *)calloc(1, sizeof(*eptr)); if (!eptr) return; eptr->timein = timenow; eptr->db_age = db_age; /* * This is going to hurt a lot malloc()-wise due to the need to * allocate memory for the krb5_data and krb5_address elements. * ARGH! */ if (krb5_copy_data(kdc_context, inpkt, &eptr->req_packet)) { free(eptr); return; } if (krb5_copy_data(kdc_context, outpkt, &eptr->reply_packet)) { krb5_free_data(kdc_context, eptr->req_packet); free(eptr); return; } eptr->next = root_ptr.next; root_ptr.next = eptr; num_entries++; return; }
krb5_boolean kdc_check_lookaside(krb5_data *inpkt, krb5_data **outpkt) { krb5_int32 timenow; register krb5_kdc_replay_ent *eptr, *last, *hold; time_t db_age; if (krb5_timeofday(kdc_context, &timenow) || krb5_db_get_age(kdc_context, 0, &db_age)) return FALSE; calls++; /* search for a replay entry in the queue, possibly removing stale entries while we're here */ if (root_ptr.next) { for (last = &root_ptr, eptr = root_ptr.next; eptr; eptr = eptr->next) { if (MATCH(eptr)) { eptr->num_hits++; hits++; if (krb5_copy_data(kdc_context, eptr->reply_packet, outpkt)) return FALSE; else return TRUE; /* return here, don't bother flushing even if it is stale. if we just matched, we may get another retransmit... */ } if (STALE(eptr)) { /* flush it and collect stats */ max_hits_per_entry = max(max_hits_per_entry, eptr->num_hits); krb5_free_data(kdc_context, eptr->req_packet); krb5_free_data(kdc_context, eptr->reply_packet); hold = eptr; last->next = eptr->next; eptr = last; free(hold); } else { /* this isn't it, just move along */ last = eptr; } } } return FALSE; }
static krb5_error_code make_etype_info_entry(krb5_context context, ETYPE_INFO_ENTRY *ent, Key *key) { ent->etype = key->key.keytype; if(key->salt){ #if 0 ALLOC(ent->salttype); if(key->salt->type == hdb_pw_salt) *ent->salttype = 0; /* or 1? or NULL? */ else if(key->salt->type == hdb_afs3_salt) *ent->salttype = 2; else { kdc_log(context, config, 0, "unknown salt-type: %d", key->salt->type); return KRB5KRB_ERR_GENERIC; } /* according to `the specs', we can't send a salt if we have AFS3 salted key, but that requires that you *know* what cell you are using (e.g by assuming that the cell is the same as the realm in lower case) */ #elif 0 ALLOC(ent->salttype); *ent->salttype = key->salt->type; #else /* * We shouldn't sent salttype since it is incompatible with the * specification and it breaks windows clients. The afs * salting problem is solved by using KRB5-PADATA-AFS3-SALT * implemented in Heimdal 0.7 and later. */ ent->salttype = NULL; #endif krb5_copy_data(context, &key->salt->salt, &ent->salt); } else { /* we return no salt type at all, as that should indicate * the default salt type and make everybody happy. some * systems (like w2k) dislike being told the salt type * here. */ ent->salttype = NULL; ent->salt = NULL; } return 0; }
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL krb5_pac_verify(krb5_context context, const krb5_pac pac, time_t authtime, krb5_const_principal principal, const krb5_keyblock *server, const krb5_keyblock *privsvr) { krb5_error_code ret; if (pac->server_checksum == NULL) { krb5_set_error_message(context, EINVAL, "PAC missing server checksum"); return EINVAL; } if (pac->privsvr_checksum == NULL) { krb5_set_error_message(context, EINVAL, "PAC missing kdc checksum"); return EINVAL; } if (pac->logon_name == NULL) { krb5_set_error_message(context, EINVAL, "PAC missing logon name"); return EINVAL; } ret = verify_logonname(context, pac->logon_name, &pac->data, authtime, principal); if (ret) return ret; /* * in the service case, clean out data option of the privsvr and * server checksum before checking the checksum. */ { krb5_data *copy; ret = krb5_copy_data(context, &pac->data, ©); if (ret) return ret; if (pac->server_checksum->buffersize < 4) return EINVAL; if (pac->privsvr_checksum->buffersize < 4) return EINVAL; memset((char *)copy->data + pac->server_checksum->offset_lo + 4, 0, pac->server_checksum->buffersize - 4); memset((char *)copy->data + pac->privsvr_checksum->offset_lo + 4, 0, pac->privsvr_checksum->buffersize - 4); ret = verify_checksum(context, pac->server_checksum, &pac->data, copy->data, copy->length, server); krb5_free_data(context, copy); if (ret) return ret; } if (privsvr) { /* The priv checksum covers the server checksum */ ret = verify_checksum(context, pac->privsvr_checksum, &pac->data, (char *)pac->data.data + pac->server_checksum->offset_lo + 4, pac->server_checksum->buffersize - 4, privsvr); if (ret) return ret; } return 0; }
int create_keys(krb5_context krbctx, krb5_principal princ, char *password, const char *enctypes_string, struct keys_container *keys, char **err_msg) { struct krb_key_salt *ksdata; krb5_error_code krberr; krb5_data key_password; krb5_data *realm = NULL; int i, nkeys; int ret; *err_msg = NULL; ret = prep_ksdata(krbctx, enctypes_string, keys, err_msg); if (ret == 0) return 0; ksdata = keys->ksdata; nkeys = keys->nkeys; if (password) { key_password.data = password; key_password.length = strlen(password); realm = krb5_princ_realm(krbctx, princ); } for (i = 0; i < nkeys; i++) { krb5_data *salt; if (!password) { /* cool, random keys */ krberr = krb5_c_make_random_key(krbctx, ksdata[i].enctype, &ksdata[i].key); if (krberr) { *err_msg = _("Failed to create random key!\n"); return 0; } /* set the salt to NO_SALT as the key was random */ ksdata[i].salttype = NO_SALT; continue; } /* Make keys using password and required salt */ switch (ksdata[i].salttype) { case KRB5_KDB_SALTTYPE_ONLYREALM: krberr = krb5_copy_data(krbctx, realm, &salt); if (krberr) { *err_msg = _("Failed to create key!\n"); return 0; } ksdata[i].salt.length = salt->length; ksdata[i].salt.data = malloc(salt->length); if (!ksdata[i].salt.data) { *err_msg = _("Out of memory!\n"); return 0; } memcpy(ksdata[i].salt.data, salt->data, salt->length); krb5_free_data(krbctx, salt); break; case KRB5_KDB_SALTTYPE_NOREALM: krberr = ipa_krb5_principal2salt_norealm(krbctx, princ, &ksdata[i].salt); if (krberr) { *err_msg = _("Failed to create key!\n"); return 0; } break; case KRB5_KDB_SALTTYPE_NORMAL: krberr = krb5_principal2salt(krbctx, princ, &ksdata[i].salt); if (krberr) { *err_msg = _("Failed to create key!\n"); return 0; } break; /* no KRB5_KDB_SALTTYPE_V4, we do not support krb v4 */ case KRB5_KDB_SALTTYPE_AFS3: /* Comment from MIT sources: * * Why do we do this? Well, the afs_mit_string_to_key * * needs to use strlen, and the realm is not NULL * * terminated.... */ ksdata[i].salt.data = (char *)malloc(realm->length + 1); if (NULL == ksdata[i].salt.data) { *err_msg = _("Out of memory!\n"); return 0; } memcpy((char *)ksdata[i].salt.data, (char *)realm->data, realm->length); ksdata[i].salt.data[realm->length] = '\0'; /* AFS uses a special length (UGLY) */ ksdata[i].salt.length = SALT_TYPE_AFS_LENGTH; break; default: *err_msg = _("Bad or unsupported salt type.\n"); /* FIXME: fprintf(stderr, _("Bad or unsupported salt type (%d)!\n"), ksdata[i].salttype); */ return 0; } krberr = krb5_c_string_to_key(krbctx, ksdata[i].enctype, &key_password, &ksdata[i].salt, &ksdata[i].key); if (krberr) { *err_msg = _("Failed to create key!\n"); return 0; } /* set back salt length to real value if AFS3 */ if (ksdata[i].salttype == KRB5_KDB_SALTTYPE_AFS3) { ksdata[i].salt.length = realm->length; } } return nkeys; }
/*ARGSUSED*/ void process_as_req(krb5_kdc_req *request, krb5_data *req_pkt, const krb5_fulladdr *from, kdc_realm_t *kdc_active_realm, verto_ctx *vctx, loop_respond_fn respond, void *arg) { krb5_error_code errcode; krb5_timestamp rtime; unsigned int s_flags = 0; krb5_data encoded_req_body; krb5_enctype useenctype; struct as_req_state *state; state = k5alloc(sizeof(*state), &errcode); if (state == NULL) { (*respond)(arg, errcode, NULL); return; } state->respond = respond; state->arg = arg; state->request = request; state->req_pkt = req_pkt; state->from = from; state->active_realm = kdc_active_realm; errcode = kdc_make_rstate(kdc_active_realm, &state->rstate); if (errcode != 0) { (*respond)(arg, errcode, NULL); return; } if (state->request->msg_type != KRB5_AS_REQ) { state->status = "msg_type mismatch"; errcode = KRB5_BADMSGTYPE; goto errout; } if (fetch_asn1_field((unsigned char *) req_pkt->data, 1, 4, &encoded_req_body) != 0) { errcode = ASN1_BAD_ID; state->status = "Finding req_body"; goto errout; } errcode = kdc_find_fast(&state->request, &encoded_req_body, NULL, NULL, state->rstate, &state->inner_body); if (errcode) { state->status = "error decoding FAST"; goto errout; } if (state->inner_body == NULL) { /* Not a FAST request; copy the encoded request body. */ errcode = krb5_copy_data(kdc_context, &encoded_req_body, &state->inner_body); if (errcode) { state->status = "storing req body"; goto errout; } } state->rock.request = state->request; state->rock.inner_body = state->inner_body; state->rock.rstate = state->rstate; state->rock.vctx = vctx; if (!state->request->client) { state->status = "NULL_CLIENT"; errcode = KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN; goto errout; } if ((errcode = krb5_unparse_name(kdc_context, state->request->client, &state->cname))) { state->status = "UNPARSING_CLIENT"; goto errout; } limit_string(state->cname); if (!state->request->server) { state->status = "NULL_SERVER"; errcode = KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN; goto errout; } if ((errcode = krb5_unparse_name(kdc_context, state->request->server, &state->sname))) { state->status = "UNPARSING_SERVER"; goto errout; } limit_string(state->sname); /* * We set KRB5_KDB_FLAG_CLIENT_REFERRALS_ONLY as a hint * to the backend to return naming information in lieu * of cross realm TGS entries. */ setflag(state->c_flags, KRB5_KDB_FLAG_CLIENT_REFERRALS_ONLY); /* * Note that according to the referrals draft we should * always canonicalize enterprise principal names. */ if (isflagset(state->request->kdc_options, KDC_OPT_CANONICALIZE) || state->request->client->type == KRB5_NT_ENTERPRISE_PRINCIPAL) { setflag(state->c_flags, KRB5_KDB_FLAG_CANONICALIZE); setflag(state->c_flags, KRB5_KDB_FLAG_ALIAS_OK); } if (include_pac_p(kdc_context, state->request)) { setflag(state->c_flags, KRB5_KDB_FLAG_INCLUDE_PAC); } errcode = krb5_db_get_principal(kdc_context, state->request->client, state->c_flags, &state->client); if (errcode == KRB5_KDB_CANTLOCK_DB) errcode = KRB5KDC_ERR_SVC_UNAVAILABLE; if (errcode == KRB5_KDB_NOENTRY) { state->status = "CLIENT_NOT_FOUND"; if (vague_errors) errcode = KRB5KRB_ERR_GENERIC; else errcode = KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN; goto errout; } else if (errcode) { state->status = "LOOKING_UP_CLIENT"; goto errout; } state->rock.client = state->client; /* * If the backend returned a principal that is not in the local * realm, then we need to refer the client to that realm. */ if (!is_local_principal(kdc_active_realm, state->client->princ)) { /* Entry is a referral to another realm */ state->status = "REFERRAL"; errcode = KRB5KDC_ERR_WRONG_REALM; goto errout; } s_flags = 0; setflag(s_flags, KRB5_KDB_FLAG_ALIAS_OK); if (isflagset(state->request->kdc_options, KDC_OPT_CANONICALIZE)) { setflag(s_flags, KRB5_KDB_FLAG_CANONICALIZE); } errcode = krb5_db_get_principal(kdc_context, state->request->server, s_flags, &state->server); if (errcode == KRB5_KDB_CANTLOCK_DB) errcode = KRB5KDC_ERR_SVC_UNAVAILABLE; if (errcode == KRB5_KDB_NOENTRY) { state->status = "SERVER_NOT_FOUND"; errcode = KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN; goto errout; } else if (errcode) { state->status = "LOOKING_UP_SERVER"; goto errout; } if ((errcode = krb5_timeofday(kdc_context, &state->kdc_time))) { state->status = "TIMEOFDAY"; goto errout; } state->authtime = state->kdc_time; /* for audit_as_request() */ if ((errcode = validate_as_request(kdc_active_realm, state->request, *state->client, *state->server, state->kdc_time, &state->status, &state->e_data))) { if (!state->status) state->status = "UNKNOWN_REASON"; errcode += ERROR_TABLE_BASE_krb5; goto errout; } /* * Select the keytype for the ticket session key. */ if ((useenctype = select_session_keytype(kdc_active_realm, state->server, state->request->nktypes, state->request->ktype)) == 0) { /* unsupported ktype */ state->status = "BAD_ENCRYPTION_TYPE"; errcode = KRB5KDC_ERR_ETYPE_NOSUPP; goto errout; } if ((errcode = krb5_c_make_random_key(kdc_context, useenctype, &state->session_key))) { state->status = "RANDOM_KEY_FAILED"; goto errout; } /* * Canonicalization is only effective if we are issuing a TGT * (the intention is to allow support for Windows "short" realm * aliases, nothing more). */ if (isflagset(s_flags, KRB5_KDB_FLAG_CANONICALIZE) && krb5_is_tgs_principal(state->request->server) && krb5_is_tgs_principal(state->server->princ)) { state->ticket_reply.server = state->server->princ; } else { state->ticket_reply.server = state->request->server; } state->enc_tkt_reply.flags = 0; state->enc_tkt_reply.times.authtime = state->authtime; setflag(state->enc_tkt_reply.flags, TKT_FLG_INITIAL); setflag(state->enc_tkt_reply.flags, TKT_FLG_ENC_PA_REP); /* * It should be noted that local policy may affect the * processing of any of these flags. For example, some * realms may refuse to issue renewable tickets */ if (isflagset(state->request->kdc_options, KDC_OPT_FORWARDABLE)) setflag(state->enc_tkt_reply.flags, TKT_FLG_FORWARDABLE); if (isflagset(state->request->kdc_options, KDC_OPT_PROXIABLE)) setflag(state->enc_tkt_reply.flags, TKT_FLG_PROXIABLE); if (isflagset(state->request->kdc_options, KDC_OPT_ALLOW_POSTDATE)) setflag(state->enc_tkt_reply.flags, TKT_FLG_MAY_POSTDATE); state->enc_tkt_reply.session = &state->session_key; if (isflagset(state->c_flags, KRB5_KDB_FLAG_CANONICALIZE)) { state->client_princ = *(state->client->princ); } else { state->client_princ = *(state->request->client); /* The realm is always canonicalized */ state->client_princ.realm = state->client->princ->realm; } state->enc_tkt_reply.client = &state->client_princ; state->enc_tkt_reply.transited.tr_type = KRB5_DOMAIN_X500_COMPRESS; state->enc_tkt_reply.transited.tr_contents = empty_string; if (isflagset(state->request->kdc_options, KDC_OPT_POSTDATED)) { setflag(state->enc_tkt_reply.flags, TKT_FLG_POSTDATED); setflag(state->enc_tkt_reply.flags, TKT_FLG_INVALID); state->enc_tkt_reply.times.starttime = state->request->from; } else state->enc_tkt_reply.times.starttime = state->kdc_time; kdc_get_ticket_endtime(kdc_active_realm, state->enc_tkt_reply.times.starttime, kdc_infinity, state->request->till, state->client, state->server, &state->enc_tkt_reply.times.endtime); if (isflagset(state->request->kdc_options, KDC_OPT_RENEWABLE_OK) && !isflagset(state->client->attributes, KRB5_KDB_DISALLOW_RENEWABLE) && (state->enc_tkt_reply.times.endtime < state->request->till)) { /* we set the RENEWABLE option for later processing */ setflag(state->request->kdc_options, KDC_OPT_RENEWABLE); state->request->rtime = state->request->till; } rtime = (state->request->rtime == 0) ? kdc_infinity : state->request->rtime; if (isflagset(state->request->kdc_options, KDC_OPT_RENEWABLE)) { /* * XXX Should we squelch the output renew_till to be no * earlier than the endtime of the ticket? */ setflag(state->enc_tkt_reply.flags, TKT_FLG_RENEWABLE); state->enc_tkt_reply.times.renew_till = min(rtime, state->enc_tkt_reply.times.starttime + min(state->client->max_renewable_life, min(state->server->max_renewable_life, max_renewable_life_for_realm))); } else state->enc_tkt_reply.times.renew_till = 0; /* XXX */ /* * starttime is optional, and treated as authtime if not present. * so we can nuke it if it matches */ if (state->enc_tkt_reply.times.starttime == state->enc_tkt_reply.times.authtime) state->enc_tkt_reply.times.starttime = 0; state->enc_tkt_reply.caddrs = state->request->addresses; state->enc_tkt_reply.authorization_data = 0; /* If anonymous requests are being used, adjust the realm of the client * principal. */ if (isflagset(state->request->kdc_options, KDC_OPT_REQUEST_ANONYMOUS)) { if (!krb5_principal_compare_any_realm(kdc_context, state->request->client, krb5_anonymous_principal())) { errcode = KRB5KDC_ERR_BADOPTION; state->status = "Anonymous requested but anonymous " "principal not used."; goto errout; } setflag(state->enc_tkt_reply.flags, TKT_FLG_ANONYMOUS); krb5_free_principal(kdc_context, state->request->client); state->request->client = NULL; errcode = krb5_copy_principal(kdc_context, krb5_anonymous_principal(), &state->request->client); if (errcode) { state->status = "Copying anonymous principal"; goto errout; } state->enc_tkt_reply.client = state->request->client; setflag(state->client->attributes, KRB5_KDB_REQUIRES_PRE_AUTH); } /* * Check the preauthentication if it is there. */ if (state->request->padata) { check_padata(kdc_context, &state->rock, state->req_pkt, state->request, &state->enc_tkt_reply, &state->pa_context, &state->e_data, &state->typed_e_data, finish_preauth, state); } else finish_preauth(state, 0); return; errout: finish_process_as_req(state, errcode); }
krb5_error_code kdc_find_fast(krb5_kdc_req **requestptr, krb5_data *checksummed_data, krb5_keyblock *tgs_subkey, krb5_keyblock *tgs_session, struct kdc_request_state *state, krb5_data **inner_body_out) { krb5_error_code retval = 0; krb5_pa_data *fast_padata, *cookie_padata = NULL; krb5_data scratch, *inner_body = NULL; krb5_fast_req * fast_req = NULL; krb5_kdc_req *request = *requestptr; krb5_fast_armored_req *fast_armored_req = NULL; krb5_checksum *cksum; krb5_boolean cksum_valid; krb5_keyblock empty_keyblock; kdc_realm_t *kdc_active_realm = state->realm_data; if (inner_body_out != NULL) *inner_body_out = NULL; scratch.data = NULL; krb5_clear_error_message(kdc_context); memset(&empty_keyblock, 0, sizeof(krb5_keyblock)); fast_padata = krb5int_find_pa_data(kdc_context, request->padata, KRB5_PADATA_FX_FAST); if (fast_padata != NULL){ scratch.length = fast_padata->length; scratch.data = (char *) fast_padata->contents; retval = decode_krb5_pa_fx_fast_request(&scratch, &fast_armored_req); if (retval == 0 &&fast_armored_req->armor) { switch (fast_armored_req->armor->armor_type) { case KRB5_FAST_ARMOR_AP_REQUEST: if (tgs_subkey) { retval = KRB5KDC_ERR_PREAUTH_FAILED; krb5_set_error_message(kdc_context, retval, _("Ap-request armor not permitted " "with TGS")); break; } retval = armor_ap_request(state, fast_armored_req->armor); break; default: krb5_set_error_message(kdc_context, KRB5KDC_ERR_PREAUTH_FAILED, _("Unknown FAST armor type %d"), fast_armored_req->armor->armor_type); retval = KRB5KDC_ERR_PREAUTH_FAILED; } } if (retval == 0 && !state->armor_key) { if (tgs_subkey) retval = krb5_c_fx_cf2_simple(kdc_context, tgs_subkey, "subkeyarmor", tgs_session, "ticketarmor", &state->armor_key); else { retval = KRB5KDC_ERR_PREAUTH_FAILED; krb5_set_error_message(kdc_context, retval, _("No armor key but FAST armored " "request present")); } } if (retval == 0) { krb5_data plaintext; plaintext.length = fast_armored_req->enc_part.ciphertext.length; plaintext.data = malloc(plaintext.length); if (plaintext.data == NULL) retval = ENOMEM; retval = krb5_c_decrypt(kdc_context, state->armor_key, KRB5_KEYUSAGE_FAST_ENC, NULL, &fast_armored_req->enc_part, &plaintext); if (retval == 0) retval = decode_krb5_fast_req(&plaintext, &fast_req); if (retval == 0 && inner_body_out != NULL) { retval = fetch_asn1_field((unsigned char *)plaintext.data, 1, 2, &scratch); if (retval == 0) { retval = krb5_copy_data(kdc_context, &scratch, &inner_body); } } if (plaintext.data) free(plaintext.data); } cksum = &fast_armored_req->req_checksum; if (retval == 0) retval = krb5_c_verify_checksum(kdc_context, state->armor_key, KRB5_KEYUSAGE_FAST_REQ_CHKSUM, checksummed_data, cksum, &cksum_valid); if (retval == 0 && !cksum_valid) { retval = KRB5KRB_AP_ERR_MODIFIED; krb5_set_error_message(kdc_context, retval, _("FAST req_checksum invalid; request " "modified")); } if (retval == 0) { if (!krb5_c_is_keyed_cksum(cksum->checksum_type)) { retval = KRB5KDC_ERR_POLICY; krb5_set_error_message(kdc_context, retval, _("Unkeyed checksum used in fast_req")); } } if (retval == 0) { if ((fast_req->fast_options & UNSUPPORTED_CRITICAL_FAST_OPTIONS) != 0) retval = KRB5KDC_ERR_UNKNOWN_CRITICAL_FAST_OPTION; } if (retval == 0) cookie_padata = krb5int_find_pa_data(kdc_context, fast_req->req_body->padata, KRB5_PADATA_FX_COOKIE); if (retval == 0) { state->fast_options = fast_req->fast_options; krb5_free_kdc_req( kdc_context, request); *requestptr = fast_req->req_body; fast_req->req_body = NULL; } } else { cookie_padata = krb5int_find_pa_data(kdc_context, request->padata, KRB5_PADATA_FX_COOKIE); } if (retval == 0 && cookie_padata != NULL) { krb5_pa_data *new_padata = malloc(sizeof (krb5_pa_data)); if (new_padata == NULL) { retval = ENOMEM; } else { new_padata->pa_type = KRB5_PADATA_FX_COOKIE; new_padata->length = cookie_padata->length; new_padata->contents = malloc(new_padata->length); if (new_padata->contents == NULL) { retval = ENOMEM; free(new_padata); } else { memcpy(new_padata->contents, cookie_padata->contents, new_padata->length); state->cookie = new_padata; } } } if (retval == 0 && inner_body_out != NULL) { *inner_body_out = inner_body; inner_body = NULL; } krb5_free_data(kdc_context, inner_body); if (fast_req) krb5_free_fast_req( kdc_context, fast_req); if (fast_armored_req) krb5_free_fast_armored_req(kdc_context, fast_armored_req); return retval; }
static void change (krb5_auth_context auth_context, krb5_principal admin_principal, uint16_t version, int s, struct sockaddr *sa, int sa_size, krb5_data *in_data) { krb5_error_code ret; char *client = NULL, *admin = NULL; const char *pwd_reason; kadm5_config_params conf; void *kadm5_handle = NULL; krb5_principal principal = NULL; krb5_data *pwd_data = NULL; char *tmp; ChangePasswdDataMS chpw; memset (&conf, 0, sizeof(conf)); memset(&chpw, 0, sizeof(chpw)); if (version == KRB5_KPASSWD_VERS_CHANGEPW) { ret = krb5_copy_data(context, in_data, &pwd_data); if (ret) { krb5_warn (context, ret, "krb5_copy_data"); reply_priv (auth_context, s, sa, sa_size, KRB5_KPASSWD_MALFORMED, "out out memory copying password"); return; } principal = admin_principal; } else if (version == KRB5_KPASSWD_VERS_SETPW) { size_t len; ret = decode_ChangePasswdDataMS(in_data->data, in_data->length, &chpw, &len); if (ret) { krb5_warn (context, ret, "decode_ChangePasswdDataMS"); reply_priv (auth_context, s, sa, sa_size, KRB5_KPASSWD_MALFORMED, "malformed ChangePasswdData"); return; } ret = krb5_copy_data(context, &chpw.newpasswd, &pwd_data); if (ret) { krb5_warn (context, ret, "krb5_copy_data"); reply_priv (auth_context, s, sa, sa_size, KRB5_KPASSWD_MALFORMED, "out out memory copying password"); goto out; } if (chpw.targname == NULL && chpw.targrealm != NULL) { krb5_warn (context, ret, "kadm5_init_with_password_ctx"); reply_priv (auth_context, s, sa, sa_size, KRB5_KPASSWD_MALFORMED, "targrealm but not targname"); goto out; } if (chpw.targname) { krb5_principal_data princ; princ.name = *chpw.targname; princ.realm = *chpw.targrealm; if (princ.realm == NULL) { ret = krb5_get_default_realm(context, &princ.realm); if (ret) { krb5_warnx (context, "kadm5_init_with_password_ctx: " "failed to allocate realm"); reply_priv (auth_context, s, sa, sa_size, KRB5_KPASSWD_SOFTERROR, "failed to allocate realm"); goto out; } } ret = krb5_copy_principal(context, &princ, &principal); if (*chpw.targrealm == NULL) free(princ.realm); if (ret) { krb5_warn(context, ret, "krb5_copy_principal"); reply_priv(auth_context, s, sa, sa_size, KRB5_KPASSWD_HARDERROR, "failed to allocate principal"); goto out; } } else principal = admin_principal; } else { krb5_warnx (context, "kadm5_init_with_password_ctx: unknown proto"); reply_priv (auth_context, s, sa, sa_size, KRB5_KPASSWD_HARDERROR, "Unknown protocol used"); return; } ret = krb5_unparse_name (context, admin_principal, &admin); if (ret) { krb5_warn (context, ret, "unparse_name failed"); reply_priv (auth_context, s, sa, sa_size, KRB5_KPASSWD_HARDERROR, "out of memory error"); goto out; } conf.realm = principal->realm; conf.mask |= KADM5_CONFIG_REALM; ret = kadm5_init_with_password_ctx(context, admin, NULL, KADM5_ADMIN_SERVICE, &conf, 0, 0, &kadm5_handle); if (ret) { krb5_warn (context, ret, "kadm5_init_with_password_ctx"); reply_priv (auth_context, s, sa, sa_size, 2, "Internal error"); goto out; } ret = krb5_unparse_name(context, principal, &client); if (ret) { krb5_warn (context, ret, "unparse_name failed"); reply_priv (auth_context, s, sa, sa_size, KRB5_KPASSWD_HARDERROR, "out of memory error"); goto out; } /* * Check password quality if not changing as administrator */ if (krb5_principal_compare(context, admin_principal, principal) == TRUE) { pwd_reason = kadm5_check_password_quality (context, principal, pwd_data); if (pwd_reason != NULL ) { krb5_warnx (context, "%s didn't pass password quality check with error: %s", client, pwd_reason); reply_priv (auth_context, s, sa, sa_size, KRB5_KPASSWD_SOFTERROR, pwd_reason); goto out; } krb5_warnx (context, "Changing password for %s", client); } else { ret = _kadm5_acl_check_permission(kadm5_handle, KADM5_PRIV_CPW, principal); if (ret) { krb5_warn (context, ret, "Check ACL failed for %s for changing %s password", admin, client); reply_priv (auth_context, s, sa, sa_size, KRB5_KPASSWD_HARDERROR, "permission denied"); goto out; } krb5_warnx (context, "%s is changing password for %s", admin, client); } ret = krb5_data_realloc(pwd_data, pwd_data->length + 1); if (ret) { krb5_warn (context, ret, "malloc: out of memory"); reply_priv (auth_context, s, sa, sa_size, KRB5_KPASSWD_HARDERROR, "Internal error"); goto out; } tmp = pwd_data->data; tmp[pwd_data->length - 1] = '\0'; ret = kadm5_s_chpass_principal_cond (kadm5_handle, principal, tmp); krb5_free_data (context, pwd_data); pwd_data = NULL; if (ret) { const char *str = krb5_get_error_message(context, ret); krb5_warnx(context, "kadm5_s_chpass_principal_cond: %s", str); reply_priv (auth_context, s, sa, sa_size, KRB5_KPASSWD_SOFTERROR, str ? str : "Internal error"); krb5_free_error_message(context, str); goto out; } reply_priv (auth_context, s, sa, sa_size, KRB5_KPASSWD_SUCCESS, "Password changed"); out: free_ChangePasswdDataMS(&chpw); if (principal != admin_principal) krb5_free_principal(context, principal); if (admin) free(admin); if (client) free(client); if (pwd_data) krb5_free_data(context, pwd_data); if (kadm5_handle) kadm5_destroy (kadm5_handle); }
static krb5_error_code kdcrep2creds(krb5_context context, krb5_kdc_rep *pkdcrep, krb5_address *const *address, krb5_data *psectkt, krb5_creds **ppcreds) { krb5_error_code retval; krb5_data *pdata; if ((*ppcreds = (krb5_creds *)calloc(1,sizeof(krb5_creds))) == NULL) { return ENOMEM; } if ((retval = krb5_copy_principal(context, pkdcrep->client, &(*ppcreds)->client))) goto cleanup; if ((retval = krb5_copy_principal(context, pkdcrep->enc_part2->server, &(*ppcreds)->server))) goto cleanup; if ((retval = krb5_copy_keyblock_contents(context, pkdcrep->enc_part2->session, &(*ppcreds)->keyblock))) goto cleanup; TRACE_TGS_REPLY(context, (*ppcreds)->client, (*ppcreds)->server, &(*ppcreds)->keyblock); if ((retval = krb5_copy_data(context, psectkt, &pdata))) goto cleanup_keyblock; (*ppcreds)->second_ticket = *pdata; free(pdata); (*ppcreds)->ticket_flags = pkdcrep->enc_part2->flags; (*ppcreds)->times = pkdcrep->enc_part2->times; (*ppcreds)->magic = KV5M_CREDS; (*ppcreds)->authdata = NULL; /* not used */ (*ppcreds)->is_skey = psectkt->length != 0; if (pkdcrep->enc_part2->caddrs) { if ((retval = krb5_copy_addresses(context, pkdcrep->enc_part2->caddrs, &(*ppcreds)->addresses))) goto cleanup_keyblock; } else { /* no addresses in the list means we got what we had */ if ((retval = krb5_copy_addresses(context, address, &(*ppcreds)->addresses))) goto cleanup_keyblock; } if ((retval = encode_krb5_ticket(pkdcrep->ticket, &pdata))) goto cleanup_keyblock; (*ppcreds)->ticket = *pdata; free(pdata); return 0; cleanup_keyblock: krb5_free_keyblock_contents(context, &(*ppcreds)->keyblock); cleanup: free (*ppcreds); *ppcreds = NULL; return retval; }
/* Decode a reply to produce the clear-text output. */ static krb5_error_code get_clear_result(krb5_context context, krb5_auth_context auth_context, const krb5_data *packet, krb5_data **clear_out, krb5_boolean *is_error_out) { krb5_error_code ret; char *ptr, *end = packet->data + packet->length; unsigned int plen, vno, aplen; krb5_data ap_rep, cipher, error; krb5_ap_rep_enc_part *ap_rep_enc; krb5_replay_data replay; krb5_key send_subkey = NULL; krb5_data clear = empty_data(); *clear_out = NULL; *is_error_out = FALSE; /* Check for an unframed KRB-ERROR (expected for RFC 3244 requests; also * received from MS AD for version 1 requests). */ if (krb5_is_krb_error(packet)) { *is_error_out = TRUE; return get_error_edata(context, packet, clear_out); } if (packet->length < 6) return KRB5KRB_AP_ERR_MODIFIED; /* Decode and verify the length. */ ptr = packet->data; plen = (*ptr++ & 0xff); plen = (plen << 8) | (*ptr++ & 0xff); if (plen != packet->length) return KRB5KRB_AP_ERR_MODIFIED; /* Decode and verify the version number. */ vno = (*ptr++ & 0xff); vno = (vno << 8) | (*ptr++ & 0xff); if (vno != 1 && vno != 0xff80) return KRB5KDC_ERR_BAD_PVNO; /* Decode and check the AP-REP length. */ aplen = (*ptr++ & 0xff); aplen = (aplen << 8) | (*ptr++ & 0xff); if (aplen > end - ptr) return KRB5KRB_AP_ERR_MODIFIED; /* A zero-length AP-REQ indicates a framed KRB-ERROR response. (Expected * for protocol version 1; specified but unusual for RFC 3244 requests.) */ if (aplen == 0) { *is_error_out = TRUE; error = make_data(ptr, end - ptr); return get_error_edata(context, &error, clear_out); } /* We have an AP-REP. Save send_subkey to later smash recv_subkey. */ ret = krb5_auth_con_getsendsubkey_k(context, auth_context, &send_subkey); if (ret) return ret; /* Verify the AP-REP. */ ap_rep = make_data(ptr, aplen); ptr += ap_rep.length; ret = krb5_rd_rep(context, auth_context, &ap_rep, &ap_rep_enc); if (ret) goto cleanup; krb5_free_ap_rep_enc_part(context, ap_rep_enc); /* Smash recv_subkey to be send_subkey, per spec. */ ret = krb5_auth_con_setrecvsubkey_k(context, auth_context, send_subkey); if (ret) goto cleanup; /* Extract and decrypt the result. */ cipher = make_data(ptr, end - ptr); ret = krb5_rd_priv(context, auth_context, &cipher, &clear, &replay); if (ret) goto cleanup; ret = krb5_copy_data(context, &clear, clear_out); if (ret) goto cleanup; *is_error_out = FALSE; cleanup: krb5_k_free_key(context, send_subkey); krb5_free_data_contents(context, &clear); return ret; }