static krb5_error_code fetch_server (const Ticket *t, char **spn, hdb_entry **server, const char *from) { krb5_error_code ret; krb5_principal sprinc; ret = _krb5_principalname2krb5_principal(&sprinc, t->sname, t->realm); if (ret) { kdc_log(0, "_krb5_principalname2krb5_principal: %s", krb5_get_err_text(context, ret)); return ret; } ret = krb5_unparse_name(context, sprinc, spn); if (ret) { krb5_free_principal(context, sprinc); kdc_log(0, "krb5_unparse_name: %s", krb5_get_err_text(context, ret)); return ret; } ret = db_fetch(sprinc, server); krb5_free_principal(context, sprinc); if (ret) { kdc_log(0, "Request to convert ticket from %s for unknown principal %s: %s", from, *spn, krb5_get_err_text(context, ret)); if (ret == HDB_ERR_NOENTRY) ret = KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN; return ret; } return 0; }
static krb5_error_code log_524 (krb5_context context, krb5_kdc_configuration *config, const EncTicketPart *et, const char *from, const char *spn) { krb5_principal client; char *cpn; krb5_error_code ret; ret = _krb5_principalname2krb5_principal(context, &client, et->cname, et->crealm); if (ret) { kdc_log(context, config, 0, "_krb5_principalname2krb5_principal: %s", krb5_get_err_text (context, ret)); return ret; } ret = krb5_unparse_name(context, client, &cpn); if (ret) { krb5_free_principal(context, client); kdc_log(context, config, 0, "krb5_unparse_name: %s", krb5_get_err_text (context, ret)); return ret; } kdc_log(context, config, 1, "524-REQ %s from %s for %s", cpn, from, spn); free(cpn); krb5_free_principal(context, client); return 0; }
static OM_uint32 send_error_token(OM_uint32 *minor_status, krb5_context context, krb5_error_code kret, krb5_principal server, krb5_data *indata, gss_buffer_t output_token) { krb5_principal ap_req_server = NULL; krb5_error_code ret; krb5_data outbuf; /* this e_data value encodes KERB_AP_ERR_TYPE_SKEW_RECOVERY which tells windows to try again with the corrected timestamp. See [MS-KILE] 2.2.1 KERB-ERROR-DATA */ krb5_data e_data = { 7, rk_UNCONST("\x30\x05\xa1\x03\x02\x01\x02") }; /* build server from request if the acceptor had not selected one */ if (server == NULL) { AP_REQ ap_req; ret = krb5_decode_ap_req(context, indata, &ap_req); if (ret) { *minor_status = ret; return GSS_S_FAILURE; } ret = _krb5_principalname2krb5_principal(context, &ap_req_server, ap_req.ticket.sname, ap_req.ticket.realm); free_AP_REQ(&ap_req); if (ret) { *minor_status = ret; return GSS_S_FAILURE; } server = ap_req_server; } ret = krb5_mk_error(context, kret, NULL, &e_data, NULL, server, NULL, NULL, &outbuf); if (ap_req_server) krb5_free_principal(context, ap_req_server); if (ret) { *minor_status = ret; return GSS_S_FAILURE; } ret = _gsskrb5_encapsulate(minor_status, &outbuf, output_token, "\x03\x00", GSS_KRB5_MECHANISM); krb5_data_free (&outbuf); if (ret) return ret; *minor_status = 0; return GSS_S_CONTINUE_NEEDED; }
static krb5_error_code tgs_build_reply(krb5_context context, krb5_kdc_configuration *config, KDC_REQ *req, KDC_REQ_BODY *b, hdb_entry_ex *krbtgt, krb5_enctype krbtgt_etype, krb5_ticket *ticket, krb5_data *reply, const char *from, const char **e_text, AuthorizationData *auth_data, const struct sockaddr *from_addr, int datagram_reply) { krb5_error_code ret; krb5_principal cp = NULL, sp = NULL; krb5_principal client_principal = NULL; char *spn = NULL, *cpn = NULL; hdb_entry_ex *server = NULL, *client = NULL; EncTicketPart *tgt = &ticket->ticket; KRB5SignedPathPrincipals *spp = NULL; const EncryptionKey *ekey; krb5_keyblock sessionkey; krb5_kvno kvno; krb5_data rspac; int cross_realm = 0; PrincipalName *s; Realm r; int nloop = 0; EncTicketPart adtkt; char opt_str[128]; int require_signedpath = 0; memset(&sessionkey, 0, sizeof(sessionkey)); memset(&adtkt, 0, sizeof(adtkt)); krb5_data_zero(&rspac); s = b->sname; r = b->realm; if(b->kdc_options.enc_tkt_in_skey){ Ticket *t; hdb_entry_ex *uu; krb5_principal p; Key *uukey; if(b->additional_tickets == NULL || b->additional_tickets->len == 0){ ret = KRB5KDC_ERR_BADOPTION; /* ? */ kdc_log(context, config, 0, "No second ticket present in request"); goto out; } t = &b->additional_tickets->val[0]; if(!get_krbtgt_realm(&t->sname)){ kdc_log(context, config, 0, "Additional ticket is not a ticket-granting ticket"); ret = KRB5KDC_ERR_POLICY; goto out; } _krb5_principalname2krb5_principal(context, &p, t->sname, t->realm); ret = _kdc_db_fetch(context, config, p, HDB_F_GET_CLIENT|HDB_F_GET_SERVER, NULL, &uu); krb5_free_principal(context, p); if(ret){ if (ret == HDB_ERR_NOENTRY) ret = KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN; goto out; } ret = hdb_enctype2key(context, &uu->entry, t->enc_part.etype, &uukey); if(ret){ _kdc_free_ent(context, uu); ret = KRB5KDC_ERR_ETYPE_NOSUPP; /* XXX */ goto out; } ret = krb5_decrypt_ticket(context, t, &uukey->key, &adtkt, 0); _kdc_free_ent(context, uu); if(ret) goto out; ret = verify_flags(context, config, &adtkt, spn); if (ret) goto out; s = &adtkt.cname; r = adtkt.crealm; } _krb5_principalname2krb5_principal(context, &sp, *s, r); ret = krb5_unparse_name(context, sp, &spn); if (ret) goto out; _krb5_principalname2krb5_principal(context, &cp, tgt->cname, tgt->crealm); ret = krb5_unparse_name(context, cp, &cpn); if (ret) goto out; unparse_flags (KDCOptions2int(b->kdc_options), asn1_KDCOptions_units(), opt_str, sizeof(opt_str)); if(*opt_str) kdc_log(context, config, 0, "TGS-REQ %s from %s for %s [%s]", cpn, from, spn, opt_str); else kdc_log(context, config, 0, "TGS-REQ %s from %s for %s", cpn, from, spn); /* * Fetch server */ server_lookup: ret = _kdc_db_fetch(context, config, sp, HDB_F_GET_SERVER, NULL, &server); if(ret){ const char *new_rlm; Realm req_rlm; krb5_realm *realms; if ((req_rlm = get_krbtgt_realm(&sp->name)) != NULL) { if(nloop++ < 2) { new_rlm = find_rpath(context, tgt->crealm, req_rlm); if(new_rlm) { kdc_log(context, config, 5, "krbtgt for realm %s " "not found, trying %s", req_rlm, new_rlm); krb5_free_principal(context, sp); free(spn); krb5_make_principal(context, &sp, r, KRB5_TGS_NAME, new_rlm, NULL); ret = krb5_unparse_name(context, sp, &spn); if (ret) goto out; goto server_lookup; } } } else if(need_referral(context, sp, &realms)) { if (strcmp(realms[0], sp->realm) != 0) { kdc_log(context, config, 5, "Returning a referral to realm %s for " "server %s that was not found", realms[0], spn); krb5_free_principal(context, sp); free(spn); krb5_make_principal(context, &sp, r, KRB5_TGS_NAME, realms[0], NULL); ret = krb5_unparse_name(context, sp, &spn); if (ret) goto out; krb5_free_host_realm(context, realms); goto server_lookup; } krb5_free_host_realm(context, realms); } kdc_log(context, config, 0, "Server not found in database: %s: %s", spn, krb5_get_err_text(context, ret)); if (ret == HDB_ERR_NOENTRY) ret = KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN; goto out; } ret = _kdc_db_fetch(context, config, cp, HDB_F_GET_CLIENT, NULL, &client); if(ret) { const char *krbtgt_realm; /* * If the client belongs to the same realm as our krbtgt, it * should exist in the local database. * */ krbtgt_realm = krb5_principal_get_comp_string(context, krbtgt->entry.principal, 1); if(strcmp(krb5_principal_get_realm(context, cp), krbtgt_realm) == 0) { if (ret == HDB_ERR_NOENTRY) ret = KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN; kdc_log(context, config, 1, "Client no longer in database: %s", cpn); goto out; } kdc_log(context, config, 1, "Client not found in database: %s: %s", cpn, krb5_get_err_text(context, ret)); cross_realm = 1; } /* * Check that service is in the same realm as the krbtgt. If its * not the same, its someone that is using a uni-directional trust * backward. */ if (strcmp(krb5_principal_get_realm(context, sp), krb5_principal_get_comp_string(context, krbtgt->entry.principal, 1)) != 0) { char *tpn; ret = krb5_unparse_name(context, krbtgt->entry.principal, &tpn); kdc_log(context, config, 0, "Request with wrong krbtgt: %s", (ret == 0) ? tpn : "<unknown>"); if(ret == 0) free(tpn); ret = KRB5KRB_AP_ERR_NOT_US; goto out; } /* * */ client_principal = cp; if (client) { const PA_DATA *sdata; int i = 0; sdata = _kdc_find_padata(req, &i, KRB5_PADATA_S4U2SELF); if (sdata) { krb5_crypto crypto; krb5_data datack; PA_S4U2Self self; char *selfcpn = NULL; const char *str; ret = decode_PA_S4U2Self(sdata->padata_value.data, sdata->padata_value.length, &self, NULL); if (ret) { kdc_log(context, config, 0, "Failed to decode PA-S4U2Self"); goto out; } ret = _krb5_s4u2self_to_checksumdata(context, &self, &datack); if (ret) goto out; ret = krb5_crypto_init(context, &tgt->key, 0, &crypto); if (ret) { free_PA_S4U2Self(&self); krb5_data_free(&datack); kdc_log(context, config, 0, "krb5_crypto_init failed: %s", krb5_get_err_text(context, ret)); goto out; } ret = krb5_verify_checksum(context, crypto, KRB5_KU_OTHER_CKSUM, datack.data, datack.length, &self.cksum); krb5_data_free(&datack); krb5_crypto_destroy(context, crypto); if (ret) { free_PA_S4U2Self(&self); kdc_log(context, config, 0, "krb5_verify_checksum failed for S4U2Self: %s", krb5_get_err_text(context, ret)); goto out; } ret = _krb5_principalname2krb5_principal(context, &client_principal, self.name, self.realm); free_PA_S4U2Self(&self); if (ret) goto out; ret = krb5_unparse_name(context, client_principal, &selfcpn); if (ret) goto out; /* * Check that service doing the impersonating is * requesting a ticket to it-self. */ if (krb5_principal_compare(context, cp, sp) != TRUE) { kdc_log(context, config, 0, "S4U2Self: %s is not allowed " "to impersonate some other user " "(tried for user %s to service %s)", cpn, selfcpn, spn); free(selfcpn); ret = KRB5KDC_ERR_BADOPTION; /* ? */ goto out; } /* * If the service isn't trusted for authentication to * delegation, remove the forward flag. */ if (client->entry.flags.trusted_for_delegation) { str = "[forwardable]"; } else { b->kdc_options.forwardable = 0; str = ""; } kdc_log(context, config, 0, "s4u2self %s impersonating %s to " "service %s %s", cpn, selfcpn, spn, str); free(selfcpn); } } /* * Constrained delegation */ if (client != NULL && b->additional_tickets != NULL && b->additional_tickets->len != 0 && b->kdc_options.enc_tkt_in_skey == 0) { Key *clientkey; Ticket *t; char *str; t = &b->additional_tickets->val[0]; ret = hdb_enctype2key(context, &client->entry, t->enc_part.etype, &clientkey); if(ret){ ret = KRB5KDC_ERR_ETYPE_NOSUPP; /* XXX */ goto out; } ret = krb5_decrypt_ticket(context, t, &clientkey->key, &adtkt, 0); if (ret) { kdc_log(context, config, 0, "failed to decrypt ticket for " "constrained delegation from %s to %s ", spn, cpn); goto out; } /* check that ticket is valid */ if (adtkt.flags.forwardable == 0) { kdc_log(context, config, 0, "Missing forwardable flag on ticket for " "constrained delegation from %s to %s ", spn, cpn); ret = KRB5KDC_ERR_ETYPE_NOSUPP; /* XXX */ goto out; } ret = check_constrained_delegation(context, config, client, sp); if (ret) { kdc_log(context, config, 0, "constrained delegation from %s to %s not allowed", spn, cpn); goto out; } ret = _krb5_principalname2krb5_principal(context, &client_principal, adtkt.cname, adtkt.crealm); if (ret) goto out; ret = krb5_unparse_name(context, client_principal, &str); if (ret) goto out; ret = verify_flags(context, config, &adtkt, str); if (ret) { free(str); goto out; } /* * Check KRB5SignedPath in authorization data and add new entry to * make sure servers can't fake a ticket to us. */ ret = check_KRB5SignedPath(context, config, krbtgt, &adtkt, &spp, 1); if (ret) { kdc_log(context, config, 0, "KRB5SignedPath check from service %s failed " "for delegation to %s for client %s " "from %s failed with %s", spn, str, cpn, from, krb5_get_err_text(context, ret)); free(str); goto out; } kdc_log(context, config, 0, "constrained delegation for %s " "from %s to %s", str, cpn, spn); free(str); /* * Also require that the KDC have issue the service's krbtgt * used to do the request. */ require_signedpath = 1; } /* * Check flags */ ret = _kdc_check_flags(context, config, client, cpn, server, spn, FALSE); if(ret) goto out; if((b->kdc_options.validate || b->kdc_options.renew) && !krb5_principal_compare(context, krbtgt->entry.principal, server->entry.principal)){ kdc_log(context, config, 0, "Inconsistent request."); ret = KRB5KDC_ERR_SERVER_NOMATCH; goto out; } /* check for valid set of addresses */ if(!_kdc_check_addresses(context, config, tgt->caddr, from_addr)) { ret = KRB5KRB_AP_ERR_BADADDR; kdc_log(context, config, 0, "Request from wrong address"); goto out; } /* * Select enctype, return key and kvno. */ { krb5_enctype etype; if(b->kdc_options.enc_tkt_in_skey) { int i; ekey = &adtkt.key; for(i = 0; i < b->etype.len; i++) if (b->etype.val[i] == adtkt.key.keytype) break; if(i == b->etype.len) { krb5_clear_error_string(context); return KRB5KDC_ERR_ETYPE_NOSUPP; } etype = b->etype.val[i]; kvno = 0; } else { Key *skey; ret = _kdc_find_etype(context, server, b->etype.val, b->etype.len, &skey, &etype); if(ret) { kdc_log(context, config, 0, "Server (%s) has no support for etypes", spp); return ret; } ekey = &skey->key; kvno = server->entry.kvno; } ret = krb5_generate_random_keyblock(context, etype, &sessionkey); if (ret) goto out; } /* check PAC if not cross realm and if there is one */ if (!cross_realm) { Key *tkey; ret = hdb_enctype2key(context, &krbtgt->entry, krbtgt_etype, &tkey); if(ret) { kdc_log(context, config, 0, "Failed to find key for krbtgt PAC check"); goto out; } ret = check_PAC(context, config, client_principal, client, server, ekey, &tkey->key, tgt, &rspac, &require_signedpath); if (ret) { kdc_log(context, config, 0, "Verify PAC failed for %s (%s) from %s with %s", spn, cpn, from, krb5_get_err_text(context, ret)); goto out; } } /* also check the krbtgt for signature */ ret = check_KRB5SignedPath(context, config, krbtgt, tgt, &spp, require_signedpath); if (ret) { kdc_log(context, config, 0, "KRB5SignedPath check failed for %s (%s) from %s with %s", spn, cpn, from, krb5_get_err_text(context, ret)); goto out; } /* * */ ret = tgs_make_reply(context, config, b, client_principal, tgt, ekey, &sessionkey, kvno, auth_data, server, spn, client, cp, krbtgt, krbtgt_etype, spp, &rspac, e_text, reply); out: free(spn); free(cpn); krb5_data_free(&rspac); krb5_free_keyblock_contents(context, &sessionkey); if(server) _kdc_free_ent(context, server); if(client) _kdc_free_ent(context, client); if (client_principal && client_principal != cp) krb5_free_principal(context, client_principal); if (cp) krb5_free_principal(context, cp); if (sp) krb5_free_principal(context, sp); free_EncTicketPart(&adtkt); return ret; }
static krb5_error_code tgs_parse_request(krb5_context context, krb5_kdc_configuration *config, KDC_REQ_BODY *b, const PA_DATA *tgs_req, hdb_entry_ex **krbtgt, krb5_enctype *krbtgt_etype, krb5_ticket **ticket, const char **e_text, const char *from, const struct sockaddr *from_addr, time_t **csec, int **cusec, AuthorizationData **auth_data) { krb5_ap_req ap_req; krb5_error_code ret; krb5_principal princ; krb5_auth_context ac = NULL; krb5_flags ap_req_options; krb5_flags verify_ap_req_flags; krb5_crypto crypto; Key *tkey; *auth_data = NULL; *csec = NULL; *cusec = NULL; memset(&ap_req, 0, sizeof(ap_req)); ret = krb5_decode_ap_req(context, &tgs_req->padata_value, &ap_req); if(ret){ kdc_log(context, config, 0, "Failed to decode AP-REQ: %s", krb5_get_err_text(context, ret)); goto out; } if(!get_krbtgt_realm(&ap_req.ticket.sname)){ /* XXX check for ticket.sname == req.sname */ kdc_log(context, config, 0, "PA-DATA is not a ticket-granting ticket"); ret = KRB5KDC_ERR_POLICY; /* ? */ goto out; } _krb5_principalname2krb5_principal(context, &princ, ap_req.ticket.sname, ap_req.ticket.realm); ret = _kdc_db_fetch(context, config, princ, HDB_F_GET_KRBTGT, NULL, krbtgt); if(ret) { char *p; ret = krb5_unparse_name(context, princ, &p); if (ret != 0) p = "<unparse_name failed>"; krb5_free_principal(context, princ); kdc_log(context, config, 0, "Ticket-granting ticket not found in database: %s: %s", p, krb5_get_err_text(context, ret)); if (ret == 0) free(p); ret = KRB5KRB_AP_ERR_NOT_US; goto out; } if(ap_req.ticket.enc_part.kvno && *ap_req.ticket.enc_part.kvno != (*krbtgt)->entry.kvno){ char *p; ret = krb5_unparse_name (context, princ, &p); krb5_free_principal(context, princ); if (ret != 0) p = "<unparse_name failed>"; kdc_log(context, config, 0, "Ticket kvno = %d, DB kvno = %d (%s)", *ap_req.ticket.enc_part.kvno, (*krbtgt)->entry.kvno, p); if (ret == 0) free (p); ret = KRB5KRB_AP_ERR_BADKEYVER; goto out; } *krbtgt_etype = ap_req.ticket.enc_part.etype; ret = hdb_enctype2key(context, &(*krbtgt)->entry, ap_req.ticket.enc_part.etype, &tkey); if(ret){ char *str, *p; krb5_enctype_to_string(context, ap_req.ticket.enc_part.etype, &str); krb5_unparse_name(context, princ, &p); kdc_log(context, config, 0, "No server key with enctype %s found for %s", str, p); free(str); free(p); ret = KRB5KRB_AP_ERR_BADKEYVER; goto out; } if (b->kdc_options.validate) verify_ap_req_flags = KRB5_VERIFY_AP_REQ_IGNORE_INVALID; else verify_ap_req_flags = 0; ret = krb5_verify_ap_req2(context, &ac, &ap_req, princ, &tkey->key, verify_ap_req_flags, &ap_req_options, ticket, KRB5_KU_TGS_REQ_AUTH); krb5_free_principal(context, princ); if(ret) { kdc_log(context, config, 0, "Failed to verify AP-REQ: %s", krb5_get_err_text(context, ret)); goto out; } { krb5_authenticator auth; ret = krb5_auth_con_getauthenticator(context, ac, &auth); if (ret == 0) { *csec = malloc(sizeof(**csec)); if (*csec == NULL) { krb5_free_authenticator(context, &auth); kdc_log(context, config, 0, "malloc failed"); goto out; } **csec = auth->ctime; *cusec = malloc(sizeof(**cusec)); if (*cusec == NULL) { krb5_free_authenticator(context, &auth); kdc_log(context, config, 0, "malloc failed"); goto out; } **cusec = auth->cusec; krb5_free_authenticator(context, &auth); } } ret = tgs_check_authenticator(context, config, ac, b, e_text, &(*ticket)->ticket.key); if (ret) { krb5_auth_con_free(context, ac); goto out; } if (b->enc_authorization_data) { krb5_keyblock *subkey; krb5_data ad; ret = krb5_auth_con_getremotesubkey(context, ac, &subkey); if(ret){ krb5_auth_con_free(context, ac); kdc_log(context, config, 0, "Failed to get remote subkey: %s", krb5_get_err_text(context, ret)); goto out; } if(subkey == NULL){ ret = krb5_auth_con_getkey(context, ac, &subkey); if(ret) { krb5_auth_con_free(context, ac); kdc_log(context, config, 0, "Failed to get session key: %s", krb5_get_err_text(context, ret)); goto out; } } if(subkey == NULL){ krb5_auth_con_free(context, ac); kdc_log(context, config, 0, "Failed to get key for enc-authorization-data"); ret = KRB5KRB_AP_ERR_BAD_INTEGRITY; /* ? */ goto out; } ret = krb5_crypto_init(context, subkey, 0, &crypto); if (ret) { krb5_auth_con_free(context, ac); kdc_log(context, config, 0, "krb5_crypto_init failed: %s", krb5_get_err_text(context, ret)); goto out; } ret = krb5_decrypt_EncryptedData (context, crypto, KRB5_KU_TGS_REQ_AUTH_DAT_SUBKEY, b->enc_authorization_data, &ad); krb5_crypto_destroy(context, crypto); if(ret){ krb5_auth_con_free(context, ac); kdc_log(context, config, 0, "Failed to decrypt enc-authorization-data"); ret = KRB5KRB_AP_ERR_BAD_INTEGRITY; /* ? */ goto out; } krb5_free_keyblock(context, subkey); ALLOC(*auth_data); if (*auth_data == NULL) { krb5_auth_con_free(context, ac); ret = KRB5KRB_AP_ERR_BAD_INTEGRITY; /* ? */ goto out; } ret = decode_AuthorizationData(ad.data, ad.length, *auth_data, NULL); if(ret){ krb5_auth_con_free(context, ac); free(*auth_data); *auth_data = NULL; kdc_log(context, config, 0, "Failed to decode authorization data"); ret = KRB5KRB_AP_ERR_BAD_INTEGRITY; /* ? */ goto out; } } krb5_auth_con_free(context, ac); out: free_AP_REQ(&ap_req); return ret; }
int main(int argc, char **argv) { krb5_error_code ret; krb5_context context; krb5_auth_context ac = NULL; krb5_principal c1, c2; krb5_authenticator authent; krb5_keytab keytab; krb5_socket_t sock = rk_INVALID_SOCKET; HDB *db = NULL; int optidx = 0; char *tmp_db; krb5_log_facility *fac; int nprincs; setprogname(argv[0]); ret = krb5_init_context(&context); if(ret) exit(1); ret = krb5_openlog(context, "hpropd", &fac); if(ret) errx(1, "krb5_openlog"); krb5_set_warn_dest(context, fac); if(getarg(args, num_args, argc, argv, &optidx)) usage(1); if(local_realm != NULL) krb5_set_default_realm(context, local_realm); if(help_flag) usage(0); if(version_flag) { print_version(NULL); exit(0); } argc -= optidx; argv += optidx; if (argc != 0) usage(1); if (database == NULL) database = hdb_default_db(context); if(from_stdin) { sock = STDIN_FILENO; } else { struct sockaddr_storage ss; struct sockaddr *sa = (struct sockaddr *)&ss; socklen_t sin_len = sizeof(ss); char addr_name[256]; krb5_ticket *ticket; char *server; sock = STDIN_FILENO; #ifdef SUPPORT_INETD if (inetd_flag == -1) { if (getpeername (sock, sa, &sin_len) < 0) { inetd_flag = 0; } else { inetd_flag = 1; } } #else inetd_flag = 0; #endif if (!inetd_flag) { mini_inetd (krb5_getportbyname (context, "hprop", "tcp", HPROP_PORT), &sock); } sin_len = sizeof(ss); if(getpeername(sock, sa, &sin_len) < 0) krb5_err(context, 1, errno, "getpeername"); if (inet_ntop(sa->sa_family, socket_get_address (sa), addr_name, sizeof(addr_name)) == NULL) strlcpy (addr_name, "unknown address", sizeof(addr_name)); krb5_log(context, fac, 0, "Connection from %s", addr_name); ret = krb5_kt_register(context, &hdb_kt_ops); if(ret) krb5_err(context, 1, ret, "krb5_kt_register"); if (ktname != NULL) { ret = krb5_kt_resolve(context, ktname, &keytab); if (ret) krb5_err (context, 1, ret, "krb5_kt_resolve %s", ktname); } else { ret = krb5_kt_default (context, &keytab); if (ret) krb5_err (context, 1, ret, "krb5_kt_default"); } ret = krb5_recvauth(context, &ac, &sock, HPROP_VERSION, NULL, 0, keytab, &ticket); if(ret) krb5_err(context, 1, ret, "krb5_recvauth"); ret = krb5_unparse_name(context, ticket->server, &server); if (ret) krb5_err(context, 1, ret, "krb5_unparse_name"); if (strncmp(server, "hprop/", 5) != 0) krb5_errx(context, 1, "ticket not for hprop (%s)", server); free(server); krb5_free_ticket (context, ticket); ret = krb5_auth_con_getauthenticator(context, ac, &authent); if(ret) krb5_err(context, 1, ret, "krb5_auth_con_getauthenticator"); ret = krb5_make_principal(context, &c1, NULL, "kadmin", "hprop", NULL); if(ret) krb5_err(context, 1, ret, "krb5_make_principal"); _krb5_principalname2krb5_principal(context, &c2, authent->cname, authent->crealm); if(!krb5_principal_compare(context, c1, c2)) { char *s; ret = krb5_unparse_name(context, c2, &s); if (ret) s = unparseable_name; krb5_errx(context, 1, "Unauthorized connection from %s", s); } krb5_free_principal(context, c1); krb5_free_principal(context, c2); ret = krb5_kt_close(context, keytab); if(ret) krb5_err(context, 1, ret, "krb5_kt_close"); } if(!print_dump) { asprintf(&tmp_db, "%s~", database); ret = hdb_create(context, &db, tmp_db); if(ret) krb5_err(context, 1, ret, "hdb_create(%s)", tmp_db); ret = db->hdb_open(context, db, O_RDWR | O_CREAT | O_TRUNC, 0600); if(ret) krb5_err(context, 1, ret, "hdb_open(%s)", tmp_db); } nprincs = 0; while(1){ krb5_data data; hdb_entry_ex entry; if(from_stdin) { ret = krb5_read_message(context, &sock, &data); if(ret != 0 && ret != HEIM_ERR_EOF) krb5_err(context, 1, ret, "krb5_read_message"); } else { ret = krb5_read_priv_message(context, ac, &sock, &data); if(ret) krb5_err(context, 1, ret, "krb5_read_priv_message"); } if(ret == HEIM_ERR_EOF || data.length == 0) { if(!from_stdin) { data.data = NULL; data.length = 0; krb5_write_priv_message(context, ac, &sock, &data); } if(!print_dump) { ret = db->hdb_close(context, db); if(ret) krb5_err(context, 1, ret, "db_close"); ret = db->hdb_rename(context, db, database); if(ret) krb5_err(context, 1, ret, "db_rename"); } break; } memset(&entry, 0, sizeof(entry)); ret = hdb_value2entry(context, &data, &entry.entry); krb5_data_free(&data); if(ret) krb5_err(context, 1, ret, "hdb_value2entry"); if(print_dump) hdb_print_entry(context, db, &entry, stdout); else { ret = db->hdb_store(context, db, 0, &entry); if(ret == HDB_ERR_EXISTS) { char *s; ret = krb5_unparse_name(context, entry.entry.principal, &s); if (ret) s = strdup(unparseable_name); krb5_warnx(context, "Entry exists: %s", s); free(s); } else if(ret) krb5_err(context, 1, ret, "db_store"); else nprincs++; } hdb_free_entry(context, &entry); } if (!print_dump) krb5_log(context, fac, 0, "Received %d principals", nprincs); if (inetd_flag == 0) rk_closesocket(sock); exit(0); }
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL krb5_rd_req_ctx(krb5_context context, krb5_auth_context *auth_context, const krb5_data *inbuf, krb5_const_principal server, krb5_rd_req_in_ctx inctx, krb5_rd_req_out_ctx *outctx) { krb5_error_code ret; krb5_ap_req ap_req; krb5_rd_req_out_ctx o = NULL; krb5_keytab id = NULL, keytab = NULL; krb5_principal service = NULL; *outctx = NULL; o = calloc(1, sizeof(*o)); if (o == NULL) { krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", "")); return ENOMEM; } if (*auth_context == NULL) { ret = krb5_auth_con_init(context, auth_context); if (ret) goto out; } ret = krb5_decode_ap_req(context, inbuf, &ap_req); if(ret) goto out; /* Save that principal that was in the request */ ret = _krb5_principalname2krb5_principal(context, &o->server, ap_req.ticket.sname, ap_req.ticket.realm); if (ret) goto out; if (ap_req.ap_options.use_session_key && (*auth_context)->keyblock == NULL) { ret = KRB5KRB_AP_ERR_NOKEY; krb5_set_error_message(context, ret, N_("krb5_rd_req: user to user auth " "without session key given", "")); goto out; } if (inctx && inctx->keytab) id = inctx->keytab; if((*auth_context)->keyblock){ ret = krb5_copy_keyblock(context, (*auth_context)->keyblock, &o->keyblock); if (ret) goto out; } else if(inctx && inctx->keyblock){ ret = krb5_copy_keyblock(context, inctx->keyblock, &o->keyblock); if (ret) goto out; } else { if(id == NULL) { krb5_kt_default(context, &keytab); id = keytab; } if (id == NULL) goto out; if (server == NULL) { ret = _krb5_principalname2krb5_principal(context, &service, ap_req.ticket.sname, ap_req.ticket.realm); if (ret) goto out; server = service; } ret = get_key_from_keytab(context, &ap_req, server, id, &o->keyblock); if (ret) { /* If caller specified a server, fail. */ if (service == NULL && (context->flags & KRB5_CTX_F_RD_REQ_IGNORE) == 0) goto out; /* Otherwise, fall back to iterating over the keytab. This * have serious performace issues for larger keytab. */ o->keyblock = NULL; } } if (o->keyblock) { /* * We got an exact keymatch, use that. */ ret = krb5_verify_ap_req2(context, auth_context, &ap_req, server, o->keyblock, 0, &o->ap_req_options, &o->ticket, KRB5_KU_AP_REQ_AUTH); if (ret) goto out; } else { /* * Interate over keytab to find a key that can decrypt the request. */ krb5_keytab_entry entry; krb5_kt_cursor cursor; int done = 0, kvno = 0; memset(&cursor, 0, sizeof(cursor)); if (ap_req.ticket.enc_part.kvno) kvno = *ap_req.ticket.enc_part.kvno; ret = krb5_kt_start_seq_get(context, id, &cursor); if (ret) goto out; done = 0; while (!done) { krb5_principal p; ret = krb5_kt_next_entry(context, id, &entry, &cursor); if (ret) { _krb5_kt_principal_not_found(context, ret, id, o->server, ap_req.ticket.enc_part.etype, kvno); krb5_kt_end_seq_get(context, id, &cursor); goto out; } if (entry.keyblock.keytype != ap_req.ticket.enc_part.etype) { krb5_kt_free_entry (context, &entry); continue; } ret = krb5_verify_ap_req2(context, auth_context, &ap_req, server, &entry.keyblock, 0, &o->ap_req_options, &o->ticket, KRB5_KU_AP_REQ_AUTH); if (ret) { krb5_kt_free_entry(context, &entry); continue; } /* * Found a match, save the keyblock for PAC processing, * and update the service principal in the ticket to match * whatever is in the keytab. */ ret = krb5_copy_keyblock(context, &entry.keyblock, &o->keyblock); if (ret) { krb5_kt_free_entry(context, &entry); krb5_kt_end_seq_get(context, id, &cursor); goto out; } ret = krb5_copy_principal(context, entry.principal, &p); if (ret) { krb5_kt_free_entry(context, &entry); krb5_kt_end_seq_get(context, id, &cursor); goto out; } krb5_free_principal(context, o->ticket->server); o->ticket->server = p; krb5_kt_free_entry(context, &entry); done = 1; } krb5_kt_end_seq_get(context, id, &cursor); } /* If there is a PAC, verify its server signature */ if (inctx == NULL || inctx->check_pac) { krb5_pac pac; krb5_data data; ret = krb5_ticket_get_authorization_data_type(context, o->ticket, KRB5_AUTHDATA_WIN2K_PAC, &data); if (ret == 0) { ret = krb5_pac_parse(context, data.data, data.length, &pac); krb5_data_free(&data); if (ret) goto out; ret = krb5_pac_verify(context, pac, o->ticket->ticket.authtime, o->ticket->client, o->keyblock, NULL); krb5_pac_free(context, pac); if (ret == 0) o->flags |= KRB5_RD_REQ_OUT_PAC_VALID; ret = 0; } else ret = 0; } out: if (ret || outctx == NULL) { krb5_rd_req_out_ctx_free(context, o); } else *outctx = o; free_AP_REQ(&ap_req); if (service) krb5_free_principal(context, service); if (keytab) krb5_kt_close(context, keytab); return ret; }
krb5_error_code _gsspku2u_principal(krb5_context context, struct hx509_cert_data *cert, krb5_principal *principal) { hx509_octet_string_list list; krb5_error_code ret; int found = 0; unsigned i; char *name; *principal = NULL; /* * First try to map PKINIT SAN to a Kerberos principal */ ret = hx509_cert_find_subjectAltName_otherName(context->hx509ctx, cert, &asn1_oid_id_pkinit_san, &list); if (ret == 0) { for (i = 0; !found && i < list.len; i++) { KRB5PrincipalName r; ret = decode_KRB5PrincipalName(list.val[i].data, list.val[i].length, &r, NULL); if (ret) continue; ret = _krb5_principalname2krb5_principal(context, principal, r.principalName, KRB5_PKU2U_REALM_NAME); free_KRB5PrincipalName(&r); if (ret == 0) found = 1; } hx509_free_octet_string_list(&list); } if (found) return 0; /* * */ ret = hx509_cert_get_appleid(context->hx509ctx, cert, &name); if (ret == 0) { ret = krb5_make_principal(context, principal, KRB5_PKU2U_REALM_NAME, name, NULL); hx509_xfree(name); if (ret == 0) { (*principal)->name.name_type = KRB5_NT_ENTERPRISE_PRINCIPAL; return 0; } } /* * Give up and just WELLKNOWN and assertion instead */ ret = krb5_make_principal(context, principal, KRB5_PKU2U_REALM_NAME, KRB5_WELLKNOWN_NAME, KRB5_NULL_NAME, NULL); if (ret == 0) (*principal)->name.name_type = KRB5_NT_WELLKNOWN; return ret; }
int _krb5_extract_ticket(krb5_context context, krb5_kdc_rep *rep, krb5_creds *creds, krb5_keyblock *key, krb5_const_pointer keyseed, krb5_key_usage key_usage, krb5_addresses *addrs, unsigned nonce, unsigned flags, krb5_decrypt_proc decrypt_proc, krb5_const_pointer decryptarg) { krb5_error_code ret; krb5_principal tmp_principal; size_t len = 0; time_t tmp_time; krb5_timestamp sec_now; /* decrypt */ if (decrypt_proc == NULL) decrypt_proc = decrypt_tkt; ret = (*decrypt_proc)(context, key, key_usage, decryptarg, rep); if (ret) goto out; /* save session key */ creds->session.keyvalue.length = 0; creds->session.keyvalue.data = NULL; creds->session.keytype = rep->enc_part.key.keytype; ret = krb5_data_copy (&creds->session.keyvalue, rep->enc_part.key.keyvalue.data, rep->enc_part.key.keyvalue.length); if (ret) { krb5_clear_error_message(context); goto out; } /* compare client and save */ ret = _krb5_principalname2krb5_principal (context, &tmp_principal, rep->kdc_rep.cname, rep->kdc_rep.crealm); if (ret) goto out; /* check client referral and save principal */ /* anonymous here ? */ if((flags & EXTRACT_TICKET_ALLOW_CNAME_MISMATCH) == 0) { ret = check_client_referral(context, rep, creds->client, tmp_principal, &creds->session); if (ret) { krb5_free_principal (context, tmp_principal); goto out; } } krb5_free_principal (context, creds->client); creds->client = tmp_principal; /* check server referral and save principal */ ret = _krb5_principalname2krb5_principal (context, &tmp_principal, rep->kdc_rep.ticket.sname, rep->kdc_rep.ticket.realm); if (ret) goto out; if((flags & EXTRACT_TICKET_ALLOW_SERVER_MISMATCH) == 0){ ret = check_server_referral(context, rep, flags, creds->server, tmp_principal, &creds->session); if (ret) { krb5_free_principal (context, tmp_principal); goto out; } } krb5_free_principal(context, creds->server); creds->server = tmp_principal; /* verify names */ if(flags & EXTRACT_TICKET_MATCH_REALM){ const char *srealm = krb5_principal_get_realm(context, creds->server); const char *crealm = krb5_principal_get_realm(context, creds->client); if (strcmp(rep->enc_part.srealm, srealm) != 0 || strcmp(rep->enc_part.srealm, crealm) != 0) { ret = KRB5KRB_AP_ERR_MODIFIED; krb5_clear_error_message(context); goto out; } } /* compare nonces */ if (nonce != (unsigned)rep->enc_part.nonce) { ret = KRB5KRB_AP_ERR_MODIFIED; krb5_set_error_message(context, ret, N_("malloc: out of memory", "")); goto out; } /* set kdc-offset */ krb5_timeofday (context, &sec_now); if (rep->enc_part.flags.initial && (flags & EXTRACT_TICKET_TIMESYNC) && context->kdc_sec_offset == 0 && krb5_config_get_bool (context, NULL, "libdefaults", "kdc_timesync", NULL)) { context->kdc_sec_offset = rep->enc_part.authtime - sec_now; krb5_timeofday (context, &sec_now); } /* check all times */ if (rep->enc_part.starttime) { tmp_time = *rep->enc_part.starttime; } else tmp_time = rep->enc_part.authtime; if (creds->times.starttime == 0 && abs(tmp_time - sec_now) > context->max_skew) { ret = KRB5KRB_AP_ERR_SKEW; krb5_set_error_message (context, ret, N_("time skew (%d) larger than max (%d)", ""), abs(tmp_time - sec_now), (int)context->max_skew); goto out; } if (creds->times.starttime != 0 && tmp_time != creds->times.starttime) { krb5_clear_error_message (context); ret = KRB5KRB_AP_ERR_MODIFIED; goto out; } creds->times.starttime = tmp_time; if (rep->enc_part.renew_till) { tmp_time = *rep->enc_part.renew_till; } else tmp_time = 0; if (creds->times.renew_till != 0 && tmp_time > creds->times.renew_till) { krb5_clear_error_message (context); ret = KRB5KRB_AP_ERR_MODIFIED; goto out; } creds->times.renew_till = tmp_time; creds->times.authtime = rep->enc_part.authtime; if (creds->times.endtime != 0 && rep->enc_part.endtime > creds->times.endtime) { krb5_clear_error_message (context); ret = KRB5KRB_AP_ERR_MODIFIED; goto out; } creds->times.endtime = rep->enc_part.endtime; if(rep->enc_part.caddr) krb5_copy_addresses (context, rep->enc_part.caddr, &creds->addresses); else if(addrs) krb5_copy_addresses (context, addrs, &creds->addresses); else { creds->addresses.len = 0; creds->addresses.val = NULL; } creds->flags.b = rep->enc_part.flags; creds->authdata.len = 0; creds->authdata.val = NULL; /* extract ticket */ ASN1_MALLOC_ENCODE(Ticket, creds->ticket.data, creds->ticket.length, &rep->kdc_rep.ticket, &len, ret); if(ret) goto out; if (creds->ticket.length != len) krb5_abortx(context, "internal error in ASN.1 encoder"); creds->second_ticket.length = 0; creds->second_ticket.data = NULL; out: memset (rep->enc_part.key.keyvalue.data, 0, rep->enc_part.key.keyvalue.length); return ret; }
krb5_error_code KRB5_LIB_FUNCTION krb5_rd_req_ctx(krb5_context context, krb5_auth_context *auth_context, const krb5_data *inbuf, krb5_const_principal server, krb5_rd_req_in_ctx inctx, krb5_rd_req_out_ctx *outctx) { krb5_error_code ret; krb5_ap_req ap_req; krb5_principal service = NULL; krb5_rd_req_out_ctx o = NULL; ret = _krb5_rd_req_out_ctx_alloc(context, &o); if (ret) goto out; if (*auth_context == NULL) { ret = krb5_auth_con_init(context, auth_context); if (ret) goto out; } ret = krb5_decode_ap_req(context, inbuf, &ap_req); if(ret) goto out; if(server == NULL){ ret = _krb5_principalname2krb5_principal(context, &service, ap_req.ticket.sname, ap_req.ticket.realm); if (ret) goto out; server = service; } if (ap_req.ap_options.use_session_key && (*auth_context)->keyblock == NULL) { krb5_set_error_string(context, "krb5_rd_req: user to user auth " "without session key given"); ret = KRB5KRB_AP_ERR_NOKEY; goto out; } if((*auth_context)->keyblock){ ret = krb5_copy_keyblock(context, (*auth_context)->keyblock, &o->keyblock); if (ret) goto out; } else if(inctx->keyblock){ ret = krb5_copy_keyblock(context, inctx->keyblock, &o->keyblock); if (ret) goto out; } else { krb5_keytab keytab = NULL; if (inctx && inctx->keytab) keytab = inctx->keytab; ret = get_key_from_keytab(context, auth_context, &ap_req, server, keytab, &o->keyblock); if(ret) goto out; } ret = krb5_verify_ap_req2(context, auth_context, &ap_req, server, o->keyblock, 0, &o->ap_req_options, &o->ticket, KRB5_KU_AP_REQ_AUTH); if (ret) goto out; /* If there is a PAC, verify its server signature */ if (inctx->check_pac) { krb5_pac pac; krb5_data data; ret = krb5_ticket_get_authorization_data_type(context, o->ticket, KRB5_AUTHDATA_WIN2K_PAC, &data); if (ret == 0) { ret = krb5_pac_parse(context, data.data, data.length, &pac); krb5_data_free(&data); if (ret) goto out; ret = krb5_pac_verify(context, pac, o->ticket->ticket.authtime, o->ticket->client, o->keyblock, NULL); krb5_pac_free(context, pac); if (ret) goto out; } ret = 0; } out: if (ret || outctx == NULL) { krb5_rd_req_out_ctx_free(context, o); } else *outctx = o; free_AP_REQ(&ap_req); if(service) krb5_free_principal(context, service); return ret; }
krb5_error_code _kdc_as_rep(krb5_context context, krb5_kdc_configuration *config, KDC_REQ *req, const krb5_data *req_buffer, krb5_data *reply, const char *from, struct sockaddr *from_addr, int datagram_reply) { KDC_REQ_BODY *b = &req->req_body; AS_REP rep; KDCOptions f = b->kdc_options; hdb_entry_ex *client = NULL, *server = NULL; krb5_enctype cetype, setype, sessionetype; krb5_data e_data; EncTicketPart et; EncKDCRepPart ek; krb5_principal client_princ = NULL, server_princ = NULL; char *client_name = NULL, *server_name = NULL; krb5_error_code ret = 0; const char *e_text = NULL; krb5_crypto crypto; Key *ckey, *skey; EncryptionKey *reply_key; int flags = 0; #ifdef PKINIT pk_client_params *pkp = NULL; #endif memset(&rep, 0, sizeof(rep)); krb5_data_zero(&e_data); if (f.canonicalize) flags |= HDB_F_CANON; if(b->sname == NULL){ ret = KRB5KRB_ERR_GENERIC; e_text = "No server in request"; } else{ ret = _krb5_principalname2krb5_principal (context, &server_princ, *(b->sname), b->realm); if (ret == 0) ret = krb5_unparse_name(context, server_princ, &server_name); } if (ret) { kdc_log(context, config, 0, "AS-REQ malformed server name from %s", from); goto out; } if(b->cname == NULL){ ret = KRB5KRB_ERR_GENERIC; e_text = "No client in request"; } else { if (b->cname->name_type == KRB5_NT_ENTERPRISE_PRINCIPAL) { if (b->cname->name_string.len != 1) { kdc_log(context, config, 0, "AS-REQ malformed canon request from %s, " "enterprise name with %d name components", from, b->cname->name_string.len); ret = KRB5_PARSE_MALFORMED; goto out; } ret = krb5_parse_name(context, b->cname->name_string.val[0], &client_princ); if (ret) goto out; } else { ret = _krb5_principalname2krb5_principal (context, &client_princ, *(b->cname), b->realm); if (ret) goto out; } ret = krb5_unparse_name(context, client_princ, &client_name); } if (ret) { kdc_log(context, config, 0, "AS-REQ malformed client name from %s", from); goto out; } kdc_log(context, config, 0, "AS-REQ %s from %s for %s", client_name, from, server_name); ret = _kdc_db_fetch(context, config, client_princ, HDB_F_GET_CLIENT | flags, NULL, &client); if(ret){ kdc_log(context, config, 0, "UNKNOWN -- %s: %s", client_name, krb5_get_err_text(context, ret)); ret = KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN; goto out; } ret = _kdc_db_fetch(context, config, server_princ, HDB_F_GET_SERVER|HDB_F_GET_KRBTGT, NULL, &server); if(ret){ kdc_log(context, config, 0, "UNKNOWN -- %s: %s", server_name, krb5_get_err_text(context, ret)); ret = KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN; goto out; } ret = _kdc_windc_client_access(context, client, req); if(ret) goto out; ret = _kdc_check_flags(context, config, client, client_name, server, server_name, TRUE); if(ret) goto out; memset(&et, 0, sizeof(et)); memset(&ek, 0, sizeof(ek)); if(req->padata){ int i; const PA_DATA *pa; int found_pa = 0; log_patypes(context, config, req->padata); #ifdef PKINIT kdc_log(context, config, 5, "Looking for PKINIT pa-data -- %s", client_name); e_text = "No PKINIT PA found"; i = 0; if ((pa = _kdc_find_padata(req, &i, KRB5_PADATA_PK_AS_REQ))) ; if (pa == NULL) { i = 0; if((pa = _kdc_find_padata(req, &i, KRB5_PADATA_PK_AS_REQ_WIN))) ; } if (pa) { char *client_cert = NULL; ret = _kdc_pk_rd_padata(context, config, req, pa, &pkp); if (ret) { ret = KRB5KRB_AP_ERR_BAD_INTEGRITY; kdc_log(context, config, 5, "Failed to decode PKINIT PA-DATA -- %s", client_name); goto ts_enc; } if (ret == 0 && pkp == NULL) goto ts_enc; ret = _kdc_pk_check_client(context, config, client, pkp, &client_cert); if (ret) { e_text = "PKINIT certificate not allowed to " "impersonate principal"; _kdc_pk_free_client_param(context, pkp); kdc_log(context, config, 0, "%s", e_text); pkp = NULL; goto out; } found_pa = 1; et.flags.pre_authent = 1; kdc_log(context, config, 0, "PKINIT pre-authentication succeeded -- %s using %s", client_name, client_cert); free(client_cert); if (pkp) goto preauth_done; } ts_enc: #endif kdc_log(context, config, 5, "Looking for ENC-TS pa-data -- %s", client_name); i = 0; e_text = "No ENC-TS found"; while((pa = _kdc_find_padata(req, &i, KRB5_PADATA_ENC_TIMESTAMP))){ krb5_data ts_data; PA_ENC_TS_ENC p; size_t len; EncryptedData enc_data; Key *pa_key; char *str; found_pa = 1; ret = decode_EncryptedData(pa->padata_value.data, pa->padata_value.length, &enc_data, &len); if (ret) { ret = KRB5KRB_AP_ERR_BAD_INTEGRITY; kdc_log(context, config, 5, "Failed to decode PA-DATA -- %s", client_name); goto out; } ret = hdb_enctype2key(context, &client->entry, enc_data.etype, &pa_key); if(ret){ char *estr; e_text = "No key matches pa-data"; ret = KRB5KDC_ERR_ETYPE_NOSUPP; if(krb5_enctype_to_string(context, enc_data.etype, &estr)) estr = NULL; if(estr == NULL) kdc_log(context, config, 5, "No client key matching pa-data (%d) -- %s", enc_data.etype, client_name); else kdc_log(context, config, 5, "No client key matching pa-data (%s) -- %s", estr, client_name); free(estr); free_EncryptedData(&enc_data); continue; } try_next_key: ret = krb5_crypto_init(context, &pa_key->key, 0, &crypto); if (ret) { kdc_log(context, config, 0, "krb5_crypto_init failed: %s", krb5_get_err_text(context, ret)); free_EncryptedData(&enc_data); continue; } ret = krb5_decrypt_EncryptedData (context, crypto, KRB5_KU_PA_ENC_TIMESTAMP, &enc_data, &ts_data); krb5_crypto_destroy(context, crypto); if(ret){ krb5_error_code ret2; ret2 = krb5_enctype_to_string(context, pa_key->key.keytype, &str); if (ret2) str = NULL; kdc_log(context, config, 5, "Failed to decrypt PA-DATA -- %s " "(enctype %s) error %s", client_name, str ? str : "unknown enctype", krb5_get_err_text(context, ret)); free(str); if(hdb_next_enctype2key(context, &client->entry, enc_data.etype, &pa_key) == 0) goto try_next_key; e_text = "Failed to decrypt PA-DATA"; free_EncryptedData(&enc_data); ret = KRB5KDC_ERR_PREAUTH_FAILED; continue; } free_EncryptedData(&enc_data); ret = decode_PA_ENC_TS_ENC(ts_data.data, ts_data.length, &p, &len); krb5_data_free(&ts_data); if(ret){ e_text = "Failed to decode PA-ENC-TS-ENC"; ret = KRB5KDC_ERR_PREAUTH_FAILED; kdc_log(context, config, 5, "Failed to decode PA-ENC-TS_ENC -- %s", client_name); continue; } free_PA_ENC_TS_ENC(&p); if (abs(kdc_time - p.patimestamp) > context->max_skew) { char client_time[100]; krb5_format_time(context, p.patimestamp, client_time, sizeof(client_time), TRUE); ret = KRB5KRB_AP_ERR_SKEW; kdc_log(context, config, 0, "Too large time skew, " "client time %s is out by %u > %u seconds -- %s", client_time, (unsigned)abs(kdc_time - p.patimestamp), context->max_skew, client_name); #if 0 /* This code is from samba, needs testing */ /* * the following is needed to make windows clients * to retry using the timestamp in the error message * * this is maybe a bug in windows to not trying when e_text * is present... */ e_text = NULL; #else e_text = "Too large time skew"; #endif goto out; } et.flags.pre_authent = 1; ret = krb5_enctype_to_string(context,pa_key->key.keytype, &str); if (ret) str = NULL; kdc_log(context, config, 2, "ENC-TS Pre-authentication succeeded -- %s using %s", client_name, str ? str : "unknown enctype"); free(str); break; } #ifdef PKINIT preauth_done: #endif if(found_pa == 0 && config->require_preauth) goto use_pa; /* We come here if we found a pa-enc-timestamp, but if there was some problem with it, other than too large skew */ if(found_pa && et.flags.pre_authent == 0){ kdc_log(context, config, 0, "%s -- %s", e_text, client_name); e_text = NULL; goto out; } }else if (config->require_preauth || client->entry.flags.require_preauth || server->entry.flags.require_preauth) { METHOD_DATA method_data; PA_DATA *pa; unsigned char *buf; size_t len; use_pa: method_data.len = 0; method_data.val = NULL; ret = realloc_method_data(&method_data); pa = &method_data.val[method_data.len-1]; pa->padata_type = KRB5_PADATA_ENC_TIMESTAMP; pa->padata_value.length = 0; pa->padata_value.data = NULL; #ifdef PKINIT ret = realloc_method_data(&method_data); pa = &method_data.val[method_data.len-1]; pa->padata_type = KRB5_PADATA_PK_AS_REQ; pa->padata_value.length = 0; pa->padata_value.data = NULL; ret = realloc_method_data(&method_data); pa = &method_data.val[method_data.len-1]; pa->padata_type = KRB5_PADATA_PK_AS_REQ_WIN; pa->padata_value.length = 0; pa->padata_value.data = NULL; #endif /* * RFC4120 requires: * - If the client only knows about old enctypes, then send * both info replies (we send 'info' first in the list). * - If the client is 'modern', because it knows about 'new' * enctype types, then only send the 'info2' reply. */ /* XXX check ret */ if (only_older_enctype_p(req)) ret = get_pa_etype_info(context, config, &method_data, &client->entry, b->etype.val, b->etype.len); /* XXX check ret */ ret = get_pa_etype_info2(context, config, &method_data, &client->entry, b->etype.val, b->etype.len); ASN1_MALLOC_ENCODE(METHOD_DATA, buf, len, &method_data, &len, ret); free_METHOD_DATA(&method_data); e_data.data = buf; e_data.length = len; e_text ="Need to use PA-ENC-TIMESTAMP/PA-PK-AS-REQ", ret = KRB5KDC_ERR_PREAUTH_REQUIRED; kdc_log(context, config, 0, "No preauth found, returning PREAUTH-REQUIRED -- %s", client_name); goto out; } /* * Find the client key (for preauth ENC-TS verification and reply * encryption). Then the best encryption type for the KDC and * last the best session key that shared between the client and * KDC runtime enctypes. */ ret = _kdc_find_etype(context, client, b->etype.val, b->etype.len, &ckey, &cetype); if (ret) { kdc_log(context, config, 0, "Client (%s) has no support for etypes", client_name); goto out; } ret = _kdc_get_preferred_key(context, config, server, server_name, &setype, &skey); if(ret) goto out; /* * Select a session enctype from the list of the crypto systems * supported enctype, is supported by the client and is one of the * enctype of the enctype of the krbtgt. * * The later is used as a hint what enctype all KDC are supporting * to make sure a newer version of KDC wont generate a session * enctype that and older version of a KDC in the same realm can't * decrypt. * * But if the KDC admin is paranoid and doesn't want to have "no * the best" enctypes on the krbtgt, lets save the best pick from * the client list and hope that that will work for any other * KDCs. */ { const krb5_enctype *p; krb5_enctype clientbest = ETYPE_NULL; int i, j; p = krb5_kerberos_enctypes(context); sessionetype = ETYPE_NULL; for (i = 0; p[i] != ETYPE_NULL && sessionetype == ETYPE_NULL; i++) { if (krb5_enctype_valid(context, p[i]) != 0) continue; for (j = 0; j < b->etype.len && sessionetype == ETYPE_NULL; j++) { Key *dummy; /* check with client */ if (p[i] != b->etype.val[j]) continue; /* save best of union of { client, crypto system } */ if (clientbest == ETYPE_NULL) clientbest = p[i]; /* check with krbtgt */ ret = hdb_enctype2key(context, &server->entry, p[i], &dummy); if (ret) continue; sessionetype = p[i]; } } /* if krbtgt had no shared keys with client, pick clients best */ if (clientbest != ETYPE_NULL && sessionetype == ETYPE_NULL) { sessionetype = clientbest; } else if (sessionetype == ETYPE_NULL) { kdc_log(context, config, 0, "Client (%s) from %s has no common enctypes with KDC" "to use for the session key", client_name, from); goto out; } } log_as_req(context, config, cetype, setype, b); if(f.renew || f.validate || f.proxy || f.forwarded || f.enc_tkt_in_skey || (f.request_anonymous && !config->allow_anonymous)) { ret = KRB5KDC_ERR_BADOPTION; kdc_log(context, config, 0, "Bad KDC options -- %s", client_name); goto out; } rep.pvno = 5; rep.msg_type = krb_as_rep; copy_Realm(&client->entry.principal->realm, &rep.crealm); if (f.request_anonymous) _kdc_make_anonymous_principalname (&rep.cname); else _krb5_principal2principalname(&rep.cname, client->entry.principal); rep.ticket.tkt_vno = 5; copy_Realm(&server->entry.principal->realm, &rep.ticket.realm); _krb5_principal2principalname(&rep.ticket.sname, server->entry.principal); /* java 1.6 expects the name to be the same type, lets allow that * uncomplicated name-types. */ #define CNT(sp,t) (((sp)->sname->name_type) == KRB5_NT_##t) if (CNT(b, UNKNOWN) || CNT(b, PRINCIPAL) || CNT(b, SRV_INST) || CNT(b, SRV_HST) || CNT(b, SRV_XHST)) rep.ticket.sname.name_type = b->sname->name_type; #undef CNT et.flags.initial = 1; if(client->entry.flags.forwardable && server->entry.flags.forwardable) et.flags.forwardable = f.forwardable; else if (f.forwardable) { ret = KRB5KDC_ERR_POLICY; kdc_log(context, config, 0, "Ticket may not be forwardable -- %s", client_name); goto out; } if(client->entry.flags.proxiable && server->entry.flags.proxiable) et.flags.proxiable = f.proxiable; else if (f.proxiable) { ret = KRB5KDC_ERR_POLICY; kdc_log(context, config, 0, "Ticket may not be proxiable -- %s", client_name); goto out; } if(client->entry.flags.postdate && server->entry.flags.postdate) et.flags.may_postdate = f.allow_postdate; else if (f.allow_postdate){ ret = KRB5KDC_ERR_POLICY; kdc_log(context, config, 0, "Ticket may not be postdatable -- %s", client_name); goto out; } /* check for valid set of addresses */ if(!_kdc_check_addresses(context, config, b->addresses, from_addr)) { ret = KRB5KRB_AP_ERR_BADADDR; kdc_log(context, config, 0, "Bad address list requested -- %s", client_name); goto out; } ret = krb5_generate_random_keyblock(context, sessionetype, &et.key); if (ret) goto out; copy_PrincipalName(&rep.cname, &et.cname); copy_Realm(&rep.crealm, &et.crealm); { time_t start; time_t t; start = et.authtime = kdc_time; if(f.postdated && req->req_body.from){ ALLOC(et.starttime); start = *et.starttime = *req->req_body.from; et.flags.invalid = 1; et.flags.postdated = 1; /* XXX ??? */ } _kdc_fix_time(&b->till); t = *b->till; /* be careful not overflowing */ if(client->entry.max_life) t = start + min(t - start, *client->entry.max_life); if(server->entry.max_life) t = start + min(t - start, *server->entry.max_life); #if 0 t = min(t, start + realm->max_life); #endif et.endtime = t; if(f.renewable_ok && et.endtime < *b->till){ f.renewable = 1; if(b->rtime == NULL){ ALLOC(b->rtime); *b->rtime = 0; } if(*b->rtime < *b->till) *b->rtime = *b->till; } if(f.renewable && b->rtime){ t = *b->rtime; if(t == 0) t = MAX_TIME; if(client->entry.max_renew) t = start + min(t - start, *client->entry.max_renew); if(server->entry.max_renew) t = start + min(t - start, *server->entry.max_renew); #if 0 t = min(t, start + realm->max_renew); #endif ALLOC(et.renew_till); *et.renew_till = t; et.flags.renewable = 1; } } if (f.request_anonymous) et.flags.anonymous = 1; if(b->addresses){ ALLOC(et.caddr); copy_HostAddresses(b->addresses, et.caddr); } et.transited.tr_type = DOMAIN_X500_COMPRESS; krb5_data_zero(&et.transited.contents); copy_EncryptionKey(&et.key, &ek.key); /* The MIT ASN.1 library (obviously) doesn't tell lengths encoded * as 0 and as 0x80 (meaning indefinite length) apart, and is thus * incapable of correctly decoding SEQUENCE OF's of zero length. * * To fix this, always send at least one no-op last_req * * If there's a pw_end or valid_end we will use that, * otherwise just a dummy lr. */ ek.last_req.val = malloc(2 * sizeof(*ek.last_req.val)); if (ek.last_req.val == NULL) { ret = ENOMEM; goto out; } ek.last_req.len = 0; if (client->entry.pw_end && (config->kdc_warn_pwexpire == 0 || kdc_time + config->kdc_warn_pwexpire >= *client->entry.pw_end)) { ek.last_req.val[ek.last_req.len].lr_type = LR_PW_EXPTIME; ek.last_req.val[ek.last_req.len].lr_value = *client->entry.pw_end; ++ek.last_req.len; } if (client->entry.valid_end) { ek.last_req.val[ek.last_req.len].lr_type = LR_ACCT_EXPTIME; ek.last_req.val[ek.last_req.len].lr_value = *client->entry.valid_end; ++ek.last_req.len; } if (ek.last_req.len == 0) { ek.last_req.val[ek.last_req.len].lr_type = LR_NONE; ek.last_req.val[ek.last_req.len].lr_value = 0; ++ek.last_req.len; } ek.nonce = b->nonce; if (client->entry.valid_end || client->entry.pw_end) { ALLOC(ek.key_expiration); if (client->entry.valid_end) { if (client->entry.pw_end) *ek.key_expiration = min(*client->entry.valid_end, *client->entry.pw_end); else *ek.key_expiration = *client->entry.valid_end; } else *ek.key_expiration = *client->entry.pw_end; } else ek.key_expiration = NULL; ek.flags = et.flags; ek.authtime = et.authtime; if (et.starttime) { ALLOC(ek.starttime); *ek.starttime = *et.starttime; } ek.endtime = et.endtime; if (et.renew_till) { ALLOC(ek.renew_till); *ek.renew_till = *et.renew_till; } copy_Realm(&rep.ticket.realm, &ek.srealm); copy_PrincipalName(&rep.ticket.sname, &ek.sname); if(et.caddr){ ALLOC(ek.caddr); copy_HostAddresses(et.caddr, ek.caddr); } ALLOC(rep.padata); rep.padata->len = 0; rep.padata->val = NULL; reply_key = &ckey->key; #if PKINIT if (pkp) { ret = _kdc_pk_mk_pa_reply(context, config, pkp, client, req, req_buffer, &reply_key, rep.padata); if (ret) goto out; ret = _kdc_add_inital_verified_cas(context, config, pkp, &et); if (ret) goto out; } #endif set_salt_padata (rep.padata, ckey->salt); /* Add signing of alias referral */ if (f.canonicalize) { PA_ClientCanonicalized canon; krb5_data data; PA_DATA pa; krb5_crypto crypto; size_t len; memset(&canon, 0, sizeof(canon)); canon.names.requested_name = *b->cname; canon.names.real_name = client->entry.principal->name; ASN1_MALLOC_ENCODE(PA_ClientCanonicalizedNames, data.data, data.length, &canon.names, &len, ret); if (ret) goto out; if (data.length != len) krb5_abortx(context, "internal asn.1 error"); /* sign using "returned session key" */ ret = krb5_crypto_init(context, &et.key, 0, &crypto); if (ret) { free(data.data); goto out; } ret = krb5_create_checksum(context, crypto, KRB5_KU_CANONICALIZED_NAMES, 0, data.data, data.length, &canon.canon_checksum); free(data.data); krb5_crypto_destroy(context, crypto); if (ret) goto out; ASN1_MALLOC_ENCODE(PA_ClientCanonicalized, data.data, data.length, &canon, &len, ret); free_Checksum(&canon.canon_checksum); if (ret) goto out; if (data.length != len) krb5_abortx(context, "internal asn.1 error"); pa.padata_type = KRB5_PADATA_CLIENT_CANONICALIZED; pa.padata_value = data; ret = add_METHOD_DATA(rep.padata, &pa); free(data.data); if (ret) goto out; } if (rep.padata->len == 0) { free(rep.padata); rep.padata = NULL; } /* Add the PAC */ if (send_pac_p(context, req)) { krb5_pac p = NULL; krb5_data data; ret = _kdc_pac_generate(context, client, &p); if (ret) { kdc_log(context, config, 0, "PAC generation failed for -- %s", client_name); goto out; } if (p != NULL) { ret = _krb5_pac_sign(context, p, et.authtime, client->entry.principal, &skey->key, /* Server key */ &skey->key, /* FIXME: should be krbtgt key */ &data); krb5_pac_free(context, p); if (ret) { kdc_log(context, config, 0, "PAC signing failed for -- %s", client_name); goto out; } ret = _kdc_tkt_add_if_relevant_ad(context, &et, KRB5_AUTHDATA_WIN2K_PAC, &data); krb5_data_free(&data); if (ret) goto out; } } _kdc_log_timestamp(context, config, "AS-REQ", et.authtime, et.starttime, et.endtime, et.renew_till); /* do this as the last thing since this signs the EncTicketPart */ ret = _kdc_add_KRB5SignedPath(context, config, server, setype, NULL, NULL, &et); if (ret) goto out; ret = _kdc_encode_reply(context, config, &rep, &et, &ek, setype, server->entry.kvno, &skey->key, client->entry.kvno, reply_key, &e_text, reply); free_EncTicketPart(&et); free_EncKDCRepPart(&ek); if (ret) goto out; /* */ if (datagram_reply && reply->length > config->max_datagram_reply_length) { krb5_data_free(reply); ret = KRB5KRB_ERR_RESPONSE_TOO_BIG; e_text = "Reply packet too large"; } out: free_AS_REP(&rep); if(ret){ krb5_mk_error(context, ret, e_text, (e_data.data ? &e_data : NULL), client_princ, server_princ, NULL, NULL, reply); ret = 0; } #ifdef PKINIT if (pkp) _kdc_pk_free_client_param(context, pkp); #endif if (e_data.data) free(e_data.data); if (client_princ) krb5_free_principal(context, client_princ); free(client_name); if (server_princ) krb5_free_principal(context, server_princ); free(server_name); if(client) _kdc_free_ent(context, client); if(server) _kdc_free_ent(context, server); return ret; }
static bool kpasswd_process_request(struct kdc_server *kdc, TALLOC_CTX *mem_ctx, struct gensec_security *gensec_security, uint16_t version, DATA_BLOB *input, DATA_BLOB *reply) { struct auth_session_info *session_info; size_t pw_len; if (!NT_STATUS_IS_OK(gensec_session_info(gensec_security, &session_info))) { return kpasswdd_make_error_reply(kdc, mem_ctx, KRB5_KPASSWD_HARDERROR, "gensec_session_info failed!", reply); } switch (version) { case KRB5_KPASSWD_VERS_CHANGEPW: { DATA_BLOB password; if (!convert_string_talloc_convenience(mem_ctx, lp_iconv_convenience(kdc->task->lp_ctx), CH_UTF8, CH_UTF16, (const char *)input->data, input->length, (void **)&password.data, &pw_len, false)) { return false; } password.length = pw_len; return kpasswdd_change_password(kdc, mem_ctx, session_info, &password, reply); break; } case KRB5_KPASSWD_VERS_SETPW: { NTSTATUS status; enum samPwdChangeReason reject_reason = SAM_PWD_CHANGE_NO_ERROR; struct samr_DomInfo1 *dominfo = NULL; struct ldb_context *samdb; struct ldb_message *msg; krb5_context context = kdc->smb_krb5_context->krb5_context; ChangePasswdDataMS chpw; DATA_BLOB password; krb5_principal principal; char *set_password_on_princ; struct ldb_dn *set_password_on_dn; size_t len; int ret; msg = ldb_msg_new(mem_ctx); if (!msg) { return false; } ret = decode_ChangePasswdDataMS(input->data, input->length, &chpw, &len); if (ret) { return kpasswdd_make_error_reply(kdc, mem_ctx, KRB5_KPASSWD_MALFORMED, "failed to decode password change structure", reply); } if (!convert_string_talloc_convenience(mem_ctx, lp_iconv_convenience(kdc->task->lp_ctx), CH_UTF8, CH_UTF16, (const char *)chpw.newpasswd.data, chpw.newpasswd.length, (void **)&password.data, &pw_len, false)) { free_ChangePasswdDataMS(&chpw); return false; } password.length = pw_len; if ((chpw.targname && !chpw.targrealm) || (!chpw.targname && chpw.targrealm)) { return kpasswdd_make_error_reply(kdc, mem_ctx, KRB5_KPASSWD_MALFORMED, "Realm and principal must be both present, or neither present", reply); } if (chpw.targname && chpw.targrealm) { #ifdef SAMBA4_INTERNAL_HEIMDAL if (_krb5_principalname2krb5_principal(kdc->smb_krb5_context->krb5_context, &principal, *chpw.targname, *chpw.targrealm) != 0) { free_ChangePasswdDataMS(&chpw); return kpasswdd_make_error_reply(kdc, mem_ctx, KRB5_KPASSWD_MALFORMED, "failed to extract principal to set", reply); } #else /* SAMBA4_INTERNAL_HEIMDAL */ return kpasswdd_make_error_reply(kdc, mem_ctx, KRB5_KPASSWD_BAD_VERSION, "Operation Not Implemented", reply); #endif /* SAMBA4_INTERNAL_HEIMDAL */ } else { free_ChangePasswdDataMS(&chpw); return kpasswdd_change_password(kdc, mem_ctx, session_info, &password, reply); } free_ChangePasswdDataMS(&chpw); if (krb5_unparse_name(context, principal, &set_password_on_princ) != 0) { krb5_free_principal(context, principal); return kpasswdd_make_error_reply(kdc, mem_ctx, KRB5_KPASSWD_MALFORMED, "krb5_unparse_name failed!", reply); } krb5_free_principal(context, principal); samdb = samdb_connect(mem_ctx, kdc->task->event_ctx, kdc->task->lp_ctx, session_info); if (!samdb) { return kpasswdd_make_error_reply(kdc, mem_ctx, KRB5_KPASSWD_HARDERROR, "Unable to open database!", reply); } DEBUG(3, ("%s\\%s (%s) is changing password of %s\n", session_info->server_info->domain_name, session_info->server_info->account_name, dom_sid_string(mem_ctx, session_info->security_token->user_sid), set_password_on_princ)); ret = ldb_transaction_start(samdb); if (ret) { status = NT_STATUS_TRANSACTION_ABORTED; return kpasswd_make_pwchange_reply(kdc, mem_ctx, status, SAM_PWD_CHANGE_NO_ERROR, NULL, reply); } status = crack_user_principal_name(samdb, mem_ctx, set_password_on_princ, &set_password_on_dn, NULL); free(set_password_on_princ); if (!NT_STATUS_IS_OK(status)) { ldb_transaction_cancel(samdb); return kpasswd_make_pwchange_reply(kdc, mem_ctx, status, SAM_PWD_CHANGE_NO_ERROR, NULL, reply); } msg = ldb_msg_new(mem_ctx); if (msg == NULL) { ldb_transaction_cancel(samdb); status = NT_STATUS_NO_MEMORY; } else { msg->dn = ldb_dn_copy(msg, set_password_on_dn); if (!msg->dn) { status = NT_STATUS_NO_MEMORY; } } if (NT_STATUS_IS_OK(status)) { /* Admin password set */ status = samdb_set_password(samdb, mem_ctx, set_password_on_dn, NULL, msg, &password, NULL, NULL, false, /* this is not a user password change */ &reject_reason, &dominfo); } if (NT_STATUS_IS_OK(status)) { /* modify the samdb record */ ret = samdb_replace(samdb, mem_ctx, msg); if (ret != 0) { DEBUG(2,("Failed to modify record to set password on %s: %s\n", ldb_dn_get_linearized(msg->dn), ldb_errstring(samdb))); status = NT_STATUS_ACCESS_DENIED; } } if (NT_STATUS_IS_OK(status)) { ret = ldb_transaction_commit(samdb); if (ret != 0) { DEBUG(1,("Failed to commit transaction to set password on %s: %s\n", ldb_dn_get_linearized(msg->dn), ldb_errstring(samdb))); status = NT_STATUS_TRANSACTION_ABORTED; } } else { ldb_transaction_cancel(samdb); } return kpasswd_make_pwchange_reply(kdc, mem_ctx, status, reject_reason, dominfo, reply); } default: return kpasswdd_make_error_reply(kdc, mem_ctx, KRB5_KPASSWD_BAD_VERSION, talloc_asprintf(mem_ctx, "Protocol version %u not supported", version), reply); } return true; }
krb5_error_code _kdc_fast_unwrap_request(kdc_request_t r) { krb5_principal armor_server = NULL; hdb_entry_ex *armor_user = NULL; PA_FX_FAST_REQUEST fxreq; krb5_auth_context ac = NULL; krb5_ticket *ticket = NULL; krb5_flags ap_req_options; Key *armor_key = NULL; krb5_keyblock armorkey; krb5_error_code ret; krb5_ap_req ap_req; unsigned char *buf = NULL; KrbFastReq fastreq; size_t len, size; krb5_data data; const PA_DATA *pa; int i = 0; /* * First look for FX_COOKIE and and process it */ pa = _kdc_find_padata(&r->req, &i, KRB5_PADATA_FX_COOKIE); if (pa) { ret = fast_parse_cookie(r, pa); if (ret) goto out; } i = 0; pa = _kdc_find_padata(&r->req, &i, KRB5_PADATA_FX_FAST); if (pa == NULL) return 0; ret = decode_PA_FX_FAST_REQUEST(pa->padata_value.data, pa->padata_value.length, &fxreq, &len); if (ret) goto out; if (len != pa->padata_value.length) { ret = KRB5KDC_ERR_PREAUTH_FAILED; goto out; } if (fxreq.element != choice_PA_FX_FAST_REQUEST_armored_data) { kdc_log(r->context, r->config, 0, "AS-REQ FAST contain unknown type: %d", (int)fxreq.element); ret = KRB5KDC_ERR_PREAUTH_FAILED; goto out; } /* pull out armor key */ if (fxreq.u.armored_data.armor == NULL) { kdc_log(r->context, r->config, 0, "AS-REQ armor missing"); ret = KRB5KDC_ERR_PREAUTH_FAILED; goto out; } if (fxreq.u.armored_data.armor->armor_type != 1) { kdc_log(r->context, r->config, 0, "AS-REQ armor type not ap-req"); ret = KRB5KDC_ERR_PREAUTH_FAILED; goto out; } ret = krb5_decode_ap_req(r->context, &fxreq.u.armored_data.armor->armor_value, &ap_req); if(ret) { kdc_log(r->context, r->config, 0, "AP-REQ decode failed"); goto out; } /* Save that principal that was in the request */ ret = _krb5_principalname2krb5_principal(r->context, &armor_server, ap_req.ticket.sname, ap_req.ticket.realm); if (ret) { free_AP_REQ(&ap_req); goto out; } ret = _kdc_db_fetch(r->context, r->config, armor_server, HDB_F_GET_SERVER, NULL, NULL, &armor_user); if(ret == HDB_ERR_NOT_FOUND_HERE) { kdc_log(r->context, r->config, 5, "armor key does not have secrets at this KDC, " "need to proxy"); goto out; } else if (ret) { free_AP_REQ(&ap_req); ret = KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN; goto out; } ret = hdb_enctype2key(r->context, &armor_user->entry, NULL, ap_req.ticket.enc_part.etype, &armor_key); if (ret) { free_AP_REQ(&ap_req); goto out; } ret = krb5_verify_ap_req2(r->context, &ac, &ap_req, armor_server, &armor_key->key, 0, &ap_req_options, &ticket, KRB5_KU_AP_REQ_AUTH); free_AP_REQ(&ap_req); if (ret) goto out; if (ac->remote_subkey == NULL) { krb5_auth_con_free(r->context, ac); kdc_log(r->context, r->config, 0, "FAST AP-REQ remote subkey missing"); ret = KRB5KDC_ERR_PREAUTH_FAILED; goto out; } ret = _krb5_fast_armor_key(r->context, ac->remote_subkey, &ticket->ticket.key, &armorkey, &r->armor_crypto); krb5_auth_con_free(r->context, ac); krb5_free_ticket(r->context, ticket); if (ret) goto out; krb5_free_keyblock_contents(r->context, &armorkey); /* verify req-checksum of the outer body */ ASN1_MALLOC_ENCODE(KDC_REQ_BODY, buf, len, &r->req.req_body, &size, ret); if (ret) goto out; if (size != len) { ret = KRB5KDC_ERR_PREAUTH_FAILED; goto out; } ret = krb5_verify_checksum(r->context, r->armor_crypto, KRB5_KU_FAST_REQ_CHKSUM, buf, len, &fxreq.u.armored_data.req_checksum); if (ret) { kdc_log(r->context, r->config, 0, "FAST request have a bad checksum"); goto out; } ret = krb5_decrypt_EncryptedData(r->context, r->armor_crypto, KRB5_KU_FAST_ENC, &fxreq.u.armored_data.enc_fast_req, &data); if (ret) { kdc_log(r->context, r->config, 0, "Failed to decrypt FAST request"); goto out; } ret = decode_KrbFastReq(data.data, data.length, &fastreq, &size); if (ret) { krb5_data_free(&data); goto out; } if (data.length != size) { krb5_data_free(&data); ret = KRB5KDC_ERR_PREAUTH_FAILED; goto out; } krb5_data_free(&data); free_KDC_REQ_BODY(&r->req.req_body); ret = copy_KDC_REQ_BODY(&fastreq.req_body, &r->req.req_body); if (ret) goto out; /* check for unsupported mandatory options */ if (FastOptions2int(fastreq.fast_options) & 0xfffc) { kdc_log(r->context, r->config, 0, "FAST unsupported mandatory option set"); ret = KRB5KDC_ERR_PREAUTH_FAILED; goto out; } /* KDC MUST ignore outer pa data preauth-14 - 6.5.5 */ if (r->req.padata) free_METHOD_DATA(r->req.padata); else ALLOC(r->req.padata); ret = copy_METHOD_DATA(&fastreq.padata, r->req.padata); if (ret) goto out; free_KrbFastReq(&fastreq); free_PA_FX_FAST_REQUEST(&fxreq); out: if (armor_server) krb5_free_principal(r->context, armor_server); if(armor_user) _kdc_free_ent(r->context, armor_user); return ret; }
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL krb5_rd_cred(krb5_context context, krb5_auth_context auth_context, krb5_data *in_data, krb5_creds ***ret_creds, krb5_replay_data *outdata) { krb5_error_code ret; size_t len; KRB_CRED cred; EncKrbCredPart enc_krb_cred_part; krb5_data enc_krb_cred_part_data; krb5_crypto crypto; int i; memset(&enc_krb_cred_part, 0, sizeof(enc_krb_cred_part)); if ((auth_context->flags & (KRB5_AUTH_CONTEXT_RET_TIME | KRB5_AUTH_CONTEXT_RET_SEQUENCE)) && outdata == NULL) return KRB5_RC_REQUIRED; /* XXX better error, MIT returns this */ *ret_creds = NULL; ret = decode_KRB_CRED(in_data->data, in_data->length, &cred, &len); if(ret) { krb5_clear_error_message(context); return ret; } if (cred.pvno != 5) { ret = KRB5KRB_AP_ERR_BADVERSION; krb5_clear_error_message (context); goto out; } if (cred.msg_type != krb_cred) { ret = KRB5KRB_AP_ERR_MSG_TYPE; krb5_clear_error_message (context); goto out; } if (cred.enc_part.etype == ETYPE_NULL) { /* DK: MIT GSS-API Compatibility */ enc_krb_cred_part_data.length = cred.enc_part.cipher.length; enc_krb_cred_part_data.data = cred.enc_part.cipher.data; } else { /* Try both subkey and session key. * * RFC4120 claims we should use the session key, but Heimdal * before 0.8 used the remote subkey if it was send in the * auth_context. */ if (auth_context->remote_subkey) { ret = krb5_crypto_init(context, auth_context->remote_subkey, 0, &crypto); if (ret) goto out; ret = krb5_decrypt_EncryptedData(context, crypto, KRB5_KU_KRB_CRED, &cred.enc_part, &enc_krb_cred_part_data); krb5_crypto_destroy(context, crypto); } /* * If there was not subkey, or we failed using subkey, * retry using the session key */ if (auth_context->remote_subkey == NULL || ret == KRB5KRB_AP_ERR_BAD_INTEGRITY) { ret = krb5_crypto_init(context, auth_context->keyblock, 0, &crypto); if (ret) goto out; ret = krb5_decrypt_EncryptedData(context, crypto, KRB5_KU_KRB_CRED, &cred.enc_part, &enc_krb_cred_part_data); krb5_crypto_destroy(context, crypto); } if (ret) goto out; } ret = decode_EncKrbCredPart(enc_krb_cred_part_data.data, enc_krb_cred_part_data.length, &enc_krb_cred_part, &len); if (enc_krb_cred_part_data.data != cred.enc_part.cipher.data) krb5_data_free(&enc_krb_cred_part_data); if (ret) { krb5_set_error_message(context, ret, N_("Failed to decode " "encrypte credential part", "")); goto out; } /* check sender address */ if (enc_krb_cred_part.s_address && auth_context->remote_address && auth_context->remote_port) { krb5_address *a; ret = krb5_make_addrport (context, &a, auth_context->remote_address, auth_context->remote_port); if (ret) goto out; ret = compare_addrs(context, a, enc_krb_cred_part.s_address, N_("sender address is wrong " "in received creds", "")); krb5_free_address(context, a); free(a); if(ret) goto out; } /* check receiver address */ if (enc_krb_cred_part.r_address && auth_context->local_address) { if(auth_context->local_port && enc_krb_cred_part.r_address->addr_type == KRB5_ADDRESS_ADDRPORT) { krb5_address *a; ret = krb5_make_addrport (context, &a, auth_context->local_address, auth_context->local_port); if (ret) goto out; ret = compare_addrs(context, a, enc_krb_cred_part.r_address, N_("receiver address is wrong " "in received creds", "")); krb5_free_address(context, a); free(a); if(ret) goto out; } else { ret = compare_addrs(context, auth_context->local_address, enc_krb_cred_part.r_address, N_("receiver address is wrong " "in received creds", "")); if(ret) goto out; } } /* check timestamp */ if (auth_context->flags & KRB5_AUTH_CONTEXT_DO_TIME) { krb5_timestamp sec; krb5_timeofday (context, &sec); if (enc_krb_cred_part.timestamp == NULL || enc_krb_cred_part.usec == NULL || abs(*enc_krb_cred_part.timestamp - sec) > context->max_skew) { krb5_clear_error_message (context); ret = KRB5KRB_AP_ERR_SKEW; goto out; } } if ((auth_context->flags & (KRB5_AUTH_CONTEXT_RET_TIME | KRB5_AUTH_CONTEXT_RET_SEQUENCE))) { /* if these fields are not present in the cred-part, silently return zero */ memset(outdata, 0, sizeof(*outdata)); if(enc_krb_cred_part.timestamp) outdata->timestamp = *enc_krb_cred_part.timestamp; if(enc_krb_cred_part.usec) outdata->usec = *enc_krb_cred_part.usec; if(enc_krb_cred_part.nonce) outdata->seq = *enc_krb_cred_part.nonce; } /* Convert to NULL terminated list of creds */ *ret_creds = calloc(enc_krb_cred_part.ticket_info.len + 1, sizeof(**ret_creds)); if (*ret_creds == NULL) { ret = ENOMEM; krb5_set_error_message(context, ret, N_("malloc: out of memory", "")); goto out; } for (i = 0; i < enc_krb_cred_part.ticket_info.len; ++i) { KrbCredInfo *kci = &enc_krb_cred_part.ticket_info.val[i]; krb5_creds *creds; creds = calloc(1, sizeof(*creds)); if(creds == NULL) { ret = ENOMEM; krb5_set_error_message(context, ret, N_("malloc: out of memory", "")); goto out; } ASN1_MALLOC_ENCODE(Ticket, creds->ticket.data, creds->ticket.length, &cred.tickets.val[i], &len, ret); if (ret) { free(creds); goto out; } if(creds->ticket.length != len) krb5_abortx(context, "internal error in ASN.1 encoder"); copy_EncryptionKey (&kci->key, &creds->session); if (kci->prealm && kci->pname) _krb5_principalname2krb5_principal (context, &creds->client, *kci->pname, *kci->prealm); if (kci->flags) creds->flags.b = *kci->flags; if (kci->authtime) creds->times.authtime = *kci->authtime; if (kci->starttime) creds->times.starttime = *kci->starttime; if (kci->endtime) creds->times.endtime = *kci->endtime; if (kci->renew_till) creds->times.renew_till = *kci->renew_till; if (kci->srealm && kci->sname) _krb5_principalname2krb5_principal (context, &creds->server, *kci->sname, *kci->srealm); if (kci->caddr) krb5_copy_addresses (context, kci->caddr, &creds->addresses); (*ret_creds)[i] = creds; } (*ret_creds)[i] = NULL; free_KRB_CRED (&cred); free_EncKrbCredPart(&enc_krb_cred_part); return 0; out: free_EncKrbCredPart(&enc_krb_cred_part); free_KRB_CRED (&cred); if(*ret_creds) { for(i = 0; (*ret_creds)[i]; i++) krb5_free_creds(context, (*ret_creds)[i]); free(*ret_creds); *ret_creds = NULL; } return ret; }
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL krb5_verify_ap_req2(krb5_context context, krb5_auth_context *auth_context, krb5_ap_req *ap_req, krb5_const_principal server, krb5_keyblock *keyblock, krb5_flags flags, krb5_flags *ap_req_options, krb5_ticket **ticket, krb5_key_usage usage) { krb5_ticket *t; krb5_auth_context ac; krb5_error_code ret; EtypeList etypes; memset(&etypes, 0, sizeof(etypes)); if(ticket) *ticket = NULL; if (auth_context && *auth_context) { ac = *auth_context; } else { ret = krb5_auth_con_init(context, &ac); if (ret) return ret; } t = calloc(1, sizeof(*t)); if (t == NULL) { ret = ENOMEM; krb5_clear_error_message(context); goto out; } if (ap_req->ap_options.use_session_key && ac->keyblock){ ret = krb5_decrypt_ticket(context, &ap_req->ticket, ac->keyblock, &t->ticket, flags); krb5_free_keyblock(context, ac->keyblock); ac->keyblock = NULL; }else ret = krb5_decrypt_ticket(context, &ap_req->ticket, keyblock, &t->ticket, flags); if(ret) goto out; ret = _krb5_principalname2krb5_principal(context, &t->server, ap_req->ticket.sname, ap_req->ticket.realm); if (ret) goto out; ret = _krb5_principalname2krb5_principal(context, &t->client, t->ticket.cname, t->ticket.crealm); if (ret) goto out; ret = decrypt_authenticator(context, &t->ticket.key, &ap_req->authenticator, ac->authenticator, usage); if (ret) goto out; { krb5_principal p1, p2; krb5_boolean res; _krb5_principalname2krb5_principal(context, &p1, ac->authenticator->cname, ac->authenticator->crealm); _krb5_principalname2krb5_principal(context, &p2, t->ticket.cname, t->ticket.crealm); res = krb5_principal_compare(context, p1, p2); krb5_free_principal(context, p1); krb5_free_principal(context, p2); if (!res) { ret = KRB5KRB_AP_ERR_BADMATCH; krb5_clear_error_message(context); goto out; } } /* check addresses */ if (t->ticket.caddr && ac->remote_address && !krb5_address_search(context, ac->remote_address, t->ticket.caddr)) { ret = KRB5KRB_AP_ERR_BADADDR; krb5_clear_error_message(context); goto out; } /* check timestamp in authenticator */ { krb5_timestamp now; krb5_timeofday(context, &now); if (krb5_time_abs(ac->authenticator->ctime, now) > context->max_skew) { ret = KRB5KRB_AP_ERR_SKEW; krb5_clear_error_message(context); goto out; } } if (ac->authenticator->seq_number) krb5_auth_con_setremoteseqnumber(context, ac, *ac->authenticator->seq_number); /* XXX - Xor sequence numbers */ if (ac->authenticator->subkey) { ret = krb5_auth_con_setremotesubkey(context, ac, ac->authenticator->subkey); if (ret) goto out; } ret = find_etypelist(context, ac, &etypes); if (ret) goto out; ac->keytype = (krb5_keytype)ETYPE_NULL; if (etypes.val) { size_t i; for (i = 0; i < etypes.len; i++) { if (krb5_enctype_valid(context, etypes.val[i]) == 0) { ac->keytype = etypes.val[i]; break; } } } /* save key */ ret = krb5_copy_keyblock(context, &t->ticket.key, &ac->keyblock); if (ret) goto out; if (ap_req_options) { *ap_req_options = 0; if (ac->keytype != ETYPE_NULL) *ap_req_options |= AP_OPTS_USE_SUBKEY; if (ap_req->ap_options.use_session_key) *ap_req_options |= AP_OPTS_USE_SESSION_KEY; if (ap_req->ap_options.mutual_required) *ap_req_options |= AP_OPTS_MUTUAL_REQUIRED; } if(ticket) *ticket = t; else krb5_free_ticket(context, t); if (auth_context) { if (*auth_context == NULL) *auth_context = ac; } else krb5_auth_con_free(context, ac); free_EtypeList(&etypes); return 0; out: free_EtypeList(&etypes); if (t) krb5_free_ticket(context, t); if (auth_context == NULL || *auth_context == NULL) krb5_auth_con_free(context, ac); return ret; }
krb5_error_code _kdc_as_rep(krb5_context context, krb5_kdc_configuration *config, KDC_REQ *req, const krb5_data *req_buffer, krb5_data *reply, const char *from, struct sockaddr *from_addr, int datagram_reply) { KDC_REQ_BODY *b = &req->req_body; AS_REP rep; KDCOptions f = b->kdc_options; hdb_entry_ex *client = NULL, *server = NULL; HDB *clientdb; krb5_enctype setype, sessionetype; krb5_data e_data; EncTicketPart et; EncKDCRepPart ek; krb5_principal client_princ = NULL, server_princ = NULL; char *client_name = NULL, *server_name = NULL; krb5_error_code ret = 0; const char *e_text = NULL; krb5_crypto crypto; Key *ckey, *skey; EncryptionKey *reply_key = NULL, session_key; int flags = HDB_F_FOR_AS_REQ; #ifdef PKINIT pk_client_params *pkp = NULL; #endif memset(&rep, 0, sizeof(rep)); memset(&session_key, 0, sizeof(session_key)); krb5_data_zero(&e_data); ALLOC(rep.padata); rep.padata->len = 0; rep.padata->val = NULL; if (f.canonicalize) flags |= HDB_F_CANON; if(b->sname == NULL){ ret = KRB5KRB_ERR_GENERIC; e_text = "No server in request"; } else{ ret = _krb5_principalname2krb5_principal (context, &server_princ, *(b->sname), b->realm); if (ret == 0) ret = krb5_unparse_name(context, server_princ, &server_name); } if (ret) { kdc_log(context, config, 0, "AS-REQ malformed server name from %s", from); goto out; } if(b->cname == NULL){ ret = KRB5KRB_ERR_GENERIC; e_text = "No client in request"; } else { ret = _krb5_principalname2krb5_principal (context, &client_princ, *(b->cname), b->realm); if (ret) goto out; ret = krb5_unparse_name(context, client_princ, &client_name); } if (ret) { kdc_log(context, config, 0, "AS-REQ malformed client name from %s", from); goto out; } kdc_log(context, config, 0, "AS-REQ %s from %s for %s", client_name, from, server_name); /* * */ if (_kdc_is_anonymous(context, client_princ)) { if (!b->kdc_options.request_anonymous) { kdc_log(context, config, 0, "Anonymous ticket w/o anonymous flag"); ret = KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN; goto out; } } else if (b->kdc_options.request_anonymous) { kdc_log(context, config, 0, "Request for a anonymous ticket with non " "anonymous client name: %s", client_name); ret = KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN; goto out; } /* * */ ret = _kdc_db_fetch(context, config, client_princ, HDB_F_GET_CLIENT | flags, NULL, &clientdb, &client); if(ret == HDB_ERR_NOT_FOUND_HERE) { kdc_log(context, config, 5, "client %s does not have secrets at this KDC, need to proxy", client_name); goto out; } else if(ret){ const char *msg = krb5_get_error_message(context, ret); kdc_log(context, config, 0, "UNKNOWN -- %s: %s", client_name, msg); krb5_free_error_message(context, msg); ret = KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN; goto out; } ret = _kdc_db_fetch(context, config, server_princ, HDB_F_GET_SERVER|HDB_F_GET_KRBTGT | flags, NULL, NULL, &server); if(ret == HDB_ERR_NOT_FOUND_HERE) { kdc_log(context, config, 5, "target %s does not have secrets at this KDC, need to proxy", server_name); goto out; } else if(ret){ const char *msg = krb5_get_error_message(context, ret); kdc_log(context, config, 0, "UNKNOWN -- %s: %s", server_name, msg); krb5_free_error_message(context, msg); ret = KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN; goto out; } memset(&et, 0, sizeof(et)); memset(&ek, 0, sizeof(ek)); /* * Select a session enctype from the list of the crypto system * supported enctypes that is supported by the client and is one of * the enctype of the enctype of the service (likely krbtgt). * * The latter is used as a hint of what enctypes all KDC support, * to make sure a newer version of KDC won't generate a session * enctype that an older version of a KDC in the same realm can't * decrypt. */ ret = _kdc_find_etype(context, config->as_use_strongest_session_key, FALSE, client, b->etype.val, b->etype.len, &sessionetype, NULL); if (ret) { kdc_log(context, config, 0, "Client (%s) from %s has no common enctypes with KDC " "to use for the session key", client_name, from); goto out; } /* * But if the KDC admin is paranoid and doesn't want to have "not * the best" enctypes on the krbtgt, lets save the best pick from * the client list and hope that that will work for any other * KDCs. */ /* * Pre-auth processing */ if(req->padata){ int i; const PA_DATA *pa; int found_pa = 0; log_patypes(context, config, req->padata); #ifdef PKINIT kdc_log(context, config, 5, "Looking for PKINIT pa-data -- %s", client_name); e_text = "No PKINIT PA found"; i = 0; pa = _kdc_find_padata(req, &i, KRB5_PADATA_PK_AS_REQ); if (pa == NULL) { i = 0; pa = _kdc_find_padata(req, &i, KRB5_PADATA_PK_AS_REQ_WIN); } if (pa) { char *client_cert = NULL; ret = _kdc_pk_rd_padata(context, config, req, pa, client, &pkp); if (ret) { ret = KRB5KRB_AP_ERR_BAD_INTEGRITY; kdc_log(context, config, 5, "Failed to decode PKINIT PA-DATA -- %s", client_name); goto ts_enc; } if (ret == 0 && pkp == NULL) goto ts_enc; ret = _kdc_pk_check_client(context, config, clientdb, client, pkp, &client_cert); if (ret) { e_text = "PKINIT certificate not allowed to " "impersonate principal"; _kdc_pk_free_client_param(context, pkp); kdc_log(context, config, 0, "%s", e_text); pkp = NULL; goto out; } found_pa = 1; et.flags.pre_authent = 1; kdc_log(context, config, 0, "PKINIT pre-authentication succeeded -- %s using %s", client_name, client_cert); free(client_cert); if (pkp) goto preauth_done; } ts_enc: #endif if (client->entry.flags.locked_out) { ret = KRB5KDC_ERR_CLIENT_REVOKED; kdc_log(context, config, 0, "Client (%s) is locked out", client_name); goto out; } kdc_log(context, config, 5, "Looking for ENC-TS pa-data -- %s", client_name); i = 0; e_text = "No ENC-TS found"; while((pa = _kdc_find_padata(req, &i, KRB5_PADATA_ENC_TIMESTAMP))){ krb5_data ts_data; PA_ENC_TS_ENC p; size_t len; EncryptedData enc_data; Key *pa_key; char *str; found_pa = 1; if (b->kdc_options.request_anonymous) { ret = KRB5KRB_AP_ERR_BAD_INTEGRITY; kdc_log(context, config, 0, "ENC-TS doesn't support anon"); goto out; } ret = decode_EncryptedData(pa->padata_value.data, pa->padata_value.length, &enc_data, &len); if (ret) { ret = KRB5KRB_AP_ERR_BAD_INTEGRITY; kdc_log(context, config, 5, "Failed to decode PA-DATA -- %s", client_name); goto out; } ret = hdb_enctype2key(context, &client->entry, enc_data.etype, &pa_key); if(ret){ char *estr; e_text = "No key matches pa-data"; ret = KRB5KDC_ERR_ETYPE_NOSUPP; if(krb5_enctype_to_string(context, enc_data.etype, &estr)) estr = NULL; if(estr == NULL) kdc_log(context, config, 5, "No client key matching pa-data (%d) -- %s", enc_data.etype, client_name); else kdc_log(context, config, 5, "No client key matching pa-data (%s) -- %s", estr, client_name); free(estr); free_EncryptedData(&enc_data); continue; } try_next_key: ret = krb5_crypto_init(context, &pa_key->key, 0, &crypto); if (ret) { const char *msg = krb5_get_error_message(context, ret); kdc_log(context, config, 0, "krb5_crypto_init failed: %s", msg); krb5_free_error_message(context, msg); free_EncryptedData(&enc_data); continue; } ret = krb5_decrypt_EncryptedData (context, crypto, KRB5_KU_PA_ENC_TIMESTAMP, &enc_data, &ts_data); krb5_crypto_destroy(context, crypto); /* * Since the user might have several keys with the same * enctype but with diffrent salting, we need to try all * the keys with the same enctype. */ if(ret){ krb5_error_code ret2; const char *msg = krb5_get_error_message(context, ret); ret2 = krb5_enctype_to_string(context, pa_key->key.keytype, &str); if (ret2) str = NULL; kdc_log(context, config, 5, "Failed to decrypt PA-DATA -- %s " "(enctype %s) error %s", client_name, str ? str : "unknown enctype", msg); krb5_free_error_message(context, msg); free(str); if(hdb_next_enctype2key(context, &client->entry, enc_data.etype, &pa_key) == 0) goto try_next_key; e_text = "Failed to decrypt PA-DATA"; free_EncryptedData(&enc_data); if (clientdb->hdb_auth_status) (clientdb->hdb_auth_status)(context, clientdb, client, HDB_AUTH_WRONG_PASSWORD); ret = KRB5KDC_ERR_PREAUTH_FAILED; continue; } free_EncryptedData(&enc_data); ret = decode_PA_ENC_TS_ENC(ts_data.data, ts_data.length, &p, &len); krb5_data_free(&ts_data); if(ret){ e_text = "Failed to decode PA-ENC-TS-ENC"; ret = KRB5KDC_ERR_PREAUTH_FAILED; kdc_log(context, config, 5, "Failed to decode PA-ENC-TS_ENC -- %s", client_name); continue; } free_PA_ENC_TS_ENC(&p); if (abs(kdc_time - p.patimestamp) > context->max_skew) { char client_time[100]; krb5_format_time(context, p.patimestamp, client_time, sizeof(client_time), TRUE); ret = KRB5KRB_AP_ERR_SKEW; kdc_log(context, config, 0, "Too large time skew, " "client time %s is out by %u > %u seconds -- %s", client_time, (unsigned)abs(kdc_time - p.patimestamp), context->max_skew, client_name); /* * The following is needed to make windows clients to * retry using the timestamp in the error message, if * there is a e_text, they become unhappy. */ e_text = NULL; goto out; } et.flags.pre_authent = 1; set_salt_padata(rep.padata, pa_key->salt); reply_key = &pa_key->key; ret = krb5_enctype_to_string(context, pa_key->key.keytype, &str); if (ret) str = NULL; kdc_log(context, config, 2, "ENC-TS Pre-authentication succeeded -- %s using %s", client_name, str ? str : "unknown enctype"); free(str); break; } #ifdef PKINIT preauth_done: #endif if(found_pa == 0 && config->require_preauth) goto use_pa; /* We come here if we found a pa-enc-timestamp, but if there was some problem with it, other than too large skew */ if(found_pa && et.flags.pre_authent == 0){ kdc_log(context, config, 0, "%s -- %s", e_text, client_name); e_text = NULL; goto out; } }else if (config->require_preauth || b->kdc_options.request_anonymous /* hack to force anon */ || client->entry.flags.require_preauth || server->entry.flags.require_preauth) { METHOD_DATA method_data; PA_DATA *pa; unsigned char *buf; size_t len; use_pa: method_data.len = 0; method_data.val = NULL; ret = realloc_method_data(&method_data); if (ret) { free_METHOD_DATA(&method_data); goto out; } pa = &method_data.val[method_data.len-1]; pa->padata_type = KRB5_PADATA_ENC_TIMESTAMP; pa->padata_value.length = 0; pa->padata_value.data = NULL; #ifdef PKINIT ret = realloc_method_data(&method_data); if (ret) { free_METHOD_DATA(&method_data); goto out; } pa = &method_data.val[method_data.len-1]; pa->padata_type = KRB5_PADATA_PK_AS_REQ; pa->padata_value.length = 0; pa->padata_value.data = NULL; ret = realloc_method_data(&method_data); if (ret) { free_METHOD_DATA(&method_data); goto out; } pa = &method_data.val[method_data.len-1]; pa->padata_type = KRB5_PADATA_PK_AS_REQ_WIN; pa->padata_value.length = 0; pa->padata_value.data = NULL; #endif /* * If there is a client key, send ETYPE_INFO{,2} */ ret = _kdc_find_etype(context, config->preauth_use_strongest_session_key, TRUE, client, b->etype.val, b->etype.len, NULL, &ckey); if (ret == 0) { /* * RFC4120 requires: * - If the client only knows about old enctypes, then send * both info replies (we send 'info' first in the list). * - If the client is 'modern', because it knows about 'new' * enctype types, then only send the 'info2' reply. * * Before we send the full list of etype-info data, we pick * the client key we would have used anyway below, just pick * that instead. */ if (older_enctype(ckey->key.keytype)) { ret = get_pa_etype_info(context, config, &method_data, ckey); if (ret) { free_METHOD_DATA(&method_data); goto out; } } ret = get_pa_etype_info2(context, config, &method_data, ckey); if (ret) { free_METHOD_DATA(&method_data); goto out; } } ASN1_MALLOC_ENCODE(METHOD_DATA, buf, len, &method_data, &len, ret); free_METHOD_DATA(&method_data); e_data.data = buf; e_data.length = len; e_text ="Need to use PA-ENC-TIMESTAMP/PA-PK-AS-REQ", ret = KRB5KDC_ERR_PREAUTH_REQUIRED; kdc_log(context, config, 0, "No preauth found, returning PREAUTH-REQUIRED -- %s", client_name); goto out; } /* * Verify flags after the user been required to prove its identity * with in a preauth mech. */ ret = _kdc_check_access(context, config, client, client_name, server, server_name, req, &e_data); if(ret) goto out; if (clientdb->hdb_auth_status) (clientdb->hdb_auth_status)(context, clientdb, client, HDB_AUTH_SUCCESS); /* * Selelct the best encryption type for the KDC with out regard to * the client since the client never needs to read that data. */ ret = _kdc_get_preferred_key(context, config, server, server_name, &setype, &skey); if(ret) goto out; if(f.renew || f.validate || f.proxy || f.forwarded || f.enc_tkt_in_skey || (f.request_anonymous && !config->allow_anonymous)) { ret = KRB5KDC_ERR_BADOPTION; e_text = "Bad KDC options"; kdc_log(context, config, 0, "Bad KDC options -- %s", client_name); goto out; } rep.pvno = 5; rep.msg_type = krb_as_rep; ret = copy_Realm(&client->entry.principal->realm, &rep.crealm); if (ret) goto out; ret = _krb5_principal2principalname(&rep.cname, client->entry.principal); if (ret) goto out; rep.ticket.tkt_vno = 5; copy_Realm(&server->entry.principal->realm, &rep.ticket.realm); _krb5_principal2principalname(&rep.ticket.sname, server->entry.principal); /* java 1.6 expects the name to be the same type, lets allow that * uncomplicated name-types. */ #define CNT(sp,t) (((sp)->sname->name_type) == KRB5_NT_##t) if (CNT(b, UNKNOWN) || CNT(b, PRINCIPAL) || CNT(b, SRV_INST) || CNT(b, SRV_HST) || CNT(b, SRV_XHST)) rep.ticket.sname.name_type = b->sname->name_type; #undef CNT et.flags.initial = 1; if(client->entry.flags.forwardable && server->entry.flags.forwardable) et.flags.forwardable = f.forwardable; else if (f.forwardable) { e_text = "Ticket may not be forwardable"; ret = KRB5KDC_ERR_POLICY; kdc_log(context, config, 0, "Ticket may not be forwardable -- %s", client_name); goto out; } if(client->entry.flags.proxiable && server->entry.flags.proxiable) et.flags.proxiable = f.proxiable; else if (f.proxiable) { e_text = "Ticket may not be proxiable"; ret = KRB5KDC_ERR_POLICY; kdc_log(context, config, 0, "Ticket may not be proxiable -- %s", client_name); goto out; } if(client->entry.flags.postdate && server->entry.flags.postdate) et.flags.may_postdate = f.allow_postdate; else if (f.allow_postdate){ e_text = "Ticket may not be postdate"; ret = KRB5KDC_ERR_POLICY; kdc_log(context, config, 0, "Ticket may not be postdatable -- %s", client_name); goto out; } /* check for valid set of addresses */ if(!_kdc_check_addresses(context, config, b->addresses, from_addr)) { e_text = "Bad address list in requested"; ret = KRB5KRB_AP_ERR_BADADDR; kdc_log(context, config, 0, "Bad address list requested -- %s", client_name); goto out; } ret = copy_PrincipalName(&rep.cname, &et.cname); if (ret) goto out; ret = copy_Realm(&rep.crealm, &et.crealm); if (ret) goto out; { time_t start; time_t t; start = et.authtime = kdc_time; if(f.postdated && req->req_body.from){ ALLOC(et.starttime); start = *et.starttime = *req->req_body.from; et.flags.invalid = 1; et.flags.postdated = 1; /* XXX ??? */ } _kdc_fix_time(&b->till); t = *b->till; /* be careful not overflowing */ if(client->entry.max_life) t = start + min(t - start, *client->entry.max_life); if(server->entry.max_life) t = start + min(t - start, *server->entry.max_life); #if 0 t = min(t, start + realm->max_life); #endif et.endtime = t; if(f.renewable_ok && et.endtime < *b->till){ f.renewable = 1; if(b->rtime == NULL){ ALLOC(b->rtime); *b->rtime = 0; } if(*b->rtime < *b->till) *b->rtime = *b->till; } if(f.renewable && b->rtime){ t = *b->rtime; if(t == 0) t = MAX_TIME; if(client->entry.max_renew) t = start + min(t - start, *client->entry.max_renew); if(server->entry.max_renew) t = start + min(t - start, *server->entry.max_renew); #if 0 t = min(t, start + realm->max_renew); #endif ALLOC(et.renew_till); *et.renew_till = t; et.flags.renewable = 1; } } if (f.request_anonymous) et.flags.anonymous = 1; if(b->addresses){ ALLOC(et.caddr); copy_HostAddresses(b->addresses, et.caddr); } et.transited.tr_type = DOMAIN_X500_COMPRESS; krb5_data_zero(&et.transited.contents); /* The MIT ASN.1 library (obviously) doesn't tell lengths encoded * as 0 and as 0x80 (meaning indefinite length) apart, and is thus * incapable of correctly decoding SEQUENCE OF's of zero length. * * To fix this, always send at least one no-op last_req * * If there's a pw_end or valid_end we will use that, * otherwise just a dummy lr. */ ek.last_req.val = malloc(2 * sizeof(*ek.last_req.val)); if (ek.last_req.val == NULL) { ret = ENOMEM; goto out; } ek.last_req.len = 0; if (client->entry.pw_end && (config->kdc_warn_pwexpire == 0 || kdc_time + config->kdc_warn_pwexpire >= *client->entry.pw_end)) { ek.last_req.val[ek.last_req.len].lr_type = LR_PW_EXPTIME; ek.last_req.val[ek.last_req.len].lr_value = *client->entry.pw_end; ++ek.last_req.len; } if (client->entry.valid_end) { ek.last_req.val[ek.last_req.len].lr_type = LR_ACCT_EXPTIME; ek.last_req.val[ek.last_req.len].lr_value = *client->entry.valid_end; ++ek.last_req.len; } if (ek.last_req.len == 0) { ek.last_req.val[ek.last_req.len].lr_type = LR_NONE; ek.last_req.val[ek.last_req.len].lr_value = 0; ++ek.last_req.len; } ek.nonce = b->nonce; if (client->entry.valid_end || client->entry.pw_end) { ALLOC(ek.key_expiration); if (client->entry.valid_end) { if (client->entry.pw_end) *ek.key_expiration = min(*client->entry.valid_end, *client->entry.pw_end); else *ek.key_expiration = *client->entry.valid_end; } else *ek.key_expiration = *client->entry.pw_end; } else ek.key_expiration = NULL; ek.flags = et.flags; ek.authtime = et.authtime; if (et.starttime) { ALLOC(ek.starttime); *ek.starttime = *et.starttime; } ek.endtime = et.endtime; if (et.renew_till) { ALLOC(ek.renew_till); *ek.renew_till = *et.renew_till; } copy_Realm(&rep.ticket.realm, &ek.srealm); copy_PrincipalName(&rep.ticket.sname, &ek.sname); if(et.caddr){ ALLOC(ek.caddr); copy_HostAddresses(et.caddr, ek.caddr); } #if PKINIT if (pkp) { e_text = "Failed to build PK-INIT reply"; ret = _kdc_pk_mk_pa_reply(context, config, pkp, client, sessionetype, req, req_buffer, &reply_key, &et.key, rep.padata); if (ret) goto out; ret = _kdc_add_inital_verified_cas(context, config, pkp, &et); if (ret) goto out; } else #endif { ret = krb5_generate_random_keyblock(context, sessionetype, &et.key); if (ret) goto out; } if (reply_key == NULL) { e_text = "Client have no reply key"; ret = KRB5KDC_ERR_CLIENT_NOTYET; goto out; } ret = copy_EncryptionKey(&et.key, &ek.key); if (ret) goto out; if (rep.padata->len == 0) { free(rep.padata); rep.padata = NULL; } /* Add the PAC */ if (send_pac_p(context, req)) { krb5_pac p = NULL; krb5_data data; ret = _kdc_pac_generate(context, client, &p); if (ret) { kdc_log(context, config, 0, "PAC generation failed for -- %s", client_name); goto out; } if (p != NULL) { ret = _krb5_pac_sign(context, p, et.authtime, client->entry.principal, &skey->key, /* Server key */ &skey->key, /* FIXME: should be krbtgt key */ &data); krb5_pac_free(context, p); if (ret) { kdc_log(context, config, 0, "PAC signing failed for -- %s", client_name); goto out; } ret = _kdc_tkt_add_if_relevant_ad(context, &et, KRB5_AUTHDATA_WIN2K_PAC, &data); krb5_data_free(&data); if (ret) goto out; } } _kdc_log_timestamp(context, config, "AS-REQ", et.authtime, et.starttime, et.endtime, et.renew_till); /* do this as the last thing since this signs the EncTicketPart */ ret = _kdc_add_KRB5SignedPath(context, config, server, setype, client->entry.principal, NULL, NULL, &et); if (ret) goto out; log_as_req(context, config, reply_key->keytype, setype, b); ret = _kdc_encode_reply(context, config, &rep, &et, &ek, setype, server->entry.kvno, &skey->key, client->entry.kvno, reply_key, 0, &e_text, reply); free_EncTicketPart(&et); free_EncKDCRepPart(&ek); if (ret) goto out; /* */ if (datagram_reply && reply->length > config->max_datagram_reply_length) { krb5_data_free(reply); ret = KRB5KRB_ERR_RESPONSE_TOO_BIG; e_text = "Reply packet too large"; } out: free_AS_REP(&rep); if(ret != 0 && ret != HDB_ERR_NOT_FOUND_HERE){ krb5_mk_error(context, ret, e_text, (e_data.data ? &e_data : NULL), client_princ, server_princ, NULL, NULL, reply); ret = 0; } #ifdef PKINIT if (pkp) _kdc_pk_free_client_param(context, pkp); #endif if (e_data.data) free(e_data.data); if (client_princ) krb5_free_principal(context, client_princ); free(client_name); if (server_princ) krb5_free_principal(context, server_princ); free(server_name); if(client) _kdc_free_ent(context, client); if(server) _kdc_free_ent(context, server); return ret; }
krb5_error_code KRB5_LIB_FUNCTION krb5_rd_req(krb5_context context, krb5_auth_context *auth_context, const krb5_data *inbuf, krb5_const_principal server, krb5_keytab keytab, krb5_flags *ap_req_options, krb5_ticket **ticket) { krb5_error_code ret; krb5_ap_req ap_req; krb5_keyblock *keyblock = NULL; krb5_principal service = NULL; if (*auth_context == NULL) { ret = krb5_auth_con_init(context, auth_context); if (ret) return ret; } ret = krb5_decode_ap_req(context, inbuf, &ap_req); if(ret) return ret; if(server == NULL){ _krb5_principalname2krb5_principal(&service, ap_req.ticket.sname, ap_req.ticket.realm); server = service; } if (ap_req.ap_options.use_session_key && (*auth_context)->keyblock == NULL) { krb5_set_error_string(context, "krb5_rd_req: user to user auth " "without session key given"); ret = KRB5KRB_AP_ERR_NOKEY; goto out; } if((*auth_context)->keyblock == NULL){ ret = get_key_from_keytab(context, auth_context, &ap_req, server, keytab, &keyblock); if(ret) goto out; } else { ret = krb5_copy_keyblock(context, (*auth_context)->keyblock, &keyblock); if (ret) goto out; } ret = krb5_verify_ap_req(context, auth_context, &ap_req, server, keyblock, 0, ap_req_options, ticket); krb5_free_keyblock(context, keyblock); out: free_AP_REQ(&ap_req); if(service) krb5_free_principal(context, service); return ret; }