s4:kdc: Implement KDC plugin hardware authentication policy
[samba.git] / source3 / libads / ldap.c
index 04b2f35022d01be9a9d90ad7870e3854c96672db..08f3a138e822d3a530f323e78e31039481f7b2ef 100644 (file)
@@ -34,6 +34,8 @@
 #include "lib/param/loadparm.h"
 #include "libsmb/namequery.h"
 #include "../librpc/gen_ndr/ndr_ads.h"
+#include "auth/credentials/credentials.h"
+#include "passdb.h"
 
 #ifdef HAVE_LDAP
 
@@ -730,7 +732,7 @@ static NTSTATUS ads_find_dc(ADS_STRUCT *ads)
         * In case of LDAP we use get_dc_name() as that
         * creates the custom krb5.conf file
         */
-       if (!(ads->auth.flags & ADS_AUTH_NO_BIND)) {
+       if (ads->auth.flags & ADS_AUTH_GENERATE_KRB5_CONFIG) {
                fstring srv_name;
                struct sockaddr_storage ip_out;
 
@@ -810,21 +812,41 @@ static NTSTATUS ads_find_dc(ADS_STRUCT *ads)
                  c_realm, c_domain, nt_errstr(status)));
        return status;
 }
+
 /**
  * Connect to the LDAP server
  * @param ads Pointer to an existing ADS_STRUCT
  * @return status of connection
  **/
-ADS_STATUS ads_connect(ADS_STRUCT *ads)
+static ADS_STATUS ads_connect_internal(ADS_STRUCT *ads,
+                                      struct cli_credentials *creds)
 {
        int version = LDAP_VERSION3;
        ADS_STATUS status;
        NTSTATUS ntstatus;
        char addr[INET6_ADDRSTRLEN];
        struct sockaddr_storage existing_ss;
+       bool tls = false;
+       bool start_tls = false;
 
        zero_sockaddr(&existing_ss);
 
+       if (!(ads->auth.flags & ADS_AUTH_NO_BIND)) {
+               SMB_ASSERT(creds != NULL);
+       }
+
+       if (ads->auth.flags & ADS_AUTH_ANON_BIND) {
+               /*
+                * Simple anonyous binds are only
+                * allowed for anonymous credentials
+                */
+               SMB_ASSERT(cli_credentials_is_anonymous(creds));
+       }
+
+       if (!(ads->auth.flags & (ADS_AUTH_NO_BIND|ADS_AUTH_ANON_BIND))) {
+               ads->auth.flags |= ADS_AUTH_GENERATE_KRB5_CONFIG;
+       }
+
        /*
         * ads_connect can be passed in a reused ADS_STRUCT
         * with an existing non-zero ads->ldap.ss IP address
@@ -849,6 +871,7 @@ ADS_STATUS ads_connect(ADS_STRUCT *ads)
        }
 
        ads_zero_ldap(ads);
+       ZERO_STRUCT(ads->ldap_tls_data);
        ZERO_STRUCT(ads->ldap_wrap_data);
        ads->ldap.last_attempt  = time_mono(NULL);
        ads->ldap_wrap_data.wrap_type   = ADS_SASLWRAP_TYPE_PLAIN;
@@ -930,27 +953,6 @@ got_connection:
        print_sockaddr(addr, sizeof(addr), &ads->ldap.ss);
        DEBUG(3,("Successfully contacted LDAP server %s\n", addr));
 
-       if (!ads->auth.user_name) {
-               /* Must use the userPrincipalName value here or sAMAccountName
-                  and not servicePrincipalName; found by Guenther Deschner */
-               ads->auth.user_name = talloc_asprintf(ads,
-                                                     "%s$",
-                                                     lp_netbios_name());
-               if (ads->auth.user_name == NULL) {
-                       DBG_ERR("talloc_asprintf failed\n");
-                       status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
-                       goto out;
-               }
-       }
-
-       if (ads->auth.realm == NULL) {
-               ads->auth.realm = talloc_strdup(ads, ads->config.realm);
-               if (ads->auth.realm == NULL) {
-                       status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
-                       goto out;
-               }
-       }
-
        if (!ads->auth.kdc_server) {
                print_sockaddr(addr, sizeof(addr), &ads->ldap.ss);
                ads->auth.kdc_server = talloc_strdup(ads, addr);
@@ -967,6 +969,12 @@ got_connection:
                goto out;
        }
 
+       ads->ldap_tls_data.mem_ctx = talloc_init("ads LDAP TLS connection memory");
+       if (!ads->ldap_tls_data.mem_ctx) {
+               status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
+               goto out;
+       }
+
        ads->ldap_wrap_data.mem_ctx = talloc_init("ads LDAP connection memory");
        if (!ads->ldap_wrap_data.mem_ctx) {
                status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
@@ -975,6 +983,17 @@ got_connection:
 
        /* Otherwise setup the TCP LDAP session */
 
+       if (ads->auth.flags & ADS_AUTH_SASL_LDAPS) {
+               tls = true;
+               ads->ldap.port = 636;
+       } else if (ads->auth.flags & ADS_AUTH_SASL_STARTTLS) {
+               tls = true;
+               start_tls = true;
+               ads->ldap.port = 389;
+       } else {
+               ads->ldap.port = 389;
+       }
+
        ads->ldap.ld = ldap_open_with_timeout(ads->config.ldap_server_name,
                                              &ads->ldap.ss,
                                              ads->ldap.port, lp_ldap_timeout());
@@ -984,14 +1003,85 @@ got_connection:
        }
        DEBUG(3,("Connected to LDAP server %s\n", ads->config.ldap_server_name));
 
