diff options
-rw-r--r-- | source4/auth/credentials/credentials.c | 27 | ||||
-rw-r--r-- | source4/auth/credentials/credentials.h | 5 | ||||
-rw-r--r-- | source4/auth/credentials/credentials_files.c | 57 | ||||
-rw-r--r-- | source4/auth/credentials/credentials_krb5.c | 61 | ||||
-rw-r--r-- | source4/auth/gensec/gensec.c | 18 | ||||
-rw-r--r-- | source4/auth/gensec/gensec_gssapi.c | 55 | ||||
-rw-r--r-- | source4/auth/gensec/gensec_krb5.c | 10 | ||||
-rw-r--r-- | source4/auth/kerberos/kerberos.h | 31 | ||||
-rw-r--r-- | source4/auth/kerberos/kerberos_util.c | 111 | ||||
-rw-r--r-- | source4/auth/kerberos/kerberos_verify.c | 207 | ||||
-rw-r--r-- | source4/ldap_server/ldap_bind.c | 18 | ||||
-rw-r--r-- | source4/rpc_server/dcesrv_auth.c | 18 | ||||
-rw-r--r-- | source4/setup/secrets.ldif | 13 | ||||
-rw-r--r-- | source4/smb_server/negprot.c | 28 | ||||
-rw-r--r-- | source4/smb_server/sesssetup.c | 2 | ||||
-rw-r--r-- | source4/smb_server/smb_server.h | 4 | ||||
-rw-r--r-- | source4/utils/ntlm_auth.c | 10 |
17 files changed, 389 insertions, 286 deletions
diff --git a/source4/auth/credentials/credentials.c b/source4/auth/credentials/credentials.c index f24c4e2231..9be877dd2c 100644 --- a/source4/auth/credentials/credentials.c +++ b/source4/auth/credentials/credentials.c @@ -46,7 +46,12 @@ struct cli_credentials *cli_credentials_init(TALLOC_CTX *mem_ctx) cred->domain_obtained = CRED_UNINITIALISED; cred->realm_obtained = CRED_UNINITIALISED; cred->ccache_obtained = CRED_UNINITIALISED; + cred->keytab_obtained = CRED_UNINITIALISED; cred->principal_obtained = CRED_UNINITIALISED; + + cred->old_password = NULL; + cred->smb_krb5_context = NULL; + return cred; } @@ -210,6 +215,28 @@ BOOL cli_credentials_set_password_callback(struct cli_credentials *cred, } /** + * Obtain the 'old' password for this credentials context (used for join accounts). + * @param cred credentials context + * @retval If set, the cleartext password, otherwise NULL + */ +const char *cli_credentials_get_old_password(struct cli_credentials *cred) +{ + if (cred->machine_account_pending) { + cli_credentials_set_machine_account(cred); + } + + return cred->old_password; +} + +BOOL cli_credentials_set_old_password(struct cli_credentials *cred, + const char *val, + enum credentials_obtained obtained) +{ + cred->old_password = talloc_strdup(cred, val); + return True; +} + +/** * Obtain the password for this credentials context. * @param cred credentials context * @retval If set, the cleartext password, otherwise NULL diff --git a/source4/auth/credentials/credentials.h b/source4/auth/credentials/credentials.h index 324b518462..aa2a0d0ac2 100644 --- a/source4/auth/credentials/credentials.h +++ b/source4/auth/credentials/credentials.h @@ -48,10 +48,12 @@ struct cli_credentials { enum credentials_obtained realm_obtained; enum credentials_obtained ccache_obtained; enum credentials_obtained principal_obtained; + enum credentials_obtained keytab_obtained; const char *workstation; const char *username; const char *password; + const char *old_password; const char *domain; const char *realm; const char *principal; @@ -59,6 +61,7 @@ struct cli_credentials { struct samr_Password *nt_hash; struct ccache_container *ccache; + struct keytab_container *keytab; const char *(*workstation_cb) (struct cli_credentials *); const char *(*password_cb) (struct cli_credentials *); @@ -74,6 +77,8 @@ struct cli_credentials { enum netr_SchannelType secure_channel_type; int kvno; + struct smb_krb5_context *smb_krb5_context; + /* We are flagged to get machine account details from the * secrets.ldb when we are asked for a username or password */ diff --git a/source4/auth/credentials/credentials_files.c b/source4/auth/credentials/credentials_files.c index 353ff61720..aa0a7f3213 100644 --- a/source4/auth/credentials/credentials_files.c +++ b/source4/auth/credentials/credentials_files.c @@ -164,7 +164,9 @@ BOOL cli_credentials_parse_file(struct cli_credentials *cred, const char *file, * @param cred Credentials structure to fill in * @retval NTSTATUS error detailing any failure */ -NTSTATUS cli_credentials_set_machine_account(struct cli_credentials *cred) +static NTSTATUS cli_credentials_set_secrets(struct cli_credentials *cred, + const char *base, + const char *filter) { TALLOC_CTX *mem_ctx; @@ -184,6 +186,7 @@ NTSTATUS cli_credentials_set_machine_account(struct cli_credentials *cred) const char *machine_account; const char *password; + const char *old_password; const char *domain; const char *realm; enum netr_SchannelType sct; @@ -201,10 +204,9 @@ NTSTATUS cli_credentials_set_machine_account(struct cli_credentials *cred) /* search for the secret record */ ldb_ret = gendb_search(ldb, - mem_ctx, ldb_dn_explode(mem_ctx, SECRETS_PRIMARY_DOMAIN_DN), + mem_ctx, ldb_dn_explode(mem_ctx, base), &msgs, attrs, - SECRETS_PRIMARY_DOMAIN_FILTER, - cli_credentials_get_domain(cred)); + "%s", filter); if (ldb_ret == 0) { DEBUG(1, ("Could not find join record to domain: %s\n", cli_credentials_get_domain(cred))); @@ -218,6 +220,7 @@ NTSTATUS cli_credentials_set_machine_account(struct cli_credentials *cred) } password = ldb_msg_find_string(msgs[0], "secret", NULL); + old_password = ldb_msg_find_string(msgs[0], "priorSecret", NULL); machine_account = ldb_msg_find_string(msgs[0], "samAccountName", NULL); @@ -278,6 +281,52 @@ NTSTATUS cli_credentials_set_machine_account(struct cli_credentials *cred) } /** + * Fill in credentials for the machine trust account, from the secrets database. + * + * @param cred Credentials structure to fill in + * @retval NTSTATUS error detailing any failure + */ +NTSTATUS cli_credentials_set_machine_account(struct cli_credentials *cred) +{ + char *filter = talloc_asprintf(cred, SECRETS_PRIMARY_DOMAIN_FILTER, + cli_credentials_get_domain(cred)); + return cli_credentials_set_secrets(cred, SECRETS_PRIMARY_DOMAIN_DN, + filter); +} + +/** + * Fill in credentials for the machine trust account, from the secrets database. + * + * @param cred Credentials structure to fill in + * @retval NTSTATUS error detailing any failure + */ +NTSTATUS cli_credentials_set_krbtgt(struct cli_credentials *cred) +{ + char *filter = talloc_asprintf(cred, SECRETS_KRBTGT_SEARCH, + cli_credentials_get_realm(cred), + cli_credentials_get_domain(cred)); + return cli_credentials_set_secrets(cred, SECRETS_PRINCIPALS_DN, + filter); +} + +/** + * Fill in credentials for the machine trust account, from the secrets database. + * + * @param cred Credentials structure to fill in + * @retval NTSTATUS error detailing any failure + */ +NTSTATUS cli_credentials_set_stored_principal(struct cli_credentials *cred, + const char *serviceprincipal) +{ + char *filter = talloc_asprintf(cred, SECRETS_PRINCIPAL_SEARCH, + cli_credentials_get_realm(cred), + cli_credentials_get_domain(cred), + serviceprincipal); + return cli_credentials_set_secrets(cred, SECRETS_PRINCIPALS_DN, + filter); +} + +/** * Ask that when required, the credentials system will be filled with * machine trust account, from the secrets database. * diff --git a/source4/auth/credentials/credentials_krb5.c b/source4/auth/credentials/credentials_krb5.c index fb3239494e..b20d9ee750 100644 --- a/source4/auth/credentials/credentials_krb5.c +++ b/source4/auth/credentials/credentials_krb5.c @@ -26,6 +26,22 @@ #include "system/kerberos.h" #include "auth/kerberos/kerberos.h" +int cli_credentials_get_krb5_context(struct cli_credentials *cred, + struct smb_krb5_context **smb_krb5_context) +{ + int ret; + if (cred->smb_krb5_context) { + *smb_krb5_context = cred->smb_krb5_context; + return 0; + } + + ret = smb_krb5_init_context(cred, &cred->smb_krb5_context); + if (ret) { + return ret; + } + *smb_krb5_context = cred->smb_krb5_context; + return 0; +} int cli_credentials_set_from_ccache(struct cli_credentials *cred, enum credentials_obtained obtained) @@ -95,11 +111,13 @@ int cli_credentials_set_ccache(struct cli_credentials *cred, return ENOMEM; } - ret = smb_krb5_init_context(ccc, &ccc->smb_krb5_context); + ret = cli_credentials_get_krb5_context(cred, &ccc->smb_krb5_context); if (ret) { talloc_free(ccc); return ret; } + talloc_reference(ccc, ccc->smb_krb5_context); + if (name) { ret = krb5_cc_resolve(ccc->smb_krb5_context->krb5_context, name, &ccc->ccache); if (ret) { @@ -162,7 +180,7 @@ int cli_credentials_new_ccache(struct cli_credentials *cred) } ccache_name = talloc_asprintf(ccc, "MEMORY:%s", - rand_string); + rand_string); talloc_free(rand_string); if (!ccache_name) { @@ -170,12 +188,12 @@ int cli_credentials_new_ccache(struct cli_credentials *cred) return ENOMEM; } - ret = smb_krb5_init_context(ccc, &ccc->smb_krb5_context); + ret = cli_credentials_get_krb5_context(cred, &ccc->smb_krb5_context); if (ret) { - talloc_free(ccache_name); talloc_free(ccc); return ret; } + talloc_reference(ccc, ccc->smb_krb5_context); ret = krb5_cc_resolve(ccc->smb_krb5_context->krb5_context, ccache_name, &ccc->ccache); if (ret) { @@ -227,6 +245,41 @@ int cli_credentials_get_ccache(struct cli_credentials *cred, return ret; } +int cli_credentials_get_keytab(struct cli_credentials *cred, + struct keytab_container **_ktc) +{ + krb5_error_code ret; + struct keytab_container *ktc; + struct smb_krb5_context *smb_krb5_context; + + if (cred->keytab_obtained >= (MAX(cred->principal_obtained, + cred->username_obtained))) { + *_ktc = cred->keytab; + return 0; + } + + if (cli_credentials_is_anonymous(cred)) { + return EINVAL; + } + + ret = cli_credentials_get_krb5_context(cred, &smb_krb5_context); + if (ret) { + return ret; + } + + ret = create_memory_keytab(cred, cred, smb_krb5_context, &ktc); + if (ret) { + return ret; + } + + cred->keytab_obtained = (MAX(cred->principal_obtained, + cred->username_obtained)); + + cred->keytab = ktc; + *_ktc = cred->keytab; + return ret; +} + /** * Set Kerberos KVNO */ diff --git a/source4/auth/gensec/gensec.c b/source4/auth/gensec/gensec.c index 21e70e1c0e..375c55e3ba 100644 --- a/source4/auth/gensec/gensec.c +++ b/source4/auth/gensec/gensec.c @@ -439,7 +439,7 @@ const char *gensec_get_name_by_oid(const char *oid_string) if (ops) { return ops->name; } - return NULL; + return oid_string; } @@ -489,6 +489,22 @@ NTSTATUS gensec_start_mech_by_sasl_name(struct gensec_security *gensec_security, return gensec_start_mech(gensec_security); } +/** + * Start a GENSEC sub-mechanism by an internal name + * + */ + +NTSTATUS gensec_start_mech_by_name(struct gensec_security *gensec_security, + const char *name) +{ + gensec_security->ops = gensec_security_by_name(name); + if (!gensec_security->ops) { + DEBUG(3, ("Could not find GENSEC backend for name=%s\n", name)); + return NT_STATUS_INVALID_PARAMETER; + } + return gensec_start_mech(gensec_security); +} + /* wrappers for the gensec function pointers */ diff --git a/source4/auth/gensec/gensec_gssapi.c b/source4/auth/gensec/gensec_gssapi.c index 8eae8bda71..97543de445 100644 --- a/source4/auth/gensec/gensec_gssapi.c +++ b/source4/auth/gensec/gensec_gssapi.c @@ -43,7 +43,7 @@ struct gensec_gssapi_state { struct smb_krb5_context *smb_krb5_context; krb5_ccache ccache; const char *ccache_name; - krb5_keytab keytab; + struct keytab_container *keytab; gss_cred_id_t cred; }; @@ -154,6 +154,7 @@ static NTSTATUS gensec_gssapi_server_start(struct gensec_security *gensec_securi { NTSTATUS nt_status; OM_uint32 maj_stat, min_stat; + int ret; gss_buffer_desc name_token; struct gensec_gssapi_state *gensec_gssapi_state; struct cli_credentials *machine_account; @@ -165,45 +166,43 @@ static NTSTATUS gensec_gssapi_server_start(struct gensec_security *gensec_securi gensec_gssapi_state = gensec_security->private_data; - machine_account = cli_credentials_init(gensec_gssapi_state); - cli_credentials_set_conf(machine_account); - nt_status = cli_credentials_set_machine_account(machine_account); + machine_account = gensec_get_credentials(gensec_security); - if (!NT_STATUS_IS_OK(nt_status)) { - DEBUG(3, ("Could not obtain machine account credentials from the local database\n")); - talloc_free(machine_account); - return nt_status; + if (!machine_account) { + DEBUG(3, ("No machine account credentials specified\n")); + return NT_STATUS_INVALID_PARAMETER; } else { - nt_status = create_memory_keytab(gensec_gssapi_state, - machine_account, - gensec_gssapi_state->smb_krb5_context, - &gensec_gssapi_state->keytab); - if (!NT_STATUS_IS_OK(nt_status)) { + ret = cli_credentials_get_keytab(machine_account, &gensec_gssapi_state->keytab); + if (ret) { DEBUG(3, ("Could not create memory keytab!\n")); - talloc_free(machine_account); - return nt_status; + return NT_STATUS_CANT_ACCESS_DOMAIN_INFO; } } name_token.value = cli_credentials_get_principal(machine_account, machine_account); - name_token.length = strlen(name_token.value); - - maj_stat = gss_import_name (&min_stat, - &name_token, - GSS_C_NT_USER_NAME, - &gensec_gssapi_state->server_name); - talloc_free(machine_account); - if (maj_stat) { - DEBUG(2, ("GSS Import name of %s failed: %s\n", - (char *)name_token.value, - gssapi_error_string(gensec_gssapi_state, maj_stat, min_stat))); - return NT_STATUS_UNSUCCESSFUL; + /* This might have been explicity set to NULL, ie use what the client calls us */ + if (name_token.value) { + name_token.length = strlen(name_token.value); + + maj_stat = gss_import_name (&min_stat, + &name_token, + GSS_C_NT_USER_NAME, + &gensec_gssapi_state->server_name); + + if (maj_stat) { + DEBUG(2, ("GSS Import name of %s failed: %s\n", + (char *)name_token.value, + gssapi_error_string(gensec_gssapi_state, maj_stat, min_stat))); + return NT_STATUS_UNSUCCESSFUL; + } + } else { + gensec_gssapi_state->server_name = GSS_C_NO_NAME; } maj_stat = gsskrb5_acquire_cred(&min_stat, - gensec_gssapi_state->keytab, NULL, + gensec_gssapi_state->keytab->keytab, NULL, gensec_gssapi_state->server_name, GSS_C_INDEFINITE, GSS_C_NULL_OID_SET, diff --git a/source4/auth/gensec/gensec_krb5.c b/source4/auth/gensec/gensec_krb5.c index 71974790b1..d999559a49 100644 --- a/source4/auth/gensec/gensec_krb5.c +++ b/source4/auth/gensec/gensec_krb5.c @@ -86,6 +86,10 @@ static NTSTATUS gensec_krb5_start(struct gensec_security *gensec_security) { struct gensec_krb5_state *gensec_krb5_state; + if (!gensec_get_credentials(gensec_security)) { + return NT_STATUS_INVALID_PARAMETER; + } + gensec_krb5_state = talloc(gensec_security, struct gensec_krb5_state); if (!gensec_krb5_state) { return NT_STATUS_NO_MEMORY; @@ -185,7 +189,7 @@ static NTSTATUS gensec_krb5_client_start(struct gensec_security *gensec_security gensec_krb5_state = gensec_security->private_data; gensec_krb5_state->state_position = GENSEC_KRB5_CLIENT_START; - ret = cli_credentials_get_ccache(gensec_security->credentials, &ccache_container); + ret = cli_credentials_get_ccache(gensec_get_credentials(gensec_security), &ccache_container); if (ret) { DEBUG(1,("gensec_krb5_start: cli_credentials_get_ccache failed: %s\n", error_message(ret))); @@ -391,7 +395,7 @@ static NTSTATUS gensec_krb5_update(struct gensec_security *gensec_security, nt_status = ads_verify_ticket(out_mem_ctx, gensec_krb5_state->smb_krb5_context, &gensec_krb5_state->auth_context, - lp_realm(), + gensec_get_credentials(gensec_security), gensec_get_target_service(gensec_security), &unwrapped_in, &gensec_krb5_state->ticket, &unwrapped_out, &gensec_krb5_state->keyblock); @@ -400,7 +404,7 @@ static NTSTATUS gensec_krb5_update(struct gensec_security *gensec_security, nt_status = ads_verify_ticket(out_mem_ctx, gensec_krb5_state->smb_krb5_context, &gensec_krb5_state->auth_context, - lp_realm(), + gensec_get_credentials(gensec_security), gensec_get_target_service(gensec_security), &in, &gensec_krb5_state->ticket, &unwrapped_out, diff --git a/source4/auth/kerberos/kerberos.h b/source4/auth/kerberos/kerberos.h index bc2a1babe9..13a82b0575 100644 --- a/source4/auth/kerberos/kerberos.h +++ b/source4/auth/kerberos/kerberos.h @@ -28,6 +28,12 @@ struct ccache_container { krb5_ccache ccache; }; + +struct keytab_container { + struct smb_krb5_context *smb_krb5_context; + krb5_keytab keytab; +}; + /* not really ASN.1, but RFC 1964 */ #define TOK_ID_KRB_AP_REQ "\x01\x00" #define TOK_ID_KRB_AP_REP "\x02\x00" @@ -81,14 +87,15 @@ krb5_error_code ads_krb5_mk_req(krb5_context context, krb5_data *outbuf); DATA_BLOB get_auth_data_from_tkt(TALLOC_CTX *mem_ctx, krb5_ticket *tkt); -NTSTATUS ads_verify_ticket(TALLOC_CTX *mem_ctx, - struct smb_krb5_context *smb_krb5_context, - krb5_auth_context *auth_context, - const char *realm, const char *service, - const DATA_BLOB *enc_ticket, - krb5_ticket **tkt, - DATA_BLOB *ap_rep, - krb5_keyblock **keyblock); + NTSTATUS ads_verify_ticket(TALLOC_CTX *mem_ctx, + struct smb_krb5_context *smb_krb5_context, + krb5_auth_context *auth_context, + struct cli_credentials *machine_account, + const char *service, + const DATA_BLOB *enc_ticket, + krb5_ticket **tkt, + DATA_BLOB *ap_rep, + krb5_keyblock **keyblock); int kerberos_kinit_password_cc(krb5_context ctx, krb5_ccache cc, krb5_principal principal, const char *password, time_t *expire_time, time_t *kdc_time); @@ -115,10 +122,10 @@ krb5_error_code principal_from_credentials(TALLOC_CTX *parent_ctx, struct cli_credentials *credentials, struct smb_krb5_context *smb_krb5_context, krb5_principal *princ); -NTSTATUS create_memory_keytab(TALLOC_CTX *parent_ctx, - struct cli_credentials *machine_account, - struct smb_krb5_context *smb_krb5_context, - krb5_keytab *keytab); +int create_memory_keytab(TALLOC_CTX *parent_ctx, + struct cli_credentials *machine_account, + struct smb_krb5_context *smb_krb5_context, + struct keytab_container **keytab_container); NTSTATUS kerberos_decode_pac(TALLOC_CTX *mem_ctx, struct PAC_DATA **pac_data_out, DATA_BLOB blob, diff --git a/source4/auth/kerberos/kerberos_util.c b/source4/auth/kerberos/kerberos_util.c index 3a6aff9df8..3d7084aa0d 100644 --- a/source4/auth/kerberos/kerberos_util.c +++ b/source4/auth/kerberos/kerberos_util.c @@ -33,11 +33,6 @@ struct principal_container { krb5_principal principal; }; -struct keytab_container { - struct smb_krb5_context *smb_krb5_context; - krb5_keytab keytab; -}; - static int free_principal(void *ptr) { struct principal_container *pc = ptr; /* current heimdal - 0.6.3, which we need anyway, fixes segfaults here */ @@ -111,7 +106,8 @@ krb5_error_code principal_from_credentials(TALLOC_CTX *parent_ctx, if (!princ_string) { talloc_free(mem_ctx); - return EINVAL; + princ = NULL; + return 0; } ret = krb5_parse_name(smb_krb5_context->krb5_context, @@ -131,9 +127,9 @@ krb5_error_code principal_from_credentials(TALLOC_CTX *parent_ctx, */ krb5_error_code kinit_to_ccache(TALLOC_CTX *parent_ctx, - struct cli_credentials *credentials, - struct smb_krb5_context *smb_krb5_context, - krb5_ccache ccache) + struct cli_credentials *credentials, + struct smb_krb5_context *smb_krb5_context, + krb5_ccache ccache) { krb5_error_code ret; const char *password; @@ -216,36 +212,40 @@ static int free_keytab(void *ptr) { return 0; } - NTSTATUS create_memory_keytab(TALLOC_CTX *parent_ctx, - struct cli_credentials *machine_account, - struct smb_krb5_context *smb_krb5_context, - krb5_keytab *keytab) + int create_memory_keytab(TALLOC_CTX *parent_ctx, + struct cli_credentials *machine_account, + struct smb_krb5_context *smb_krb5_context, + struct keytab_container **keytab_container) { krb5_error_code ret; const char *password_s; + char *old_secret; krb5_data password; - int i; - struct keytab_container *mem_ctx = talloc(parent_ctx, struct keytab_container); + int i, kvno; krb5_enctype *enctypes; krb5_principal salt_princ; krb5_principal princ; - + krb5_keytab keytab; + + TALLOC_CTX *mem_ctx = talloc_new(parent_ctx); if (!mem_ctx) { - return NT_STATUS_NO_MEMORY; + return ENOMEM; } + + *keytab_container = talloc(mem_ctx, struct keytab_container); - ret = krb5_kt_resolve(smb_krb5_context->krb5_context, "MEMORY:", keytab); + ret = krb5_kt_resolve(smb_krb5_context->krb5_context, "MEMORY:", &keytab); if (ret) { DEBUG(1,("failed to generate a new krb5 keytab: %s\n", error_message(ret))); talloc_free(mem_ctx); - return NT_STATUS_INTERNAL_ERROR; + return ret; } - mem_ctx->smb_krb5_context = talloc_reference(mem_ctx, smb_krb5_context); - mem_ctx->keytab = *keytab; + (*keytab_container)->smb_krb5_context = talloc_reference(*keytab_container, smb_krb5_context); + (*keytab_container)->keytab = keytab; - talloc_set_destructor(mem_ctx, free_keytab); + talloc_set_destructor(*keytab_container, free_keytab); ret = salt_principal_from_credentials(mem_ctx, machine_account, smb_krb5_context, @@ -255,7 +255,7 @@ static int free_keytab(void *ptr) { smb_get_krb5_error_message(smb_krb5_context->krb5_context, ret, mem_ctx))); talloc_free(mem_ctx); - return NT_STATUS_INTERNAL_ERROR; + return ret; } ret = principal_from_credentials(mem_ctx, machine_account, smb_krb5_context, &princ); @@ -264,10 +264,10 @@ static int free_keytab(void *ptr) { smb_get_krb5_error_message(smb_krb5_context->krb5_context, ret, mem_ctx))); talloc_free(mem_ctx); - return NT_STATUS_INTERNAL_ERROR; + return ret; } - password_s = talloc_strdup(mem_ctx, cli_credentials_get_password(machine_account)); + password_s = cli_credentials_get_password(machine_account); if (!password_s) { /* If we don't have the plaintext password, try for * the MD4 password hash */ @@ -279,7 +279,7 @@ static int free_keytab(void *ptr) { talloc_free(mem_ctx); DEBUG(1, ("create_memory_keytab: Domain trust informaton for account %s not available\n", cli_credentials_get_principal(machine_account, mem_ctx))); - return NT_STATUS_CANT_ACCESS_DOMAIN_INFO; + return EINVAL; } ret = krb5_keyblock_init(smb_krb5_context->krb5_context, ENCTYPE_ARCFOUR_HMAC, @@ -289,12 +289,12 @@ static int free_keytab(void *ptr) { DEBUG(1, ("create_memory_keytab: krb5_keyblock_init failed: %s\n", smb_get_krb5_error_message(smb_krb5_context->krb5_context, ret, mem_ctx))); - return NT_STATUS_INTERNAL_ERROR; + return ret; } entry.principal = princ; entry.vno = cli_credentials_get_kvno(machine_account); - ret = krb5_kt_add_entry(smb_krb5_context->krb5_context, *keytab, &entry); + ret = krb5_kt_add_entry(smb_krb5_context->krb5_context, keytab, &entry); if (ret) { DEBUG(1, ("Failed to add ARCFOUR_HMAC (only) entry for %s to keytab: %s", cli_credentials_get_principal(machine_account, mem_ctx), @@ -302,11 +302,14 @@ static int free_keytab(void *ptr) { ret, mem_ctx))); talloc_free(mem_ctx); krb5_free_keyblock_contents(smb_krb5_context->krb5_context, &entry.keyblock); - return NT_STATUS_INTERNAL_ERROR; + return ret; } krb5_free_keyblock_contents(smb_krb5_context->krb5_context, &entry.keyblock); - return NT_STATUS_OK; + + talloc_steal(parent_ctx, *keytab_container); + talloc_free(mem_ctx); + return 0; } /* good, we actually have the real plaintext */ @@ -317,24 +320,25 @@ static int free_keytab(void *ptr) { DEBUG(1,("create_memory_keytab: getting encrption types failed (%s)\n", error_message(ret))); talloc_free(mem_ctx); - return NT_STATUS_INTERNAL_ERROR; + return ret; } password.data = discard_const_p(char *, password_s); password.length = strlen(password_s); - + kvno = cli_credentials_get_kvno(machine_account); + for (i=0; enctypes[i]; i++) { krb5_keytab_entry entry; ret = create_kerberos_key_from_string(smb_krb5_context->krb5_context, salt_princ, &password, &entry.keyblock, enctypes[i]); if (ret) { talloc_free(mem_ctx); - return NT_STATUS_INTERNAL_ERROR; + return ret; } entry.principal = princ; - entry.vno = cli_credentials_get_kvno(machine_account); - ret = krb5_kt_add_entry(smb_krb5_context->krb5_context, *keytab, &entry); + entry.vno = kvno; + ret = krb5_kt_add_entry(smb_krb5_context->krb5_context, keytab, &entry); if (ret) { DEBUG(1, ("Failed to add entry for %s to keytab: %s", cli_credentials_get_principal(machine_account, mem_ctx), @@ -342,15 +346,48 @@ static int free_keytab(void *ptr) { ret, mem_ctx))); talloc_free(mem_ctx); krb5_free_keyblock_contents(smb_krb5_context->krb5_context, &entry.keyblock); - return NT_STATUS_INTERNAL_ERROR; + return ret; } krb5_free_keyblock_contents(smb_krb5_context->krb5_context, &entry.keyblock); } + old_secret = cli_credentials_get_old_password(machine_account); + if (kvno != 0 && old_secret) { + password.data = discard_const_p(char *, old_secret); + password.length = strlen(old_secret); + + for (i=0; enctypes[i]; i++) { + krb5_keytab_entry entry; + ret = create_kerberos_key_from_string(smb_krb5_context->krb5_context, + salt_princ, &password, &entry.keyblock, enctypes[i]); + if (ret) { + talloc_free(mem_ctx); + return ret; + } + + entry.principal = princ; + entry.vno = kvno - 1; + ret = krb5_kt_add_entry(smb_krb5_context->krb5_context, keytab, &entry); + if (ret) { + DEBUG(1, ("Failed to add 'old password' entry for %s to keytab: %s", + cli_credentials_get_principal(machine_account, mem_ctx), + smb_get_krb5_error_message(smb_krb5_context->krb5_context, + ret, mem_ctx))); + talloc_free(mem_ctx); + krb5_free_keyblock_contents(smb_krb5_context->krb5_context, &entry.keyblock); + return ret; + } + + krb5_free_keyblock_contents(smb_krb5_context->krb5_context, &entry.keyblock); + } + } + free_kerberos_etypes(smb_krb5_context->krb5_context, enctypes); - return NT_STATUS_OK; + talloc_steal(parent_ctx, *keytab_container); + talloc_free(mem_ctx); + return 0; } diff --git a/source4/auth/kerberos/kerberos_verify.c b/source4/auth/kerberos/kerberos_verify.c index 514acde5eb..13d2c9fc26 100644 --- a/source4/auth/kerberos/kerberos_verify.c +++ b/source4/auth/kerberos/kerberos_verify.c @@ -64,156 +64,6 @@ DATA_BLOB unwrap_pac(TALLOC_CTX *mem_ctx, DATA_BLOB *auth_data) } /********************************************************************************** - Try to verify a ticket using the system keytab... the system keytab has kvno -1 entries, so - it's more like what microsoft does... see comment in utils/net_ads.c in the - ads_keytab_add_entry function for details. -***********************************************************************************/ - -static krb5_error_code ads_keytab_verify_ticket(TALLOC_CTX *mem_ctx, krb5_context context, - krb5_auth_context *auth_context, - const char *service, - const krb5_data *p_packet, - krb5_flags *ap_req_options, - krb5_ticket **pp_tkt, - krb5_keyblock **keyblock) -{ - krb5_error_code ret = 0; - krb5_keytab keytab = NULL; - krb5_kt_cursor kt_cursor; - krb5_keytab_entry kt_entry; - char *valid_princ_formats[7] = { NULL, NULL, NULL, NULL, NULL, NULL, NULL }; - char *entry_princ_s = NULL; - const char *my_name, *my_fqdn; - int i; - int number_matched_principals = 0; - const char *last_error_message; - - /* Generate the list of principal names which we expect - * clients might want to use for authenticating to the file - * service. We allow name$,{host,service}/{name,fqdn,name.REALM}. - * (where service is specified by the caller) */ - - my_name = lp_netbios_name(); - - my_fqdn = name_to_fqdn(mem_ctx, my_name); - - asprintf(&valid_princ_formats[0], "%s$@%s", my_name, lp_realm()); - asprintf(&valid_princ_formats[1], "host/%s@%s", my_name, lp_realm()); - asprintf(&valid_princ_formats[2], "host/%s@%s", my_fqdn, lp_realm()); - asprintf(&valid_princ_formats[3], "host/%s.%s@%s", my_name, lp_realm(), lp_realm()); - asprintf(&valid_princ_formats[4], "%s/%s@%s", service, my_name, lp_realm()); - asprintf(&valid_princ_formats[5], "%s/%s@%s", service, my_fqdn, lp_realm()); - asprintf(&valid_princ_formats[6], "%s/%s.%s@%s", service, my_name, lp_realm(), lp_realm()); - - ZERO_STRUCT(kt_entry); - ZERO_STRUCT(kt_cursor); - - ret = krb5_kt_default(context, &keytab); - if (ret) { - last_error_message = smb_get_krb5_error_message(context, ret, mem_ctx); - DEBUG(1, ("ads_keytab_verify_ticket: krb5_kt_default failed (%s)\n", - last_error_message)); - goto out; - } - - /* Iterate through the keytab. For each key, if the principal - * name case-insensitively matches one of the allowed formats, - * try verifying the ticket using that principal. */ - - ret = krb5_kt_start_seq_get(context, keytab, &kt_cursor); - if (ret == KRB5_KT_END || ret == ENOENT ) { - last_error_message = smb_get_krb5_error_message(context, ret, mem_ctx); - } else if (ret) { - last_error_message = smb_get_krb5_error_message(context, ret, mem_ctx); - DEBUG(1, ("ads_keytab_verify_ticket: krb5_kt_start_seq_get failed (%s)\n", - last_error_message)); - } else { - ret = KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN; /* Pick an error... */ - last_error_message = "No principals in Keytab"; - while (ret && (krb5_kt_next_entry(context, keytab, &kt_entry, &kt_cursor) == 0)) { - krb5_error_code upn_ret; - upn_ret = krb5_unparse_name(context, kt_entry.principal, &entry_princ_s); - if (upn_ret) { - last_error_message = smb_get_krb5_error_message(context, ret, mem_ctx); - DEBUG(1, ("ads_keytab_verify_ticket: krb5_unparse_name failed (%s)\n", - last_error_message)); - ret = upn_ret; - break; - } - for (i = 0; i < ARRAY_SIZE(valid_princ_formats); i++) { - if (!strequal(entry_princ_s, valid_princ_formats[i])) { - continue; - } - - number_matched_principals++; - *pp_tkt = NULL; - ret = krb5_rd_req_return_keyblock(context, auth_context, p_packet, - kt_entry.principal, keytab, - ap_req_options, pp_tkt, keyblock); - if (ret) { - last_error_message = smb_get_krb5_error_message(context, ret, mem_ctx); - DEBUG(10, ("ads_keytab_verify_ticket: krb5_rd_req(%s) failed: %s\n", - entry_princ_s, last_error_message)); - } else { - DEBUG(3,("ads_keytab_verify_ticket: krb5_rd_req succeeded for principal %s\n", - entry_princ_s)); - break; - } - } - - /* Free the name we parsed. */ - krb5_free_unparsed_name(context, entry_princ_s); - entry_princ_s = NULL; - - /* Free the entry we just read. */ - smb_krb5_kt_free_entry(context, &kt_entry); - ZERO_STRUCT(kt_entry); - } - krb5_kt_end_seq_get(context, keytab, &kt_cursor); - } - - ZERO_STRUCT(kt_cursor); - - out: - - if (ret) { - if (!number_matched_principals) { - DEBUG(3, ("ads_keytab_verify_ticket: no keytab principals matched expected file service name.\n")); - } else { - DEBUG(3, ("ads_keytab_verify_ticket: krb5_rd_req failed for all %d matched keytab principals\n", - number_matched_principals)); - } - DEBUG(3, ("ads_keytab_verify_ticket: last error: %s\n", last_error_message)); - } - - if (entry_princ_s) { - krb5_free_unparsed_name(context, entry_princ_s); - } - - { - krb5_keytab_entry zero_kt_entry; - ZERO_STRUCT(zero_kt_entry); - if (memcmp(&zero_kt_entry, &kt_entry, sizeof(krb5_keytab_entry))) { - smb_krb5_kt_free_entry(context, &kt_entry); - } - } - - { - krb5_kt_cursor zero_csr; - ZERO_STRUCT(zero_csr); - if ((memcmp(&kt_cursor, &zero_csr, sizeof(krb5_kt_cursor)) != 0) && keytab) { - krb5_kt_end_seq_get(context, keytab, &kt_cursor); - } - } - - if (keytab) { - krb5_kt_close(context, keytab); - } - - return ret; -} - -/********************************************************************************** Verify an incoming ticket and parse out the principal name and authorization_data if available. ***********************************************************************************/ @@ -221,7 +71,8 @@ static krb5_error_code ads_keytab_verify_ticket(TALLOC_CTX *mem_ctx, krb5_contex NTSTATUS ads_verify_ticket(TALLOC_CTX *mem_ctx, struct smb_krb5_context *smb_krb5_context, krb5_auth_context *auth_context, - const char *realm, const char *service, + struct cli_credentials *machine_account, + const char *service, const DATA_BLOB *enc_ticket, krb5_ticket **tkt, DATA_BLOB *ap_rep, @@ -229,32 +80,12 @@ static krb5_error_code ads_keytab_verify_ticket(TALLOC_CTX *mem_ctx, krb5_contex { krb5_keyblock *local_keyblock; krb5_data packet; - krb5_principal salt_princ; int ret; krb5_flags ap_req_options = 0; + krb5_principal server; - NTSTATUS creds_nt_status, status; - struct cli_credentials *machine_account; + struct keytab_container *keytab_container; - machine_account = cli_credentials_init(mem_ctx); - cli_credentials_set_conf(machine_account); - creds_nt_status = cli_credentials_set_machine_account(machine_account); - - if (!NT_STATUS_IS_OK(creds_nt_status)) { - DEBUG(3, ("Could not obtain machine account credentials from the local database\n")); - talloc_free(machine_account); - machine_account = NULL; - } else { - ret = salt_principal_from_credentials(mem_ctx, machine_account, - smb_krb5_context, - &salt_princ); - if (ret) { - DEBUG(1,("ads_verify_ticket: maksing salt principal failed (%s)\n", - error_message(ret))); - return NT_STATUS_INTERNAL_ERROR; - } - } - /* This whole process is far more complex than I would like. We have to go through all this to allow us to store the secret internally, instead of using /etc/krb5.keytab */ @@ -268,24 +99,18 @@ static krb5_error_code ads_keytab_verify_ticket(TALLOC_CTX *mem_ctx, krb5_contex packet.length = enc_ticket->length; packet.data = (krb5_pointer)enc_ticket->data; - ret = ads_keytab_verify_ticket(mem_ctx, smb_krb5_context->krb5_context, auth_context, - service, &packet, &ap_req_options, tkt, &local_keyblock); - if (ret && machine_account) { - krb5_keytab keytab; - krb5_principal server; - status = create_memory_keytab(mem_ctx, machine_account, smb_krb5_context, - &keytab); - if (!NT_STATUS_IS_OK(status)) { - return status; - } - ret = principal_from_credentials(mem_ctx, machine_account, smb_krb5_context, - &server); - if (ret == 0) { - ret = krb5_rd_req_return_keyblock(smb_krb5_context->krb5_context, auth_context, &packet, - server, - keytab, &ap_req_options, tkt, - &local_keyblock); - } + ret = cli_credentials_get_keytab(machine_account, &keytab_container); + if (ret) { + return NT_STATUS_CANT_ACCESS_DOMAIN_INFO; + } + + ret = principal_from_credentials(mem_ctx, machine_account, smb_krb5_context, + &server); + if (ret == 0) { + ret = krb5_rd_req_return_keyblock(smb_krb5_context->krb5_context, auth_context, &packet, + server, + keytab_container->keytab, &ap_req_options, tkt, + &local_keyblock); } if (ret) { diff --git a/source4/ldap_server/ldap_bind.c b/source4/ldap_server/ldap_bind.c index e9d38ad93b..7a296d01ac 100644 --- a/source4/ldap_server/ldap_bind.c +++ b/source4/ldap_server/ldap_bind.c @@ -63,6 +63,7 @@ static NTSTATUS ldapsrv_BindSASL(struct ldapsrv_call *call) DEBUG(10, ("BindSASL dn: %s\n",req->dn)); if (!call->conn->gensec) { + struct cli_credentials *server_credentials; call->conn->session_info = NULL; status = gensec_server_start(call->conn, &call->conn->gensec, @@ -74,6 +75,23 @@ static NTSTATUS ldapsrv_BindSASL(struct ldapsrv_call *call) gensec_set_target_service(call->conn->gensec, "ldap"); + server_credentials + = cli_credentials_init(call); + if (!server_credentials) { + DEBUG(1, ("Failed to init server credentials\n")); + return NT_STATUS_NO_MEMORY; + } + + cli_credentials_set_conf(server_credentials); + status = cli_credentials_set_machine_account(server_credentials); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(10, ("Failed to obtain server credentials, perhaps a standalone server?: %s\n", nt_errstr(status))); + talloc_free(server_credentials); + server_credentials = NULL; + } + + gensec_set_credentials(call->conn->gensec, server_credentials); + gensec_want_feature(call->conn->gensec, GENSEC_FEATURE_SIGN); gensec_want_feature(call->conn->gensec, GENSEC_FEATURE_SEAL); gensec_want_feature(call->conn->gensec, GENSEC_FEATURE_ASYNC_REPLIES); diff --git a/source4/rpc_server/dcesrv_auth.c b/source4/rpc_server/dcesrv_auth.c index a2ba709f56..6caef7424d 100644 --- a/source4/rpc_server/dcesrv_auth.c +++ b/source4/rpc_server/dcesrv_auth.c @@ -32,6 +32,7 @@ */ BOOL dcesrv_auth_bind(struct dcesrv_call_state *call) { + struct cli_credentials *server_credentials; struct ncacn_packet *pkt = &call->pkt; struct dcesrv_connection *dce_conn = call->conn; struct dcesrv_auth *auth = &dce_conn->auth_state; @@ -61,6 +62,23 @@ BOOL dcesrv_auth_bind(struct dcesrv_call_state *call) return False; } + server_credentials + = cli_credentials_init(call); + if (!server_credentials) { + DEBUG(1, ("Failed to init server credentials\n")); + return False; + } + + cli_credentials_set_conf(server_credentials); + status = cli_credentials_set_machine_account(server_credentials); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(10, ("Failed to obtain server credentials, perhaps a standalone server?: %s\n", nt_errstr(status))); + talloc_free(server_credentials); + server_credentials = NULL; + } + + gensec_set_credentials(auth->gensec_security, server_credentials); + status = gensec_start_mech_by_authtype(auth->gensec_security, auth->auth_info->auth_type, auth->auth_info->auth_level); diff --git a/source4/setup/secrets.ldif b/source4/setup/secrets.ldif index 3dcb82dbdd..eccb768db5 100644 --- a/source4/setup/secrets.ldif +++ b/source4/setup/secrets.ldif @@ -35,3 +35,16 @@ whenCreated: ${LDAPTIME} whenChanged: ${LDAPTIME} msDS-KeyVersionNumber: 1 objectSid: ${DOMAINSID} + +dn: samAccountName=krbtgt,flatname=${DOMAIN},CN=Principals +objectClass: top +objectClass: secret +flatname: ${DOMAIN} +realm: ${REALM} +secret: ${KRBTGTPASS} +sAMAccountName: krbtgt +whenCreated: ${LDAPTIME} +whenChanged: ${LDAPTIME} +msDS-KeyVersionNumber: 1 +objectSid: ${DOMAINSID} +servicePrincipalName: kadmin/changepw diff --git a/source4/smb_server/negprot.c b/source4/smb_server/negprot.c index 31f31272e0..a9cc05e251 100644 --- a/source4/smb_server/negprot.c +++ b/source4/smb_server/negprot.c @@ -326,6 +326,7 @@ static void reply_nt1(struct smbsrv_request *req, uint16_t choice) req_push_str(req, NULL, lp_netbios_name(), -1, STR_UNICODE|STR_TERMINATE|STR_NOALIGN); DEBUG(3,("not using SPNEGO\n")); } else { + struct cli_credentials *server_credentials; struct gensec_security *gensec_security; DATA_BLOB null_data_blob = data_blob(NULL, 0); DATA_BLOB blob; @@ -333,19 +334,38 @@ static void reply_nt1(struct smbsrv_request *req, uint16_t choice) &gensec_security, req->smb_conn->connection->event.ctx); + if (!NT_STATUS_IS_OK(nt_status)) { + DEBUG(0, ("Failed to start GENSEC: %s\n", nt_errstr(nt_status))); + smbsrv_terminate_connection(req->smb_conn, "Failed to start GENSEC\n"); + return; + } + if (req->smb_conn->negotiate.auth_context) { smbsrv_terminate_connection(req->smb_conn, "reply_nt1: is this a secondary negprot? auth_context is non-NULL!\n"); return; } - req->smb_conn->negotiate.auth_context = NULL; + server_credentials + = cli_credentials_init(req); + if (!server_credentials) { + smbsrv_terminate_connection(req->smb_conn, "Failed to init server credentials\n"); + return; + } + cli_credentials_set_conf(server_credentials); + nt_status = cli_credentials_set_machine_account(server_credentials); if (!NT_STATUS_IS_OK(nt_status)) { - DEBUG(0, ("Failed to start GENSEC: %s\n", nt_errstr(nt_status))); - smbsrv_terminate_connection(req->smb_conn, "Failed to start GENSEC\n"); - return; + DEBUG(10, ("Failed to obtain server credentials, perhaps a standalone server?: %s\n", nt_errstr(nt_status))); + talloc_free(server_credentials); + server_credentials = NULL; } + req->smb_conn->negotiate.server_credentials = talloc_steal(req->smb_conn, server_credentials); + + gensec_set_target_service(gensec_security, "cifs"); + + gensec_set_credentials(gensec_security, server_credentials); + nt_status = gensec_start_mech_by_oid(gensec_security, GENSEC_OID_SPNEGO); if (!NT_STATUS_IS_OK(nt_status)) { diff --git a/source4/smb_server/sesssetup.c b/source4/smb_server/sesssetup.c index 1fa04b99e5..bdd4a3fab2 100644 --- a/source4/smb_server/sesssetup.c +++ b/source4/smb_server/sesssetup.c @@ -293,6 +293,8 @@ static NTSTATUS sesssetup_spnego(struct smbsrv_request *req, union smb_sesssetup return status; } + gensec_set_credentials(gensec_ctx, req->smb_conn->negotiate.server_credentials); + gensec_set_target_service(gensec_ctx, "cifs"); gensec_want_feature(gensec_ctx, GENSEC_FEATURE_SESSION_KEY); diff --git a/source4/smb_server/smb_server.h b/source4/smb_server/smb_server.h index 01fb1e26a3..360ea7ddfb 100644 --- a/source4/smb_server/smb_server.h +++ b/source4/smb_server/smb_server.h @@ -194,8 +194,8 @@ struct smbsrv_connection { /* authentication context for multi-part negprot */ struct auth_context *auth_context; - /* state of NTLMSSP auth */ - struct auth_ntlmssp_state *ntlmssp_state; + /* reference to the kerberos keytab, or machine trust account */ + struct cli_credentials *server_credentials; /* did we tell the client we support encrypted passwords? */ BOOL encrypted_passwords; diff --git a/source4/utils/ntlm_auth.c b/source4/utils/ntlm_auth.c index 3b94640f22..7072262653 100644 --- a/source4/utils/ntlm_auth.c +++ b/source4/utils/ntlm_auth.c @@ -392,6 +392,16 @@ static void manage_gensec_request(enum stdio_helper_mode stdio_helper_mode, if (opt_workstation) { cli_credentials_set_workstation(creds, opt_workstation, CRED_SPECIFIED); } + + switch (stdio_helper_mode) { + case GSS_SPNEGO_SERVER: + case SQUID_2_5_NTLMSSP: + cli_credentials_set_machine_account(creds); + break; + default: + break; + } + gensec_set_credentials(state->gensec_state, creds); switch (stdio_helper_mode) { |