Exemple #1
0
    Status HostAndPort::initialize(const StringData& s) {
        const size_t colonPos = s.rfind(':');
        const StringData hostPart = s.substr(0, colonPos);
        if (hostPart.empty()) {
            return Status(ErrorCodes::FailedToParse, str::stream() <<
                          "Empty host component parsing HostAndPort from \"" <<
                          escape(s.toString()) << "\"");
        }

        int port;
        if (colonPos != std::string::npos) {
            const StringData portPart = s.substr(colonPos + 1);
            Status status = parseNumberFromStringWithBase(portPart, 10, &port);
            if (!status.isOK()) {
                return status;
            }
            if (port <= 0) {
                return Status(ErrorCodes::FailedToParse, str::stream() << "Port number " << port <<
                              " out of range parsing HostAndPort from \"" << escape(s.toString()) <<
                              "\"");
            }
        }
        else {
            port = -1;
        }
        _host = hostPart.toString();
        _port = port;
        return Status::OK();
    }
    std::ostream& MessageEventDetailsEncoder::encode(const MessageEventEphemeral& event,
                                                     std::ostream &os) {

        static const size_t maxLogLine = 10 * 1024;

        char dateString[64];
        curTimeString(dateString);
        os << dateString << ' ';
        StringData contextName = event.getContextName();
        if (!contextName.empty()) {
            os << '[' << contextName << "] ";
        }

        LogSeverity severity = event.getSeverity();
        if (severity >= LogSeverity::Info()) {
            os << severity << ": ";
        }

        StringData msg = event.getMessage();
        if (msg.size() > maxLogLine) {
            os << "warning: log line attempted (" << msg.size() / 1024 << "k) over max size (" <<
                maxLogLine / 1024 << "k), printing beginning and end ... ";
            os << msg.substr(0, maxLogLine / 3);
            os << " .......... ";
            os << msg.substr(msg.size() - (maxLogLine / 3));
        }
        else {
            os << msg;
        }
        if (!msg.endsWith(StringData("\n", StringData::LiteralTag())))
            os << '\n';
        return os;
    }
