diff options
Diffstat (limited to 'kdc/misc.c')
| -rw-r--r-- | kdc/misc.c | 267 |
1 files changed, 211 insertions, 56 deletions
diff --git a/kdc/misc.c b/kdc/misc.c index 15fae0f3a3b2..34d93908aa27 100644 --- a/kdc/misc.c +++ b/kdc/misc.c @@ -51,16 +51,87 @@ name_type_ok(krb5_context context, struct timeval _kdc_now; -krb5_error_code +static krb5_error_code +synthesize_hdb_close(krb5_context context, struct HDB *db) +{ + (void) context; + (void) db; + return 0; +} + +/* + * Synthesize an HDB entry suitable for PKINIT and GSS preauth. + */ +static krb5_error_code +synthesize_client(krb5_context context, + krb5_kdc_configuration *config, + krb5_const_principal princ, + HDB **db, + hdb_entry **h) +{ + static HDB null_db; + krb5_error_code ret; + hdb_entry *e; + + /* Hope this works! */ + null_db.hdb_destroy = synthesize_hdb_close; + null_db.hdb_close = synthesize_hdb_close; + if (db) + *db = &null_db; + + ret = (e = calloc(1, sizeof(*e))) ? 0 : krb5_enomem(context); + if (ret == 0) { + e->flags.client = 1; + e->flags.immutable = 1; + e->flags.virtual = 1; + e->flags.synthetic = 1; + e->flags.do_not_store = 1; + e->kvno = 1; + e->keys.len = 0; + e->keys.val = NULL; + e->created_by.time = time(NULL); + e->modified_by = NULL; + e->valid_start = NULL; + e->valid_end = NULL; + e->pw_end = NULL; + e->etypes = NULL; + e->generation = NULL; + e->extensions = NULL; + } + if (ret == 0) + ret = (e->max_renew = calloc(1, sizeof(*e->max_renew))) ? + 0 : krb5_enomem(context); + if (ret == 0) + ret = (e->max_life = calloc(1, sizeof(*e->max_life))) ? + 0 : krb5_enomem(context); + if (ret == 0) + ret = krb5_copy_principal(context, princ, &e->principal); + if (ret == 0) + ret = krb5_copy_principal(context, princ, &e->created_by.principal); + if (ret == 0) { + /* + * We can't check OCSP in the TGS path, so we can't let tickets for + * synthetic principals live very long. + */ + *(e->max_renew) = config->synthetic_clients_max_renew; + *(e->max_life) = config->synthetic_clients_max_life; + *h = e; + } else if (e) { + hdb_free_entry(context, &null_db, e); + } + return ret; +} + +KDC_LIB_FUNCTION krb5_error_code KDC_LIB_CALL _kdc_db_fetch(krb5_context context, krb5_kdc_configuration *config, krb5_const_principal principal, unsigned flags, krb5uint32 *kvno_ptr, HDB **db, - hdb_entry_ex **h) + hdb_entry **h) { - hdb_entry_ex *ent = NULL; + hdb_entry *ent = NULL; krb5_error_code ret = HDB_ERR_NOENTRY; int i; unsigned kvno = 0; @@ -68,10 +139,13 @@ _kdc_db_fetch(krb5_context context, krb5_const_principal princ; *h = NULL; + if (db) + *db = NULL; if (!name_type_ok(context, config, principal)) - goto out2; + return HDB_ERR_NOENTRY; + flags |= HDB_F_DECRYPT; if (kvno_ptr != NULL && *kvno_ptr != 0) { kvno = *kvno_ptr; flags |= HDB_F_KVNO_SPECIFIED; @@ -99,7 +173,12 @@ _kdc_db_fetch(krb5_context context, } for (i = 0; i < config->num_db; i++) { - ret = config->db[i]->hdb_open(context, config->db[i], O_RDONLY, 0); + HDB *curdb = config->db[i]; + + if (db) + *db = curdb; + + ret = curdb->hdb_open(context, curdb, O_RDONLY, 0); if (ret) { const char *msg = krb5_get_error_message(context, ret); kdc_log(context, config, 0, "Failed to open database: %s", msg); @@ -108,62 +187,70 @@ _kdc_db_fetch(krb5_context context, } princ = principal; - if (!(config->db[i]->hdb_capability_flags & HDB_CAP_F_HANDLE_ENTERPRISE_PRINCIPAL) && enterprise_principal) + if (!(curdb->hdb_capability_flags & HDB_CAP_F_HANDLE_ENTERPRISE_PRINCIPAL) && enterprise_principal) princ = enterprise_principal; - ret = config->db[i]->hdb_fetch_kvno(context, - config->db[i], - princ, - flags | HDB_F_DECRYPT, - kvno, - ent); - config->db[i]->hdb_close(context, config->db[i]); - - switch (ret) { - case HDB_ERR_WRONG_REALM: - /* - * the ent->entry.principal just contains hints for the client - * to retry. This is important for enterprise principal routing - * between trusts. - */ - /* fall through */ - case 0: - if (db) - *db = config->db[i]; - *h = ent; - ent = NULL; - goto out; + ret = hdb_fetch_kvno(context, curdb, princ, flags, 0, 0, kvno, ent); + curdb->hdb_close(context, curdb); - case HDB_ERR_NOENTRY: - /* Check the other databases */ - continue; + if (ret == HDB_ERR_NOENTRY) + continue; /* Check the other databases */ - default: - /* - * This is really important, because errors like - * HDB_ERR_NOT_FOUND_HERE (used to indicate to Samba that - * the RODC on which this code is running does not have - * the key we need, and so a proxy to the KDC is required) - * have specific meaning, and need to be propogated up. - */ - goto out; - } + /* + * This is really important, because errors like + * HDB_ERR_NOT_FOUND_HERE (used to indicate to Samba that + * the RODC on which this code is running does not have + * the key we need, and so a proxy to the KDC is required) + * have specific meaning, and need to be propogated up. + */ + break; } -out2: - if (ret == HDB_ERR_NOENTRY) { - krb5_set_error_message(context, ret, "no such entry found in hdb"); + switch (ret) { + case HDB_ERR_WRONG_REALM: + case 0: + /* + * the ent->entry.principal just contains hints for the client + * to retry. This is important for enterprise principal routing + * between trusts. + */ + *h = ent; + ent = NULL; + break; + + case HDB_ERR_NOENTRY: + if (db) + *db = NULL; + if ((flags & HDB_F_GET_CLIENT) && (flags & HDB_F_SYNTHETIC_OK) && + config->synthetic_clients) { + ret = synthesize_client(context, config, principal, db, h); + if (ret) { + krb5_set_error_message(context, ret, "could not synthesize " + "HDB client principal entry"); + ret = HDB_ERR_NOENTRY; + krb5_prepend_error_message(context, ret, "no such entry found in hdb"); + } + } else { + krb5_set_error_message(context, ret, "no such entry found in hdb"); + } + break; + + default: + if (db) + *db = NULL; + break; } + out: krb5_free_principal(context, enterprise_principal); free(ent); return ret; } -void -_kdc_free_ent(krb5_context context, hdb_entry_ex *ent) +KDC_LIB_FUNCTION void KDC_LIB_CALL +_kdc_free_ent(krb5_context context, HDB *db, hdb_entry *ent) { - hdb_free_entry (context, ent); + hdb_free_entry (context, db, ent); free (ent); } @@ -175,7 +262,7 @@ _kdc_free_ent(krb5_context context, hdb_entry_ex *ent) krb5_error_code _kdc_get_preferred_key(krb5_context context, krb5_kdc_configuration *config, - hdb_entry_ex *h, + hdb_entry *h, const char *name, krb5_enctype *enctype, Key **key) @@ -186,11 +273,11 @@ _kdc_get_preferred_key(krb5_context context, if (config->use_strongest_server_key) { const krb5_enctype *p = krb5_kerberos_enctypes(context); - for (i = 0; p[i] != (krb5_enctype)ETYPE_NULL; i++) { + for (i = 0; p[i] != ETYPE_NULL; i++) { if (krb5_enctype_valid(context, p[i]) != 0 && - !_kdc_is_weak_exception(h->entry.principal, p[i])) + !_kdc_is_weak_exception(h->principal, p[i])) continue; - ret = hdb_enctype2key(context, &h->entry, NULL, p[i], key); + ret = hdb_enctype2key(context, h, NULL, p[i], key); if (ret != 0) continue; if (enctype != NULL) @@ -200,12 +287,12 @@ _kdc_get_preferred_key(krb5_context context, } else { *key = NULL; - for (i = 0; i < h->entry.keys.len; i++) { - if (krb5_enctype_valid(context, h->entry.keys.val[i].key.keytype) != 0 && - !_kdc_is_weak_exception(h->entry.principal, h->entry.keys.val[i].key.keytype)) + for (i = 0; i < h->keys.len; i++) { + if (krb5_enctype_valid(context, h->keys.val[i].key.keytype) != 0 && + !_kdc_is_weak_exception(h->principal, h->keys.val[i].key.keytype)) continue; - ret = hdb_enctype2key(context, &h->entry, NULL, - h->entry.keys.val[i].key.keytype, key); + ret = hdb_enctype2key(context, h, NULL, + h->keys.val[i].key.keytype, key); if (ret != 0) continue; if (enctype != NULL) @@ -219,3 +306,71 @@ _kdc_get_preferred_key(krb5_context context, return EINVAL; /* XXX */ } +krb5_error_code +_kdc_verify_checksum(krb5_context context, + krb5_crypto crypto, + krb5_key_usage usage, + const krb5_data *data, + Checksum *cksum) +{ + krb5_error_code ret; + + ret = krb5_verify_checksum(context, crypto, usage, + data->data, data->length, + cksum); + if (ret == KRB5_PROG_SUMTYPE_NOSUPP) + ret = KRB5KDC_ERR_SUMTYPE_NOSUPP; + + return ret; +} + +/* + * Returns TRUE if a PAC should be included in ticket authorization data. + * + * Per [MS-KILE] 3.3.5.3, PACs are always included for TGTs; for service + * tickets, policy is governed by whether the client explicitly requested + * a PAC be omitted when requesting a TGT, or if the no-auth-data-reqd + * flag is set on the service principal entry. + * + * However, when issuing a cross-realm TGT to an AD realm our PAC might not + * interoperate correctly. Therefore we honor the no-auth-data-reqd HDB entry + * flag on cross-realm TGTs. + */ + +krb5_boolean +_kdc_include_pac_p(astgs_request_t r) +{ + /* Always include a PAC in root TGTs */ + if (krb5_principal_is_krbtgt(r->context, r->server->principal)) { + if (krb5_principal_is_root_krbtgt(r->context, r->server->principal) || + !r->server->flags.no_auth_data_reqd) + return TRUE; + } + if (r->server->flags.no_auth_data_reqd) + return FALSE; + if (r->server->flags.auth_data_reqd) + return TRUE; + if (r->config->disable_pac) + return FALSE; + + return !!(r->pac_attributes & (KRB5_PAC_WAS_REQUESTED | KRB5_PAC_WAS_GIVEN_IMPLICITLY)); +} + +/* + * Notify the HDB backend and KDC plugin of the audited event. + */ + +krb5_error_code +_kdc_audit_request(astgs_request_t r) +{ + krb5_error_code ret; + struct HDB *hdb; + + ret = _kdc_plugin_audit(r); + if (ret == 0 && + (hdb = r->clientdb ? r->clientdb : r->config->db[0]) && + hdb->hdb_audit) + ret = hdb->hdb_audit(r->context, hdb, r->client, (hdb_request_t)r); + + return ret; +} |
