auth/gensec: make sure we keep a DCERPC_AUTH_TYPE_SCHANNEL backend if required
[obnox/samba/samba-obnox.git] / auth / gensec / gensec_start.c
index 10fcb67b255de32a276fb1d9e84279f13789b78b..955cc36f4cb37ab2653bfcdba8fe9d46379677b9 100644 (file)
 #include "system/network.h"
 #include "tevent.h"
 #include "../lib/util/tevent_ntstatus.h"
-#include "librpc/rpc/dcerpc.h"
+#include "librpc/gen_ndr/dcerpc.h"
 #include "auth/credentials/credentials.h"
 #include "auth/gensec/gensec.h"
+#include "auth/gensec/gensec_internal.h"
 #include "lib/param/param.h"
 #include "lib/util/tsort.h"
 #include "lib/util/samba_modules.h"
 
 /* the list of currently registered GENSEC backends */
-static struct gensec_security_ops **generic_security_ops;
+static const struct gensec_security_ops **generic_security_ops;
 static int gensec_num_backends;
 
 /* Return all the registered mechs.  Don't modify the return pointer,
- * but you may talloc_reference it if convient */
-_PUBLIC_ struct gensec_security_ops **gensec_security_all(void)
+ * but you may talloc_referen it if convient */
+_PUBLIC_ const struct gensec_security_ops * const *gensec_security_all(void)
 {
        return generic_security_ops;
 }
 
-bool gensec_security_ops_enabled(struct gensec_security_ops *ops, struct gensec_security *security)
+bool gensec_security_ops_enabled(const struct gensec_security_ops *ops, struct gensec_security *security)
 {
        return lpcfg_parm_bool(security->settings->lp_ctx, NULL, "gensec", ops->name, ops->enabled);
 }
@@ -50,32 +51,46 @@ bool gensec_security_ops_enabled(struct gensec_security_ops *ops, struct gensec_
 /* Sometimes we want to force only kerberos, sometimes we want to
  * force it's avoidance.  The old list could be either
  * gensec_security_all(), or from cli_credentials_gensec_list() (ie,
- * an existing list we have trimmed down) */
+ * an existing list we have trimmed down)
+ *
+ * The intended logic is:
+ *
+ * if we are in the default AUTO have kerberos:
+ * - take a reference to the master list
+ * otherwise
+ * - always add spnego then:
+ * - if we 'MUST' have kerberos:
+ *   only add kerberos mechs
+ * - if we 'DONT' want kerberos':
+ *   only add non-kerberos mechs
+ *
+ * Once we get things like NegoEx or moonshot, this will of course get
+ * more compplex.
+ */
 
