wmi-1.3.16 from opsview.com
This commit is contained in:
@@ -0,0 +1,210 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
simple kerberos5 routines for active directory
|
||||
Copyright (C) Andrew Tridgell 2001
|
||||
Copyright (C) Luke Howard 2002-2003
|
||||
Copyright (C) Andrew Bartlett <abartlet@samba.org> 2005
|
||||
|
||||
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
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
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.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "system/network.h"
|
||||
#include "system/kerberos.h"
|
||||
#include "system/time.h"
|
||||
#include "auth/kerberos/kerberos.h"
|
||||
|
||||
#ifdef HAVE_KRB5
|
||||
|
||||
#if defined(HAVE_KRB5_SET_DEFAULT_IN_TKT_ETYPES) && !defined(HAVE_KRB5_SET_DEFAULT_TGS_KTYPES)
|
||||
krb5_error_code krb5_set_default_tgs_ktypes(krb5_context ctx, const krb5_enctype *enc)
|
||||
{
|
||||
return krb5_set_default_in_tkt_etypes(ctx, enc);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(HAVE_ADDR_TYPE_IN_KRB5_ADDRESS)
|
||||
/* HEIMDAL */
|
||||
void setup_kaddr( krb5_address *pkaddr, struct sockaddr *paddr)
|
||||
{
|
||||
pkaddr->addr_type = KRB5_ADDRESS_INET;
|
||||
pkaddr->address.length = sizeof(((struct sockaddr_in *)paddr)->sin_addr);
|
||||
pkaddr->address.data = (char *)&(((struct sockaddr_in *)paddr)->sin_addr);
|
||||
}
|
||||
#elif defined(HAVE_ADDRTYPE_IN_KRB5_ADDRESS)
|
||||
/* MIT */
|
||||
void setup_kaddr( krb5_address *pkaddr, struct sockaddr *paddr)
|
||||
{
|
||||
pkaddr->addrtype = ADDRTYPE_INET;
|
||||
pkaddr->length = sizeof(((struct sockaddr_in *)paddr)->sin_addr);
|
||||
pkaddr->contents = (krb5_octet *)&(((struct sockaddr_in *)paddr)->sin_addr);
|
||||
}
|
||||
#else
|
||||
#error UNKNOWN_ADDRTYPE
|
||||
#endif
|
||||
|
||||
#if defined(HAVE_KRB5_PRINCIPAL2SALT) && defined(HAVE_KRB5_USE_ENCTYPE) && defined(HAVE_KRB5_STRING_TO_KEY) && defined(HAVE_KRB5_ENCRYPT_BLOCK)
|
||||
int create_kerberos_key_from_string(krb5_context context,
|
||||
krb5_principal host_princ,
|
||||
krb5_data *password,
|
||||
krb5_keyblock *key,
|
||||
krb5_enctype enctype)
|
||||
{
|
||||
int ret;
|
||||
krb5_data salt;
|
||||
krb5_encrypt_block eblock;
|
||||
|
||||
ret = krb5_principal2salt(context, host_princ, &salt);
|
||||
if (ret) {
|
||||
DEBUG(1,("krb5_principal2salt failed (%s)\n", error_message(ret)));
|
||||
return ret;
|
||||
}
|
||||
krb5_use_enctype(context, &eblock, enctype);
|
||||
ret = krb5_string_to_key(context, &eblock, key, password, &salt);
|
||||
SAFE_FREE(salt.data);
|
||||
return ret;
|
||||
}
|
||||
#elif defined(HAVE_KRB5_GET_PW_SALT) && defined(HAVE_KRB5_STRING_TO_KEY_SALT)
|
||||
int create_kerberos_key_from_string(krb5_context context,
|
||||
krb5_principal host_princ,
|
||||
krb5_data *password,
|
||||
krb5_keyblock *key,
|
||||
krb5_enctype enctype)
|
||||
{
|
||||
int ret;
|
||||
krb5_salt salt;
|
||||
|
||||
ret = krb5_get_pw_salt(context, host_princ, &salt);
|
||||
if (ret) {
|
||||
DEBUG(1,("krb5_get_pw_salt failed (%s)\n", error_message(ret)));
|
||||
return ret;
|
||||
}
|
||||
ret = krb5_string_to_key_salt(context, enctype, password->data,
|
||||
salt, key);
|
||||
krb5_free_salt(context, salt);
|
||||
return ret;
|
||||
}
|
||||
#else
|
||||
#error UNKNOWN_CREATE_KEY_FUNCTIONS
|
||||
#endif
|
||||
|
||||
#if defined(HAVE_KRB5_GET_PERMITTED_ENCTYPES)
|
||||
krb5_error_code get_kerberos_allowed_etypes(krb5_context context,
|
||||
krb5_enctype **enctypes)
|
||||
{
|
||||
return krb5_get_permitted_enctypes(context, enctypes);
|
||||
}
|
||||
#elif defined(HAVE_KRB5_GET_DEFAULT_IN_TKT_ETYPES)
|
||||
krb5_error_code get_kerberos_allowed_etypes(krb5_context context,
|
||||
krb5_enctype **enctypes)
|
||||
{
|
||||
return krb5_get_default_in_tkt_etypes(context, enctypes);
|
||||
}
|
||||
#else
|
||||
#error UNKNOWN_GET_ENCTYPES_FUNCTIONS
|
||||
#endif
|
||||
|
||||
void free_kerberos_etypes(krb5_context context,
|
||||
krb5_enctype *enctypes)
|
||||
{
|
||||
#if defined(HAVE_KRB5_FREE_KTYPES)
|
||||
krb5_free_ktypes(context, enctypes);
|
||||
return;
|
||||
#else
|
||||
SAFE_FREE(enctypes);
|
||||
return;
|
||||
#endif
|
||||
}
|
||||
|
||||
#if defined(HAVE_KRB5_AUTH_CON_SETKEY) && !defined(HAVE_KRB5_AUTH_CON_SETUSERUSERKEY)
|
||||
krb5_error_code krb5_auth_con_setuseruserkey(krb5_context context,
|
||||
krb5_auth_context auth_context,
|
||||
krb5_keyblock *keyblock)
|
||||
{
|
||||
return krb5_auth_con_setkey(context, auth_context, keyblock);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if !defined(HAVE_KRB5_FREE_UNPARSED_NAME)
|
||||
void krb5_free_unparsed_name(krb5_context context, char *val)
|
||||
{
|
||||
SAFE_FREE(val);
|
||||
}
|
||||
#endif
|
||||
|
||||
void kerberos_free_data_contents(krb5_context context, krb5_data *pdata)
|
||||
{
|
||||
#if defined(HAVE_KRB5_FREE_DATA_CONTENTS)
|
||||
if (pdata->data) {
|
||||
krb5_free_data_contents(context, pdata);
|
||||
}
|
||||
#else
|
||||
SAFE_FREE(pdata->data);
|
||||
#endif
|
||||
}
|
||||
|
||||
void kerberos_set_creds_enctype(krb5_creds *pcreds, int enctype)
|
||||
{
|
||||
#if defined(HAVE_KRB5_KEYBLOCK_IN_CREDS)
|
||||
KRB5_KEY_TYPE((&pcreds->keyblock)) = enctype;
|
||||
#elif defined(HAVE_KRB5_SESSION_IN_CREDS)
|
||||
KRB5_KEY_TYPE((&pcreds->session)) = enctype;
|
||||
#else
|
||||
#error UNKNOWN_KEYBLOCK_MEMBER_IN_KRB5_CREDS_STRUCT
|
||||
#endif
|
||||
}
|
||||
|
||||
BOOL kerberos_compatible_enctypes(krb5_context context,
|
||||
krb5_enctype enctype1,
|
||||
krb5_enctype enctype2)
|
||||
{
|
||||
#if defined(HAVE_KRB5_C_ENCTYPE_COMPARE)
|
||||
krb5_boolean similar = 0;
|
||||
|
||||
krb5_c_enctype_compare(context, enctype1, enctype2, &similar);
|
||||
return similar ? True : False;
|
||||
#elif defined(HAVE_KRB5_ENCTYPES_COMPATIBLE_KEYS)
|
||||
return krb5_enctypes_compatible_keys(context, enctype1, enctype2) ? True : False;
|
||||
#endif
|
||||
}
|
||||
|
||||
krb5_error_code smb_krb5_kt_free_entry(krb5_context context, krb5_keytab_entry *kt_entry)
|
||||
{
|
||||
#if defined(HAVE_KRB5_KT_FREE_ENTRY)
|
||||
return krb5_kt_free_entry(context, kt_entry);
|
||||
#elif defined(HAVE_KRB5_FREE_KEYTAB_ENTRY_CONTENTS)
|
||||
return krb5_free_keytab_entry_contents(context, kt_entry);
|
||||
#else
|
||||
#error UNKNOWN_KT_FREE_FUNCTION
|
||||
#endif
|
||||
}
|
||||
|
||||
char *smb_get_krb5_error_message(krb5_context context, krb5_error_code code, TALLOC_CTX *mem_ctx)
|
||||
{
|
||||
char *ret;
|
||||
|
||||
#if defined(HAVE_KRB5_GET_ERROR_STRING) && defined(HAVE_KRB5_FREE_ERROR_STRING)
|
||||
char *context_error = krb5_get_error_string(context);
|
||||
if (context_error) {
|
||||
ret = talloc_asprintf(mem_ctx, "%s: %s", error_message(code), context_error);
|
||||
krb5_free_error_string(context, context_error);
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
ret = talloc_strdup(mem_ctx, error_message(code));
|
||||
return ret;
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,540 @@
|
||||
# NOTE! this whole m4 file is disabled in configure.in for now
|
||||
|
||||
#################################################
|
||||
# KRB5 support
|
||||
KRB5_CFLAGS=""
|
||||
KRB5_CPPFLAGS=""
|
||||
KRB5_LDFLAGS=""
|
||||
KRB5_LIBS=""
|
||||
with_krb5_support=auto
|
||||
krb5_withval=auto
|
||||
AC_MSG_CHECKING([for KRB5 support])
|
||||
|
||||
# Do no harm to the values of CFLAGS and LIBS while testing for
|
||||
# Kerberos support.
|
||||
AC_ARG_WITH(krb5,
|
||||
[ --with-krb5=base-dir Locate Kerberos 5 support (default=auto)],
|
||||
[ case "$withval" in
|
||||
no)
|
||||
with_krb5_support=no
|
||||
AC_MSG_RESULT(no)
|
||||
krb5_withval=no
|
||||
;;
|
||||
yes)
|
||||
with_krb5_support=yes
|
||||
AC_MSG_RESULT(yes)
|
||||
krb5_withval=yes
|
||||
;;
|
||||
auto)
|
||||
with_krb5_support=auto
|
||||
AC_MSG_RESULT(auto)
|
||||
krb5_withval=auto
|
||||
;;
|
||||
*)
|
||||
with_krb5_support=yes
|
||||
AC_MSG_RESULT(yes)
|
||||
krb5_withval=$withval
|
||||
KRB5CONFIG="$krb5_withval/bin/krb5-config"
|
||||
;;
|
||||
esac ],
|
||||
AC_MSG_RESULT($with_krb5_support)
|
||||
)
|
||||
|
||||
if test x$with_krb5_support != x"no"; then
|
||||
FOUND_KRB5=no
|
||||
FOUND_KRB5_VIA_CONFIG=no
|
||||
|
||||
#################################################
|
||||
# check for krb5-config from recent MIT and Heimdal kerberos 5
|
||||
AC_MSG_CHECKING(for working specified location for krb5-config)
|
||||
if test x$KRB5CONFIG != "x"; then
|
||||
if test -x "$KRB5CONFIG"; then
|
||||
ac_save_CFLAGS=$CFLAGS
|
||||
CFLAGS="";export CFLAGS
|
||||
ac_save_LDFLAGS=$LDFLAGS
|
||||
LDFLAGS="";export LDFLAGS
|
||||
KRB5_LIBS="`$KRB5CONFIG --libs gssapi`"
|
||||
KRB5_CFLAGS="`$KRB5CONFIG --cflags | sed s/@INCLUDE_des@//`"
|
||||
KRB5_CPPFLAGS="`$KRB5CONFIG --cflags | sed s/@INCLUDE_des@//`"
|
||||
CFLAGS=$ac_save_CFLAGS;export CFLAGS
|
||||
LDFLAGS=$ac_save_LDFLAGS;export LDFLAGS
|
||||
FOUND_KRB5=yes
|
||||
FOUND_KRB5_VIA_CONFIG=yes
|
||||
AC_MSG_RESULT(yes. Found $KRB5CONFIG)
|
||||
else
|
||||
AC_MSG_RESULT(no. Fallback to specified directory)
|
||||
fi
|
||||
else
|
||||
AC_MSG_RESULT(no. Fallback to finding krb5-config in path)
|
||||
#################################################
|
||||
# check for krb5-config from recent MIT and Heimdal kerberos 5
|
||||
AC_PATH_PROG(KRB5CONFIG, krb5-config)
|
||||
AC_MSG_CHECKING(for working krb5-config in path)
|
||||
if test -x "$KRB5CONFIG"; then
|
||||
ac_save_CFLAGS=$CFLAGS
|
||||
CFLAGS="";export CFLAGS
|
||||
ac_save_LDFLAGS=$LDFLAGS
|
||||
LDFLAGS="";export LDFLAGS
|
||||
KRB5_LIBS="`$KRB5CONFIG --libs gssapi`"
|
||||
KRB5_CFLAGS="`$KRB5CONFIG --cflags | sed s/@INCLUDE_des@//`"
|
||||
KRB5_CPPFLAGS="`$KRB5CONFIG --cflags | sed s/@INCLUDE_des@//`"
|
||||
CFLAGS=$ac_save_CFLAGS;export CFLAGS
|
||||
LDFLAGS=$ac_save_LDFLAGS;export LDFLAGS
|
||||
FOUND_KRB5=yes
|
||||
FOUND_KRB5_VIA_CONFIG=yes
|
||||
AC_MSG_RESULT(yes. Found $KRB5CONFIG)
|
||||
else
|
||||
AC_MSG_RESULT(no. Fallback to previous krb5 detection strategy)
|
||||
fi
|
||||
fi
|
||||
|
||||
if test x$FOUND_KRB5 != x"yes"; then
|
||||
#################################################
|
||||
# check for location of Kerberos 5 install
|
||||
AC_MSG_CHECKING(for kerberos 5 install path)
|
||||
case "$krb5_withval" in
|
||||
no)
|
||||
AC_MSG_RESULT(no krb5-path given)
|
||||
;;
|
||||
yes)
|
||||
AC_MSG_RESULT(/usr)
|
||||
FOUND_KRB5=yes
|
||||
;;
|
||||
*)
|
||||
AC_MSG_RESULT($krb5_withval)
|
||||
KRB5_CFLAGS="-I$krb5_withval/include"
|
||||
KRB5_CPPFLAGS="-I$krb5_withval/include"
|
||||
KRB5_LDFLAGS="-L$krb5_withval/lib"
|
||||
FOUND_KRB5=yes
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
|
||||
if test x$FOUND_KRB5 != x"yes"; then
|
||||
#################################################
|
||||
# see if this box has the SuSE location for the heimdal krb implementation
|
||||
AC_MSG_CHECKING(for /usr/include/heimdal)
|
||||
if test -d /usr/include/heimdal; then
|
||||
if test -f /usr/lib/heimdal/lib/libkrb5.a; then
|
||||
KRB5_CFLAGS="-I/usr/include/heimdal"
|
||||
KRB5_CPPFLAGS="-I/usr/include/heimdal"
|
||||
KRB5_LDFLAGS="-L/usr/lib/heimdal/lib"
|
||||
AC_MSG_RESULT(yes)
|
||||
else
|
||||
KRB5_CFLAGS="-I/usr/include/heimdal"
|
||||
KRB5_CPPFLAGS="-I/usr/include/heimdal"
|
||||
AC_MSG_RESULT(yes)
|
||||
fi
|
||||
else
|
||||
AC_MSG_RESULT(no)
|
||||
fi
|
||||
fi
|
||||
|
||||
if test x$FOUND_KRB5 != x"yes"; then
|
||||
#################################################
|
||||
# see if this box has the RedHat location for kerberos
|
||||
AC_MSG_CHECKING(for /usr/kerberos)
|
||||
if test -d /usr/kerberos -a -f /usr/kerberos/lib/libkrb5.a; then
|
||||
KRB5_LDFLAGS="-L/usr/kerberos/lib"
|
||||
KRB5_CFLAGS="-I/usr/kerberos/include"
|
||||
KRB5_CPPFLAGS="-I/usr/kerberos/include"
|
||||
AC_MSG_RESULT(yes)
|
||||
else
|
||||
AC_MSG_RESULT(no)
|
||||
fi
|
||||
fi
|
||||
|
||||
ac_save_CFLAGS=$CFLAGS
|
||||
ac_save_CPPFLAGS=$CPPFLAGS
|
||||
ac_save_LDFLAGS=$LDFLAGS
|
||||
|
||||
#MIT needs this, to let us see 'internal' parts of the headers we use
|
||||
KRB5_CFLAGS="${KRB5_CFLAGS} -DKRB5_PRIVATE -DKRB5_DEPRECATED"
|
||||
|
||||
#Heimdal needs this
|
||||
#TODO: we need to parse KRB5_LIBS for -L path
|
||||
# and set -Wl,-rpath -Wl,path
|
||||
|
||||
CFLAGS="$CFLAGS $KRB5_CFLAGS"
|
||||
CPPFLAGS="$CPPFLAGS $KRB5_CPPFLAGS"
|
||||
LDFLAGS="$LDFLAGS $KRB5_LDFLAGS"
|
||||
|
||||
KRB5_LIBS="$KRB5_LDFLAGS $KRB5_LIBS"
|
||||
|
||||
# now check for krb5.h. Some systems have the libraries without the headers!
|
||||
# note that this check is done here to allow for different kerberos
|
||||
# include paths
|
||||
AC_CHECK_HEADERS(krb5.h)
|
||||
|
||||
if test x"$ac_cv_header_krb5_h" = x"no"; then
|
||||
# Give a warning if KRB5 support was not explicitly requested,
|
||||
# i.e with_krb5_support = auto, otherwise die with an error.
|
||||
if test x"$with_krb5_support" = x"yes"; then
|
||||
AC_MSG_ERROR([KRB5 cannot be supported without krb5.h])
|
||||
else
|
||||
AC_MSG_WARN([KRB5 cannot be supported without krb5.h])
|
||||
fi
|
||||
# Turn off AD support and restore CFLAGS and LIBS variables
|
||||
with_krb5_support="no"
|
||||
fi
|
||||
|
||||
CFLAGS=$ac_save_CFLAGS
|
||||
CPPFLAGS=$ac_save_CPPFLAGS
|
||||
LDFLAGS=$ac_save_LDFLAGS
|
||||
fi
|
||||
|
||||
# Now we have determined whether we really want KRB5 support
|
||||
|
||||
if test x"$with_krb5_support" != x"no"; then
|
||||
ac_save_CFLAGS=$CFLAGS
|
||||
ac_save_CPPFLAGS=$CPPFLAGS
|
||||
ac_save_LDFLAGS=$LDFLAGS
|
||||
ac_save_LIBS=$LIBS
|
||||
|
||||
CFLAGS="$CFLAGS $KRB5_CFLAGS"
|
||||
CPPFLAGS="$CPPFLAGS $KRB5_CPPFLAGS"
|
||||
LDFLAGS="$LDFLAGS $KRB5_LDFLAGS"
|
||||
|
||||
# now check for gssapi headers. This is also done here to allow for
|
||||
# different kerberos include paths
|
||||
AC_CHECK_HEADERS(gssapi.h gssapi_krb5.h gssapi/gssapi.h gssapi/gssapi_generic.h gssapi/gssapi_krb5.h com_err.h)
|
||||
|
||||
|
||||
# Heimdal checks.
|
||||
# But only if we didn't have a krb5-config to tell us this already
|
||||
if test x"$FOUND_KRB5_VIA_CONFIG" != x"yes"; then
|
||||
##################################################################
|
||||
# we might need the k5crypto and com_err libraries on some systems
|
||||
AC_CHECK_LIB_EXT(com_err, KRB5_LIBS, _et_list)
|
||||
AC_CHECK_LIB_EXT(k5crypto, KRB5_LIBS, krb5_encrypt_data)
|
||||
|
||||
AC_CHECK_LIB_EXT(crypto, KRB5_LIBS, des_set_key)
|
||||
AC_CHECK_LIB_EXT(asn1, KRB5_LIBS, copy_Authenticator)
|
||||
AC_CHECK_LIB_EXT(roken, KRB5_LIBS, roken_getaddrinfo_hostspec)
|
||||
fi
|
||||
|
||||
# Heimdal checks. On static Heimdal gssapi must be linked before krb5.
|
||||
AC_CHECK_LIB_EXT(gssapi, KRB5_LIBS, gss_display_status,[],[],
|
||||
AC_DEFINE(HAVE_GSSAPI,1,[Whether GSSAPI is available]))
|
||||
|
||||
########################################################
|
||||
# now see if we can find the krb5 libs in standard paths
|
||||
# or as specified above
|
||||
AC_CHECK_LIB_EXT(krb5, KRB5_LIBS, krb5_mk_req_extended)
|
||||
AC_CHECK_LIB_EXT(krb5, KRB5_LIBS, krb5_kt_compare)
|
||||
|
||||
########################################################
|
||||
# now see if we can find the gssapi libs in standard paths
|
||||
if test x"$ac_cv_lib_ext_gssapi_gss_display_status" != x"yes"; then
|
||||
AC_CHECK_LIB_EXT(gssapi_krb5, KRB5_LIBS,gss_display_status,[],[],
|
||||
AC_DEFINE(HAVE_GSSAPI,1,[Whether GSSAPI is available]))
|
||||
fi
|
||||
|
||||
AC_CHECK_FUNC_EXT(krb5_set_real_time, $KRB5_LIBS)
|
||||
AC_CHECK_FUNC_EXT(krb5_set_default_in_tkt_etypes, $KRB5_LIBS)
|
||||
AC_CHECK_FUNC_EXT(krb5_set_default_tgs_ktypes, $KRB5_LIBS)
|
||||
AC_CHECK_FUNC_EXT(krb5_principal2salt, $KRB5_LIBS)
|
||||
AC_CHECK_FUNC_EXT(krb5_use_enctype, $KRB5_LIBS)
|
||||
AC_CHECK_FUNC_EXT(krb5_string_to_key, $KRB5_LIBS)
|
||||
AC_CHECK_FUNC_EXT(krb5_get_pw_salt, $KRB5_LIBS)
|
||||
AC_CHECK_FUNC_EXT(krb5_string_to_key_salt, $KRB5_LIBS)
|
||||
AC_CHECK_FUNC_EXT(krb5_auth_con_setkey, $KRB5_LIBS)
|
||||
AC_CHECK_FUNC_EXT(krb5_auth_con_setuseruserkey, $KRB5_LIBS)
|
||||
AC_CHECK_FUNC_EXT(krb5_locate_kdc, $KRB5_LIBS)
|
||||
AC_CHECK_FUNC_EXT(krb5_get_permitted_enctypes, $KRB5_LIBS)
|
||||
AC_CHECK_FUNC_EXT(krb5_get_default_in_tkt_etypes, $KRB5_LIBS)
|
||||
AC_CHECK_FUNC_EXT(krb5_free_ktypes, $KRB5_LIBS)
|
||||
AC_CHECK_FUNC_EXT(krb5_free_data_contents, $KRB5_LIBS)
|
||||
AC_CHECK_FUNC_EXT(krb5_principal_get_comp_string, $KRB5_LIBS)
|
||||
AC_CHECK_FUNC_EXT(krb5_free_unparsed_name, $KRB5_LIBS)
|
||||
AC_CHECK_FUNC_EXT(krb5_free_keytab_entry_contents, $KRB5_LIBS)
|
||||
AC_CHECK_FUNC_EXT(krb5_kt_free_entry, $KRB5_LIBS)
|
||||
AC_CHECK_FUNC_EXT(krb5_krbhst_get_addrinfo, $KRB5_LIBS)
|
||||
AC_CHECK_FUNC_EXT(krb5_verify_checksum, $KRB5_LIBS)
|
||||
AC_CHECK_FUNC_EXT(krb5_c_verify_checksum, $KRB5_LIBS)
|
||||
AC_CHECK_FUNC_EXT(krb5_ticket_get_authorization_data_type, $KRB5_LIBS)
|
||||
AC_CHECK_FUNC_EXT(krb5_krbhst_get_addrinfo, $KRB5_LIBS)
|
||||
AC_CHECK_FUNC_EXT(krb5_c_enctype_compare, $KRB5_LIBS)
|
||||
AC_CHECK_FUNC_EXT(krb5_enctypes_compatible_keys, $KRB5_LIBS)
|
||||
AC_CHECK_FUNC_EXT(krb5_get_error_string, $KRB5_LIBS)
|
||||
AC_CHECK_FUNC_EXT(krb5_free_error_string, $KRB5_LIBS)
|
||||
AC_CHECK_FUNC_EXT(krb5_initlog, $KRB5_LIBS)
|
||||
AC_CHECK_FUNC_EXT(krb5_addlog_func, $KRB5_LIBS)
|
||||
AC_CHECK_FUNC_EXT(krb5_set_warn_dest, $KRB5_LIBS)
|
||||
|
||||
LIBS="$LIBS $KRB5_LIBS"
|
||||
|
||||
AC_CACHE_CHECK([for krb5_log_facility type],
|
||||
samba_cv_HAVE_KRB5_LOG_FACILITY,[
|
||||
AC_TRY_COMPILE([#include <krb5.h>],
|
||||
[krb5_log_facility block;],
|
||||
samba_cv_HAVE_KRB5_LOG_FACILITY=yes,
|
||||
samba_cv_HAVE_KRB5_LOG_FACILITY=no)])
|
||||
|
||||
if test x"$samba_cv_HAVE_KRB5_LOG_FACILITY" = x"yes"; then
|
||||
AC_DEFINE(HAVE_KRB5_LOG_FACILITY,1,
|
||||
[Whether the type krb5_log_facility exists])
|
||||
fi
|
||||
|
||||
AC_CACHE_CHECK([for krb5_encrypt_block type],
|
||||
samba_cv_HAVE_KRB5_ENCRYPT_BLOCK,[
|
||||
AC_TRY_COMPILE([#include <krb5.h>],
|
||||
[krb5_encrypt_block block;],
|
||||
samba_cv_HAVE_KRB5_ENCRYPT_BLOCK=yes,
|
||||
samba_cv_HAVE_KRB5_ENCRYPT_BLOCK=no)])
|
||||
|
||||
if test x"$samba_cv_HAVE_KRB5_ENCRYPT_BLOCK" = x"yes"; then
|
||||
AC_DEFINE(HAVE_KRB5_ENCRYPT_BLOCK,1,
|
||||
[Whether the type krb5_encrypt_block exists])
|
||||
fi
|
||||
|
||||
AC_CACHE_CHECK([for addrtype in krb5_address],
|
||||
samba_cv_HAVE_ADDRTYPE_IN_KRB5_ADDRESS,[
|
||||
AC_TRY_COMPILE([#include <krb5.h>],
|
||||
[krb5_address kaddr; kaddr.addrtype = ADDRTYPE_INET;],
|
||||
samba_cv_HAVE_ADDRTYPE_IN_KRB5_ADDRESS=yes,
|
||||
samba_cv_HAVE_ADDRTYPE_IN_KRB5_ADDRESS=no)])
|
||||
if test x"$samba_cv_HAVE_ADDRTYPE_IN_KRB5_ADDRESS" = x"yes"; then
|
||||
AC_DEFINE(HAVE_ADDRTYPE_IN_KRB5_ADDRESS,1,
|
||||
[Whether the krb5_address struct has a addrtype property])
|
||||
fi
|
||||
|
||||
AC_CACHE_CHECK([for addr_type in krb5_address],
|
||||
samba_cv_HAVE_ADDR_TYPE_IN_KRB5_ADDRESS,[
|
||||
AC_TRY_COMPILE([#include <krb5.h>],
|
||||
[krb5_address kaddr; kaddr.addr_type = KRB5_ADDRESS_INET;],
|
||||
samba_cv_HAVE_ADDR_TYPE_IN_KRB5_ADDRESS=yes,
|
||||
samba_cv_HAVE_ADDR_TYPE_IN_KRB5_ADDRESS=no)])
|
||||
if test x"$samba_cv_HAVE_ADDR_TYPE_IN_KRB5_ADDRESS" = x"yes"; then
|
||||
AC_DEFINE(HAVE_ADDR_TYPE_IN_KRB5_ADDRESS,1,
|
||||
[Whether the krb5_address struct has a addr_type property])
|
||||
fi
|
||||
|
||||
AC_CACHE_CHECK([for enc_part2 in krb5_ticket],
|
||||
samba_cv_HAVE_KRB5_TKT_ENC_PART2,[
|
||||
AC_TRY_COMPILE([#include <krb5.h>],
|
||||
[krb5_ticket tkt; tkt.enc_part2->authorization_data[0]->contents = NULL;],
|
||||
samba_cv_HAVE_KRB5_TKT_ENC_PART2=yes,
|
||||
samba_cv_HAVE_KRB5_TKT_ENC_PART2=no)])
|
||||
if test x"$samba_cv_HAVE_KRB5_TKT_ENC_PART2" = x"yes"; then
|
||||
AC_DEFINE(HAVE_KRB5_TKT_ENC_PART2,1,
|
||||
[Whether the krb5_ticket struct has a enc_part2 property])
|
||||
fi
|
||||
|
||||
AC_CACHE_CHECK([for keyblock in krb5_creds],
|
||||
samba_cv_HAVE_KRB5_KEYBLOCK_IN_CREDS,[
|
||||
AC_TRY_COMPILE([#include <krb5.h>],
|
||||
[krb5_creds creds; krb5_keyblock kb; creds.keyblock = kb;],
|
||||
samba_cv_HAVE_KRB5_KEYBLOCK_IN_CREDS=yes,
|
||||
samba_cv_HAVE_KRB5_KEYBLOCK_IN_CREDS=no)])
|
||||
|
||||
if test x"$samba_cv_HAVE_KRB5_KEYBLOCK_IN_CREDS" = x"yes"; then
|
||||
AC_DEFINE(HAVE_KRB5_KEYBLOCK_IN_CREDS,1,
|
||||
[Whether the krb5_creds struct has a keyblock property])
|
||||
fi
|
||||
|
||||
AC_CACHE_CHECK([for session in krb5_creds],
|
||||
samba_cv_HAVE_KRB5_SESSION_IN_CREDS,[
|
||||
AC_TRY_COMPILE([#include <krb5.h>],
|
||||
[krb5_creds creds; krb5_keyblock kb; creds.session = kb;],
|
||||
samba_cv_HAVE_KRB5_SESSION_IN_CREDS=yes,
|
||||
samba_cv_HAVE_KRB5_SESSION_IN_CREDS=no)])
|
||||
|
||||
if test x"$samba_cv_HAVE_KRB5_SESSION_IN_CREDS" = x"yes"; then
|
||||
AC_DEFINE(HAVE_KRB5_SESSION_IN_CREDS,1,
|
||||
[Whether the krb5_creds struct has a session property])
|
||||
fi
|
||||
|
||||
AC_CACHE_CHECK([for keyvalue in krb5_keyblock],
|
||||
samba_cv_HAVE_KRB5_KEYBLOCK_KEYVALUE,[
|
||||
AC_TRY_COMPILE([#include <krb5.h>],
|
||||
[krb5_keyblock key; key.keyvalue.data = NULL;],
|
||||
samba_cv_HAVE_KRB5_KEYBLOCK_KEYVALUE=yes,
|
||||
samba_cv_HAVE_KRB5_KEYBLOCK_KEYVALUE=no)])
|
||||
if test x"$samba_cv_HAVE_KRB5_KEYBLOCK_KEYVALUE" = x"yes"; then
|
||||
AC_DEFINE(HAVE_KRB5_KEYBLOCK_KEYVALUE,1,
|
||||
[Whether the krb5_keyblock struct has a keyvalue property])
|
||||
fi
|
||||
|
||||
AC_CACHE_CHECK([for ENCTYPE_ARCFOUR_HMAC_MD5],
|
||||
samba_cv_HAVE_ENCTYPE_ARCFOUR_HMAC_MD5,[
|
||||
AC_TRY_COMPILE([#include <krb5.h>],
|
||||
[krb5_enctype enctype; enctype = ENCTYPE_ARCFOUR_HMAC_MD5;],
|
||||
samba_cv_HAVE_ENCTYPE_ARCFOUR_HMAC_MD5=yes,
|
||||
samba_cv_HAVE_ENCTYPE_ARCFOUR_HMAC_MD5=no)])
|
||||
AC_CACHE_CHECK([for KEYTYPE_ARCFOUR_56],
|
||||
samba_cv_HAVE_KEYTYPE_ARCFOUR_56,[
|
||||
AC_TRY_COMPILE([#include <krb5.h>],
|
||||
[krb5_keytype keytype; keytype = KEYTYPE_ARCFOUR_56;],
|
||||
samba_cv_HAVE_KEYTYPE_ARCFOUR_56=yes,
|
||||
samba_cv_HAVE_KEYTYPE_ARCFOUR_56=no)])
|
||||
# Heimdals with KEYTYPE_ARCFOUR but not KEYTYPE_ARCFOUR_56 are broken
|
||||
# w.r.t. arcfour and windows, so we must not enable it here
|
||||
if test x"$samba_cv_HAVE_ENCTYPE_ARCFOUR_HMAC_MD5" = x"yes" -a\
|
||||
x"$samba_cv_HAVE_KEYTYPE_ARCFOUR_56" = x"yes"; then
|
||||
AC_DEFINE(HAVE_ENCTYPE_ARCFOUR_HMAC_MD5,1,
|
||||
[Whether the ENCTYPE_ARCFOUR_HMAC_MD5 key type is available])
|
||||
fi
|
||||
|
||||
AC_CACHE_CHECK([for AP_OPTS_USE_SUBKEY],
|
||||
samba_cv_HAVE_AP_OPTS_USE_SUBKEY,[
|
||||
AC_TRY_COMPILE([#include <krb5.h>],
|
||||
[krb5_flags ap_options; ap_options = AP_OPTS_USE_SUBKEY;],
|
||||
samba_cv_HAVE_AP_OPTS_USE_SUBKEY=yes,
|
||||
samba_cv_HAVE_AP_OPTS_USE_SUBKEY=no)])
|
||||
if test x"$samba_cv_HAVE_AP_OPTS_USE_SUBKEY" = x"yes"; then
|
||||
AC_DEFINE(HAVE_AP_OPTS_USE_SUBKEY,1,
|
||||
[Whether the AP_OPTS_USE_SUBKEY ap option is available])
|
||||
fi
|
||||
|
||||
AC_CACHE_CHECK([for KV5M_KEYTAB],
|
||||
samba_cv_HAVE_KV5M_KEYTAB,[
|
||||
AC_TRY_COMPILE([#include <krb5.h>],
|
||||
[krb5_keytab_entry entry; entry.magic = KV5M_KEYTAB;],
|
||||
samba_cv_HAVE_KV5M_KEYTAB=yes,
|
||||
samba_cv_HAVE_KV5M_KEYTAB=no)])
|
||||
if test x"$samba_cv_HAVE_KV5M_KEYTAB" = x"yes"; then
|
||||
AC_DEFINE(HAVE_KV5M_KEYTAB,1,
|
||||
[Whether the KV5M_KEYTAB option is available])
|
||||
fi
|
||||
|
||||
AC_CACHE_CHECK([for the krb5_princ_component macro],
|
||||
samba_cv_HAVE_KRB5_PRINC_COMPONENT,[
|
||||
AC_TRY_LINK([#include <krb5.h>],
|
||||
[const krb5_data *pkdata; krb5_context context; krb5_principal principal;
|
||||
pkdata = krb5_princ_component(context, principal, 0);],
|
||||
samba_cv_HAVE_KRB5_PRINC_COMPONENT=yes,
|
||||
samba_cv_HAVE_KRB5_PRINC_COMPONENT=no)])
|
||||
if test x"$samba_cv_HAVE_KRB5_PRINC_COMPONENT" = x"yes"; then
|
||||
AC_DEFINE(HAVE_KRB5_PRINC_COMPONENT,1,
|
||||
[Whether krb5_princ_component is available])
|
||||
fi
|
||||
|
||||
AC_CACHE_CHECK([for key in krb5_keytab_entry],
|
||||
samba_cv_HAVE_KRB5_KEYTAB_ENTRY_KEY,[
|
||||
AC_TRY_COMPILE([#include <krb5.h>],
|
||||
[krb5_keytab_entry entry; krb5_keyblock e; entry.key = e;],
|
||||
samba_cv_HAVE_KRB5_KEYTAB_ENTRY_KEY=yes,
|
||||
samba_cv_HAVE_KRB5_KEYTAB_ENTRY_KEY=no)])
|
||||
if test x"$samba_cv_HAVE_KRB5_KEYTAB_ENTRY_KEY" = x"yes"; then
|
||||
AC_DEFINE(HAVE_KRB5_KEYTAB_ENTRY_KEY,1,
|
||||
[Whether krb5_keytab_entry has key member])
|
||||
fi
|
||||
|
||||
AC_CACHE_CHECK([for keyblock in krb5_keytab_entry],
|
||||
samba_cv_HAVE_KRB5_KEYTAB_ENTRY_KEYBLOCK,[
|
||||
AC_TRY_COMPILE([#include <krb5.h>],
|
||||
[krb5_keytab_entry entry; entry.keyblock.keytype = 0;],
|
||||
samba_cv_HAVE_KRB5_KEYTAB_ENTRY_KEYBLOCK=yes,
|
||||
samba_cv_HAVE_KRB5_KEYTAB_ENTRY_KEYBLOCK=no)])
|
||||
if test x"$samba_cv_HAVE_KRB5_KEYTAB_ENTRY_KEYBLOCK" = x"yes"; then
|
||||
AC_DEFINE(HAVE_KRB5_KEYTAB_ENTRY_KEYBLOCK,1,
|
||||
[Whether krb5_keytab_entry has keyblock member])
|
||||
fi
|
||||
|
||||
AC_CACHE_CHECK([for WRFILE: keytab support],
|
||||
samba_cv_HAVE_WRFILE_KEYTAB,[
|
||||
AC_TRY_RUN([
|
||||
#include<krb5.h>
|
||||
main()
|
||||
{
|
||||
krb5_context context;
|
||||
krb5_keytab keytab;
|
||||
krb5_init_context(&context);
|
||||
return krb5_kt_resolve(context, "WRFILE:api", &keytab);
|
||||
}],
|
||||
samba_cv_HAVE_WRFILE_KEYTAB=yes,
|
||||
samba_cv_HAVE_WRFILE_KEYTAB=no)])
|
||||
if test x"$samba_cv_HAVE_WRFILE_KEYTAB" = x"yes"; then
|
||||
AC_DEFINE(HAVE_WRFILE_KEYTAB,1,
|
||||
[Whether the WRFILE:-keytab is supported])
|
||||
fi
|
||||
|
||||
AC_CACHE_CHECK([for krb5_princ_realm returns krb5_realm or krb5_data],
|
||||
samba_cv_KRB5_PRINC_REALM_RETURNS_REALM,[
|
||||
AC_TRY_COMPILE([#include <krb5.h>],
|
||||
[krb5_context context;krb5_principal principal;krb5_realm realm;
|
||||
realm = *krb5_princ_realm(context, principal);],
|
||||
samba_cv_KRB5_PRINC_REALM_RETURNS_REALM=yes,
|
||||
samba_cv_KRB5_PRINC_REALM_RETURNS_REALM=no)])
|
||||
if test x"$samba_cv_KRB5_PRINC_REALM_RETURNS_REALM" = x"yes"; then
|
||||
AC_DEFINE(KRB5_PRINC_REALM_RETURNS_REALM,1,
|
||||
[Whether krb5_princ_realm returns krb5_realm or krb5_data])
|
||||
fi
|
||||
|
||||
# TODO: check all gssapi headers for this
|
||||
AC_CACHE_CHECK([for GSS_C_DCE_STYLE in gssapi.h],
|
||||
samba_cv_GSS_C_DCE_STYLE,[
|
||||
AC_TRY_COMPILE([#include <gssapi.h>],
|
||||
[int flags = GSS_C_DCE_STYLE;],
|
||||
samba_cv_GSS_C_DCE_STYLE=yes,
|
||||
samba_cv_GSS_C_DCE_STYLE=no)])
|
||||
|
||||
AC_CHECK_FUNC_EXT(gsskrb5_get_initiator_subkey, $KRB5_LIBS)
|
||||
AC_CHECK_FUNC_EXT(gsskrb5_extract_authz_data_from_sec_context, $KRB5_LIBS)
|
||||
AC_CHECK_FUNC_EXT(gsskrb5_register_acceptor_identity, $KRB5_LIBS)
|
||||
AC_CHECK_FUNC_EXT(gss_krb5_ccache_name, $KRB5_LIBS)
|
||||
if test x"$ac_cv_lib_ext_krb5_krb5_mk_req_extended" = x"yes"; then
|
||||
AC_DEFINE(HAVE_KRB5,1,[Whether to have KRB5 support])
|
||||
AC_MSG_CHECKING(whether KRB5 support is used)
|
||||
SMB_ENABLE(KRB5,YES)
|
||||
AC_MSG_RESULT(yes)
|
||||
echo "KRB5_CFLAGS: ${KRB5_CFLAGS}"
|
||||
echo "KRB5_CPPFLAGS: ${KRB5_CPPFLAGS}"
|
||||
echo "KRB5_LDFLAGS: ${KRB5_LDFLAGS}"
|
||||
echo "KRB5_LIBS: ${KRB5_LIBS}"
|
||||
else
|
||||
if test x"$with_krb5_support" = x"yes"; then
|
||||
AC_MSG_ERROR(a working krb5 library is needed for KRB5 support)
|
||||
else
|
||||
AC_MSG_WARN(a working krb5 library is needed for KRB5 support)
|
||||
fi
|
||||
KRB5_CFLAGS=""
|
||||
KRB5_CPPFLAGS=""
|
||||
KRB5_LDFLAGS=""
|
||||
KRB5_LIBS=""
|
||||
with_krb5_support=no
|
||||
fi
|
||||
|
||||
# checks if we have access to a libkdc
|
||||
# and can use it for our builtin kdc server_service
|
||||
KDC_CFLAGS=""
|
||||
KDC_CPPFLAGS=""
|
||||
KDC_DLFLAGS=""
|
||||
KDC_LIBS=""
|
||||
AC_CHECK_HEADERS(kdc.h)
|
||||
AC_CHECK_LIB_EXT(kdc, KDC_LIBS, krb5_kdc_default_config)
|
||||
AC_CHECK_LIB_EXT(hdb, KDC_LIBS, hdb_generate_key_set_password)
|
||||
|
||||
AC_MSG_CHECKING(whether libkdc is used)
|
||||
if test x"$ac_cv_header_kdc_h" = x"yes"; then
|
||||
if test x"$ac_cv_lib_ext_kdc_krb5_kdc_default_config" = x"yes"; then
|
||||
if test x"$ac_cv_lib_ext_hdb_hdb_generate_key_set_password" = x"yes"; then
|
||||
SMB_ENABLE(KDC,YES)
|
||||
AC_MSG_RESULT(yes)
|
||||
echo "KDC_LIBS: ${KDC_LIBS}"
|
||||
else
|
||||
AC_MSG_RESULT(no)
|
||||
fi
|
||||
else
|
||||
AC_MSG_RESULT(no)
|
||||
fi
|
||||
else
|
||||
AC_MSG_RESULT(no)
|
||||
fi
|
||||
|
||||
CFLAGS=$ac_save_CFLAGS
|
||||
CPPFLAGS=$ac_save_CPPFLAGS
|
||||
LDFLAGS=$ac_save_LDFLAGS
|
||||
LIBS="$ac_save_LIBS"
|
||||
|
||||
# as a nasty hack add the krb5 stuff to the global vars,
|
||||
# at some point this should not be needed anymore when the build system
|
||||
# can handle that alone
|
||||
CFLAGS="$CFLAGS $KRB5_CFLAGS"
|
||||
CPPFLAGS="$CPPFLAGS $KRB5_CPPFLAGS"
|
||||
LDFLAGS="$LDFLAGS $KRB5_LDFLAGS"
|
||||
fi
|
||||
|
||||
SMB_EXT_LIB(KRB5,[${KRB5_LIBS}],[${KRB5_CFLAGS}],[${KRB5_CPPFLAGS}],[${KRB5_LDFLAGS}])
|
||||
SMB_EXT_LIB(KDC,[${KDC_LIBS}],[${KDC_CFLAGS}],[${KDC_CPPFLAGS}],[${KDC_LDFLAGS}])
|
||||
@@ -0,0 +1,15 @@
|
||||
#################################
|
||||
# Start SUBSYSTEM KERBEROS
|
||||
[SUBSYSTEM::KERBEROS]
|
||||
PRIVATE_PROTO_HEADER = proto.h
|
||||
OBJ_FILES = kerberos.o \
|
||||
clikrb5.o \
|
||||
kerberos_heimdal.o \
|
||||
kerberos_util.o \
|
||||
kerberos_pac.o \
|
||||
gssapi_parse.o \
|
||||
krb5_init_context.o
|
||||
PUBLIC_DEPENDENCIES = HEIMDAL_KRB5 NDR_KRB5PAC samba-socket LIBCLI_RESOLVE
|
||||
PRIVATE_DEPENDENCIES = ASN1_UTIL HEIMDAL_ROKEN_ADDRINFO auth_sam CREDENTIALS_KRB5
|
||||
# End SUBSYSTEM KERBEROS
|
||||
#################################
|
||||
@@ -0,0 +1,114 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
|
||||
simple GSSAPI wrappers
|
||||
|
||||
Copyright (C) Andrew Tridgell 2001
|
||||
Copyright (C) Jim McDonough <jmcd@us.ibm.com> 2002
|
||||
Copyright (C) Luke Howard 2003
|
||||
|
||||
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
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
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.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "libcli/util/asn_1.h"
|
||||
#include "auth/gensec/gensec.h"
|
||||
|
||||
/*
|
||||
generate a krb5 GSS-API wrapper packet given a ticket
|
||||
*/
|
||||
DATA_BLOB gensec_gssapi_gen_krb5_wrap(TALLOC_CTX *mem_ctx, const DATA_BLOB *ticket, const uint8_t tok_id[2])
|
||||
{
|
||||
struct asn1_data data;
|
||||
DATA_BLOB ret = data_blob(NULL,0);
|
||||
|
||||
if (!ticket->data) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
ZERO_STRUCT(data);
|
||||
|
||||
asn1_push_tag(&data, ASN1_APPLICATION(0));
|
||||
asn1_write_OID(&data, GENSEC_OID_KERBEROS5);
|
||||
|
||||
asn1_write(&data, tok_id, 2);
|
||||
asn1_write(&data, ticket->data, ticket->length);
|
||||
asn1_pop_tag(&data);
|
||||
|
||||
if (data.has_error) {
|
||||
DEBUG(1,("Failed to build krb5 wrapper at offset %d\n", (int)data.ofs));
|
||||
asn1_free(&data);
|
||||
}
|
||||
|
||||
ret = data_blob_talloc(mem_ctx, data.data, data.length);
|
||||
asn1_free(&data);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
parse a krb5 GSS-API wrapper packet giving a ticket
|
||||
*/
|
||||
BOOL gensec_gssapi_parse_krb5_wrap(TALLOC_CTX *mem_ctx, const DATA_BLOB *blob, DATA_BLOB *ticket, uint8_t tok_id[2])
|
||||
{
|
||||
BOOL ret;
|
||||
struct asn1_data data;
|
||||
int data_remaining;
|
||||
|
||||
asn1_load(&data, *blob);
|
||||
asn1_start_tag(&data, ASN1_APPLICATION(0));
|
||||
asn1_check_OID(&data, GENSEC_OID_KERBEROS5);
|
||||
|
||||
data_remaining = asn1_tag_remaining(&data);
|
||||
|
||||
if (data_remaining < 3) {
|
||||
data.has_error = True;
|
||||
} else {
|
||||
asn1_read(&data, tok_id, 2);
|
||||
data_remaining -= 2;
|
||||
*ticket = data_blob_talloc(mem_ctx, NULL, data_remaining);
|
||||
asn1_read(&data, ticket->data, ticket->length);
|
||||
}
|
||||
|
||||
asn1_end_tag(&data);
|
||||
|
||||
ret = !data.has_error;
|
||||
|
||||
asn1_free(&data);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
check a GSS-API wrapper packet givin an expected OID
|
||||
*/
|
||||
BOOL gensec_gssapi_check_oid(const DATA_BLOB *blob, const char *oid)
|
||||
{
|
||||
BOOL ret;
|
||||
struct asn1_data data;
|
||||
|
||||
asn1_load(&data, *blob);
|
||||
asn1_start_tag(&data, ASN1_APPLICATION(0));
|
||||
asn1_check_OID(&data, GENSEC_OID_KERBEROS5);
|
||||
|
||||
ret = !data.has_error;
|
||||
|
||||
asn1_free(&data);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,466 @@
|
||||
AllowedWorkstationNames and Krb5
|
||||
--------------------------------
|
||||
|
||||
Microsoft uses the clientAddresses *multiple value* field in the krb5
|
||||
protocol (particularly the AS_REQ) to communicate it's netbios name.
|
||||
This is (my guess) to permit the userWorkstations field to work.
|
||||
|
||||
The KDC I imagine checks the netbios address against this value, in
|
||||
the same way that the Samba server does this.
|
||||
|
||||
The checking of this implies a little of the next question:
|
||||
|
||||
Is a DAL the layer we need?
|
||||
---------------------------
|
||||
|
||||
Looking at what we need to pass around, I start to seriously wonder if
|
||||
the DAL is even the right layer - we seem to want to create an account
|
||||
authorization abstraction layer - is this account permitted to login to
|
||||
this computer, at this time?
|
||||
|
||||
This information in AD is much richer than the Heimdal HDB, and it
|
||||
seems to make sense to do AD-specific access control checks in an
|
||||
AD-specific layer, not in the back-end agnostic server.
|
||||
|
||||
Because the DAL only reads in the principalName as the key, it has
|
||||
trouble performing access control decisions on things other than the
|
||||
name.
|
||||
|
||||
I'll be very interested if the DAL really works for eDirectory too.
|
||||
Perhaps all we need to do is add in the same kludges as we have in
|
||||
Samba 3.0 for eDirectory. Hmm...
|
||||
|
||||
That said, the current layer provides us with a very good start, and
|
||||
any redefinition would occour from that basis.
|
||||
|
||||
|
||||
GSSAPI layer requirements
|
||||
-------------------------
|
||||
|
||||
Welcome to the wonderful world of canonicalisation
|
||||
|
||||
The MIT GSSAPI libs do not support kinit returning a different
|
||||
realm to what the client asked for, even just in case differences.
|
||||
|
||||
Heimdal has the same problem, and this applies to the krb5 layer, not
|
||||
just gssapi.
|
||||
|
||||
We need to test if the canonicalisation is controlled by the KDCOption
|
||||
flags, windows always sends the Canonicalize flags
|
||||
|
||||
Old Clients (samba3 and HPUX clients) uses 'selfmade' gssapi/krb5
|
||||
for using it in the CIFS session setup. Because they use krb5_mk_req()
|
||||
they get a chksum field depending on the encryption type, but that's wrong
|
||||
for GSSAPI (see rfc 1964 section 1.1.1). The Cheksum type 8003
|
||||
should be used in the Authenticator of the AP-REQ! That allows the channel bindings,
|
||||
the GCC_C_* req_flags and optional delegation tickets to be passed from the client to the server.
|
||||
Hower windows doesn't seems to care about if the checksum is of the wrong type,
|
||||
for CIFS SessionSetups, it seems that the req_flags are just set to 0.
|
||||
So this can't work for LDAP connections with sign or seal, or for any DCERPC
|
||||
connection.
|
||||
|
||||
So we need to also support old clients!
|
||||
|
||||
Principal Names, long and short names
|
||||
-------------------------------------
|
||||
|
||||
As far as servicePrincipalNames are concerned, these are not
|
||||
canonicalised, except as regards the realm in the reply. That is, the
|
||||
client gets back the principal it asked for, with the realm portion
|
||||
'fixed' to uppercase, long form.
|
||||
|
||||
The short name of the realm seems to be accepted for at least AS_REQ
|
||||
operations, but because the server performs canonicalisation, this
|
||||
causes pain for current client libraries.
|
||||
|
||||
The canonicalisation of names matters not only for the KDC, but also
|
||||
for code that has to deal with keytabs.
|
||||
|
||||
We also need to handle type 10 names (NT-ENTERPRISE), which are a full
|
||||
principal name in the principal field, unrelated to the realm.
|
||||
|
||||
HOST/ Aliases
|
||||
-------------
|
||||
|
||||
There is another post somewhere (ref lost for the moment) that details
|
||||
where in active directory the list of stored aliases for HOST/ is.
|
||||
This should be read, parsed and used to allow any of these requests to
|
||||
use the HOST/ key.
|
||||
|
||||
For example, this is how HTTP/, DNS/ and CIFS/ can use HOST/ without
|
||||
any explicit entry.
|
||||
|
||||
|
||||
Jean-Baptiste.Marchand@hsc.fr reminds me:
|
||||
|
||||
> This is the SPNMappings attribute in Active Directory:
|
||||
|
||||
> http://msdn.microsoft.com/library/en-us/adschema/adschema/a_spnmappings.asp
|
||||
|
||||
We implement this in hdb-ldb.
|
||||
|
||||
Implicit names for Win2000 Accounts
|
||||
-----------------------------------
|
||||
|
||||
Despite not having a DNS name, nor a servicePrincipalName on accounts
|
||||
created by computers running win2000, it appears we are expected to
|
||||
have an implicit mapping from host/computer.full.name and
|
||||
host/computer to it's entry.
|
||||
|
||||
Returned Salt for PreAuthentication
|
||||
-----------------------------------
|
||||
|
||||
When the server replies for pre-authentication, it returns the Salt,
|
||||
which may be in the form of a principalName that is in no way
|
||||
connected with the current names. (ie, even if the userPrincipalName
|
||||
and samAccountName are renamed, the old salt is returned).
|
||||
|
||||
This is probably the kerberos standard salt, kept in the 'Key'. The
|
||||
standard generation rules are found in a Mail from Luke Howard dated
|
||||
10 Nov 2004:
|
||||
|
||||
|
||||
From: Luke Howard <lukeh@padl.com>
|
||||
Message-Id: <200411100231.iAA2VLUW006101@au.padl.com>
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=US-ASCII
|
||||
Organization: PADL Software Pty Ltd
|
||||
To: lukeh@padl.com
|
||||
Date: Wed, 10 Nov 2004 13:31:21 +1100
|
||||
Versions: dmail (bsd44) 2.6d/makemail 2.10
|
||||
Cc: huaraz@moeller.plus.com, samba-technical@lists.samba.org
|
||||
Subject: Re: Samba-3.0.7-1.3E Active Directory Issues
|
||||
X-BeenThere: samba-technical@lists.samba.org
|
||||
X-Mailman-Version: 2.1.4
|
||||
Precedence: list
|
||||
Reply-To: lukeh@padl.com
|
||||
|
||||
Did some more testing, it appears the behaviour has another
|
||||
explanation. It appears that the standard Kerberos password salt
|
||||
algorithm is applied in Windows 2003, just that the source principal
|
||||
name is different.
|
||||
|
||||
Here is what I've been able to deduce from creating a bunch of
|
||||
different accounts:
|
||||
|
||||
Type of account Principal for Salting
|
||||
========================================================================
|
||||
Computer Account host/<SAM-Name-Without-$>.realm@REALM
|
||||
User Account Without UPN <SAM-Name>@REALM
|
||||
User Account With UPN <LHS-Of-UPN>@REALM
|
||||
|
||||
Note that if the computer account's SAM account name does not include
|
||||
the trailing '$', then the entire SAM account name is used as input to
|
||||
the salting principal. Setting a UPN for a computer account has no
|
||||
effect.
|
||||
|
||||
It seems to me odd that the RHS of the UPN is not used in the salting
|
||||
principal. For example, a user with UPN foo@mydomain.com in the realm
|
||||
MYREALM.COM would have a salt of MYREALM.COMfoo. Perhaps this is to
|
||||
allow a user's UPN suffix to be changed without changing the salt. And
|
||||
perhaps using the UPN for salting signifies a move away SAM names and
|
||||
their associated constraints.
|
||||
|
||||
For more information on how UPNs relate to the Kerberos protocol,
|
||||
see:
|
||||
|
||||
http://www.ietf.org/proceedings/01dec/I-D/draft-ietf-krb-wg-kerberos-referrals-02.txt
|
||||
|
||||
-- Luke
|
||||
|
||||
--
|
||||
|
||||
|
||||
|
||||
|
||||
Heimdal oddities
|
||||
----------------
|
||||
|
||||
Heimdal is built such that it should be able to serve multiple realms
|
||||
at the same time. This isn't relevant for Samba's use, but it shows
|
||||
up in a lot of generalisations throughout the code.
|
||||
|
||||
Other odd things:
|
||||
- Support for multiple passwords on a client account: we seem to
|
||||
call hdb_next_enctype2key() in the pre-authentication routines to
|
||||
allow multiple passwords per account in krb5. (I think this was
|
||||
intened to allow multiple salts)
|
||||
|
||||
State Machine safety
|
||||
--------------------
|
||||
|
||||
Samba is a giant state machine, and as such have very different
|
||||
requirements to those traditionally expressed for kerberos and GSSAPI
|
||||
libraries.
|
||||
|
||||
Samba requires all of the libraries it uses to be state machine safe in
|
||||
their use of internal data. This does not mean thread safe, and an
|
||||
application could be thread safe, but not state machine safe (if it
|
||||
instead used thread-local variables).
|
||||
|
||||
So, what does it mean for a library to be state machine safe? This is
|
||||
mostly a question of context, and how the library manages whatever
|
||||
internal state machines it has. If the library uses a context
|
||||
variable, passed in by the caller, which contains all the information
|
||||
about the current state of the library, then it is safe. An example
|
||||
of this state is the sequence number and session keys for an ongoing
|
||||
encrypted session).
|
||||
|
||||
The other issue affecting state machines is 'blocking' (waiting for a
|
||||
read on a network socket).
|
||||
|
||||
Heimdal has this 'state machine safety' in parts, and we have modified
|
||||
the lorikeet branch to improve this behviour, when using a new,
|
||||
non-standard API.
|
||||
|
||||
Heimdal uses a per-context variable for the 'krb5_auth_context', which
|
||||
controls the ongoing encrypted connection, but does use global
|
||||
variables for the ubiquitous krb5_context parameter.
|
||||
|
||||
The modification that has added most to 'state machine safety' of
|
||||
GSSAPI is the addition of the gsskrb5_acquire_creds function. This
|
||||
allows the caller to specify a keytab and ccache, for use by the
|
||||
GSSAPI code. Therefore there is no need to use global variables to
|
||||
communicate this information.
|
||||
|
||||
At a more theoritical level (simply counting static and global
|
||||
variables) Heimdal is not state machine safe for the GSSAPI layer.
|
||||
The Krb5 layer alone is much closer, as far as I can tell, blocking
|
||||
excepted. .
|
||||
|
||||
To deal with blocking, we could have a fork()ed child per context,
|
||||
using the 'GSSAPI export context' function to transfer
|
||||
the GSSAPI state back into the main code for the wrap()/unwrap() part
|
||||
of the operation. This will still hit issues of static storage (one
|
||||
gss_krb5_context per process, and multiple GSSAPI encrypted sessions
|
||||
at a time) but these may not matter in practice.
|
||||
|
||||
In the short-term, we deal with blocking by taking over the network
|
||||
send() and recv() functions, therefore making them 'semi-async'. This
|
||||
doens't apply to DNS yet.
|
||||
|
||||
GSSAPI and Kerberos extensions
|
||||
------------------------------
|
||||
|
||||
This is a general list of the other extensions we have made to / need from
|
||||
the kerberos libraries
|
||||
|
||||
- DCE_STYLE
|
||||
|
||||
- gsskrb5_get_initiator_subkey() (return the exact key that Samba3
|
||||
has always asked for. gsskrb5_get_subkey() might do what we need
|
||||
anyway)
|
||||
|
||||
- gsskrb5_acquire_creds() (takes keytab and/or ccache as input
|
||||
parameters, see keytab and state machine discussion)
|
||||
|
||||
- gss_krb5_copy_service_keyblock() (get the key used to actually
|
||||
encrypt the ticket to the server, because the same key is used for
|
||||
the PAC validation).
|
||||
- gsskrb5_extract_authtime_from_sec_context (get authtime from
|
||||
kerberos ticket)
|
||||
- gsskrb5_extract_authz_data_from_sec_context (get authdata from
|
||||
ticket, ie the PAC. Must unwrap the data if in an AD-IFRELEVENT)
|
||||
- gsskrb5_wrap_size (find out how big the wrapped packet will be,
|
||||
given input length).
|
||||
|
||||
Keytab requirements
|
||||
-------------------
|
||||
|
||||
Because windows machine account handling is very different to the
|
||||
tranditional 'MIT' keytab operation. This starts when we look at the
|
||||
basis of the secrets handling:
|
||||
|
||||
Traditional 'MIT' behaviour is to use a keytab, continaing salted key
|
||||
data, extracted from the KDC. (In this modal, there is no 'service
|
||||
password', instead the keys are often simply application of random
|
||||
bytes). Heimdal also implements this behaviour.
|
||||
|
||||
The windows modal is very different - instead of sharing a keytab with
|
||||
each member server, a password is stored for the whole machine. The
|
||||
password is set with non-kerberos mechanisms (particularly SAMR, a
|
||||
DCE-RPC service) and when interacting on a kerberos basis, the
|
||||
password is salted by the client. (That is, no salt infromation
|
||||
appears to be convayed from the KDC to the member).
|
||||
|
||||
In dealing with this modal, we leverage both the traditional file
|
||||
keytab and in-MEMORY keytabs.
|
||||
|
||||
When dealing with a windows KDC, the behaviour regarding case
|
||||
sensitivity and canonacolisation must be accomidated. This means that
|
||||
an incoming request to a member server may have a wide variety of
|
||||
service principal names. These include:
|
||||
|
||||
machine$@REALM (samba clients)
|
||||
HOST/foo.bar@realm (win2k clients)
|
||||
HOST/foo@realm (win2k clients, using netbios)
|
||||
cifs/foo.bar@realm (winxp clients)
|
||||
cifs/foo@realm (winxp clients, using netbios)
|
||||
|
||||
as well as all case variations on the above.
|
||||
|
||||
Because that all got 'too hard' to put into a keytab in the
|
||||
traditional way (with the client to specify the name), we either
|
||||
pre-compute the keys into a traditional keytab or make an in-MEMORY
|
||||
keytab at run time. In both cases we specifiy the principal name to
|
||||
GSSAPI, which avoids the need to store duplicate principals.
|
||||
|
||||
We use a 'private' keytab in our private dir, referenced from the
|
||||
secrets.ldb by default.
|
||||
|
||||
Extra Heimdal functions used
|
||||
----------------------------
|
||||
(an attempt to list some of the Heimdal-specific functions I know we use)
|
||||
|
||||
krb5_free_keyblock_contents()
|
||||
|
||||
also a raft of prinicpal manipulation functions:
|
||||
|
||||
Prncipal Manipulation
|
||||
---------------------
|
||||
|
||||
Samba makes extensive use of the principal manipulation functions in
|
||||
Heimdal, including the known structure behind krb_principal and
|
||||
krb5_realm (a char *).
|
||||
|
||||
Authz data extraction
|
||||
---------------------
|
||||
|
||||
We use krb5_ticket_get_authorization_data_type(), and expect it to
|
||||
return the correct authz data, even if wrapped in an AD-IFRELEVENT container.
|
||||
|
||||
|
||||
KDC/hdb Extensions
|
||||
--------------
|
||||
|
||||
We have modified Heimdal's 'hdb' interface to specify the 'type' of
|
||||
Principal being requested. This allows us to correctly behave with
|
||||
the different 'classes' of Principal name.
|
||||
|
||||
We currently define 2 classes:
|
||||
- client (kinit)
|
||||
- server (tgt)
|
||||
|
||||
I also now specify the kerberos principal as an explict parameter, not
|
||||
an in/out value on the entry itself.
|
||||
|
||||
Inside hdb-ldb, we add krbtgt as a special class of principal, because
|
||||
of particular special-case backend requirements.
|
||||
|
||||
Callbacks:
|
||||
In addition, I have added a new interface hdb_fetch_ex(), which
|
||||
returns a structure including callbacks, which provide the hook for
|
||||
the PAC, as well as a callback into the main access control routines.
|
||||
|
||||
A new callback should be added to increment the bad password counter
|
||||
on failure.
|
||||
|
||||
Another possability for a callback is to obtain the keys. This would
|
||||
allow the plaintext password to only be hashed into the encryption
|
||||
types we need. This idea from the eDirectory/MIT DAL work.
|
||||
|
||||
This probably should be combined with storing the hashed passwords in
|
||||
the supplementalCredentials attribute. If combined with a kvno
|
||||
parameter, this could also allow changing of the krbtgt password
|
||||
(valuable for security).
|
||||
|
||||
libkdc
|
||||
------
|
||||
|
||||
Samba4 needs to be built as a single binary (design requirement), and
|
||||
this should include the KDC. Samba also (and perhaps more
|
||||
importantly) needs to control the configuration environment of the
|
||||
KDC.
|
||||
|
||||
The interface we have defined for libkdc allow for packet injection
|
||||
into the post-socket layer, with a defined krb5_context and
|
||||
kdb5_kdc_configuration structure. These effectively redirect the
|
||||
kerberos warnings, logging and database calls as we require.
|
||||
|
||||
Using our socket lib
|
||||
--------------------
|
||||
|
||||
An important detail in the use of libkdc is that we use our own socket
|
||||
lib. This allows the KDC code to be as portable as the rest of samba
|
||||
(this cuts both ways), but far more importantly it ensures a
|
||||
consistancy in the handling of requests, binding to sockets etc.
|
||||
|
||||
To handle TCP, we use of our socket layer in much the same way as
|
||||
we deal with TCP for CIFS. Tridge created a generic packet handling
|
||||
layer for this.
|
||||
|
||||
For the client, we likewise must take over the socket functions, so
|
||||
that our single thread smbd will not lock up talking to itself. (We
|
||||
allow processing while waiting for packets in our socket routines).
|
||||
|
||||
Kerberos logging support
|
||||
------------------------
|
||||
|
||||
Samba now (optionally in the main code, required for the KDC) uses the
|
||||
krb5_log_facility from Heimdal. This allows us to redirect the
|
||||
warnings and status from the KDC (and client/server kerberos code) to
|
||||
Samba's DEBUG() system.
|
||||
|
||||
Similarly important is the Heimdal-specific krb5_get_error_string()
|
||||
function, which does a lot to reduce the 'administrator pain' level,
|
||||
by providing specific, english text-string error messages instead of
|
||||
just error code translations.
|
||||
|
||||
|
||||
Short name rules
|
||||
----------------
|
||||
|
||||
Samba is highly likely to be misconfigured, in many weird and
|
||||
interesting ways. As such, we have a patch for Heimdal that avoids
|
||||
DNS lookups on names without a . in them. This should avoid some
|
||||
delay and root server load.
|
||||
|
||||
PAC Correctness
|
||||
---------------
|
||||
|
||||
We now put the PAC into the TGT, not just the service ticket.
|
||||
|
||||
Forwarded tickets
|
||||
-----------------
|
||||
|
||||
We extract forwarded tickets from the GSSAPI layer, and put
|
||||
them into the credentials. We can then use them for proxy work.
|
||||
|
||||
|
||||
Kerberos TODO
|
||||
=============
|
||||
|
||||
(Feel free to contribute to any of these tasks, or ask
|
||||
abartlet@samba.org about them).
|
||||
|
||||
Lockout Control
|
||||
--------------
|
||||
|
||||
We need to get (either if PADL publishes their patch, or write our
|
||||
own) access control hooks in the Heimdal KDC. We need to lockout
|
||||
accounts, and perform other controls.
|
||||
|
||||
Gssmonger
|
||||
---------
|
||||
|
||||
Microsoft has released a testsuite called gssmonger, which tests
|
||||
interop. We should compile it against lorikeet-heimdal, MIT and see
|
||||
if we can build a 'Samba4' server for it.
|
||||
|
||||
Kpasswd server
|
||||
--------------
|
||||
|
||||
I have a partial kpasswd server which needs finishing, and a we need a
|
||||
client testsuite written, either via the krb5 API or directly against
|
||||
GENSEC and the ASN.1 routines.
|
||||
|
||||
Currently it only works for Heimdal, not MIT clients. This may be due
|
||||
to call ordering constraints.
|
||||
|
||||
|
||||
Correct TCP support
|
||||
-------------------
|
||||
|
||||
Our current TCP support does not send back 'too large' error messages
|
||||
if the high bit is set. This is needed for a proposed extension
|
||||
mechanism, but is likewise unsupported in both current Heimdal and MIT.
|
||||
@@ -0,0 +1,124 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
kerberos utility library
|
||||
Copyright (C) Andrew Tridgell 2001
|
||||
Copyright (C) Remus Koos 2001
|
||||
Copyright (C) Nalin Dahyabhai 2004.
|
||||
Copyright (C) Jeremy Allison 2004.
|
||||
Copyright (C) Andrew Bartlett <abartlet@samba.org> 2004-2005
|
||||
|
||||
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
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
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.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "system/kerberos.h"
|
||||
#include "roken.h"
|
||||
|
||||
#ifdef HAVE_KRB5
|
||||
|
||||
/*
|
||||
simulate a kinit, putting the tgt in the given credentials cache.
|
||||
Orignally by remus@snapserver.com
|
||||
|
||||
This version is built to use a keyblock, rather than needing the
|
||||
original password.
|
||||
*/
|
||||
int kerberos_kinit_keyblock_cc(krb5_context ctx, krb5_ccache cc,
|
||||
krb5_principal principal, krb5_keyblock *keyblock,
|
||||
time_t *expire_time, time_t *kdc_time)
|
||||
{
|
||||
krb5_error_code code = 0;
|
||||
krb5_creds my_creds;
|
||||
krb5_get_init_creds_opt options;
|
||||
|
||||
krb5_get_init_creds_opt_init(&options);
|
||||
|
||||
krb5_get_init_creds_opt_set_default_flags(ctx, NULL, NULL, &options);
|
||||
|
||||
if ((code = krb5_get_init_creds_keyblock(ctx, &my_creds, principal, keyblock,
|
||||
0, NULL, &options))) {
|
||||
return code;
|
||||
}
|
||||
|
||||
if ((code = krb5_cc_initialize(ctx, cc, principal))) {
|
||||
krb5_free_cred_contents(ctx, &my_creds);
|
||||
return code;
|
||||
}
|
||||
|
||||
if ((code = krb5_cc_store_cred(ctx, cc, &my_creds))) {
|
||||
krb5_free_cred_contents(ctx, &my_creds);
|
||||
return code;
|
||||
}
|
||||
|
||||
if (expire_time) {
|
||||
*expire_time = (time_t) my_creds.times.endtime;
|
||||
}
|
||||
|
||||
if (kdc_time) {
|
||||
*kdc_time = (time_t) my_creds.times.starttime;
|
||||
}
|
||||
|
||||
krb5_free_cred_contents(ctx, &my_creds);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
simulate a kinit, putting the tgt in the given credentials cache.
|
||||
Orignally by remus@snapserver.com
|
||||
*/
|
||||
int kerberos_kinit_password_cc(krb5_context ctx, krb5_ccache cc,
|
||||
krb5_principal principal, const char *password,
|
||||
time_t *expire_time, time_t *kdc_time)
|
||||
{
|
||||
krb5_error_code code = 0;
|
||||
krb5_creds my_creds;
|
||||
krb5_get_init_creds_opt options;
|
||||
|
||||
krb5_get_init_creds_opt_init(&options);
|
||||
|
||||
krb5_get_init_creds_opt_set_default_flags(ctx, NULL, NULL, &options);
|
||||
|
||||
if ((code = krb5_get_init_creds_password(ctx, &my_creds, principal, password,
|
||||
NULL,
|
||||
NULL, 0, NULL, &options))) {
|
||||
return code;
|
||||
}
|
||||
|
||||
if ((code = krb5_cc_initialize(ctx, cc, principal))) {
|
||||
krb5_free_cred_contents(ctx, &my_creds);
|
||||
return code;
|
||||
}
|
||||
|
||||
if ((code = krb5_cc_store_cred(ctx, cc, &my_creds))) {
|
||||
krb5_free_cred_contents(ctx, &my_creds);
|
||||
return code;
|
||||
}
|
||||
|
||||
if (expire_time) {
|
||||
*expire_time = (time_t) my_creds.times.endtime;
|
||||
}
|
||||
|
||||
if (kdc_time) {
|
||||
*kdc_time = (time_t) my_creds.times.starttime;
|
||||
}
|
||||
|
||||
krb5_free_cred_contents(ctx, &my_creds);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,158 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
simple kerberos5 routines for active directory
|
||||
Copyright (C) Andrew Tridgell 2001
|
||||
Copyright (C) Luke Howard 2002-2003
|
||||
|
||||
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
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
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.
|
||||
*/
|
||||
|
||||
#if defined(HAVE_KRB5)
|
||||
|
||||
#include "auth/kerberos/krb5_init_context.h"
|
||||
#include "librpc/gen_ndr/krb5pac.h"
|
||||
|
||||
struct auth_serversupplied_info;
|
||||
struct cli_credentials;
|
||||
|
||||
struct ccache_container {
|
||||
struct smb_krb5_context *smb_krb5_context;
|
||||
krb5_ccache ccache;
|
||||
};
|
||||
|
||||
struct keytab_container {
|
||||
struct smb_krb5_context *smb_krb5_context;
|
||||
krb5_keytab keytab;
|
||||
};
|
||||
|
||||
/* not really ASN.1, but RFC 1964 */
|
||||
#define TOK_ID_KRB_AP_REQ ((const uint8_t *)"\x01\x00")
|
||||
#define TOK_ID_KRB_AP_REP ((const uint8_t *)"\x02\x00")
|
||||
#define TOK_ID_KRB_ERROR ((const uint8_t *)"\x03\x00")
|
||||
#define TOK_ID_GSS_GETMIC ((const uint8_t *)"\x01\x01")
|
||||
#define TOK_ID_GSS_WRAP ((const uint8_t *)"\x02\x01")
|
||||
|
||||
#ifdef HAVE_KRB5_KEYBLOCK_KEYVALUE
|
||||
#define KRB5_KEY_TYPE(k) ((k)->keytype)
|
||||
#define KRB5_KEY_LENGTH(k) ((k)->keyvalue.length)
|
||||
#define KRB5_KEY_DATA(k) ((k)->keyvalue.data)
|
||||
#else
|
||||
#define KRB5_KEY_TYPE(k) ((k)->enctype)
|
||||
#define KRB5_KEY_LENGTH(k) ((k)->length)
|
||||
#define KRB5_KEY_DATA(k) ((k)->contents)
|
||||
#endif /* HAVE_KRB5_KEYBLOCK_KEYVALUE */
|
||||
|
||||
#ifndef HAVE_KRB5_SET_REAL_TIME
|
||||
krb5_error_code krb5_set_real_time(krb5_context context, int32_t seconds, int32_t microseconds);
|
||||
#endif
|
||||
|
||||
#ifndef HAVE_KRB5_SET_DEFAULT_TGS_KTYPES
|
||||
krb5_error_code krb5_set_default_tgs_ktypes(krb5_context ctx, const krb5_enctype *enc);
|
||||
#endif
|
||||
|
||||
#if defined(HAVE_KRB5_AUTH_CON_SETKEY) && !defined(HAVE_KRB5_AUTH_CON_SETUSERUSERKEY)
|
||||
krb5_error_code krb5_auth_con_setuseruserkey(krb5_context context, krb5_auth_context auth_context, krb5_keyblock *keyblock);
|
||||
#endif
|
||||
|
||||
#ifndef HAVE_KRB5_FREE_UNPARSED_NAME
|
||||
void krb5_free_unparsed_name(krb5_context ctx, char *val);
|
||||
#endif
|
||||
|
||||
#if defined(HAVE_KRB5_PRINCIPAL_GET_COMP_STRING) && !defined(HAVE_KRB5_PRINC_COMPONENT)
|
||||
const krb5_data *krb5_princ_component(krb5_context context, krb5_principal principal, int i );
|
||||
#endif
|
||||
|
||||
/* Samba wrapper function for krb5 functionality. */
|
||||
void setup_kaddr( krb5_address *pkaddr, struct sockaddr *paddr);
|
||||
int create_kerberos_key_from_string(krb5_context context, krb5_principal host_princ, krb5_data *password, krb5_keyblock *key, krb5_enctype enctype);
|
||||
int create_kerberos_key_from_string_direct(krb5_context context, krb5_principal host_princ, krb5_data *password, krb5_keyblock *key, krb5_enctype enctype);
|
||||
krb5_const_principal get_principal_from_tkt(krb5_ticket *tkt);
|
||||
krb5_error_code get_kerberos_allowed_etypes(krb5_context context, krb5_enctype **enctypes);
|
||||
void free_kerberos_etypes(krb5_context context, krb5_enctype *enctypes);
|
||||
BOOL get_krb5_smb_session_key(krb5_context context, krb5_auth_context auth_context, DATA_BLOB *session_key, BOOL remote);
|
||||
krb5_error_code ads_krb5_mk_req(krb5_context context,
|
||||
krb5_auth_context *auth_context,
|
||||
const krb5_flags ap_req_options,
|
||||
const char *principal,
|
||||
krb5_ccache ccache,
|
||||
krb5_data *outbuf);
|
||||
BOOL get_auth_data_from_tkt(TALLOC_CTX *mem_ctx, DATA_BLOB *auth_data, krb5_ticket *tkt);
|
||||
NTSTATUS ads_verify_ticket(TALLOC_CTX *mem_ctx,
|
||||
struct smb_krb5_context *smb_krb5_context,
|
||||
krb5_auth_context *auth_context,
|
||||
struct cli_credentials *machine_account,
|
||||
const char *service,
|
||||
const DATA_BLOB *enc_ticket,
|
||||
krb5_ticket **tkt,
|
||||
DATA_BLOB *ap_rep,
|
||||
krb5_keyblock **keyblock);
|
||||
int kerberos_kinit_password_cc(krb5_context ctx, krb5_ccache cc,
|
||||
krb5_principal principal, const char *password,
|
||||
time_t *expire_time, time_t *kdc_time);
|
||||
int kerberos_kinit_keyblock_cc(krb5_context ctx, krb5_ccache cc,
|
||||
krb5_principal principal, krb5_keyblock *keyblock,
|
||||
time_t *expire_time, time_t *kdc_time);
|
||||
krb5_principal kerberos_fetch_salt_princ_for_host_princ(krb5_context context,
|
||||
krb5_principal host_princ,
|
||||
int enctype);
|
||||
void kerberos_set_creds_enctype(krb5_creds *pcreds, int enctype);
|
||||
BOOL kerberos_compatible_enctypes(krb5_context context, krb5_enctype enctype1, krb5_enctype enctype2);
|
||||
void kerberos_free_data_contents(krb5_context context, krb5_data *pdata);
|
||||
krb5_error_code smb_krb5_kt_free_entry(krb5_context context, krb5_keytab_entry *kt_entry);
|
||||
char *smb_get_krb5_error_message(krb5_context context, krb5_error_code code, TALLOC_CTX *mem_ctx);
|
||||
krb5_error_code kinit_to_ccache(TALLOC_CTX *parent_ctx,
|
||||
struct cli_credentials *credentials,
|
||||
struct smb_krb5_context *smb_krb5_context,
|
||||
krb5_ccache ccache);
|
||||
krb5_error_code principal_from_credentials(TALLOC_CTX *parent_ctx,
|
||||
struct cli_credentials *credentials,
|
||||
struct smb_krb5_context *smb_krb5_context,
|
||||
krb5_principal *princ);
|
||||
NTSTATUS kerberos_decode_pac(TALLOC_CTX *mem_ctx,
|
||||
struct PAC_DATA **pac_data_out,
|
||||
DATA_BLOB blob,
|
||||
krb5_context context,
|
||||
const krb5_keyblock *krbtgt_keyblock,
|
||||
const krb5_keyblock *service_keyblock,
|
||||
krb5_const_principal client_principal,
|
||||
time_t tgs_authtime,
|
||||
krb5_error_code *k5ret);
|
||||
NTSTATUS kerberos_pac_logon_info(TALLOC_CTX *mem_ctx,
|
||||
struct PAC_LOGON_INFO **logon_info,
|
||||
DATA_BLOB blob,
|
||||
krb5_context context,
|
||||
const krb5_keyblock *krbtgt_keyblock,
|
||||
const krb5_keyblock *service_keyblock,
|
||||
krb5_const_principal client_principal,
|
||||
time_t tgs_authtime,
|
||||
krb5_error_code *k5ret);
|
||||
krb5_error_code kerberos_encode_pac(TALLOC_CTX *mem_ctx,
|
||||
struct PAC_DATA *pac_data,
|
||||
krb5_context context,
|
||||
const krb5_keyblock *krbtgt_keyblock,
|
||||
const krb5_keyblock *service_keyblock,
|
||||
DATA_BLOB *pac);
|
||||
krb5_error_code kerberos_create_pac(TALLOC_CTX *mem_ctx,
|
||||
struct auth_serversupplied_info *server_info,
|
||||
krb5_context context,
|
||||
const krb5_keyblock *krbtgt_keyblock,
|
||||
const krb5_keyblock *service_keyblock,
|
||||
krb5_principal client_principal,
|
||||
time_t tgs_authtime,
|
||||
DATA_BLOB *pac);
|
||||
|
||||
#include "auth/kerberos/proto.h"
|
||||
|
||||
#endif /* HAVE_KRB5 */
|
||||
@@ -0,0 +1,101 @@
|
||||
/*
|
||||
* Copyright (c) 1997 - 2006 Kungliga Tekniska Högskolan
|
||||
* (Royal Institute of Technology, Stockholm, Sweden).
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* 3. Neither the name of the Institute nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/* This file for code taken from the Heimdal code, to preserve licence */
|
||||
/* Modified by Andrew Bartlett <abartlet@samba.org> */
|
||||
|
||||
#include "includes.h"
|
||||
#include "system/kerberos.h"
|
||||
|
||||
/* Taken from accept_sec_context.c,v 1.65 */
|
||||
krb5_error_code smb_rd_req_return_stuff(krb5_context context,
|
||||
krb5_auth_context *auth_context,
|
||||
const krb5_data *inbuf,
|
||||
krb5_keytab keytab,
|
||||
krb5_principal acceptor_principal,
|
||||
krb5_data *outbuf,
|
||||
krb5_ticket **ticket,
|
||||
krb5_keyblock **keyblock)
|
||||
{
|
||||
krb5_rd_req_in_ctx in = NULL;
|
||||
krb5_rd_req_out_ctx out = NULL;
|
||||
krb5_error_code kret;
|
||||
|
||||
*keyblock = NULL;
|
||||
*ticket = NULL;
|
||||
outbuf->length = 0;
|
||||
outbuf->data = NULL;
|
||||
|
||||
kret = krb5_rd_req_in_ctx_alloc(context, &in);
|
||||
if (kret == 0)
|
||||
kret = krb5_rd_req_in_set_keytab(context, in, keytab);
|
||||
if (kret) {
|
||||
if (in)
|
||||
krb5_rd_req_in_ctx_free(context, in);
|
||||
return kret;
|
||||
}
|
||||
|
||||
kret = krb5_rd_req_ctx(context,
|
||||
auth_context,
|
||||
inbuf,
|
||||
acceptor_principal,
|
||||
in, &out);
|
||||
krb5_rd_req_in_ctx_free(context, in);
|
||||
if (kret) {
|
||||
return kret;
|
||||
}
|
||||
|
||||
/*
|
||||
* We need to remember some data on the context_handle.
|
||||
*/
|
||||
kret = krb5_rd_req_out_get_ticket(context, out,
|
||||
ticket);
|
||||
if (kret == 0) {
|
||||
kret = krb5_rd_req_out_get_keyblock(context, out,
|
||||
keyblock);
|
||||
}
|
||||
krb5_rd_req_out_ctx_free(context, out);
|
||||
|
||||
if (kret == 0) {
|
||||
kret = krb5_mk_rep(context, *auth_context, outbuf);
|
||||
}
|
||||
|
||||
if (kret) {
|
||||
krb5_free_ticket(context, *ticket);
|
||||
krb5_free_keyblock(context, *keyblock);
|
||||
krb5_data_free(outbuf);
|
||||
}
|
||||
|
||||
return kret;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,617 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
|
||||
Create and parse the krb5 PAC
|
||||
|
||||
Copyright (C) Andrew Bartlett <abartlet@samba.org> 2004-2005
|
||||
Copyright (C) Andrew Tridgell 2001
|
||||
Copyright (C) Luke Howard 2002-2003
|
||||
Copyright (C) Stefan Metzmacher 2004-2005
|
||||
|
||||
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
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
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.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "system/kerberos.h"
|
||||
#include "auth/kerberos/kerberos.h"
|
||||
#include "librpc/gen_ndr/ndr_krb5pac.h"
|
||||
#include "lib/ldb/include/ldb.h"
|
||||
#include "auth/auth_sam.h"
|
||||
|
||||
static krb5_error_code check_pac_checksum(TALLOC_CTX *mem_ctx,
|
||||
DATA_BLOB pac_data,
|
||||
struct PAC_SIGNATURE_DATA *sig,
|
||||
krb5_context context,
|
||||
const krb5_keyblock *keyblock)
|
||||
{
|
||||
krb5_error_code ret;
|
||||
krb5_crypto crypto;
|
||||
Checksum cksum;
|
||||
|
||||
cksum.cksumtype = (CKSUMTYPE)sig->type;
|
||||
cksum.checksum.length = sig->signature.length;
|
||||
cksum.checksum.data = sig->signature.data;
|
||||
|
||||
ret = krb5_crypto_init(context,
|
||||
keyblock,
|
||||
0,
|
||||
&crypto);
|
||||
if (ret) {
|
||||
DEBUG(0,("krb5_crypto_init() failed: %s\n",
|
||||
smb_get_krb5_error_message(context, ret, mem_ctx)));
|
||||
return ret;
|
||||
}
|
||||
ret = krb5_verify_checksum(context,
|
||||
crypto,
|
||||
KRB5_KU_OTHER_CKSUM,
|
||||
pac_data.data,
|
||||
pac_data.length,
|
||||
&cksum);
|
||||
krb5_crypto_destroy(context, crypto);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
NTSTATUS kerberos_decode_pac(TALLOC_CTX *mem_ctx,
|
||||
struct PAC_DATA **pac_data_out,
|
||||
DATA_BLOB blob,
|
||||
krb5_context context,
|
||||
const krb5_keyblock *krbtgt_keyblock,
|
||||
const krb5_keyblock *service_keyblock,
|
||||
krb5_const_principal client_principal,
|
||||
time_t tgs_authtime,
|
||||
krb5_error_code *k5ret)
|
||||
{
|
||||
krb5_error_code ret;
|
||||
NTSTATUS status;
|
||||
struct PAC_SIGNATURE_DATA *srv_sig_ptr = NULL;
|
||||
struct PAC_SIGNATURE_DATA *kdc_sig_ptr = NULL;
|
||||
struct PAC_SIGNATURE_DATA *srv_sig_wipe = NULL;
|
||||
struct PAC_SIGNATURE_DATA *kdc_sig_wipe = NULL;
|
||||
struct PAC_LOGON_INFO *logon_info = NULL;
|
||||
struct PAC_LOGON_NAME *logon_name = NULL;
|
||||
struct PAC_DATA *pac_data;
|
||||
struct PAC_DATA_RAW *pac_data_raw;
|
||||
|
||||
DATA_BLOB *srv_sig_blob = NULL;
|
||||
DATA_BLOB *kdc_sig_blob = NULL;
|
||||
|
||||
DATA_BLOB modified_pac_blob;
|
||||
NTTIME tgs_authtime_nttime;
|
||||
krb5_principal client_principal_pac;
|
||||
int i;
|
||||
|
||||
krb5_clear_error_string(context);
|
||||
|
||||
if (k5ret) {
|
||||
*k5ret = KRB5_PARSE_MALFORMED;
|
||||
}
|
||||
|
||||
pac_data = talloc(mem_ctx, struct PAC_DATA);
|
||||
pac_data_raw = talloc(mem_ctx, struct PAC_DATA_RAW);
|
||||
kdc_sig_wipe = talloc(mem_ctx, struct PAC_SIGNATURE_DATA);
|
||||
srv_sig_wipe = talloc(mem_ctx, struct PAC_SIGNATURE_DATA);
|
||||
if (!pac_data_raw || !pac_data || !kdc_sig_wipe || !srv_sig_wipe) {
|
||||
if (k5ret) {
|
||||
*k5ret = ENOMEM;
|
||||
}
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
|
||||
status = ndr_pull_struct_blob(&blob, pac_data, pac_data,
|
||||
(ndr_pull_flags_fn_t)ndr_pull_PAC_DATA);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
DEBUG(0,("can't parse the PAC\n"));
|
||||
return status;
|
||||
}
|
||||
|
||||
if (pac_data->num_buffers < 4) {
|
||||
/* we need logon_ingo, service_key and kdc_key */
|
||||
DEBUG(0,("less than 4 PAC buffers\n"));
|
||||
return NT_STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
status = ndr_pull_struct_blob(&blob, pac_data_raw, pac_data_raw,
|
||||
(ndr_pull_flags_fn_t)ndr_pull_PAC_DATA_RAW);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
DEBUG(0,("can't parse the PAC\n"));
|
||||
return status;
|
||||
}
|
||||
|
||||
if (pac_data_raw->num_buffers < 4) {
|
||||
/* we need logon_ingo, service_key and kdc_key */
|
||||
DEBUG(0,("less than 4 PAC buffers\n"));
|
||||
return NT_STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
if (pac_data->num_buffers != pac_data_raw->num_buffers) {
|
||||
/* we need logon_ingo, service_key and kdc_key */
|
||||
DEBUG(0,("misparse! PAC_DATA has %d buffers while PAC_DATA_RAW has %d\n",
|
||||
pac_data->num_buffers, pac_data_raw->num_buffers));
|
||||
return NT_STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
for (i=0; i < pac_data->num_buffers; i++) {
|
||||
if (pac_data->buffers[i].type != pac_data_raw->buffers[i].type) {
|
||||
DEBUG(0,("misparse! PAC_DATA buffer %d has type %d while PAC_DATA_RAW has %d\n",
|
||||
i, pac_data->buffers[i].type, pac_data->buffers[i].type));
|
||||
return NT_STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
switch (pac_data->buffers[i].type) {
|
||||
case PAC_TYPE_LOGON_INFO:
|
||||
if (!pac_data->buffers[i].info) {
|
||||
break;
|
||||
}
|
||||
logon_info = pac_data->buffers[i].info->logon_info.info;
|
||||
break;
|
||||
case PAC_TYPE_SRV_CHECKSUM:
|
||||
if (!pac_data->buffers[i].info) {
|
||||
break;
|
||||
}
|
||||
srv_sig_ptr = &pac_data->buffers[i].info->srv_cksum;
|
||||
srv_sig_blob = &pac_data_raw->buffers[i].info->remaining;
|
||||
break;
|
||||
case PAC_TYPE_KDC_CHECKSUM:
|
||||
if (!pac_data->buffers[i].info) {
|
||||
break;
|
||||
}
|
||||
kdc_sig_ptr = &pac_data->buffers[i].info->kdc_cksum;
|
||||
kdc_sig_blob = &pac_data_raw->buffers[i].info->remaining;
|
||||
break;
|
||||
case PAC_TYPE_LOGON_NAME:
|
||||
logon_name = &pac_data->buffers[i].info->logon_name;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!logon_info) {
|
||||
DEBUG(0,("PAC no logon_info\n"));
|
||||
return NT_STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
if (!logon_name) {
|
||||
DEBUG(0,("PAC no logon_name\n"));
|
||||
return NT_STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
if (!srv_sig_ptr || !srv_sig_blob) {
|
||||
DEBUG(0,("PAC no srv_key\n"));
|
||||
return NT_STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
if (!kdc_sig_ptr || !kdc_sig_blob) {
|
||||
DEBUG(0,("PAC no kdc_key\n"));
|
||||
return NT_STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
/* Find and zero out the signatures, as required by the signing algorithm */
|
||||
|
||||
/* We find the data blobs above, now we parse them to get at the exact portion we should zero */
|
||||
status = ndr_pull_struct_blob(kdc_sig_blob, kdc_sig_wipe, kdc_sig_wipe,
|
||||
(ndr_pull_flags_fn_t)ndr_pull_PAC_SIGNATURE_DATA);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
DEBUG(0,("can't parse the KDC signature\n"));
|
||||
return status;
|
||||
}
|
||||
|
||||
status = ndr_pull_struct_blob(srv_sig_blob, srv_sig_wipe, srv_sig_wipe,
|
||||
(ndr_pull_flags_fn_t)ndr_pull_PAC_SIGNATURE_DATA);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
DEBUG(0,("can't parse the SRV signature\n"));
|
||||
return status;
|
||||
}
|
||||
|
||||
/* Now zero the decoded structure */
|
||||
memset(kdc_sig_wipe->signature.data, '\0', kdc_sig_wipe->signature.length);
|
||||
memset(srv_sig_wipe->signature.data, '\0', srv_sig_wipe->signature.length);
|
||||
|
||||
/* and reencode, back into the same place it came from */
|
||||
status = ndr_push_struct_blob(kdc_sig_blob, pac_data_raw, kdc_sig_wipe,
|
||||
(ndr_push_flags_fn_t)ndr_push_PAC_SIGNATURE_DATA);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
DEBUG(0,("can't repack the KDC signature\n"));
|
||||
return status;
|
||||
}
|
||||
status = ndr_push_struct_blob(srv_sig_blob, pac_data_raw, srv_sig_wipe,
|
||||
(ndr_push_flags_fn_t)ndr_push_PAC_SIGNATURE_DATA);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
DEBUG(0,("can't repack the SRV signature\n"));
|
||||
return status;
|
||||
}
|
||||
|
||||
/* push out the whole structure, but now with zero'ed signatures */
|
||||
status = ndr_push_struct_blob(&modified_pac_blob, pac_data_raw, pac_data_raw,
|
||||
(ndr_push_flags_fn_t)ndr_push_PAC_DATA_RAW);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
DEBUG(0,("can't repack the RAW PAC\n"));
|
||||
return status;
|
||||
}
|
||||
|
||||
/* verify by service_key */
|
||||
ret = check_pac_checksum(mem_ctx,
|
||||
modified_pac_blob, srv_sig_ptr,
|
||||
context,
|
||||
service_keyblock);
|
||||
if (ret) {
|
||||
DEBUG(1, ("PAC Decode: Failed to verify the service signature: %s\n",
|
||||
smb_get_krb5_error_message(context, ret, mem_ctx)));
|
||||
if (k5ret) {
|
||||
*k5ret = ret;
|
||||
}
|
||||
return NT_STATUS_ACCESS_DENIED;
|
||||
}
|
||||
|
||||
if (krbtgt_keyblock) {
|
||||
ret = check_pac_checksum(mem_ctx,
|
||||
srv_sig_ptr->signature, kdc_sig_ptr,
|
||||
context, krbtgt_keyblock);
|
||||
if (ret) {
|
||||
DEBUG(1, ("PAC Decode: Failed to verify the KDC signature: %s\n",
|
||||
smb_get_krb5_error_message(context, ret, mem_ctx)));
|
||||
if (k5ret) {
|
||||
*k5ret = ret;
|
||||
}
|
||||
return NT_STATUS_ACCESS_DENIED;
|
||||
}
|
||||
}
|
||||
|
||||
/* Convert to NT time, so as not to loose accuracy in comparison */
|
||||
unix_to_nt_time(&tgs_authtime_nttime, tgs_authtime);
|
||||
|
||||
if (tgs_authtime_nttime != logon_name->logon_time) {
|
||||
DEBUG(2, ("PAC Decode: Logon time mismatch between ticket and PAC!\n"));
|
||||
DEBUG(2, ("PAC Decode: PAC: %s\n", nt_time_string(mem_ctx, logon_name->logon_time)));
|
||||
DEBUG(2, ("PAC Decode: Ticket: %s\n", nt_time_string(mem_ctx, tgs_authtime_nttime)));
|
||||
return NT_STATUS_ACCESS_DENIED;
|
||||
}
|
||||
|
||||
ret = krb5_parse_name_flags(context, logon_name->account_name, KRB5_PRINCIPAL_PARSE_NO_REALM,
|
||||
&client_principal_pac);
|
||||
if (ret) {
|
||||
DEBUG(2, ("Could not parse name from incoming PAC: [%s]: %s\n",
|
||||
logon_name->account_name,
|
||||
smb_get_krb5_error_message(context, ret, mem_ctx)));
|
||||
if (k5ret) {
|
||||
*k5ret = ret;
|
||||
}
|
||||
return NT_STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
if (!krb5_principal_compare_any_realm(context, client_principal, client_principal_pac)) {
|
||||
DEBUG(2, ("Name in PAC [%s] does not match principal name in ticket\n",
|
||||
logon_name->account_name));
|
||||
return NT_STATUS_ACCESS_DENIED;
|
||||
}
|
||||
|
||||
#if 0
|
||||
if (strcasecmp(logon_info->info3.base.account_name.string,
|
||||
"Administrator")== 0) {
|
||||
file_save("tmp_pac_data-admin.dat",blob.data,blob.length);
|
||||
}
|
||||
#endif
|
||||
|
||||
DEBUG(3,("Found account name from PAC: %s [%s]\n",
|
||||
logon_info->info3.base.account_name.string,
|
||||
logon_info->info3.base.full_name.string));
|
||||
*pac_data_out = pac_data;
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
_PUBLIC_ NTSTATUS kerberos_pac_logon_info(TALLOC_CTX *mem_ctx,
|
||||
struct PAC_LOGON_INFO **logon_info,
|
||||
DATA_BLOB blob,
|
||||
krb5_context context,
|
||||
const krb5_keyblock *krbtgt_keyblock,
|
||||
const krb5_keyblock *service_keyblock,
|
||||
krb5_const_principal client_principal,
|
||||
time_t tgs_authtime,
|
||||
krb5_error_code *k5ret)
|
||||
{
|
||||
NTSTATUS nt_status;
|
||||
struct PAC_DATA *pac_data;
|
||||
int i;
|
||||
nt_status = kerberos_decode_pac(mem_ctx, &pac_data,
|
||||
blob,
|
||||
context,
|
||||
krbtgt_keyblock,
|
||||
service_keyblock,
|
||||
client_principal,
|
||||
tgs_authtime,
|
||||
k5ret);
|
||||
if (!NT_STATUS_IS_OK(nt_status)) {
|
||||
return nt_status;
|
||||
}
|
||||
|
||||
*logon_info = NULL;
|
||||
for (i=0; i < pac_data->num_buffers; i++) {
|
||||
if (pac_data->buffers[i].type != PAC_TYPE_LOGON_INFO) {
|
||||
continue;
|
||||
}
|
||||
*logon_info = pac_data->buffers[i].info->logon_info.info;
|
||||
}
|
||||
if (!*logon_info) {
|
||||
return NT_STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
static krb5_error_code make_pac_checksum(TALLOC_CTX *mem_ctx,
|
||||
DATA_BLOB *pac_data,
|
||||
struct PAC_SIGNATURE_DATA *sig,
|
||||
krb5_context context,
|
||||
const krb5_keyblock *keyblock)
|
||||
{
|
||||
krb5_error_code ret;
|
||||
krb5_crypto crypto;
|
||||
Checksum cksum;
|
||||
|
||||
|
||||
ret = krb5_crypto_init(context,
|
||||
keyblock,
|
||||
0,
|
||||
&crypto);
|
||||
if (ret) {
|
||||
DEBUG(0,("krb5_crypto_init() failed: %s\n",
|
||||
smb_get_krb5_error_message(context, ret, mem_ctx)));
|
||||
return ret;
|
||||
}
|
||||
ret = krb5_create_checksum(context,
|
||||
crypto,
|
||||
KRB5_KU_OTHER_CKSUM,
|
||||
0,
|
||||
pac_data->data,
|
||||
pac_data->length,
|
||||
&cksum);
|
||||
if (ret) {
|
||||
DEBUG(2, ("PAC Verification failed: %s\n",
|
||||
smb_get_krb5_error_message(context, ret, mem_ctx)));
|
||||
}
|
||||
|
||||
krb5_crypto_destroy(context, crypto);
|
||||
|
||||
if (ret) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
sig->type = cksum.cksumtype;
|
||||
sig->signature = data_blob_talloc(mem_ctx, cksum.checksum.data, cksum.checksum.length);
|
||||
free_Checksum(&cksum);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
krb5_error_code kerberos_encode_pac(TALLOC_CTX *mem_ctx,
|
||||
struct PAC_DATA *pac_data,
|
||||
krb5_context context,
|
||||
const krb5_keyblock *krbtgt_keyblock,
|
||||
const krb5_keyblock *service_keyblock,
|
||||
DATA_BLOB *pac)
|
||||
{
|
||||
NTSTATUS nt_status;
|
||||
krb5_error_code ret;
|
||||
DATA_BLOB zero_blob = data_blob(NULL, 0);
|
||||
DATA_BLOB tmp_blob = data_blob(NULL, 0);
|
||||
struct PAC_SIGNATURE_DATA *kdc_checksum = NULL;
|
||||
struct PAC_SIGNATURE_DATA *srv_checksum = NULL;
|
||||
int i;
|
||||
|
||||
/* First, just get the keytypes filled in (and lengths right, eventually) */
|
||||
for (i=0; i < pac_data->num_buffers; i++) {
|
||||
if (pac_data->buffers[i].type != PAC_TYPE_KDC_CHECKSUM) {
|
||||
continue;
|
||||
}
|
||||
kdc_checksum = &pac_data->buffers[i].info->kdc_cksum,
|
||||
ret = make_pac_checksum(mem_ctx, &zero_blob,
|
||||
kdc_checksum,
|
||||
context, krbtgt_keyblock);
|
||||
if (ret) {
|
||||
DEBUG(2, ("making krbtgt PAC checksum failed: %s\n",
|
||||
smb_get_krb5_error_message(context, ret, mem_ctx)));
|
||||
talloc_free(pac_data);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
for (i=0; i < pac_data->num_buffers; i++) {
|
||||
if (pac_data->buffers[i].type != PAC_TYPE_SRV_CHECKSUM) {
|
||||
continue;
|
||||
}
|
||||
srv_checksum = &pac_data->buffers[i].info->srv_cksum;
|
||||
ret = make_pac_checksum(mem_ctx, &zero_blob,
|
||||
srv_checksum,
|
||||
context, service_keyblock);
|
||||
if (ret) {
|
||||
DEBUG(2, ("making service PAC checksum failed: %s\n",
|
||||
smb_get_krb5_error_message(context, ret, mem_ctx)));
|
||||
talloc_free(pac_data);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
if (!kdc_checksum) {
|
||||
DEBUG(2, ("Invalid PAC constructed for signing, no KDC checksum present!"));
|
||||
return EINVAL;
|
||||
}
|
||||
if (!srv_checksum) {
|
||||
DEBUG(2, ("Invalid PAC constructed for signing, no SRV checksum present!"));
|
||||
return EINVAL;
|
||||
}
|
||||
|
||||
/* But wipe out the actual signatures */
|
||||
memset(kdc_checksum->signature.data, '\0', kdc_checksum->signature.length);
|
||||
memset(srv_checksum->signature.data, '\0', srv_checksum->signature.length);
|
||||
|
||||
nt_status = ndr_push_struct_blob(&tmp_blob, mem_ctx, pac_data,
|
||||
(ndr_push_flags_fn_t)ndr_push_PAC_DATA);
|
||||
if (!NT_STATUS_IS_OK(nt_status)) {
|
||||
DEBUG(1, ("PAC (presig) push failed: %s\n", nt_errstr(nt_status)));
|
||||
talloc_free(pac_data);
|
||||
return EINVAL;
|
||||
}
|
||||
|
||||
/* Then sign the result of the previous push, where the sig was zero'ed out */
|
||||
ret = make_pac_checksum(mem_ctx, &tmp_blob, srv_checksum,
|
||||
context, service_keyblock);
|
||||
|
||||
/* Then sign Server checksum */
|
||||
ret = make_pac_checksum(mem_ctx, &srv_checksum->signature, kdc_checksum, context, krbtgt_keyblock);
|
||||
if (ret) {
|
||||
DEBUG(2, ("making krbtgt PAC checksum failed: %s\n",
|
||||
smb_get_krb5_error_message(context, ret, mem_ctx)));
|
||||
talloc_free(pac_data);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* And push it out again, this time to the world. This relies on determanistic pointer values */
|
||||
nt_status = ndr_push_struct_blob(&tmp_blob, mem_ctx, pac_data,
|
||||
(ndr_push_flags_fn_t)ndr_push_PAC_DATA);
|
||||
if (!NT_STATUS_IS_OK(nt_status)) {
|
||||
DEBUG(1, ("PAC (final) push failed: %s\n", nt_errstr(nt_status)));
|
||||
talloc_free(pac_data);
|
||||
return EINVAL;
|
||||
}
|
||||
|
||||
*pac = tmp_blob;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
krb5_error_code kerberos_create_pac(TALLOC_CTX *mem_ctx,
|
||||
struct auth_serversupplied_info *server_info,
|
||||
krb5_context context,
|
||||
const krb5_keyblock *krbtgt_keyblock,
|
||||
const krb5_keyblock *service_keyblock,
|
||||
krb5_principal client_principal,
|
||||
time_t tgs_authtime,
|
||||
DATA_BLOB *pac)
|
||||
{
|
||||
NTSTATUS nt_status;
|
||||
krb5_error_code ret;
|
||||
struct PAC_DATA *pac_data = talloc(mem_ctx, struct PAC_DATA);
|
||||
struct netr_SamInfo3 *sam3;
|
||||
union PAC_INFO *u_LOGON_INFO;
|
||||
struct PAC_LOGON_INFO *LOGON_INFO;
|
||||
union PAC_INFO *u_LOGON_NAME;
|
||||
struct PAC_LOGON_NAME *LOGON_NAME;
|
||||
union PAC_INFO *u_KDC_CHECKSUM;
|
||||
union PAC_INFO *u_SRV_CHECKSUM;
|
||||
|
||||
char *name;
|
||||
|
||||
enum {
|
||||
PAC_BUF_LOGON_INFO = 0,
|
||||
PAC_BUF_LOGON_NAME = 1,
|
||||
PAC_BUF_SRV_CHECKSUM = 2,
|
||||
PAC_BUF_KDC_CHECKSUM = 3,
|
||||
PAC_BUF_NUM_BUFFERS = 4
|
||||
};
|
||||
|
||||
if (!pac_data) {
|
||||
return ENOMEM;
|
||||
}
|
||||
|
||||
pac_data->num_buffers = PAC_BUF_NUM_BUFFERS;
|
||||
pac_data->version = 0;
|
||||
|
||||
pac_data->buffers = talloc_array(pac_data,
|
||||
struct PAC_BUFFER,
|
||||
pac_data->num_buffers);
|
||||
if (!pac_data->buffers) {
|
||||
talloc_free(pac_data);
|
||||
return ENOMEM;
|
||||
}
|
||||
|
||||
/* LOGON_INFO */
|
||||
u_LOGON_INFO = talloc_zero(pac_data->buffers, union PAC_INFO);
|
||||
if (!u_LOGON_INFO) {
|
||||
talloc_free(pac_data);
|
||||
return ENOMEM;
|
||||
}
|
||||
pac_data->buffers[PAC_BUF_LOGON_INFO].type = PAC_TYPE_LOGON_INFO;
|
||||
pac_data->buffers[PAC_BUF_LOGON_INFO].info = u_LOGON_INFO;
|
||||
|
||||
/* LOGON_NAME */
|
||||
u_LOGON_NAME = talloc_zero(pac_data->buffers, union PAC_INFO);
|
||||
if (!u_LOGON_NAME) {
|
||||
talloc_free(pac_data);
|
||||
return ENOMEM;
|
||||
}
|
||||
pac_data->buffers[PAC_BUF_LOGON_NAME].type = PAC_TYPE_LOGON_NAME;
|
||||
pac_data->buffers[PAC_BUF_LOGON_NAME].info = u_LOGON_NAME;
|
||||
LOGON_NAME = &u_LOGON_NAME->logon_name;
|
||||
|
||||
/* SRV_CHECKSUM */
|
||||
u_SRV_CHECKSUM = talloc_zero(pac_data->buffers, union PAC_INFO);
|
||||
if (!u_SRV_CHECKSUM) {
|
||||
talloc_free(pac_data);
|
||||
return ENOMEM;
|
||||
}
|
||||
pac_data->buffers[PAC_BUF_SRV_CHECKSUM].type = PAC_TYPE_SRV_CHECKSUM;
|
||||
pac_data->buffers[PAC_BUF_SRV_CHECKSUM].info = u_SRV_CHECKSUM;
|
||||
|
||||
/* KDC_CHECKSUM */
|
||||
u_KDC_CHECKSUM = talloc_zero(pac_data->buffers, union PAC_INFO);
|
||||
if (!u_KDC_CHECKSUM) {
|
||||
talloc_free(pac_data);
|
||||
return ENOMEM;
|
||||
}
|
||||
pac_data->buffers[PAC_BUF_KDC_CHECKSUM].type = PAC_TYPE_KDC_CHECKSUM;
|
||||
pac_data->buffers[PAC_BUF_KDC_CHECKSUM].info = u_KDC_CHECKSUM;
|
||||
|
||||
/* now the real work begins... */
|
||||
|
||||
LOGON_INFO = talloc_zero(u_LOGON_INFO, struct PAC_LOGON_INFO);
|
||||
if (!LOGON_INFO) {
|
||||
talloc_free(pac_data);
|
||||
return ENOMEM;
|
||||
}
|
||||
nt_status = auth_convert_server_info_saminfo3(LOGON_INFO, server_info, &sam3);
|
||||
if (!NT_STATUS_IS_OK(nt_status)) {
|
||||
DEBUG(1, ("Getting Samba info failed: %s\n", nt_errstr(nt_status)));
|
||||
talloc_free(pac_data);
|
||||
return EINVAL;
|
||||
}
|
||||
|
||||
u_LOGON_INFO->logon_info.info = LOGON_INFO;
|
||||
LOGON_INFO->info3 = *sam3;
|
||||
|
||||
ret = krb5_unparse_name_flags(context, client_principal,
|
||||
KRB5_PRINCIPAL_UNPARSE_NO_REALM, &name);
|
||||
if (ret) {
|
||||
return ret;
|
||||
}
|
||||
LOGON_NAME->account_name = talloc_strdup(LOGON_NAME, name);
|
||||
free(name);
|
||||
/*
|
||||
this logon_time field is absolutely critical. This is what
|
||||
caused all our PAC troubles :-)
|
||||
*/
|
||||
unix_to_nt_time(&LOGON_NAME->logon_time, tgs_authtime);
|
||||
|
||||
ret = kerberos_encode_pac(mem_ctx,
|
||||
pac_data,
|
||||
context,
|
||||
krbtgt_keyblock,
|
||||
service_keyblock,
|
||||
pac);
|
||||
talloc_free(pac_data);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,700 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
|
||||
Kerberos utility functions for GENSEC
|
||||
|
||||
Copyright (C) Andrew Bartlett <abartlet@samba.org> 2004-2005
|
||||
|
||||
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
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
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.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "system/kerberos.h"
|
||||
#include "auth/kerberos/kerberos.h"
|
||||
#include "auth/credentials/credentials.h"
|
||||
#include "auth/credentials/credentials_krb5.h"
|
||||
|
||||
struct principal_container {
|
||||
struct smb_krb5_context *smb_krb5_context;
|
||||
krb5_principal principal;
|
||||
};
|
||||
|
||||
static int free_principal(struct principal_container *pc)
|
||||
{
|
||||
/* current heimdal - 0.6.3, which we need anyway, fixes segfaults here */
|
||||
krb5_free_principal(pc->smb_krb5_context->krb5_context, pc->principal);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static krb5_error_code salt_principal_from_credentials(TALLOC_CTX *parent_ctx,
|
||||
struct cli_credentials *machine_account,
|
||||
struct smb_krb5_context *smb_krb5_context,
|
||||
krb5_principal *salt_princ)
|
||||
{
|
||||
krb5_error_code ret;
|
||||
char *machine_username;
|
||||
char *salt_body;
|
||||
char *lower_realm;
|
||||
const char *salt_principal;
|
||||
struct principal_container *mem_ctx = talloc(parent_ctx, struct principal_container);
|
||||
if (!mem_ctx) {
|
||||
return ENOMEM;
|
||||
}
|
||||
|
||||
salt_principal = cli_credentials_get_salt_principal(machine_account);
|
||||
if (salt_principal) {
|
||||
ret = krb5_parse_name(smb_krb5_context->krb5_context, salt_principal, salt_princ);
|
||||
} else {
|
||||
machine_username = talloc_strdup(mem_ctx, cli_credentials_get_username(machine_account));
|
||||
|
||||
if (!machine_username) {
|
||||
talloc_free(mem_ctx);
|
||||
return ENOMEM;
|
||||
}
|
||||
|
||||
if (machine_username[strlen(machine_username)-1] == '$') {
|
||||
machine_username[strlen(machine_username)-1] = '\0';
|
||||
}
|
||||
lower_realm = strlower_talloc(mem_ctx, cli_credentials_get_realm(machine_account));
|
||||
if (!lower_realm) {
|
||||
talloc_free(mem_ctx);
|
||||
return ENOMEM;
|
||||
}
|
||||
|
||||
salt_body = talloc_asprintf(mem_ctx, "%s.%s", machine_username,
|
||||
lower_realm);
|
||||
if (!salt_body) {
|
||||
talloc_free(mem_ctx);
|
||||
return ENOMEM;
|
||||
}
|
||||
|
||||
ret = krb5_make_principal(smb_krb5_context->krb5_context, salt_princ,
|
||||
cli_credentials_get_realm(machine_account),
|
||||
"host", salt_body, NULL);
|
||||
}
|
||||
|
||||
if (ret == 0) {
|
||||
/* This song-and-dance effectivly puts the principal
|
||||
* into talloc, so we can't loose it. */
|
||||
mem_ctx->smb_krb5_context = talloc_reference(mem_ctx, smb_krb5_context);
|
||||
mem_ctx->principal = *salt_princ;
|
||||
talloc_set_destructor(mem_ctx, free_principal);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Obtain the principal set on this context. Requires a
|
||||
* smb_krb5_context because we are doing krb5 principal parsing with
|
||||
* the library routines. The returned princ is placed in the talloc
|
||||
* system by means of a destructor (do *not* free). */
|
||||
|
||||
krb5_error_code principal_from_credentials(TALLOC_CTX *parent_ctx,
|
||||
struct cli_credentials *credentials,
|
||||
struct smb_krb5_context *smb_krb5_context,
|
||||
krb5_principal *princ)
|
||||
{
|
||||
krb5_error_code ret;
|
||||
const char *princ_string;
|
||||
struct principal_container *mem_ctx = talloc(parent_ctx, struct principal_container);
|
||||
if (!mem_ctx) {
|
||||
return ENOMEM;
|
||||
}
|
||||
|
||||
princ_string = cli_credentials_get_principal(credentials, mem_ctx);
|
||||
|
||||
/* A NULL here has meaning, as the gssapi server case will
|
||||
* then use the principal from the client */
|
||||
if (!princ_string) {
|
||||
talloc_free(mem_ctx);
|
||||
princ = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
ret = krb5_parse_name(smb_krb5_context->krb5_context,
|
||||
princ_string, princ);
|
||||
|
||||
if (ret == 0) {
|
||||
/* This song-and-dance effectivly puts the principal
|
||||
* into talloc, so we can't loose it. */
|
||||
mem_ctx->smb_krb5_context = talloc_reference(mem_ctx, smb_krb5_context);
|
||||
mem_ctx->principal = *princ;
|
||||
talloc_set_destructor(mem_ctx, free_principal);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a freshly allocated ccache (destroyed by destructor on child
|
||||
* of parent_ctx), for a given set of client credentials
|
||||
*/
|
||||
|
||||
krb5_error_code kinit_to_ccache(TALLOC_CTX *parent_ctx,
|
||||
struct cli_credentials *credentials,
|
||||
struct smb_krb5_context *smb_krb5_context,
|
||||
krb5_ccache ccache)
|
||||
{
|
||||
krb5_error_code ret;
|
||||
const char *password;
|
||||
time_t kdc_time = 0;
|
||||
krb5_principal princ;
|
||||
int tries;
|
||||
TALLOC_CTX *mem_ctx = talloc_new(parent_ctx);
|
||||
|
||||
if (!mem_ctx) {
|
||||
return ENOMEM;
|
||||
}
|
||||
|
||||
ret = principal_from_credentials(mem_ctx, credentials, smb_krb5_context, &princ);
|
||||
if (ret) {
|
||||
talloc_free(mem_ctx);
|
||||
return ret;
|
||||
}
|
||||
|
||||
password = cli_credentials_get_password(credentials);
|
||||
|
||||
tries = 2;
|
||||
while (tries--) {
|
||||
if (password) {
|
||||
ret = kerberos_kinit_password_cc(smb_krb5_context->krb5_context, ccache,
|
||||
princ,
|
||||
password, NULL, &kdc_time);
|
||||
} else {
|
||||
/* No password available, try to use a keyblock instead */
|
||||
|
||||
krb5_keyblock keyblock;
|
||||
const struct samr_Password *mach_pwd;
|
||||
mach_pwd = cli_credentials_get_nt_hash(credentials, mem_ctx);
|
||||
if (!mach_pwd) {
|
||||
talloc_free(mem_ctx);
|
||||
DEBUG(1, ("kinit_to_ccache: No password available for kinit\n"));
|
||||
return EINVAL;
|
||||
}
|
||||
ret = krb5_keyblock_init(smb_krb5_context->krb5_context,
|
||||
ETYPE_ARCFOUR_HMAC_MD5,
|
||||
mach_pwd->hash, sizeof(mach_pwd->hash),
|
||||
&keyblock);
|
||||
|
||||
if (ret == 0) {
|
||||
ret = kerberos_kinit_keyblock_cc(smb_krb5_context->krb5_context, ccache,
|
||||
princ,
|
||||
&keyblock, NULL, &kdc_time);
|
||||
krb5_free_keyblock_contents(smb_krb5_context->krb5_context, &keyblock);
|
||||
}
|
||||
}
|
||||
|
||||
if (ret == KRB5KRB_AP_ERR_SKEW || ret == KRB5_KDCREP_SKEW) {
|
||||
/* Perhaps we have been given an invalid skew, so try again without it */
|
||||
time_t t = time(NULL);
|
||||
krb5_set_real_time(smb_krb5_context->krb5_context, t, 0);
|
||||
} else {
|
||||
/* not a skew problem */
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (ret == KRB5KRB_AP_ERR_SKEW || ret == KRB5_KDCREP_SKEW) {
|
||||
DEBUG(1,("kinit for %s failed (%s)\n",
|
||||
cli_credentials_get_principal(credentials, mem_ctx),
|
||||
smb_get_krb5_error_message(smb_krb5_context->krb5_context,
|
||||
ret, mem_ctx)));
|
||||
talloc_free(mem_ctx);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* cope with ticket being in the future due to clock skew */
|
||||
if ((unsigned)kdc_time > time(NULL)) {
|
||||
time_t t = time(NULL);
|
||||
int time_offset =(unsigned)kdc_time-t;
|
||||
DEBUG(4,("Advancing clock by %d seconds to cope with clock skew\n", time_offset));
|
||||
krb5_set_real_time(smb_krb5_context->krb5_context, t + time_offset + 1, 0);
|
||||
}
|
||||
|
||||
if (ret == KRB5KDC_ERR_PREAUTH_FAILED && cli_credentials_wrong_password(credentials)) {
|
||||
ret = kinit_to_ccache(parent_ctx,
|
||||
credentials,
|
||||
smb_krb5_context,
|
||||
ccache);
|
||||
}
|
||||
if (ret) {
|
||||
DEBUG(1,("kinit for %s failed (%s)\n",
|
||||
cli_credentials_get_principal(credentials, mem_ctx),
|
||||
smb_get_krb5_error_message(smb_krb5_context->krb5_context,
|
||||
ret, mem_ctx)));
|
||||
talloc_free(mem_ctx);
|
||||
return ret;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int free_keytab(struct keytab_container *ktc)
|
||||
{
|
||||
krb5_kt_close(ktc->smb_krb5_context->krb5_context, ktc->keytab);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int smb_krb5_open_keytab(TALLOC_CTX *mem_ctx,
|
||||
struct smb_krb5_context *smb_krb5_context,
|
||||
const char *keytab_name, struct keytab_container **ktc)
|
||||
{
|
||||
krb5_keytab keytab;
|
||||
int ret;
|
||||
ret = krb5_kt_resolve(smb_krb5_context->krb5_context, keytab_name, &keytab);
|
||||
if (ret) {
|
||||
DEBUG(1,("failed to open krb5 keytab: %s\n",
|
||||
smb_get_krb5_error_message(smb_krb5_context->krb5_context,
|
||||
ret, mem_ctx)));
|
||||
return ret;
|
||||
}
|
||||
|
||||
*ktc = talloc(mem_ctx, struct keytab_container);
|
||||
if (!*ktc) {
|
||||
return ENOMEM;
|
||||
}
|
||||
|
||||
(*ktc)->smb_krb5_context = talloc_reference(*ktc, smb_krb5_context);
|
||||
(*ktc)->keytab = keytab;
|
||||
talloc_set_destructor(*ktc, free_keytab);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct enctypes_container {
|
||||
struct smb_krb5_context *smb_krb5_context;
|
||||
krb5_enctype *enctypes;
|
||||
};
|
||||
|
||||
static int free_enctypes(struct enctypes_container *etc)
|
||||
{
|
||||
free_kerberos_etypes(etc->smb_krb5_context->krb5_context, etc->enctypes);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static krb5_error_code keytab_add_keys(TALLOC_CTX *parent_ctx,
|
||||
const char *princ_string,
|
||||
krb5_principal princ,
|
||||
krb5_principal salt_princ,
|
||||
int kvno,
|
||||
const char *password_s,
|
||||
struct smb_krb5_context *smb_krb5_context,
|
||||
krb5_keytab keytab)
|
||||
{
|
||||
int i;
|
||||
krb5_error_code ret;
|
||||
krb5_enctype *enctypes;
|
||||
char *enctype_string;
|
||||
struct enctypes_container *etc;
|
||||
krb5_data password;
|
||||
TALLOC_CTX *mem_ctx = talloc_new(parent_ctx);
|
||||
if (!mem_ctx) {
|
||||
return ENOMEM;
|
||||
}
|
||||
|
||||
etc = talloc(mem_ctx, struct enctypes_container);
|
||||
if (!etc) {
|
||||
talloc_free(mem_ctx);
|
||||
return ENOMEM;
|
||||
}
|
||||
ret = get_kerberos_allowed_etypes(smb_krb5_context->krb5_context,
|
||||
&enctypes);
|
||||
if (ret != 0) {
|
||||
DEBUG(1,("keytab_add_keys: getting encrption types failed (%s)\n",
|
||||
error_message(ret)));
|
||||
talloc_free(mem_ctx);
|
||||
return ret;
|
||||
}
|
||||
|
||||
etc->smb_krb5_context = talloc_reference(etc, smb_krb5_context);
|
||||
etc->enctypes = enctypes;
|
||||
|
||||
talloc_set_destructor(etc, free_enctypes);
|
||||
|
||||
password.data = discard_const_p(char *, password_s);
|
||||
password.length = strlen(password_s);
|
||||
|
||||
for (i=0; enctypes[i]; i++) {
|
||||
krb5_keytab_entry entry;
|
||||
ret = create_kerberos_key_from_string(smb_krb5_context->krb5_context,
|
||||
salt_princ, &password, &entry.keyblock, enctypes[i]);
|
||||
if (ret != 0) {
|
||||
talloc_free(mem_ctx);
|
||||
return ret;
|
||||
}
|
||||
|
||||
entry.principal = princ;
|
||||
entry.vno = kvno;
|
||||
ret = krb5_kt_add_entry(smb_krb5_context->krb5_context, keytab, &entry);
|
||||
enctype_string = NULL;
|
||||
krb5_enctype_to_string(smb_krb5_context->krb5_context, enctypes[i], &enctype_string);
|
||||
if (ret != 0) {
|
||||
DEBUG(1, ("Failed to add %s entry for %s(kvno %d) to keytab: %s\n",
|
||||
enctype_string,
|
||||
princ_string,
|
||||
kvno,
|
||||
smb_get_krb5_error_message(smb_krb5_context->krb5_context,
|
||||
ret, mem_ctx)));
|
||||
talloc_free(mem_ctx);
|
||||
free(enctype_string);
|
||||
krb5_free_keyblock_contents(smb_krb5_context->krb5_context, &entry.keyblock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
DEBUG(5, ("Added %s(kvno %d) to keytab (%s)\n",
|
||||
princ_string, kvno,
|
||||
enctype_string));
|
||||
free(enctype_string);
|
||||
|
||||
krb5_free_keyblock_contents(smb_krb5_context->krb5_context, &entry.keyblock);
|
||||
}
|
||||
talloc_free(mem_ctx);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int create_keytab(TALLOC_CTX *parent_ctx,
|
||||
struct cli_credentials *machine_account,
|
||||
struct smb_krb5_context *smb_krb5_context,
|
||||
krb5_keytab keytab,
|
||||
BOOL add_old)
|
||||
{
|
||||
krb5_error_code ret;
|
||||
const char *password_s;
|
||||
char *enctype_string;
|
||||
const char *old_secret;
|
||||
int kvno;
|
||||
krb5_principal salt_princ;
|
||||
krb5_principal princ;
|
||||
const char *princ_string;
|
||||
|
||||
TALLOC_CTX *mem_ctx = talloc_new(parent_ctx);
|
||||
if (!mem_ctx) {
|
||||
return ENOMEM;
|
||||
}
|
||||
|
||||
princ_string = cli_credentials_get_principal(machine_account, mem_ctx);
|
||||
/* Get the principal we will store the new keytab entries under */
|
||||
ret = principal_from_credentials(mem_ctx, machine_account, smb_krb5_context, &princ);
|
||||
if (ret) {
|
||||
DEBUG(1,("create_keytab: makeing krb5 principal failed (%s)\n",
|
||||
smb_get_krb5_error_message(smb_krb5_context->krb5_context,
|
||||
ret, mem_ctx)));
|
||||
talloc_free(mem_ctx);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* The salt used to generate these entries may be different however, fetch that */
|
||||
ret = salt_principal_from_credentials(mem_ctx, machine_account,
|
||||
smb_krb5_context,
|
||||
&salt_princ);
|
||||
if (ret) {
|
||||
DEBUG(1,("create_keytab: makeing salt principal failed (%s)\n",
|
||||
smb_get_krb5_error_message(smb_krb5_context->krb5_context,
|
||||
ret, mem_ctx)));
|
||||
talloc_free(mem_ctx);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Finally, do the dance to get the password to put in the entry */
|
||||
password_s = cli_credentials_get_password(machine_account);
|
||||
if (!password_s) {
|
||||
/* If we don't have the plaintext password, try for
|
||||
* the MD4 password hash */
|
||||
|
||||
krb5_keytab_entry entry;
|
||||
const struct samr_Password *mach_pwd;
|
||||
mach_pwd = cli_credentials_get_nt_hash(machine_account, mem_ctx);
|
||||
if (!mach_pwd) {
|
||||
DEBUG(1, ("create_keytab: Domain trust informaton for account %s not available\n",
|
||||
cli_credentials_get_principal(machine_account, mem_ctx)));
|
||||
talloc_free(mem_ctx);
|
||||
return EINVAL;
|
||||
}
|
||||
ret = krb5_keyblock_init(smb_krb5_context->krb5_context,
|
||||
ETYPE_ARCFOUR_HMAC_MD5,
|
||||
mach_pwd->hash, sizeof(mach_pwd->hash),
|
||||
&entry.keyblock);
|
||||
if (ret) {
|
||||
DEBUG(1, ("create_keytab: krb5_keyblock_init failed: %s\n",
|
||||
smb_get_krb5_error_message(smb_krb5_context->krb5_context,
|
||||
ret, mem_ctx)));
|
||||
talloc_free(mem_ctx);
|
||||
return ret;
|
||||
}
|
||||
|
||||
entry.principal = princ;
|
||||
entry.vno = cli_credentials_get_kvno(machine_account);
|
||||
ret = krb5_kt_add_entry(smb_krb5_context->krb5_context, keytab, &entry);
|
||||
if (ret) {
|
||||
DEBUG(1, ("Failed to add ARCFOUR_HMAC (only) entry for %s to keytab: %s",
|
||||
cli_credentials_get_principal(machine_account, mem_ctx),
|
||||
smb_get_krb5_error_message(smb_krb5_context->krb5_context,
|
||||
ret, mem_ctx)));
|
||||
talloc_free(mem_ctx);
|
||||
krb5_free_keyblock_contents(smb_krb5_context->krb5_context, &entry.keyblock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
krb5_enctype_to_string(smb_krb5_context->krb5_context,
|
||||
ETYPE_ARCFOUR_HMAC_MD5,
|
||||
&enctype_string);
|
||||
DEBUG(5, ("Added %s(kvno %d) to keytab (%s)\n",
|
||||
cli_credentials_get_principal(machine_account, mem_ctx),
|
||||
cli_credentials_get_kvno(machine_account),
|
||||
enctype_string));
|
||||
free(enctype_string);
|
||||
|
||||
krb5_free_keyblock_contents(smb_krb5_context->krb5_context, &entry.keyblock);
|
||||
|
||||
/* Can't go any further, we only have this one key */
|
||||
talloc_free(mem_ctx);
|
||||
return 0;
|
||||
}
|
||||
|
||||
kvno = cli_credentials_get_kvno(machine_account);
|
||||
/* good, we actually have the real plaintext */
|
||||
ret = keytab_add_keys(mem_ctx, princ_string, princ, salt_princ,
|
||||
kvno, password_s, smb_krb5_context, keytab);
|
||||
if (!ret) {
|
||||
talloc_free(mem_ctx);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (!add_old || kvno == 0) {
|
||||
talloc_free(mem_ctx);
|
||||
return 0;
|
||||
}
|
||||
|
||||
old_secret = cli_credentials_get_old_password(machine_account);
|
||||
if (!old_secret) {
|
||||
talloc_free(mem_ctx);
|
||||
return 0;
|
||||
}
|
||||
|
||||
ret = keytab_add_keys(mem_ctx, princ_string, princ, salt_princ,
|
||||
kvno - 1, old_secret, smb_krb5_context, keytab);
|
||||
if (!ret) {
|
||||
talloc_free(mem_ctx);
|
||||
return ret;
|
||||
}
|
||||
|
||||
talloc_free(mem_ctx);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Walk the keytab, looking for entries of this principal name, with KVNO other than current kvno -1.
|
||||
*
|
||||
* These entries are now stale, we only keep the current, and previous entries around.
|
||||
*
|
||||
* Inspired by the code in Samba3 for 'use kerberos keytab'.
|
||||
*
|
||||
*/
|
||||
|
||||
static krb5_error_code remove_old_entries(TALLOC_CTX *parent_ctx,
|
||||
struct cli_credentials *machine_account,
|
||||
struct smb_krb5_context *smb_krb5_context,
|
||||
krb5_keytab keytab, BOOL *found_previous)
|
||||
{
|
||||
krb5_error_code ret, ret2;
|
||||
krb5_kt_cursor cursor;
|
||||
krb5_principal princ;
|
||||
int kvno;
|
||||
TALLOC_CTX *mem_ctx = talloc_new(parent_ctx);
|
||||
const char *princ_string;
|
||||
if (!mem_ctx) {
|
||||
return ENOMEM;
|
||||
}
|
||||
|
||||
*found_previous = False;
|
||||
princ_string = cli_credentials_get_principal(machine_account, mem_ctx);
|
||||
|
||||
/* Get the principal we will store the new keytab entries under */
|
||||
ret = principal_from_credentials(mem_ctx, machine_account, smb_krb5_context, &princ);
|
||||
if (ret) {
|
||||
DEBUG(1,("update_keytab: makeing krb5 principal failed (%s)\n",
|
||||
smb_get_krb5_error_message(smb_krb5_context->krb5_context,
|
||||
ret, mem_ctx)));
|
||||
talloc_free(mem_ctx);
|
||||
return ret;
|
||||
}
|
||||
|
||||
kvno = cli_credentials_get_kvno(machine_account);
|
||||
|
||||
/* for each entry in the keytab */
|
||||
ret = krb5_kt_start_seq_get(smb_krb5_context->krb5_context, keytab, &cursor);
|
||||
switch (ret) {
|
||||
case 0:
|
||||
break;
|
||||
case HEIM_ERR_OPNOTSUPP:
|
||||
case ENOENT:
|
||||
case KRB5_KT_END:
|
||||
/* no point enumerating if there isn't anything here */
|
||||
talloc_free(mem_ctx);
|
||||
return 0;
|
||||
default:
|
||||
DEBUG(1,("failed to open keytab for read of old entries: %s\n",
|
||||
smb_get_krb5_error_message(smb_krb5_context->krb5_context,
|
||||
ret, mem_ctx)));
|
||||
talloc_free(mem_ctx);
|
||||
return ret;
|
||||
}
|
||||
|
||||
while (!ret) {
|
||||
krb5_keytab_entry entry;
|
||||
ret = krb5_kt_next_entry(smb_krb5_context->krb5_context, keytab, &entry, &cursor);
|
||||
if (ret) {
|
||||
break;
|
||||
}
|
||||
/* if it matches our principal */
|
||||
if (!krb5_kt_compare(smb_krb5_context->krb5_context, &entry, princ, 0, 0)) {
|
||||
/* Free the entry, it wasn't the one we were looking for anyway */
|
||||
krb5_kt_free_entry(smb_krb5_context->krb5_context, &entry);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* delete it, if it is not kvno -1 */
|
||||
if (entry.vno != (kvno - 1 )) {
|
||||
/* Release the enumeration. We are going to
|
||||
* have to start this from the top again,
|
||||
* because deletes during enumeration may not
|
||||
* always be consistant.
|
||||
*
|
||||
* Also, the enumeration locks a FILE: keytab
|
||||
*/
|
||||
|
||||
krb5_kt_end_seq_get(smb_krb5_context->krb5_context, keytab, &cursor);
|
||||
|
||||
ret = krb5_kt_remove_entry(smb_krb5_context->krb5_context, keytab, &entry);
|
||||
krb5_kt_free_entry(smb_krb5_context->krb5_context, &entry);
|
||||
|
||||
/* Deleted: Restart from the top */
|
||||
ret2 = krb5_kt_start_seq_get(smb_krb5_context->krb5_context, keytab, &cursor);
|
||||
if (ret2) {
|
||||
krb5_kt_free_entry(smb_krb5_context->krb5_context, &entry);
|
||||
DEBUG(1,("failed to restart enumeration of keytab: %s\n",
|
||||
smb_get_krb5_error_message(smb_krb5_context->krb5_context,
|
||||
ret, mem_ctx)));
|
||||
|
||||
talloc_free(mem_ctx);
|
||||
return ret2;
|
||||
}
|
||||
|
||||
if (ret) {
|
||||
break;
|
||||
}
|
||||
|
||||
} else {
|
||||
*found_previous = True;
|
||||
}
|
||||
|
||||
/* Free the entry, we don't need it any more */
|
||||
krb5_kt_free_entry(smb_krb5_context->krb5_context, &entry);
|
||||
|
||||
|
||||
}
|
||||
krb5_kt_end_seq_get(smb_krb5_context->krb5_context, keytab, &cursor);
|
||||
|
||||
switch (ret) {
|
||||
case 0:
|
||||
break;
|
||||
case ENOENT:
|
||||
case KRB5_KT_END:
|
||||
ret = 0;
|
||||
break;
|
||||
default:
|
||||
DEBUG(1,("failed in deleting old entries for principal: %s: %s\n",
|
||||
princ_string,
|
||||
smb_get_krb5_error_message(smb_krb5_context->krb5_context,
|
||||
ret, mem_ctx)));
|
||||
}
|
||||
talloc_free(mem_ctx);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int smb_krb5_update_keytab(TALLOC_CTX *parent_ctx,
|
||||
struct cli_credentials *machine_account,
|
||||
struct smb_krb5_context *smb_krb5_context,
|
||||
struct keytab_container *keytab_container)
|
||||
{
|
||||
krb5_error_code ret;
|
||||
BOOL found_previous;
|
||||
TALLOC_CTX *mem_ctx = talloc_new(parent_ctx);
|
||||
if (!mem_ctx) {
|
||||
return ENOMEM;
|
||||
}
|
||||
|
||||
ret = remove_old_entries(mem_ctx, machine_account,
|
||||
smb_krb5_context, keytab_container->keytab, &found_previous);
|
||||
if (ret != 0) {
|
||||
talloc_free(mem_ctx);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Create a new keytab. If during the cleanout we found
|
||||
* entires for kvno -1, then don't try and duplicate them.
|
||||
* Otherwise, add kvno, and kvno -1 */
|
||||
|
||||
ret = create_keytab(mem_ctx, machine_account, smb_krb5_context,
|
||||
keytab_container->keytab,
|
||||
found_previous ? False : True);
|
||||
talloc_free(mem_ctx);
|
||||
return ret;
|
||||
}
|
||||
|
||||
_PUBLIC_ int smb_krb5_create_memory_keytab(TALLOC_CTX *parent_ctx,
|
||||
struct cli_credentials *machine_account,
|
||||
struct smb_krb5_context *smb_krb5_context,
|
||||
struct keytab_container **keytab_container)
|
||||
{
|
||||
krb5_error_code ret;
|
||||
TALLOC_CTX *mem_ctx = talloc_new(parent_ctx);
|
||||
const char *rand_string;
|
||||
const char *keytab_name;
|
||||
if (!mem_ctx) {
|
||||
return ENOMEM;
|
||||
}
|
||||
|
||||
*keytab_container = talloc(mem_ctx, struct keytab_container);
|
||||
|
||||
rand_string = generate_random_str(mem_ctx, 16);
|
||||
if (!rand_string) {
|
||||
talloc_free(mem_ctx);
|
||||
return ENOMEM;
|
||||
}
|
||||
|
||||
keytab_name = talloc_asprintf(mem_ctx, "MEMORY:%s",
|
||||
rand_string);
|
||||
if (!keytab_name) {
|
||||
talloc_free(mem_ctx);
|
||||
return ENOMEM;
|
||||
}
|
||||
|
||||
ret = smb_krb5_open_keytab(mem_ctx, smb_krb5_context, keytab_name, keytab_container);
|
||||
if (ret) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = smb_krb5_update_keytab(mem_ctx, machine_account, smb_krb5_context, *keytab_container);
|
||||
if (ret == 0) {
|
||||
talloc_steal(parent_ctx, *keytab_container);
|
||||
} else {
|
||||
*keytab_container = NULL;
|
||||
}
|
||||
talloc_free(mem_ctx);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,455 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
Wrapper for krb5_init_context
|
||||
|
||||
Copyright (C) Andrew Bartlett <abartlet@samba.org> 2005
|
||||
Copyright (C) Andrew Tridgell 2005
|
||||
Copyright (C) Stefan Metzmacher 2004
|
||||
|
||||
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
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
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.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "system/kerberos.h"
|
||||
#include "heimdal/lib/krb5/krb5_locl.h"
|
||||
#include "auth/kerberos/kerberos.h"
|
||||
#include "lib/socket/socket.h"
|
||||
#include "system/network.h"
|
||||
#include "lib/events/events.h"
|
||||
#include "roken.h"
|
||||
|
||||
/*
|
||||
context structure for operations on cldap packets
|
||||
*/
|
||||
struct smb_krb5_socket {
|
||||
struct socket_context *sock;
|
||||
|
||||
/* the fd event */
|
||||
struct fd_event *fde;
|
||||
|
||||
BOOL timeout;
|
||||
NTSTATUS status;
|
||||
DATA_BLOB request, reply, partial;
|
||||
|
||||
size_t partial_read;
|
||||
|
||||
krb5_krbhst_info *hi;
|
||||
};
|
||||
|
||||
static int smb_krb5_context_destroy_1(struct smb_krb5_context *ctx)
|
||||
{
|
||||
krb5_free_context(ctx->krb5_context);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int smb_krb5_context_destroy_2(struct smb_krb5_context *ctx)
|
||||
{
|
||||
/* Otherwise krb5_free_context will try and close what we have already free()ed */
|
||||
krb5_set_warn_dest(ctx->krb5_context, NULL);
|
||||
krb5_closelog(ctx->krb5_context, ctx->logf);
|
||||
smb_krb5_context_destroy_1(ctx);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* We never close down the DEBUG system, and no need to unreference the use */
|
||||
static void smb_krb5_debug_close(void *private) {
|
||||
return;
|
||||
}
|
||||
|
||||
static void smb_krb5_debug_wrapper(const char *timestr, const char *msg, void *private)
|
||||
{
|
||||
DEBUG(2, ("Kerberos: %s\n", msg));
|
||||
}
|
||||
|
||||
/*
|
||||
handle recv events on a smb_krb5 socket
|
||||
*/
|
||||
static void smb_krb5_socket_recv(struct smb_krb5_socket *smb_krb5)
|
||||
{
|
||||
TALLOC_CTX *tmp_ctx = talloc_new(smb_krb5);
|
||||
DATA_BLOB blob;
|
||||
size_t nread, dsize;
|
||||
|
||||
switch (smb_krb5->hi->proto) {
|
||||
case KRB5_KRBHST_UDP:
|
||||
smb_krb5->status = socket_pending(smb_krb5->sock, &dsize);
|
||||
if (!NT_STATUS_IS_OK(smb_krb5->status)) {
|
||||
talloc_free(tmp_ctx);
|
||||
return;
|
||||
}
|
||||
|
||||
blob = data_blob_talloc(tmp_ctx, NULL, dsize);
|
||||
if (blob.data == NULL && dsize != 0) {
|
||||
smb_krb5->status = NT_STATUS_NO_MEMORY;
|
||||
talloc_free(tmp_ctx);
|
||||
return;
|
||||
}
|
||||
|
||||
smb_krb5->status = socket_recv(smb_krb5->sock, blob.data, blob.length, &nread);
|
||||
if (!NT_STATUS_IS_OK(smb_krb5->status)) {
|
||||
talloc_free(tmp_ctx);
|
||||
return;
|
||||
}
|
||||
blob.length = nread;
|
||||
|
||||
if (nread == 0) {
|
||||
smb_krb5->status = NT_STATUS_UNEXPECTED_NETWORK_ERROR;
|
||||
talloc_free(tmp_ctx);
|
||||
return;
|
||||
}
|
||||
|
||||
DEBUG(2,("Received smb_krb5 packet of length %d\n",
|
||||
(int)blob.length));
|
||||
|
||||
talloc_steal(smb_krb5, blob.data);
|
||||
smb_krb5->reply = blob;
|
||||
talloc_free(tmp_ctx);
|
||||
break;
|
||||
case KRB5_KRBHST_TCP:
|
||||
if (smb_krb5->partial.length == 0) {
|
||||
smb_krb5->partial = data_blob_talloc(smb_krb5, NULL, 4);
|
||||
if (!smb_krb5->partial.data) {
|
||||
smb_krb5->status = NT_STATUS_NO_MEMORY;
|
||||
return;
|
||||
}
|
||||
|
||||
smb_krb5->partial_read = 0;
|
||||
}
|
||||
|
||||
/* read in the packet length */
|
||||
if (smb_krb5->partial_read < 4) {
|
||||
uint32_t packet_length;
|
||||
|
||||
smb_krb5->status = socket_recv(smb_krb5->sock,
|
||||
smb_krb5->partial.data + smb_krb5->partial_read,
|
||||
4 - smb_krb5->partial_read,
|
||||
&nread);
|
||||
/* todo: this should be converted to the packet_*() routines */
|
||||
if (!NT_STATUS_IS_OK(smb_krb5->status)) {
|
||||
return;
|
||||
}
|
||||
|
||||
smb_krb5->partial_read += nread;
|
||||
if (smb_krb5->partial_read != 4) {
|
||||
return;
|
||||
}
|
||||
|
||||
packet_length = RIVAL(smb_krb5->partial.data, 0);
|
||||
|
||||
smb_krb5->partial.data = talloc_realloc(smb_krb5, smb_krb5->partial.data,
|
||||
uint8_t, packet_length + 4);
|
||||
if (!smb_krb5->partial.data) {
|
||||
smb_krb5->status = NT_STATUS_NO_MEMORY;
|
||||
return;
|
||||
}
|
||||
|
||||
smb_krb5->partial.length = packet_length + 4;
|
||||
}
|
||||
|
||||
/* read in the body */
|
||||
smb_krb5->status = socket_recv(smb_krb5->sock,
|
||||
smb_krb5->partial.data + smb_krb5->partial_read,
|
||||
smb_krb5->partial.length - smb_krb5->partial_read,
|
||||
&nread);
|
||||
if (!NT_STATUS_IS_OK(smb_krb5->status)) return;
|
||||
|
||||
smb_krb5->partial_read += nread;
|
||||
|
||||
if (smb_krb5->partial_read != smb_krb5->partial.length) return;
|
||||
|
||||
smb_krb5->reply = data_blob_talloc(smb_krb5, smb_krb5->partial.data + 4, smb_krb5->partial.length - 4);
|
||||
break;
|
||||
case KRB5_KRBHST_HTTP:
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
handle request timeouts
|
||||
*/
|
||||
static void smb_krb5_request_timeout(struct event_context *event_ctx,
|
||||
struct timed_event *te, struct timeval t,
|
||||
void *private)
|
||||
{
|
||||
struct smb_krb5_socket *smb_krb5 = talloc_get_type(private, struct smb_krb5_socket);
|
||||
DEBUG(5,("Timed out smb_krb5 packet\n"));
|
||||
smb_krb5->timeout = True;
|
||||
}
|
||||
|
||||
/*
|
||||
handle send events on a smb_krb5 socket
|
||||
*/
|
||||
static void smb_krb5_socket_send(struct smb_krb5_socket *smb_krb5)
|
||||
{
|
||||
NTSTATUS status;
|
||||
|
||||
size_t len;
|
||||
|
||||
len = smb_krb5->request.length;
|
||||
status = socket_send(smb_krb5->sock, &smb_krb5->request, &len);
|
||||
|
||||
if (!NT_STATUS_IS_OK(status)) return;
|
||||
|
||||
EVENT_FD_READABLE(smb_krb5->fde);
|
||||
|
||||
EVENT_FD_NOT_WRITEABLE(smb_krb5->fde);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
handle fd events on a smb_krb5_socket
|
||||
*/
|
||||
static void smb_krb5_socket_handler(struct event_context *ev, struct fd_event *fde,
|
||||
uint16_t flags, void *private)
|
||||
{
|
||||
struct smb_krb5_socket *smb_krb5 = talloc_get_type(private, struct smb_krb5_socket);
|
||||
if (flags & EVENT_FD_WRITE) {
|
||||
smb_krb5_socket_send(smb_krb5);
|
||||
}
|
||||
if (flags & EVENT_FD_READ) {
|
||||
smb_krb5_socket_recv(smb_krb5);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
krb5_error_code smb_krb5_send_and_recv_func(krb5_context context,
|
||||
void *data,
|
||||
krb5_krbhst_info *hi,
|
||||
const krb5_data *send_buf,
|
||||
krb5_data *recv_buf)
|
||||
{
|
||||
krb5_error_code ret;
|
||||
NTSTATUS status;
|
||||
struct socket_address *remote_addr;
|
||||
const char *name;
|
||||
struct addrinfo *ai, *a;
|
||||
struct smb_krb5_socket *smb_krb5;
|
||||
|
||||
struct event_context *ev = talloc_get_type(data, struct event_context);
|
||||
|
||||
DATA_BLOB send_blob = data_blob_const(send_buf->data, send_buf->length);
|
||||
|
||||
ret = krb5_krbhst_get_addrinfo(context, hi, &ai);
|
||||
if (ret) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
for (a = ai; a; a = ai->ai_next) {
|
||||
smb_krb5 = talloc(NULL, struct smb_krb5_socket);
|
||||
if (!smb_krb5) {
|
||||
return ENOMEM;
|
||||
}
|
||||
smb_krb5->hi = hi;
|
||||
|
||||
switch (a->ai_family) {
|
||||
case PF_INET:
|
||||
name = "ipv4";
|
||||
break;
|
||||
#ifdef HAVE_SOCKET_IPV6
|
||||
case PF_INET6:
|
||||
name = "ipv6";
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
talloc_free(smb_krb5);
|
||||
return EINVAL;
|
||||
}
|
||||
|
||||
status = NT_STATUS_INVALID_PARAMETER;
|
||||
switch (hi->proto) {
|
||||
case KRB5_KRBHST_UDP:
|
||||
if (lp_parm_bool(-1, "krb5", "udp", True)) {
|
||||
status = socket_create(name, SOCKET_TYPE_DGRAM, &smb_krb5->sock, 0);
|
||||
}
|
||||
break;
|
||||
case KRB5_KRBHST_TCP:
|
||||
if (lp_parm_bool(-1, "krb5", "tcp", True)) {
|
||||
status = socket_create(name, SOCKET_TYPE_STREAM, &smb_krb5->sock, 0);
|
||||
}
|
||||
break;
|
||||
case KRB5_KRBHST_HTTP:
|
||||
talloc_free(smb_krb5);
|
||||
return EINVAL;
|
||||
}
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
talloc_free(smb_krb5);
|
||||
continue;
|
||||
}
|
||||
|
||||
talloc_steal(smb_krb5, smb_krb5->sock);
|
||||
|
||||
remote_addr = socket_address_from_sockaddr(smb_krb5, a->ai_addr, a->ai_addrlen);
|
||||
if (!remote_addr) {
|
||||
talloc_free(smb_krb5);
|
||||
continue;
|
||||
}
|
||||
|
||||
status = socket_connect_ev(smb_krb5->sock, NULL, remote_addr, 0, ev);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
talloc_free(smb_krb5);
|
||||
continue;
|
||||
}
|
||||
talloc_free(remote_addr);
|
||||
|
||||
smb_krb5->fde = event_add_fd(ev, smb_krb5,
|
||||
socket_get_fd(smb_krb5->sock), 0,
|
||||
smb_krb5_socket_handler, smb_krb5);
|
||||
|
||||
event_add_timed(ev, smb_krb5,
|
||||
timeval_current_ofs(context->kdc_timeout, 0),
|
||||
smb_krb5_request_timeout, smb_krb5);
|
||||
|
||||
EVENT_FD_WRITEABLE(smb_krb5->fde);
|
||||
|
||||
switch (hi->proto) {
|
||||
case KRB5_KRBHST_UDP:
|
||||
smb_krb5->request = send_blob;
|
||||
break;
|
||||
case KRB5_KRBHST_TCP:
|
||||
smb_krb5->request = data_blob_talloc(smb_krb5, NULL, send_blob.length + 4);
|
||||
RSIVAL(smb_krb5->request.data, 0, send_blob.length);
|
||||
memcpy(smb_krb5->request.data+4, send_blob.data, send_blob.length);
|
||||
break;
|
||||
case KRB5_KRBHST_HTTP:
|
||||
talloc_free(smb_krb5);
|
||||
return EINVAL;
|
||||
}
|
||||
smb_krb5->timeout = False;
|
||||
smb_krb5->status = NT_STATUS_OK;
|
||||
smb_krb5->reply = data_blob(NULL, 0);
|
||||
smb_krb5->partial = data_blob(NULL, 0);
|
||||
|
||||
while (!smb_krb5->timeout && (NT_STATUS_IS_OK(smb_krb5->status)) && !smb_krb5->reply.length) {
|
||||
if (event_loop_once(ev) != 0) {
|
||||
talloc_free(smb_krb5);
|
||||
return EINVAL;
|
||||
}
|
||||
}
|
||||
if (!NT_STATUS_IS_OK(smb_krb5->status)) {
|
||||
DEBUG(2,("Error reading smb_krb5 reply packet: %s\n", nt_errstr(smb_krb5->status)));
|
||||
talloc_free(smb_krb5);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (smb_krb5->timeout) {
|
||||
talloc_free(smb_krb5);
|
||||
continue;
|
||||
}
|
||||
|
||||
ret = krb5_data_copy(recv_buf, smb_krb5->reply.data, smb_krb5->reply.length);
|
||||
if (ret) {
|
||||
talloc_free(smb_krb5);
|
||||
return ret;
|
||||
}
|
||||
talloc_free(smb_krb5);
|
||||
|
||||
break;
|
||||
}
|
||||
if (a) {
|
||||
return 0;
|
||||
}
|
||||
return KRB5_KDC_UNREACH;
|
||||
}
|
||||
|
||||
krb5_error_code smb_krb5_init_context(void *parent_ctx,
|
||||
struct smb_krb5_context **smb_krb5_context)
|
||||
{
|
||||
krb5_error_code ret;
|
||||
TALLOC_CTX *tmp_ctx;
|
||||
struct event_context *ev;
|
||||
|
||||
initialize_krb5_error_table();
|
||||
|
||||
tmp_ctx = talloc_new(parent_ctx);
|
||||
*smb_krb5_context = talloc(tmp_ctx, struct smb_krb5_context);
|
||||
|
||||
if (!*smb_krb5_context || !tmp_ctx) {
|
||||
talloc_free(*smb_krb5_context);
|
||||
talloc_free(tmp_ctx);
|
||||
return ENOMEM;
|
||||
}
|
||||
|
||||
ret = krb5_init_context(&(*smb_krb5_context)->krb5_context);
|
||||
if (ret) {
|
||||
DEBUG(1,("krb5_init_context failed (%s)\n",
|
||||
error_message(ret)));
|
||||
return ret;
|
||||
}
|
||||
|
||||
talloc_set_destructor(*smb_krb5_context, smb_krb5_context_destroy_1);
|
||||
|
||||
if (lp_realm() && *lp_realm()) {
|
||||
char *upper_realm = strupper_talloc(tmp_ctx, lp_realm());
|
||||
if (!upper_realm) {
|
||||
DEBUG(1,("gensec_krb5_start: could not uppercase realm: %s\n", lp_realm()));
|
||||
talloc_free(tmp_ctx);
|
||||
return ENOMEM;
|
||||
}
|
||||
ret = krb5_set_default_realm((*smb_krb5_context)->krb5_context, upper_realm);
|
||||
if (ret) {
|
||||
DEBUG(1,("krb5_set_default_realm failed (%s)\n",
|
||||
smb_get_krb5_error_message((*smb_krb5_context)->krb5_context, ret, tmp_ctx)));
|
||||
talloc_free(tmp_ctx);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
/* TODO: Should we have a different name here? */
|
||||
ret = krb5_initlog((*smb_krb5_context)->krb5_context, "Samba", &(*smb_krb5_context)->logf);
|
||||
|
||||
if (ret) {
|
||||
DEBUG(1,("krb5_initlog failed (%s)\n",
|
||||
smb_get_krb5_error_message((*smb_krb5_context)->krb5_context, ret, tmp_ctx)));
|
||||
talloc_free(tmp_ctx);
|
||||
return ret;
|
||||
}
|
||||
|
||||
talloc_set_destructor(*smb_krb5_context, smb_krb5_context_destroy_2);
|
||||
|
||||
ret = krb5_addlog_func((*smb_krb5_context)->krb5_context, (*smb_krb5_context)->logf, 0 /* min */, -1 /* max */,
|
||||
smb_krb5_debug_wrapper, smb_krb5_debug_close, NULL);
|
||||
if (ret) {
|
||||
DEBUG(1,("krb5_addlog_func failed (%s)\n",
|
||||
smb_get_krb5_error_message((*smb_krb5_context)->krb5_context, ret, tmp_ctx)));
|
||||
talloc_free(tmp_ctx);
|
||||
return ret;
|
||||
}
|
||||
krb5_set_warn_dest((*smb_krb5_context)->krb5_context, (*smb_krb5_context)->logf);
|
||||
|
||||
ev = event_context_find(*smb_krb5_context);
|
||||
/* Set use of our socket lib */
|
||||
ret = krb5_set_send_to_kdc_func((*smb_krb5_context)->krb5_context,
|
||||
smb_krb5_send_and_recv_func,
|
||||
ev);
|
||||
if (ret) {
|
||||
DEBUG(1,("krb5_set_send_recv_func failed (%s)\n",
|
||||
smb_get_krb5_error_message((*smb_krb5_context)->krb5_context, ret, tmp_ctx)));
|
||||
talloc_free(tmp_ctx);
|
||||
return ret;
|
||||
}
|
||||
|
||||
(*smb_krb5_context)->krb5_context->mem_ctx = *smb_krb5_context;
|
||||
|
||||
talloc_steal(parent_ctx, *smb_krb5_context);
|
||||
talloc_free(tmp_ctx);
|
||||
|
||||
/* Set options in kerberos */
|
||||
|
||||
krb5_set_dns_canonicalize_hostname((*smb_krb5_context)->krb5_context, FALSE);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
simple kerberos5 routines for active directory
|
||||
Copyright (C) Andrew Bartlett 2005
|
||||
|
||||
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
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
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.
|
||||
*/
|
||||
|
||||
struct smb_krb5_context {
|
||||
struct krb5_context_data *krb5_context;
|
||||
krb5_log_facility *logf;
|
||||
};
|
||||
|
||||
krb5_error_code smb_krb5_init_context(void *parent_ctx,
|
||||
struct smb_krb5_context **smb_krb5_context);
|
||||
void smb_krb5_free_context(struct smb_krb5_context *smb_krb5_context);
|
||||
|
||||
krb5_error_code smb_krb5_send_and_recv_func(krb5_context context,
|
||||
void *data,
|
||||
krb5_krbhst_info *hi,
|
||||
const krb5_data *send_buf,
|
||||
krb5_data *recv_buf);
|
||||
Reference in New Issue
Block a user