<commit_before>// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements.  See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership.  The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License.  You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#ifndef __STOUT_OS_POSIX_SU_HPP__
#define __STOUT_OS_POSIX_SU_HPP__

#include <string>

#include <grp.h>
#include <pwd.h>

#include <stout/error.hpp>
#include <stout/nothing.hpp>
#include <stout/try.hpp>
#include <stout/unreachable.hpp>

namespace os {

inline Result<uid_t> getuid(const Option<std::string>& user = None())
{
  if (user.isNone()) {
    return ::getuid();
  }

  struct passwd passwd;
  struct passwd* result = nullptr;

  int size = sysconf(_SC_GETPW_R_SIZE_MAX);
  if (size == -1) {
    // Initial value for buffer size.
    size = 1024;
  }

  while (true) {
    char* buffer = new char[size];

    if (getpwnam_r(user.get().c_str(), &passwd, buffer, size, &result) == 0) {
      // The usual interpretation of POSIX is that getpwnam_r will
      // return 0 but set result == nullptr if the user is not found.
      if (result == nullptr) {
        delete[] buffer;
        return None();
      }

      uid_t uid = passwd.pw_uid;
      delete[] buffer;
      return uid;
    } else {
      // RHEL7 (and possibly other systems) will return non-zero and
      // set one of the following errors for "The given name or uid
      // was not found." See 'man getpwnam_r'. We only check for the
      // errors explicitly listed, and do not consider the ellipsis.
      if (errno == ENOENT ||
          errno == ESRCH ||
          errno == EBADF ||
          errno == EPERM) {
        delete[] buffer;
        return None();
      }

      if (errno != ERANGE) {
        delete[] buffer;
        return ErrnoError("Failed to get username information");
      }
      // getpwnam_r set ERANGE so try again with a larger buffer.
      size *= 2;
      delete[] buffer;
    }
  }

  UNREACHABLE();
}

inline Try<Nothing> setuid(uid_t uid)
{
  if (::setuid(uid) == -1) {
    return ErrnoError();
  }

  return Nothing();
}

inline Result<gid_t> getgid(const Option<std::string>& user = None())
{
  if (user.isNone()) {
    return ::getgid();
  }

  struct passwd passwd;
  struct passwd* result = nullptr;

  int size = sysconf(_SC_GETPW_R_SIZE_MAX);
  if (size == -1) {
    // Initial value for buffer size.
    size = 1024;
  }

  while (true) {
    char* buffer = new char[size];

    if (getpwnam_r(user.get().c_str(), &passwd, buffer, size, &result) == 0) {
      // The usual interpretation of POSIX is that getpwnam_r will
      // return 0 but set result == nullptr if the group is not found.
      if (result == nullptr) {
        delete[] buffer;
        return None();
      }

      gid_t gid = passwd.pw_gid;
      delete[] buffer;
      return gid;
    } else {
      // RHEL7 (and possibly other systems) will return non-zero and
      // set one of the following errors for "The given name or uid
      // was not found." See 'man getpwnam_r'. We only check for the
      // errors explicitly listed, and do not consider the ellipsis.
      if (errno == ENOENT ||
          errno == ESRCH ||
          errno == EBADF ||
          errno == EPERM) {
        delete[] buffer;
        return None();
      }

      if (errno != ERANGE) {
        delete[] buffer;
        return ErrnoError("Failed to get username information");
      }
      // getpwnam_r set ERANGE so try again with a larger buffer.
      size *= 2;
      delete[] buffer;
    }
  }

  UNREACHABLE();
}

inline Try<Nothing> setgid(gid_t gid)
{
  if (::setgid(gid) == -1) {
    return ErrnoError();
  }

  return Nothing();
}

inline Result<std::string> user(Option<uid_t> uid = None())
{
  if (uid.isNone()) {
    uid = ::getuid();
  }

  int size = sysconf(_SC_GETPW_R_SIZE_MAX);
  if (size == -1) {
    // Initial value for buffer size.
    size = 1024;
  }

  struct passwd passwd;
  struct passwd* result = nullptr;

  while (true) {
    char* buffer = new char[size];

    if (getpwuid_r(uid.get(), &passwd, buffer, size, &result) == 0) {
      // getpwuid_r will return 0 but set result == nullptr if the uid is
      // not found.
      if (result == nullptr) {
        delete[] buffer;
        return None();
      }

      std::string user(passwd.pw_name);
      delete[] buffer;
      return user;
    } else {
      if (errno != ERANGE) {
        delete[] buffer;
        return ErrnoError();
      }

      // getpwuid_r set ERANGE so try again with a larger buffer.
      size *= 2;
      delete[] buffer;
    }
  }
}

inline Try<Nothing> su(const std::string& user)
{
  Result<gid_t> gid = os::getgid(user);
  if (gid.isError() || gid.isNone()) {
    return Error("Failed to getgid: " +
        (gid.isError() ? gid.error() : "unknown user"));
  } else if (::setgid(gid.get())) {
    return ErrnoError("Failed to set gid");
  }

  // Set the supplementary group list. We ignore EPERM because
  // performing a no-op call (switching to same group) still
  // requires being privileged, unlike 'setgid' and 'setuid'.
  if (::initgroups(user.c_str(), gid.get()) == -1 && errno != EPERM) {
    return ErrnoError("Failed to set supplementary groups");
  }

  Result<uid_t> uid = os::getuid(user);
  if (uid.isError() || uid.isNone()) {
    return Error("Failed to getuid: " +
        (uid.isError() ? uid.error() : "unknown user"));
  } else if (::setuid(uid.get())) {
    return ErrnoError("Failed to setuid");
  }

  return Nothing();
}

} // namespace os {

#endif // __STOUT_OS_POSIX_SU_HPP__
<commit_msg>Added stout functions to get and set supplementary groud ids.<commit_after>// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements.  See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership.  The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License.  You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#ifndef __STOUT_OS_POSIX_SU_HPP__
#define __STOUT_OS_POSIX_SU_HPP__

#include <string>

#include <grp.h>
#include <pwd.h>

#include <sys/syscall.h>

#include <stout/error.hpp>
#include <stout/nothing.hpp>
#include <stout/try.hpp>
#include <stout/unreachable.hpp>

namespace os {

inline Result<uid_t> getuid(const Option<std::string>& user = None())
{
  if (user.isNone()) {
    return ::getuid();
  }

  struct passwd passwd;
  struct passwd* result = nullptr;

  int size = sysconf(_SC_GETPW_R_SIZE_MAX);
  if (size == -1) {
    // Initial value for buffer size.
    size = 1024;
  }

  while (true) {
    char* buffer = new char[size];

    if (getpwnam_r(user.get().c_str(), &passwd, buffer, size, &result) == 0) {
      // The usual interpretation of POSIX is that getpwnam_r will
      // return 0 but set result == nullptr if the user is not found.
      if (result == nullptr) {
        delete[] buffer;
        return None();
      }

      uid_t uid = passwd.pw_uid;
      delete[] buffer;
      return uid;
    } else {
      // RHEL7 (and possibly other systems) will return non-zero and
      // set one of the following errors for "The given name or uid
      // was not found." See 'man getpwnam_r'. We only check for the
      // errors explicitly listed, and do not consider the ellipsis.
      if (errno == ENOENT ||
          errno == ESRCH ||
          errno == EBADF ||
          errno == EPERM) {
        delete[] buffer;
        return None();
      }

      if (errno != ERANGE) {
        delete[] buffer;
        return ErrnoError("Failed to get username information");
      }
      // getpwnam_r set ERANGE so try again with a larger buffer.
      size *= 2;
      delete[] buffer;
    }
  }

  UNREACHABLE();
}

inline Try<Nothing> setuid(uid_t uid)
{
  if (::setuid(uid) == -1) {
    return ErrnoError();
  }

  return Nothing();
}

inline Result<gid_t> getgid(const Option<std::string>& user = None())
{
  if (user.isNone()) {
    return ::getgid();
  }

  struct passwd passwd;
  struct passwd* result = nullptr;

  int size = sysconf(_SC_GETPW_R_SIZE_MAX);
  if (size == -1) {
    // Initial value for buffer size.
    size = 1024;
  }

  while (true) {
    char* buffer = new char[size];

    if (getpwnam_r(user.get().c_str(), &passwd, buffer, size, &result) == 0) {
      // The usual interpretation of POSIX is that getpwnam_r will
      // return 0 but set result == nullptr if the group is not found.
      if (result == nullptr) {
        delete[] buffer;
        return None();
      }

      gid_t gid = passwd.pw_gid;
      delete[] buffer;
      return gid;
    } else {
      // RHEL7 (and possibly other systems) will return non-zero and
      // set one of the following errors for "The given name or uid
      // was not found." See 'man getpwnam_r'. We only check for the
      // errors explicitly listed, and do not consider the ellipsis.
      if (errno == ENOENT ||
          errno == ESRCH ||
          errno == EBADF ||
          errno == EPERM) {
        delete[] buffer;
        return None();
      }

      if (errno != ERANGE) {
        delete[] buffer;
        return ErrnoError("Failed to get username information");
      }
      // getpwnam_r set ERANGE so try again with a larger buffer.
      size *= 2;
      delete[] buffer;
    }
  }

  UNREACHABLE();
}

inline Try<Nothing> setgid(gid_t gid)
{
  if (::setgid(gid) == -1) {
    return ErrnoError();
  }

  return Nothing();
}

inline Try<std::vector<gid_t>> getgrouplist(const std::string& user)
{
  // TODO(jieyu): Consider adding a 'gid' parameter and avoid calling
  // getgid here. In some cases, the primary gid might be known.
  Result<gid_t> gid = os::getgid(user);
  if (!gid.isSome()) {
    return Error("Failed to get the gid of the user: " +
                 (gid.isError() ? gid.error() : "group not found"));
  }

#ifdef __APPLE__
  // TODO(gilbert): Instead of setting 'ngroups' as a large value,
  // we should figure out a way to probe 'ngroups' on OS X. Currently
  // neither '_SC_NGROUPS_MAX' nor 'NGROUPS_MAX' is appropriate,
  // because both are fixed as 16 on Darwin kernel, which is the
  // cache size.
  int ngroups = 65536;
  int gids[ngroups];
#else
  int ngroups = NGROUPS_MAX;
  gid_t gids[ngroups];
#endif
  if (::getgrouplist(user.c_str(), gid.get(), gids, &ngroups) == -1) {
    return ErrnoError();
  }

  return std::vector<gid_t>(gids, gids + ngroups);
}

inline Try<Nothing> setgroups(
    const std::vector<gid_t>& gids,
    const Option<uid_t>& uid = None())
{
  int ngroups = static_cast<int>(gids.size());
  gid_t _gids[ngroups];

  for (int i = 0; i < ngroups; i++) {
    _gids[i] = gids[i];
  }

#ifdef __APPLE__
  // Cannot simply call 'setgroups' here because it only updates
  // the list of groups in kernel cache, but not the ones in
  // opendirectoryd. Darwin kernel caches part of the groups in
  // kernel, and the rest in opendirectoryd.
  // For more detail please see:
  // https://github.com/practicalswift/osx/blob/master/src/samba/patches/support-darwin-initgroups-syscall // NOLINT
  int maxgroups = sysconf(_SC_NGROUPS_MAX);
  if (maxgroups == -1) {
    return Error("Failed to get sysconf(_SC_NGROUPS_MAX)");
  }

  if (ngroups > maxgroups) {
    ngroups = maxgroups;
  }

  if (uid.isNone()) {
    return Error(
        "The uid of the user who is associated with the group "
        "list we are setting is missing");
  }

  // NOTE: By default, the maxgroups on Darwin kernel is fixed
  // as 16. If we have more than 16 gids to set for a specific
  // user, then SYS_initgroups would send up to 16 of them to
  // kernel cache, while the rest would still be performed
  // correctly by the kernel (asking Directory Service to resolve
  // the groups membership).
  if (::syscall(SYS_initgroups, ngroups, _gids, uid.get()) == -1) {
    return ErrnoError();
  }
#else
  if (::setgroups(ngroups, _gids) == -1) {
    return ErrnoError();
  }
#endif

  return Nothing();
}

inline Result<std::string> user(Option<uid_t> uid = None())
{
  if (uid.isNone()) {
    uid = ::getuid();
  }

  int size = sysconf(_SC_GETPW_R_SIZE_MAX);
  if (size == -1) {
    // Initial value for buffer size.
    size = 1024;
  }

  struct passwd passwd;
  struct passwd* result = nullptr;

  while (true) {
    char* buffer = new char[size];

    if (getpwuid_r(uid.get(), &passwd, buffer, size, &result) == 0) {
      // getpwuid_r will return 0 but set result == nullptr if the uid is
      // not found.
      if (result == nullptr) {
        delete[] buffer;
        return None();
      }

      std::string user(passwd.pw_name);
      delete[] buffer;
      return user;
    } else {
      if (errno != ERANGE) {
        delete[] buffer;
        return ErrnoError();
      }

      // getpwuid_r set ERANGE so try again with a larger buffer.
      size *= 2;
      delete[] buffer;
    }
  }
}

inline Try<Nothing> su(const std::string& user)
{
  Result<gid_t> gid = os::getgid(user);
  if (gid.isError() || gid.isNone()) {
    return Error("Failed to getgid: " +
        (gid.isError() ? gid.error() : "unknown user"));
  } else if (::setgid(gid.get())) {
    return ErrnoError("Failed to set gid");
  }

  // Set the supplementary group list. We ignore EPERM because
  // performing a no-op call (switching to same group) still
  // requires being privileged, unlike 'setgid' and 'setuid'.
  if (::initgroups(user.c_str(), gid.get()) == -1 && errno != EPERM) {
    return ErrnoError("Failed to set supplementary groups");
  }

  Result<uid_t> uid = os::getuid(user);
  if (uid.isError() || uid.isNone()) {
    return Error("Failed to getuid: " +
        (uid.isError() ? uid.error() : "unknown user"));
  } else if (::setuid(uid.get())) {
    return ErrnoError("Failed to setuid");
  }

  return Nothing();
}

} // namespace os {