std::ostream& MessageEventDetailsEncoder::encode(const MessageEventEphemeral& event,
                                                 std::ostream& os) {
    static const size_t maxLogLine = 10 * 1024;

    _dateFormatter(os, event.getDate());
    os << ' ';

    os << event.getSeverity().toChar();
    os << ' ';

    LogComponent component = event.getComponent();
    os << component;
    os << ' ';

    StringData contextName = event.getContextName();
    if (!contextName.empty()) {
        os << '[' << contextName << "] ";
    }

    StringData msg = event.getMessage();
    if (msg.size() > maxLogLine) {
        os << "warning: log line attempted (" << msg.size() / 1024 << "k) over max size ("
           << maxLogLine / 1024 << "k), printing beginning and end ... ";
        os << msg.substr(0, maxLogLine / 3);
        os << " .......... ";
        os << msg.substr(msg.size() - (maxLogLine / 3));
    } else {
        os << msg;
    }
    if (!msg.endsWith(StringData("\n", StringData::LiteralTag())))
        os << '\n';
    return os;
}
std::ostream& MessageEventDetailsEncoder::encode(const MessageEventEphemeral& event,
                                                 std::ostream& os) {
    const auto maxLogSizeKB = getMaxLogSizeKB();

    const size_t maxLogSize = maxLogSizeKB * 1024;

    getDateFormatter()(os, event.getDate());
    os << ' ';

    const auto severity = event.getSeverity();
    os << severity.toStringDataCompact();
    os << ' ';

    LogComponent component = event.getComponent();
    os << component;
    os << ' ';

    StringData contextName = event.getContextName();
    if (!contextName.empty()) {
        os << '[' << contextName << "] ";
    }

    StringData msg = event.getMessage();

#ifdef _WIN32
    // We need to translate embedded Unix style line endings into Windows style endings.
    std::string tempstr;
    size_t embeddedNewLine = msg.find('\n');

    if (embeddedNewLine != std::string::npos) {
        tempstr = msg.toString().replace(embeddedNewLine, 1, "\r\n");

        embeddedNewLine = tempstr.find('\n', embeddedNewLine + 2);
        while (embeddedNewLine != std::string::npos) {
            tempstr = tempstr.replace(embeddedNewLine, 1, "\r\n");

            embeddedNewLine = tempstr.find('\n', embeddedNewLine + 2);
        }

        msg = tempstr;
    }
#endif

    if (event.isTruncatable() && msg.size() > maxLogSize) {
        os << "warning: log line attempted (" << msg.size() / 1024 << "kB) over max size ("
           << maxLogSizeKB << "kB), printing beginning and end ... ";
        os << msg.substr(0, maxLogSize / 3);
        os << " .......... ";
        os << msg.substr(msg.size() - (maxLogSize / 3));
    } else {
        os << msg;
    }

    if (!msg.endsWith(kEOL))
        os << kEOL;

    return os;
}
BSONElement extractElementAtPath(const BSONObj& obj, StringData path) {
    BSONElement e = obj.getField(path);
    if (e.eoo()) {
        size_t dot_offset = path.find('.');
        if (dot_offset != std::string::npos) {
            StringData left = path.substr(0, dot_offset);
            StringData right = path.substr(dot_offset + 1);
            BSONObj sub = obj.getObjectField(left);
            return sub.isEmpty() ? BSONElement() : extractElementAtPath(sub, right);
        }
    }

    return e;
}
Exemple #6
0
bool FieldRef::equalsDottedField( const StringData& other ) const {
    StringData rest = other;

    for ( size_t i = 0; i < _size; i++ ) {

        StringData part = getPart( i );

        if ( !rest.startsWith( part ) )
            return false;

        if ( i == _size - 1 )
            return rest.size() == part.size();

        // make sure next thing is a dot
        if ( rest.size() == part.size() )
            return false;

        if ( rest[part.size()] != '.' )
            return false;

        rest = rest.substr( part.size() + 1 );
    }

    return false;
}
    bool CmdAuthenticate::authenticateX509(const string& dbname,
                                           BSONObj& cmdObj, 
                                           string& errmsg, 
                                           BSONObjBuilder& result) {
        if(dbname != "$external") {
            errmsg = "X.509 authentication must always use the $external database.";
            result.append(saslCommandCodeFieldName, ErrorCodes::AuthenticationFailed);
            return false;
        }

        std::string user = cmdObj.getStringField("user");
        ClientBasic *client = ClientBasic::getCurrent();
        AuthorizationSession* authorizationSession = client->getAuthorizationSession();
        StringData subjectName = client->port()->getX509SubjectName();
        
        if (user != subjectName) {
            errmsg = "There is no x.509 client certificate matching the user.";
            result.append(saslCommandCodeFieldName, ErrorCodes::AuthenticationFailed);
            return false;
        }
        else {
            StringData srvSubjectName = getSSLManager()->getSubjectName();
            StringData srvClusterId = srvSubjectName.substr(0, srvSubjectName.find("/CN")+1);
            StringData peerClusterId = subjectName.substr(0, subjectName.find("/CN")+1);

            // Handle internal cluster member 
            if (srvClusterId == peerClusterId) {
                authorizationSession->grantInternalAuthorization(UserName(user, "$external"));
            }
            // Handle normal client authentication
            else {
                Principal* principal = new Principal(UserName(user, "$external"));
                principal->setImplicitPrivilegeAcquisition(true);
                authorizationSession->addAuthorizedPrincipal(principal);
            }
            result.append( "dbname" , dbname );
            result.append( "user" , user );
            return true;
        }
    }
    Status CmdAuthenticate::_authenticateX509(const UserName& user, const BSONObj& cmdObj) {
        if(user.getDB() != "$external") {
            return Status(ErrorCodes::ProtocolError,
                          "X.509 authentication must always use the $external database.");
        }

        ClientBasic *client = ClientBasic::getCurrent();
        AuthorizationSession* authorizationSession = client->getAuthorizationSession();
        StringData subjectName = client->port()->getX509SubjectName();

        if (user.getUser() != subjectName) {
            return Status(ErrorCodes::AuthenticationFailed,
                          "There is no x.509 client certificate matching the user.");
        }
        else {
            StringData srvSubjectName = getSSLManager()->getServerSubjectName();
            StringData srvClusterId = srvSubjectName.substr(srvSubjectName.find(",OU="));
            StringData peerClusterId = subjectName.substr(subjectName.find(",OU="));

            fassert(17002, !srvClusterId.empty() && srvClusterId != srvSubjectName);

            // Handle internal cluster member auth, only applies to server-server connections 
            if (srvClusterId == peerClusterId) {
                if (cmdLine.clusterAuthMode.empty() || cmdLine.clusterAuthMode == "keyfile") {
                    return Status(ErrorCodes::AuthenticationFailed,
                                  "X509 authentication is not allowed for cluster authentication");
                }
                authorizationSession->grantInternalAuthorization(user);
            }
            // Handle normal client authentication, only applies to client-server connections
            else {
                Principal* principal = new Principal(user);
                authorizationSession->addAndAuthorizePrincipal(principal);
            }
            return Status::OK();
        }
    }
