X-Git-Url: http://git.samba.org/samba.git/?a=blobdiff_plain;f=source3%2Flibads%2Fkerberos.c;h=d5d8e2afc034f946589226466e584a87257a7789;hb=a616df1848d65bcbfec745823e312baf230887ee;hp=b99525047fa8c80e164f9589cbddb58498e4a9cf;hpb=70426bdd307be2bbaa2ec6f111440bae69216933;p=kai%2Fsamba-autobuild%2F.git
diff --git a/source3/libads/kerberos.c b/source3/libads/kerberos.c
index b99525047fa..d5d8e2afc03 100644
--- a/source3/libads/kerberos.c
+++ b/source3/libads/kerberos.c
@@ -11,17 +11,24 @@
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
-
+
This program is distributed in the hope that it will be useful,
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, see .
*/
#include "includes.h"
+#include "system/filesys.h"
+#include "smb_krb5.h"
+#include "../librpc/gen_ndr/ndr_misc.h"
+#include "libads/kerberos_proto.h"
+#include "libads/cldap.h"
+#include "secrets.h"
+#include "../lib/tsocket/tsocket.h"
#ifdef HAVE_KRB5
@@ -46,9 +53,9 @@ kerb_prompter(krb5_context ctx, void *data,
memset(prompts[0].reply->data, '\0', prompts[0].reply->length);
if (prompts[0].reply->length > 0) {
if (data) {
- strncpy(prompts[0].reply->data, (const char *)data,
+ strncpy((char *)prompts[0].reply->data, (const char *)data,
prompts[0].reply->length-1);
- prompts[0].reply->length = strlen(prompts[0].reply->data);
+ prompts[0].reply->length = strlen((const char *)prompts[0].reply->data);
} else {
prompts[0].reply->length = 0;
}
@@ -56,47 +63,14 @@ kerb_prompter(krb5_context ctx, void *data,
return 0;
}
-static bool smb_krb5_err_io_nstatus(TALLOC_CTX *mem_ctx,
- DATA_BLOB *edata_blob,
- KRB5_EDATA_NTSTATUS *edata)
-{
- bool ret = False;
- prs_struct ps;
-
- if (!mem_ctx || !edata_blob || !edata)
- return False;
-
- if (!prs_init(&ps, edata_blob->length, mem_ctx, UNMARSHALL))
- return False;
-
- if (!prs_copy_data_in(&ps, (char *)edata_blob->data, edata_blob->length))
- goto out;
-
- prs_set_offset(&ps, 0);
-
- if (!prs_ntstatus("ntstatus", &ps, 1, &edata->ntstatus))
- goto out;
-
- if (!prs_uint32("unknown1", &ps, 1, &edata->unknown1))
- goto out;
-
- if (!prs_uint32("unknown2", &ps, 1, &edata->unknown2)) /* only seen 00000001 here */
- goto out;
-
- ret = True;
- out:
- prs_mem_free(&ps);
-
- return ret;
-}
-
static bool smb_krb5_get_ntstatus_from_krb5_error(krb5_error *error,
NTSTATUS *nt_status)
{
DATA_BLOB edata;
DATA_BLOB unwrapped_edata;
TALLOC_CTX *mem_ctx;
- KRB5_EDATA_NTSTATUS parsed_edata;
+ struct KRB5_EDATA_NTSTATUS parsed_edata;
+ enum ndr_err_code ndr_err;
#ifdef HAVE_E_DATA_POINTER_IN_KRB5_ERROR
edata = data_blob(error->e_data->data, error->e_data->length);
@@ -122,7 +96,9 @@ static bool smb_krb5_err_io_nstatus(TALLOC_CTX *mem_ctx,
data_blob_free(&edata);
- if (!smb_krb5_err_io_nstatus(mem_ctx, &unwrapped_edata, &parsed_edata)) {
+ ndr_err = ndr_pull_struct_blob_all(&unwrapped_edata, mem_ctx,
+ &parsed_edata, (ndr_pull_flags_fn_t)ndr_pull_KRB5_EDATA_NTSTATUS);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
data_blob_free(&unwrapped_edata);
TALLOC_FREE(mem_ctx);
return False;
@@ -219,7 +195,7 @@ int kerberos_kinit_password_ext(const char *principal,
if ((code = krb5_cc_resolve(ctx, cache_name ? cache_name : krb5_cc_default_name(ctx), &cc))) {
goto out;
}
-
+
if ((code = smb_krb5_parse_name(ctx, principal, &me))) {
goto out;
}
@@ -243,14 +219,15 @@ int kerberos_kinit_password_ext(const char *principal,
}
#endif
if (add_netbios_addr) {
- if ((code = smb_krb5_gen_netbios_krb5_address(&addr))) {
+ if ((code = smb_krb5_gen_netbios_krb5_address(&addr,
+ lp_netbios_name()))) {
goto out;
}
krb5_get_init_creds_opt_set_address_list(opt, addr->addrs);
}
- if ((code = krb5_get_init_creds_password(ctx, &my_creds, me, CONST_DISCARD(char *,password),
- kerb_prompter, CONST_DISCARD(char *,password),
+ if ((code = krb5_get_init_creds_password(ctx, &my_creds, me, discard_const_p(char,password),
+ kerb_prompter, discard_const_p(char, password),
0, NULL, opt))) {
goto out;
}
@@ -258,7 +235,7 @@ int kerberos_kinit_password_ext(const char *principal,
if ((code = krb5_cc_initialize(ctx, cc, me))) {
goto out;
}
-
+
if ((code = krb5_cc_store_cred(ctx, cc, &my_creds))) {
goto out;
}
@@ -313,52 +290,6 @@ int kerberos_kinit_password_ext(const char *principal,
return code;
}
-
-
-/* run kinit to setup our ccache */
-int ads_kinit_password(ADS_STRUCT *ads)
-{
- char *s;
- int ret;
- const char *account_name;
- fstring acct_name;
-
- if ( IS_DC ) {
- /* this will end up getting a ticket for DOMAIN@RUSTED.REA.LM */
- account_name = lp_workgroup();
- } else {
- /* always use the sAMAccountName for security = domain */
- /* global_myname()$@REA.LM */
- if ( lp_security() == SEC_DOMAIN ) {
- fstr_sprintf( acct_name, "%s$", global_myname() );
- account_name = acct_name;
- }
- else
- /* This looks like host/global_myname()@REA.LM */
- account_name = ads->auth.user_name;
- }
-
- if (asprintf(&s, "%s@%s", account_name, ads->auth.realm) == -1) {
- return KRB5_CC_NOMEM;
- }
-
- if (!ads->auth.password) {
- SAFE_FREE(s);
- return KRB5_LIBOS_CANTREADPWD;
- }
-
- ret = kerberos_kinit_password_ext(s, ads->auth.password, ads->auth.time_offset,
- &ads->auth.tgt_expire, NULL, NULL, False, False, ads->auth.renewable,
- NULL);
-
- if (ret) {
- DEBUG(0,("kerberos_kinit_password %s failed: %s\n",
- s, error_message(ret)));
- }
- SAFE_FREE(s);
- return ret;
-}
-
int ads_kdestroy(const char *cc_name)
{
krb5_error_code code;
@@ -371,7 +302,7 @@ int ads_kdestroy(const char *cc_name)
error_message(code)));
return code;
}
-
+
if (!cc_name) {
if ((code = krb5_cc_default(ctx, &cc))) {
krb5_free_context(ctx);
@@ -407,8 +338,8 @@ static char *kerberos_secrets_fetch_salting_principal(const char *service, int e
char *key = NULL;
char *ret = NULL;
- asprintf(&key, "%s/%s/enctype=%d", SECRETS_SALTING_PRINCIPAL, service, enctype);
- if (!key) {
+ if (asprintf(&key, "%s/%s/enctype=%d",
+ SECRETS_SALTING_PRINCIPAL, service, enctype) == -1) {
return NULL;
}
ret = (char *)secrets_fetch(key, NULL);
@@ -424,8 +355,8 @@ char* kerberos_standard_des_salt( void )
{
fstring salt;
- fstr_sprintf( salt, "host/%s.%s@", global_myname(), lp_realm() );
- strlower_m( salt );
+ fstr_sprintf( salt, "host/%s.%s@", lp_netbios_name(), lp_realm() );
+ (void)strlower_m( salt );
fstrcat( salt, lp_realm() );
return SMB_STRDUP( salt );
@@ -438,7 +369,10 @@ static char* des_salt_key( void )
{
char *key;
- asprintf(&key, "%s/DES/%s", SECRETS_SALTING_PRINCIPAL, lp_realm());
+ if (asprintf(&key, "%s/DES/%s", SECRETS_SALTING_PRINCIPAL,
+ lp_realm()) == -1) {
+ return NULL;
+ }
return key;
}
@@ -474,13 +408,14 @@ bool kerberos_secrets_store_des_salt( const char* salt )
/************************************************************************
************************************************************************/
+static
char* kerberos_secrets_fetch_des_salt( void )
{
char *salt, *key;
if ( (key = des_salt_key()) == NULL ) {
DEBUG(0,("kerberos_secrets_fetch_des_salt: failed to generate key!\n"));
- return False;
+ return NULL;
}
salt = (char*)secrets_fetch( key, NULL );
@@ -490,62 +425,6 @@ char* kerberos_secrets_fetch_des_salt( void )
return salt;
}
-/************************************************************************
- Routine to get the default realm from the kerberos credentials cache.
- Caller must free if the return value is not NULL.
-************************************************************************/
-
-char *kerberos_get_default_realm_from_ccache( void )
-{
- char *realm = NULL;
- krb5_context ctx = NULL;
- krb5_ccache cc = NULL;
- krb5_principal princ = NULL;
-
- initialize_krb5_error_table();
- if (krb5_init_context(&ctx)) {
- return NULL;
- }
-
- DEBUG(5,("kerberos_get_default_realm_from_ccache: "
- "Trying to read krb5 cache: %s\n",
- krb5_cc_default_name(ctx)));
- if (krb5_cc_default(ctx, &cc)) {
- DEBUG(0,("kerberos_get_default_realm_from_ccache: "
- "failed to read default cache\n"));
- goto out;
- }
- if (krb5_cc_get_principal(ctx, cc, &princ)) {
- DEBUG(0,("kerberos_get_default_realm_from_ccache: "
- "failed to get default principal\n"));
- goto out;
- }
-
-#if defined(HAVE_KRB5_PRINCIPAL_GET_REALM)
- realm = SMB_STRDUP(krb5_principal_get_realm(ctx, princ));
-#elif defined(HAVE_KRB5_PRINC_REALM)
- {
- krb5_data *realm_data = krb5_princ_realm(ctx, princ);
- realm = SMB_STRNDUP(realm_data->data, realm_data->length);
- }
-#endif
-
- out:
-
- if (princ) {
- krb5_free_principal(ctx, princ);
- }
- if (cc) {
- krb5_cc_close(ctx, cc);
- }
- if (ctx) {
- krb5_free_context(ctx);
- }
-
- return realm;
-}
-
-
/************************************************************************
Routine to get the salting principal for this service. This is
maintained for backwards compatibilty with releases prior to 3.0.24.
@@ -553,20 +432,21 @@ char *kerberos_get_default_realm_from_ccache( void )
to look for the older tdb keys. Caller must free if return is not null.
************************************************************************/
+static
krb5_principal kerberos_fetch_salt_princ_for_host_princ(krb5_context context,
krb5_principal host_princ,
int enctype)
{
char *unparsed_name = NULL, *salt_princ_s = NULL;
krb5_principal ret_princ = NULL;
-
+
/* lookup new key first */
if ( (salt_princ_s = kerberos_secrets_fetch_des_salt()) == NULL ) {
-
+
/* look under the old key. If this fails, just use the standard key */
- if (smb_krb5_unparse_name(context, host_princ, &unparsed_name) != 0) {
+ if (smb_krb5_unparse_name(talloc_tos(), context, host_princ, &unparsed_name) != 0) {
return (krb5_principal)NULL;
}
if ((salt_princ_s = kerberos_secrets_fetch_salting_principal(unparsed_name, enctype)) == NULL) {
@@ -578,13 +458,50 @@ krb5_principal kerberos_fetch_salt_princ_for_host_princ(krb5_context context,
if (smb_krb5_parse_name(context, salt_princ_s, &ret_princ) != 0) {
ret_princ = NULL;
}
-
- SAFE_FREE(unparsed_name);
+
+ TALLOC_FREE(unparsed_name);
SAFE_FREE(salt_princ_s);
-
+
return ret_princ;
}
+int create_kerberos_key_from_string(krb5_context context,
+ krb5_principal host_princ,
+ krb5_data *password,
+ krb5_keyblock *key,
+ krb5_enctype enctype,
+ bool no_salt)
+{
+ krb5_principal salt_princ = NULL;
+ int ret;
+ /*
+ * Check if we've determined that the KDC is salting keys for this
+ * principal/enctype in a non-obvious way. If it is, try to match
+ * its behavior.
+ */
+ if (no_salt) {
+ KRB5_KEY_DATA(key) = (KRB5_KEY_DATA_CAST *)SMB_MALLOC(password->length);
+ if (!KRB5_KEY_DATA(key)) {
+ return ENOMEM;
+ }
+ memcpy(KRB5_KEY_DATA(key), password->data, password->length);
+ KRB5_KEY_LENGTH(key) = password->length;
+ KRB5_KEY_TYPE(key) = enctype;
+ return 0;
+ }
+ salt_princ = kerberos_fetch_salt_princ_for_host_princ(context, host_princ, enctype);
+ ret = smb_krb5_create_key_from_string(context,
+ salt_princ ? salt_princ : host_princ,
+ NULL,
+ password,
+ enctype,
+ key);
+ if (salt_princ) {
+ krb5_free_principal(context, salt_princ);
+ }
+ return ret;
+}
+
/************************************************************************
Routine to set the salting principal for this service. Active
Directory may use a non-obvious principal name to generate the salt
@@ -603,27 +520,33 @@ bool kerberos_secrets_store_salting_principal(const char *service,
krb5_principal princ = NULL;
char *princ_s = NULL;
char *unparsed_name = NULL;
+ krb5_error_code code;
- krb5_init_context(&context);
- if (!context) {
+ if (((code = krb5_init_context(&context)) != 0) || (context == NULL)) {
+ DEBUG(5, ("kerberos_secrets_store_salting_pricipal: kdb5_init_context failed: %s\n",
+ error_message(code)));
return False;
}
if (strchr_m(service, '@')) {
- asprintf(&princ_s, "%s", service);
+ if (asprintf(&princ_s, "%s", service) == -1) {
+ goto out;
+ }
} else {
- asprintf(&princ_s, "%s@%s", service, lp_realm());
+ if (asprintf(&princ_s, "%s@%s", service, lp_realm()) == -1) {
+ goto out;
+ }
}
if (smb_krb5_parse_name(context, princ_s, &princ) != 0) {
goto out;
-
}
- if (smb_krb5_unparse_name(context, princ, &unparsed_name) != 0) {
+ if (smb_krb5_unparse_name(talloc_tos(), context, princ, &unparsed_name) != 0) {
goto out;
}
- asprintf(&key, "%s/%s/enctype=%d", SECRETS_SALTING_PRINCIPAL, unparsed_name, enctype);
- if (!key) {
+ if (asprintf(&key, "%s/%s/enctype=%d",
+ SECRETS_SALTING_PRINCIPAL, unparsed_name, enctype)
+ == -1) {
goto out;
}
@@ -637,7 +560,11 @@ bool kerberos_secrets_store_salting_principal(const char *service,
SAFE_FREE(key);
SAFE_FREE(princ_s);
- SAFE_FREE(unparsed_name);
+ TALLOC_FREE(unparsed_name);
+
+ if (princ) {
+ krb5_free_principal(context, princ);
+ }
if (context) {
krb5_free_context(context);
@@ -670,142 +597,182 @@ int kerberos_kinit_password(const char *principal,
/************************************************************************
************************************************************************/
-static char *print_kdc_line(char *mem_ctx,
- const char *prev_line,
- const struct sockaddr_storage *pss)
+/************************************************************************
+ Create a string list of available kdc's, possibly searching by sitename.
+ Does DNS queries.
+
+ If "sitename" is given, the DC's in that site are listed first.
+
+************************************************************************/
+
+static void add_sockaddr_unique(struct sockaddr_storage *addrs, int *num_addrs,
+ const struct sockaddr_storage *addr)
{
- char *kdc_str = NULL;
+ int i;
- if (pss->ss_family == AF_INET) {
- kdc_str = talloc_asprintf(mem_ctx, "%s\tkdc = %s\n",
- prev_line,
- print_canonical_sockaddr(mem_ctx, pss));
- } else {
- char addr[INET6_ADDRSTRLEN];
- uint16_t port = get_sockaddr_port(pss);
-
- if (port != 0 && port != DEFAULT_KRB5_PORT) {
- /* Currently for IPv6 we can't specify a non-default
- krb5 port with an address, as this requires a ':'.
- Resolve to a name. */
- char hostname[MAX_DNS_NAME_LENGTH];
- int ret = sys_getnameinfo((const struct sockaddr *)pss,
- sizeof(*pss),
- hostname, sizeof(hostname),
- NULL, 0,
- NI_NAMEREQD);
- if (ret) {
- DEBUG(0,("print_kdc_line: can't resolve name "
- "for kdc with non-default port %s. "
- "Error %s\n.",
- print_canonical_sockaddr(mem_ctx, pss),
- gai_strerror(ret)));
- }
- /* Success, use host:port */
- kdc_str = talloc_asprintf(mem_ctx,
- "%s\tkdc = %s:%u\n",
- prev_line,
- hostname,
- (unsigned int)port);
- } else {
- kdc_str = talloc_asprintf(mem_ctx, "%s\tkdc = %s\n",
- prev_line,
- print_sockaddr(addr,
- sizeof(addr),
- pss));
+ for (i=0; i<*num_addrs; i++) {
+ if (sockaddr_equal((const struct sockaddr *)&addrs[i],
+ (const struct sockaddr *)addr)) {
+ return;
}
}
- return kdc_str;
+ addrs[i] = *addr;
+ *num_addrs += 1;
}
-/************************************************************************
- Create a string list of available kdc's, possibly searching by sitename.
- Does DNS queries.
-************************************************************************/
+/* print_canonical_sockaddr prints an ipv6 addr in the form of
+* [ipv6.addr]. This string, when put in a generated krb5.conf file is not
+* always properly dealt with by some older krb5 libraries. Adding the hard-coded
+* portnumber workarounds the issue. - gd */
+
+static char *print_canonical_sockaddr_with_port(TALLOC_CTX *mem_ctx,
+ const struct sockaddr_storage *pss)
+{
+ char *str = NULL;
+
+ str = print_canonical_sockaddr(mem_ctx, pss);
+ if (str == NULL) {
+ return NULL;
+ }
+
+ if (pss->ss_family != AF_INET6) {
+ return str;
+ }
+
+#if defined(HAVE_IPV6)
+ str = talloc_asprintf_append(str, ":88");
+#endif
+ return str;
+}
static char *get_kdc_ip_string(char *mem_ctx,
const char *realm,
const char *sitename,
- struct sockaddr_storage *pss)
+ const struct sockaddr_storage *pss)
{
+ TALLOC_CTX *frame = talloc_stackframe();
int i;
struct ip_service *ip_srv_site = NULL;
struct ip_service *ip_srv_nonsite = NULL;
int count_site = 0;
int count_nonsite;
- char *kdc_str = print_kdc_line(mem_ctx, "", pss);
+ int num_dcs;
+ struct sockaddr_storage *dc_addrs;
+ struct tsocket_address **dc_addrs2 = NULL;
+ const struct tsocket_address * const *dc_addrs3 = NULL;
+ char *result = NULL;
+ struct netlogon_samlogon_response **responses = NULL;
+ NTSTATUS status;
+ char *kdc_str = talloc_asprintf(mem_ctx, "%s\tkdc = %s\n", "",
+ print_canonical_sockaddr_with_port(mem_ctx, pss));
if (kdc_str == NULL) {
+ TALLOC_FREE(frame);
return NULL;
}
- /* Get the KDC's only in this site. */
+ /*
+ * First get the KDC's only in this site, the rest will be
+ * appended later
+ */
if (sitename) {
-
get_kdc_list(realm, sitename, &ip_srv_site, &count_site);
-
- for (i = 0; i < count_site; i++) {
- if (addr_equal(&ip_srv_site[i].ss, pss)) {
- continue;
- }
- /* Append to the string - inefficient
- * but not done often. */
- kdc_str = print_kdc_line(mem_ctx,
- kdc_str,
- &ip_srv_site[i].ss);
- if (!kdc_str) {
- SAFE_FREE(ip_srv_site);
- return NULL;
- }
- }
}
/* Get all KDC's. */
get_kdc_list(realm, NULL, &ip_srv_nonsite, &count_nonsite);
- for (i = 0; i < count_nonsite; i++) {
- int j;
+ dc_addrs = talloc_array(talloc_tos(), struct sockaddr_storage,
+ 1 + count_site + count_nonsite);
+ if (dc_addrs == NULL) {
+ goto fail;
+ }
- if (addr_equal(&ip_srv_nonsite[i].ss, pss)) {
- continue;
- }
+ dc_addrs[0] = *pss;
+ num_dcs = 1;
- /* Ensure this isn't an IP already seen (YUK! this is n*n....) */
- for (j = 0; j < count_site; j++) {
- if (addr_equal(&ip_srv_nonsite[i].ss,
- &ip_srv_site[j].ss)) {
- break;
- }
- /* As the lists are sorted we can break early if nonsite > site. */
- if (ip_service_compare(&ip_srv_nonsite[i], &ip_srv_site[j]) > 0) {
- break;
- }
+ for (i=0; i