#endif // __STOUT_OS_POSIX_SU_HPP__
<|endoftext|>


<commit_before>/**
 * Copyright (c) 2014-present, The osquery authors
 *
 * This source code is licensed as defined by the LICENSE file found in the
 * root directory of this source tree.
 *
 * SPDX-License-Identifier: (Apache-2.0 OR GPL-2.0-only)
 */

#include <Windows.h>
#include <winevt.h>

#include <boost/algorithm/string.hpp>
#include <boost/property_tree/ptree.hpp>
#include <boost/property_tree/xml_parser.hpp>

#include <osquery/core/tables.h>
#include <osquery/logger/logger.h>

#include <osquery/core/windows/wmi.h>
#include <osquery/events/windows/windowseventlogparser.h>
#include <osquery/sql/dynamic_table_row.h>
#include <osquery/tables/system/windows/windows_eventlog.h>
#include <osquery/utils/conversions/join.h>
#include <osquery/utils/conversions/split.h>
#include <osquery/utils/conversions/windows/strings.h>

namespace pt = boost::property_tree;

namespace osquery {
namespace tables {

const std::string kEventLogXmlPrefix = "<QueryList><Query Id=\"0\">";
const std::string kEventLogXmlSuffix = "</Query></QueryList>";

const int kNumEventsBlock = 1024;

Status parseWelXml(QueryContext& context, std::wstring& xml_event, Row& row) {
  pt::ptree propTree;
  WELEvent windows_event;
  auto xml_status = parseWindowsEventLogXML(propTree, xml_event);
  if (!xml_status.ok()) {
    VLOG(1) << "Error parsing event log XML: " << xml_status.toString();
    return xml_status;
  }

  auto pt_status = parseWindowsEventLogPTree(windows_event, propTree);
  if (!pt_status.ok()) {
    VLOG(1) << "Error parsing event log PTree: " << pt_status.toString();
    return pt_status;
  }

  row["time"] = INTEGER(windows_event.osquery_time);
  row["datetime"] = SQL_TEXT(windows_event.datetime);
  row["channel"] = SQL_TEXT(windows_event.source);
  row["provider_name"] = SQL_TEXT(windows_event.provider_name);
  row["provider_guid"] = SQL_TEXT(windows_event.provider_guid);
  row["eventid"] = INTEGER(windows_event.event_id);
  row["task"] = INTEGER(windows_event.task_id);
  row["level"] = INTEGER(windows_event.level);
  row["pid"] = INTEGER(windows_event.pid);
  row["tid"] = INTEGER(windows_event.tid);

  row["keywords"] = SQL_TEXT(windows_event.keywords);
  row["data"] = SQL_TEXT(windows_event.data);

  if (context.hasConstraint("time_range", EQUALS)) {
    auto time_range = context.constraints["time_range"].getAll(EQUALS);
    row["time_range"] = SQL_TEXT(*time_range.begin());
  } else {
    row["time_range"] = SQL_TEXT("");
  }

  if (context.hasConstraint("timestamp", EQUALS)) {
    auto timestamp = context.constraints["timestamp"].getAll(EQUALS);
    row["timestamp"] = SQL_TEXT(*timestamp.begin());
  }

  if (context.hasConstraint("xpath", EQUALS)) {
    auto xpaths = context.constraints["xpath"].getAll(EQUALS);
    row["xpath"] = SQL_TEXT(*xpaths.begin());
  }

  return Status::success();
}

void renderQueryResults(QueryContext& context,
                        EVT_HANDLE queryResults,
                        RowYield& yield) {
  std::vector<EVT_HANDLE> events(kNumEventsBlock);
  unsigned long numEvents = 0;

  // Retrieve the events one block at a time
  auto ret = EvtNext(
      queryResults, kNumEventsBlock, events.data(), INFINITE, 0, &numEvents);
  while (ret != FALSE) {
    for (unsigned long i = 0; i < numEvents; i++) {
      unsigned long renderedBuffSize = 0;
      unsigned long renderedBuffUsed = 0;
      unsigned long propCount = 0;
      if (!EvtRender(nullptr,
                     events[i],
                     EvtRenderEventXml,
                     renderedBuffSize,
                     nullptr,
                     &renderedBuffUsed,
                     &propCount)) {
        if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
          LOG(WARNING) << "Failed to get the size of rendered event "
                       << GetLastError();
          EvtClose(events[i]);
          continue;
        }
      }

      std::vector<wchar_t> renderedContent(renderedBuffUsed);
      renderedBuffSize = renderedBuffUsed;
      if (!EvtRender(nullptr,
                     events[i],
                     EvtRenderEventXml,
                     renderedBuffSize,
                     renderedContent.data(),
                     &renderedBuffUsed,
                     &propCount)) {
        LOG(WARNING) << "Failed to render windows event with "
                     << GetLastError();
        EvtClose(events[i]);
        continue;
      }

      EvtClose(events[i]);

      Row row;
      std::wstringstream xml_event;
      xml_event << renderedContent.data();
      auto status = parseWelXml(context, xml_event.str(), row);
      if (status.ok()) {
        yield(TableRowHolder(new DynamicTableRow(std::move(row))));
      }
    }

    ret = EvtNext(
        queryResults, kNumEventsBlock, events.data(), INFINITE, 0, &numEvents);
  }
  if (ERROR_NO_MORE_ITEMS != GetLastError()) {
    // No need to close the handler after error; The query
    // EvtClose will also close all the event handler
    VLOG(1) << "EvtNext failed with error " << GetLastError();
  }
}

void genXfilterFromConstraints(QueryContext& context, std::string& xfilter) {
  std::vector<std::string> xfilterList;

  auto eids = context.constraints["eventid"].getAll(EQUALS);
  if (!eids.empty()) {
    xfilterList.emplace_back(
        "(EventID=" + osquery::join(eids, ") or (EventID=") + ")");
  }

  auto pids = context.constraints["pid"].getAll(EQUALS);
  if (!pids.empty()) {
    xfilterList.emplace_back(
        "(Execution[@ProcessID=" +
        osquery::join(pids, "]) or (Execution[@ProcessID=") + "])");
  }

  auto times = context.constraints["time_range"].getAll(EQUALS);
  auto timestamps = context.constraints["timestamp"].getAll(EQUALS);
  if (!times.empty()) {
    auto datetime = *times.begin();
    auto time_vec = osquery::split(datetime, ";");

    if (time_vec.size() == 1) {
      auto _start = time_vec.front();
      xfilterList.emplace_back("TimeCreated[@SystemTime>='" + _start + "']");
    } else if (time_vec.size() == 2) {
      auto _start = time_vec.front();
      auto _end = time_vec.at(1);
      xfilterList.emplace_back("TimeCreated[@SystemTime>='" + _start +
                               "' and @SystemTime<='" + _end + "']");
    }
  } else if (!timestamps.empty()) {
    auto time_diff = *timestamps.begin();
    xfilterList.emplace_back(
        "TimeCreated[timediff(@SystemTime) <= " + time_diff + "]");
  }

  xfilter = xfilterList.empty()
                ? "*"
                : "*[System[" + osquery::join(xfilterList, " and ") + "]]";
}

bool shouldHandleXpath(QueryContext& context) {
  // xpaths are mutually execlusive and can't be used with itself
  // and the other constraints
  auto xpaths = context.constraints["xpath"].getAll(EQUALS);
  if (xpaths.size() > 1) {
    return false;
  }

  return !(context.hasConstraint("channel", EQUALS) ||
           context.hasConstraint("time_range", EQUALS) ||
           context.hasConstraint("timestamp", EQUALS));
}

void genWindowsEventLog(RowYield& yield, QueryContext& context) {
  std::set<std::pair<std::string, std::string>> xpath_set;
  auto hasXpath = context.hasConstraint("xpath", EQUALS);

  if (hasXpath && !shouldHandleXpath(context)) {
    LOG(WARNING) << "Xpaths are mutually exclusive and cannot be "
                    "used with constraints (channel, time_range, timestamp)";
    return;
  }

  // Check if the `xpath` constraint is available and query
  // the events with xpath
  if (hasXpath) {
    auto xpaths = context.constraints["xpath"].getAll(EQUALS);
    auto xpath = *xpaths.begin();
    pt::ptree propTree;
    std::stringstream ss;
    ss << xpath;
    pt::read_xml(ss, propTree);
    auto channel = propTree.get("QueryList.Query.Select.<xmlattr>.Path", "");
    if (!channel.empty()) {
      xpath_set.insert(std::make_pair(channel, xpath));
    } else {
      LOG(WARNING) << "Invalid xpath format: " << xpath;
    }

  } else if (context.hasConstraint("channel", EQUALS)) {
    auto channels = context.constraints["channel"].getAll(EQUALS);
    std::string xfilter("");
    genXfilterFromConstraints(context, xfilter);
    std::string welSearchQuery = kEventLogXmlPrefix;

    for (const auto& channel : channels) {
      welSearchQuery += "<Select Path=\"" + channel + "\">";
      welSearchQuery += xfilter;
      welSearchQuery += "</Select>" + kEventLogXmlSuffix;
      xpath_set.insert(std::make_pair(channel, welSearchQuery));
    }

  } else {
    LOG(WARNING) << "Query constraints are invalid: the event "
                    "channel or xpath must be specified";
    return;
  }

  for (const auto& path : xpath_set) {
    auto queryResults =
        EvtQuery(nullptr,
                 stringToWstring(path.first).c_str(),
                 stringToWstring(path.second).c_str(),
                 EvtQueryChannelPath | EvtQueryReverseDirection);

    if (queryResults == nullptr) {
      LOG(WARNING) << "Failed to search event log for query with "
                   << GetLastError();
      return;
    }

    renderQueryResults(context, queryResults, yield);
    EvtClose(queryResults);
  }
}

}; // namespace tables
}; // namespace osquery
<commit_msg>Fixed handling of invalid array bound error with EvtNext function (#6660)<commit_after>/**
 * Copyright (c) 2014-present, The osquery authors
 *
 * This source code is licensed as defined by the LICENSE file found in the
 * root directory of this source tree.
 *
 * SPDX-License-Identifier: (Apache-2.0 OR GPL-2.0-only)
 */

#include <Windows.h>
#include <winevt.h>

#include <boost/algorithm/string.hpp>
#include <boost/property_tree/ptree.hpp>
#include <boost/property_tree/xml_parser.hpp>

#include <osquery/core/tables.h>
#include <osquery/logger/logger.h>

#include <osquery/core/windows/wmi.h>
#include <osquery/events/windows/windowseventlogparser.h>
#include <osquery/sql/dynamic_table_row.h>
#include <osquery/tables/system/windows/windows_eventlog.h>
#include <osquery/utils/conversions/join.h>
#include <osquery/utils/conversions/split.h>
#include <osquery/utils/conversions/windows/strings.h>

namespace pt = boost::property_tree;

namespace osquery {
namespace tables {

const std::string kEventLogXmlPrefix = "<QueryList><Query Id=\"0\">";
const std::string kEventLogXmlSuffix = "</Query></QueryList>";

Status parseWelXml(QueryContext& context, std::wstring& xml_event, Row& row) {
  pt::ptree propTree;
  WELEvent windows_event;
  auto xml_status = parseWindowsEventLogXML(propTree, xml_event);
  if (!xml_status.ok()) {
    VLOG(1) << "Error parsing event log XML: " << xml_status.toString();
    return xml_status;
  }

  auto pt_status = parseWindowsEventLogPTree(windows_event, propTree);
  if (!pt_status.ok()) {
    VLOG(1) << "Error parsing event log PTree: " << pt_status.toString();
    return pt_status;
  }

  row["time"] = INTEGER(windows_event.osquery_time);
  row["datetime"] = SQL_TEXT(windows_event.datetime);
  row["channel"] = SQL_TEXT(windows_event.source);
  row["provider_name"] = SQL_TEXT(windows_event.provider_name);
  row["provider_guid"] = SQL_TEXT(windows_event.provider_guid);
  row["eventid"] = INTEGER(windows_event.event_id);
  row["task"] = INTEGER(windows_event.task_id);
  row["level"] = INTEGER(windows_event.level);
  row["pid"] = INTEGER(windows_event.pid);
  row["tid"] = INTEGER(windows_event.tid);

  row["keywords"] = SQL_TEXT(windows_event.keywords);
  row["data"] = SQL_TEXT(windows_event.data);

  if (context.hasConstraint("time_range", EQUALS)) {
    auto time_range = context.constraints["time_range"].getAll(EQUALS);
    row["time_range"] = SQL_TEXT(*time_range.begin());
  } else {
    row["time_range"] = SQL_TEXT("");
  }

  if (context.hasConstraint("timestamp", EQUALS)) {
    auto timestamp = context.constraints["timestamp"].getAll(EQUALS);
    row["timestamp"] = SQL_TEXT(*timestamp.begin());
  }

  if (context.hasConstraint("xpath", EQUALS)) {
    auto xpaths = context.constraints["xpath"].getAll(EQUALS);
    row["xpath"] = SQL_TEXT(*xpaths.begin());
  }

  return Status::success();
}

void renderQueryResults(QueryContext& context,
                        EVT_HANDLE queryResults,
                        RowYield& yield) {
  uint32_t numEventsBlock = 1024;
  uint32_t position = 0;
  std::vector<EVT_HANDLE> events(numEventsBlock);

  // The batch size should be more than 32. It is not documented
  // but `EvtNext` should not fail (RPC_S_INVALID_BOUND error)
  // with low batch size.
  while (numEventsBlock > 32) {
    unsigned long numEvents = 0;
    // Retrieve the events one block at a time
    auto ret = EvtNext(
        queryResults, numEventsBlock, events.data(), INFINITE, 0, &numEvents);
    while (ret != FALSE) {
      for (unsigned long i = 0; i < numEvents; i++) {
        unsigned long renderedBuffSize = 0;
        unsigned long renderedBuffUsed = 0;
        unsigned long propCount = 0;
        position += 1;
        if (!EvtRender(nullptr,
                       events[i],
                       EvtRenderEventXml,
                       renderedBuffSize,
                       nullptr,
                       &renderedBuffUsed,
                       &propCount)) {
          if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
            LOG(WARNING) << "Failed to get the size of rendered event "
                         << GetLastError();
            EvtClose(events[i]);
            continue;
          }
        }

        std::vector<wchar_t> renderedContent(renderedBuffUsed);
        renderedBuffSize = renderedBuffUsed;
        if (!EvtRender(nullptr,
                       events[i],
                       EvtRenderEventXml,
                       renderedBuffSize,
                       renderedContent.data(),
                       &renderedBuffUsed,
                       &propCount)) {
          LOG(WARNING) << "Failed to render windows event with "
                       << GetLastError();
          EvtClose(events[i]);
          continue;
        }

        EvtClose(events[i]);

        Row row;
        std::wstringstream xml_event;
        xml_event << renderedContent.data();
        auto status = parseWelXml(context, xml_event.str(), row);
        if (status.ok()) {
          yield(TableRowHolder(new DynamicTableRow(std::move(row))));
        }
      }

      ret = EvtNext(
          queryResults, numEventsBlock, events.data(), INFINITE, 0, &numEvents);
    }

    // While reading a batch of large event log reports `EvtNext` may
    // fail with error code 1734 (RPC_S_INVALID_BOUND) and loose the
    // chunk of events. This is an unusual behavior and not documented.
    // The fix reduces the batch size to half and retries `EvtNext`
    if (RPC_S_INVALID_BOUND == GetLastError()) {
      numEventsBlock = numEventsBlock / 2;

      // Resize the events vector to the current batch size
      events.resize(numEventsBlock);

      // `EvtNext` may update the event position in query handler on
      // failure with RPC_S_INVALID_BOUND error. `EvtSeek` reset the
      // position before calling EvtNext with lower batch size.
      if (!EvtSeek(
              queryResults, position, nullptr, 0, EvtSeekRelativeToFirst)) {
        VLOG(1) << "EvtSeek failed with error " << GetLastError();
      }
      continue;
    }
    break;
  }

  if (ERROR_NO_MORE_ITEMS != GetLastError()) {
    // No need to close the handler after error; The query
    // EvtClose will also close all the event handler
    VLOG(1) << "EvtNext failed with error " << GetLastError();
  }
}

void genXfilterFromConstraints(QueryContext& context, std::string& xfilter) {
  std::vector<std::string> xfilterList;

  auto eids = context.constraints["eventid"].getAll(EQUALS);
  if (!eids.empty()) {
    xfilterList.emplace_back(
        "(EventID=" + osquery::join(eids, ") or (EventID=") + ")");
  }

  auto pids = context.constraints["pid"].getAll(EQUALS);
  if (!pids.empty()) {
    xfilterList.emplace_back(
        "(Execution[@ProcessID=" +
        osquery::join(pids, "]) or (Execution[@ProcessID=") + "])");
  }

  auto times = context.constraints["time_range"].getAll(EQUALS);
  auto timestamps = context.constraints["timestamp"].getAll(EQUALS);
  if (!times.empty()) {
    auto datetime = *times.begin();
    auto time_vec = osquery::split(datetime, ";");

    if (time_vec.size() == 1) {
      auto _start = time_vec.front();
      xfilterList.emplace_back("TimeCreated[@SystemTime>='" + _start + "']");
    } else if (time_vec.size() == 2) {
      auto _start = time_vec.front();
      auto _end = time_vec.at(1);
      xfilterList.emplace_back("TimeCreated[@SystemTime>='" + _start +
                               "' and @SystemTime<='" + _end + "']");
    }
  } else if (!timestamps.empty()) {
    auto time_diff = *timestamps.begin();
    xfilterList.emplace_back(
        "TimeCreated[timediff(@SystemTime) <= " + time_diff + "]");
  }

  xfilter = xfilterList.empty()
                ? "*"
                : "*[System[" + osquery::join(xfilterList, " and ") + "]]";
}

bool shouldHandleXpath(QueryContext& context) {
  // xpaths are mutually execlusive and can't be used with itself
  // and the other constraints
  auto xpaths = context.constraints["xpath"].getAll(EQUALS);
  if (xpaths.size() > 1) {
    return false;
  }

  return !(context.hasConstraint("channel", EQUALS) ||
           context.hasConstraint("time_range", EQUALS) ||
           context.hasConstraint("timestamp", EQUALS));
}

void genWindowsEventLog(RowYield& yield, QueryContext& context) {
  std::set<std::pair<std::string, std::string>> xpath_set;
  auto hasXpath = context.hasConstraint("xpath", EQUALS);

  if (hasXpath && !shouldHandleXpath(context)) {
    LOG(WARNING) << "Xpaths are mutually exclusive and cannot be "
                    "used with constraints (channel, time_range, timestamp)";
    return;
  }

  // Check if the `xpath` constraint is available and query
  // the events with xpath
  if (hasXpath) {
    auto xpaths = context.constraints["xpath"].getAll(EQUALS);
    auto xpath = *xpaths.begin();
    try {
      pt::ptree propTree;
      std::stringstream ss;
      ss << xpath;
      pt::read_xml(ss, propTree);
      auto channel = propTree.get("QueryList.Query.Select.<xmlattr>.Path", "");
      if (!channel.empty()) {
        xpath_set.insert(std::make_pair(channel, xpath));
      } else {
        LOG(WARNING) << "Invalid xpath format: " << xpath;
      }
    } catch (std::exception& e) {
      LOG(WARNING) << "Failed to parse the xpath xml string " << e.what();
      return;
    }

  } else if (context.hasConstraint("channel", EQUALS)) {
    auto channels = context.constraints["channel"].getAll(EQUALS);
    std::string xfilter("");
    genXfilterFromConstraints(context, xfilter);
    std::string welSearchQuery = kEventLogXmlPrefix;

    for (const auto& channel : channels) {
      welSearchQuery += "<Select Path=\"" + channel + "\">";
      welSearchQuery += xfilter;
      welSearchQuery += "</Select>" + kEventLogXmlSuffix;
      xpath_set.insert(std::make_pair(channel, welSearchQuery));
    }

  } else {
    LOG(WARNING) << "Query constraints are invalid: the event "
                    "channel or xpath must be specified";
    return;
  }

  for (const auto& path : xpath_set) {
    auto queryResults =
        EvtQuery(nullptr,
                 stringToWstring(path.first).c_str(),
                 stringToWstring(path.second).c_str(),
                 EvtQueryChannelPath | EvtQueryReverseDirection);

    if (queryResults == nullptr) {
      LOG(WARNING) << "Failed to search event log for query with "
                   << GetLastError();
      return;
    }

    renderQueryResults(context, queryResults, yield);
    EvtClose(queryResults);
  }
}

}; // namespace tables
}; // namespace osquery
<|endoftext|>


