Restrict the enctypes in the generated krb5.conf files to Win2003 types.
[samba.git] / source / libads / kerberos.c
index 281ca2fd6836fdd513eb478b6f04bf004ab4b566..d47e8a3ff1ce577764beca5404e61dfa1f69d36b 100644 (file)
@@ -25,6 +25,8 @@
 
 #ifdef HAVE_KRB5
 
+#define DEFAULT_KRB5_PORT 88
+
 #define LIBADS_CCACHE_NAME "MEMORY:libads"
 
 /*
@@ -209,7 +211,8 @@ int kerberos_kinit_password_ext(const char *principal,
                krb5_set_real_time(ctx, time(NULL) + time_offset, 0);
        }
 
-       DEBUG(10,("kerberos_kinit_password: using [%s] as ccache and config [%s]\n",
+       DEBUG(10,("kerberos_kinit_password: as %s using [%s] as ccache and config [%s]\n",
+                       principal,
                        cache_name ? cache_name: krb5_cc_default_name(ctx),
                        getenv("KRB5_CONFIG")));
 
@@ -487,6 +490,61 @@ 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 
@@ -609,20 +667,73 @@ int kerberos_kinit_password(const char *principal,
                                           NULL);
 }
 
+/************************************************************************
+************************************************************************/
+
+static char *print_kdc_line(char *mem_ctx,
+                       const char *prev_line,
+                       const struct sockaddr_storage *pss)
+{
+       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);
+
+               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));
+               }
+       }
+       return kdc_str;
+}
+
 /************************************************************************
  Create a string list of available kdc's, possibly searching by sitename.
  Does DNS queries.
 ************************************************************************/
 