Exemple #9
0
Status parseCertificateSelector(SSLParams::CertificateSelector* selector,
                                StringData name,
                                StringData value) {
    selector->subject.clear();
    selector->thumbprint.clear();

    const auto delim = value.find('=');
    if (delim == std::string::npos) {
        return {ErrorCodes::BadValue,
                str::stream() << "Certificate selector for '" << name
                              << "' must be a key=value pair"};
    }

    auto key = value.substr(0, delim);
    if (key == "subject") {
        selector->subject = value.substr(delim + 1).toString();
        return Status::OK();
    }

    if (key != "thumbprint") {
        return {ErrorCodes::BadValue,
                str::stream() << "Unknown certificate selector property for '" << name << "': '"
                              << key
                              << "'"};
    }

    auto swHex = hexToVector(value.substr(delim + 1));
    if (!swHex.isOK()) {
        return {ErrorCodes::BadValue,
                str::stream() << "Invalid certificate selector value for '" << name << "': "
                              << swHex.getStatus().reason()};
    }

    selector->thumbprint = std::move(swHex.getValue());

    return Status::OK();
}
Exemple #10
0
void StringData::inflate(int size, const StringData &fillval, bool before)
{
	if ((int) buf.length() >= size)
		return;
	StringData dtVal;
	int iInflateSize = size - buf.length();
	for (unsigned int i = 0; i < iInflateSize / fillval.length(); i++)
	{
		dtVal += fillval;
	}
	if (iInflateSize % fillval.length() > 0)
		dtVal += fillval.substr(0, iInflateSize % fillval.length());
	if (before)
		operator =(dtVal + *this);
	else
		operator =(*this + dtVal);
}
Exemple #11
0
// Index works as follows: All non-NULL values are stored as if they had appended an 'X' character at the end. So
// "foo" is stored as if it was "fooX", and "" (empty string) is stored as "X". And NULLs are stored as empty strings.
inline StringIndex::key_type StringIndex::create_key(StringData str, size_t offset) noexcept
{
    if (str.is_null())
        return 0;

    if (offset > str.size())
        return 0;

    // for very short strings
    size_t tail = str.size() - offset;
    if (tail <= sizeof(key_type)-1) {
        char buf[sizeof(key_type)];
        memset(buf, 0, sizeof(key_type));
        buf[tail] = 'X';
        memcpy(buf, str.data() + offset, tail);
        return create_key(StringData(buf, tail + 1));
    }
    // else fallback
    return create_key(str.substr(offset));
}
void WiredTigerKVEngine::_checkIdentPath(StringData ident) {
    size_t start = 0;
    size_t idx;
    while ((idx = ident.find('/', start)) != string::npos) {
        StringData dir = ident.substr(0, idx);

        boost::filesystem::path subdir = _path;
        subdir /= dir.toString();
        if (!boost::filesystem::exists(subdir)) {
            LOG(1) << "creating subdirectory: " << dir;
            try {
                boost::filesystem::create_directory(subdir);
            } catch (const std::exception& e) {
                error() << "error creating path " << subdir.string() << ' ' << e.what();
                throw;
            }
        }

        start = idx + 1;
    }
}
Exemple #13
0
StringData ObjectStore::object_type_for_table_name(StringData table_name) {
    if (table_name.begins_with(c_object_table_prefix)) {
        return table_name.substr(sizeof(c_object_table_prefix) - 1);
    }
    return StringData();
}
Exemple #14
0
    Status HostAndPort::initialize(const StringData& s) {
        size_t colonPos = s.rfind(':');
        StringData hostPart = s.substr(0, colonPos);

        // handle ipv6 hostPart (which we require to be wrapped in []s)
        const size_t openBracketPos = s.find('[');
        const size_t closeBracketPos = s.find(']');
        if (openBracketPos != std::string::npos) {
            if (openBracketPos != 0) {
                return Status(ErrorCodes::FailedToParse,
                              str::stream() << "'[' present, but not first character in "
                                            << s.toString());
            }
            if (closeBracketPos == std::string::npos) {
                return Status(ErrorCodes::FailedToParse,
                              str::stream() << "ipv6 address is missing closing ']' in hostname in "
                                            << s.toString());
            }

            hostPart = s.substr(openBracketPos+1, closeBracketPos-openBracketPos-1);
            // prevent accidental assignment of port to the value of the final portion of hostPart
            if (colonPos < closeBracketPos) {
                colonPos = std::string::npos;
            }
            else if (colonPos != closeBracketPos+1) {
                return Status(ErrorCodes::FailedToParse,
                              str::stream() << "Extraneous characters between ']' and pre-port ':'"
                                            << " in " << s.toString());
            }
        }
        else if (closeBracketPos != std::string::npos) {
            return Status(ErrorCodes::FailedToParse,
                          str::stream() << "']' present without '[' in " << s.toString());
        }

        if (hostPart.empty()) {
            return Status(ErrorCodes::FailedToParse, str::stream() <<
                          "Empty host component parsing HostAndPort from \"" <<
                          escape(s.toString()) << "\"");
        }

        int port;
        if (colonPos != std::string::npos) {
            const StringData portPart = s.substr(colonPos + 1);
            Status status = parseNumberFromStringWithBase(portPart, 10, &port);
            if (!status.isOK()) {
                return status;
            }
            if (port <= 0) {
                return Status(ErrorCodes::FailedToParse, str::stream() << "Port number " << port <<
                              " out of range parsing HostAndPort from \"" << escape(s.toString()) <<
                              "\"");
            }
        }
        else {
            port = -1;
        }
        _host = hostPart.toString();
        _port = port;
        return Status::OK();
    }
Exemple #15
0
 void CurOp::setNS( const StringData& ns ) {
     ns.substr( 0, Namespace::MaxNsLen ).copyTo( _ns, true );
 }