<commit_before>/*
 * SVN.cpp : Implementation of CSVNWorkingCopy
 *
 * ====================================================================
 * Copyright (c) 2000-2002 CollabNet.  All rights reserved.
 *
 * This software is licensed as described in the file COPYING, which
 * you should have received as part of this distribution.  The terms
 * are also available at http://subversion.tigris.org/license-1.html.
 * If newer versions of this license are posted there, you may use a
 * newer version instead, at your option.
 *
 * This software consists of voluntary contributions made by many
 * individuals.  For exact contribution history, see the revision
 * history and logs, available at http://subversion.tigris.org/.
 * ====================================================================
 */

#include "stdafx.h"

#include "SVNCOM.h"
#include "SVN.h"
#include "misc.h"
#include "SVNStatus.h"

/////////////////////////////////////////////////////////////////////////////
// CSVNWorkingCopy

STDMETHODIMP
CSVNWorkingCopy::InterfaceSupportsErrorInfo(REFIID riid)
{
	static const IID* arr[] =
	{
		&IID_ISVNWorkingCopy
	};
	for (int i=0; i < sizeof(arr) / sizeof(arr[0]); i++)
	{
		if (::ATL::InlineIsEqualGUID(*arr[i],riid))
			return S_OK;
	}
	return S_FALSE;
}