-static char *get_kdc_ip_string(char *mem_ctx, const char *realm, const char *sitename, struct in_addr primary_ip)
+static char *get_kdc_ip_string(char *mem_ctx,
+               const char *realm,
+               const char *sitename,
+               struct sockaddr_storage *pss)
 {
        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",
-                                       inet_ntoa(primary_ip));
+       char *kdc_str = print_kdc_line(mem_ctx, "", pss);
 
        if (kdc_str == NULL) {
                return NULL;
@@ -635,12 +746,14 @@ static char *get_kdc_ip_string(char *mem_ctx, const char *realm, const char *sit
                get_kdc_list(realm, sitename, &ip_srv_site, &count_site);
 
                for (i = 0; i < count_site; i++) {
-                       if (ip_equal(ip_srv_site[i].ip, primary_ip)) {
+                       if (addr_equal(&ip_srv_site[i].ss, pss)) {
                                continue;
                        }
-                       /* Append to the string - inefficient but not done often. */
-                       kdc_str = talloc_asprintf(mem_ctx, "%s\tkdc = %s\n",
-                               kdc_str, inet_ntoa(ip_srv_site[i].ip));
+                       /* 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;
@@ -655,13 +768,14 @@ static char *get_kdc_ip_string(char *mem_ctx, const char *realm, const char *sit
        for (i = 0; i < count_nonsite; i++) {
                int j;
 
-               if (ip_equal(ip_srv_nonsite[i].ip, primary_ip)) {
+               if (addr_equal(&ip_srv_nonsite[i].ss, pss)) {
                        continue;
                }
 
                /* Ensure this isn't an IP already seen (YUK! this is n*n....) */
                for (j = 0; j < count_site; j++) {
-                       if (ip_equal(ip_srv_nonsite[i].ip, ip_srv_site[j].ip)) {
+                       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. */
@@ -674,8 +788,9 @@ static char *get_kdc_ip_string(char *mem_ctx, const char *realm, const char *sit
                }
 
                /* Append to the string - inefficient but not done often. */
-               kdc_str = talloc_asprintf(mem_ctx, "%s\tkdc = %s\n",
-                       kdc_str, inet_ntoa(ip_srv_nonsite[i].ip));
+               kdc_str = print_kdc_line(mem_ctx,
+                               kdc_str,
+                               &ip_srv_nonsite[i].ss);
                if (!kdc_str) {
                        SAFE_FREE(ip_srv_site);
                        SAFE_FREE(ip_srv_nonsite);
@@ -700,8 +815,10 @@ static char *get_kdc_ip_string(char *mem_ctx, const char *realm, const char *sit
  run as root or will fail (which is a good thing :-).
 ************************************************************************/
 
-bool create_local_private_krb5_conf_for_domain(const char *realm, const char *domain,
-                                       const char *sitename, struct in_addr ip)
+bool create_local_private_krb5_conf_for_domain(const char *realm,
+                                               const char *domain,
+                                               const char *sitename,
+                                               struct sockaddr_storage *pss)
 {
        char *dname = talloc_asprintf(NULL, "%s/smb_krb5", lp_lockdir());
        char *tmpname = NULL;
@@ -742,16 +859,20 @@ bool create_local_private_krb5_conf_for_domain(const char *realm, const char *do
        realm_upper = talloc_strdup(fname, realm);
        strupper_m(realm_upper);
 
-       kdc_ip_string = get_kdc_ip_string(dname, realm, sitename, ip);
+       kdc_ip_string = get_kdc_ip_string(dname, realm, sitename, pss);
        if (!kdc_ip_string) {
                TALLOC_FREE(dname);
                return False;
        }
-               
-       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"
+                                       "default_tgs_enctypes = RC4-HMAC DES-CBC-CRC DES-CBC-MD5\n"
+                                       "default_tkt_enctypes = RC4-HMAC DES-CBC-CRC DES-CBC-MD5\n"
+                                       "preferred_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);
@@ -805,8 +926,8 @@ bool create_local_private_krb5_conf_for_domain(const char *realm, const char *do
        }
 
        DEBUG(5,("create_local_private_krb5_conf_for_domain: wrote "
-               "file %s with realm %s KDC = %s\n",
-               fname, realm_upper, inet_ntoa(ip) ));
+               "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);
@@ -817,13 +938,15 @@ bool create_local_private_krb5_conf_for_domain(const char *realm, const char *do
        /* Insanity, sheer insanity..... */
 
        if (strequal(realm, lp_realm())) {
-               pstring linkpath;
+               char linkpath[PATH_MAX+1];
                int lret;
 
                lret = readlink(SYSTEM_KRB5_CONF_PATH, linkpath, sizeof(linkpath)-1);
-               linkpath[sizeof(pstring)-1] = '\0';
+               if (lret != -1) {
+                       linkpath[lret] = '\0';
+               }
 
-               if (lret == 0 || strcmp(linkpath, fname) == 0) {
+               if (lret != -1 || strcmp(linkpath, fname) == 0) {
                        /* Symlink already exists. */
                        TALLOC_FREE(dname);
                        return True;
@@ -831,6 +954,7 @@ bool create_local_private_krb5_conf_for_domain(const char *realm, const char *do
 
                /* Try and replace with a symlink. */
                if (symlink(fname, SYSTEM_KRB5_CONF_PATH) == -1) {
+                       const char *newpath = SYSTEM_KRB5_CONF_PATH ## ".saved";
                        if (errno != EEXIST) {
                                DEBUG(0,("create_local_private_krb5_conf_for_domain: symlink "
                                        "of %s to %s failed. Errno %s\n",
@@ -839,20 +963,17 @@ bool create_local_private_krb5_conf_for_domain(const char *realm, const char *do
                                return True; /* Not a fatal error. */
                        }
 
-                       pstrcpy(linkpath, SYSTEM_KRB5_CONF_PATH);
-                       pstrcat(linkpath, ".saved");
-
                        /* Yes, this is a race conditon... too bad. */
-                       if (rename(SYSTEM_KRB5_CONF_PATH, linkpath) == -1) {
+                       if (rename(SYSTEM_KRB5_CONF_PATH, newpath) == -1) {
                                DEBUG(0,("create_local_private_krb5_conf_for_domain: rename "
                                        "of %s to %s failed. Errno %s\n",
-                                       SYSTEM_KRB5_CONF_PATH, linkpath,
+                                       SYSTEM_KRB5_CONF_PATH, newpath,
                                        strerror(errno) ));
                                TALLOC_FREE(dname);
                                return True; /* Not a fatal error. */
                        }
 
-                       if (symlink(fname, "/etc/krb5.conf") == -1) {
+                       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) ));