along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#define KRB5_PRIVATE 1 /* this file uses PRIVATE interfaces! */
-#define KRB5_DEPRECATED 1 /* this file uses DEPRECATED interfaces! */
-
#include "includes.h"
+#include "smb_krb5.h"
+#include "../librpc/gen_ndr/krb5pac.h"
+#include "../lib/util/asn1.h"
+#include "libsmb/nmblib.h"
+
+#ifndef KRB5_AUTHDATA_WIN2K_PAC
+#define KRB5_AUTHDATA_WIN2K_PAC 128
+#endif
+
+#ifndef KRB5_AUTHDATA_IF_RELEVANT
+#define KRB5_AUTHDATA_IF_RELEVANT 1
+#endif
#ifdef HAVE_KRB5
#define GSSAPI_CHECKSUM 0x8003 /* Checksum type value for Kerberos */
#define GSSAPI_BNDLENGTH 16 /* Bind Length (rfc-1964 pg.3) */
-#define GSSAPI_CHECKSUM_SIZE (12+GSSAPI_BNDLENGTH)
-
-#if defined(TKT_FLG_OK_AS_DELEGATE ) && defined(HAVE_KRB5_FWD_TGT_CREDS) && defined(HAVE_KRB5_AUTH_CON_SET_REQ_CKSUMTYPE) && defined(KRB5_AUTH_CONTEXT_USE_SUBKEY)
-static krb5_error_code ads_krb5_get_fwd_ticket( krb5_context context,
- krb5_auth_context *auth_context,
- krb5_creds *credsp,
- krb5_ccache ccache,
- krb5_data *authenticator);
+#define GSSAPI_CHECKSUM_SIZE (4+GSSAPI_BNDLENGTH+4) /* Length of bind length,
+ bind field, flags field. */
+
+/* MIT krb5 1.7beta3 (in Ubuntu Karmic) is missing the prototype,
+ but still has the symbol */
+#if !HAVE_DECL_KRB5_AUTH_CON_SET_REQ_CKSUMTYPE
+krb5_error_code krb5_auth_con_set_req_cksumtype(
+ krb5_context context,
+ krb5_auth_context auth_context,
+ krb5_cksumtype cksumtype);
#endif
/**************************************************************
}
asn1_start_tag(data, ASN1_CONTEXT(2));
- asn1_read_OctetString(data, talloc_autofree_context(), &edata_contents);
+ asn1_read_OctetString(data, talloc_tos(), &edata_contents);
asn1_end_tag(data);
asn1_end_tag(data);
asn1_end_tag(data);
asn1_end_tag(data);
asn1_start_tag(data, ASN1_CONTEXT(1));
- asn1_read_OctetString(data, talloc_autofree_context(), &pac_contents);
+ asn1_read_OctetString(data, talloc_tos(), &pac_contents);
asn1_end_tag(data);
asn1_end_tag(data);
asn1_end_tag(data);
return True;
}
+/* Allocate and setup the auth context into the state we need. */
+
+static krb5_error_code setup_auth_context(krb5_context context,
+ krb5_auth_context *auth_context)
+{
+ krb5_error_code retval;
+
+ retval = krb5_auth_con_init(context, auth_context );
+ if (retval) {
+ DEBUG(1,("krb5_auth_con_init failed (%s)\n",
+ error_message(retval)));
+ return retval;
+ }
+
+ /* Ensure this is an addressless ticket. */
+ retval = krb5_auth_con_setaddrs(context, *auth_context, NULL, NULL);
+ if (retval) {
+ DEBUG(1,("krb5_auth_con_setaddrs failed (%s)\n",
+ error_message(retval)));
+ }
+
+ return retval;
+}
+
+static krb5_error_code create_gss_checksum(krb5_data *in_data, /* [inout] */
+ uint32_t gss_flags)
+{
+ unsigned int orig_length = in_data->length;
+ unsigned int base_cksum_size = GSSAPI_CHECKSUM_SIZE;
+ char *gss_cksum = NULL;
+
+ if (orig_length) {
+ /* Extra length field for delgated ticket. */
+ base_cksum_size += 4;
+ }
+
+ if ((unsigned int)base_cksum_size + orig_length <
+ (unsigned int)base_cksum_size) {
+ return EINVAL;
+ }
+
+ gss_cksum = (char *)SMB_MALLOC(base_cksum_size + orig_length);
+ if (gss_cksum == NULL) {
+ return ENOMEM;
+ }
+
+ memset(gss_cksum, '\0', base_cksum_size + orig_length);
+ SIVAL(gss_cksum, 0, GSSAPI_BNDLENGTH);
+
+ /*
+ * GSS_C_NO_CHANNEL_BINDINGS means 16 zero bytes.
+ * This matches the behavior of heimdal and mit.
+ *
+ * And it is needed to work against some closed source
+ * SMB servers.
+ *
+ * See bug #7883
+ */
+ memset(&gss_cksum[4], 0x00, GSSAPI_BNDLENGTH);
+
+ SIVAL(gss_cksum, 20, gss_flags);
+
+ if (orig_length) {
+ SSVAL(gss_cksum, 24, 1); /* The Delegation Option identifier */
+ SSVAL(gss_cksum, 26, orig_length);
+ /* Copy the kerberos KRB_CRED data */
+ memcpy(gss_cksum + 28, in_data->data, orig_length);
+ free(in_data->data);
+ in_data->data = NULL;
+ in_data->length = 0;
+ }
+ in_data->data = gss_cksum;
+ in_data->length = base_cksum_size + orig_length;
+ return 0;
+}
+
/*
we can't use krb5_mk_req because w2k wants the service to be in a particular format
*/
krb5_data in_data;
bool creds_ready = False;
int i = 0, maxtries = 3;
-
+ uint32_t gss_flags = 0;
+
ZERO_STRUCT(in_data);
retval = smb_krb5_parse_name(context, principal, &server);
*expire_time = (time_t)credsp->times.endtime;
}
-#if defined(TKT_FLG_OK_AS_DELEGATE ) && defined(HAVE_KRB5_FWD_TGT_CREDS) && defined(HAVE_KRB5_AUTH_CON_SET_REQ_CKSUMTYPE) && defined(KRB5_AUTH_CONTEXT_USE_SUBKEY)
+ /* Allocate the auth_context. */
+ retval = setup_auth_context(context, auth_context);
+ if (retval) {
+ DEBUG(1,("setup_auth_context failed (%s)\n",
+ error_message(retval)));
+ goto cleanup_creds;
+ }
+
+#if defined(TKT_FLG_OK_AS_DELEGATE ) && defined(HAVE_KRB5_FWD_TGT_CREDS) && defined(HAVE_KRB5_AUTH_CON_SETUSERUSERKEY) && defined(KRB5_AUTH_CONTEXT_USE_SUBKEY) && defined(HAVE_KRB5_AUTH_CON_SET_REQ_CKSUMTYPE)
if( credsp->ticket_flags & TKT_FLG_OK_AS_DELEGATE ) {
/* Fetch a forwarded TGT from the KDC so that we can hand off a 2nd ticket
as part of the kerberos exchange. */
DEBUG( 3, ("ads_krb5_mk_req: server marked as OK to delegate to, building forwardable TGT\n") );
- if( *auth_context == NULL ) {
- /* Allocate if it has not yet been allocated. */
- retval = krb5_auth_con_init( context, auth_context );
- if (retval) {
- DEBUG(1,("ads_krb5_mk_req: krb5_auth_con_init failed (%s)\n",
- error_message(retval)));
- goto cleanup_creds;
- }
- }
-
- retval = krb5_auth_con_setuseruserkey( context, *auth_context, &credsp->keyblock );
+ retval = krb5_auth_con_setuseruserkey(context,
+ *auth_context,
+ &credsp->keyblock );
if (retval) {
- DEBUG(1,("ads_krb5_mk_req: krb5_auth_con_setuseruserkey failed (%s)\n",
+ DEBUG(1,("krb5_auth_con_setuseruserkey failed (%s)\n",
error_message(retval)));
goto cleanup_creds;
}
/* Must use a subkey for forwarded tickets. */
- retval = krb5_auth_con_setflags( context, *auth_context, KRB5_AUTH_CONTEXT_USE_SUBKEY);
+ retval = krb5_auth_con_setflags(context,
+ *auth_context,
+ KRB5_AUTH_CONTEXT_USE_SUBKEY);
if (retval) {
- DEBUG(1,("ads_krb5_mk_req: krb5_auth_con_setflags failed (%s)\n",
+ DEBUG(1,("krb5_auth_con_setflags failed (%s)\n",
error_message(retval)));
goto cleanup_creds;
}
- retval = ads_krb5_get_fwd_ticket( context,
- auth_context,
- credsp,
- ccache,
- &in_data );
+ retval = krb5_fwd_tgt_creds(context,/* Krb5 context [in] */
+ *auth_context, /* Authentication context [in] */
+ CONST_DISCARD(char *, KRB5_TGS_NAME), /* Ticket service name ("krbtgt") [in] */
+ credsp->client, /* Client principal for the tgt [in] */
+ credsp->server, /* Server principal for the tgt [in] */
+ ccache, /* Credential cache to use for storage [in] */
+ 1, /* Turn on for "Forwardable ticket" [in] */
+ &in_data ); /* Resulting response [out] */
+
if (retval) {
- DEBUG( 3, ("ads_krb5_get_fwd_ticket failed (%s)\n",
+ DEBUG( 3, ("krb5_fwd_tgt_creds failed (%s)\n",
error_message( retval ) ) );
/*
}
krb5_auth_con_free(context, *auth_context);
*auth_context = NULL;
+ retval = setup_auth_context(context, auth_context);
+ if (retval) {
+ DEBUG(1,("setup_auth_context failed (%s)\n",
+ error_message(retval)));
+ goto cleanup_creds;
+ }
+ } else {
+ /* We got a delegated ticket. */
+ gss_flags |= GSS_C_DELEG_FLAG;
}
}
+
+ /* Frees and reallocates in_data into a GSS checksum blob. */
+ retval = create_gss_checksum(&in_data, gss_flags);
+ if (retval) {
+ goto cleanup_data;
+ }
+
+ /* We always want GSS-checksum types. */
+ retval = krb5_auth_con_set_req_cksumtype(context, *auth_context, GSSAPI_CHECKSUM );
+ if (retval) {
+ DEBUG(1,("krb5_auth_con_set_req_cksumtype failed (%s)\n",
+ error_message(retval)));
+ goto cleanup_data;
+ }
#endif
retval = krb5_mk_req_extended(context, auth_context, ap_req_options,
error_message(retval)));
}
+cleanup_data:
if (in_data.data) {
free( in_data.data );
in_data.length = 0;
}
/*
- get a kerberos5 ticket for the given service
+ 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, const char *ccname,
+int cli_krb5_get_ticket(TALLOC_CTX *mem_ctx,
+ const char *principal, time_t time_offset,
+ DATA_BLOB *ticket, DATA_BLOB *session_key_krb5,
+ uint32_t extra_ap_opts, const char *ccname,
time_t *tgs_expire,
const char *impersonate_princ_s)
krb5_ccache ccdef = NULL;
krb5_auth_context auth_context = NULL;
krb5_enctype enc_types[] = {
-#ifdef ENCTYPE_ARCFOUR_HMAC
ENCTYPE_ARCFOUR_HMAC,
-#endif
- ENCTYPE_DES_CBC_MD5,
- ENCTYPE_DES_CBC_CRC,
+ 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",
+ DEBUG(1, ("krb5_init_context failed (%s)\n",
error_message(retval)));
goto failed;
}
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",
+ DEBUG(1, ("krb5_cc_default failed (%s)\n",
error_message(retval)));
goto failed;
}
if ((retval = krb5_set_default_tgs_ktypes(context, enc_types))) {
- DEBUG(1,("cli_krb5_get_ticket: krb5_set_default_tgs_ktypes failed (%s)\n",
+ DEBUG(1, ("krb5_set_default_tgs_ktypes failed (%s)\n",
error_message(retval)));
goto failed;
}
- if ((retval = ads_krb5_mk_req(context,
- &auth_context,
- AP_OPTS_USE_SUBKEY | (krb5_flags)extra_ap_opts,
- principal,
- ccdef, &packet,
- tgs_expire,
- impersonate_princ_s))) {
+ retval = ads_krb5_mk_req(context, &auth_context,
+ AP_OPTS_USE_SUBKEY | (krb5_flags)extra_ap_opts,
+ principal, ccdef, &packet,
+ tgs_expire, impersonate_princ_s);
+ if (retval) {
goto failed;
}
- get_krb5_smb_session_key(context, auth_context, session_key_krb5, False);
+ get_krb5_smb_session_key(mem_ctx, context, auth_context,
+ session_key_krb5, false);
- *ticket = data_blob(packet.data, packet.length);
+ *ticket = data_blob_talloc(mem_ctx, packet.data, packet.length);
- kerberos_free_data_contents(context, &packet);
+ kerberos_free_data_contents(context, &packet);
failed:
- if ( context ) {
+ if (context) {
if (ccdef)
krb5_cc_close(context, ccdef);
if (auth_context)
krb5_auth_con_free(context, auth_context);
krb5_free_context(context);
}
-
+
return retval;
}
- bool get_krb5_smb_session_key(krb5_context context, krb5_auth_context auth_context, DATA_BLOB *session_key, bool remote)
- {
+bool get_krb5_smb_session_key(TALLOC_CTX *mem_ctx,
+ krb5_context context,
+ krb5_auth_context auth_context,
+ DATA_BLOB *session_key, bool remote)
+{
krb5_keyblock *skey = NULL;
krb5_error_code err = 0;
bool ret = false;
if (remote) {
- err = krb5_auth_con_getremotesubkey(context, auth_context, &skey);
+ err = krb5_auth_con_getremotesubkey(context,
+ auth_context, &skey);
} else {
- err = krb5_auth_con_getlocalsubkey(context, auth_context, &skey);
+ err = krb5_auth_con_getlocalsubkey(context,
+ auth_context, &skey);
}
if (err || skey == NULL) {
goto done;
}
- 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);
+ DEBUG(10, ("Got KRB5 session key of length %d\n",
+ (int)KRB5_KEY_LENGTH(skey)));
+
+ *session_key = data_blob_talloc(mem_ctx,
+ KRB5_KEY_DATA(skey),
+ KRB5_KEY_LENGTH(skey));
+ dump_data_pw("KRB5 Session Key:\n",
+ session_key->data,
+ session_key->length);
ret = true;
- done:
+done:
if (skey) {
krb5_free_keyblock(context, skey);
}
return ret;
- }
+}
#if defined(HAVE_KRB5_PRINCIPAL_GET_COMP_STRING) && !defined(HAVE_KRB5_PRINC_COMPONENT)
}
if (krberror->e_data.data == NULL) {
+#if defined(ERROR_TABLE_BASE_krb5)
ret = ERROR_TABLE_BASE_krb5 + (krb5_error_code) krberror->error;
+#else
+ ret = (krb5_error_code)krberror->error;
+#endif
got_error_code = True;
}
smb_krb5_free_error(context, krberror);
return ret;
}
-#if defined(TKT_FLG_OK_AS_DELEGATE ) && defined(HAVE_KRB5_FWD_TGT_CREDS) && defined(HAVE_KRB5_AUTH_CON_SET_REQ_CKSUMTYPE) && defined(KRB5_AUTH_CONTEXT_USE_SUBKEY)
-/**************************************************************
-Routine: ads_krb5_get_fwd_ticket
- Description:
- When a service ticket is flagged as trusted
- for delegation we should provide a forwardable
- ticket so that the remote host can act on our
- behalf. This is done by taking the 2nd forwardable
- TGT and storing it in the GSS-API authenticator
- "checksum". This routine will populate
- the krb5_data authenticator with this TGT.
- Parameters:
- krb5_context context: The kerberos context for this authentication.
- krb5_auth_context: The authentication context.
- krb5_creds *credsp: The ticket credentials (AS-REP).
- krb5_ccache ccache: The credentials cache.
- krb5_data &authenticator: The checksum field that will store the TGT, and
- authenticator.data must be freed by the caller.
-
- Returns:
- krb5_error_code: 0 if no errors, otherwise set.
-**************************************************************/
-
-static krb5_error_code ads_krb5_get_fwd_ticket( krb5_context context,
- krb5_auth_context *auth_context,
- krb5_creds *credsp,
- krb5_ccache ccache,
- krb5_data *authenticator)
-{
- krb5_data fwdData;
- krb5_error_code retval = 0;
- char *pChksum = NULL;
- char *p = NULL;
-
-/* MIT krb5 1.7beta3 (in Ubuntu Karmic) is missing the prototype,
- but still has the symbol */
-#if !HAVE_DECL_KRB5_AUTH_CON_SET_REQ_CKSUMTYPE
-krb5_error_code krb5_auth_con_set_req_cksumtype(
- krb5_context context,
- krb5_auth_context auth_context,
- krb5_cksumtype cksumtype);
-#endif
-
- ZERO_STRUCT(fwdData);
- ZERO_STRUCTP(authenticator);
-
- retval = krb5_fwd_tgt_creds(context,/* Krb5 context [in] */
- *auth_context, /* Authentication context [in] */
- CONST_DISCARD(char *, KRB5_TGS_NAME), /* Ticket service name ("krbtgt") [in] */
- credsp->client, /* Client principal for the tgt [in] */
- credsp->server, /* Server principal for the tgt [in] */
- ccache, /* Credential cache to use for storage [in] */
- 1, /* Turn on for "Forwardable ticket" [in] */
- &fwdData ); /* Resulting response [out] */
-
-
- if (retval) {
- DEBUG(1,("ads_krb5_get_fwd_ticket: krb5_fwd_tgt_creds failed (%s)\n",
- error_message(retval)));
- goto out;
- }
-
- if ((unsigned int)GSSAPI_CHECKSUM_SIZE + (unsigned int)fwdData.length <
- (unsigned int)GSSAPI_CHECKSUM_SIZE) {
- retval = EINVAL;
- goto out;
- }
-
- /* We're going to allocate a gssChecksum structure with a little
- extra data the length of the kerberos credentials length
- (APPLICATION 22) so that we can pack it on the end of the structure.
- */
-
- pChksum = (char *)SMB_MALLOC(GSSAPI_CHECKSUM_SIZE + fwdData.length );
- if (!pChksum) {
- retval = ENOMEM;
- goto out;
- }
-
- p = pChksum;
-
- SIVAL(p, 0, GSSAPI_BNDLENGTH);
- p += 4;
-
- /* Zero out the bindings fields */
- memset(p, '\0', GSSAPI_BNDLENGTH );
- p += GSSAPI_BNDLENGTH;
-
- SIVAL(p, 0, GSS_C_DELEG_FLAG );
- p += 4;
- SSVAL(p, 0, 1 );
- p += 2;
- SSVAL(p, 0, fwdData.length );
- p += 2;
-
- /* Migrate the kerberos KRB_CRED data to the checksum delegation */
- memcpy(p, fwdData.data, fwdData.length );
- p += fwdData.length;
-
- /* We need to do this in order to allow our GSS-API */
- retval = krb5_auth_con_set_req_cksumtype( context, *auth_context, GSSAPI_CHECKSUM );
- if (retval) {
- goto out;
- }
-
- /* We now have a service ticket, now turn it into an AP-REQ. */
- authenticator->length = fwdData.length + GSSAPI_CHECKSUM_SIZE;
-
- /* Caller should call free() when they're done with this. */
- authenticator->data = (char *)pChksum;
-
- out:
-
- /* Remove that input data, we never needed it anyway. */
- if (fwdData.length > 0) {
- krb5_free_data_contents( context, &fwdData );
- }
-
- return retval;
-}
-#endif
-
#if defined(HAVE_KRB5_GET_CREDS_OPT_SET_IMPERSONATE) && \
defined(HAVE_KRB5_GET_CREDS_OPT_ALLOC) && \
defined(HAVE_KRB5_GET_CREDS)
#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,
+ int cli_krb5_get_ticket(TALLOC_CTX *mem_ctx,
+ const char *principal, time_t time_offset,
+ DATA_BLOB *ticket, DATA_BLOB *session_key_krb5,
+ uint32_t extra_ap_opts,
const char *ccname, time_t *tgs_expire,
const char *impersonate_princ_s)
{
return 1;
}
-#endif
+bool unwrap_pac(TALLOC_CTX *mem_ctx, DATA_BLOB *auth_data, DATA_BLOB *unwrapped_pac_data)
+{
+ DEBUG(0,("NO KERBEROS SUPPORT\n"));
+ return false;
+}
+
+#endif /* HAVE_KRB5 */