//
// Sets pfIsValid to VARIANT_TRUE if the directory contains
// valid SVN meta-data.
//
STDMETHODIMP
CSVNWorkingCopy::check_wc(BSTR bstrDir, VARIANT_BOOL *pfIsValid)
{
	USES_CONVERSION;
	HRESULT hr;
	svn_error_t *error;
	svn_boolean_t is_wc;
	svn_stringbuf_t *path;

	if (pfIsValid == NULL)
		return E_POINTER;

	path = svn_stringbuf_create(W2A(bstrDir), g_pool);

	error = svn_wc_check_wc(path, &is_wc, g_pool);

	if (error) {
		hr = convert_err_to_hresult(error);
		goto Cleanup;
	}

	if (is_wc)
		*pfIsValid = VARIANT_TRUE;
	else
		*pfIsValid = VARIANT_FALSE;

	hr = S_OK;

Cleanup:
	svn_pool_clear(g_pool);
	return hr;
}

//
// Create a secondary thread to watch for file changes
// in the specified directory (bstrDir).
// This secondary thread fires "RefreshFiles" when
// a change is detected.
//
STDMETHODIMP
CSVNWorkingCopy::watch_dir(BSTR bstrDir)
{
	USES_CONVERSION;
	HRESULT hr;
	CHAR *psz;

	EnterCriticalSection(&_csNewDir);

	if (_hFindNotification_Stop == NULL)
	{
		_hFindNotification_NewDir = CreateEvent(
			NULL, FALSE, FALSE, NULL);
		if (_hFindNotification_NewDir == NULL)
		{
			hr = E_FAIL;
			goto Error;
		}

		_hFindNotification_Stop = CreateEvent(
			NULL, FALSE, FALSE, NULL);
		if (_hFindNotification_Stop == NULL)
		{
			hr = E_FAIL;
			goto Error;
		}

		_hFindNotification_Thread = (HANDLE)_beginthread(
			FileNotificationThreadProc,
			0,
			this);
		if (_hFindNotification_Thread == NULL)
		{
			hr = E_FAIL;
			goto Cleanup;
		}
	}
	else
	{
		// Free previous directory.
		free(_pszNotification_Dir);
	}

	// Setup new direcotry
	psz = W2A(bstrDir);
	_pszNotification_Dir = (char *)malloc(strlen(psz) + 1);
	strcpy(_pszNotification_Dir, psz);

	// Tell the thread that we have a directory to care about.
	SetEvent(_hFindNotification_NewDir);

	hr = S_OK;
Cleanup:
	LeaveCriticalSection(&_csNewDir);
	return hr;
Error:
	Assert(FAILED(hr));
	if (_hFindNotification_Stop != NULL)
		CloseHandle(_hFindNotification_Stop);
	if (_hFindNotification_NewDir != NULL)
		CloseHandle(_hFindNotification_NewDir);
	goto Cleanup;
}

// Pump any waiting messages in the message queue.
static void
PumpWaitingMessages(void)
{
	MSG msg;

	//
	// Read all of the messages in this next loop,
	// removing each message as we read it.
	//
	while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
	{
		//
		// If it's a quit message, we're out of here.
		//
		if (msg.message == WM_QUIT)
		{
			break;
		}

		//
		// Otherwise, dispatch the message.
		//
		DispatchMessage(&msg);
	}
}

//
// Thread procedure for the file/directory notification
// thread. This thread is created for the first time when
// some calls watch_dir.
//
void
CSVNWorkingCopy::FileNotificationThreadProc(void *pv)
{
	USES_CONVERSION;
	CSVNWorkingCopy *wc;
	// Keep track of the current directory we're watching.
	CHAR *pszCurrentDir = NULL;
	HANDLE aHandles[3];
	DWORD dw;
	CComBSTR sbstr;

	// Initialize COM on this thread
	if (FAILED(CoInitializeEx(NULL, COINIT_MULTITHREADED)))
		abort();

	wc = static_cast<CSVNWorkingCopy *>(pv);

	// Wait for initial all clear.
	WaitForSingleObject(wc->_hFindNotification_NewDir, INFINITE);

	aHandles[1] = wc->_hFindNotification_NewDir;
	aHandles[2] = wc->_hFindNotification_Stop;

NewDirectory:
	if (wc->_hFindNotification != NULL)
	{
		FindCloseChangeNotification(wc->_hFindNotification);
	}

	// Enter critical section to lock down _pszNotification_Dir
	EnterCriticalSection(&wc->_csNewDir);

	if (pszCurrentDir != NULL)
	{
		free(pszCurrentDir);
	}

	pszCurrentDir = (CHAR *)malloc(strlen(wc->_pszNotification_Dir) + 1);
	if (pszCurrentDir == NULL)
	{
		abort();
	}

	strcpy(pszCurrentDir, wc->_pszNotification_Dir);

	// Leave the critical section.
	LeaveCriticalSection(&wc->_csNewDir);

	sbstr.Empty();
	sbstr = A2BSTR(pszCurrentDir);

	wc->_hFindNotification = FindFirstChangeNotification(
		pszCurrentDir, FALSE,
		FILE_NOTIFY_CHANGE_FILE_NAME |
		FILE_NOTIFY_CHANGE_LAST_WRITE);
	if (wc->_hFindNotification == INVALID_HANDLE_VALUE)
	{
		abort();
	}

	aHandles[0] = wc->_hFindNotification;

	while (1)
	{
		dw = MsgWaitForMultipleObjects(
			3, aHandles,
			FALSE, INFINITE, QS_ALLINPUT);
		switch (dw)
		{
		// Change notification
		case WAIT_OBJECT_0:
			wc->Fire_RefreshFiles(sbstr);
			if (!FindNextChangeNotification(wc->_hFindNotification))
				abort();
			continue;
		// New directory
		case WAIT_OBJECT_0 + 1:
			goto NewDirectory;
		// End Thread
		case WAIT_OBJECT_0 + 2:
			_endthread();
		// Since this thread is COM enabled,
		// we have to be good citizens, and
		// pump any incoming messages to
		// our thread, so that we prevent broadcasted
		// SendMessage calls from hanging.
		case WAIT_OBJECT_0 + 3:
			PumpWaitingMessages();
			continue;
		}
	}
}

STDMETHODIMP
CSVNWorkingCopy::wc_statuses(BSTR bstrPath, VARIANT_BOOL getAll, SAFEARRAY **ppsa)
{
	USES_CONVERSION;
	HRESULT hr;
	apr_hash_t *hash;
	svn_error_t *error;
	svn_wc_status_t *status;
	svn_stringbuf_t *path;
	apr_hash_index_t *hi;
	SAFEARRAY *psa;
	SAFEARRAYBOUND rgsBound;
	IDispatch **paDisp;
	apr_ssize_t klen;
	int i;
	apr_size_t count;
	CHAR *pszKey;
	CComObject<CSVNStatus> *com_status = NULL;
	svn_boolean_t fLockedSA = FALSE;
        svn_boolean_t get_all = (getAll != VARIANT_TRUE ? FALSE : TRUE);

	hash = apr_hash_make(g_pool);
	path = svn_stringbuf_create(W2A(bstrPath), g_pool);
	error = svn_wc_statuses(hash, path, FALSE, get_all, g_pool);
	if (error) {
		hr = convert_err_to_hresult(error);
		goto Cleanup;
	}

	count = apr_hash_count(hash);
	rgsBound.cElements = count;
	rgsBound.lLbound = 0;

	psa = SafeArrayCreate(VT_DISPATCH, 1, &rgsBound);
	if (psa == NULL)
		abort();

	hr = SafeArrayAccessData(psa, (void **)&paDisp);
	if (FAILED(hr))
		goto Cleanup;
	fLockedSA = TRUE;

	for (i = 0, hi = apr_hash_first(g_pool, hash); hi;
									 i++, hi = apr_hash_next(hi)) {
		apr_hash_this(hi, (const void **)&pszKey, &klen, (void **)&status);
		hr = CComObject<CSVNStatus>::CreateInstance(&com_status);
		if (FAILED(hr))
			goto Cleanup;
		// This is what we want since the hash key is an absolute path.
		pszKey = ((svn_stringbuf_t *)apr_hash_get (status->entry->attributes,
							   (const void *)SVN_WC_ENTRY_ATTR_NAME,
							   strlen(SVN_WC_ENTRY_ATTR_NAME)))->data;
		// SVN_WC_ENTRY_THIS_DIR is ".", we don't care about its status.
		if (strcmp(pszKey, SVN_WC_ENTRY_THIS_DIR) == 0) {
			delete com_status;
			com_status = NULL;
			// Pretend the iteration didn't happen.
			i--;
			continue;
		}
		hr = com_status->init(status, pszKey);
		if (FAILED(hr))
			goto Cleanup;
		hr = com_status->QueryInterface(&paDisp[i]);
		if (FAILED(hr))
			goto Cleanup;

		// NULL out the local variable,
		// the COM reference count is now owned by paDisp[i]
		com_status = NULL;
	}

	SafeArrayUnaccessData(psa);
	fLockedSA = FALSE;

	if (rgsBound.cElements > (UINT)i) {
		rgsBound.cElements = i;
		hr = SafeArrayRedim(psa, &rgsBound);
		if (FAILED(hr))
			goto Cleanup;
	}

	*ppsa = psa;

	hr = S_OK;
Cleanup:
	if (fLockedSA)
		SafeArrayUnaccessData(psa);
	delete com_status;
	svn_pool_clear(g_pool);
	return hr;
}
<commit_msg>* subversion/clients/win32/svn_com/SVN.cpp (CSVNWorkingCopy::wc_statuses): Align with new signature of svn_wc_statuses.<commit_after>/*
 * SVN.cpp : Implementation of CSVNWorkingCopy
 *
 * ====================================================================
 * Copyright (c) 2000-2002 CollabNet.  All rights reserved.
 *
 * This software is licensed as described in the file COPYING, which
 * you should have received as part of this distribution.  The terms
 * are also available at http://subversion.tigris.org/license-1.html.
 * If newer versions of this license are posted there, you may use a
 * newer version instead, at your option.
 *
 * This software consists of voluntary contributions made by many
 * individuals.  For exact contribution history, see the revision
 * history and logs, available at http://subversion.tigris.org/.
 * ====================================================================
 */

