CVE-2016-2111: auth/gensec: require DCERPC_AUTH_LEVEL_INTEGRITY or higher in schannel...
[samba.git] / auth / gensec / schannel.c
index 3d30e83b3a476af33cf0945b45ce5eaf41c06f09..be2e94e14d805715313d8c0e3771649262f52a7b 100644 (file)
@@ -38,6 +38,7 @@ struct schannel_state {
        uint64_t seq_num;
        bool initiator;
        struct netlogon_creds_CredentialState *creds;
+       struct auth_user_info_dc *user_info_dc;
 };
 
 #define SETUP_SEQNUM(state, buf, initiator) do { \
@@ -58,14 +59,13 @@ static struct schannel_state *netsec_create_state(
 {
        struct schannel_state *state;
 
-       state = talloc(gensec, struct schannel_state);
+       state = talloc_zero(gensec, struct schannel_state);
        if (state == NULL) {
                return NULL;
        }
 
        state->gensec = gensec;
        state->initiator = initiator;
-       state->seq_num = 0;
        state->creds = netlogon_creds_copy(state, creds);
        if (state->creds == NULL) {
                talloc_free(state);
@@ -459,7 +459,7 @@ static NTSTATUS schannel_update(struct gensec_security *gensec_security, TALLOC_
                struct schannel_state);
        NTSTATUS status;
        enum ndr_err_code ndr_err;
-       struct NL_AUTH_MESSAGE bind_schannel;
+       struct NL_AUTH_MESSAGE bind_schannel = {};
        struct NL_AUTH_MESSAGE bind_schannel_ack;
        struct netlogon_creds_CredentialState *creds;
        const char *workstation;
@@ -467,6 +467,16 @@ static NTSTATUS schannel_update(struct gensec_security *gensec_security, TALLOC_
 
        *out = data_blob(NULL, 0);
 
+       if (gensec_security->dcerpc_auth_level < DCERPC_AUTH_LEVEL_INTEGRITY) {
+               switch (gensec_security->gensec_role) {
+               case GENSEC_CLIENT:
+                       return NT_STATUS_INVALID_PARAMETER_MIX;
+               case GENSEC_SERVER:
+                       return NT_STATUS_INVALID_PARAMETER;
+               }
+               return NT_STATUS_INTERNAL_ERROR;
+       }
+
        switch (gensec_security->gensec_role) {
        case GENSEC_CLIENT:
                if (state != NULL) {
@@ -486,26 +496,19 @@ static NTSTATUS schannel_update(struct gensec_security *gensec_security, TALLOC_
                }
 
                bind_schannel.MessageType = NL_NEGOTIATE_REQUEST;
-#if 0
-               /* to support this we'd need to have access to the full domain name */
-               /* 0x17, 23 */
-               bind_schannel.Flags = NL_FLAG_OEM_NETBIOS_DOMAIN_NAME |
-                                     NL_FLAG_OEM_NETBIOS_COMPUTER_NAME |
-                                     NL_FLAG_UTF8_DNS_DOMAIN_NAME |
-                                     NL_FLAG_UTF8_NETBIOS_COMPUTER_NAME;
-               bind_schannel.oem_netbios_domain.a = cli_credentials_get_domain(gensec_security->credentials);
-               bind_schannel.oem_netbios_computer.a = creds->computer_name;
-               bind_schannel.utf8_dns_domain = cli_credentials_get_realm(gensec_security->credentials);
-               /* w2k3 refuses us if we use the full DNS workstation?
-                why? perhaps because we don't fill in the dNSHostName
-                attribute in the machine account? */
-               bind_schannel.utf8_netbios_computer = creds->computer_name;
-#else
+
                bind_schannel.Flags = NL_FLAG_OEM_NETBIOS_DOMAIN_NAME |
                                      NL_FLAG_OEM_NETBIOS_COMPUTER_NAME;
                bind_schannel.oem_netbios_domain.a = cli_credentials_get_domain(gensec_security->credentials);
                bind_schannel.oem_netbios_computer.a = creds->computer_name;
-#endif
+
+               if (creds->secure_channel_type == SEC_CHAN_DNS_DOMAIN) {
+                       bind_schannel.Flags |= NL_FLAG_UTF8_DNS_DOMAIN_NAME;
+                       bind_schannel.utf8_dns_domain.u = cli_credentials_get_realm(gensec_security->credentials);
+
+                       bind_schannel.Flags |= NL_FLAG_UTF8_NETBIOS_COMPUTER_NAME;
+                       bind_schannel.utf8_netbios_computer.u = creds->computer_name;
+               }
 
                ndr_err = ndr_push_struct_blob(out, out_mem_ctx, &bind_schannel,
                                               (ndr_push_flags_fn_t)ndr_push_NL_AUTH_MESSAGE);
@@ -580,6 +583,13 @@ static NTSTATUS schannel_update(struct gensec_security *gensec_security, TALLOC_
                        return NT_STATUS_NO_MEMORY;
                }
 
+               status = auth_anonymous_user_info_dc(state,
+                               lpcfg_netbios_name(gensec_security->settings->lp_ctx),
+                               &state->user_info_dc);
+               if (!NT_STATUS_IS_OK(status)) {
+                       return status;
+               }
+
                bind_schannel_ack.MessageType = NL_NEGOTIATE_RESPONSE;
                bind_schannel_ack.Flags = 0;
                bind_schannel_ack.Buffer.dummy = 0x6c0000; /* actually I think
@@ -610,7 +620,43 @@ static NTSTATUS schannel_session_info(struct gensec_security *gensec_security,
                                      TALLOC_CTX *mem_ctx,
                                      struct auth_session_info **_session_info)
 {
-       return auth_anonymous_session_info(mem_ctx, gensec_security->settings->lp_ctx, _session_info);
+       struct schannel_state *state =
+               talloc_get_type(gensec_security->private_data,
+               struct schannel_state);
+       struct auth4_context *auth_ctx = gensec_security->auth_context;
+       struct auth_session_info *session_info = NULL;
+       uint32_t session_info_flags = 0;
+       NTSTATUS status;
+
+       if (auth_ctx == NULL) {
+               DEBUG(0, ("Cannot generate a session_info without the auth_context\n"));
+               return NT_STATUS_INTERNAL_ERROR;
+       }
+
+       if (auth_ctx->generate_session_info == NULL) {
+               DEBUG(0, ("Cannot generate a session_info without the generate_session_info hook\n"));
+               return NT_STATUS_INTERNAL_ERROR;
+       }
+
+       if (gensec_security->want_features & GENSEC_FEATURE_UNIX_TOKEN) {
+               session_info_flags |= AUTH_SESSION_INFO_UNIX_TOKEN;
+       }
+
+       session_info_flags |= AUTH_SESSION_INFO_SIMPLE_PRIVILEGES;
+
+       status = auth_ctx->generate_session_info(
+                               auth_ctx,
+                               mem_ctx,
+                               state->user_info_dc,
+                               state->user_info_dc->info->account_name,
+                               session_info_flags,
+                               &session_info);
+       if (!NT_STATUS_IS_OK(status)) {
+               return status;
+       }
+
+       *_session_info = session_info;
+       return NT_STATUS_OK;
 }
 
 static NTSTATUS schannel_server_start(struct gensec_security *gensec_security)