*/
#include "includes.h"
+#include "smb_krb5.h"
#ifdef HAVE_KRB5
+#define DEFAULT_KRB5_PORT 88
+
#define LIBADS_CCACHE_NAME "MEMORY:libads"
/*
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;
}
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);
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;
const char *account_name;
fstring acct_name;
+ if (ads->auth.flags & ADS_AUTH_USER_CREDS) {
+ account_name = ads->auth.user_name;
+ goto got_accountname;
+ }
+
if ( IS_DC ) {
/* this will end up getting a ticket for DOMAIN@RUSTED.REA.LM */
account_name = lp_workgroup();
account_name = ads->auth.user_name;
}
+ got_accountname:
if (asprintf(&s, "%s@%s", account_name, ads->auth.realm) == -1) {
return KRB5_CC_NOMEM;
}
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);
{
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;
}
out:
- if (princ) {
- krb5_free_principal(ctx, princ);
- }
- if (cc) {
- krb5_cc_close(ctx, cc);
- }
if (ctx) {
+ if (princ) {
+ krb5_free_principal(ctx, princ);
+ }
+ if (cc) {
+ krb5_cc_close(ctx, cc);
+ }
krb5_free_context(ctx);
}
return realm;
}
+/************************************************************************
+ Routine to get the realm from a given DNS name. Returns malloc'ed memory.
+ Caller must free() if the return value is not NULL.
+************************************************************************/
+
+char *kerberos_get_realm_from_hostname(const char *hostname)
+{
+#if defined(HAVE_KRB5_GET_HOST_REALM) && defined(HAVE_KRB5_FREE_HOST_REALM)
+#if defined(HAVE_KRB5_REALM_TYPE)
+ /* Heimdal. */
+ krb5_realm *realm_list = NULL;
+#else
+ /* MIT */
+ char **realm_list = NULL;
+#endif
+ char *realm = NULL;
+ krb5_error_code kerr;
+ krb5_context ctx = NULL;
+
+ initialize_krb5_error_table();
+ if (krb5_init_context(&ctx)) {
+ return NULL;
+ }
+
+ kerr = krb5_get_host_realm(ctx, hostname, &realm_list);
+ if (kerr != 0) {
+ DEBUG(3,("kerberos_get_realm_from_hostname %s: "
+ "failed %s\n",
+ hostname ? hostname : "(NULL)",
+ error_message(kerr) ));
+ goto out;
+ }
+
+ if (realm_list && realm_list[0]) {
+ realm = SMB_STRDUP(realm_list[0]);
+ }
+
+ out:
+
+ if (ctx) {
+ if (realm_list) {
+ krb5_free_host_realm(ctx, realm_list);
+ realm_list = NULL;
+ }
+ krb5_free_context(ctx);
+ ctx = NULL;
+ }
+ return realm;
+#else
+ return NULL;
+#endif
+}
/************************************************************************
Routine to get the salting principal for this service. This is
/* 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) {
ret_princ = NULL;
}
- SAFE_FREE(unparsed_name);
+ TALLOC_FREE(unparsed_name);
SAFE_FREE(salt_princ_s);
return ret_princ;
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;
}
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);
NULL);
}
+/************************************************************************
+************************************************************************/
+
+static char *print_kdc_line(char *mem_ctx,
+ const char *prev_line,
+ const struct sockaddr_storage *pss,
+ const char *kdc_name)
+{
+ char *kdc_str = NULL;
+
+ 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);
+
+ DEBUG(10,("print_kdc_line: IPv6 case for kdc_name: %s, port: %d\n",
+ kdc_name, port));
+
+ 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)));
+ return NULL;
+ }
+ /* Success, use host:port */
+ kdc_str = talloc_asprintf(mem_ctx,
+ "%s\tkdc = %s:%u\n",
+ prev_line,
+ hostname,
+ (unsigned int)port);
+ } else {
+
+ /* no krb5 lib currently supports "kdc = ipv6 address"
+ * at all, so just fill in just the kdc_name if we have
+ * it and let the krb5 lib figure out the appropriate
+ * ipv6 address - gd */
+
+ if (kdc_name) {
+ kdc_str = talloc_asprintf(mem_ctx, "%s\tkdc = %s\n",
+ prev_line, kdc_name);
+ } else {
+ kdc_str = talloc_asprintf(mem_ctx, "%s\tkdc = %s\n",
+ prev_line,
+ print_sockaddr(addr,
+ sizeof(addr),
+ pss));
+ }
+ }
+ }
+ return kdc_str;
+}
+
/************************************************************************
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 char *get_kdc_ip_string(char *mem_ctx,
const char *realm,
const char *sitename,
- struct sockaddr_storage *pss)
+ struct sockaddr_storage *pss,
+ const char *kdc_name)
{
int i;
struct ip_service *ip_srv_site = NULL;
- struct ip_service *ip_srv_nonsite;
+ struct ip_service *ip_srv_nonsite = NULL;
int count_site = 0;
int count_nonsite;
- char *kdc_str = talloc_asprintf(mem_ctx, "\tkdc = %s\n",
- print_canonical_sockaddr(mem_ctx,
- pss));
+ char *kdc_str = print_kdc_line(mem_ctx, "", pss, kdc_name);
if (kdc_str == NULL) {
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)) {
+ if (sockaddr_equal((struct sockaddr *)&ip_srv_site[i].ss,
+ (struct sockaddr *)pss)) {
continue;
}
/* Append to the string - inefficient
* but not done often. */
- kdc_str = talloc_asprintf(mem_ctx, "%s\tkdc = %s\n",
- kdc_str,
- print_canonical_sockaddr(mem_ctx,
- &ip_srv_site[i].ss));
+ kdc_str = print_kdc_line(mem_ctx,
+ kdc_str,
+ &ip_srv_site[i].ss,
+ NULL);
if (!kdc_str) {
SAFE_FREE(ip_srv_site);
return NULL;
for (i = 0; i < count_nonsite; i++) {
int j;
- if (addr_equal(&ip_srv_nonsite[i].ss, pss)) {
+ if (sockaddr_equal((struct sockaddr *)&ip_srv_nonsite[i].ss, (struct sockaddr *)pss)) {
continue;
}
/* 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)) {
+ if (sockaddr_equal((struct sockaddr *)&ip_srv_nonsite[i].ss,
+ (struct sockaddr *)&ip_srv_site[j].ss)) {
break;
}
/* As the lists are sorted we can break early if nonsite > site. */
}
/* Append to the string - inefficient but not done often. */
- kdc_str = talloc_asprintf(mem_ctx, "%s\tkdc = %s\n",
+ kdc_str = print_kdc_line(mem_ctx,
kdc_str,
- print_canonical_sockaddr(mem_ctx,
- &ip_srv_nonsite[i].ss));
+ &ip_srv_nonsite[i].ss,
+ NULL);
if (!kdc_str) {
SAFE_FREE(ip_srv_site);
SAFE_FREE(ip_srv_nonsite);
bool create_local_private_krb5_conf_for_domain(const char *realm,
const char *domain,
const char *sitename,
- struct sockaddr_storage *pss)
+ struct sockaddr_storage *pss,
+ const char *kdc_name)
{
- char *dname = talloc_asprintf(NULL, "%s/smb_krb5", lp_lockdir());
+ char *dname;
char *tmpname = NULL;
char *fname = NULL;
char *file_contents = NULL;
ssize_t ret;
int fd;
char *realm_upper = NULL;
+ bool result = false;
+
+ if (!lp_create_krb5_conf()) {
+ return false;
+ }
+ dname = lock_path("smb_krb5");
if (!dname) {
- return False;
+ return false;
}
if ((mkdir(dname, 0755)==-1) && (errno != EEXIST)) {
DEBUG(0,("create_local_private_krb5_conf_for_domain: "
"failed to create directory %s. Error was %s\n",
dname, strerror(errno) ));
- TALLOC_FREE(dname);
- return False;
+ goto done;
}
- tmpname = talloc_asprintf(dname, "%s/smb_tmp_krb5.XXXXXX", lp_lockdir());
+ tmpname = lock_path("smb_tmp_krb5.XXXXXX");
if (!tmpname) {
- TALLOC_FREE(dname);
- return False;
+ goto done;
}
fname = talloc_asprintf(dname, "%s/krb5.conf.%s", dname, domain);
if (!fname) {
- TALLOC_FREE(dname);
- return False;
+ goto done;
}
DEBUG(10,("create_local_private_krb5_conf_for_domain: fname = %s, realm = %s, domain = %s\n",
realm_upper = talloc_strdup(fname, realm);
strupper_m(realm_upper);
- kdc_ip_string = get_kdc_ip_string(dname, realm, sitename, pss);
+ kdc_ip_string = get_kdc_ip_string(dname, realm, sitename, pss, kdc_name);
if (!kdc_ip_string) {
- TALLOC_FREE(dname);
- return False;
+ goto done;
}
- file_contents = talloc_asprintf(fname, "[libdefaults]\n\tdefault_realm = %s\n\n"
- "[realms]\n\t%s = {\n"
- "\t%s\t}\n",
- realm_upper, realm_upper, kdc_ip_string);
+ file_contents = talloc_asprintf(fname,
+ "[libdefaults]\n\tdefault_realm = %s\n"
+ "\tdefault_tgs_enctypes = RC4-HMAC DES-CBC-CRC DES-CBC-MD5\n"
+ "\tdefault_tkt_enctypes = RC4-HMAC DES-CBC-CRC DES-CBC-MD5\n"
+ "\tpreferred_enctypes = RC4-HMAC DES-CBC-CRC DES-CBC-MD5\n\n"
+ "[realms]\n\t%s = {\n"
+ "\t%s\t}\n",
+ realm_upper, realm_upper, kdc_ip_string);
if (!file_contents) {
- TALLOC_FREE(dname);
- return False;
+ goto done;
}
flen = strlen(file_contents);
- fd = smb_mkstemp(tmpname);
+ fd = mkstemp(tmpname);
if (fd == -1) {
DEBUG(0,("create_local_private_krb5_conf_for_domain: smb_mkstemp failed,"
" for file %s. Errno %s\n",
tmpname, strerror(errno) ));
+ goto done;
}
if (fchmod(fd, 0644)==-1) {
tmpname, strerror(errno) ));
unlink(tmpname);
close(fd);
- TALLOC_FREE(dname);
- return False;
+ goto done;
}
ret = write(fd, file_contents, flen);
(int)ret, (unsigned int)flen, strerror(errno) ));
unlink(tmpname);
close(fd);
- TALLOC_FREE(dname);
- return False;
+ goto done;
}
if (close(fd)==-1) {
DEBUG(0,("create_local_private_krb5_conf_for_domain: close failed."
" Errno %s\n", strerror(errno) ));
unlink(tmpname);
- TALLOC_FREE(dname);
- return False;
+ goto done;
}
if (rename(tmpname, fname) == -1) {
"of %s to %s failed. Errno %s\n",
tmpname, fname, strerror(errno) ));
unlink(tmpname);
- TALLOC_FREE(dname);
- return False;
+ goto done;
}
DEBUG(5,("create_local_private_krb5_conf_for_domain: wrote "
- "file %s with realm %s KDC = %s\n",
- fname, realm_upper, print_canonical_sockaddr(dname, pss) ));
+ "file %s with realm %s KDC list = %s\n",
+ fname, realm_upper, kdc_ip_string));
/* Set the environment variable to this file. */
setenv("KRB5_CONFIG", fname, 1);
+ result = true;
+
#if defined(OVERWRITE_SYSTEM_KRB5_CONF)
#define SYSTEM_KRB5_CONF_PATH "/etc/krb5.conf"
if (lret != -1 || strcmp(linkpath, fname) == 0) {
/* Symlink already exists. */
- TALLOC_FREE(dname);
- return True;
+ goto done;
}
/* Try and replace with a symlink. */
DEBUG(0,("create_local_private_krb5_conf_for_domain: symlink "
"of %s to %s failed. Errno %s\n",
fname, SYSTEM_KRB5_CONF_PATH, strerror(errno) ));
- TALLOC_FREE(dname);
- return True; /* Not a fatal error. */
+ goto done; /* Not a fatal error. */
}
/* Yes, this is a race conditon... too bad. */
"of %s to %s failed. Errno %s\n",
SYSTEM_KRB5_CONF_PATH, newpath,
strerror(errno) ));
- TALLOC_FREE(dname);
- return True; /* Not a fatal error. */
+ goto done; /* Not a fatal error. */
}
if (symlink(fname, SYSTEM_KRB5_CONF_PATH) == -1) {
DEBUG(0,("create_local_private_krb5_conf_for_domain: "
"forced symlink of %s to /etc/krb5.conf failed. Errno %s\n",
fname, strerror(errno) ));
- TALLOC_FREE(dname);
- return True; /* Not a fatal error. */
+ goto done; /* Not a fatal error. */
}
}
}
#endif
+done:
+ TALLOC_FREE(tmpname);
TALLOC_FREE(dname);
- return True;
+ return result;
}
#endif