#include "stdafx.h"

#include "SVNCOM.h"
#include "SVN.h"
#include "misc.h"
#include "SVNStatus.h"

/////////////////////////////////////////////////////////////////////////////
// CSVNWorkingCopy

STDMETHODIMP
CSVNWorkingCopy::InterfaceSupportsErrorInfo(REFIID riid)
{
	static const IID* arr[] =
	{
		&IID_ISVNWorkingCopy
	};
	for (int i=0; i < sizeof(arr) / sizeof(arr[0]); i++)
	{
		if (::ATL::InlineIsEqualGUID(*arr[i],riid))
			return S_OK;
	}
	return S_FALSE;
}

//
// Sets pfIsValid to VARIANT_TRUE if the directory contains
// valid SVN meta-data.
//
STDMETHODIMP
CSVNWorkingCopy::check_wc(BSTR bstrDir, VARIANT_BOOL *pfIsValid)
{
	USES_CONVERSION;
	HRESULT hr;
	svn_error_t *error;
	svn_boolean_t is_wc;
	svn_stringbuf_t *path;

	if (pfIsValid == NULL)
		return E_POINTER;

	path = svn_stringbuf_create(W2A(bstrDir), g_pool);

	error = svn_wc_check_wc(path, &is_wc, g_pool);

	if (error) {
		hr = convert_err_to_hresult(error);
		goto Cleanup;
	}

	if (is_wc)
		*pfIsValid = VARIANT_TRUE;
	else
		*pfIsValid = VARIANT_FALSE;

	hr = S_OK;

Cleanup:
	svn_pool_clear(g_pool);
	return hr;
}

//
// Create a secondary thread to watch for file changes
// in the specified directory (bstrDir).
// This secondary thread fires "RefreshFiles" when
// a change is detected.
//
STDMETHODIMP
CSVNWorkingCopy::watch_dir(BSTR bstrDir)
{
	USES_CONVERSION;
	HRESULT hr;
	CHAR *psz;

	EnterCriticalSection(&_csNewDir);

	if (_hFindNotification_Stop == NULL)
	{
		_hFindNotification_NewDir = CreateEvent(
			NULL, FALSE, FALSE, NULL);
		if (_hFindNotification_NewDir == NULL)
		{
			hr = E_FAIL;
			goto Error;
		}

		_hFindNotification_Stop = CreateEvent(
			NULL, FALSE, FALSE, NULL);
		if (_hFindNotification_Stop == NULL)
		{
			hr = E_FAIL;
			goto Error;
		}

		_hFindNotification_Thread = (HANDLE)_beginthread(
			FileNotificationThreadProc,
			0,
			this);
		if (_hFindNotification_Thread == NULL)
		{
			hr = E_FAIL;
			goto Cleanup;
		}
	}
	else
	{
		// Free previous directory.
		free(_pszNotification_Dir);
	}

	// Setup new direcotry
	psz = W2A(bstrDir);
	_pszNotification_Dir = (char *)malloc(strlen(psz) + 1);
	strcpy(_pszNotification_Dir, psz);

	// Tell the thread that we have a directory to care about.
	SetEvent(_hFindNotification_NewDir);

	hr = S_OK;
Cleanup:
	LeaveCriticalSection(&_csNewDir);
	return hr;
Error:
	Assert(FAILED(hr));
	if (_hFindNotification_Stop != NULL)
		CloseHandle(_hFindNotification_Stop);
	if (_hFindNotification_NewDir != NULL)
		CloseHandle(_hFindNotification_NewDir);
	goto Cleanup;
}

// Pump any waiting messages in the message queue.
static void
PumpWaitingMessages(void)
{
	MSG msg;

	//
	// Read all of the messages in this next loop,
	// removing each message as we read it.
	//
	while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
	{
		//
		// If it's a quit message, we're out of here.
		//
		if (msg.message == WM_QUIT)
		{
			break;
		}

		//
		// Otherwise, dispatch the message.
		//
		DispatchMessage(&msg);
	}
}

//
// Thread procedure for the file/directory notification
// thread. This thread is created for the first time when
// some calls watch_dir.
//
void
CSVNWorkingCopy::FileNotificationThreadProc(void *pv)
{
	USES_CONVERSION;
	CSVNWorkingCopy *wc;
	// Keep track of the current directory we're watching.
	CHAR *pszCurrentDir = NULL;
	HANDLE aHandles[3];
	DWORD dw;
	CComBSTR sbstr;

	// Initialize COM on this thread
	if (FAILED(CoInitializeEx(NULL, COINIT_MULTITHREADED)))
		abort();

	wc = static_cast<CSVNWorkingCopy *>(pv);

	// Wait for initial all clear.
	WaitForSingleObject(wc->_hFindNotification_NewDir, INFINITE);

	aHandles[1] = wc->_hFindNotification_NewDir;
	aHandles[2] = wc->_hFindNotification_Stop;

NewDirectory:
	if (wc->_hFindNotification != NULL)
	{
		FindCloseChangeNotification(wc->_hFindNotification);
	}

	// Enter critical section to lock down _pszNotification_Dir
	EnterCriticalSection(&wc->_csNewDir);

	if (pszCurrentDir != NULL)
	{
		free(pszCurrentDir);
	}

	pszCurrentDir = (CHAR *)malloc(strlen(wc->_pszNotification_Dir) + 1);
	if (pszCurrentDir == NULL)
	{
		abort();
	}

	strcpy(pszCurrentDir, wc->_pszNotification_Dir);

	// Leave the critical section.
	LeaveCriticalSection(&wc->_csNewDir);

	sbstr.Empty();
	sbstr = A2BSTR(pszCurrentDir);

	wc->_hFindNotification = FindFirstChangeNotification(
		pszCurrentDir, FALSE,
		FILE_NOTIFY_CHANGE_FILE_NAME |
		FILE_NOTIFY_CHANGE_LAST_WRITE);
	if (wc->_hFindNotification == INVALID_HANDLE_VALUE)
	{
		abort();
	}

	aHandles[0] = wc->_hFindNotification;

	while (1)
	{
		dw = MsgWaitForMultipleObjects(
			3, aHandles,
			FALSE, INFINITE, QS_ALLINPUT);
		switch (dw)
		{
		// Change notification
		case WAIT_OBJECT_0:
			wc->Fire_RefreshFiles(sbstr);
			if (!FindNextChangeNotification(wc->_hFindNotification))
				abort();
			continue;
		// New directory
		case WAIT_OBJECT_0 + 1:
			goto NewDirectory;
		// End Thread
		case WAIT_OBJECT_0 + 2:
			_endthread();
		// Since this thread is COM enabled,
		// we have to be good citizens, and
		// pump any incoming messages to
		// our thread, so that we prevent broadcasted
		// SendMessage calls from hanging.
		case WAIT_OBJECT_0 + 3:
			PumpWaitingMessages();
			continue;
		}
	}
}

STDMETHODIMP
CSVNWorkingCopy::wc_statuses(BSTR bstrPath, VARIANT_BOOL getAll, SAFEARRAY **ppsa)
{
	USES_CONVERSION;
	HRESULT hr;
	apr_hash_t *hash;
	svn_error_t *error;
	svn_wc_status_t *status;
	svn_stringbuf_t *path;
	apr_hash_index_t *hi;
	SAFEARRAY *psa;
	SAFEARRAYBOUND rgsBound;
	IDispatch **paDisp;
	apr_ssize_t klen;
	int i;
	apr_size_t count;
	CHAR *pszKey;
	CComObject<CSVNStatus> *com_status = NULL;
	svn_boolean_t fLockedSA = FALSE;
        svn_boolean_t get_all = (getAll != VARIANT_TRUE ? FALSE : TRUE);

	hash = apr_hash_make(g_pool);
	path = svn_stringbuf_create(W2A(bstrPath), g_pool);
	error = svn_wc_statuses(hash, path,
                                FALSE, // FIXME: descend or not, rassilon?
                                FALSE, get_all, g_pool);
	if (error) {
		hr = convert_err_to_hresult(error);
		goto Cleanup;
	}

	count = apr_hash_count(hash);
	rgsBound.cElements = count;
	rgsBound.lLbound = 0;

	psa = SafeArrayCreate(VT_DISPATCH, 1, &rgsBound);
	if (psa == NULL)
		abort();

	hr = SafeArrayAccessData(psa, (void **)&paDisp);
	if (FAILED(hr))
		goto Cleanup;
	fLockedSA = TRUE;

	for (i = 0, hi = apr_hash_first(g_pool, hash); hi;
									 i++, hi = apr_hash_next(hi)) {
		apr_hash_this(hi, (const void **)&pszKey, &klen, (void **)&status);
		hr = CComObject<CSVNStatus>::CreateInstance(&com_status);
		if (FAILED(hr))
			goto Cleanup;
		// This is what we want since the hash key is an absolute path.
		pszKey = ((svn_stringbuf_t *)apr_hash_get (status->entry->attributes,
							   (const void *)SVN_WC_ENTRY_ATTR_NAME,
							   strlen(SVN_WC_ENTRY_ATTR_NAME)))->data;
		// SVN_WC_ENTRY_THIS_DIR is ".", we don't care about its status.
		if (strcmp(pszKey, SVN_WC_ENTRY_THIS_DIR) == 0) {
			delete com_status;
			com_status = NULL;
			// Pretend the iteration didn't happen.
			i--;
			continue;
		}
		hr = com_status->init(status, pszKey);
		if (FAILED(hr))
			goto Cleanup;
		hr = com_status->QueryInterface(&paDisp[i]);
		if (FAILED(hr))
			goto Cleanup;

		// NULL out the local variable,
		// the COM reference count is now owned by paDisp[i]
		com_status = NULL;
	}

	SafeArrayUnaccessData(psa);
	fLockedSA = FALSE;

	if (rgsBound.cElements > (UINT)i) {
		rgsBound.cElements = i;
		hr = SafeArrayRedim(psa, &rgsBound);
		if (FAILED(hr))
			goto Cleanup;
	}

	*ppsa = psa;

	hr = S_OK;
Cleanup:
	if (fLockedSA)
		SafeArrayUnaccessData(psa);
	delete com_status;
	svn_pool_clear(g_pool);
	return hr;
}
<|endoftext|>


<commit_before>#include <assert.h>

#include <sstream>

#include "linear/log.h"
#include "event_loop.h"
#include "wss_socket_impl.h"

using namespace linear::log;

