summaryrefslogtreecommitdiff
path: root/source4/kdc/pac-glue.c
diff options
context:
space:
mode:
Diffstat (limited to 'source4/kdc/pac-glue.c')
-rw-r--r--source4/kdc/pac-glue.c317
1 files changed, 293 insertions, 24 deletions
diff --git a/source4/kdc/pac-glue.c b/source4/kdc/pac-glue.c
index ae306b4062..03b53fa3af 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;
+}
+