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 <http://www.gnu.org/licenses/>.
*/
#include "includes.h"
+#include "system/filesys.h"
+#include "smb_krb5.h"
+#include "../librpc/gen_ndr/ndr_misc.h"
+#include "libads/kerberos_proto.h"
+#include "secrets.h"
#ifdef HAVE_KRB5
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;
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;
}
if ((code = krb5_cc_initialize(ctx, cc, me))) {
goto out;
}
-
+
if ((code = krb5_cc_store_cred(ctx, cc, &my_creds))) {
goto out;
}
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 (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();
- } 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;
- }
-
- got_accountname:
- 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;
error_message(code)));
return code;
}
-
+
if (!cc_name) {
if ((code = krb5_cc_default(ctx, &cc))) {
krb5_free_context(ctx);
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
{
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) {
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;
}
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;
}
SAFE_FREE(key);
SAFE_FREE(princ_s);
- SAFE_FREE(unparsed_name);
+ TALLOC_FREE(unparsed_name);
if (princ) {
krb5_free_principal(context, princ);
static char *print_kdc_line(char *mem_ctx,
const char *prev_line,
- const struct sockaddr_storage *pss)
+ const struct sockaddr_storage *pss,
+ const char *kdc_name)
{
char *kdc_str = NULL;
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 ':'.
"Error %s\n.",
print_canonical_sockaddr(mem_ctx, pss),
gai_strerror(ret)));
+ return NULL;
}
/* Success, use host:port */
kdc_str = talloc_asprintf(mem_ctx,
hostname,
(unsigned int)port);
} else {
- kdc_str = talloc_asprintf(mem_ctx, "%s\tkdc = %s\n",
- prev_line,
- print_sockaddr(addr,
- sizeof(addr),
- pss));
+
+ /* 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;
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 = NULL;
int count_site = 0;
int count_nonsite;
- char *kdc_str = print_kdc_line(mem_ctx, "", pss);
+ char *kdc_str = print_kdc_line(mem_ctx, "", pss, kdc_name);
if (kdc_str == NULL) {
return NULL;
* but not done often. */
kdc_str = print_kdc_line(mem_ctx,
kdc_str,
- &ip_srv_site[i].ss);
+ &ip_srv_site[i].ss,
+ NULL);
if (!kdc_str) {
SAFE_FREE(ip_srv_site);
return NULL;
/* Append to the string - inefficient but not done often. */
kdc_str = print_kdc_line(mem_ctx,
kdc_str,
- &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 = lock_path("smb_krb5");
+ char *dname;
char *tmpname = NULL;
char *fname = NULL;
char *file_contents = NULL;
int fd;
char *realm_upper = NULL;
bool result = false;
+ char *aes_enctypes = NULL;
+
+ if (!lp_create_krb5_conf()) {
+ return false;
+ }
+
+ if (realm == NULL) {
+ DEBUG(0, ("No realm has been specified! Do you really want to "
+ "join an Active Directory server?\n"));
+ return false;
+ }
+ if (domain == NULL || pss == NULL || kdc_name == NULL) {
+ return false;
+ }
+
+ dname = lock_path("smb_krb5");
if (!dname) {
return false;
}
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) {
goto done;
}
+ aes_enctypes = talloc_strdup(fname, "");
+ if (aes_enctypes == NULL) {
+ goto done;
+ }
+
+#ifdef HAVE_ENCTYPE_AES256_CTS_HMAC_SHA1_96
+ aes_enctypes = talloc_asprintf_append(aes_enctypes, "%s", "aes256-cts-hmac-sha1-96 ");
+ if (aes_enctypes == NULL) {
+ goto done;
+ }
+#endif
+#ifdef HAVE_ENCTYPE_AES128_CTS_HMAC_SHA1_96
+ aes_enctypes = talloc_asprintf_append(aes_enctypes, "%s", "aes128-cts-hmac-sha1-96");
+ if (aes_enctypes == NULL) {
+ goto done;
+ }
+#endif
+
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"
+ "\tdefault_tgs_enctypes = %s RC4-HMAC DES-CBC-CRC DES-CBC-MD5\n"
+ "\tdefault_tkt_enctypes = %s RC4-HMAC DES-CBC-CRC DES-CBC-MD5\n"
+ "\tpreferred_enctypes = %s 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);
+ realm_upper, aes_enctypes, aes_enctypes, aes_enctypes,
+ realm_upper, kdc_ip_string);
if (!file_contents) {
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",