summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndrew Bartlett <abartlet@samba.org>2009-07-28 14:05:19 +1000
committerAndrew Bartlett <abartlet@samba.org>2009-07-28 14:10:47 +1000
commit8ff1f50b0c47f7ff92d557ef4caf64a44b387ab4 (patch)
tree98f832f301c2e5c92a1391f1ae2bb9a6a1590e52
parent47a7a2e442c7e006eca8188c6a01707d85c4e61c (diff)
downloadsamba-8ff1f50b0c47f7ff92d557ef4caf64a44b387ab4.tar.gz
samba-8ff1f50b0c47f7ff92d557ef4caf64a44b387ab4.tar.bz2
samba-8ff1f50b0c47f7ff92d557ef4caf64a44b387ab4.zip
s4:kerberos Add support for user principal names in certificates
This extends the PKINIT code in Heimdal to ask the HDB layer if the User Principal Name name in the certificate is an alias (perhaps just by case change) of the name given in the AS-REQ. (This was a TODO in the Heimdal KDC) The testsuite is extended to test this behaviour, and the other PKINIT certficate (using the standard method to specify a principal name in a certificate) is updated to use a Administrator (not administrator). (This fixes the kinit test). Andrew Bartlett
-rw-r--r--selftest/target/Samba4.pm42
-rw-r--r--source4/auth/ntlm/auth_sam.c2
-rw-r--r--source4/auth/sam.c6
-rw-r--r--source4/heimdal/kdc/kerberos5.c1
-rw-r--r--source4/heimdal/kdc/pkinit.c38
-rw-r--r--source4/heimdal/lib/hdb/hdb.h7
-rw-r--r--source4/kdc/hdb-samba4.c103
-rwxr-xr-xtestprogs/blackbox/test_kinit.sh4
8 files changed, 161 insertions, 42 deletions
diff --git a/selftest/target/Samba4.pm b/selftest/target/Samba4.pm
index 7833bf4479..d2c11e4f32 100644
--- a/selftest/target/Samba4.pm
+++ b/selftest/target/Samba4.pm
@@ -295,6 +295,7 @@ sub mk_keyblobs($$)
my $adminkeyfile = "$tlsdir/adminkey.pem";
my $reqadmin = "$tlsdir/req-admin.der";
my $admincertfile = "$tlsdir/admincert.pem";
+ my $admincertupnfile = "$tlsdir/admincertupn.pem";
mkdir($tlsdir, 0777);
@@ -442,24 +443,51 @@ EOF
open(ADMINCERTFILE, ">$admincertfile");
print ADMINCERTFILE <<EOF;
-----BEGIN CERTIFICATE-----
-MIIDHTCCAoagAwIBAgIUC0W5dW/N9kE+NgD0mKK34YgyqQ0wCwYJKoZIhvcNAQEFMFIxEzAR
+MIIDHTCCAoagAwIBAgIUUggzW4lLRkMKe1DAR2NKatkMDYwwCwYJKoZIhvcNAQELMFIxEzAR
BgoJkiaJk/IsZAEZDANjb20xFzAVBgoJkiaJk/IsZAEZDAdleGFtcGxlMRUwEwYKCZImiZPy
-LGQBGQwFc2FtYmExCzAJBgNVBAMMAkNBMCIYDzIwMDgwMzAxMTMyMzAwWhgPMjAzMzAyMjQx
-MzIzMDBaMG0xEzARBgoJkiaJk/IsZAEZDANjb20xFzAVBgoJkiaJk/IsZAEZDAdleGFtcGxl
+LGQBGQwFc2FtYmExCzAJBgNVBAMMAkNBMCIYDzIwMDkwNzI3MDMzMjE1WhgPMjAzNDA3MjIw
+MzMyMTVaMG0xEzARBgoJkiaJk/IsZAEZDANjb20xFzAVBgoJkiaJk/IsZAEZDAdleGFtcGxl
MRUwEwYKCZImiZPyLGQBGQwFc2FtYmExDjAMBgNVBAMMBXVzZXJzMRYwFAYDVQQDDA1BZG1p
bmlzdHJhdG9yMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQD0+OL7TQBj0RejbIH1+g5G
eRaWaM9xF43uE5y7jUHEsi5owhZF5iIoHZeeL6cpDF5y1BZRs0JlA1VqMry1jjKlzFYVEMMF
xB6esnXhl0Jpip1JkUMMXLOP1m/0dqayuHBWozj9f/cdyCJr0wJIX1Z8Pr+EjYRGPn/MF0xd
l3JRlwIDAQABo4HSMIHPMA4GA1UdDwEB/wQEAwIFoDAoBgNVHSUEITAfBgcrBgEFAgMEBggr
BgEFBQcDAgYKKwYBBAGCNxQCAjBIBgNVHREEQTA/oD0GBisGAQUCAqAzMDGgExsRU0FNQkEu
-RVhBTVBMRS5DT02hGjAYoAMCAQGhETAPGw1hZG1pbmlzdHJhdG9yMB8GA1UdIwQYMBaAFMLZ
+RVhBTVBMRS5DT02hGjAYoAMCAQGhETAPGw1BZG1pbmlzdHJhdG9yMB8GA1UdIwQYMBaAFMLZ
ufegDKLZs0VOyFXYK1L6M8oyMB0GA1UdDgQWBBQg81bLyfCA88C2B/BDjXlGuaFaxjAJBgNV
-HRMEAjAAMA0GCSqGSIb3DQEBBQUAA4GBAHsqSqul0hZCXn4t8Kfp3v/JLMiUMJihR1XOgzoa
-ufLOQ1HNzFUHKuo1JEQ1+i5gHT/arLu/ZBF4BfQol7vW27gKIEt0fkRV8EvoPxXvSokHq0Ku
-HCuPOhYNEP3wYiwB3g93NMCinWVlz0mh5aijEU7y/XrjlZxBKFFrTE+BJi1o
+HRMEAjAAMA0GCSqGSIb3DQEBCwUAA4GBAEf/OSHUDJaGdtWGNuJeqcVYVMwrfBAc0OSwVhz1
+7/xqKHWo8wIMPkYRtaRHKLNDsF8GkhQPCpVsa6mX/Nt7YQnNvwd+1SBP5E8GvwWw9ZzLJvma
+nk2n89emuayLpVtp00PymrDLRBcNaRjFReQU8f0o509kiVPHduAp3jOiy13l
-----END CERTIFICATE-----
EOF
close(ADMINCERTFILE);
+
+ # hxtool issue-certificate --ca-certificate=FILE:$CAFILE,$KEYFILE \
+ # --type="pkinit-client" \
+ # --ms-upn="administrator@samba.example.com" \
+ # --req="PKCS10:$ADMINREQFILE" --certificate="FILE:$ADMINCERTUPNFILE" \
+ # --lifetime="25 years"
+
+ open(ADMINCERTUPNFILE, ">$admincertupnfile");
+ print ADMINCERTUPNFILE <<EOF;
+-----BEGIN CERTIFICATE-----
+MIIDDzCCAnigAwIBAgIUUp3CJMuNaEaAdPKp3QdNIwG7a4wwCwYJKoZIhvcNAQELMFIxEzAR
+BgoJkiaJk/IsZAEZDANjb20xFzAVBgoJkiaJk/IsZAEZDAdleGFtcGxlMRUwEwYKCZImiZPy
+LGQBGQwFc2FtYmExCzAJBgNVBAMMAkNBMCIYDzIwMDkwNzI3MDMzMzA1WhgPMjAzNDA3MjIw
+MzMzMDVaMG0xEzARBgoJkiaJk/IsZAEZDANjb20xFzAVBgoJkiaJk/IsZAEZDAdleGFtcGxl
+MRUwEwYKCZImiZPyLGQBGQwFc2FtYmExDjAMBgNVBAMMBXVzZXJzMRYwFAYDVQQDDA1BZG1p
+bmlzdHJhdG9yMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQD0+OL7TQBj0RejbIH1+g5G
+eRaWaM9xF43uE5y7jUHEsi5owhZF5iIoHZeeL6cpDF5y1BZRs0JlA1VqMry1jjKlzFYVEMMF
+xB6esnXhl0Jpip1JkUMMXLOP1m/0dqayuHBWozj9f/cdyCJr0wJIX1Z8Pr+EjYRGPn/MF0xd
+l3JRlwIDAQABo4HEMIHBMA4GA1UdDwEB/wQEAwIFoDAoBgNVHSUEITAfBgcrBgEFAgMEBggr
+BgEFBQcDAgYKKwYBBAGCNxQCAjA6BgNVHREEMzAxoC8GCisGAQQBgjcUAgOgIQwfYWRtaW5p
+c3RyYXRvckBzYW1iYS5leGFtcGxlLmNvbTAfBgNVHSMEGDAWgBTC2bn3oAyi2bNFTshV2CtS
++jPKMjAdBgNVHQ4EFgQUIPNWy8nwgPPAtgfwQ415RrmhWsYwCQYDVR0TBAIwADANBgkqhkiG
+9w0BAQsFAAOBgQBk42+egeUB3Ji2PC55fbt3FNKxvmm2xUUFkV9POK/YR9rajKOwk5jtYSeS
+Zd7J9s//rNFNa7waklFkDaY56+QWTFtdvxfE+KoHaqt6X8u6pqi7p3M4wDKQox+9Dx8yWFyq
+Wfz/8alZ5aMezCQzXJyIaJsCLeKABosSwHcpAFmxlQ==
+-----END CERTIFICATE-----
+EOF
}
#
diff --git a/source4/auth/ntlm/auth_sam.c b/source4/auth/ntlm/auth_sam.c
index 253ddf2286..a64c56d920 100644
--- a/source4/auth/ntlm/auth_sam.c
+++ b/source4/auth/ntlm/auth_sam.c
@@ -330,7 +330,7 @@ NTSTATUS authsam_get_server_info_principal(TALLOC_CTX *mem_ctx,
}
nt_status = sam_get_results_principal(sam_ctx, tmp_ctx, principal,
- &domain_dn, &msg);
+ user_attrs, &domain_dn, &msg);
if (!NT_STATUS_IS_OK(nt_status)) {
return nt_status;
}
diff --git a/source4/auth/sam.c b/source4/auth/sam.c
index 635d94242f..8865170b14 100644
--- a/source4/auth/sam.c
+++ b/source4/auth/sam.c
@@ -399,6 +399,7 @@ _PUBLIC_ NTSTATUS authsam_make_server_info(TALLOC_CTX *mem_ctx, struct ldb_conte
NTSTATUS sam_get_results_principal(struct ldb_context *sam_ctx,
TALLOC_CTX *mem_ctx, const char *principal,
+ const char **attrs,
struct ldb_dn **domain_dn,
struct ldb_message **msg)
{
@@ -411,7 +412,8 @@ NTSTATUS sam_get_results_principal(struct ldb_context *sam_ctx,
return NT_STATUS_NO_MEMORY;
}
- nt_status = crack_user_principal_name(sam_ctx, tmp_ctx, principal, &user_dn, domain_dn);
+ nt_status = crack_user_principal_name(sam_ctx, tmp_ctx, principal,
+ &user_dn, domain_dn);
if (!NT_STATUS_IS_OK(nt_status)) {
talloc_free(tmp_ctx);
return nt_status;
@@ -419,7 +421,7 @@ NTSTATUS sam_get_results_principal(struct ldb_context *sam_ctx,
/* pull the user attributes */
ret = gendb_search_single_extended_dn(sam_ctx, tmp_ctx, user_dn, LDB_SCOPE_BASE,
- msg, user_attrs, "(objectClass=*)");
+ msg, attrs, "(objectClass=*)");
if (ret != LDB_SUCCESS) {
talloc_free(tmp_ctx);
return NT_STATUS_INTERNAL_DB_CORRUPTION;
diff --git a/source4/heimdal/kdc/kerberos5.c b/source4/heimdal/kdc/kerberos5.c
index 43d54bf702..53e9f54537 100644
--- a/source4/heimdal/kdc/kerberos5.c
+++ b/source4/heimdal/kdc/kerberos5.c
@@ -1053,6 +1053,7 @@ _kdc_as_rep(krb5_context context,
ret = _kdc_pk_check_client(context,
config,
+ clientdb,
client,
pkp,
&client_cert);
diff --git a/source4/heimdal/kdc/pkinit.c b/source4/heimdal/kdc/pkinit.c
index 22734be811..644eae0fe4 100644
--- a/source4/heimdal/kdc/pkinit.c
+++ b/source4/heimdal/kdc/pkinit.c
@@ -1613,11 +1613,12 @@ match_ms_upn_san(krb5_context context,
krb5_kdc_configuration *config,
hx509_context hx509ctx,
hx509_cert client_cert,
- krb5_const_principal match)
+ HDB *clientdb,
+ hdb_entry_ex *client)
{
hx509_octet_string_list list;
krb5_principal principal = NULL;
- int ret, found = 0;
+ int ret;
MS_UPN_SAN upn;
size_t size;
@@ -1651,32 +1652,32 @@ match_ms_upn_san(krb5_context context,
goto out;
}
- /*
- * This is very wrong, but will do for now, should really and a
- * plugin to the windc layer to very this ACL.
- */
- strupr(principal->realm);
-
- if (krb5_principal_compare(context, principal, match) == TRUE)
- found = 1;
+ if (clientdb->hdb_check_pkinit_ms_upn_match) {
+ ret = clientdb->hdb_check_pkinit_ms_upn_match(context, clientdb, client, principal);
+ } else {
+
+ /*
+ * This is very wrong, but will do for a fallback
+ */
+ strupr(principal->realm);
+
+ if (krb5_principal_compare(context, principal, client->entry.principal) == FALSE)
+ ret = KRB5_KDC_ERR_CLIENT_NAME_MISMATCH;
+ }
out:
if (principal)
krb5_free_principal(context, principal);
hx509_free_octet_string_list(&list);
- if (ret)
- return ret;
-
- if (!found)
- return KRB5_KDC_ERR_CLIENT_NAME_MISMATCH;
- return 0;
+ return ret;
}
krb5_error_code
_kdc_pk_check_client(krb5_context context,
krb5_kdc_configuration *config,
- const hdb_entry_ex *client,
+ HDB *clientdb,
+ hdb_entry_ex *client,
pk_client_params *cp,
char **subject_name)
{
@@ -1745,7 +1746,8 @@ _kdc_pk_check_client(krb5_context context,
ret = match_ms_upn_san(context, config,
kdc_identity->hx509ctx,
cp->cert,
- client->entry.principal);
+ clientdb,
+ client);
if (ret == 0) {
kdc_log(context, config, 5,
"Found matching MS UPN SAN in certificate");
diff --git a/source4/heimdal/lib/hdb/hdb.h b/source4/heimdal/lib/hdb/hdb.h
index f490dbf2f0..8eba864fd3 100644
--- a/source4/heimdal/lib/hdb/hdb.h
+++ b/source4/heimdal/lib/hdb/hdb.h
@@ -220,9 +220,14 @@ typedef struct HDB{
* Check is delegation is allowed.
*/
krb5_error_code (*hdb_check_constrained_delegation)(krb5_context, struct HDB *, hdb_entry_ex *, krb5_const_principal);
+
+ /**
+ * Check if this name is an alias for the supplied client for PKINIT userPrinicpalName logins
+ */
+ krb5_error_code (*hdb_check_pkinit_ms_upn_match)(krb5_context, struct HDB *, hdb_entry_ex *, krb5_const_principal);
}HDB;
-#define HDB_INTERFACE_VERSION 5
+#define HDB_INTERFACE_VERSION 6
struct hdb_so_method {
int version;
diff --git a/source4/kdc/hdb-samba4.c b/source4/kdc/hdb-samba4.c
index 1a0e93f7ce..e39366c407 100644
--- a/source4/kdc/hdb-samba4.c
+++ b/source4/kdc/hdb-samba4.c
@@ -978,17 +978,16 @@ static krb5_error_code hdb_samba4_rename(krb5_context context, HDB *db, const ch
return HDB_ERR_DB_INUSE;
}
-static krb5_error_code hdb_samba4_fetch_client(krb5_context context, HDB *db,
- struct loadparm_context *lp_ctx,
- TALLOC_CTX *mem_ctx,
- krb5_const_principal principal,
- unsigned flags,
- hdb_entry_ex *entry_ex) {
+static krb5_error_code hdb_samba4_lookup_client(krb5_context context, HDB *db,
+ struct loadparm_context *lp_ctx,
+ TALLOC_CTX *mem_ctx,
+ krb5_const_principal principal,
+ const char **attrs,
+ struct ldb_dn **realm_dn,
+ struct ldb_message **msg) {
NTSTATUS nt_status;
char *principal_string;
- struct ldb_dn *realm_dn;
krb5_error_code ret;
- struct ldb_message *msg = NULL;
ret = krb5_unparse_name(context, principal, &principal_string);
@@ -997,8 +996,8 @@ static krb5_error_code hdb_samba4_fetch_client(krb5_context context, HDB *db,
}
nt_status = sam_get_results_principal((struct ldb_context *)db->hdb_db,
- mem_ctx, principal_string,
- &realm_dn, &msg);
+ mem_ctx, principal_string, attrs,
+ realm_dn, msg);
free(principal_string);
if (NT_STATUS_EQUAL(nt_status, NT_STATUS_NO_SUCH_USER)) {
return HDB_ERR_NOENTRY;
@@ -1008,9 +1007,29 @@ static krb5_error_code hdb_samba4_fetch_client(krb5_context context, HDB *db,
return EINVAL;
}
+ return ret;
+}
+
+static krb5_error_code hdb_samba4_fetch_client(krb5_context context, HDB *db,
+ struct loadparm_context *lp_ctx,
+ TALLOC_CTX *mem_ctx,
+ krb5_const_principal principal,
+ unsigned flags,
+ hdb_entry_ex *entry_ex) {
+ struct ldb_dn *realm_dn;
+ krb5_error_code ret;
+ struct ldb_message *msg = NULL;
+
+ ret = hdb_samba4_lookup_client(context, db, lp_ctx,
+ mem_ctx, principal, user_attrs,
+ &realm_dn, &msg);
+ if (ret != 0) {
+ return ret;
+ }
+
ret = hdb_samba4_message2entry(context, db, lp_ctx, mem_ctx,
- principal, HDB_SAMBA4_ENT_TYPE_CLIENT,
- realm_dn, msg, entry_ex);
+ principal, HDB_SAMBA4_ENT_TYPE_CLIENT,
+ realm_dn, msg, entry_ex);
return ret;
}
@@ -1422,6 +1441,11 @@ static krb5_error_code hdb_samba4_destroy(krb5_context context, HDB *db)
return 0;
}
+
+/* Check if a given entry may delegate to this target principal
+ *
+ * This is currently a very nasty hack - allowing only delegation to itself.
+ */
krb5_error_code hdb_samba4_check_constrained_delegation(krb5_context context, HDB *db,
hdb_entry_ex *entry,
krb5_const_principal target_principal)
@@ -1491,6 +1515,60 @@ krb5_error_code hdb_samba4_check_constrained_delegation(krb5_context context, HD
return ret;
}
+/* Certificates printed by a the Certificate Authority might have a
+ * slightly different form of the user principal name to that in the
+ * database. Allow a mismatch where they both refer to the same
+ * SID */
+
+krb5_error_code hdb_samba4_check_pkinit_ms_upn_match(krb5_context context, HDB *db,
+ hdb_entry_ex *entry,
+ krb5_const_principal certificate_principal)
+{
+ struct ldb_context *ldb_ctx = (struct ldb_context *)db->hdb_db;
+ struct loadparm_context *lp_ctx = talloc_get_type(ldb_get_opaque(ldb_ctx, "loadparm"),
+ struct loadparm_context);
+ krb5_error_code ret;
+ struct ldb_dn *realm_dn;
+ struct ldb_message *msg;
+ struct dom_sid *orig_sid;
+ struct dom_sid *target_sid;
+ struct hdb_samba4_private *p = talloc_get_type(entry->ctx, struct hdb_samba4_private);
+ const char *ms_upn_check_attrs[] = {
+ "objectSid", NULL
+ };
+
+ TALLOC_CTX *mem_ctx = talloc_named(db, 0, "hdb_samba4_check_constrained_delegation");
+
+ if (!mem_ctx) {
+ ret = ENOMEM;
+ krb5_set_error_message(context, ret, "hdb_samba4_fetch: talloc_named() failed!");
+ return ret;
+ }
+
+ ret = hdb_samba4_lookup_client(context, db, lp_ctx,
+ mem_ctx, certificate_principal,
+ ms_upn_check_attrs, &realm_dn, &msg);
+
+ if (ret != 0) {
+ talloc_free(mem_ctx);
+ return ret;
+ }
+
+ orig_sid = samdb_result_dom_sid(mem_ctx, p->msg, "objectSid");
+ target_sid = samdb_result_dom_sid(mem_ctx, msg, "objectSid");
+
+ /* Consider these to be the same principal, even if by a different
+ * name. The easy and safe way to prove this is by SID
+ * comparison */
+ if (!(orig_sid && target_sid && dom_sid_equal(orig_sid, target_sid))) {
+ talloc_free(mem_ctx);
+ return KRB5_KDC_ERR_CLIENT_NAME_MISMATCH;
+ }
+
+ talloc_free(mem_ctx);
+ return ret;
+}
+
/* This interface is to be called by the KDC and libnet_keytab_dump, which is expecting Samba
* calling conventions. It is also called by a wrapper
* (hdb_samba4_create) from the kpasswdd -> krb5 -> keytab_hdb -> hdb
@@ -1556,6 +1634,7 @@ NTSTATUS hdb_samba4_create_kdc(TALLOC_CTX *mem_ctx,
(*db)->hdb_auth_status = NULL;
(*db)->hdb_check_constrained_delegation = hdb_samba4_check_constrained_delegation;
+ (*db)->hdb_check_pkinit_ms_upn_match = hdb_samba4_check_pkinit_ms_upn_match;
return NT_STATUS_OK;
}
diff --git a/testprogs/blackbox/test_kinit.sh b/testprogs/blackbox/test_kinit.sh
index 2349afae7e..91f21f473b 100755
--- a/testprogs/blackbox/test_kinit.sh
+++ b/testprogs/blackbox/test_kinit.sh
@@ -53,7 +53,9 @@ echo $PASSWORD > ./tmppassfile
testit "kinit with password" $samba4kinit --password-file=./tmppassfile --request-pac $USERNAME@$REALM || failed=`expr $failed + 1`
testit "kinit with password (enterprise style)" $samba4kinit --enterprise --password-file=./tmppassfile --request-pac $USERNAME@$REALM || failed=`expr $failed + 1`
testit "kinit with password (windows style)" $samba4kinit --windows --password-file=./tmppassfile --request-pac $USERNAME@$REALM || failed=`expr $failed + 1`
-testit "kinit with pkinit" $samba4kinit --request-pac --renewable --pk-user=FILE:$PREFIX/dc/private/tls/admincert.pem,$PREFIX/dc/private/tls/adminkey.pem $USERNAME@$REALM || failed=`expr $failed + 1`
+testit "kinit with pkinit (name specified)" $samba4kinit --request-pac --renewable --pk-user=FILE:$PREFIX/dc/private/tls/admincert.pem,$PREFIX/dc/private/tls/adminkey.pem $USERNAME@$REALM || failed=`expr $failed + 1`
+testit "kinit with pkinit (enterprise name specified)" $samba4kinit --request-pac --renewable --pk-user=FILE:$PREFIX/dc/private/tls/admincert.pem,$PREFIX/dc/private/tls/adminkey.pem --enterprise $USERNAME@$REALM || failed=`expr $failed + 1`
+testit "kinit with pkinit (enterprise name in cert)" $samba4kinit --request-pac --renewable --pk-user=FILE:$PREFIX/dc/private/tls/admincertupn.pem,$PREFIX/dc/private/tls/adminkey.pem --pk-enterprise || failed=`expr $failed + 1`
testit "kinit renew ticket" $samba4kinit --request-pac -R
test_smbclient "Test login with kerberos ccache" 'ls' -k yes || failed=`expr $failed + 1`