namespace linear {

WSSSocketImpl::WSSSocketImpl(const std::string& host, int port,
                             const WSRequestContext& ws_context, const SSLContext& ssl_context,
                             const HandlerDelegate& delegate)
  : SocketImpl(host, port, delegate, Socket::WSS), request_context_(ws_context), ssl_context_(ssl_context) {
}

WSSSocketImpl::WSSSocketImpl(tv_stream_t* stream,
                             const WSRequestContext& ws_context, const SSLContext& ssl_context,
                             const HandlerDelegate& delegate)
  : SocketImpl(stream, delegate, Socket::WSS), request_context_(ws_context), ssl_context_(ssl_context) {
}

WSSSocketImpl::~WSSSocketImpl() {
}

Error WSSSocketImpl::Connect() {
  tv_wss_t* handle = static_cast<tv_wss_t*>(malloc(sizeof(tv_wss_t)));
  if (handle == NULL) {
    return Error(LNR_ENOMEM);
  }
  int ret = tv_wss_init(EventLoop::GetDefault().GetHandle(), handle, ssl_context_.GetHandle());
  if (ret) {
    free(handle);
    return Error(ret);
  }
  std::string url;
  if (request_context_.path.compare(0, 1, "/") == 0) {
    url = request_context_.path;
  } else {
    url = "/" + request_context_.path;
  }
  if (!request_context_.query.empty()) {
    if (request_context_.query.compare(0, 1, "?") == 0 ) {
      url = url + request_context_.query;
    } else {
      url = url + "?" + request_context_.query;
    }
  }
  buffer_reset(&handle->handshake.request.url.raw);
  if (buffer_append(&handle->handshake.request.url.raw, url.c_str(), url.size())) {
    free(handle);
    return Error(LNR_ENOMEM);
  }
  if (request_context_.authenticate.type == AuthContext::BASIC || authenticate_context_.type == AuthContext::DIGEST) {
    std::string uri;
    if (request_context_.path.compare(0, 1, "/") == 0) {
      uri = request_context_.path;
    } else {
      uri = "/" + request_context_.path;
    }
    std::string authorization = authenticate_context_.CreateAuthorizationHeader(uri,
                                                                                request_context_.authenticate.username,
                                                                                request_context_.authenticate.password);
    if (!authorization.empty()) {
      request_context_.headers["Authorization"] = authorization;
    }
  }
  buffer_kv kv;
  buffer_kv_init(&kv);
  for (std::map<std::string, std::string>::iterator it = request_context_.headers.begin();
       it != request_context_.headers.end(); it++) {
    buffer_kv_reset(&kv);
    if (buffer_append(&kv.key, it->first.c_str(), it->first.size()) ||
        buffer_append(&kv.val, it->second.c_str(), it->second.size()) ||
        buffer_kvs_insert(&handle->handshake.request.headers, &kv)) {
      buffer_kv_fin(&kv);
      free(handle);
      return Error(LNR_ENOMEM);
    }
  }
  buffer_kv_fin(&kv);
  stream_ = reinterpret_cast<tv_stream_t*>(handle);
  data_ = new EventLoop::SocketEventData();
  data_->Register(this);
  stream_->data = data_;
  response_context_.headers.clear(); // clear response context
  std::ostringstream port_str;
  port_str << peer_.port;
  if (!bind_ifname_.empty()) {
    ret = tv_bindtodevice(stream_, bind_ifname_.c_str());
    if (ret != 0) {
      LINEAR_LOG(LOG_ERR, "SO_BINDTODEVICE failed(%d)", ret);
      return Error(ret);
    }
  }
  ret = tv_connect(stream_, peer_.addr.c_str(), port_str.str().c_str(), EventLoop::OnConnect);
  if (ret) {
    assert(false); // never reach now
    delete data_;
    data_ = NULL;
    stream_->data = NULL;
    free(stream_);
    stream_ = NULL;
    return Error(ret);
  }
  return Error(LNR_OK);
}

void WSSSocketImpl::OnConnect(tv_stream_t* stream, int status) {
  tv_wss_t* handle = reinterpret_cast<tv_wss_t*>(stream);

  response_context_.code = handle->handshake.response.code;
  for (const buffer_kv* kv = buffer_kvs_get_first(&handle->handshake.response.headers);
       kv; kv = buffer_kvs_get_next(kv)) {
    response_context_.headers[std::string(kv->key.ptr)] = std::string(kv->val.ptr);
  }
  const buffer* auth_val = buffer_kvs_case_find(&handle->handshake.response.headers, CONST_STRING("www-authenticate"));
  if (auth_val != NULL) {
    int nc = (authenticate_context_.nc > 0xfffd) ? 0 : authenticate_context_.nc; // for 32bit
    authenticate_context_ = AuthenticateContextImpl(std::string(auth_val->ptr, auth_val->len));
    authenticate_context_.nc = nc + 1;
  } else {
    authenticate_context_ = AuthenticateContextImpl();
  }
  SocketImpl::OnConnect(stream, status);
}

bool WSSSocketImpl::CheckRetryAuth() {
  return (response_context_.code == WSHS_UNAUTHORIZED &&
          authenticate_context_.type == AuthContext::DIGEST && authenticate_context_.nc < 2);
}

const linear::WSRequestContext& WSSSocketImpl::GetWSRequestContext() {
  return request_context_;
}

void WSSSocketImpl::SetWSRequestContext(const WSRequestContext& request_context) {
  request_context_ = request_context;
}

const linear::WSResponseContext& WSSSocketImpl::GetWSResponseContext() {
  return response_context_;
}

void WSSSocketImpl::SetWSResponseContext(const WSResponseContext& response_context) {
  response_context_ = response_context;
}

Error WSSSocketImpl::GetVerifyResult() {
  int ret = tv_ssl_get_verify_result(reinterpret_cast<tv_wss_t*>(stream_)->ssl_handle);
  if (ret) {
    return Error(ret, stream_->ssl_err);
  } else {
    return Error(LNR_OK);
  }
}

bool WSSSocketImpl::PresentPeerCertificate() {
  X509* xcert = tv_ssl_get_peer_certificate(reinterpret_cast<tv_wss_t*>(stream_)->ssl_handle);
  if (xcert == NULL) {
    return false;
  }
  X509_free(xcert);
  return true;
}

X509Certificate WSSSocketImpl::GetPeerCertificate() {
  X509* xcert = tv_ssl_get_peer_certificate(reinterpret_cast<tv_wss_t*>(stream_)->ssl_handle);
  if (xcert == NULL) {
    throw std::runtime_error("peer certificate does not exist");
  }
  return X509Certificate(xcert);
}

}  // namespace linear
<commit_msg>fix bug.  - invalid reference of ssl_err<commit_after>#include <assert.h>

#include <sstream>

#include "linear/log.h"
#include "event_loop.h"
#include "wss_socket_impl.h"

using namespace linear::log;

namespace linear {

WSSSocketImpl::WSSSocketImpl(const std::string& host, int port,
                             const WSRequestContext& ws_context, const SSLContext& ssl_context,
                             const HandlerDelegate& delegate)
  : SocketImpl(host, port, delegate, Socket::WSS), request_context_(ws_context), ssl_context_(ssl_context) {
}

WSSSocketImpl::WSSSocketImpl(tv_stream_t* stream,
                             const WSRequestContext& ws_context, const SSLContext& ssl_context,
                             const HandlerDelegate& delegate)
  : SocketImpl(stream, delegate, Socket::WSS), request_context_(ws_context), ssl_context_(ssl_context) {
}

WSSSocketImpl::~WSSSocketImpl() {
}

Error WSSSocketImpl::Connect() {
  tv_wss_t* handle = static_cast<tv_wss_t*>(malloc(sizeof(tv_wss_t)));
  if (handle == NULL) {
    return Error(LNR_ENOMEM);
  }
  int ret = tv_wss_init(EventLoop::GetDefault().GetHandle(), handle, ssl_context_.GetHandle());
  if (ret) {
    free(handle);
    return Error(ret);
  }
  std::string url;
  if (request_context_.path.compare(0, 1, "/") == 0) {
    url = request_context_.path;
  } else {
    url = "/" + request_context_.path;
  }
  if (!request_context_.query.empty()) {
    if (request_context_.query.compare(0, 1, "?") == 0 ) {
      url = url + request_context_.query;
    } else {
      url = url + "?" + request_context_.query;
    }
  }
  buffer_reset(&handle->handshake.request.url.raw);
  if (buffer_append(&handle->handshake.request.url.raw, url.c_str(), url.size())) {
    free(handle);
    return Error(LNR_ENOMEM);
  }
  if (request_context_.authenticate.type == AuthContext::BASIC || authenticate_context_.type == AuthContext::DIGEST) {
    std::string uri;
    if (request_context_.path.compare(0, 1, "/") == 0) {
      uri = request_context_.path;
    } else {
      uri = "/" + request_context_.path;
    }
    std::string authorization = authenticate_context_.CreateAuthorizationHeader(uri,
                                                                                request_context_.authenticate.username,
                                                                                request_context_.authenticate.password);
    if (!authorization.empty()) {
      request_context_.headers["Authorization"] = authorization;
    }
  }
  buffer_kv kv;
  buffer_kv_init(&kv);
  for (std::map<std::string, std::string>::iterator it = request_context_.headers.begin();
       it != request_context_.headers.end(); it++) {
    buffer_kv_reset(&kv);
    if (buffer_append(&kv.key, it->first.c_str(), it->first.size()) ||
        buffer_append(&kv.val, it->second.c_str(), it->second.size()) ||
        buffer_kvs_insert(&handle->handshake.request.headers, &kv)) {
      buffer_kv_fin(&kv);
      free(handle);
      return Error(LNR_ENOMEM);
    }
  }
  buffer_kv_fin(&kv);
  stream_ = reinterpret_cast<tv_stream_t*>(handle);
  data_ = new EventLoop::SocketEventData();
  data_->Register(this);
  stream_->data = data_;
  response_context_.headers.clear(); // clear response context
  std::ostringstream port_str;
  port_str << peer_.port;
  if (!bind_ifname_.empty()) {
    ret = tv_bindtodevice(stream_, bind_ifname_.c_str());
    if (ret != 0) {
      LINEAR_LOG(LOG_ERR, "SO_BINDTODEVICE failed(%d)", ret);
      return Error(ret);
    }
  }
  ret = tv_connect(stream_, peer_.addr.c_str(), port_str.str().c_str(), EventLoop::OnConnect);
  if (ret) {
    assert(false); // never reach now
    delete data_;
    data_ = NULL;
    stream_->data = NULL;
    free(stream_);
    stream_ = NULL;
    return Error(ret);
  }
  return Error(LNR_OK);
}

void WSSSocketImpl::OnConnect(tv_stream_t* stream, int status) {
  tv_wss_t* handle = reinterpret_cast<tv_wss_t*>(stream);

  response_context_.code = handle->handshake.response.code;
  for (const buffer_kv* kv = buffer_kvs_get_first(&handle->handshake.response.headers);
       kv; kv = buffer_kvs_get_next(kv)) {
    response_context_.headers[std::string(kv->key.ptr)] = std::string(kv->val.ptr);
  }
  const buffer* auth_val = buffer_kvs_case_find(&handle->handshake.response.headers, CONST_STRING("www-authenticate"));
  if (auth_val != NULL) {
    int nc = (authenticate_context_.nc > 0xfffd) ? 0 : authenticate_context_.nc; // for 32bit
    authenticate_context_ = AuthenticateContextImpl(std::string(auth_val->ptr, auth_val->len));
    authenticate_context_.nc = nc + 1;
  } else {
    authenticate_context_ = AuthenticateContextImpl();
  }
  SocketImpl::OnConnect(stream, status);
}

bool WSSSocketImpl::CheckRetryAuth() {
  return (response_context_.code == WSHS_UNAUTHORIZED &&
          authenticate_context_.type == AuthContext::DIGEST && authenticate_context_.nc < 2);
}

const linear::WSRequestContext& WSSSocketImpl::GetWSRequestContext() {
  return request_context_;
}

void WSSSocketImpl::SetWSRequestContext(const WSRequestContext& request_context) {
  request_context_ = request_context;
}

const linear::WSResponseContext& WSSSocketImpl::GetWSResponseContext() {
  return response_context_;
}

void WSSSocketImpl::SetWSResponseContext(const WSResponseContext& response_context) {
  response_context_ = response_context;
}

Error WSSSocketImpl::GetVerifyResult() {
  int ret = tv_ssl_get_verify_result(reinterpret_cast<tv_wss_t*>(stream_)->ssl_handle);
  if (ret) {
    return Error(ret, reinterpret_cast<tv_wss_t*>(stream_)->ssl_handle->ssl_err);
  } else {
    return Error(LNR_OK);
  }
}

bool WSSSocketImpl::PresentPeerCertificate() {
  X509* xcert = tv_ssl_get_peer_certificate(reinterpret_cast<tv_wss_t*>(stream_)->ssl_handle);
  if (xcert == NULL) {
    return false;
  }
  X509_free(xcert);
  return true;
}

X509Certificate WSSSocketImpl::GetPeerCertificate() {
  X509* xcert = tv_ssl_get_peer_certificate(reinterpret_cast<tv_wss_t*>(stream_)->ssl_handle);
  if (xcert == NULL) {
    throw std::runtime_error("peer certificate does not exist");
  }
  return X509Certificate(xcert);
}

}  // namespace linear
<|endoftext|>