void BtreeKeyGeneratorV1::getKeysImplWithArray(
    std::vector<const char*> fieldNames,
    std::vector<BSONElement> fixed,
    const BSONObj& obj,
    BSONObjSet* keys,
    unsigned numNotFound,
    const std::vector<PositionalPathInfo>& positionalInfo,
    MultikeyPaths* multikeyPaths) const {
    BSONElement arrElt;

    // A set containing the position of any indexed fields in the key pattern that traverse through
    // the 'arrElt' array value.
    std::set<size_t> arrIdxs;

    // A vector with size equal to the number of elements in the index key pattern. Each element in
    // the vector, if initialized, refers to the component within the indexed field that traverses
    // through the 'arrElt' array value. We say that this component within the indexed field
    // corresponds to a path that causes the index to be multikey if the 'arrElt' array value
    // contains multiple elements.
    //
    // For example, consider the index {'a.b': 1, 'a.c'} and the document
    // {a: [{b: 1, c: 'x'}, {b: 2, c: 'y'}]}. The path "a" causes the index to be multikey, so we'd
    // have a std::vector<boost::optional<size_t>>{{0U}, {0U}}.
    //
    // Furthermore, due to how positional key patterns are specified, it's possible for an indexed
    // field to cause the index to be multikey at a different component than another indexed field
    // that also traverses through the 'arrElt' array value. It's then also possible for an indexed
    // field not to cause the index to be multikey, even if it traverses through the 'arrElt' array
    // value, because only a particular element would be indexed.
    //
    // For example, consider the index {'a.b': 1, 'a.b.0'} and the document {a: {b: [1, 2]}}. The
    // path "a.b" causes the index to be multikey, but the key pattern "a.b.0" only indexes the
    // first element of the array, so we'd have a
    // std::vector<boost::optional<size_t>>{{1U}, boost::none}.
    std::vector<boost::optional<size_t>> arrComponents(fieldNames.size());

    bool mayExpandArrayUnembedded = true;
    for (size_t i = 0; i < fieldNames.size(); ++i) {
        if (*fieldNames[i] == '\0') {
            continue;
        }

        bool arrayNestedArray;
        // Extract element matching fieldName[ i ] from object xor array.
        BSONElement e =
            extractNextElement(obj, positionalInfo[i], &fieldNames[i], &arrayNestedArray);

        if (e.eoo()) {
            // if field not present, set to null
            fixed[i] = nullElt;
            // done expanding this field name
            fieldNames[i] = "";
            numNotFound++;
        } else if (e.type() == Array) {
            arrIdxs.insert(i);
            if (arrElt.eoo()) {
                // we only expand arrays on a single path -- track the path here
                arrElt = e;
            } else if (e.rawdata() != arrElt.rawdata()) {
                // enforce single array path here
                assertParallelArrays(e.fieldName(), arrElt.fieldName());
            }
            if (arrayNestedArray) {
                mayExpandArrayUnembedded = false;
            }
        } else {
            // not an array - no need for further expansion
            fixed[i] = e;
        }
    }

    if (arrElt.eoo()) {
        // No array, so generate a single key.
        if (_isSparse && numNotFound == fieldNames.size()) {
            return;
        }
        BSONObjBuilder b(_sizeTracker);
        for (std::vector<BSONElement>::iterator i = fixed.begin(); i != fixed.end(); ++i) {
            CollationIndexKey::collationAwareIndexKeyAppend(*i, _collator, &b);
        }
        keys->insert(b.obj());
    } else if (arrElt.embeddedObject().firstElement().eoo()) {
        // We've encountered an empty array.
        if (multikeyPaths && mayExpandArrayUnembedded) {
            // Any indexed path which traverses through the empty array must be recorded as an array
            // component.
            for (auto i : arrIdxs) {
                // We need to determine which component of the indexed field causes the index to be
                // multikey as a result of the empty array. Indexed empty arrays are considered
                // multikey and may occur mid-path. For instance, the indexed path "a.b.c" has
                // multikey components {0, 1} given the document {a: [{b: []}, {b: 1}]}.
                size_t fullPathLength = _pathLengths[i];
                size_t suffixPathLength = FieldRef{fieldNames[i]}.numParts();
                invariant(suffixPathLength < fullPathLength);
                arrComponents[i] = fullPathLength - suffixPathLength - 1;
            }
        }

        // For an empty array, set matching fields to undefined.
        _getKeysArrEltFixed(&fieldNames,
                            &fixed,
                            undefinedElt,
                            keys,
                            numNotFound,
                            arrElt,
                            arrIdxs,
                            true,
                            _emptyPositionalInfo,
                            multikeyPaths);
    } else {
        BSONObj arrObj = arrElt.embeddedObject();

        // For positional key patterns, e.g. {'a.1.b': 1}, we lookup the indexed array element
        // and then traverse the remainder of the field path up front. This prevents us from
        // having to look up the indexed element again on each recursive call (i.e. once per
        // array element).
        std::vector<PositionalPathInfo> subPositionalInfo(fixed.size());
        for (size_t i = 0; i < fieldNames.size(); ++i) {
            const bool fieldIsArray = arrIdxs.find(i) != arrIdxs.end();

            if (*fieldNames[i] == '\0') {
                // We've reached the end of the path.
                if (multikeyPaths && fieldIsArray && mayExpandArrayUnembedded) {
                    // The 'arrElt' array value isn't expanded into multiple elements when the last
                    // component of the indexed field is positional and 'arrElt' contains nested
                    // array values. In all other cases, the 'arrElt' array value may be expanded
                    // into multiple element and can therefore cause the index to be multikey.
                    arrComponents[i] = _pathLengths[i] - 1;
                }
                continue;
            }

            // The earlier call to dps::extractElementAtPathOrArrayAlongPath(..., fieldNames[i])
            // modified fieldNames[i] to refer to the suffix of the path immediately following the
            // 'arrElt' array value. If we haven't reached the end of this indexed field yet, then
            // we must have traversed through 'arrElt'.
            invariant(fieldIsArray);

            StringData part = fieldNames[i];
            part = part.substr(0, part.find('.'));
            subPositionalInfo[i].positionallyIndexedElt = arrObj[part];
            if (subPositionalInfo[i].positionallyIndexedElt.eoo()) {
                // We aren't indexing a particular element of the 'arrElt' array value, so it may be
                // expanded into multiple elements. It can therefore cause the index to be multikey.
                if (multikeyPaths) {
                    // We need to determine which component of the indexed field causes the index to
                    // be multikey as a result of the 'arrElt' array value. Since
                    //
                    //   NumComponents("<pathPrefix>") + NumComponents("<pathSuffix>")
                    //       = NumComponents("<pathPrefix>.<pathSuffix>"),
                    //
                    // we can compute the number of components in a prefix of the indexed field by
                    // subtracting the number of components in the suffix 'fieldNames[i]' from the
                    // number of components in the indexed field '_fieldNames[i]'.
                    //
                    // For example, consider the indexed field "a.b.c" and the suffix "c". The path
                    // "a.b.c" has 3 components and the suffix "c" has 1 component. Subtracting the
                    // latter from the former yields the number of components in the prefix "a.b",
                    // i.e. 2.
                    size_t fullPathLength = _pathLengths[i];
                    size_t suffixPathLength = FieldRef{fieldNames[i]}.numParts();
                    invariant(suffixPathLength < fullPathLength);
                    arrComponents[i] = fullPathLength - suffixPathLength - 1;
                }
                continue;
            }

            // We're indexing an array element by its position. Traverse the remainder of the
            // field path now.
            //
            // Indexing an array element by its position selects a particular element of the
            // 'arrElt' array value when generating keys. It therefore cannot cause the index to be
            // multikey.
            subPositionalInfo[i].arrayObj = arrObj;
            subPositionalInfo[i].remainingPath = fieldNames[i];
            subPositionalInfo[i].dottedElt = dps::extractElementAtPathOrArrayAlongPath(
                arrObj, subPositionalInfo[i].remainingPath);
        }

        // Generate a key for each element of the indexed array.
        for (const auto arrObjElem : arrObj) {
            _getKeysArrEltFixed(&fieldNames,
                                &fixed,
                                arrObjElem,
                                keys,
                                numNotFound,
                                arrElt,
                                arrIdxs,
                                mayExpandArrayUnembedded,
                                subPositionalInfo,
                                multikeyPaths);
        }
    }

    // Record multikey path components.
    if (multikeyPaths) {
        for (size_t i = 0; i < arrComponents.size(); ++i) {
            if (auto arrComponent = arrComponents[i]) {
                (*multikeyPaths)[i].insert(*arrComponent);
            }
        }
    }
}
Exemple #17
0
    Status parseNumberFromStringWithBase(
            StringData stringValue, int base, NumberType* result) {

        typedef ::std::numeric_limits<NumberType> limits;

        if (base == 1 || base < 0 || base > 36)
            return Status(ErrorCodes::BadValue, "Invalid base", 0);

        bool isNegative = false;
        StringData str = _extractBase(_extractSign(stringValue, &isNegative), base, &base);

        if (str.empty())
            return Status(ErrorCodes::FailedToParse, "No digits");

        NumberType n(0);
        if (isNegative) {
            if (limits::is_signed) {
                for (size_t i = 0; i < str.size(); ++i) {
                    NumberType digitValue = NumberType(_digitValue(str[i]));
                    if (int(digitValue) >= base) {
                        return Status(ErrorCodes::FailedToParse,
                                      "Bad digit \"" + str.substr(i, 1).toString() +
                                      "\" while parsing " + stringValue.toString());
                    }

// MSVC: warning C4146: unary minus operator applied to unsigned type, result still unsigned
// This code is statically known to be dead when NumberType is unsigned, so the warning is not real
#pragma warning(push)
#pragma warning(disable:4146)
                    if ((NumberType(limits::min() / base) > n) ||
                        ((limits::min() - NumberType(n * base)) > -digitValue)) {
#pragma warning(pop)

                        return Status(ErrorCodes::FailedToParse, "Underflow");
                    }

                    n *= NumberType(base);
                    n -= NumberType(digitValue);
                }
            }
            else {
                return Status(ErrorCodes::FailedToParse, "Negative value");
            }
        }
        else {
            for (size_t i = 0; i < str.size(); ++i) {
                NumberType digitValue = NumberType(_digitValue(str[i]));
                if (int(digitValue) >= base) {
                    return Status(ErrorCodes::FailedToParse,
                                  "Bad digit \"" + str.substr(i, 1).toString() +
                                  "\" while parsing " + stringValue.toString());
                }
                if ((NumberType(limits::max() / base) < n) ||
                    (NumberType(limits::max() - n * base) < digitValue)) {

                    return Status(ErrorCodes::FailedToParse, "Overflow");
                }

                n *= NumberType(base);
                n += NumberType(digitValue);
            }
        }
        *result = n;
        return Status::OK();
    }
