diff options
Diffstat (limited to 'source4/auth/credentials')
-rw-r--r-- | source4/auth/credentials/credentials.c | 27 | ||||
-rw-r--r-- | source4/auth/credentials/credentials.h | 6 | ||||
-rw-r--r-- | source4/auth/credentials/credentials_files.c | 92 | ||||
-rw-r--r-- | source4/auth/credentials/credentials_krb5.c | 211 |
4 files changed, 314 insertions, 22 deletions
diff --git a/source4/auth/credentials/credentials.c b/source4/auth/credentials/credentials.c index 86a3df0077..75c6795e73 100644 --- a/source4/auth/credentials/credentials.c +++ b/source4/auth/credentials/credentials.c @@ -46,7 +46,8 @@ 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->gss_creds_obtained = CRED_UNINITIALISED; + cred->client_gss_creds_obtained = CRED_UNINITIALISED; + cred->server_gss_creds_obtained = CRED_UNINITIALISED; cred->keytab_obtained = CRED_UNINITIALISED; cred->principal_obtained = CRED_UNINITIALISED; @@ -148,6 +149,9 @@ BOOL cli_credentials_set_principal(struct cli_credentials *cred, return False; } +/* Set a callback to get the principal. This could be a popup dialog, + * a terminal prompt or similar. */ + BOOL cli_credentials_set_principal_callback(struct cli_credentials *cred, const char *(*principal_cb) (struct cli_credentials *)) { @@ -160,6 +164,10 @@ BOOL cli_credentials_set_principal_callback(struct cli_credentials *cred, return False; } +/* Some of our tools are 'anonymous by default'. This is a single + * function to determine if authentication has been explicitly + * requested */ + BOOL cli_credentials_authentication_requested(struct cli_credentials *cred) { if (cred->principal_obtained >= CRED_SPECIFIED) { @@ -190,6 +198,9 @@ const char *cli_credentials_get_password(struct cli_credentials *cred) return cred->password; } +/* Set a password on the credentials context, including an indication + * of 'how' the password was obtained */ + BOOL cli_credentials_set_password(struct cli_credentials *cred, const char *val, enum credentials_obtained obtained) @@ -240,7 +251,11 @@ BOOL cli_credentials_set_old_password(struct cli_credentials *cred, } /** - * Obtain the password for this credentials context. + * Obtain the password, in the form MD4(unicode(password)) for this credentials context. + * + * Sometimes we only have this much of the password, while the rest of + * the time this call avoids calling E_md4hash themselves. + * * @param cred credentials context * @retval If set, the cleartext password, otherwise NULL */ @@ -566,7 +581,13 @@ void cli_credentials_set_anonymous(struct cli_credentials *cred) BOOL cli_credentials_is_anonymous(struct cli_credentials *cred) { - const char *username = cli_credentials_get_username(cred); + const char *username; + + if (cred->machine_account_pending) { + cli_credentials_set_machine_account(cred); + } + + username = cli_credentials_get_username(cred); /* Yes, it is deliberate that we die if we have a NULL pointer * here - anonymous is "", not NULL, which is 'never specified, diff --git a/source4/auth/credentials/credentials.h b/source4/auth/credentials/credentials.h index 3e84db52a5..81773aa70a 100644 --- a/source4/auth/credentials/credentials.h +++ b/source4/auth/credentials/credentials.h @@ -47,9 +47,10 @@ struct cli_credentials { enum credentials_obtained domain_obtained; enum credentials_obtained realm_obtained; enum credentials_obtained ccache_obtained; - enum credentials_obtained gss_creds_obtained; + enum credentials_obtained client_gss_creds_obtained; enum credentials_obtained principal_obtained; enum credentials_obtained keytab_obtained; + enum credentials_obtained server_gss_creds_obtained; const char *workstation; const char *username; @@ -63,8 +64,9 @@ struct cli_credentials { struct samr_Password *nt_hash; struct ccache_container *ccache; - struct gssapi_creds_container *gssapi_creds; + struct gssapi_creds_container *client_gss_creds; struct keytab_container *keytab; + struct gssapi_creds_container *server_gss_creds; const char *(*workstation_cb) (struct cli_credentials *); const char *(*password_cb) (struct cli_credentials *); diff --git a/source4/auth/credentials/credentials_files.c b/source4/auth/credentials/credentials_files.c index 35bbc43b34..6b3c77c4e3 100644 --- a/source4/auth/credentials/credentials_files.c +++ b/source4/auth/credentials/credentials_files.c @@ -164,9 +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 */ -static NTSTATUS cli_credentials_set_secrets(struct cli_credentials *cred, - const char *base, - const char *filter) +NTSTATUS cli_credentials_set_secrets(struct cli_credentials *cred, + const char *base, + const char *filter) { TALLOC_CTX *mem_ctx; @@ -183,6 +183,8 @@ static NTSTATUS cli_credentials_set_secrets(struct cli_credentials *cred, "ntPwdHash", "msDS-KeyVersionNumber", "saltPrincipal", + "privateKeytab", + "krb5Keytab", NULL }; @@ -193,6 +195,7 @@ static NTSTATUS cli_credentials_set_secrets(struct cli_credentials *cred, const char *realm; enum netr_SchannelType sct; const char *salt_principal; + const char *keytab; /* ok, we are going to get it now, don't recurse back here */ cred->machine_account_pending = False; @@ -201,6 +204,7 @@ static NTSTATUS cli_credentials_set_secrets(struct cli_credentials *cred, cred->machine_account = True; mem_ctx = talloc_named(cred, 0, "cli_credentials fetch machine password"); + /* Local secrets are stored in secrets.ldb */ ldb = secrets_db_connect(mem_ctx); if (!ldb) { @@ -279,7 +283,22 @@ static NTSTATUS cli_credentials_set_secrets(struct cli_credentials *cred, } cli_credentials_set_kvno(cred, ldb_msg_find_int(msgs[0], "msDS-KeyVersionNumber", 0)); - + + /* If there was an external keytab specified by reference in + * the LDB, then use this. Otherwise we will make one up + * (chewing CPU time) from the password */ + keytab = ldb_msg_find_string(msgs[0], "krb5Keytab", NULL); + if (keytab) { + cli_credentials_set_keytab(cred, keytab, CRED_SPECIFIED); + } else { + keytab = ldb_msg_find_string(msgs[0], "privateKeytab", NULL); + if (keytab) { + keytab = talloc_asprintf(mem_ctx, "FILE:%s", private_path(mem_ctx, keytab)); + if (keytab) { + cli_credentials_set_keytab(cred, keytab, CRED_SPECIFIED); + } + } + } talloc_free(mem_ctx); return NT_STATUS_OK; @@ -345,3 +364,68 @@ void cli_credentials_set_machine_account_pending(struct cli_credentials *cred) cred->machine_account_pending = True; } + +NTSTATUS cli_credentials_update_all_keytabs(TALLOC_CTX *parent_ctx) +{ + TALLOC_CTX *mem_ctx; + int ldb_ret; + struct ldb_context *ldb; + struct ldb_message **msgs; + const char *attrs[] = { NULL }; + struct cli_credentials *creds; + const char *filter; + NTSTATUS status; + int i, ret; + + mem_ctx = talloc_new(parent_ctx); + if (!mem_ctx) { + return NT_STATUS_NO_MEMORY; + } + + /* Local secrets are stored in secrets.ldb */ + ldb = secrets_db_connect(mem_ctx); + if (!ldb) { + DEBUG(1, ("Could not open secrets.ldb\n")); + talloc_free(mem_ctx); + return NT_STATUS_ACCESS_DENIED; + } + + /* search for the secret record */ + ldb_ret = gendb_search(ldb, + mem_ctx, NULL, + &msgs, attrs, + "objectClass=kerberosSecret"); + if (ldb_ret == -1) { + DEBUG(1, ("Error looking for kerberos type secrets to push into a keytab")); + talloc_free(mem_ctx); + return NT_STATUS_INTERNAL_DB_CORRUPTION; + } + + for (i=0; i < ldb_ret; i++) { + /* Make a credentials structure from it */ + creds = cli_credentials_init(mem_ctx); + if (!creds) { + DEBUG(1, ("cli_credentials_init failed!")); + talloc_free(mem_ctx); + return NT_STATUS_NO_MEMORY; + } + cli_credentials_set_conf(creds); + filter = talloc_asprintf(mem_ctx, "dn=%s", ldb_dn_linearize(mem_ctx, msgs[i]->dn)); + status = cli_credentials_set_secrets(creds, NULL, filter); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(1, ("Failed to read secrets for keytab update for %s\n", + filter)); + talloc_free(mem_ctx); + return status; + } + ret = cli_credentials_update_keytab(creds); + if (ret != 0) { + DEBUG(1, ("Failed to update keytab for %s\n", + filter)); + talloc_free(mem_ctx); + return NT_STATUS_UNSUCCESSFUL; + } + } + return NT_STATUS_OK; +} + diff --git a/source4/auth/credentials/credentials_krb5.c b/source4/auth/credentials/credentials_krb5.c index a3761e8359..173739e9b8 100644 --- a/source4/auth/credentials/credentials_krb5.c +++ b/source4/auth/credentials/credentials_krb5.c @@ -52,6 +52,10 @@ int cli_credentials_set_from_ccache(struct cli_credentials *cred, char *name; char **realm; + if (cred->ccache_obtained > obtained) { + return 0; + } + ret = krb5_cc_get_principal(cred->ccache->smb_krb5_context->krb5_context, cred->ccache->ccache, &princ); @@ -107,7 +111,12 @@ int cli_credentials_set_ccache(struct cli_credentials *cred, { krb5_error_code ret; krb5_principal princ; - struct ccache_container *ccc = talloc(cred, struct ccache_container); + struct ccache_container *ccc; + if (cred->ccache_obtained > obtained) { + return 0; + } + + ccc = talloc(cred, struct ccache_container); if (!ccc) { return ENOMEM; } @@ -265,10 +274,10 @@ int cli_credentials_get_client_gss_creds(struct cli_credentials *cred, OM_uint32 maj_stat, min_stat; struct gssapi_creds_container *gcc; struct ccache_container *ccache; - if (cred->gss_creds_obtained >= (MAX(cred->ccache_obtained, + if (cred->client_gss_creds_obtained >= (MAX(cred->ccache_obtained, MAX(cred->principal_obtained, cred->username_obtained)))) { - *_gcc = cred->gssapi_creds; + *_gcc = cred->client_gss_creds; return 0; } ret = cli_credentials_get_ccache(cred, @@ -283,8 +292,8 @@ int cli_credentials_get_client_gss_creds(struct cli_credentials *cred, return ENOMEM; } - maj_stat = gss_krb5_import_ccache(&min_stat, ccache->ccache, - &gcc->creds); + maj_stat = gss_krb5_import_cred(&min_stat, ccache->ccache, NULL, NULL, + &gcc->creds); if (maj_stat) { if (min_stat) { ret = min_stat; @@ -293,20 +302,20 @@ int cli_credentials_get_client_gss_creds(struct cli_credentials *cred, } } if (ret == 0) { - cred->gss_creds_obtained = cred->ccache_obtained; + cred->client_gss_creds_obtained = cred->ccache_obtained; talloc_set_destructor(gcc, free_gssapi_creds); - cred->gssapi_creds = gcc; + cred->client_gss_creds = gcc; *_gcc = gcc; } return ret; } /** - Set a gssapi cred_id_t into the credentails system. + Set a gssapi cred_id_t into the credentails system. (Client case) This grabs the credentials both 'intact' and getting the krb5 ccache out of it. This routine can be generalised in future for - the case where we deal with GSSAPI mechs other than krb5. + the case where we deal with GSSAPI mechs other than krb5. On sucess, the caller must not free gssapi_cred, as it now belongs to the credentials system. @@ -319,7 +328,12 @@ int cli_credentials_get_client_gss_creds(struct cli_credentials *cred, int ret; OM_uint32 maj_stat, min_stat; struct ccache_container *ccc; - struct gssapi_creds_container *gcc = talloc(cred, struct gssapi_creds_container); + struct gssapi_creds_container *gcc; + if (cred->client_gss_creds_obtained > obtained) { + return 0; + } + + gcc = talloc(cred, struct gssapi_creds_container); if (!gcc) { return ENOMEM; } @@ -346,18 +360,23 @@ int cli_credentials_get_client_gss_creds(struct cli_credentials *cred, gcc->creds = gssapi_cred; talloc_set_destructor(gcc, free_gssapi_creds); - cred->gss_creds_obtained = obtained; - cred->gssapi_creds = gcc; + cred->client_gss_creds_obtained = obtained; + cred->client_gss_creds = gcc; } return ret; } +/* Get the keytab (actually, a container containing the krb5_keytab) + * attached to this context. If this hasn't been done or set before, + * it will be generated from the password. + */ 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; + TALLOC_CTX *mem_ctx; if (cred->keytab_obtained >= (MAX(cred->principal_obtained, cred->username_obtained))) { @@ -374,16 +393,180 @@ int cli_credentials_get_keytab(struct cli_credentials *cred, return ret; } - ret = create_memory_keytab(cred, cred, smb_krb5_context, &ktc); + mem_ctx = talloc_new(cred); + if (!mem_ctx) { + return ENOMEM; + } + + ret = create_memory_keytab(mem_ctx, cred, smb_krb5_context, &ktc); if (ret) { + talloc_free(mem_ctx); return ret; } cred->keytab_obtained = (MAX(cred->principal_obtained, cred->username_obtained)); + talloc_steal(cred, ktc); cred->keytab = ktc; *_ktc = cred->keytab; + talloc_free(mem_ctx); + return ret; +} + +/* Given the name of a keytab (presumably in the format + * FILE:/etc/krb5.keytab), open it and attach it */ + +int cli_credentials_set_keytab(struct cli_credentials *cred, + const char *keytab_name, + enum credentials_obtained obtained) +{ + krb5_error_code ret; + struct keytab_container *ktc; + struct smb_krb5_context *smb_krb5_context; + krb5_keytab keytab; + TALLOC_CTX *mem_ctx; + + if (cred->keytab_obtained >= obtained) { + return 0; + } + + ret = cli_credentials_get_krb5_context(cred, &smb_krb5_context); + if (ret) { + return ret; + } + + mem_ctx = talloc_new(cred); + if (!mem_ctx) { + return ENOMEM; + } + + ret = krb5_kt_resolve(smb_krb5_context->krb5_context, keytab_name, &keytab); + if (ret) { + DEBUG(1,("failed to open krb5 keytab: %s\n", + smb_get_krb5_error_message(smb_krb5_context->krb5_context, + ret, mem_ctx))); + talloc_free(mem_ctx); + return ret; + } + + ktc = talloc(mem_ctx, struct keytab_container); + if (!ktc) { + talloc_free(mem_ctx); + return ENOMEM; + } + + ktc->smb_krb5_context = talloc_reference(ktc, smb_krb5_context); + ktc->keytab = keytab; + + cred->keytab_obtained = obtained; + + talloc_steal(cred, ktc); + cred->keytab = ktc; + talloc_free(mem_ctx); + + return ret; +} + +int cli_credentials_update_keytab(struct cli_credentials *cred) +{ + krb5_error_code ret; + struct keytab_container *ktc; + struct smb_krb5_context *smb_krb5_context; + TALLOC_CTX *mem_ctx; + + mem_ctx = talloc_new(cred); + if (!mem_ctx) { + return ENOMEM; + } + + ret = cli_credentials_get_krb5_context(cred, &smb_krb5_context); + if (ret) { + talloc_free(mem_ctx); + return ret; + } + + ret = cli_credentials_get_keytab(cred, &ktc); + if (ret != 0) { + talloc_free(mem_ctx); + return ret; + } + + ret = update_keytab(mem_ctx, cred, smb_krb5_context, ktc); + + talloc_free(mem_ctx); + return ret; +} + +/* Get server gss credentials (in gsskrb5, this means the keytab) */ + +int cli_credentials_get_server_gss_creds(struct cli_credentials *cred, + struct gssapi_creds_container **_gcc) +{ + int ret = 0; + OM_uint32 maj_stat, min_stat; + struct gssapi_creds_container *gcc; + struct keytab_container *ktc; + struct smb_krb5_context *smb_krb5_context; + TALLOC_CTX *mem_ctx; + krb5_principal princ; + + if (cred->server_gss_creds_obtained >= (MAX(cred->keytab_obtained, + MAX(cred->principal_obtained, + cred->username_obtained)))) { + *_gcc = cred->server_gss_creds; + return 0; + } + + ret = cli_credentials_get_krb5_context(cred, &smb_krb5_context); + if (ret) { + return ret; + } + + ret = cli_credentials_get_keytab(cred, + &ktc); + if (ret) { + DEBUG(1, ("Failed to get keytab for GSSAPI server: %s\n", error_message(ret))); + return ret; + } + + mem_ctx = talloc_new(cred); + if (!mem_ctx) { + return ENOMEM; + } + + ret = principal_from_credentials(mem_ctx, cred, smb_krb5_context, &princ); + if (ret) { + DEBUG(1,("cli_credentials_get_server_gss_creds: makeing krb5 principal failed (%s)\n", + smb_get_krb5_error_message(smb_krb5_context->krb5_context, + ret, mem_ctx))); + talloc_free(mem_ctx); + return ret; + } + + gcc = talloc(cred, struct gssapi_creds_container); + if (!gcc) { + talloc_free(mem_ctx); + return ENOMEM; + } + + /* This creates a GSSAPI cred_id_t with the principal and keytab set */ + maj_stat = gss_krb5_import_cred(&min_stat, NULL, princ, ktc->keytab, + &gcc->creds); + if (maj_stat) { + if (min_stat) { + ret = min_stat; + } else { + ret = EINVAL; + } + } + if (ret == 0) { + cred->server_gss_creds_obtained = cred->keytab_obtained; + talloc_set_destructor(gcc, free_gssapi_creds); + cred->server_gss_creds = gcc; + *_gcc = gcc; + } + talloc_free(mem_ctx); return ret; } @@ -415,3 +598,5 @@ void cli_credentials_set_salt_principal(struct cli_credentials *cred, const char { cred->salt_principal = talloc_strdup(cred, principal); } + + |