<commit_before>//     Copyright 2013, Kay Hayen, mailto:kay.hayen@gmail.com
//
//     Part of "Nuitka", an optimizing Python compiler that is compatible and
//     integrates with CPython, but also works on its own.
//
//     Licensed under the Apache License, Version 2.0 (the "License");
//     you may not use this file except in compliance with the License.
//     You may obtain a copy of the License at
//
//        http://www.apache.org/licenses/LICENSE-2.0
//
//     Unless required by applicable law or agreed to in writing, software
//     distributed under the License is distributed on an "AS IS" BASIS,
//     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//     See the License for the specific language governing permissions and
//     limitations under the License.
//
// This implements the loading of modules embedded. This is achieved mainly by
// registered a "sys.meta_path" loader, that gets asked for module names, and
// responds if it is an embedded one.

#include <osdefs.h>

#include "nuitka/prelude.hpp"
#include "nuitka/unfreezing.hpp"

// For Python3.3, the loader is a module attribute, so we need tp make it
// accessible from this variable.
#if PYTHON_VERSION < 330
static
#endif
PyObject *loader_frozen_modules = NULL;

static Nuitka_FreezeTableEntry *frozen_modules = NULL;

static char *_kwlist[] = {
    (char *)"fullname",
    (char *)"unused", NULL
};

static PyObject *_path_unfreezer_find_module( PyObject *self, PyObject *args, PyObject *kwds )
{
    PyObject *module_name;
    PyObject *unused;

    int res = PyArg_ParseTupleAndKeywords(
        args,
        kwds,
        "O|O:find_module",
        _kwlist,
        &module_name,
        &unused
    );

    if (unlikely( res == 0 ))
    {
        return NULL;
    }

    char const *name = Nuitka_String_AsString( module_name );

    if ( Py_VerboseFlag )
    {
        PySys_WriteStderr( "import %s # considering responsibility\n", name );
    }

    struct Nuitka_FreezeTableEntry *current = frozen_modules;

    while ( current->name != NULL )
    {
        if ( strcmp( name, current->name ) == 0 )
        {
            if ( Py_VerboseFlag )
            {
                PySys_WriteStderr( "import %s # claimed responsibility\n", name );
            }
            return INCREASE_REFCOUNT( loader_frozen_modules );
        }

        current++;
    }

    if ( Py_VerboseFlag )
    {
        PySys_WriteStderr( "import %s # denied responsibility\n", name );
    }

    return INCREASE_REFCOUNT( Py_None );
}

#ifdef _NUITKA_PORTABLE

#if PYTHON_VERSION < 300
typedef void (*entrypoint_t)( void );
#else
typedef PyObject * (*entrypoint_t)( void );
#endif

#ifndef _WIN32
// Shared libraries loading.
#include <dlfcn.h>
#endif

PyObject *callIntoShlibModule( const char *full_name, const char *filename )
{
    // Determine the package name and basename of the module to load.
    char const *dot = strrchr( full_name, '.' );
    char const *name;
    char const *package;

    if ( dot == NULL )
    {
        package = NULL;
        name = full_name;
    }
    else {
        package = (char *)full_name;
        name = dot+1;
    }

    char entry_function_name[1024];
    snprintf(
        entry_function_name, sizeof( entry_function_name ),
#if PYTHON_VERSION < 300
        "init%s",
#else
        "PyInit_%s",
#endif
        name
    );

#ifdef _WIN32

    unsigned int old_mode = SetErrorMode( SEM_FAILCRITICALERRORS );

    char abs_filename[ 260 ];
    LPTSTR unused;

    // Need to use absolute filename for Win9x to work correctly, however
    // important that is.
    GetFullPathName( filename, sizeof( abs_filename ), abs_filename, &unused );

    HINSTANCE hDLL = LoadLibraryEx( abs_filename, NULL, LOAD_WITH_ALTERED_SEARCH_PATH );
    assert( hDLL );

    entrypoint_t entrypoint = (entrypoint_t)GetProcAddress( hDLL, entry_function_name );

    SetErrorMode( old_mode );
#else
    int dlopenflags = PyThreadState_GET()->interp->dlopenflags;

    if ( Py_VerboseFlag )
    {
        PySys_WriteStderr(
            "import %s # dlopen(\"%s\", %x);\n",
            full_name,
            filename,
            dlopenflags
        );
    }

    void *handle = dlopen( filename, dlopenflags );

    if (unlikely( handle == NULL ))
    {
        const char *error = dlerror();

        if (unlikely( error == NULL ))
        {
            error = "unknown dlopen() error";
        }

        PyErr_SetString( PyExc_ImportError, error );
        MOD_RETURN_VALUE( NULL );
    }

    entrypoint_t entrypoint = (entrypoint_t)dlsym(
        handle,
        entry_function_name
    );

#endif
    assert( entrypoint );

    char *old_context = _Py_PackageContext;
    _Py_PackageContext = (char *)package;

    // Finally call into the DLL.
#if PYTHON_VERSION < 300
    (*entrypoint)();
#else
    PyObject *module = (*entrypoint)();
#endif

    _Py_PackageContext = old_context;

#if PYTHON_VERSION < 300
    PyObject *module = PyDict_GetItemString( PyImport_GetModuleDict(), name );
#endif

    assert( module );

    if (unlikely( module == NULL ))
    {
        PyErr_Format(
            PyExc_SystemError,
            "dynamic module not initialized properly"
        );

        return NULL;
    }

#if PYTHON_VERSION >= 300
    struct PyModuleDef *def = PyModule_GetDef( module );
    def->m_base.m_init = entrypoint;
#endif

    // Set filename attribute
    int res = PyModule_AddStringConstant( module, "__file__", filename );
    if (unlikely( res < 0 ))
    {
        // Might be refuted, which wouldn't be harmful.
        PyErr_Clear();
    }

#if PYTHON_VERSION >= 300
    PyObject *sys_modules = PySys_GetObject( (char *)"modules" );
    PyDict_SetItemString( PyImport_GetModuleDict(), full_name, module );
#endif

    return module;
}

#endif

static PyObject *_path_unfreezer_load_module( PyObject *self, PyObject *args, PyObject *kwds )
{
    PyObject *module_name;
    PyObject *unused;

    int res = PyArg_ParseTupleAndKeywords(
        args,
        kwds,
        "O|O:load_module",
        _kwlist,
        &module_name,
        &unused
    );

    if (unlikely( res == 0 ))
    {
        return NULL;
    }

    assert( module_name );
    assert( Nuitka_String_Check( module_name ) );

    char *name = Nuitka_String_AsString( module_name );

    struct Nuitka_FreezeTableEntry *current = frozen_modules;

    while ( current->name != NULL )
    {
        if ( strcmp( name, current->name ) == 0 )
        {
            if ( Py_VerboseFlag )
            {
                PySys_WriteStderr( "Loading %s\n", name );
            }

#ifdef _NUITKA_PORTABLE
            if ( ( current->flags & NUITKA_SHLIB_MODULE ) != 0 )
            {
                char filename[1024];

                strcpy( filename, getBinaryDirectory() );
                char *d = filename;
                d += strlen( filename );
                *d++ = SEP;

                char *s = current->name;

                while( *s )
                {
                    if ( *s == '.' )
                    {
                        *d++ = SEP;
                    }
                    else
                    {
                        *d++ = *s++;
                    }
                }
                *d = 0;

#ifdef _WIN32
                strcat( filename, ".pyd" );
#else
                strcat( filename, ".so" );
#endif

                callIntoShlibModule( current->name,  filename );
            }
            else
#endif
            {
                assert( ( current->flags & NUITKA_SHLIB_MODULE ) == 0 );
                current->python_initfunc();
            }

            if (unlikely( ERROR_OCCURED() ))
            {
                return NULL;
            }

            PyObject *sys_modules = PySys_GetObject( (char *)"modules" );

            if ( Py_VerboseFlag )
            {
                PySys_WriteStderr( "Loaded %s\n", name );
            }

            return LOOKUP_SUBSCRIPT( sys_modules, module_name );
       }

       current++;
    }

    assert( false );

    return INCREASE_REFCOUNT( Py_None );
}

static PyMethodDef _method_def_loader_find_module =
{
    "find_module",
    (PyCFunction)_path_unfreezer_find_module,
    METH_VARARGS | METH_KEYWORDS,
    NULL
};

static PyMethodDef _method_def_loader_load_module =
{
    "load_module",
    (PyCFunction)_path_unfreezer_load_module,
    METH_VARARGS | METH_KEYWORDS,
    NULL
};