Exemple #18
0
StatusWith<std::tuple<bool, std::string>> SaslSCRAMServerMechanism<Policy>::_secondStep(
    OperationContext* opCtx, StringData inputData) {
    const auto badCount = [](int got) {
        return Status(ErrorCodes::BadValue,
                      str::stream()
                          << "Incorrect number of arguments for second SCRAM client message, got "
                          << got
                          << " expected at least 3");
    };

    /**
     * client-final-message-without-proof := cbind ',' nonce ',' [ ',' extensions ]
     * client-final-message := client-final-message-without-proof ',' proof
     */
    const auto last_comma = inputData.rfind(',');
    if (last_comma == std::string::npos) {
        return badCount(1);
    }

    // add client-final-message-without-proof to authMessage
    const auto client_final_message_without_proof = inputData.substr(0, last_comma);
    _authMessage += "," + client_final_message_without_proof.toString();

    const auto last_field = inputData.substr(last_comma + 1);
    if ((last_field.size() < 3) || !last_field.startsWith("p=")) {
        return Status(ErrorCodes::BadValue,
                      str::stream() << "Incorrect SCRAM ClientProof: " << last_field);
    }
    const auto proof = last_field.substr(2);

    const auto input = StringSplitter::split(client_final_message_without_proof.toString(), ",");
    if (input.size() < 2) {
        // Add count for proof back on.
        return badCount(input.size() + 1);
    }

    if (!str::startsWith(input[0], "c=") || input[0].size() < 3) {
        return Status(ErrorCodes::BadValue,
                      str::stream() << "Incorrect SCRAM channel binding: " << input[0]);
    }
    const auto cbind = input[0].substr(2);

    if (!str::startsWith(input[1], "r=") || input[1].size() < 6) {
        return Status(ErrorCodes::BadValue,
                      str::stream() << "Incorrect SCRAM client|server nonce: " << input[1]);
    }
    const auto nonce = input[1].substr(2);

    // Concatenated nonce sent by client should equal the one in server-first-message
    if (nonce != _nonce) {
        return Status(ErrorCodes::BadValue,
                      str::stream()
                          << "Unmatched SCRAM nonce received from client in second step, expected "
                          << _nonce
                          << " but received "
                          << nonce);
    }

    // Do server side computations, compare storedKeys and generate client-final-message
    // AuthMessage     := client-first-message-bare + "," +
    //                    server-first-message + "," +
    //                    client-final-message-without-proof
    // ClientSignature := HMAC(StoredKey, AuthMessage)
    // ClientKey := ClientSignature XOR ClientProof
    // ServerSignature := HMAC(ServerKey, AuthMessage)

    if (!_secrets.verifyClientProof(_authMessage, base64::decode(proof.toString()))) {
        return Status(ErrorCodes::AuthenticationFailed,
                      "SCRAM authentication failed, storedKey mismatch");
    }

    StringBuilder sb;
    // ServerSignature := HMAC(ServerKey, AuthMessage)
    sb << "v=" << _secrets.generateServerSignature(_authMessage);

    return std::make_tuple(false, sb.str());
}
Exemple #19
0
StatusWith<std::tuple<bool, std::string>> SaslSCRAMServerMechanism<Policy>::_firstStep(
    OperationContext* opCtx, StringData inputData) {
    const auto badCount = [](int got) {
        return Status(ErrorCodes::BadValue,
                      str::stream()
                          << "Incorrect number of arguments for first SCRAM client message, got "
                          << got
                          << " expected at least 3");
    };

    /**
     * gs2-cbind-flag := ("p=" cb-name) / 'y' / 'n'
     * gs2-header := gs2-cbind-flag ',' [ authzid ] ','
     * reserved-mext := "m=" 1*(value-char)
     * client-first-message-bare := [reserved-mext  ','] username ',' nonce [',' extensions]
     * client-first-message := gs2-header client-first-message-bare
     */
    const auto gs2_cbind_comma = inputData.find(',');
    if (gs2_cbind_comma == std::string::npos) {
        return badCount(1);
    }
    const auto gs2_cbind_flag = inputData.substr(0, gs2_cbind_comma);
    if (gs2_cbind_flag.startsWith("p=")) {
        return Status(ErrorCodes::BadValue, "Server does not support channel binding");
    }

    if ((gs2_cbind_flag != "y") && (gs2_cbind_flag != "n")) {
        return Status(ErrorCodes::BadValue,
                      str::stream() << "Incorrect SCRAM client message prefix: " << gs2_cbind_flag);
    }

    const auto gs2_header_comma = inputData.find(',', gs2_cbind_comma + 1);
    if (gs2_header_comma == std::string::npos) {
        return badCount(2);
    }
    auto authzId = inputData.substr(gs2_cbind_comma + 1, gs2_header_comma - (gs2_cbind_comma + 1));
    if (authzId.size()) {
        if (authzId.startsWith("a=")) {
            authzId = authzId.substr(2);
        } else {
            return Status(ErrorCodes::BadValue,
                          str::stream() << "Incorrect SCRAM authzid: " << authzId);
        }
    }

    const auto client_first_message_bare = inputData.substr(gs2_header_comma + 1);
    if (client_first_message_bare.startsWith("m=")) {
        return Status(ErrorCodes::BadValue, "SCRAM mandatory extensions are not supported");
    }

    /* StringSplitter::split() will ignore consecutive delimiters.
     * e.g. "foo,,bar" => {"foo","bar"}
     * This makes our implementation of SCRAM *slightly* more generous
     * in what it will accept than the standard calls for.
     *
     * This does not impact _authMessage, as it's composed from the raw
     * string input, rather than the output of the split operation.
     */
    const auto input = StringSplitter::split(client_first_message_bare.toString(), ",");

    if (input.size() < 2) {
        // gs2-header is not included in this count, so add it back in.
        return badCount(input.size() + 2);
    }

    if (!str::startsWith(input[0], "n=") || input[0].size() < 3) {
        return Status(ErrorCodes::BadValue,
                      str::stream() << "Invalid SCRAM user name: " << input[0]);
    }
    ServerMechanismBase::_principalName = input[0].substr(2);
    decodeSCRAMUsername(ServerMechanismBase::_principalName);

    if (!authzId.empty() && ServerMechanismBase::_principalName != authzId) {
        return Status(ErrorCodes::BadValue,
                      str::stream() << "SCRAM user name " << ServerMechanismBase::_principalName
                                    << " does not match authzid "
                                    << authzId);
    }

    if (!str::startsWith(input[1], "r=") || input[1].size() < 6) {
        return Status(ErrorCodes::BadValue,
                      str::stream() << "Invalid SCRAM client nonce: " << input[1]);
    }
    const auto clientNonce = input[1].substr(2);


    // SERVER-16534, SCRAM-SHA-1 must be enabled for authenticating the internal user, so that
    // cluster members may communicate with each other. Hence ignore disabled auth mechanism
    // for the internal user.
    UserName user(ServerMechanismBase::ServerMechanismBase::_principalName,
                  ServerMechanismBase::getAuthenticationDatabase());
    if (!sequenceContains(saslGlobalParams.authenticationMechanisms, "SCRAM-SHA-1") &&
        user != internalSecurity.user->getName()) {
        return Status(ErrorCodes::BadValue, "SCRAM-SHA-1 authentication is disabled");
    }

    // The authentication database is also the source database for the user.
    User* userObj;
    auto authManager = AuthorizationManager::get(opCtx->getServiceContext());

    Status status = authManager->acquireUser(opCtx, user, &userObj);
    if (!status.isOK()) {
        return status;
    }

    User::CredentialData credentials = userObj->getCredentials();
    UserName userName = userObj->getName();

    authManager->releaseUser(userObj);

    _scramCredentials = credentials.scram<HashBlock>();

    if (!_scramCredentials.isValid()) {
        // Check for authentication attempts of the __system user on
        // systems started without a keyfile.
        if (userName == internalSecurity.user->getName()) {
            return Status(ErrorCodes::AuthenticationFailed,
                          "It is not possible to authenticate as the __system user "
                          "on servers started without a --keyFile parameter");
        } else {
            return Status(ErrorCodes::AuthenticationFailed,
                          "Unable to perform SCRAM authentication for a user with missing "
                          "or invalid SCRAM credentials");
        }
    }

    _secrets = scram::Secrets<HashBlock>("",
                                         base64::decode(_scramCredentials.storedKey),
                                         base64::decode(_scramCredentials.serverKey));

    // Generate server-first-message
    // Create text-based nonce as base64 encoding of a binary blob of length multiple of 3
    const int nonceLenQWords = 3;
    uint64_t binaryNonce[nonceLenQWords];

    std::unique_ptr<SecureRandom> sr(SecureRandom::create());

    binaryNonce[0] = sr->nextInt64();
    binaryNonce[1] = sr->nextInt64();
    binaryNonce[2] = sr->nextInt64();

    _nonce =
        clientNonce + base64::encode(reinterpret_cast<char*>(binaryNonce), sizeof(binaryNonce));
    StringBuilder sb;
    sb << "r=" << _nonce << ",s=" << _scramCredentials.salt
       << ",i=" << _scramCredentials.iterationCount;
    std::string outputData = sb.str();

    // add client-first-message-bare and server-first-message to _authMessage
    _authMessage = client_first_message_bare.toString() + "," + outputData;

    return std::make_tuple(false, std::move(outputData));
}
Exemple #20
0
    bool processObj(const BSONObj &obj) {
        if (obj.hasField("$err")) {
            log() << "error getting oplog: " << obj << endl;
            return false;
        }

        static const char *names[] = {"ts", "op", "ns", "o", "b"};
        BSONElement fields[5];
        obj.getFields(5, names, fields);

        BSONElement &tsElt = fields[0];
        if (!tsElt.ok()) {
            log() << "oplog format error: " << obj << " missing 'ts' field." << endl;
            return false;
        }
        if (tsElt.type() != Date && tsElt.type() != Timestamp) {
            log() << "oplog format error: " << obj << " wrong 'ts' field type." << endl;
            return false;
        }
        _thisTime = OpTime(tsElt.date());

        BSONElement &opElt = fields[1];
        if (!opElt.ok()) {
            log() << "oplog format error: " << obj << " missing 'op' field." << endl;
            return false;
        }
        StringData op = opElt.Stringdata();

        // nop
        if (op == "n") {
            return true;
        }
        // "presence of a database"
        if (op == "db") {
            return true;
        }
        if (op != "c" && op != "i" && op != "u" && op != "d") {
            log() << "oplog format error: " << obj << " has an invalid 'op' field of '" << op << "'." << endl;
            return false;
        }

        if (op != "i" && !_insertBuf.empty()) {
            flushInserts();
        }

        BSONElement &nsElt = fields[2];
        if (!nsElt.ok()) {
            log() << "oplog format error: " << obj << " missing 'ns' field." << endl;
            return false;
        }
        StringData ns = nsElt.Stringdata();
        size_t i = ns.find('.');
        if (i == string::npos) {
            log() << "oplog format error: invalid namespace '" << ns << "' in op " << obj << "." << endl;
            return false;
        }
        StringData dbname = ns.substr(0, i);
        StringData collname = ns.substr(i + 1);

        BSONElement &oElt = fields[3];
        if (!oElt.ok()) {
            log() << "oplog format error: " << obj << " missing 'o' field." << endl;
            return false;
        }
        BSONObj o = obj["o"].Obj();

        if (op == "c") {
            if (collname != "$cmd") {
                log() << "oplog format error: invalid namespace '" << ns << "' for command in op " << obj << "." << endl;
                return false;
            }
            BSONObj info;
            bool ok = _conn.runCommand(dbname.toString(), o, info);
            if (!ok) {
                StringData fieldName = o.firstElementFieldName();
                BSONElement errmsgElt = info["errmsg"];
                StringData errmsg = errmsgElt.type() == String ? errmsgElt.Stringdata() : "";
                bool isDropIndexes = (fieldName == "dropIndexes" || fieldName == "deleteIndexes");
                if (((fieldName == "drop" || isDropIndexes) && errmsg == "ns not found") ||
                    (isDropIndexes && (errmsg == "index not found" || errmsg.find("can't find index with key:") == 0))) {
                    // This is actually ok.  We don't mind dropping something that's not there.
                    LOG(1) << "Tried to replay " << o << ", got " << info << ", ignoring." << endl;
                }
                else {
                    log() << "replay of command " << o << " failed: " << info << endl;
                    return false;
                }
            }
        }
        else {
            string nsstr = ns.toString();
            if (op == "i") {
                if (collname == "system.indexes") {
                    // Can't ensure multiple indexes in the same batch.
                    flushInserts();

                    // For now, we need to strip out any background fields from
                    // ensureIndex.  Once we do hot indexing we can do something more
                    // like what vanilla applyOperation_inlock does.
                    if (o["background"].trueValue()) {
                        BSONObjBuilder builder;
                        BSONObjIterator it(o);
                        while (it.more()) {
                            BSONElement e = it.next();
                            if (strncmp(e.fieldName(), "background", sizeof("background")) != 0) {
                                builder.append(e);
                            }
                        }
                        o = builder.obj();
                    }
                    // We need to warn very carefully about dropDups.
                    if (o["dropDups"].trueValue()) {
                        BSONObjBuilder builder;
                        BSONObjIterator it(o);
                        while (it.more()) {
                            BSONElement e = it.next();
                            if (strncmp(e.fieldName(), "dropDups", sizeof("dropDups")) != 0) {
                                builder.append(e);
                            }
                        }
                        warning() << "Detected an ensureIndex with dropDups: true in " << o << "." << endl;
                        warning() << "This option is not supported in TokuMX, because it deletes arbitrary data." << endl;
                        warning() << "If it were replayed, it could result in a completely different data set than the source database." << endl;
                        warning() << "We will attempt to replay it without dropDups, but if that fails, you must restart your migration process." << endl;
                        _conn.insert(nsstr, o);
                        string err = _conn.getLastError(dbname.toString(), false, false);
                        if (!err.empty()) {
                            log() << "replay of operation " << obj << " failed: " << err << endl;
                            warning() << "You cannot continue processing this replication stream.  You need to restart the migration process." << endl;
                            _running = false;
                            _logAtExit = false;
                            return true;
                        }
                    }
                }
                pushInsert(nsstr, o);
                // Don't call GLE or update _maxOpTimeSynced yet.
                _thisTime = OpTime();
                return true;
            }
            else if (op == "u") {
                BSONElement o2Elt = obj["o2"];
                if (!o2Elt.ok()) {
                    log() << "oplog format error: " << obj << " missing 'o2' field." << endl;
                    return false;
                }
                BSONElement &bElt = fields[4];
                bool upsert = bElt.booleanSafe();
                BSONObj o2 = o2Elt.Obj();
                _conn.update(nsstr, o2, o, upsert, false);
            }
            else if (op == "d") {
                BSONElement &bElt = fields[4];
                bool justOne = bElt.booleanSafe();
                _conn.remove(nsstr, o, justOne);
            }
            string err = _conn.getLastError(dbname.toString(), false, false);
            if (!err.empty()) {
                log() << "replay of operation " << obj << " failed: " << err << endl;
                return false;
            }
        }

        // If we got here, we completed the operation successfully.
        _maxOpTimeSynced = _thisTime;
        _thisTime = OpTime();
        return true;
    }
Exemple #21
0
inline StringData table_name_to_class_name(StringData table_name)
{
    REALM_ASSERT(table_name.begins_with("class_"));
    return table_name.substr(6);
}