Copyright (C) Andrew Tridgell 2001
Copyright (C) Luke Howard 2002-2003
Copyright (C) Andrew Bartlett <abartlet@samba.org> 2005
- Copyright (C) Guenther Deschner 2005
+ Copyright (C) Guenther Deschner 2005-2007
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
+ the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#define KRB5_PRIVATE 1 /* this file uses PRIVATE interfaces! */
may be in the middle of a keytab enumeration when this is
called. JRA. */
- ret = krb5_kt_default(context, &keytab);
+ ret = smb_krb5_open_keytab(context, NULL, False, &keytab);
if (ret) {
- DEBUG(0,("get_key_from_keytab: failed to open keytab: %s\n", error_message(ret)));
+ DEBUG(1,("get_key_from_keytab: smb_krb5_open_keytab failed (%s)\n", error_message(ret)));
return ret;
}
krb5_error_code smb_krb5_renew_ticket(const char *ccache_string, /* FILE:/tmp/krb5cc_0 */
const char *client_string, /* gd@BER.SUSE.DE */
const char *service_string, /* krbtgt/BER.SUSE.DE@BER.SUSE.DE */
- time_t *new_start_time)
+ time_t *expire_time)
{
krb5_error_code ret;
krb5_context context = NULL;
krb5_ccache ccache = NULL;
krb5_principal client = NULL;
+ krb5_creds creds, creds_in, *creds_out = NULL;
+
+ ZERO_STRUCT(creds);
+ ZERO_STRUCT(creds_in);
initialize_krb5_error_table();
ret = krb5_init_context(&context);
ccache_string = krb5_cc_default_name(context);
}
+ if (!ccache_string) {
+ ret = EINVAL;
+ goto done;
+ }
+
DEBUG(10,("smb_krb5_renew_ticket: using %s as ccache\n", ccache_string));
/* FIXME: we should not fall back to defaults */
goto done;
}
-#ifdef HAVE_KRB5_GET_RENEWED_CREDS /* MIT */
- {
- krb5_creds creds;
-
- if (client_string) {
- ret = smb_krb5_parse_name(context, client_string, &client);
- if (ret) {
- goto done;
- }
- } else {
- ret = krb5_cc_get_principal(context, ccache, &client);
- if (ret) {
- goto done;
- }
- }
-
- ret = krb5_get_renewed_creds(context, &creds, client, ccache, CONST_DISCARD(char *, service_string));
+ if (client_string) {
+ ret = smb_krb5_parse_name(context, client_string, &client);
if (ret) {
- DEBUG(10,("smb_krb5_renew_ticket: krb5_get_kdc_cred failed: %s\n", error_message(ret)));
goto done;
}
-
- /* hm, doesn't that create a new one if the old one wasn't there? - Guenther */
- ret = krb5_cc_initialize(context, ccache, client);
+ } else {
+ ret = krb5_cc_get_principal(context, ccache, &client);
if (ret) {
goto done;
}
-
- ret = krb5_cc_store_cred(context, ccache, &creds);
+ }
- if (new_start_time) {
- *new_start_time = (time_t) creds.times.renew_till;
+#ifdef HAVE_KRB5_GET_RENEWED_CREDS /* MIT */
+ {
+ ret = krb5_get_renewed_creds(context, &creds, client, ccache, CONST_DISCARD(char *, service_string));
+ if (ret) {
+ DEBUG(10,("smb_krb5_renew_ticket: krb5_get_kdc_cred failed: %s\n", error_message(ret)));
+ goto done;
}
-
- krb5_free_cred_contents(context, &creds);
}
#elif defined(HAVE_KRB5_GET_KDC_CRED) /* Heimdal */
{
krb5_kdc_flags flags;
- krb5_creds creds_in;
- krb5_realm *client_realm;
- krb5_creds *creds;
+ krb5_realm *client_realm = NULL;
- memset(&creds_in, 0, sizeof(creds_in));
-
- if (client_string) {
- ret = smb_krb5_parse_name(context, client_string, &creds_in.client);
- if (ret) {
- goto done;
- }
- } else {
- ret = krb5_cc_get_principal(context, ccache, &creds_in.client);
- if (ret) {
- goto done;
- }
+ ret = krb5_copy_principal(context, client, &creds_in.client);
+ if (ret) {
+ goto done;
}
if (service_string) {
}
} else {
/* build tgt service by default */
- client_realm = krb5_princ_realm(context, client);
+ client_realm = krb5_princ_realm(context, creds_in.client);
+ if (!client_realm) {
+ ret = ENOMEM;
+ goto done;
+ }
ret = krb5_make_principal(context, &creds_in.server, *client_realm, KRB5_TGS_NAME, *client_realm, NULL);
if (ret) {
goto done;
flags.i = 0;
flags.b.renewable = flags.b.renew = True;
- ret = krb5_get_kdc_cred(context, ccache, flags, NULL, NULL, &creds_in, &creds);
+ ret = krb5_get_kdc_cred(context, ccache, flags, NULL, NULL, &creds_in, &creds_out);
if (ret) {
DEBUG(10,("smb_krb5_renew_ticket: krb5_get_kdc_cred failed: %s\n", error_message(ret)));
goto done;
}
-
- /* hm, doesn't that create a new one if the old one wasn't there? - Guenther */
- ret = krb5_cc_initialize(context, ccache, creds_in.client);
- if (ret) {
- goto done;
- }
-
- ret = krb5_cc_store_cred(context, ccache, creds);
- if (new_start_time) {
- *new_start_time = (time_t) creds->times.renew_till;
- }
-
- krb5_free_cred_contents(context, &creds_in);
- krb5_free_creds(context, creds);
+ creds = *creds_out;
}
#else
-#error NO_SUITABKE_KRB5_TICKET_RENEW_FUNCTION_AVAILABLE
+#error NO_SUITABLE_KRB5_TICKET_RENEW_FUNCTION_AVAILABLE
#endif
+ /* hm, doesn't that create a new one if the old one wasn't there? - Guenther */
+ ret = krb5_cc_initialize(context, ccache, client);
+ if (ret) {
+ goto done;
+ }
+
+ ret = krb5_cc_store_cred(context, ccache, &creds);
+
+ if (expire_time) {
+ *expire_time = (time_t) creds.times.endtime;
+ }
done:
+ krb5_free_cred_contents(context, &creds_in);
+
+ if (creds_out) {
+ krb5_free_creds(context, creds_out);
+ } else {
+ krb5_free_cred_contents(context, &creds);
+ }
+
if (client) {
krb5_free_principal(context, client);
}
- if (context) {
- krb5_free_context(context);
- }
if (ccache) {
krb5_cc_close(context, ccache);
}
+ if (context) {
+ krb5_free_context(context);
+ }
return ret;
-
}
krb5_error_code smb_krb5_free_addresses(krb5_context context, smb_krb5_addresses *addr)
#endif
}
+/**********************************************************************
+ * Open a krb5 keytab with flags, handles readonly or readwrite access and
+ * allows to process non-default keytab names.
+ * @param context krb5_context
+ * @param keytab_name_req string
+ * @param write_access BOOL if writable keytab is required
+ * @param krb5_keytab pointer to krb5_keytab (close with krb5_kt_close())
+ * @return krb5_error_code
+**********************************************************************/
+
+/* This MAX_NAME_LEN is a constant defined in krb5.h */
+#ifndef MAX_KEYTAB_NAME_LEN
+#define MAX_KEYTAB_NAME_LEN 1100
+#endif
+
+ krb5_error_code smb_krb5_open_keytab(krb5_context context,
+ const char *keytab_name_req,
+ BOOL write_access,
+ krb5_keytab *keytab)
+{
+ krb5_error_code ret = 0;
+ TALLOC_CTX *mem_ctx;
+ char keytab_string[MAX_KEYTAB_NAME_LEN];
+ BOOL found_valid_name = False;
+ const char *pragma = "FILE";
+ const char *tmp = NULL;
+
+ if (!write_access && !keytab_name_req) {
+ /* caller just wants to read the default keytab readonly, so be it */
+ return krb5_kt_default(context, keytab);
+ }
+
+ mem_ctx = talloc_init("smb_krb5_open_keytab");
+ if (!mem_ctx) {
+ return ENOMEM;
+ }
+
+#ifdef HAVE_WRFILE_KEYTAB
+ if (write_access) {
+ pragma = "WRFILE";
+ }
+#endif
+
+ if (keytab_name_req) {
+
+ if (strlen(keytab_name_req) > MAX_KEYTAB_NAME_LEN) {
+ ret = KRB5_CONFIG_NOTENUFSPACE;
+ goto out;
+ }
+
+ if ((strncmp(keytab_name_req, "WRFILE:/", 8) == 0) ||
+ (strncmp(keytab_name_req, "FILE:/", 6) == 0)) {
+ tmp = keytab_name_req;
+ goto resolve;
+ }
+
+ if (keytab_name_req[0] != '/') {
+ ret = KRB5_KT_BADNAME;
+ goto out;
+ }
+
+ tmp = talloc_asprintf(mem_ctx, "%s:%s", pragma, keytab_name_req);
+ if (!tmp) {
+ ret = ENOMEM;
+ goto out;
+ }
+
+ goto resolve;
+ }
+
+ /* we need to handle more complex keytab_strings, like:
+ * "ANY:FILE:/etc/krb5.keytab,krb4:/etc/srvtab" */
+
+ ret = krb5_kt_default_name(context, &keytab_string[0], MAX_KEYTAB_NAME_LEN - 2);
+ if (ret) {
+ goto out;
+ }
+
+ DEBUG(10,("smb_krb5_open_keytab: krb5_kt_default_name returned %s\n", keytab_string));
+
+ tmp = talloc_strdup(mem_ctx, keytab_string);
+ if (!tmp) {
+ ret = ENOMEM;
+ goto out;
+ }
+
+ if (strncmp(tmp, "ANY:", 4) == 0) {
+ tmp += 4;
+ }
+
+ memset(&keytab_string, '\0', sizeof(keytab_string));
+
+ while (next_token(&tmp, keytab_string, ",", sizeof(keytab_string))) {
+
+ if (strncmp(keytab_string, "WRFILE:", 7) == 0) {
+ found_valid_name = True;
+ tmp = keytab_string;
+ tmp += 7;
+ }
+
+ if (strncmp(keytab_string, "FILE:", 5) == 0) {
+ found_valid_name = True;
+ tmp = keytab_string;
+ tmp += 5;
+ }
+
+ if (found_valid_name) {
+
+ if (tmp[0] != '/') {
+ ret = KRB5_KT_BADNAME;
+ goto out;
+ }
+
+ tmp = talloc_asprintf(mem_ctx, "%s:%s", pragma, tmp);
+ if (!tmp) {
+ ret = ENOMEM;
+ goto out;
+ }
+ break;
+ }
+ }
+
+ if (!found_valid_name) {
+ ret = KRB5_KT_UNKNOWN_TYPE;
+ goto out;
+ }
+
+ resolve:
+ DEBUG(10,("smb_krb5_open_keytab: resolving: %s\n", tmp));
+ ret = krb5_kt_resolve(context, tmp, keytab);
+
+ out:
+ TALLOC_FREE(mem_ctx);
+ return ret;
+}
+
#else /* HAVE_KRB5 */
/* this saves a few linking headaches */
int cli_krb5_get_ticket(const char *principal, time_t time_offset,