void registerMetaPathBasedUnfreezer( struct Nuitka_FreezeTableEntry *_frozen_modules )
{
    // Do it only once.
    if ( frozen_modules )
    {
        assert( _frozen_modules == frozen_modules );

        return;
    }

    frozen_modules = _frozen_modules;

    // Build the dictionary of the "loader" object, which needs to have two
    // methods "find_module" where we acknowledge that we are capable of loading
    // the module, and "load_module" that does the actual thing.
    PyObject *method_dict = PyDict_New();
    assertObject( method_dict );

    PyObject *loader_find_module = PyCFunction_New(
        &_method_def_loader_find_module,
        NULL
    );
    assertObject( loader_find_module );
    PyDict_SetItemString( method_dict, "find_module", loader_find_module );

    PyObject *loader_load_module = PyCFunction_New(
        &_method_def_loader_load_module,
        NULL
    );
    assertObject( loader_load_module );
    PyDict_SetItemString( method_dict, "load_module", loader_load_module );

    // Build the actual class.
    loader_frozen_modules = PyObject_CallFunctionObjArgs(
        (PyObject *)&PyType_Type,
#if PYTHON_VERSION < 300
        PyString_FromString( "_nuitka_compiled_modules_loader" ),
#else
        PyUnicode_FromString( "_nuitka_compiled_modules_loader" ),
#endif
        const_tuple_empty,
        method_dict,
        NULL
    );

    assertObject( loader_frozen_modules );

    if ( Py_VerboseFlag )
    {
        PySys_WriteStderr( "setup nuitka compiled module/shlib importer\n" );
    }

    // And also provide it as a meta path loader.
    int res = PyList_Insert( PySys_GetObject( ( char *)"meta_path" ), 0, loader_frozen_modules );
    assert( res == 0 );
}
<commit_msg>Python3.3: Fix, do not prevent built-in and frozen module importers to shadow compiled modules<commit_after>//     Copyright 2013, Kay Hayen, mailto:kay.hayen@gmail.com
//
//     Part of "Nuitka", an optimizing Python compiler that is compatible and
//     integrates with CPython, but also works on its own.
//
//     Licensed under the Apache License, Version 2.0 (the "License");
//     you may not use this file except in compliance with the License.
//     You may obtain a copy of the License at
//
//        http://www.apache.org/licenses/LICENSE-2.0
//
//     Unless required by applicable law or agreed to in writing, software
//     distributed under the License is distributed on an "AS IS" BASIS,
//     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//     See the License for the specific language governing permissions and
//     limitations under the License.
//
// This implements the loading of modules embedded. This is achieved mainly by
// registered a "sys.meta_path" loader, that gets asked for module names, and
// responds if it is an embedded one.

#include <osdefs.h>

#include "nuitka/prelude.hpp"
#include "nuitka/unfreezing.hpp"

// For Python3.3, the loader is a module attribute, so we need tp make it
// accessible from this variable.
#if PYTHON_VERSION < 330
static
#endif
PyObject *loader_frozen_modules = NULL;

static Nuitka_FreezeTableEntry *frozen_modules = NULL;

static char *_kwlist[] = {
    (char *)"fullname",
    (char *)"unused", NULL
};

static PyObject *_path_unfreezer_find_module( PyObject *self, PyObject *args, PyObject *kwds )
{
    PyObject *module_name;
    PyObject *unused;

    int res = PyArg_ParseTupleAndKeywords(
        args,
        kwds,
        "O|O:find_module",
        _kwlist,
        &module_name,
        &unused
    );

    if (unlikely( res == 0 ))
    {
        return NULL;
    }

    char const *name = Nuitka_String_AsString( module_name );

    if ( Py_VerboseFlag )
    {
        PySys_WriteStderr( "import %s # considering responsibility\n", name );
    }

    struct Nuitka_FreezeTableEntry *current = frozen_modules;

    while ( current->name != NULL )
    {
        if ( strcmp( name, current->name ) == 0 )
        {
            if ( Py_VerboseFlag )
            {
                PySys_WriteStderr( "import %s # claimed responsibility\n", name );
            }
            return INCREASE_REFCOUNT( loader_frozen_modules );
        }

        current++;
    }

    if ( Py_VerboseFlag )
    {
        PySys_WriteStderr( "import %s # denied responsibility\n", name );
    }

    return INCREASE_REFCOUNT( Py_None );
}

#ifdef _NUITKA_PORTABLE

#if PYTHON_VERSION < 300
typedef void (*entrypoint_t)( void );
#else
typedef PyObject * (*entrypoint_t)( void );
#endif

#ifndef _WIN32
// Shared libraries loading.
#include <dlfcn.h>
#endif

PyObject *callIntoShlibModule( const char *full_name, const char *filename )
{
    // Determine the package name and basename of the module to load.
    char const *dot = strrchr( full_name, '.' );
    char const *name;
    char const *package;

    if ( dot == NULL )
    {
        package = NULL;
        name = full_name;
    }
    else {
        package = (char *)full_name;
        name = dot+1;
    }

    char entry_function_name[1024];
    snprintf(
        entry_function_name, sizeof( entry_function_name ),
#if PYTHON_VERSION < 300
        "init%s",
#else
        "PyInit_%s",
#endif
        name
    );

#ifdef _WIN32

    unsigned int old_mode = SetErrorMode( SEM_FAILCRITICALERRORS );

    char abs_filename[ 260 ];
    LPTSTR unused;

    // Need to use absolute filename for Win9x to work correctly, however
    // important that is.
    GetFullPathName( filename, sizeof( abs_filename ), abs_filename, &unused );

    HINSTANCE hDLL = LoadLibraryEx( abs_filename, NULL, LOAD_WITH_ALTERED_SEARCH_PATH );
    assert( hDLL );

    entrypoint_t entrypoint = (entrypoint_t)GetProcAddress( hDLL, entry_function_name );

    SetErrorMode( old_mode );
#else
    int dlopenflags = PyThreadState_GET()->interp->dlopenflags;

    if ( Py_VerboseFlag )
    {
        PySys_WriteStderr(
            "import %s # dlopen(\"%s\", %x);\n",
            full_name,
            filename,
            dlopenflags
        );
    }

    void *handle = dlopen( filename, dlopenflags );

    if (unlikely( handle == NULL ))
    {
        const char *error = dlerror();

        if (unlikely( error == NULL ))
        {
            error = "unknown dlopen() error";
        }

        PyErr_SetString( PyExc_ImportError, error );
        MOD_RETURN_VALUE( NULL );
    }

    entrypoint_t entrypoint = (entrypoint_t)dlsym(
        handle,
        entry_function_name
    );

#endif
    assert( entrypoint );

    char *old_context = _Py_PackageContext;
    _Py_PackageContext = (char *)package;

    // Finally call into the DLL.
#if PYTHON_VERSION < 300
    (*entrypoint)();
#else
    PyObject *module = (*entrypoint)();
#endif

    _Py_PackageContext = old_context;

#if PYTHON_VERSION < 300
    PyObject *module = PyDict_GetItemString( PyImport_GetModuleDict(), name );
#endif

    assert( module );

    if (unlikely( module == NULL ))
    {
        PyErr_Format(
            PyExc_SystemError,
            "dynamic module not initialized properly"
        );

        return NULL;
    }

#if PYTHON_VERSION >= 300
    struct PyModuleDef *def = PyModule_GetDef( module );
    def->m_base.m_init = entrypoint;
#endif

    // Set filename attribute
    int res = PyModule_AddStringConstant( module, "__file__", filename );
    if (unlikely( res < 0 ))
    {
        // Might be refuted, which wouldn't be harmful.
        PyErr_Clear();
    }

#if PYTHON_VERSION >= 300
    PyObject *sys_modules = PySys_GetObject( (char *)"modules" );
    PyDict_SetItemString( PyImport_GetModuleDict(), full_name, module );
#endif

    return module;
}

#endif

static PyObject *_path_unfreezer_load_module( PyObject *self, PyObject *args, PyObject *kwds )
{
    PyObject *module_name;
    PyObject *unused;

    int res = PyArg_ParseTupleAndKeywords(
        args,
        kwds,
        "O|O:load_module",
        _kwlist,
        &module_name,
        &unused
    );

    if (unlikely( res == 0 ))
    {
        return NULL;
    }

    assert( module_name );
    assert( Nuitka_String_Check( module_name ) );

    char *name = Nuitka_String_AsString( module_name );

    struct Nuitka_FreezeTableEntry *current = frozen_modules;

    while ( current->name != NULL )
    {
        if ( strcmp( name, current->name ) == 0 )
        {
            if ( Py_VerboseFlag )
            {
                PySys_WriteStderr( "Loading %s\n", name );
            }

#ifdef _NUITKA_PORTABLE
            if ( ( current->flags & NUITKA_SHLIB_MODULE ) != 0 )
            {
                char filename[1024];

                strcpy( filename, getBinaryDirectory() );
                char *d = filename;
                d += strlen( filename );
                *d++ = SEP;

                char *s = current->name;

                while( *s )
                {
                    if ( *s == '.' )
                    {
                        *d++ = SEP;
                    }
                    else
                    {
                        *d++ = *s++;
                    }
                }
                *d = 0;

#ifdef _WIN32
                strcat( filename, ".pyd" );
#else
                strcat( filename, ".so" );
#endif

                callIntoShlibModule( current->name,  filename );
            }
            else
#endif
            {
                assert( ( current->flags & NUITKA_SHLIB_MODULE ) == 0 );
                current->python_initfunc();
            }

            if (unlikely( ERROR_OCCURED() ))
            {
                return NULL;
            }

            PyObject *sys_modules = PySys_GetObject( (char *)"modules" );

            if ( Py_VerboseFlag )
            {
                PySys_WriteStderr( "Loaded %s\n", name );
            }

            return LOOKUP_SUBSCRIPT( sys_modules, module_name );
       }

       current++;
    }

    assert( false );

    return INCREASE_REFCOUNT( Py_None );
}

static PyMethodDef _method_def_loader_find_module =
{
    "find_module",
    (PyCFunction)_path_unfreezer_find_module,
    METH_VARARGS | METH_KEYWORDS,
    NULL
};

static PyMethodDef _method_def_loader_load_module =
{
    "load_module",
    (PyCFunction)_path_unfreezer_load_module,
    METH_VARARGS | METH_KEYWORDS,
    NULL
};

void registerMetaPathBasedUnfreezer( struct Nuitka_FreezeTableEntry *_frozen_modules )
{
    // Do it only once.
    if ( frozen_modules )
    {
        assert( _frozen_modules == frozen_modules );

        return;
    }

    frozen_modules = _frozen_modules;

    // Build the dictionary of the "loader" object, which needs to have two
    // methods "find_module" where we acknowledge that we are capable of loading
    // the module, and "load_module" that does the actual thing.
    PyObject *method_dict = PyDict_New();
    assertObject( method_dict );

    PyObject *loader_find_module = PyCFunction_New(
        &_method_def_loader_find_module,
        NULL
    );
    assertObject( loader_find_module );
    PyDict_SetItemString( method_dict, "find_module", loader_find_module );

    PyObject *loader_load_module = PyCFunction_New(
        &_method_def_loader_load_module,
        NULL
    );
    assertObject( loader_load_module );
    PyDict_SetItemString( method_dict, "load_module", loader_load_module );

    // Build the actual class.
    loader_frozen_modules = PyObject_CallFunctionObjArgs(
        (PyObject *)&PyType_Type,
#if PYTHON_VERSION < 300
        PyString_FromString( "_nuitka_compiled_modules_loader" ),
#else
        PyUnicode_FromString( "_nuitka_compiled_modules_loader" ),
#endif
        const_tuple_empty,
        method_dict,
        NULL
    );

    assertObject( loader_frozen_modules );

    if ( Py_VerboseFlag )
    {
        PySys_WriteStderr( "setup nuitka compiled module/shlib importer\n" );
    }

    // And also provide it as a meta path loader.
    int res = PyList_Insert(
        PySys_GetObject( ( char *)"meta_path" ),
#if PYTHON_VERSION < 330
        0,
#else
        2,
#endif
        loader_frozen_modules
    );
    assert( res == 0 );
}
<|endoftext|>