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:
parent
47a7a2e442
commit
8ff1f50b0c
@ -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
|
||||
}
|
||||
|
||||
#
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -1053,6 +1053,7 @@ _kdc_as_rep(krb5_context context,
|
||||
|
||||
ret = _kdc_pk_check_client(context,
|
||||
config,
|
||||
clientdb,
|
||||
client,
|
||||
pkp,
|
||||
&client_cert);
|
||||
|
@ -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");
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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`
|
||||
|
Loading…
Reference in New Issue
Block a user