#define KRB5_KEY_DATA(k) ((k)->contents)
#endif /* HAVE_KRB5_KEYBLOCK_KEYVALUE */
+/**************************************************************
+ Wrappers around kerberos string functions that convert from
+ utf8 -> unix charset and vica versa.
+**************************************************************/
+
+/**************************************************************
+ krb5_parse_name that takes a UNIX charset.
+**************************************************************/
+
+ krb5_error_code smb_krb5_parse_name(krb5_context context,
+ const char *name, /* in unix charset */
+ krb5_principal *principal)
+{
+ krb5_error_code ret;
+ char *utf8_name;
+
+ if (push_utf8_allocate(&utf8_name, name) == (size_t)-1) {
+ return ENOMEM;
+ }
+
+ ret = krb5_parse_name(context, utf8_name, principal);
+ SAFE_FREE(utf8_name);
+ return ret;
+}
+
+#ifdef HAVE_KRB5_PARSE_NAME_NOREALM
+/**************************************************************
+ krb5_parse_name_norealm that takes a UNIX charset.
+**************************************************************/
+
+static krb5_error_code smb_krb5_parse_name_norealm_conv(krb5_context context,
+ const char *name, /* in unix charset */
+ krb5_principal *principal)
+{
+ krb5_error_code ret;
+ char *utf8_name;
+
+ if (push_utf8_allocate(&utf8_name, name) == (size_t)-1) {
+ return ENOMEM;
+ }
+
+ ret = krb5_parse_name_norealm(context, utf8_name, principal);
+ SAFE_FREE(utf8_name);
+ return ret;
+}
+#endif
+
+/**************************************************************
+ krb5_parse_name that returns a UNIX charset name. Must
+ be freed with normal free() call.
+**************************************************************/
+
+ krb5_error_code smb_krb5_unparse_name(krb5_context context,
+ krb5_const_principal principal,
+ char **unix_name)
+{
+ krb5_error_code ret;
+ char *utf8_name;
+
+ ret = krb5_unparse_name(context, principal, &utf8_name);
+ if (ret) {
+ return ret;
+ }
+
+ if (pull_utf8_allocate(unix_name, utf8_name)==-1) {
+ krb5_free_unparsed_name(context, utf8_name);
+ return ENOMEM;
+ }
+ krb5_free_unparsed_name(context, utf8_name);
+ return 0;
+}
+
#ifndef HAVE_KRB5_SET_REAL_TIME
/*
* This function is not in the Heimdal mainline.
krb5_creds *credsp)
{
krb5_error_code retval;
+ const char *cc_type = krb5_cc_get_type(context, ccache);
- DEBUG(3, ("Ticket in ccache[%s] expiration %s\n",
- krb5_cc_default_name(context),
+ DEBUG(3, ("ads_cleanup_expired_creds: Ticket in ccache[%s:%s] expiration %s\n",
+ cc_type, krb5_cc_get_name(context, ccache),
http_timestring(credsp->times.endtime)));
/* we will probably need new tickets if the current ones
use memory ccaches, and a FILE one probably means that
we're using creds obtained outside of our exectuable
*/
- if (StrCaseCmp(krb5_cc_get_type(context, ccache), "FILE") == 0) {
- DEBUG(5, ("ads_cleanup_expired_creds: We do not remove creds from a FILE ccache\n"));
+ if (strequal(cc_type, "FILE")) {
+ DEBUG(5, ("ads_cleanup_expired_creds: We do not remove creds from a %s ccache\n", cc_type));
return False;
}
-
+
retval = krb5_cc_remove_cred(context, ccache, 0, credsp);
if (retval) {
DEBUG(1, ("ads_cleanup_expired_creds: krb5_cc_remove_cred failed, err %s\n",
krb5_creds creds;
krb5_data in_data;
BOOL creds_ready = False;
+ int i = 0, maxtries = 3;
- retval = krb5_parse_name(context, principal, &server);
+ retval = smb_krb5_parse_name(context, principal, &server);
if (retval) {
DEBUG(1,("ads_krb5_mk_req: Failed to parse principal %s\n", principal));
return retval;
/* obtain ticket & session key */
ZERO_STRUCT(creds);
if ((retval = krb5_copy_principal(context, server, &creds.server))) {
- DEBUG(1,("krb5_copy_principal failed (%s)\n",
+ DEBUG(1,("ads_krb5_mk_req: krb5_copy_principal failed (%s)\n",
error_message(retval)));
goto cleanup_princ;
}
goto cleanup_creds;
}
- while(!creds_ready) {
+ while (!creds_ready && (i < maxtries)) {
if ((retval = krb5_get_credentials(context, 0, ccache,
&creds, &credsp))) {
DEBUG(1,("ads_krb5_mk_req: krb5_get_credentials failed for %s (%s)\n",
if (!ads_cleanup_expired_creds(context, ccache, credsp))
creds_ready = True;
+
+ i++;
}
- DEBUG(10,("ads_krb5_mk_req: Ticket (%s) in ccache (%s) is valid until: (%s - %u)\n",
- principal, krb5_cc_default_name(context),
+ DEBUG(10,("ads_krb5_mk_req: Ticket (%s) in ccache (%s:%s) is valid until: (%s - %u)\n",
+ principal, krb5_cc_get_type(context, ccache), krb5_cc_get_name(context, ccache),
http_timestring((unsigned)credsp->times.endtime),
(unsigned)credsp->times.endtime));
get a kerberos5 ticket for the given service
*/
int cli_krb5_get_ticket(const char *principal, time_t time_offset,
- DATA_BLOB *ticket, DATA_BLOB *session_key_krb5, uint32 extra_ap_opts)
+ DATA_BLOB *ticket, DATA_BLOB *session_key_krb5,
+ uint32 extra_ap_opts, const char *ccname)
{
krb5_error_code retval;
krb5_data packet;
ENCTYPE_DES_CBC_MD5,
ENCTYPE_DES_CBC_CRC,
ENCTYPE_NULL};
-
+
+ initialize_krb5_error_table();
retval = krb5_init_context(&context);
if (retval) {
DEBUG(1,("cli_krb5_get_ticket: krb5_init_context failed (%s)\n",
krb5_set_real_time(context, time(NULL) + time_offset, 0);
}
- if ((retval = krb5_cc_default(context, &ccdef))) {
+ if ((retval = krb5_cc_resolve(context, ccname ?
+ ccname : krb5_cc_default_name(context), &ccdef))) {
DEBUG(1,("cli_krb5_get_ticket: krb5_cc_default failed (%s)\n",
error_message(retval)));
goto failed;
else
err = krb5_auth_con_getlocalsubkey(context, auth_context, &skey);
if (err == 0 && skey != NULL) {
- DEBUG(10, ("Got KRB5 session key of length %d\n", KRB5_KEY_LENGTH(skey)));
+ DEBUG(10, ("Got KRB5 session key of length %d\n", (int)KRB5_KEY_LENGTH(skey)));
*session_key = data_blob(KRB5_KEY_DATA(skey), KRB5_KEY_LENGTH(skey));
dump_data_pw("KRB5 Session Key:\n", session_key->data, session_key->length);
static krb5_error_code
get_key_from_keytab(krb5_context context,
- krb5_keytab keytab,
krb5_const_principal server,
krb5_enctype enctype,
krb5_kvno kvno,
{
krb5_keytab_entry entry;
krb5_error_code ret;
- krb5_keytab real_keytab;
+ krb5_keytab keytab;
char *name = NULL;
- if (keytab == NULL) {
- krb5_kt_default(context, &real_keytab);
- } else {
- real_keytab = keytab;
+ /* We have to open a new keytab handle here, as MIT does
+ an implicit open/getnext/close on krb5_kt_get_entry. We
+ may be in the middle of a keytab enumeration when this is
+ called. JRA. */
+
+ ret = krb5_kt_default(context, &keytab);
+ if (ret) {
+ DEBUG(0,("get_key_from_keytab: failed to open keytab: %s\n", error_message(ret)));
+ return ret;
}
if ( DEBUGLEVEL >= 10 ) {
- krb5_unparse_name(context, server, &name);
- DEBUG(10,("get_key_from_keytab: will look for kvno %d, enctype %d and name: %s\n",
- kvno, enctype, name));
- krb5_free_unparsed_name(context, name);
+ if (smb_krb5_unparse_name(context, server, &name) == 0) {
+ DEBUG(10,("get_key_from_keytab: will look for kvno %d, enctype %d and name: %s\n",
+ kvno, enctype, name));
+ SAFE_FREE(name);
+ }
}
ret = krb5_kt_get_entry(context,
- real_keytab,
+ keytab,
server,
kvno,
enctype,
smb_krb5_kt_free_entry(context, &entry);
out:
- if (keytab == NULL) {
- krb5_kt_close(context, real_keytab);
- }
-
+ krb5_kt_close(context, keytab);
return ret;
}
krb5_keyblock **keyblock)
{
krb5_error_code ret;
- krb5_ap_req *ap_req = NULL;
krb5_kvno kvno;
krb5_enctype enctype;
krb5_keyblock *local_keyblock;
}
ret = get_key_from_keytab(context,
- keytab,
server,
enctype,
kvno,
}
out:
- if (ap_req) {
- smb_krb5_free_ap_req(context, ap_req);
- }
-
if (ret && local_keyblock != NULL) {
krb5_free_keyblock(context, local_keyblock);
} else {
krb5_principal *principal)
{
#ifdef HAVE_KRB5_PARSE_NAME_NOREALM
- return krb5_parse_name_norealm(context, name, principal);
+ return smb_krb5_parse_name_norealm_conv(context, name, principal);
#endif
/* we are cheating here because parse_name will in fact set the realm.
* ignores the realm anyway when calling
* smb_krb5_principal_compare_any_realm later - Guenther */
- return krb5_parse_name(context, name, principal);
+ return smb_krb5_parse_name(context, name, principal);
}
BOOL smb_krb5_principal_compare_any_realm(krb5_context context,
#endif
}
+ 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)
+{
+ krb5_error_code ret;
+ krb5_context context = NULL;
+ krb5_ccache ccache = NULL;
+ krb5_principal client = NULL;
+
+ initialize_krb5_error_table();
+ ret = krb5_init_context(&context);
+ if (ret) {
+ goto done;
+ }
+
+ if (!ccache_string) {
+ ccache_string = krb5_cc_default_name(context);
+ }
+
+ DEBUG(10,("smb_krb5_renew_ticket: using %s as ccache\n", ccache_string));
+
+ /* FIXME: we should not fall back to defaults */
+ ret = krb5_cc_resolve(context, CONST_DISCARD(char *, ccache_string), &ccache);
+ if (ret) {
+ 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 (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);
+ 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);
+ }
+#elif defined(HAVE_KRB5_GET_KDC_CRED) /* Heimdal */
+ {
+ krb5_kdc_flags flags;
+ krb5_creds creds_in;
+ krb5_realm *client_realm;
+ krb5_creds *creds;
+
+ 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;
+ }
+ }
+
+ if (service_string) {
+ ret = smb_krb5_parse_name(context, service_string, &creds_in.server);
+ if (ret) {
+ goto done;
+ }
+ } else {
+ /* build tgt service by default */
+ client_realm = krb5_princ_realm(context, client);
+ 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);
+ 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);
+ }
+#else
+#error No suitable krb5 ticket renew function available
+#endif
+
+
+done:
+ if (client) {
+ krb5_free_principal(context, client);
+ }
+ if (context) {
+ krb5_free_context(context);
+ }
+ if (ccache) {
+ krb5_cc_close(context, ccache);
+ }
+
+ return ret;
+
+}
+
+ krb5_error_code smb_krb5_free_addresses(krb5_context context, smb_krb5_addresses *addr)
+{
+ krb5_error_code ret = 0;
+ if (addr == NULL) {
+ return ret;
+ }
+#if defined(HAVE_MAGIC_IN_KRB5_ADDRESS) && defined(HAVE_ADDRTYPE_IN_KRB5_ADDRESS) /* MIT */
+ krb5_free_addresses(context, addr->addrs);
+#elif defined(HAVE_ADDR_TYPE_IN_KRB5_ADDRESS) /* Heimdal */
+ ret = krb5_free_addresses(context, addr->addrs);
+ SAFE_FREE(addr->addrs);
+#endif
+ SAFE_FREE(addr);
+ addr = NULL;
+ return ret;
+}
+
+ krb5_error_code smb_krb5_gen_netbios_krb5_address(smb_krb5_addresses **kerb_addr)
+{
+ krb5_error_code ret = 0;
+ nstring buf;
+#if defined(HAVE_MAGIC_IN_KRB5_ADDRESS) && defined(HAVE_ADDRTYPE_IN_KRB5_ADDRESS) /* MIT */
+ krb5_address **addrs = NULL;
+#elif defined(HAVE_ADDR_TYPE_IN_KRB5_ADDRESS) /* Heimdal */
+ krb5_addresses *addrs = NULL;
+#endif
+
+ *kerb_addr = (smb_krb5_addresses *)SMB_MALLOC(sizeof(smb_krb5_addresses));
+ if (*kerb_addr == NULL) {
+ return ENOMEM;
+ }
+
+ put_name(buf, global_myname(), ' ', 0x20);
+
+#if defined(HAVE_MAGIC_IN_KRB5_ADDRESS) && defined(HAVE_ADDRTYPE_IN_KRB5_ADDRESS) /* MIT */
+ {
+ int num_addr = 2;
+
+ addrs = (krb5_address **)SMB_MALLOC(sizeof(krb5_address *) * num_addr);
+ if (addrs == NULL) {
+ SAFE_FREE(kerb_addr);
+ return ENOMEM;
+ }
+
+ memset(addrs, 0, sizeof(krb5_address *) * num_addr);
+
+ addrs[0] = (krb5_address *)SMB_MALLOC(sizeof(krb5_address));
+ if (addrs[0] == NULL) {
+ SAFE_FREE(addrs);
+ SAFE_FREE(kerb_addr);
+ return ENOMEM;
+ }
+
+ addrs[0]->magic = KV5M_ADDRESS;
+ addrs[0]->addrtype = KRB5_ADDR_NETBIOS;
+ addrs[0]->length = MAX_NETBIOSNAME_LEN;
+ addrs[0]->contents = (unsigned char *)SMB_MALLOC(addrs[0]->length);
+ if (addrs[0]->contents == NULL) {
+ SAFE_FREE(addrs[0]);
+ SAFE_FREE(addrs);
+ SAFE_FREE(kerb_addr);
+ return ENOMEM;
+ }
+
+ memcpy(addrs[0]->contents, buf, addrs[0]->length);
+
+ addrs[1] = NULL;
+ }
+#elif defined(HAVE_ADDR_TYPE_IN_KRB5_ADDRESS) /* Heimdal */
+ {
+ addrs = (krb5_addresses *)SMB_MALLOC(sizeof(krb5_addresses));
+ if (addrs == NULL) {
+ SAFE_FREE(kerb_addr);
+ return ENOMEM;
+ }
+
+ memset(addrs, 0, sizeof(krb5_addresses));
+
+ addrs->len = 1;
+ addrs->val = (krb5_address *)SMB_MALLOC(sizeof(krb5_address));
+ if (addrs->val == NULL) {
+ SAFE_FREE(addrs);
+ SAFE_FREE(kerb_addr);
+ return ENOMEM;
+ }
+
+ addrs->val[0].addr_type = KRB5_ADDR_NETBIOS;
+ addrs->val[0].address.length = MAX_NETBIOSNAME_LEN;
+ addrs->val[0].address.data = (unsigned char *)SMB_MALLOC(addrs->val[0].address.length);
+ if (addrs->val[0].address.data == NULL) {
+ SAFE_FREE(addrs->val);
+ SAFE_FREE(addrs);
+ SAFE_FREE(kerb_addr);
+ return ENOMEM;
+ }
+
+ memcpy(addrs->val[0].address.data, buf, addrs->val[0].address.length);
+ }
+#else
+#error UNKNOWN_KRB5_ADDRESS_FORMAT
+#endif
+ (*kerb_addr)->addrs = addrs;
+
+ return ret;
+}
+
+ void smb_krb5_free_error(krb5_context context, krb5_error *krberror)
+{
+#ifdef HAVE_KRB5_FREE_ERROR_CONTENTS /* Heimdal */
+ krb5_free_error_contents(context, krberror);
+#else /* MIT */
+ krb5_free_error(context, krberror);
+#endif
+}
+
+ krb5_error_code handle_krberror_packet(krb5_context context,
+ krb5_data *packet)
+{
+ krb5_error_code ret;
+ BOOL got_error_code = False;
+
+ DEBUG(10,("handle_krberror_packet: got error packet\n"));
+
+#ifdef HAVE_E_DATA_POINTER_IN_KRB5_ERROR /* Heimdal */
+ {
+ krb5_error krberror;
+
+ if ((ret = krb5_rd_error(context, packet, &krberror))) {
+ DEBUG(10,("handle_krberror_packet: krb5_rd_error failed with: %s\n",
+ error_message(ret)));
+ return ret;
+ }
+
+ if (krberror.e_data == NULL || krberror.e_data->data == NULL) {
+ ret = (krb5_error_code) krberror.error_code;
+ got_error_code = True;
+ }
+
+ smb_krb5_free_error(context, &krberror);
+ }
+#else /* MIT */
+ {
+ krb5_error *krberror;
+
+ if ((ret = krb5_rd_error(context, packet, &krberror))) {
+ DEBUG(10,("handle_krberror_packet: krb5_rd_error failed with: %s\n",
+ error_message(ret)));
+ return ret;
+ }
+
+ if (krberror->e_data.data == NULL) {
+ ret = ERROR_TABLE_BASE_krb5 + (krb5_error_code) krberror->error;
+ got_error_code = True;
+ }
+ smb_krb5_free_error(context, krberror);
+ }
+#endif
+ if (got_error_code) {
+ DEBUG(5,("handle_krberror_packet: got KERBERR from kpasswd: %s (%d)\n",
+ error_message(ret), ret));
+ }
+ return ret;
+}
+
#else /* HAVE_KRB5 */
/* this saves a few linking headaches */
int cli_krb5_get_ticket(const char *principal, time_t time_offset,
- DATA_BLOB *ticket, DATA_BLOB *session_key_krb5, uint32 extra_ap_opts)
+ DATA_BLOB *ticket, DATA_BLOB *session_key_krb5, uint32 extra_ap_opts,
+ const char *ccname)
{
DEBUG(0,("NO KERBEROS SUPPORT\n"));
return 1;