diff options
-rw-r--r-- | selftest/target/Samba4.pm | 42 | ||||
-rw-r--r-- | source4/auth/ntlm/auth_sam.c | 2 | ||||
-rw-r--r-- | source4/auth/sam.c | 6 | ||||
-rw-r--r-- | source4/heimdal/kdc/kerberos5.c | 1 | ||||
-rw-r--r-- | source4/heimdal/kdc/pkinit.c | 38 | ||||
-rw-r--r-- | source4/heimdal/lib/hdb/hdb.h | 7 | ||||
-rw-r--r-- | source4/kdc/hdb-samba4.c | 103 | ||||
-rwxr-xr-x | testprogs/blackbox/test_kinit.sh | 4 |
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` |