diff options
Diffstat (limited to 'source4/kdc')
-rw-r--r-- | source4/kdc/hdb-ldb.c | 131 | ||||
-rw-r--r-- | source4/kdc/pac-glue.c | 317 | ||||
-rw-r--r-- | source4/kdc/pac-glue.h | 52 |
3 files changed, 390 insertions, 110 deletions
diff --git a/source4/kdc/hdb-ldb.c b/source4/kdc/hdb-ldb.c index 87e82738983..8b61914daef 100644 --- a/source4/kdc/hdb-ldb.c +++ b/source4/kdc/hdb-ldb.c @@ -73,12 +73,6 @@ static const char *realm_ref_attrs[] = { NULL }; -struct hdb_ldb_private { - struct ldb_context *samdb; - struct ldb_message **msg; - struct ldb_message **realm_ref_msg; -}; - static KerberosTime ldb_msg_find_krb5time_ldap_time(struct ldb_message *msg, const char *attr, KerberosTime default_val) { const char *tmp; @@ -177,13 +171,10 @@ static HDBFlags uf2HDBFlags(krb5_context context, int userAccountControl, enum h if (userAccountControl & UF_SMARTCARD_REQUIRED) { flags.require_hwauth = 1; } - if (flags.server && (userAccountControl & UF_TRUSTED_FOR_DELEGATION)) { - flags.forwardable = 1; - flags.proxiable = 1; - } else if (flags.client && (userAccountControl & UF_NOT_DELEGATED)) { - flags.forwardable = 0; - flags.proxiable = 0; - } else { + if (userAccountControl & UF_TRUSTED_FOR_DELEGATION) { + flags.ok_as_delegate = 1; + } + if (!(userAccountControl & UF_NOT_DELEGATED)) { flags.forwardable = 1; flags.proxiable = 1; } @@ -205,6 +196,12 @@ static HDBFlags uf2HDBFlags(krb5_context context, int userAccountControl, enum h return flags; } +static krb5_error_code hdb_ldb_free_private(krb5_context context, hdb_entry_ex *entry_ex) +{ + talloc_free(entry_ex->private); + return 0; +} + /* * Construct an hdb_entry from a directory entry. */ @@ -213,15 +210,17 @@ static krb5_error_code LDB_message2entry(krb5_context context, HDB *db, enum hdb_ldb_ent_type ent_type, struct ldb_message *msg, struct ldb_message *realm_ref_msg, - hdb_entry *ent) + hdb_entry_ex *entry_ex) { const char *unicodePwd; - int userAccountControl; + unsigned int userAccountControl; int i; krb5_error_code ret = 0; const char *dnsdomain = ldb_msg_find_string(realm_ref_msg, "dnsRoot", NULL); char *realm = strupper_talloc(mem_ctx, dnsdomain); + struct hdb_ldb_private *private; + hdb_entry *ent = &entry_ex->entry; memset(ent, 0, sizeof(*ent)); @@ -233,7 +232,7 @@ static krb5_error_code LDB_message2entry(krb5_context context, HDB *db, goto out; } - userAccountControl = ldb_msg_find_int(msg, "userAccountControl", 0); + userAccountControl = ldb_msg_find_uint(msg, "userAccountControl", 0); ent->principal = malloc(sizeof(*(ent->principal))); if (ent_type == HDB_LDB_ENT_TYPE_ANY && principal == NULL) { @@ -279,6 +278,7 @@ static krb5_error_code LDB_message2entry(krb5_context context, HDB *db, if (ent_type == HDB_LDB_ENT_TYPE_KRBTGT) { ent->flags.invalid = 0; ent->flags.server = 1; + ent->flags.forwardable = 1; } if (lp_parm_bool(-1, "kdc", "require spn for service", True)) { @@ -399,7 +399,7 @@ static krb5_error_code LDB_message2entry(krb5_context context, HDB *db, } else { ret = krb5_data_alloc (&keyvalue, 16); if (ret) { - krb5_set_error_string(context, "malloc: out of memory"); + krb5_clear_error_string(context); ret = ENOMEM; goto out; } @@ -409,7 +409,7 @@ static krb5_error_code LDB_message2entry(krb5_context context, HDB *db, ent->keys.val = malloc(sizeof(ent->keys.val[0])); if (ent->keys.val == NULL) { krb5_data_free(&keyvalue); - krb5_set_error_string(context, "malloc: out of memory"); + krb5_clear_error_string(context); ret = ENOMEM; goto out; } @@ -425,14 +425,14 @@ static krb5_error_code LDB_message2entry(krb5_context context, HDB *db, ent->etypes = malloc(sizeof(*(ent->etypes))); if (ent->etypes == NULL) { - krb5_set_error_string(context, "malloc: out of memory"); + krb5_clear_error_string(context); ret = ENOMEM; goto out; } ent->etypes->len = ent->keys.len; ent->etypes->val = calloc(ent->etypes->len, sizeof(int)); if (ent->etypes->val == NULL) { - krb5_set_error_string(context, "malloc: out of memory"); + krb5_clear_error_string(context); ret = ENOMEM; goto out; } @@ -440,10 +440,27 @@ static krb5_error_code LDB_message2entry(krb5_context context, HDB *db, ent->etypes->val[i] = ent->keys.val[i].key.keytype; } + + private = talloc(db, struct hdb_ldb_private); + if (!private) { + ret = ENOMEM; + goto out; + } + + private->msg = talloc_steal(private, msg); + private->realm_ref_msg = talloc_steal(private, realm_ref_msg); + private->samdb = (struct ldb_context *)db->hdb_db; + + entry_ex->private = private; + entry_ex->free_private = hdb_ldb_free_private; + entry_ex->check_client_access = hdb_ldb_check_client_access; + entry_ex->authz_data_tgs_req = hdb_ldb_authz_data_tgs_req; + entry_ex->authz_data_as_req = hdb_ldb_authz_data_as_req; + out: if (ret != 0) { - /* I don't think this frees ent itself. */ - hdb_free_entry(context, ent); + /* This doesn't free ent itself, that is for the eventual caller to do */ + hdb_free_entry(context, &entry_ex->entry); } return ret; @@ -618,44 +635,6 @@ static krb5_error_code LDB_rename(krb5_context context, HDB *db, const char *new return HDB_ERR_DB_INUSE; } -static krb5_error_code hdb_ldb_free_private(krb5_context context, hdb_entry_ex *entry_ex) -{ - talloc_free(entry_ex->private); - return 0; -} - -static krb5_error_code hdb_ldb_check_client_access(krb5_context context, hdb_entry_ex *entry_ex, - HostAddresses *addresses) -{ - krb5_error_code ret; - NTSTATUS nt_status; - TALLOC_CTX *tmp_ctx = talloc_new(entry_ex->private); - struct hdb_ldb_private *private = entry_ex->private; - char *name, *workstation = NULL; - if (!tmp_ctx) { - return ENOMEM; - } - - ret = krb5_unparse_name(context, entry_ex->entry.principal, &name); - if (ret != 0) { - talloc_free(tmp_ctx); - } - nt_status = authsam_account_ok(tmp_ctx, - private->samdb, - MSV1_0_ALLOW_SERVER_TRUST_ACCOUNT | MSV1_0_ALLOW_WORKSTATION_TRUST_ACCOUNT, - private->msg, - private->realm_ref_msg, - workstation, - name); - free(name); - - if (!NT_STATUS_IS_OK(nt_status)) { - return KRB5KDC_ERR_POLICY; - } - return 0; -} - - static krb5_error_code LDB_fetch_ex(krb5_context context, HDB *db, unsigned flags, krb5_const_principal principal, enum hdb_ent_type ent_type, @@ -703,22 +682,7 @@ static krb5_error_code LDB_fetch_ex(krb5_context context, HDB *db, unsigned flag ret = LDB_message2entry(context, db, mem_ctx, principal, ldb_ent_type, - msg[0], realm_ref_msg[0], &entry_ex->entry); - - if (ret == 0) { - struct hdb_ldb_private *private = talloc(db, struct hdb_ldb_private); - if (!private) { - hdb_free_entry(context, &entry_ex->entry); - ret = ENOMEM; - } - private->msg = talloc_steal(private, msg); - private->realm_ref_msg = talloc_steal(private, realm_ref_msg); - private->samdb = (struct ldb_context *)db->hdb_db; - - entry_ex->private = private; - entry_ex->free_private = hdb_ldb_free_private; - entry_ex->check_client_access = hdb_ldb_check_client_access; - } + msg[0], realm_ref_msg[0], entry_ex); talloc_free(mem_ctx); return ret; @@ -802,7 +766,7 @@ static krb5_error_code LDB_fetch_ex(krb5_context context, HDB *db, unsigned flag ret = LDB_message2entry(context, db, mem_ctx, principal, ldb_ent_type, - msg[0], realm_ref_msg[0], &entry_ex->entry); + msg[0], realm_ref_msg[0], entry_ex); talloc_free(mem_ctx); return ret; @@ -845,7 +809,7 @@ static krb5_error_code LDB_fetch_ex(krb5_context context, HDB *db, unsigned flag } else { ret = LDB_message2entry(context, db, mem_ctx, principal, ldb_ent_type, - msg[0], realm_ref_msg[0], &entry_ex->entry); + msg[0], realm_ref_msg[0], entry_ex); if (ret != 0) { krb5_warnx(context, "LDB_fetch: message2entry failed\n"); } @@ -898,6 +862,9 @@ static krb5_error_code LDB_seq(krb5_context context, HDB *db, unsigned flags, hd krb5_error_code ret; struct hdb_ldb_seq *priv = (struct hdb_ldb_seq *)db->hdb_openp; TALLOC_CTX *mem_ctx; + hdb_entry_ex entry_ex; + memset(&entry_ex, '\0', sizeof(entry_ex)); + if (!priv) { return HDB_ERR_NOENTRY; } @@ -913,7 +880,13 @@ static krb5_error_code LDB_seq(krb5_context context, HDB *db, unsigned flags, hd ret = LDB_message2entry(context, db, mem_ctx, NULL, HDB_LDB_ENT_TYPE_ANY, priv->msgs[priv->index++], - priv->realm_ref_msgs[0], entry); + priv->realm_ref_msgs[0], &entry_ex); + if (ret == 0) { + if (entry_ex.free_private) { + entry_ex.free_private(context, &entry_ex); + } + *entry = entry_ex.entry; + } } else { ret = HDB_ERR_NOENTRY; } diff --git a/source4/kdc/pac-glue.c b/source4/kdc/pac-glue.c index ae306b4062e..03b53fa3af0 100644 --- a/source4/kdc/pac-glue.c +++ b/source4/kdc/pac-glue.c @@ -23,42 +23,41 @@ #include "includes.h" #include "kdc/kdc.h" -#include "kdc/pac-glue.h" /* Ensure we don't get this prototype wrong, as that could be painful */ - - krb5_error_code samba_get_pac(krb5_context context, - struct krb5_kdc_configuration *config, - krb5_principal client, - krb5_keyblock *krbtgt_keyblock, - krb5_keyblock *server_keyblock, - time_t tgs_authtime, - krb5_data *pac) +#include "include/ads.h" +#include "lib/ldb/include/ldb.h" +#include "heimdal/lib/krb5/krb5_locl.h" +#include "librpc/gen_ndr/krb5pac.h" +#include "auth/auth.h" + +/* Given the right private pointer from hdb_ldb, get a PAC from the attached ldb messages */ +static krb5_error_code samba_get_pac(krb5_context context, + struct hdb_ldb_private *private, + krb5_principal client, + krb5_keyblock *krbtgt_keyblock, + krb5_keyblock *server_keyblock, + time_t tgs_authtime, + krb5_data *pac) { krb5_error_code ret; NTSTATUS nt_status; struct auth_serversupplied_info *server_info; DATA_BLOB tmp_blob; - char *principal_string; - TALLOC_CTX *mem_ctx = talloc_named(config, 0, "samba_get_pac context"); + TALLOC_CTX *mem_ctx = talloc_named(private, 0, "samba_get_pac context"); + if (!mem_ctx) { return ENOMEM; } - ret = krb5_unparse_name(context, client, &principal_string); - - if (ret != 0) { - krb5_set_error_string(context, "get pac: could not unparse principal"); - krb5_warnx(context, "get pac: could not unparse principal"); - talloc_free(mem_ctx); - return ret; - } - - nt_status = sam_get_server_info_principal(mem_ctx, principal_string, - &server_info); - free(principal_string); + nt_status = authsam_make_server_info(mem_ctx, private->samdb, + private->msg, + private->realm_ref_msg, + data_blob(NULL, 0), + data_blob(NULL, 0), + &server_info); if (!NT_STATUS_IS_OK(nt_status)) { DEBUG(0, ("Getting user info for PAC failed: %s\n", nt_errstr(nt_status))); - return EINVAL; + return ENOMEM; } ret = kerberos_create_pac(mem_ctx, server_info, @@ -80,3 +79,273 @@ talloc_free(mem_ctx); return ret; } + +/* Wrap the PAC in the right ASN.1. Will always free 'pac', on success or failure */ +krb5_error_code wrap_pac(krb5_context context, krb5_data *pac, AuthorizationData **out) +{ + krb5_error_code ret; + + unsigned char *buf; + size_t buf_size; + size_t len; + + AD_IF_RELEVANT if_relevant; + AuthorizationData *auth_data; + + if_relevant.len = 1; + if_relevant.val = malloc(sizeof(*if_relevant.val)); + if (!if_relevant.val) { + krb5_data_free(pac); + *out = NULL; + return ENOMEM; + } + + if_relevant.val[0].ad_type = KRB5_AUTHDATA_WIN2K_PAC; + if_relevant.val[0].ad_data.data = NULL; + if_relevant.val[0].ad_data.length = 0; + + /* pac.data will be freed with this */ + if_relevant.val[0].ad_data.data = pac->data; + if_relevant.val[0].ad_data.length = pac->length; + + ASN1_MALLOC_ENCODE(AuthorizationData, buf, buf_size, &if_relevant, &len, ret); + free_AuthorizationData(&if_relevant); + if (ret) { + *out = NULL; + return ret; + } + + auth_data = malloc(sizeof(*auth_data)); + if (!auth_data) { + free(buf); + *out = NULL; + return ret; + } + auth_data->len = 1; + auth_data->val = malloc(sizeof(*auth_data->val)); + if (!auth_data->val) { + free(buf); + free(auth_data); + *out = NULL; + return ret; + } + auth_data->val[0].ad_type = KRB5_AUTHDATA_IF_RELEVANT; + auth_data->val[0].ad_data.length = len; + auth_data->val[0].ad_data.data = buf; + + *out = auth_data; + return 0; +} + + +/* Given a hdb_entry, create a PAC out of the private data + + Don't create it if the client has the UF_NO_AUTH_DATA_REQUIRED bit + set, or if they specificaly asked not to get it. +*/ + + krb5_error_code hdb_ldb_authz_data_as_req(krb5_context context, struct hdb_entry_ex *entry_ex, + METHOD_DATA* pa_data_seq, + time_t authtime, + EncryptionKey *tgtkey, + EncryptionKey *sessionkey, + AuthorizationData **out) +{ + krb5_error_code ret; + int i; + krb5_data pac; + krb5_boolean pac_wanted = TRUE; + unsigned int userAccountControl; + struct PA_PAC_REQUEST pac_request; + struct hdb_ldb_private *private = talloc_get_type(entry_ex->private, struct hdb_ldb_private); + + /* The user account may be set not to want the PAC */ + userAccountControl = ldb_msg_find_uint(private->msg, "userAccountControl", 0); + if (userAccountControl & UF_NO_AUTH_DATA_REQUIRED) { + *out = NULL; + return 0; + } + + /* The user may not want a PAC */ + for (i=0; i<pa_data_seq->len; i++) { + if (pa_data_seq->val[i].padata_type == KRB5_PADATA_PA_PAC_REQUEST) { + ret = decode_PA_PAC_REQUEST(pa_data_seq->val[i].padata_value.data, + pa_data_seq->val[i].padata_value.length, + &pac_request, NULL); + if (ret == 0) { + pac_wanted = !!pac_request.include_pac; + } + free_PA_PAC_REQUEST(&pac_request); + break; + } + } + + if (!pac_wanted) { + *out = NULL; + return 0; + } + + /* Get PAC from Samba */ + ret = samba_get_pac(context, + private, + entry_ex->entry.principal, + tgtkey, + tgtkey, + authtime, + &pac); + + if (ret) { + *out = NULL; + return ret; + } + + return wrap_pac(context, &pac, out); +} + +/* Resign (and reform, including possibly new groups) a PAC */ + + krb5_error_code hdb_ldb_authz_data_tgs_req(krb5_context context, struct hdb_entry_ex *entry_ex, + krb5_principal client, + AuthorizationData *in, + time_t authtime, + EncryptionKey *tgtkey, + EncryptionKey *servicekey, + EncryptionKey *sessionkey, + AuthorizationData **out) +{ + NTSTATUS nt_status; + krb5_error_code ret; + + unsigned int userAccountControl; + + struct hdb_ldb_private *private = talloc_get_type(entry_ex->private, struct hdb_ldb_private); + krb5_data k5pac_in, k5pac_out; + DATA_BLOB pac_in, pac_out; + + struct PAC_LOGON_INFO *logon_info; + union netr_Validation validation; + struct auth_serversupplied_info *server_info_out; + + krb5_boolean found = FALSE; + TALLOC_CTX *mem_ctx; + + /* The service account may be set not to want the PAC */ + userAccountControl = ldb_msg_find_uint(private->msg, "userAccountControl", 0); + if (userAccountControl & UF_NO_AUTH_DATA_REQUIRED) { + *out = NULL; + return 0; + } + + ret = _krb5_find_type_in_ad(context, KRB5_AUTHDATA_WIN2K_PAC, + &k5pac_in, &found, sessionkey, in); + if (ret || !found) { + *out = NULL; + return 0; + } + + mem_ctx = talloc_new(private); + if (!mem_ctx) { + krb5_data_free(&k5pac_in); + *out = NULL; + return ENOMEM; + } + + pac_in = data_blob_talloc(mem_ctx, k5pac_in.data, k5pac_in.length); + krb5_data_free(&k5pac_in); + if (!pac_in.data) { + talloc_free(mem_ctx); + *out = NULL; + return ENOMEM; + } + + /* Parse the PAC again, for the logon info */ + nt_status = kerberos_pac_logon_info(mem_ctx, &logon_info, + pac_in, + context, + tgtkey, + tgtkey, + client, authtime, + &ret); + + if (!NT_STATUS_IS_OK(nt_status)) { + DEBUG(1, ("Failed to parse PAC in TGT: %s/%s\n", + nt_errstr(nt_status), error_message(ret))); + talloc_free(mem_ctx); + *out = NULL; + return ret; + } + + /* Pull this right into the normal auth sysstem structures */ + validation.sam3 = &logon_info->info3; + nt_status = make_server_info_netlogon_validation(mem_ctx, + "", + 3, &validation, + &server_info_out); + if (!NT_STATUS_IS_OK(nt_status)) { + talloc_free(mem_ctx); + *out = NULL; + return ENOMEM; + } + + /* And make a new PAC, possibly containing new groups */ + ret = kerberos_create_pac(mem_ctx, + server_info_out, + context, + tgtkey, + servicekey, + client, + authtime, + &pac_out); + + if (ret != 0) { + talloc_free(mem_ctx); + *out = NULL; + return ret; + } + + ret = krb5_data_copy(&k5pac_out, pac_out.data, pac_out.length); + if (ret != 0) { + talloc_free(mem_ctx); + *out = NULL; + return ret; + } + + return wrap_pac(context, &k5pac_out, out); +} + +/* Given an hdb entry (and in particular it's private member), consult + * the account_ok routine in auth/auth_sam.c for consistancy */ + + krb5_error_code hdb_ldb_check_client_access(krb5_context context, hdb_entry_ex *entry_ex, + HostAddresses *addresses) +{ + krb5_error_code ret; + NTSTATUS nt_status; + TALLOC_CTX *tmp_ctx = talloc_new(entry_ex->private); + struct hdb_ldb_private *private = talloc_get_type(entry_ex->private, struct hdb_ldb_private); + char *name, *workstation = NULL; + if (!tmp_ctx) { + return ENOMEM; + } + + ret = krb5_unparse_name(context, entry_ex->entry.principal, &name); + if (ret != 0) { + talloc_free(tmp_ctx); + } + nt_status = authsam_account_ok(tmp_ctx, + private->samdb, + MSV1_0_ALLOW_SERVER_TRUST_ACCOUNT | MSV1_0_ALLOW_WORKSTATION_TRUST_ACCOUNT, + private->msg, + private->realm_ref_msg, + workstation, + name); + free(name); + + /* TODO: Need a more complete mapping of NTSTATUS to krb5kdc errors */ + + if (!NT_STATUS_IS_OK(nt_status)) { + return KRB5KDC_ERR_POLICY; + } + return 0; +} + diff --git a/source4/kdc/pac-glue.h b/source4/kdc/pac-glue.h index 69490bb7f3c..953ddae815f 100644 --- a/source4/kdc/pac-glue.h +++ b/source4/kdc/pac-glue.h @@ -1,8 +1,46 @@ +/* + Unix SMB/CIFS implementation. - krb5_error_code samba_get_pac(krb5_context context, - struct krb5_kdc_configuration *config, - krb5_principal client, - krb5_keyblock *krbtgt_keyblock, - krb5_keyblock *server_keyblock, - time_t tgs_authtime, - krb5_data *pac); + PAC Glue between Samba and the KDC + + Copyright (C) Andrew Bartlett <abartlet@samba.org> 2005 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + + struct hdb_ldb_private { + struct ldb_context *samdb; + struct ldb_message *msg; + struct ldb_message *realm_ref_msg; + }; + + krb5_error_code hdb_ldb_authz_data_as_req(krb5_context context, struct hdb_entry_ex *entry_ex, + METHOD_DATA* pa_data_seq, + time_t authtime, + EncryptionKey *tgtkey, + EncryptionKey *sessionkey, + AuthorizationData **out); + + krb5_error_code hdb_ldb_authz_data_tgs_req(krb5_context context, struct hdb_entry_ex *entry_ex, + krb5_principal client, + AuthorizationData *in, + time_t authtime, + EncryptionKey *tgtkey, + EncryptionKey *servicekey, + EncryptionKey *sessionkey, + AuthorizationData **out); + krb5_error_code hdb_ldb_check_client_access(krb5_context context, hdb_entry_ex *entry_ex, + HostAddresses *addresses); |