1
0
mirror of https://github.com/samba-team/samba.git synced 2024-12-22 13:34:15 +03:00

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
This commit is contained in:
Andrew Bartlett 2009-07-28 14:05:19 +10:00
parent 47a7a2e442
commit 8ff1f50b0c
8 changed files with 161 additions and 42 deletions

View File

@ -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
}
#

View File

@ -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;
}

View File

@ -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;

View File

@ -1053,6 +1053,7 @@ _kdc_as_rep(krb5_context context,
ret = _kdc_pk_check_client(context,
config,
clientdb,
client,
pkp,
&client_cert);

View File

@ -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");

View File

@ -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;

View File

@ -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;
}

View File

@ -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`