+       ldap_set_option(ads->ldap.ld, LDAP_OPT_PROTOCOL_VERSION, &version);
+
+       if (start_tls) {
+               unsigned int to = lp_ldap_connection_timeout();
+               struct berval *rspdata = NULL;
+               char *rspoid = NULL;
+               int rc;
+
+               if (to) {
+                       /* Setup timeout */
+                       gotalarm = 0;
+                       CatchSignal(SIGALRM, gotalarm_sig);
+                       alarm(to);
+                       /* End setup timeout. */
+               }
+
+               rc = ldap_extended_operation_s(ads->ldap.ld,
+                                              LDAP_EXOP_START_TLS,
+                                              NULL,
+                                              NULL,
+                                              NULL,
+                                              &rspoid,
+                                              &rspdata);
+               if (gotalarm != 0 && rc == LDAP_SUCCESS) {
+                       rc = LDAP_TIMEOUT;
+               }
+
+               if (to) {
+                       /* Teardown timeout. */
+                       alarm(0);
+                       CatchSignal(SIGALRM, SIG_IGN);
+               }
+
+               if (rspoid != NULL) {
+                       ldap_memfree(rspoid);
+               }
+
+               if (rspdata != NULL) {
+                       ber_bvfree(rspdata);
+               }
+
+               if (rc != LDAP_SUCCESS) {
+                       status = ADS_ERROR_LDAP(rc);
+                       goto out;
+               }
+       }
+
+       if (tls) {
+               unsigned int to = lp_ldap_connection_timeout();
+
+               if (to) {
+                       /* Setup timeout */
+                       gotalarm = 0;
+                       CatchSignal(SIGALRM, gotalarm_sig);
+                       alarm(to);
+                       /* End setup timeout. */
+               }
+
+               status = ads_setup_tls_wrapping(&ads->ldap_tls_data,
+                                               ads->ldap.ld,
+                                               ads->config.ldap_server_name);
+
+               if (to) {
+                       /* Teardown timeout. */
+                       alarm(0);
+                       CatchSignal(SIGALRM, SIG_IGN);
+               }
+
+               if ( !ADS_ERR_OK(status) ) {
+                       goto out;
+               }
+       }
+
        /* cache the successful connection for workgroup and realm */
        if (ads_closest_dc(ads)) {
                saf_store( ads->server.workgroup, ads->config.ldap_server_name);
                saf_store( ads->server.realm, ads->config.ldap_server_name);
        }
 
-       ldap_set_option(ads->ldap.ld, LDAP_OPT_PROTOCOL_VERSION, &version);
-
        /* fill in the current time and offsets */
 
        status = ads_current_time( ads );
@@ -1006,12 +1096,7 @@ got_connection:
                goto out;
        }
 
-       if (ads->auth.flags & ADS_AUTH_SIMPLE_BIND) {
-               status = ADS_ERROR(ldap_simple_bind_s(ads->ldap.ld, ads->auth.user_name, ads->auth.password));
-               goto out;
-       }
-
-       status = ads_sasl_bind(ads);
+       status = ads_sasl_bind(ads, creds);
 
  out:
        if (DEBUGLEVEL >= 11) {
@@ -1026,18 +1111,98 @@ got_connection:
 }
 
 /**
- * Connect to the LDAP server using given credentials
+ * Connect to the LDAP server using without a bind
+ * and without a tcp connection at all
+ *
+ * @param ads Pointer to an existing ADS_STRUCT
+ * @return status of connection
+ **/
+ADS_STATUS ads_connect_cldap_only(ADS_STRUCT *ads)
+{
+       ads->auth.flags |= ADS_AUTH_NO_BIND;
+       return ads_connect_internal(ads, NULL);
+}
+
+/**
+ * Connect to the LDAP server
  * @param ads Pointer to an existing ADS_STRUCT
  * @return status of connection
  **/