-_PUBLIC_ struct gensec_security_ops **gensec_use_kerberos_mechs(TALLOC_CTX *mem_ctx,
-                                                      struct gensec_security_ops **old_gensec_list,
-                                                      struct cli_credentials *creds)
+_PUBLIC_ const struct gensec_security_ops **gensec_use_kerberos_mechs(TALLOC_CTX *mem_ctx,
+                       const struct gensec_security_ops * const *old_gensec_list,
+                       struct cli_credentials *creds)
 {
-       struct gensec_security_ops **new_gensec_list;
+       const struct gensec_security_ops **new_gensec_list;
        int i, j, num_mechs_in;
        enum credentials_use_kerberos use_kerberos = CRED_AUTO_USE_KERBEROS;
+       bool keep_schannel = false;
 
        if (creds) {
                use_kerberos = cli_credentials_get_kerberos_state(creds);
-       }
-
-       if (use_kerberos == CRED_AUTO_USE_KERBEROS) {
-               if (!talloc_reference(mem_ctx, old_gensec_list)) {
-                       return NULL;
+               if (cli_credentials_get_netlogon_creds(creds) != NULL) {
+                       keep_schannel = true;
                }
-               return old_gensec_list;
        }
 
        for (num_mechs_in=0; old_gensec_list && old_gensec_list[num_mechs_in]; num_mechs_in++) {
                /* noop */
        }
 
-       new_gensec_list = talloc_array(mem_ctx, struct gensec_security_ops *, num_mechs_in + 1);
+       new_gensec_list = talloc_array(mem_ctx,
+                                      const struct gensec_security_ops *,
+                                      num_mechs_in + 1);
        if (!new_gensec_list) {
                return NULL;
        }
@@ -83,89 +98,79 @@ _PUBLIC_ struct gensec_security_ops **gensec_use_kerberos_mechs(TALLOC_CTX *mem_
        j = 0;
        for (i=0; old_gensec_list && old_gensec_list[i]; i++) {
                int oid_idx;
+               bool keep = false;
 
                for (oid_idx = 0; old_gensec_list[i]->oid && old_gensec_list[i]->oid[oid_idx]; oid_idx++) {
                        if (strcmp(old_gensec_list[i]->oid[oid_idx], GENSEC_OID_SPNEGO) == 0) {
-                               new_gensec_list[j] = old_gensec_list[i];
-                               j++;
+                               keep = true;
                                break;
                        }
                }
+
+               if (old_gensec_list[i]->auth_type == DCERPC_AUTH_TYPE_SCHANNEL) {
+                       keep = keep_schannel;
+               }
+
                switch (use_kerberos) {
+               case CRED_AUTO_USE_KERBEROS:
+                       keep = true;
+                       break;
+
                case CRED_DONT_USE_KERBEROS:
                        if (old_gensec_list[i]->kerberos == false) {
-                               new_gensec_list[j] = old_gensec_list[i];
-                               j++;
+                               keep = true;
                        }
+
                        break;
+
                case CRED_MUST_USE_KERBEROS:
                        if (old_gensec_list[i]->kerberos == true) {
-                               new_gensec_list[j] = old_gensec_list[i];
-                               j++;
+                               keep = true;
                        }
+
                        break;
                default:
                        /* Can't happen or invalid parameter */
                        return NULL;
                }
+
+               if (!keep) {
+                       continue;
+               }
+
+               new_gensec_list[j] = old_gensec_list[i];
+               j++;
        }
        new_gensec_list[j] = NULL;
 
        return new_gensec_list;
 }
 
-struct gensec_security_ops **gensec_security_mechs(struct gensec_security *gensec_security,
-                                                  TALLOC_CTX *mem_ctx)
+_PUBLIC_ const struct gensec_security_ops **gensec_security_mechs(
+                               struct gensec_security *gensec_security,
+                               TALLOC_CTX *mem_ctx)
 {
-       struct gensec_security_ops **backends;
-       backends = gensec_security_all();
-       if (!gensec_security) {
-               if (!talloc_reference(mem_ctx, backends)) {
-                       return NULL;
-               }
-               return backends;
-       } else {
-               struct cli_credentials *creds = gensec_get_credentials(gensec_security);
-               if (!creds) {
-                       if (!talloc_reference(mem_ctx, backends)) {
-                               return NULL;
-                       }
-                       return backends;
-               }
-               return gensec_use_kerberos_mechs(mem_ctx, backends, creds);
-       }
-}
+       struct cli_credentials *creds = NULL;
+       const struct gensec_security_ops * const *backends = gensec_security_all();
 
-static const struct gensec_security_ops *gensec_security_by_authtype(struct gensec_security *gensec_security,
-                                                                    uint8_t auth_type)
-{
-       int i;
-       struct gensec_security_ops **backends;
-       const struct gensec_security_ops *backend;
-       TALLOC_CTX *mem_ctx = talloc_new(gensec_security);
-       if (!mem_ctx) {
-               return NULL;
-       }
-       backends = gensec_security_mechs(gensec_security, mem_ctx);
-       for (i=0; backends && backends[i]; i++) {
-               if (!gensec_security_ops_enabled(backends[i], gensec_security))
-                               continue;
-               if (backends[i]->auth_type == auth_type) {
-                       backend = backends[i];
-                       talloc_free(mem_ctx);
-                       return backend;
+       if (gensec_security != NULL) {
+               creds = gensec_get_credentials(gensec_security);
+
+               if (gensec_security->settings->backends) {
+                       backends = gensec_security->settings->backends;
                }
        }
-       talloc_free(mem_ctx);
 
-       return NULL;
+       return gensec_use_kerberos_mechs(mem_ctx, backends, creds);
+
 }
 
-const struct gensec_security_ops *gensec_security_by_oid(struct gensec_security *gensec_security,
-                                                        const char *oid_string)
+_PUBLIC_ const struct gensec_security_ops *gensec_security_by_oid(
+                               struct gensec_security *gensec_security,
+                               const char *oid_string)
 {
        int i, j;
-       struct gensec_security_ops **backends;
+       const struct gensec_security_ops **backends;
        const struct gensec_security_ops *backend;
        TALLOC_CTX *mem_ctx = talloc_new(gensec_security);
        if (!mem_ctx) {
@@ -193,11 +198,12 @@ const struct gensec_security_ops *gensec_security_by_oid(struct gensec_security
        return NULL;
 }
 
-const struct gensec_security_ops *gensec_security_by_sasl_name(struct gensec_security *gensec_security,
-                                                              const char *sasl_name)
+_PUBLIC_ const struct gensec_security_ops *gensec_security_by_sasl_name(
+                               struct gensec_security *gensec_security,
+                               const char *sasl_name)
 {
        int i;
-       struct gensec_security_ops **backends;
+       const struct gensec_security_ops **backends;
        const struct gensec_security_ops *backend;
        TALLOC_CTX *mem_ctx = talloc_new(gensec_security);
        if (!mem_ctx) {
@@ -219,11 +225,39 @@ const struct gensec_security_ops *gensec_security_by_sasl_name(struct gensec_sec
        return NULL;
 }
 
+_PUBLIC_ const struct gensec_security_ops *gensec_security_by_auth_type(
+                               struct gensec_security *gensec_security,
+                               uint32_t auth_type)
+{
+       int i;
+       const struct gensec_security_ops **backends;
+       const struct gensec_security_ops *backend;
+       TALLOC_CTX *mem_ctx = talloc_new(gensec_security);
+       if (!mem_ctx) {
+               return NULL;
+       }
+       backends = gensec_security_mechs(gensec_security, mem_ctx);
+       for (i=0; backends && backends[i]; i++) {
+               if (gensec_security != NULL &&
+                   !gensec_security_ops_enabled(backends[i], gensec_security)) {
+                       continue;
+               }
+               if (backends[i]->auth_type == auth_type) {
+                       backend = backends[i];
+                       talloc_free(mem_ctx);
+                       return backend;
+               }
+       }
+       talloc_free(mem_ctx);
+
+       return NULL;
+}
+
 static const struct gensec_security_ops *gensec_security_by_name(struct gensec_security *gensec_security,
                                                                 const char *name)
 {
        int i;
-       struct gensec_security_ops **backends;
+       const struct gensec_security_ops **backends;
        const struct gensec_security_ops *backend;
        TALLOC_CTX *mem_ctx = talloc_new(gensec_security);
        if (!mem_ctx) {
@@ -253,12 +287,13 @@ static const struct gensec_security_ops *gensec_security_by_name(struct gensec_s
  * attached to the gensec_security, and return in our preferred order.
  */
 
-const struct gensec_security_ops **gensec_security_by_sasl_list(struct gensec_security *gensec_security,
-                                                               TALLOC_CTX *mem_ctx,
-                                                               const char **sasl_names)
+static const struct gensec_security_ops **gensec_security_by_sasl_list(
+       struct gensec_security *gensec_security,
+       TALLOC_CTX *mem_ctx,
+       const char **sasl_names)
 {
        const struct gensec_security_ops **backends_out;
-       struct gensec_security_ops **backends;
+       const struct gensec_security_ops **backends;
        int i, k, sasl_idx;
        int num_backends_out = 0;
 
@@ -322,13 +357,14 @@ const struct gensec_security_ops **gensec_security_by_sasl_list(struct gensec_se
  * attached to the gensec_security, and return in our preferred order.
  */
 
-const struct gensec_security_ops_wrapper *gensec_security_by_oid_list(struct gensec_security *gensec_security,
-                                                                     TALLOC_CTX *mem_ctx,
-                                                                     const char **oid_strings,
-                                                                     const char *skip)
+_PUBLIC_ const struct gensec_security_ops_wrapper *gensec_security_by_oid_list(
+                                       struct gensec_security *gensec_security,
+                                       TALLOC_CTX *mem_ctx,
+                                       const char * const *oid_strings,
+                                       const char *skip)
 {
        struct gensec_security_ops_wrapper *backends_out;
-       struct gensec_security_ops **backends;
+       const struct gensec_security_ops **backends;
        int i, j, k, oid_idx;
        int num_backends_out = 0;
 
@@ -399,10 +435,11 @@ const struct gensec_security_ops_wrapper *gensec_security_by_oid_list(struct gen
  * Return OIDS from the security subsystems listed
  */
 
-const char **gensec_security_oids_from_ops(struct gensec_security *gensec_security,
-                                                                                  TALLOC_CTX *mem_ctx,
-                                          struct gensec_security_ops **ops,
-                                          const char *skip)
+static const char **gensec_security_oids_from_ops(
+       struct gensec_security *gensec_security,
+       TALLOC_CTX *mem_ctx,
+       const struct gensec_security_ops * const *ops,
+       const char *skip)
 {
        int i;
        int j = 0;
@@ -446,8 +483,8 @@ const char **gensec_security_oids_from_ops(struct gensec_security *gensec_securi
  * Return OIDS from the security subsystems listed
  */
 
-const char **gensec_security_oids_from_ops_wrapped(TALLOC_CTX *mem_ctx,
-                                                  const struct gensec_security_ops_wrapper *wops)
+_PUBLIC_ const char **gensec_security_oids_from_ops_wrapped(TALLOC_CTX *mem_ctx,
+                               const struct gensec_security_ops_wrapper *wops)
 {
        int i;
        int j = 0;
@@ -488,12 +525,14 @@ const char **gensec_security_oids_from_ops_wrapped(TALLOC_CTX *mem_ctx,
  *
  */
 
-const char **gensec_security_oids(struct gensec_security *gensec_security,
-                                 TALLOC_CTX *mem_ctx,
-                                 const char *skip)
+_PUBLIC_ const char **gensec_security_oids(struct gensec_security *gensec_security,
+                                          TALLOC_CTX *mem_ctx,
+                                          const char *skip)
 {
-       struct gensec_security_ops **ops
-               = gensec_security_mechs(gensec_security, mem_ctx);
+       const struct gensec_security_ops **ops;
+
+       ops = gensec_security_mechs(gensec_security, mem_ctx);
+
        return gensec_security_oids_from_ops(gensec_security, mem_ctx, ops, skip);
 }
 
@@ -506,7 +545,6 @@ const char **gensec_security_oids(struct gensec_security *gensec_security,
   @ gensec_security return
 */
 static NTSTATUS gensec_start(TALLOC_CTX *mem_ctx,
-                            struct tevent_context *ev,
                             struct gensec_settings *settings,
                             struct auth4_context *auth_context,
                             struct gensec_security **gensec_security)
@@ -514,7 +552,8 @@ static NTSTATUS gensec_start(TALLOC_CTX *mem_ctx,
        (*gensec_security) = talloc_zero(mem_ctx, struct gensec_security);
        NT_STATUS_HAVE_NO_MEMORY(*gensec_security);
 
-       (*gensec_security)->event_ctx = ev;
+       (*gensec_security)->max_update_size = 0;
+
        SMB_ASSERT(settings->lp_ctx != NULL);
        (*gensec_security)->settings = talloc_reference(*gensec_security, settings);
 
@@ -547,7 +586,8 @@ _PUBLIC_ NTSTATUS gensec_subcontext_start(TALLOC_CTX *mem_ctx,
 
        (*gensec_security)->subcontext = true;
        (*gensec_security)->want_features = parent->want_features;
-       (*gensec_security)->event_ctx = parent->event_ctx;
+       (*gensec_security)->max_update_size = parent->max_update_size;
+       (*gensec_security)->dcerpc_auth_level = parent->dcerpc_auth_level;
        (*gensec_security)->auth_context = talloc_reference(*gensec_security, parent->auth_context);
        (*gensec_security)->settings = talloc_reference(*gensec_security, parent->settings);
        (*gensec_security)->auth_context = talloc_reference(*gensec_security, parent->auth_context);
@@ -563,7 +603,6 @@ _PUBLIC_ NTSTATUS gensec_subcontext_start(TALLOC_CTX *mem_ctx,
 */
 _PUBLIC_ NTSTATUS gensec_client_start(TALLOC_CTX *mem_ctx,
                             struct gensec_security **gensec_security,
-                            struct tevent_context *ev,
                             struct gensec_settings *settings)
 {
        NTSTATUS status;
@@ -573,7 +612,7 @@ _PUBLIC_ NTSTATUS gensec_client_start(TALLOC_CTX *mem_ctx,
                return NT_STATUS_INTERNAL_ERROR;
        }
 
-       status = gensec_start(mem_ctx, ev, settings, NULL, gensec_security);
+       status = gensec_start(mem_ctx, settings, NULL, gensec_security);
        if (!NT_STATUS_IS_OK(status)) {
                return status;
        }
@@ -591,7 +630,6 @@ _PUBLIC_ NTSTATUS gensec_client_start(TALLOC_CTX *mem_ctx,
   @note  The mem_ctx is only a parent and may be NULL.
 */
 _PUBLIC_ NTSTATUS gensec_server_start(TALLOC_CTX *mem_ctx,
-                                     struct tevent_context *ev,
                                      struct gensec_settings *settings,
                                      struct auth4_context *auth_context,
                                      struct gensec_security **gensec_security)
@@ -603,7 +641,7 @@ _PUBLIC_ NTSTATUS gensec_server_start(TALLOC_CTX *mem_ctx,
                return NT_STATUS_INTERNAL_ERROR;
        }
 
-       status = gensec_start(mem_ctx, ev, settings, auth_context, gensec_security);
+       status = gensec_start(mem_ctx, settings, auth_context, gensec_security);
        if (!NT_STATUS_IS_OK(status)) {
                return status;
        }
@@ -615,6 +653,20 @@ _PUBLIC_ NTSTATUS gensec_server_start(TALLOC_CTX *mem_ctx,
 NTSTATUS gensec_start_mech(struct gensec_security *gensec_security)
 {
        NTSTATUS status;
+
+       if (gensec_security->credentials) {
+               const char *forced_mech = cli_credentials_get_forced_sasl_mech(gensec_security->credentials);
+               if (forced_mech &&
+                   (gensec_security->ops->sasl_name == NULL ||
+                    strcasecmp(forced_mech, gensec_security->ops->sasl_name) != 0)) {
+                       DEBUG(5, ("GENSEC mechanism %s (%s) skipped, as it "
+                                 "did not match forced mechanism %s\n",
+                                 gensec_security->ops->name,
+                                 gensec_security->ops->sasl_name,
+                                 forced_mech));
+                       return NT_STATUS_INVALID_PARAMETER;
+               }
+       }
        DEBUG(5, ("Starting GENSEC %smechanism %s\n",
                  gensec_security->subcontext ? "sub" : "",
                  gensec_security->ops->name));
@@ -666,11 +718,12 @@ NTSTATUS gensec_start_mech_by_ops(struct gensec_security *gensec_security,
 _PUBLIC_ NTSTATUS gensec_start_mech_by_authtype(struct gensec_security *gensec_security,
                                       uint8_t auth_type, uint8_t auth_level)
 {
-       gensec_security->ops = gensec_security_by_authtype(gensec_security, auth_type);
+       gensec_security->ops = gensec_security_by_auth_type(gensec_security, auth_type);
        if (!gensec_security->ops) {
                DEBUG(3, ("Could not find GENSEC backend for auth_type=%d\n", (int)auth_type));
                return NT_STATUS_INVALID_PARAMETER;
        }
+       gensec_security->dcerpc_auth_level = auth_level;
        gensec_want_feature(gensec_security, GENSEC_FEATURE_DCE_STYLE);
        gensec_want_feature(gensec_security, GENSEC_FEATURE_ASYNC_REPLIES);
        if (auth_level == DCERPC_AUTH_LEVEL_INTEGRITY) {
@@ -692,7 +745,7 @@ _PUBLIC_ NTSTATUS gensec_start_mech_by_authtype(struct gensec_security *gensec_s
 _PUBLIC_ const char *gensec_get_name_by_authtype(struct gensec_security *gensec_security, uint8_t authtype)
 {
        const struct gensec_security_ops *ops;
-       ops = gensec_security_by_authtype(gensec_security, authtype);
+       ops = gensec_security_by_auth_type(gensec_security, authtype);
        if (ops) {
                return ops->name;
        }
@@ -815,7 +868,7 @@ _PUBLIC_ NTSTATUS gensec_set_credentials(struct gensec_security *gensec_security
   The 'name' can be later used by other backends to find the operations
   structure for this backend.
 */
-NTSTATUS gensec_register(const struct gensec_security_ops *ops)
+_PUBLIC_ NTSTATUS gensec_register(const struct gensec_security_ops *ops)
 {
        if (gensec_security_by_name(NULL, ops->name) != NULL) {
                /* its already registered! */
@@ -826,13 +879,13 @@ NTSTATUS gensec_register(const struct gensec_security_ops *ops)
 
        generic_security_ops = talloc_realloc(talloc_autofree_context(),
                                              generic_security_ops,
-                                             struct gensec_security_ops *,
+                                             const struct gensec_security_ops *,
                                              gensec_num_backends+2);
        if (!generic_security_ops) {
                return NT_STATUS_NO_MEMORY;
        }
 
-       generic_security_ops[gensec_num_backends] = discard_const_p(struct gensec_security_ops, ops);
+       generic_security_ops[gensec_num_backends] = ops;
        gensec_num_backends++;
        generic_security_ops[gensec_num_backends] = NULL;
 
@@ -847,7 +900,7 @@ NTSTATUS gensec_register(const struct gensec_security_ops *ops)
   This can be used by backends to either detect compilation errors, or provide
   multiple implementations for different smbd compilation options in one module
 */
-const struct gensec_critical_sizes *gensec_interface_version(void)
+_PUBLIC_ const struct gensec_critical_sizes *gensec_interface_version(void)
 {
        static const struct gensec_critical_sizes critical_sizes = {
                GENSEC_INTERFACE_VERSION,
@@ -858,7 +911,7 @@ const struct gensec_critical_sizes *gensec_interface_version(void)
        return &critical_sizes;
 }
 
-static int sort_gensec(struct gensec_security_ops **gs1, struct gensec_security_ops **gs2) {
+static int sort_gensec(const struct gensec_security_ops **gs1, const struct gensec_security_ops **gs2) {
        return (*gs2)->priority - (*gs1)->priority;
 }
 
@@ -879,7 +932,7 @@ _PUBLIC_ NTSTATUS gensec_init(void)
 {
        static bool initialized = false;
 #define _MODULE_PROTO(init) extern NTSTATUS init(void);
-#if _SAMBA_BUILD_ == 4
+#ifdef STATIC_gensec_MODULES
        STATIC_gensec_MODULES_PROTO;
        init_module_fn static_init[] = { STATIC_gensec_MODULES };
 #else