diff --git a/source3/libsmb/clikrb5.c b/source3/libsmb/clikrb5.c index abf6dbb..8d4c65e 100644 --- a/source3/libsmb/clikrb5.c +++ b/source3/libsmb/clikrb5.c @@ -28,14 +28,16 @@ #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 /************************************************************** @@ -645,6 +647,92 @@ static bool ads_cleanup_expired_creds(krb5_context context, 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); + + /* Precalculated MD5sum of NULL channel bindings (20 bytes) */ + /* Channel bindings are: (all ints encoded as little endian) + + [4 bytes] initiator_addrtype (255 for null bindings) + [4 bytes] initiator_address length + [n bytes] .. initiator_address data - not present + in null bindings. + [4 bytes] acceptor_addrtype (255 for null bindings) + [4 bytes] acceptor_address length + [n bytes] .. acceptor_address data - not present + in null bindings. + [4 bytes] application_data length + [n bytes] .. application_ data - not present + in null bindings. + MD5 of this is ""\x14\x8f\x0c\xf7\xb1u\xdey*J\x9a%\xdfV\xc5\x18" + */ + + memcpy(&gss_cksum[4], + "\x14\x8f\x0c\xf7\xb1u\xdey*J\x9a%\xdfV\xc5\x18", + 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 */ @@ -663,7 +751,8 @@ static krb5_error_code ads_krb5_mk_req(krb5_context context, 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); @@ -721,45 +810,51 @@ static krb5_error_code ads_krb5_mk_req(krb5_context context, *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) 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 ) ) ); /* @@ -774,10 +869,35 @@ static krb5_error_code ads_krb5_mk_req(krb5_context context, } 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; } } #endif + /* Frees and reallocates in_data into a GSS checksum blob. */ + retval = create_gss_checksum(&in_data, gss_flags); + if (retval) { + goto cleanup_data; + } + +#if defined(HAVE_KRB5_AUTH_CON_SET_REQ_CKSUMTYPE) + /* 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, &in_data, credsp, outbuf); if (retval) { @@ -785,6 +905,7 @@ static krb5_error_code ads_krb5_mk_req(krb5_context context, error_message(retval))); } +cleanup_data: if (in_data.data) { free( in_data.data ); in_data.length = 0; @@ -1823,128 +1944,6 @@ krb5_error_code smb_krb5_keytab_name(TALLOC_CTX *mem_ctx, 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 - /* * smb_krb5_principal_get_realm *