summaryrefslogtreecommitdiff
path: root/source4/kdc
diff options
context:
space:
mode:
authorStefan Metzmacher <metze@samba.org>2016-05-12 23:20:39 +0200
committerAndrew Bartlett <abartlet@samba.org>2016-07-22 23:34:21 +0200
commitaf4dc2231446b292cec91b1853a991df7c97b8e2 (patch)
tree3a789b2bd8ffe79e073078704f3739d8448c65ca /source4/kdc
parentc2b7bac37927d2532e1ca0ddc15780ebd5557533 (diff)
downloadsamba-af4dc2231446b292cec91b1853a991df7c97b8e2.tar.gz
s4:kdc: provide a PAC_CREDENTIAL_INFO element for PKINIT logons
BUG: https://bugzilla.samba.org/show_bug.cgi?id=11441 Signed-off-by: Stefan Metzmacher <metze@samba.org> Reviewed-by: Andrew Bartlett <abartlet@samba.org>
Diffstat (limited to 'source4/kdc')
-rw-r--r--source4/kdc/pac-glue.c325
-rw-r--r--source4/kdc/pac-glue.h17
-rw-r--r--source4/kdc/wdc-samba4.c40
3 files changed, 351 insertions, 31 deletions
diff --git a/source4/kdc/pac-glue.c b/source4/kdc/pac-glue.c
index af77fd4ec64..0b9fd12b764 100644
--- a/source4/kdc/pac-glue.c
+++ b/source4/kdc/pac-glue.c
@@ -48,6 +48,8 @@ NTSTATUS samba_get_logon_info_pac_blob(TALLOC_CTX *mem_ctx,
ZERO_STRUCT(pac_info);
+ *pac_data = data_blob_null;
+
nt_status = auth_convert_user_info_dc_saminfo3(mem_ctx, info, &info3);
if (!NT_STATUS_IS_OK(nt_status)) {
DEBUG(1, ("Getting Samba info failed: %s\n",
@@ -67,7 +69,7 @@ NTSTATUS samba_get_logon_info_pac_blob(TALLOC_CTX *mem_ctx,
(ndr_push_flags_fn_t)ndr_push_PAC_INFO);
if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
nt_status = ndr_map_error2ntstatus(ndr_err);
- DEBUG(1, ("PAC (presig) push failed: %s\n",
+ DEBUG(1, ("PAC_LOGON_INFO (presig) push failed: %s\n",
nt_errstr(nt_status)));
return nt_status;
}
@@ -75,53 +77,280 @@ NTSTATUS samba_get_logon_info_pac_blob(TALLOC_CTX *mem_ctx,
return NT_STATUS_OK;
}
+static
+NTSTATUS samba_get_cred_info_ndr_blob(TALLOC_CTX *mem_ctx,
+ const struct ldb_message *msg,
+ DATA_BLOB *cred_blob)
+{
+ enum ndr_err_code ndr_err;
+ NTSTATUS nt_status;
+ int ret;
+ static const struct samr_Password zero_hash;
+ struct samr_Password *lm_hash = NULL;
+ struct samr_Password *nt_hash = NULL;
+ struct PAC_CREDENTIAL_NTLM_SECPKG ntlm_secpkg = {
+ .version = 0,
+ };
+ DATA_BLOB ntlm_blob = data_blob_null;
+ struct PAC_CREDENTIAL_SUPPLEMENTAL_SECPKG secpkgs[1] = {{
+ .credential_size = 0,
+ }};
+ struct PAC_CREDENTIAL_DATA cred_data = {
+ .credential_count = 0,
+ };
+ struct PAC_CREDENTIAL_DATA_NDR cred_ndr;
+
+ ZERO_STRUCT(cred_ndr);
+
+ *cred_blob = data_blob_null;
+
+ lm_hash = samdb_result_hash(mem_ctx, msg, "dBCSPwd");
+ if (lm_hash != NULL) {
+ ret = memcmp(lm_hash->hash, zero_hash.hash, 16);
+ if (ret == 0) {
+ lm_hash = NULL;
+ }
+ }
+ if (lm_hash != NULL) {
+ DEBUG(5, ("Passing LM password hash through credentials set\n"));
+ ntlm_secpkg.flags |= PAC_CREDENTIAL_NTLM_HAS_LM_HASH;
+ ntlm_secpkg.lm_password = *lm_hash;
+ ZERO_STRUCTP(lm_hash);
+ TALLOC_FREE(lm_hash);
+ }
+
+ nt_hash = samdb_result_hash(mem_ctx, msg, "unicodePwd");
+ if (nt_hash != NULL) {
+ ret = memcmp(nt_hash->hash, zero_hash.hash, 16);
+ if (ret == 0) {
+ nt_hash = NULL;
+ }
+ }
+ if (nt_hash != NULL) {
+ DEBUG(5, ("Passing LM password hash through credentials set\n"));
+ ntlm_secpkg.flags |= PAC_CREDENTIAL_NTLM_HAS_NT_HASH;
+ ntlm_secpkg.nt_password = *nt_hash;
+ ZERO_STRUCTP(nt_hash);
+ TALLOC_FREE(nt_hash);
+ }
+
+ if (ntlm_secpkg.flags == 0) {
+ return NT_STATUS_OK;
+ }
+
+#ifdef DEBUG_PASSWORD
+ if (DEBUGLVL(11)) {
+ NDR_PRINT_DEBUG(PAC_CREDENTIAL_NTLM_SECPKG, &ntlm_secpkg);
+ }
+#endif
+
+ ndr_err = ndr_push_struct_blob(&ntlm_blob, mem_ctx, &ntlm_secpkg,
+ (ndr_push_flags_fn_t)ndr_push_PAC_CREDENTIAL_NTLM_SECPKG);
+ ZERO_STRUCT(ntlm_secpkg);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ nt_status = ndr_map_error2ntstatus(ndr_err);
+ DEBUG(1, ("PAC_CREDENTIAL_NTLM_SECPKG (presig) push failed: %s\n",
+ nt_errstr(nt_status)));
+ return nt_status;
+ }
+
+ DEBUG(10, ("NTLM credential BLOB (len %zu) for user\n",
+ ntlm_blob.length));
+ dump_data_pw("PAC_CREDENTIAL_NTLM_SECPKG",
+ ntlm_blob.data, ntlm_blob.length);
+
+ secpkgs[0].package_name.string = discard_const_p(char, "NTLM");
+ secpkgs[0].credential_size = ntlm_blob.length;
+ secpkgs[0].credential = ntlm_blob.data;
+
+ cred_data.credential_count = ARRAY_SIZE(secpkgs);
+ cred_data.credentials = secpkgs;
+
+#ifdef DEBUG_PASSWORD
+ if (DEBUGLVL(11)) {
+ NDR_PRINT_DEBUG(PAC_CREDENTIAL_DATA, &cred_data);
+ }
+#endif
+
+ cred_ndr.ctr.data = &cred_data;
+
+#ifdef DEBUG_PASSWORD
+ if (DEBUGLVL(11)) {
+ NDR_PRINT_DEBUG(PAC_CREDENTIAL_DATA_NDR, &cred_ndr);
+ }
+#endif
+
+ ndr_err = ndr_push_struct_blob(cred_blob, mem_ctx, &cred_ndr,
+ (ndr_push_flags_fn_t)ndr_push_PAC_CREDENTIAL_DATA_NDR);
+ data_blob_clear(&ntlm_blob);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ nt_status = ndr_map_error2ntstatus(ndr_err);
+ DEBUG(1, ("PAC_CREDENTIAL_DATA_NDR (presig) push failed: %s\n",
+ nt_errstr(nt_status)));
+ return nt_status;
+ }
+
+ DEBUG(10, ("Created credential BLOB (len %zu) for user\n",
+ cred_blob->length));
+ dump_data_pw("PAC_CREDENTIAL_DATA_NDR",
+ cred_blob->data, cred_blob->length);
+
+ return NT_STATUS_OK;
+}
+
+krb5_error_code samba_kdc_encrypt_pac_credentials(krb5_context context,
+ const krb5_keyblock *pkreplykey,
+ const DATA_BLOB *cred_ndr_blob,
+ TALLOC_CTX *mem_ctx,
+ DATA_BLOB *cred_info_blob)
+{
+ krb5_crypto cred_crypto;
+ krb5_enctype cred_enctype;
+ krb5_data cred_ndr_crypt;
+ struct PAC_CREDENTIAL_INFO pac_cred_info = { .version = 0, };
+ krb5_error_code ret;
+ const char *krb5err;
+ enum ndr_err_code ndr_err;
+ NTSTATUS nt_status;
+
+ *cred_info_blob = data_blob_null;
+
+ ret = krb5_crypto_init(context, pkreplykey, ETYPE_NULL,
+ &cred_crypto);
+ if (ret != 0) {
+ krb5err = krb5_get_error_message(context, ret);
+ DEBUG(1, ("Failed initializing cred data crypto: %s\n", krb5err));
+ krb5_free_error_message(context, krb5err);
+ return ret;
+ }
+
+ ret = krb5_crypto_getenctype(context, cred_crypto, &cred_enctype);
+ if (ret != 0) {
+ DEBUG(1, ("Failed getting crypto type for key\n"));
+ krb5_crypto_destroy(context, cred_crypto);
+ return ret;
+ }
+
+ DEBUG(10, ("Plain cred_ndr_blob (len %zu)\n",
+ cred_ndr_blob->length));
+ dump_data_pw("PAC_CREDENTIAL_DATA_NDR",
+ cred_ndr_blob->data, cred_ndr_blob->length);
+
+ ret = krb5_encrypt(context, cred_crypto,
+ KRB5_KU_OTHER_ENCRYPTED,
+ cred_ndr_blob->data, cred_ndr_blob->length,
+ &cred_ndr_crypt);
+ krb5_crypto_destroy(context, cred_crypto);
+ if (ret != 0) {
+ krb5err = krb5_get_error_message(context, ret);
+ DEBUG(1, ("Failed crypt of cred data: %s\n", krb5err));
+ krb5_free_error_message(context, krb5err);
+ return ret;
+ }
+
+ pac_cred_info.encryption_type = cred_enctype;
+ pac_cred_info.encrypted_data.length = cred_ndr_crypt.length;
+ pac_cred_info.encrypted_data.data = (uint8_t *)cred_ndr_crypt.data;
+
+ if (DEBUGLVL(10)) {
+ NDR_PRINT_DEBUG(PAC_CREDENTIAL_INFO, &pac_cred_info);
+ }
+
+ ndr_err = ndr_push_struct_blob(cred_info_blob, mem_ctx, &pac_cred_info,
+ (ndr_push_flags_fn_t)ndr_push_PAC_CREDENTIAL_INFO);
+ krb5_data_free(&cred_ndr_crypt);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ nt_status = ndr_map_error2ntstatus(ndr_err);
+ DEBUG(1, ("PAC_CREDENTIAL_INFO (presig) push failed: %s\n",
+ nt_errstr(nt_status)));
+ return KRB5KDC_ERR_SVC_UNAVAILABLE;
+ }
+
+ DEBUG(10, ("Encrypted credential BLOB (len %zu) with alg %d\n",
+ cred_info_blob->length, (int)pac_cred_info.encryption_type));
+ dump_data_pw("PAC_CREDENTIAL_INFO",
+ cred_info_blob->data, cred_info_blob->length);
+
+ return 0;
+}
+
krb5_error_code samba_make_krb5_pac(krb5_context context,
- DATA_BLOB *pac_blob,
- DATA_BLOB *deleg_blob,
+ const DATA_BLOB *logon_blob,
+ const DATA_BLOB *cred_blob,
+ const DATA_BLOB *deleg_blob,
krb5_pac *pac)
{
- krb5_data pac_data;
+ krb5_data logon_data;
+ krb5_data cred_data;
krb5_data deleg_data;
+ krb5_data null_data;
krb5_error_code ret;
- /* The user account may be set not to want the PAC */
- if (!pac_blob) {
+ ZERO_STRUCT(null_data);
+
+ /* The user account may be set not to want the PAC */
+ if (logon_blob == NULL) {
return 0;
}
- ret = krb5_copy_data_contents(&pac_data,
- pac_blob->data,
- pac_blob->length);
+ ret = krb5_copy_data_contents(&logon_data,
+ logon_blob->data,
+ logon_blob->length);
if (ret != 0) {
return ret;
}
+ ZERO_STRUCT(cred_data);
+ if (cred_blob != NULL) {
+ ret = krb5_copy_data_contents(&cred_data,
+ cred_blob->data,
+ cred_blob->length);
+ if (ret != 0) {
+ kerberos_free_data_contents(context, &logon_data);
+ return ret;
+ }
+ }
+
ZERO_STRUCT(deleg_data);
- if (deleg_blob) {
+ if (deleg_blob != NULL) {
ret = krb5_copy_data_contents(&deleg_data,
deleg_blob->data,
deleg_blob->length);
if (ret != 0) {
- kerberos_free_data_contents(context, &pac_data);
+ kerberos_free_data_contents(context, &logon_data);
+ kerberos_free_data_contents(context, &cred_data);
return ret;
}
}
ret = krb5_pac_init(context, pac);
if (ret != 0) {
- kerberos_free_data_contents(context, &pac_data);
+ kerberos_free_data_contents(context, &logon_data);
+ kerberos_free_data_contents(context, &cred_data);
kerberos_free_data_contents(context, &deleg_data);
return ret;
}
- ret = krb5_pac_add_buffer(context, *pac, PAC_TYPE_LOGON_INFO, &pac_data);
- kerberos_free_data_contents(context, &pac_data);
+ ret = krb5_pac_add_buffer(context, *pac, PAC_TYPE_LOGON_INFO, &logon_data);
+ kerberos_free_data_contents(context, &logon_data);
if (ret != 0) {
+ kerberos_free_data_contents(context, &cred_data);
kerberos_free_data_contents(context, &deleg_data);
return ret;
}
- if (deleg_blob) {
+ if (cred_blob != NULL) {
+ ret = krb5_pac_add_buffer(context, *pac,
+ PAC_TYPE_CREDENTIAL_INFO,
+ &cred_data);
+ kerberos_free_data_contents(context, &cred_data);
+ if (ret != 0) {
+ kerberos_free_data_contents(context, &deleg_data);
+ return ret;
+ }
+ }
+
+ if (deleg_blob != NULL) {
ret = krb5_pac_add_buffer(context, *pac,
PAC_TYPE_CONSTRAINED_DELEGATION,
&deleg_data);
@@ -219,25 +448,38 @@ int samba_krbtgt_is_in_db(struct samba_kdc_entry *p,
return 0;
}
-NTSTATUS samba_kdc_get_pac_blob(TALLOC_CTX *mem_ctx,
- struct samba_kdc_entry *p,
- DATA_BLOB **_pac_blob)
+NTSTATUS samba_kdc_get_pac_blobs(TALLOC_CTX *mem_ctx,
+ struct samba_kdc_entry *p,
+ DATA_BLOB **_logon_info_blob,
+ DATA_BLOB **_cred_ndr_blob)
{
struct auth_user_info_dc *user_info_dc;
- DATA_BLOB *pac_blob;
+ DATA_BLOB *logon_blob = NULL;
+ DATA_BLOB *cred_blob = NULL;
NTSTATUS nt_status;
+ *_logon_info_blob = NULL;
+ if (_cred_ndr_blob != NULL) {
+ *_cred_ndr_blob = NULL;
+ }
+
/* The user account may be set not to want the PAC */
if ( ! samba_princ_needs_pac(p)) {
- *_pac_blob = NULL;
return NT_STATUS_OK;
}
- pac_blob = talloc_zero(mem_ctx, DATA_BLOB);
- if (!pac_blob) {
+ logon_blob = talloc_zero(mem_ctx, DATA_BLOB);
+ if (logon_blob == NULL) {
return NT_STATUS_NO_MEMORY;
}
+ if (_cred_ndr_blob != NULL) {
+ cred_blob = talloc_zero(mem_ctx, DATA_BLOB);
+ if (cred_blob == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ }
+
nt_status = authsam_make_user_info_dc(mem_ctx, p->kdc_db_ctx->samdb,
lpcfg_netbios_name(p->kdc_db_ctx->lp_ctx),
lpcfg_sam_name(p->kdc_db_ctx->lp_ctx),
@@ -253,14 +495,47 @@ NTSTATUS samba_kdc_get_pac_blob(TALLOC_CTX *mem_ctx,
return nt_status;
}
- nt_status = samba_get_logon_info_pac_blob(mem_ctx, user_info_dc, pac_blob);
+ nt_status = samba_get_logon_info_pac_blob(logon_blob,
+ user_info_dc,
+ logon_blob);
if (!NT_STATUS_IS_OK(nt_status)) {
- DEBUG(0, ("Building PAC failed: %s\n",
+ DEBUG(0, ("Building PAC LOGON INFO failed: %s\n",
nt_errstr(nt_status)));
return nt_status;
}
- *_pac_blob = pac_blob;
+ if (cred_blob != NULL) {
+ nt_status = samba_get_cred_info_ndr_blob(cred_blob,
+ p->msg,
+ cred_blob);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ DEBUG(0, ("Building PAC CRED INFO failed: %s\n",
+ nt_errstr(nt_status)));
+ return nt_status;
+ }
+ }
+
+ TALLOC_FREE(user_info_dc);
+ *_logon_info_blob = logon_blob;
+ if (_cred_ndr_blob != NULL) {
+ *_cred_ndr_blob = cred_blob;
+ }
+ return NT_STATUS_OK;
+}
+
+NTSTATUS samba_kdc_get_pac_blob(TALLOC_CTX *mem_ctx,
+ struct samba_kdc_entry *p,
+ DATA_BLOB **_logon_info_blob)
+{
+ NTSTATUS nt_status;
+
+ nt_status = samba_kdc_get_pac_blobs(mem_ctx, p,
+ _logon_info_blob,
+ NULL);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ return nt_status;
+ }
+
return NT_STATUS_OK;
}
diff --git a/source4/kdc/pac-glue.h b/source4/kdc/pac-glue.h
index cf1f6d2b6c1..22f96c8d425 100644
--- a/source4/kdc/pac-glue.h
+++ b/source4/kdc/pac-glue.h
@@ -21,9 +21,16 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
+krb5_error_code samba_kdc_encrypt_pac_credentials(krb5_context context,
+ const krb5_keyblock *pkreplykey,
+ const DATA_BLOB *cred_ndr_blob,
+ TALLOC_CTX *mem_ctx,
+ DATA_BLOB *cred_info_blob);
+
krb5_error_code samba_make_krb5_pac(krb5_context context,
- DATA_BLOB *pac_blob,
- DATA_BLOB *deleg_blob,
+ const DATA_BLOB *logon_blob,
+ const DATA_BLOB *cred_blob,
+ const DATA_BLOB *deleg_blob,
krb5_pac *pac);
bool samba_princ_needs_pac(struct samba_kdc_entry *skdc_entry);
@@ -32,9 +39,13 @@ int samba_krbtgt_is_in_db(struct samba_kdc_entry *skdc_entry,
bool *is_in_db,
bool *is_untrusted);
+NTSTATUS samba_kdc_get_pac_blobs(TALLOC_CTX *mem_ctx,
+ struct samba_kdc_entry *skdc_entry,
+ DATA_BLOB **_logon_info_blob,
+ DATA_BLOB **_cred_ndr_blob);
NTSTATUS samba_kdc_get_pac_blob(TALLOC_CTX *mem_ctx,
struct samba_kdc_entry *skdc_entry,
- DATA_BLOB **_pac_blob);
+ DATA_BLOB **_logon_info_blob);
NTSTATUS samba_kdc_update_pac_blob(TALLOC_CTX *mem_ctx,
krb5_context context,
diff --git a/source4/kdc/wdc-samba4.c b/source4/kdc/wdc-samba4.c
index 7fa5d2c36dd..3c0a012fb80 100644
--- a/source4/kdc/wdc-samba4.c
+++ b/source4/kdc/wdc-samba4.c
@@ -37,7 +37,11 @@ static krb5_error_code samba_wdc_get_pac(void *priv, krb5_context context,
krb5_pac *pac)
{
TALLOC_CTX *mem_ctx;
- DATA_BLOB *pac_blob;
+ DATA_BLOB *logon_blob = NULL;
+ DATA_BLOB *cred_ndr = NULL;
+ DATA_BLOB **cred_ndr_ptr = NULL;
+ DATA_BLOB _cred_blob = data_blob_null;
+ DATA_BLOB *cred_blob = NULL;
krb5_error_code ret;
NTSTATUS nt_status;
struct samba_kdc_entry *skdc_entry =
@@ -49,13 +53,33 @@ static krb5_error_code samba_wdc_get_pac(void *priv, krb5_context context,
return ENOMEM;
}
- nt_status = samba_kdc_get_pac_blob(mem_ctx, skdc_entry, &pac_blob);
+ if (pk_reply_key != NULL) {
+ cred_ndr_ptr = &cred_ndr;
+ }
+
+ nt_status = samba_kdc_get_pac_blobs(mem_ctx, skdc_entry,
+ &logon_blob,
+ cred_ndr_ptr);
if (!NT_STATUS_IS_OK(nt_status)) {
talloc_free(mem_ctx);
return EINVAL;
}
- ret = samba_make_krb5_pac(context, pac_blob, NULL, pac);
+ if (pk_reply_key != NULL && cred_ndr != NULL) {
+ ret = samba_kdc_encrypt_pac_credentials(context,
+ pk_reply_key,
+ cred_ndr,
+ mem_ctx,
+ &_cred_blob);
+ if (ret != 0) {
+ talloc_free(mem_ctx);
+ return ret;
+ }
+ cred_blob = &_cred_blob;
+ }
+
+ ret = samba_make_krb5_pac(context, logon_blob, cred_blob,
+ NULL, pac);
talloc_free(mem_ctx);
return ret;
@@ -337,6 +361,16 @@ static krb5_error_code samba_wdc_reget_pac(void *priv, krb5_context context,
type_blob = *deleg_blob;
}
break;
+ case PAC_TYPE_CREDENTIAL_INFO:
+ /*
+ * Note that we copy the credential blob,
+ * as it's only usable with the PKINIT based
+ * AS-REP reply key, it's only available on the
+ * host which did the AS-REQ/AS-REP exchange.
+ *
+ * This matches Windows 2008R2...
+ */
+ break;
case PAC_TYPE_LOGON_NAME:
/*
* this is generated in the main KDC code