-ADS_STATUS ads_connect_user_creds(ADS_STRUCT *ads)
+ADS_STATUS ads_connect_creds(ADS_STRUCT *ads, struct cli_credentials *creds)
 {
-       ads->auth.flags |= ADS_AUTH_USER_CREDS;
+       SMB_ASSERT(creds != NULL);
+
+       /*
+        * We allow upgrades from
+        * ADS_AUTH_NO_BIND if credentials
+        * are specified
+        */
+       ads->auth.flags &= ~ADS_AUTH_NO_BIND;
 
-       return ads_connect(ads);
+       /*
+        * We allow upgrades from ADS_AUTH_ANON_BIND,
+        * as we don't want to use simple binds with
+        * non-anon credentials
+        */
+       if (!cli_credentials_is_anonymous(creds)) {
+               ads->auth.flags &= ~ADS_AUTH_ANON_BIND;
+       }
+
+       return ads_connect_internal(ads, creds);
 }
 
 /**
+ * Connect to the LDAP server using anonymous credentials
+ * using a simple bind without username/password
+ *
+ * @param ads Pointer to an existing ADS_STRUCT
+ * @return status of connection
+ **/
+ADS_STATUS ads_connect_simple_anon(ADS_STRUCT *ads)
+{
+       TALLOC_CTX *frame = talloc_stackframe();
+       struct cli_credentials *creds = NULL;
+       ADS_STATUS status;
+
+       creds = cli_credentials_init_anon(frame);
+       if (creds == NULL) {
+               TALLOC_FREE(frame);
+               return ADS_ERROR_SYSTEM(errno);
+       }
+
+       ads->auth.flags |= ADS_AUTH_ANON_BIND;
+       status = ads_connect_creds(ads, creds);
+       TALLOC_FREE(frame);
+       return status;
+}
+
+/**
+ * Connect to the LDAP server using the machine account
+ * @param ads Pointer to an existing ADS_STRUCT
+ * @return status of connection
+ **/
+ADS_STATUS ads_connect_machine(ADS_STRUCT *ads)
+{
+       TALLOC_CTX *frame = talloc_stackframe();
+       struct cli_credentials *creds = NULL;
+       ADS_STATUS status;
+       NTSTATUS ntstatus;
+
+       ntstatus = pdb_get_trust_credentials(ads->server.workgroup,
+                                            ads->server.realm,
+                                            frame,
+                                            &creds);
+       if (!NT_STATUS_IS_OK(ntstatus)) {
+               TALLOC_FREE(frame);
+               return ADS_ERROR_NT(ntstatus);
+       }
+
+       status = ads_connect_creds(ads, creds);
+       TALLOC_FREE(frame);
+       return status;
+}
+
+/*
  * Zero out the internal ads->ldap struct and initialize the address to zero IP.
  * @param ads Pointer to an existing ADS_STRUCT
  *
@@ -1066,6 +1231,9 @@ void ads_disconnect(ADS_STRUCT *ads)
                ldap_unbind(ads->ldap.ld);
                ads->ldap.ld = NULL;
        }
+       if (ads->ldap_tls_data.mem_ctx) {
+               talloc_free(ads->ldap_tls_data.mem_ctx);
+       }
        if (ads->ldap_wrap_data.wrap_ops &&
                ads->ldap_wrap_data.wrap_ops->disconnect) {
                ads->ldap_wrap_data.wrap_ops->disconnect(&ads->ldap_wrap_data);
@@ -1074,6 +1242,7 @@ void ads_disconnect(ADS_STRUCT *ads)
                talloc_free(ads->ldap_wrap_data.mem_ctx);
        }
        ads_zero_ldap(ads);
+       ZERO_STRUCT(ads->ldap_tls_data);
        ZERO_STRUCT(ads->ldap_wrap_data);
 }
 
@@ -1854,7 +2023,7 @@ static ADS_STATUS ads_mod_ber(TALLOC_CTX *ctx, ADS_MODLIST *mods,
        if (!val)
                return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
        return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE|LDAP_MOD_BVALUES,
-                              name, (const void **) values);
+                              name, (const void *) values);
 }
 
 static void ads_print_error(int ret, LDAP *ld)
@@ -3554,8 +3723,7 @@ ADS_STATUS ads_current_time(ADS_STRUCT *ads)
                 */
                ads_s->config.flags = 0;
 
-               ads_s->auth.flags = ADS_AUTH_ANON_BIND;
-               status = ads_connect( ads_s );
+               status = ads_connect_simple_anon(ads_s);
                if ( !ADS_ERR_OK(status))
                        goto done;
        }
@@ -3577,10 +3745,16 @@ ADS_STATUS ads_current_time(ADS_STRUCT *ads)
        ads->config.current_time = ads_parse_time(timestr);
 
        if (ads->config.current_time != 0) {
-               ads->auth.time_offset = ads->config.current_time - time(NULL);
-               DEBUG(4,("KDC time offset is %d seconds\n", ads->auth.time_offset));
+               ads->config.time_offset = ads->config.current_time - time(NULL);
+               DBG_INFO("server time offset is %d seconds\n",
+                        ads->config.time_offset);
+       } else {
+               ads->config.time_offset = 0;
        }
 
+       DBG_INFO("server time offset is %d seconds\n",
+                ads->config.time_offset);
+
        ads_msgfree(ads, res);
 
        status = ADS_SUCCESS;
@@ -3638,8 +3812,7 @@ ADS_STATUS ads_domain_func_level(ADS_STRUCT *ads, uint32_t *val)
                 */
                ads_s->config.flags = 0;
 
-               ads_s->auth.flags = ADS_AUTH_ANON_BIND;
-               status = ads_connect( ads_s );
+               status = ads_connect_simple_anon(ads_s);
                if ( !ADS_ERR_OK(status))
                        goto done;
        }