diff options
author | Andrew Bartlett <abartlet@samba.org> | 2005-08-29 04:30:22 +0000 |
---|---|---|
committer | Gerald (Jerry) Carter <jerry@samba.org> | 2007-10-10 13:34:54 -0500 |
commit | 24186a80eb4887b5fb3e72e4b877b456cbe8e35f (patch) | |
tree | 4f5b7d9147ec0b450ca0da5023113c8c5aad2182 /source4/lib | |
parent | 1a316fd8c50e501b5d69bb47ff5d1d483b02b04e (diff) | |
download | samba-24186a80eb4887b5fb3e72e4b877b456cbe8e35f.tar.gz samba-24186a80eb4887b5fb3e72e4b877b456cbe8e35f.tar.bz2 samba-24186a80eb4887b5fb3e72e4b877b456cbe8e35f.zip |
r9728: A *major* update to the credentials system, to incorporate the
Kerberos CCACHE into the system.
This again allows the use of the system ccache when no username is
specified, and brings more code in common between gensec_krb5 and
gensec_gssapi.
It also has a side-effect that may (or may not) be expected: If there
is a ccache, even if it is not used (perhaps the remote server didn't
want kerberos), it will change the default username.
Andrew Bartlett
(This used to be commit 6202267f6ec1446d6bd11d1d37d05a977bc8d315)
Diffstat (limited to 'source4/lib')
-rw-r--r-- | source4/lib/cmdline/credentials.c | 19 | ||||
-rw-r--r-- | source4/lib/cmdline/popt_common.c | 8 | ||||
-rw-r--r-- | source4/lib/credentials.c | 318 | ||||
-rw-r--r-- | source4/lib/ldb/ldb_ildap/ldb_ildap.c | 3 | ||||
-rw-r--r-- | source4/lib/samba3/samba3dump.c | 7 |
5 files changed, 312 insertions, 43 deletions
diff --git a/source4/lib/cmdline/credentials.c b/source4/lib/cmdline/credentials.c index d827baed76..7832e01e4b 100644 --- a/source4/lib/cmdline/credentials.c +++ b/source4/lib/cmdline/credentials.c @@ -29,14 +29,23 @@ static const char *cmdline_get_userpassword(struct cli_credentials *credentials) { char *prompt; char *ret; - - prompt = talloc_asprintf(NULL, "Password for [%s\\%s]:", - cli_credentials_get_domain(credentials), - cli_credentials_get_username(credentials)); + char *domain; + char *username; + TALLOC_CTX *mem_ctx = talloc_new(NULL); + + domain = cli_credentials_get_domain(credentials); + username = cli_credentials_get_username(credentials, mem_ctx); + if (domain && domain[0]) { + prompt = talloc_asprintf(mem_ctx, "Password for [%s\\%s]:", + domain, username); + } else { + prompt = talloc_asprintf(mem_ctx, "Password for [%s]:", + username); + } ret = getpass(prompt); - talloc_free(prompt); + talloc_free(mem_ctx); return ret; } diff --git a/source4/lib/cmdline/popt_common.c b/source4/lib/cmdline/popt_common.c index 4e808652f7..d3bd0a35a4 100644 --- a/source4/lib/cmdline/popt_common.c +++ b/source4/lib/cmdline/popt_common.c @@ -220,9 +220,11 @@ static void popt_common_credentials_callback(poptContext con, char *lp; cli_credentials_parse_string(cmdline_credentials, arg, CRED_SPECIFIED); - - if (cmdline_credentials->password && (lp=strchr_m(arg,'%'))) { - memset(lp,0,strlen(cmdline_credentials->password)); + /* This breaks the abstraction, including the const above */ + if (lp=strchr_m(arg,'%')) { + lp[0]='\0'; + lp++; + memset(lp,0,strlen(lp)); } } break; diff --git a/source4/lib/credentials.c b/source4/lib/credentials.c index aae55be800..69e237428c 100644 --- a/source4/lib/credentials.c +++ b/source4/lib/credentials.c @@ -25,6 +25,9 @@ #include "include/secrets.h" #include "lib/ldb/include/ldb.h" #include "librpc/gen_ndr/ndr_samr.h" /* for struct samrPassword */ +#include "system/kerberos.h" +#include "auth/kerberos/kerberos.h" + /** * Create a new credentials structure @@ -44,6 +47,8 @@ struct cli_credentials *cli_credentials_init(TALLOC_CTX *mem_ctx) cred->password_obtained = CRED_UNINITIALISED; cred->domain_obtained = CRED_UNINITIALISED; cred->realm_obtained = CRED_UNINITIALISED; + cred->ccache_obtained = CRED_UNINITIALISED; + cred->principal_obtained = CRED_UNINITIALISED; return cred; } @@ -53,18 +58,23 @@ struct cli_credentials *cli_credentials_init(TALLOC_CTX *mem_ctx) * @retval The username set on this context. * @note Return value will never be NULL except by programmer error. */ -const char *cli_credentials_get_username(struct cli_credentials *cred) +const char *cli_credentials_get_username(struct cli_credentials *cred, TALLOC_CTX *mem_ctx) { if (cred->machine_account_pending) { cli_credentials_set_machine_account(cred); } + /* If we have a principal set on this, we want to login with "" domain and user@realm */ + if (cred->username_obtained < cred->principal_obtained) { + return cli_credentials_get_principal(cred, mem_ctx); + } + if (cred->username_obtained == CRED_CALLBACK) { cred->username = cred->username_cb(cred); cred->username_obtained = CRED_SPECIFIED; } - return cred->username; + return talloc_reference(mem_ctx, cred->username); } BOOL cli_credentials_set_username(struct cli_credentials *cred, const char *val, enum credentials_obtained obtained) @@ -79,6 +89,53 @@ BOOL cli_credentials_set_username(struct cli_credentials *cred, const char *val, } /** + * Obtain the client principal for this credentials context. + * @param cred credentials context + * @retval The username set on this context. + * @note Return value will never be NULL except by programmer error. + */ +const char *cli_credentials_get_principal(struct cli_credentials *cred, TALLOC_CTX *mem_ctx) +{ + if (cred->machine_account_pending) { + cli_credentials_set_machine_account(cred); + } + + if (cred->principal_obtained == CRED_CALLBACK) { + cred->principal = cred->principal_cb(cred); + cred->principal_obtained = CRED_SPECIFIED; + } + + if (cred->principal_obtained < cred->username_obtained) { + return talloc_asprintf(mem_ctx, "%s@%s", + cli_credentials_get_username(cred, mem_ctx), + cli_credentials_get_realm(cred)); + } + return talloc_reference(mem_ctx, cred->principal); +} + +BOOL cli_credentials_set_principal(struct cli_credentials *cred, const char *val, enum credentials_obtained obtained) +{ + if (obtained >= cred->principal_obtained) { + cred->principal = talloc_strdup(cred, val); + cred->principal_obtained = obtained; + return True; + } + + return False; +} + +BOOL cli_credentials_authentication_requested(struct cli_credentials *cred) +{ + if (cred->principal_obtained == CRED_SPECIFIED) { + return True; + } + if (cred->username_obtained >= CRED_SPECIFIED) { + return True; + } + return False; +} + +/** * Obtain the password for this credentials context. * @param cred credentials context * @retval If set, the cleartext password, otherwise NULL @@ -148,6 +205,207 @@ BOOL cli_credentials_set_nt_hash(struct cli_credentials *cred, return False; } +int cli_credentials_set_from_ccache(struct cli_credentials *cred, + enum credentials_obtained obtained) +{ + + krb5_principal princ; + krb5_error_code ret; + char *name; + char **realm; + + ret = krb5_cc_get_principal(cred->ccache->smb_krb5_context->krb5_context, + cred->ccache->ccache, &princ); + + if (ret) { + char *err_mess = smb_get_krb5_error_message(cred->ccache->smb_krb5_context->krb5_context, ret, cred); + DEBUG(1,("failed to get principal from ccache: %s\n", + err_mess)); + talloc_free(err_mess); + return ret; + } + + ret = krb5_unparse_name(cred->ccache->smb_krb5_context->krb5_context, princ, &name); + if (ret) { + char *err_mess = smb_get_krb5_error_message(cred->ccache->smb_krb5_context->krb5_context, ret, cred); + DEBUG(1,("failed to unparse principal from ccache: %s\n", + err_mess)); + talloc_free(err_mess); + return ret; + } + + realm = krb5_princ_realm(cred->ccache->smb_krb5_context->krb5_context, princ); + + cli_credentials_set_realm(cred, *realm, obtained); + cli_credentials_set_principal(cred, name, obtained); + + free(name); + + krb5_free_principal(cred->ccache->smb_krb5_context->krb5_context, princ); + + cred->ccache_obtained = obtained; + + return 0; +} + + +static int free_mccache(void *ptr) { + struct ccache_container *ccc = ptr; + krb5_cc_destroy(ccc->smb_krb5_context->krb5_context, ccc->ccache); + + return 0; +} + +static int free_dccache(void *ptr) { + struct ccache_container *ccc = ptr; + krb5_cc_close(ccc->smb_krb5_context->krb5_context, ccc->ccache); + + return 0; +} + +static int cli_credentials_set_ccache(struct cli_credentials *cred, + const char *name, + enum credentials_obtained obtained) +{ + krb5_error_code ret; + krb5_principal princ; + struct ccache_container *ccc = talloc(cred, struct ccache_container); + if (!ccc) { + return ENOMEM; + } + + ret = smb_krb5_init_context(ccc, &ccc->smb_krb5_context); + if (ret) { + talloc_free(ccc); + return ret; + } + if (name) { + ret = krb5_cc_resolve(ccc->smb_krb5_context->krb5_context, name, &ccc->ccache); + if (ret) { + DEBUG(1,("failed to read krb5 ccache: %s: %s\n", + name, + smb_get_krb5_error_message(ccc->smb_krb5_context->krb5_context, ret, ccc))); + talloc_free(ccc); + return ret; + } + } else { + ret = krb5_cc_default(ccc->smb_krb5_context->krb5_context, &ccc->ccache); + if (ret) { + DEBUG(1,("failed to read default krb5 ccache: %s\n", + smb_get_krb5_error_message(ccc->smb_krb5_context->krb5_context, ret, ccc))); + talloc_free(ccc); + return ret; + } + } + + talloc_set_destructor(ccc, free_dccache); + + ret = krb5_cc_get_principal(ccc->smb_krb5_context->krb5_context, ccc->ccache, &princ); + + if (ret) { + DEBUG(1,("failed to get principal from default ccache: %s\n", + smb_get_krb5_error_message(ccc->smb_krb5_context->krb5_context, ret, ccc))); + talloc_free(ccc); + return ret; + } + + krb5_free_principal(ccc->smb_krb5_context->krb5_context, princ); + + cred->ccache = ccc; + talloc_steal(cred, ccc); + + ret = cli_credentials_set_from_ccache(cred, obtained); + + if (ret) { + return ret; + } + + return 0; +} + + +int cli_credentials_new_ccache(struct cli_credentials *cred) +{ + krb5_error_code ret; + char *rand_string; + struct ccache_container *ccc = talloc(cred, struct ccache_container); + char *ccache_name; + if (!ccc) { + return ENOMEM; + } + + rand_string = generate_random_str(NULL, 16); + if (!rand_string) { + talloc_free(ccc); + return ENOMEM; + } + + ccache_name = talloc_asprintf(ccc, "MEMORY:%s", + rand_string); + talloc_free(rand_string); + + if (!ccache_name) { + talloc_free(ccc); + return ENOMEM; + } + + ret = smb_krb5_init_context(ccc, &ccc->smb_krb5_context); + if (ret) { + talloc_free(ccache_name); + talloc_free(ccc); + return ret; + } + + ret = krb5_cc_resolve(ccc->smb_krb5_context->krb5_context, ccache_name, &ccc->ccache); + if (ret) { + DEBUG(1,("failed to generate a new krb5 ccache (%s): %s\n", + ccache_name, + smb_get_krb5_error_message(ccc->smb_krb5_context->krb5_context, ret, ccc))); + talloc_free(ccache_name); + talloc_free(ccc); + return ret; + } + + talloc_set_destructor(ccc, free_mccache); + + cred->ccache = ccc; + talloc_steal(cred, ccc); + talloc_free(ccache_name); + + return ret; +} + +int cli_credentials_get_ccache(struct cli_credentials *cred, struct ccache_container **ccc) +{ + krb5_error_code ret; + + if (cred->ccache_obtained >= (MAX(cred->principal_obtained, + cred->username_obtained))) { + *ccc = cred->ccache; + return 0; + } + if (cli_credentials_is_anonymous(cred)) { + return EINVAL; + } + + ret = cli_credentials_new_ccache(cred); + if (ret) { + return ret; + } + ret = kinit_to_ccache(cred, cred, cred->ccache->smb_krb5_context, cred->ccache->ccache); + if (ret) { + return ret; + } + ret = cli_credentials_set_from_ccache(cred, cred->principal_obtained); + + if (ret) { + return ret; + } + *ccc = cred->ccache; + return ret; +} + + /** * Obtain the 'short' or 'NetBIOS' domain for this credentials context. * @param cred credentials context @@ -160,6 +418,11 @@ const char *cli_credentials_get_domain(struct cli_credentials *cred) cli_credentials_set_machine_account(cred); } + /* If we have a principal set on this, we want to login with "" domain and user@realm */ + if (cred->domain_obtained < cred->principal_obtained) { + return ""; + } + if (cred->domain_obtained == CRED_CALLBACK) { cred->domain = cred->domain_cb(cred); cred->domain_obtained = CRED_SPECIFIED; @@ -201,21 +464,6 @@ const char *cli_credentials_get_realm(struct cli_credentials *cred) } /** - * Obtain the user's Kerberos principal for this credentials context. - * @param cred credentials context - * @param mem_ctx A talloc context to return the prinipal name on. - * @retval The user's Kerberos principal - * @note Return value may be NULL due to out-of memeory or invalid mem_ctx - */ -char *cli_credentials_get_principal(struct cli_credentials *cred, - TALLOC_CTX *mem_ctx) -{ - return talloc_asprintf(mem_ctx, "%s@%s", - cli_credentials_get_username(cred), - cli_credentials_get_realm(cred)); -} - -/** * Set the realm for this credentials context, and force it to * uppercase for the sainity of our local kerberos libraries */ @@ -419,8 +667,10 @@ void cli_credentials_parse_string(struct cli_credentials *credentials, const cha } if ((p = strchr_m(uname,'@'))) { + cli_credentials_set_principal(credentials, uname, obtained); *p = 0; cli_credentials_set_realm(credentials, p+1, obtained); + return; } else if ((p = strchr_m(uname,'\\')) || (p = strchr_m(uname, '/'))) { *p = 0; cli_credentials_set_domain(credentials, uname, obtained); @@ -437,9 +687,10 @@ void cli_credentials_parse_string(struct cli_credentials *credentials, const cha */ void cli_credentials_set_conf(struct cli_credentials *cred) { - cli_credentials_set_domain(cred, lp_workgroup(), CRED_GUESSED); - cli_credentials_set_workstation(cred, lp_netbios_name(), CRED_GUESSED); - cli_credentials_set_realm(cred, lp_realm(), CRED_GUESSED); + cli_credentials_set_username(cred, "", CRED_UNINITIALISED); + cli_credentials_set_domain(cred, lp_workgroup(), CRED_UNINITIALISED); + cli_credentials_set_workstation(cred, lp_netbios_name(), CRED_UNINITIALISED); + cli_credentials_set_realm(cred, lp_realm(), CRED_UNINITIALISED); } /** @@ -452,35 +703,36 @@ void cli_credentials_guess(struct cli_credentials *cred) { char *p; - cli_credentials_set_username(cred, "", CRED_GUESSED); cli_credentials_set_conf(cred); if (getenv("LOGNAME")) { - cli_credentials_set_username(cred, getenv("LOGNAME"), CRED_GUESSED); + cli_credentials_set_username(cred, getenv("LOGNAME"), CRED_GUESS_ENV); } if (getenv("USER")) { - cli_credentials_parse_string(cred, getenv("USER"), CRED_GUESSED); + cli_credentials_parse_string(cred, getenv("USER"), CRED_GUESS_ENV); if ((p = strchr_m(getenv("USER"),'%'))) { memset(p,0,strlen(cred->password)); } } if (getenv("DOMAIN")) { - cli_credentials_set_domain(cred, getenv("DOMAIN"), CRED_GUESSED); + cli_credentials_set_domain(cred, getenv("DOMAIN"), CRED_GUESS_ENV); } if (getenv("PASSWD")) { - cli_credentials_set_password(cred, getenv("PASSWD"), CRED_GUESSED); + cli_credentials_set_password(cred, getenv("PASSWD"), CRED_GUESS_ENV); } if (getenv("PASSWD_FD")) { - cli_credentials_parse_password_fd(cred, atoi(getenv("PASSWD_FD")), CRED_GUESSED); + cli_credentials_parse_password_fd(cred, atoi(getenv("PASSWD_FD")), CRED_GUESS_FILE); } if (getenv("PASSWD_FILE")) { - cli_credentials_parse_password_file(cred, getenv("PASSWD_FILE"), CRED_GUESSED); + cli_credentials_parse_password_file(cred, getenv("PASSWD_FILE"), CRED_GUESS_FILE); } + + cli_credentials_set_ccache(cred, NULL, CRED_GUESS_FILE); } /** @@ -690,13 +942,17 @@ 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); - + TALLOC_CTX *tmp_ctx = talloc_new(cred); + const char *username = cli_credentials_get_username(cred, tmp_ctx); + /* Yes, it is deliberate that we die if we have a NULL pointer * here - anonymous is "", not NULL, which is 'never specified, * never guessed', ie programmer bug */ - if (!username[0]) + if (!username[0]) { + talloc_free(tmp_ctx); return True; - + } + + talloc_free(tmp_ctx); return False; } diff --git a/source4/lib/ldb/ldb_ildap/ldb_ildap.c b/source4/lib/ldb/ldb_ildap/ldb_ildap.c index bb89fc910e..3d47863067 100644 --- a/source4/lib/ldb/ldb_ildap/ldb_ildap.c +++ b/source4/lib/ldb/ldb_ildap/ldb_ildap.c @@ -474,8 +474,7 @@ int ildb_connect(struct ldb_context *ldb, const char *url, ldb->modules->private_data = ildb; ldb->modules->ops = &ildb_ops; - if (cmdline_credentials != NULL && - cmdline_credentials->username_obtained > CRED_GUESSED) { + if (cmdline_credentials != NULL && cli_credentials_authentication_requested(cmdline_credentials)) { status = ldap_bind_sasl(ildb->ldap, cmdline_credentials); if (!NT_STATUS_IS_OK(status)) { ldb_debug(ldb, LDB_DEBUG_ERROR, "Failed to bind - %s\n", diff --git a/source4/lib/samba3/samba3dump.c b/source4/lib/samba3/samba3dump.c index b95fc0d4b2..6be91c0db2 100644 --- a/source4/lib/samba3/samba3dump.c +++ b/source4/lib/samba3/samba3dump.c @@ -86,8 +86,11 @@ static NTSTATUS print_samba3_secrets(struct samba3_secrets *secrets) print_header("Secrets"); printf("IPC Credentials:\n"); - if (secrets->ipc_cred->username_obtained) - printf(" User: %s\n", cli_credentials_get_username(secrets->ipc_cred)); + if (secrets->ipc_cred->username_obtained) { + TALLOC_CTX *mem_ctx = talloc_new(NULL); + printf(" User: %s\n", cli_credentials_get_username(secrets->ipc_cred, mem_ctx)); + talloc_free(mem_ctx); + } if (secrets->ipc_cred->password_obtained) printf(" Password: %s\n", cli_credentials_get_password(secrets->ipc_cred)); |