StatusWith<bool> SaslPLAINServerConversation::step(StringData inputData, std::string* outputData) { // Expecting user input on the form: user\0user\0pwd std::string input = inputData.toString(); std::string pwd = ""; try { _user = input.substr(0, inputData.find('\0')); pwd = input.substr(inputData.find('\0', _user.size() + 1) + 1); } catch (std::out_of_range& exception) { return StatusWith<bool>(ErrorCodes::AuthenticationFailed, mongoutils::str::stream() << "Incorrectly formatted PLAIN client message"); } User* userObj; // The authentication database is also the source database for the user. Status status = _saslAuthSession->getAuthorizationSession()->getAuthorizationManager().acquireUser( _saslAuthSession->getOpCtxt(), UserName(_user, _saslAuthSession->getAuthenticationDatabase()), &userObj); if (!status.isOK()) { return StatusWith<bool>(status); } const User::CredentialData creds = userObj->getCredentials(); _saslAuthSession->getAuthorizationSession()->getAuthorizationManager().releaseUser(userObj); std::string authDigest = createPasswordDigest(_user, pwd); if (!creds.password.empty()) { // Handle schemaVersion26Final (MONGODB-CR/SCRAM mixed mode) if (authDigest != creds.password) { return StatusWith<bool>(ErrorCodes::AuthenticationFailed, mongoutils::str::stream() << "Incorrect user name or password"); } } else { // Handle schemaVersion28SCRAM (SCRAM only mode) unsigned char storedKey[scram::hashSize]; unsigned char serverKey[scram::hashSize]; scram::generateSecrets( authDigest, reinterpret_cast<const unsigned char*>(base64::decode(creds.scram.salt).c_str()), 16, creds.scram.iterationCount, storedKey, serverKey); if (creds.scram.storedKey != base64::encode(reinterpret_cast<const char*>(storedKey), scram::hashSize)) { return StatusWith<bool>(ErrorCodes::AuthenticationFailed, mongoutils::str::stream() << "Incorrect user name or password"); } } *outputData = ""; return StatusWith<bool>(true); }
bool legalClientSystemNS(StringData ns) { if (ns == "local.system.replset") return true; if (ns.find(".system.users") != string::npos) return true; if (ns == "admin.system.roles") return true; if (ns == kConfigCollection) return true; if (ns == kLogicalTimeKeysCollection) return true; if (ns == "admin.system.new_users") return true; if (ns == "admin.system.backup_users") return true; if (ns.find(".system.js") != string::npos) return true; if (nsToCollectionSubstring(ns) == NamespaceString::kSystemDotViewsCollectionName) return true; return false; }
Status Database::validateDBName( StringData dbname ) { if ( dbname.size() <= 0 ) return Status( ErrorCodes::BadValue, "db name is empty" ); if ( dbname.size() >= 64 ) return Status( ErrorCodes::BadValue, "db name is too long" ); if ( dbname.find( '.' ) != string::npos ) return Status( ErrorCodes::BadValue, "db name cannot contain a ." ); if ( dbname.find( ' ' ) != string::npos ) return Status( ErrorCodes::BadValue, "db name cannot contain a space" ); #ifdef _WIN32 static const char* windowsReservedNames[] = { "con", "prn", "aux", "nul", "com1", "com2", "com3", "com4", "com5", "com6", "com7", "com8", "com9", "lpt1", "lpt2", "lpt3", "lpt4", "lpt5", "lpt6", "lpt7", "lpt8", "lpt9" }; string lower( dbname.toString() ); std::transform( lower.begin(), lower.end(), lower.begin(), ::tolower ); for ( size_t i = 0; i < (sizeof(windowsReservedNames) / sizeof(char*)); ++i ) { if ( lower == windowsReservedNames[i] ) { stringstream errorString; errorString << "db name \"" << dbname.toString() << "\" is a reserved name"; return Status( ErrorCodes::BadValue, errorString.str() ); } } #endif return Status::OK(); }
list<intrusive_ptr<DocumentSource>> DocumentSourceCount::createFromBson( BSONElement elem, const intrusive_ptr<ExpressionContext>& pExpCtx) { uassert(40156, str::stream() << "the count field must be a non-empty string", elem.type() == String); StringData elemString = elem.valueStringData(); uassert( 40157, str::stream() << "the count field must be a non-empty string", !elemString.empty()); uassert(40158, str::stream() << "the count field cannot be a $-prefixed path", elemString[0] != '$'); uassert(40159, str::stream() << "the count field cannot contain a null byte", elemString.find('\0') == string::npos); uassert(40160, str::stream() << "the count field cannot contain '.'", elemString.find('.') == string::npos); BSONObj groupObj = BSON("$group" << BSON("_id" << BSONNULL << elemString << BSON("$sum" << 1))); BSONObj projectObj = BSON("$project" << BSON("_id" << 0 << elemString << 1)); auto groupSource = DocumentSourceGroup::createFromBson(groupObj.firstElement(), pExpCtx); auto projectSource = DocumentSourceProject::createFromBson(projectObj.firstElement(), pExpCtx); return {groupSource, projectSource}; }
bool KVCatalog::isUserDataIdent(StringData ident) const { // Indexes, collections, and temporary RecordStore idents are all candidates for dropping when // the storage engine's metadata does not align with the catalog metadata. return ident.find("index-") != std::string::npos || ident.find("index/") != std::string::npos || ident.find("temp-") != std::string::npos || ident.find("collection-") != std::string::npos || ident.find("collection/") != std::string::npos; }
bool legalClientSystemNS( const StringData& ns , bool write ) { if( ns == "local.system.replset" ) return true; if ( ns.find( ".system.users" ) != string::npos ) return true; if ( ns == "admin.system.roles" ) return true; if ( ns == "admin.system.version" ) return true; if ( ns == "admin.system.new_users" ) return true; if ( ns == "admin.system.backup_users" ) return true; if ( ns.find( ".system.js" ) != string::npos ) return true; return false; }
bool getCanonicalIndexField( StringData fullName, string* out ) { // check if fieldName contains ".$" or ".###" substrings (#=digit) and skip them // however do not skip the first field even if it meets these criteria if ( fullName.find( '.' ) == string::npos ) return false; bool modified = false; StringBuilder buf; for ( size_t i=0; i<fullName.size(); i++ ) { char c = fullName[i]; if ( c != '.' ) { buf << c; continue; } if ( i + 1 == fullName.size() ) { // ends with '.' buf << c; continue; } // check for ".$", skip if present if ( fullName[i+1] == '$' ) { // only do this if its not something like $a if ( i + 2 >= fullName.size() || fullName[i+2] == '.' ) { i++; modified = true; continue; } } // check for ".###" for any number of digits (no letters) if ( isdigit( fullName[i+1] ) ) { size_t j = i; // skip digits while ( j+1 < fullName.size() && isdigit( fullName[j+1] ) ) j++; if ( j+1 == fullName.size() || fullName[j+1] == '.' ) { // only digits found, skip forward i = j; modified = true; continue; } } buf << c; } if ( !modified ) return false; *out = buf.str(); return true; }
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; }
bool MongoVersionRange::isInRange(const StringData& version) const { if (maxVersion == "") { // If a prefix of the version specified is excluded, the specified version is // excluded if (version.find(minVersion) == 0) return true; } else { // Range is inclusive, so make sure the end and beginning prefix excludes all // prefixed versions as above if (version.find(minVersion) == 0) return true; if (version.find(maxVersion) == 0) return true; if (versionCmp(minVersion, version) <= 0 && versionCmp(maxVersion, version) >= 0) { return true; } } return false; }
Status userAllowedWriteNS( const StringData& db, const StringData& coll ) { // validity checking if ( db.size() == 0 ) return Status( ErrorCodes::BadValue, "db cannot be blank" ); if ( !NamespaceString::validDBName( db ) ) return Status( ErrorCodes::BadValue, "invalid db name" ); if ( coll.size() == 0 ) return Status( ErrorCodes::BadValue, "collection cannot be blank" ); if ( !NamespaceString::validCollectionName( coll ) ) return Status( ErrorCodes::BadValue, "invalid collection name" ); if ( db.size() + 1 /* dot */ + coll.size() > Namespace::MaxNsColletionLen ) return Status( ErrorCodes::BadValue, str::stream() << "fully qualified namespace " << db << '.' << coll << " is too long " << "(max is " << Namespace::MaxNsColletionLen << " bytes)" ); // check spceial areas if ( db == "system" ) return Status( ErrorCodes::BadValue, "cannot use 'system' database" ); if ( coll.startsWith( "system." ) ) { if ( coll == "system.indexes" ) return Status::OK(); if ( coll == "system.js" ) return Status::OK(); if ( coll == "system.profile" ) return Status::OK(); if ( coll == "system.users" ) return Status::OK(); if ( db == "admin" ) { if ( coll == "system.version" ) return Status::OK(); if ( coll == "system.roles" ) return Status::OK(); if ( coll == "system.new_users" ) return Status::OK(); if ( coll == "system.backup_users" ) return Status::OK(); } if ( db == "local" ) { if ( coll == "system.replset" ) return Status::OK(); } return Status( ErrorCodes::BadValue, str::stream() << "cannot write to '" << db << "." << coll << "'" ); } // some special rules if ( coll.find( ".system." ) != string::npos ) { // this matches old (2.4 and older) behavior, but I'm not sure its a good idea return Status( ErrorCodes::BadValue, str::stream() << "cannot write to '" << db << "." << coll << "'" ); } return Status::OK(); }
StatusWith<bool> SaslPLAINServerConversation::step(const StringData& inputData, std::string* outputData) { // Expecting user input on the form: user\0user\0pwd std::string input = inputData.toString(); std::string pwd = ""; try { _user = input.substr(0, inputData.find('\0')); pwd = input.substr(inputData.find('\0', _user.size()+1)+1); } catch (std::out_of_range& exception) { return StatusWith<bool>(ErrorCodes::AuthenticationFailed, mongoutils::str::stream() << "Incorrectly formatted PLAIN client message"); } User* userObj; // The authentication database is also the source database for the user. Status status = _saslAuthSession->getAuthorizationSession()->getAuthorizationManager(). acquireUser(_saslAuthSession->getOpCtxt(), UserName(_user, _saslAuthSession->getAuthenticationDatabase()), &userObj); if (!status.isOK()) { return StatusWith<bool>(status); } const User::CredentialData creds = userObj->getCredentials(); _saslAuthSession->getAuthorizationSession()->getAuthorizationManager(). releaseUser(userObj); std::string authDigest = createPasswordDigest(_user, pwd); if (authDigest != creds.password) { return StatusWith<bool>(ErrorCodes::AuthenticationFailed, mongoutils::str::stream() << "Incorrect user name or password"); } *outputData = ""; return StatusWith<bool>(true); }
bool legalClientSystemNS(StringData ns, bool write) { if (ns == "local.system.replset") return true; if (ns.find(".system.users") != string::npos) return true; if (ns == "admin.system.roles") return true; if (ns == kConfigCollection) return true; if (ns == "admin.system.new_users") return true; if (ns == "admin.system.backup_users") return true; if (ns.find(".system.js") != string::npos) return true; return false; }
Status RegexMatchExpression::init(StringData path, StringData regex, StringData options) { if (regex.size() > MaxPatternSize) { return Status(ErrorCodes::BadValue, "Regular expression is too long"); } if (regex.find('\0') != std::string::npos) { return Status(ErrorCodes::BadValue, "Regular expression cannot contain an embedded null byte"); } if (options.find('\0') != std::string::npos) { return Status(ErrorCodes::BadValue, "Regular expression options string cannot contain an embedded null byte"); } _regex = regex.toString(); _flags = options.toString(); _re.reset(new pcrecpp::RE(_regex.c_str(), flags2options(_flags.c_str()))); return setPath(path); }
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; } }
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; }
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(); } }
// static BSONObj IndexLegacy::adjustIndexSpecObject(const BSONObj& obj) { string pluginName = IndexNames::findPluginName(obj.getObjectField("key")); if (IndexNames::TEXT == pluginName) { StringData desc = cc().desc(); if (desc.find("conn") == 0) { // this is to make sure we only complain for users // if you do get a text index created an a primary // want it to index on the secondary as well massert(16811, "text search not enabled", fts::isTextSearchEnabled() ); } return fts::FTSSpec::fixSpec(obj); } return obj; }
void ValueWriter::writeThis(BSONObjBuilder* b, StringData sd, ObjectWrapper::WriteFieldRecursionFrames* frames) { uassert(17279, str::stream() << "Exceeded depth limit of " << ObjectWrapper::kMaxWriteFieldDepth << " when converting js object to BSON. Do you have a cycle?", frames->size() < ObjectWrapper::kMaxWriteFieldDepth); // Null char should be at the end, not in the string uassert(16985, str::stream() << "JavaScript property (name) contains a null char " << "which is not allowed in BSON. " << (_originalParent ? _originalParent->jsonString() : ""), (std::string::npos == sd.find('\0'))); if (_value.isString()) { JSStringWrapper jsstr; b->append(sd, toStringData(&jsstr)); } else if (_value.isNumber()) { double val = toNumber(); // if previous type was integer, keep it int intval = static_cast<int>(val); if (val == intval && _originalParent) { // This makes copying an object of numbers O(n**2) :( BSONElement elmt = _originalParent->getField(sd); if (elmt.type() == mongo::NumberInt) { b->append(sd, intval); return; } } b->append(sd, val); } else if (_value.isObject()) { _writeObject(b, sd, frames); } else if (_value.isBoolean()) { b->appendBool(sd, _value.toBoolean()); } else if (_value.isUndefined()) { b->appendUndefined(sd); } else if (_value.isNull()) { b->appendNull(sd); } else { uasserted(16662, str::stream() << "unable to convert JavaScript property to mongo element " << sd); } }
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; } }
Status storageValid(const mb::ConstElement& elem, const bool deep) { if (!elem.ok()) return Status(ErrorCodes::BadValue, "Invalid elements cannot be stored."); // Field names of elements inside arrays are not meaningful in mutable bson, // so we do not want to validate them. // // TODO: Revisit how mutable handles array field names. We going to need to make // this better if we ever want to support ordered updates that can alter the same // element repeatedly; see SERVER-12848. const mb::ConstElement& parent = elem.parent(); const bool childOfArray = parent.ok() ? (parent.getType() == mongo::Array) : false; if (!childOfArray) { StringData fieldName = elem.getFieldName(); // Cannot start with "$", unless dbref if (fieldName[0] == '$') { Status status = validateDollarPrefixElement(elem, deep); if (!status.isOK()) return status; } else if (fieldName.find(".") != string::npos) { // Field name cannot have a "." in it. return Status(ErrorCodes::DottedFieldName, str::stream() << "The dotted field '" << elem.getFieldName() << "' in '" << mb::getFullName(elem) << "' is not valid for storage."); } } if (deep) { // Check children if there are any. Status s = storageValidChildren(elem, deep); if (!s.isOK()) return s; } return Status::OK(); }
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(); }
// ns is either a full namespace or "dbname." when invalidating for a whole db void ClientCursor::invalidate(const StringData &ns) { Lock::assertWriteLocked(ns); size_t dotpos = ns.find('.'); verify(dotpos != string::npos); bool isDB = (dotpos + 1) == ns.size(); // first (and only) dot is the last char { //cout << "\nTEMP invalidate " << ns << endl; Database *db = cc().database(); verify(db); verify( ns.startsWith(db->name()) ); for( LockedIterator i; i.ok(); ) { ClientCursor *cc = i.current(); bool shouldDelete = false; if (cc->c()->shouldDestroyOnNSDeletion() && cc->_db == db) { if (isDB) { // already checked that db matched above dassert( StringData(cc->_ns).startsWith(ns) ); shouldDelete = true; } else { if ( ns == cc->_ns ) shouldDelete = true; } } if ( shouldDelete ) { i.deleteAndAdvance(); } else { i.advance(); } } } }
StatusWith<bool> SaslPLAINServerConversation::step(StringData inputData, std::string* outputData) { if (_saslAuthSession->getAuthenticationDatabase() == "$external") { return Status(ErrorCodes::AuthenticationFailed, "PLAIN mechanism must be used with internal users"); } // Expecting user input on the form: [authz-id]\0authn-id\0pwd std::string input = inputData.toString(); SecureAllocatorAuthDomain::SecureString pwd = ""; try { size_t firstNull = inputData.find('\0'); if (firstNull == std::string::npos) { return Status( ErrorCodes::AuthenticationFailed, str::stream() << "Incorrectly formatted PLAIN client message, missing first NULL delimiter"); } size_t secondNull = inputData.find('\0', firstNull + 1); if (secondNull == std::string::npos) { return Status( ErrorCodes::AuthenticationFailed, str::stream() << "Incorrectly formatted PLAIN client message, missing second NULL delimiter"); } std::string authorizationIdentity = input.substr(0, firstNull); _user = input.substr(firstNull + 1, (secondNull - firstNull) - 1); if (_user.empty()) { return Status(ErrorCodes::AuthenticationFailed, str::stream() << "Incorrectly formatted PLAIN client message, empty username"); } else if (!authorizationIdentity.empty() && authorizationIdentity != _user) { return Status(ErrorCodes::AuthenticationFailed, str::stream() << "SASL authorization identity must match authentication identity"); } pwd = SecureAllocatorAuthDomain::SecureString(input.substr(secondNull + 1).c_str()); if (pwd->empty()) { return Status(ErrorCodes::AuthenticationFailed, str::stream() << "Incorrectly formatted PLAIN client message, empty password"); } } catch (std::out_of_range&) { return Status(ErrorCodes::AuthenticationFailed, mongoutils::str::stream() << "Incorrectly formatted PLAIN client message"); } User* userObj; // The authentication database is also the source database for the user. Status status = _saslAuthSession->getAuthorizationSession()->getAuthorizationManager().acquireUser( _saslAuthSession->getOpCtxt(), UserName(_user, _saslAuthSession->getAuthenticationDatabase()), &userObj); if (!status.isOK()) { return status; } const auto creds = userObj->getCredentials(); _saslAuthSession->getAuthorizationSession()->getAuthorizationManager().releaseUser(userObj); *outputData = ""; const auto sha256Status = trySCRAM<SHA256Block>(creds, pwd->c_str()); if (!sha256Status.isOK()) { return sha256Status; } if (sha256Status.getValue()) { return true; } const auto authDigest = createPasswordDigest(_user, pwd->c_str()); const auto sha1Status = trySCRAM<SHA1Block>(creds, authDigest); if (!sha1Status.isOK()) { return sha1Status; } if (sha1Status.getValue()) { return true; } return Status(ErrorCodes::AuthenticationFailed, str::stream() << "No credentials available."); }
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; }
StatusWith<bool> SaslPLAINServerConversation::step(StringData inputData, std::string* outputData) { if (_saslAuthSession->getAuthenticationDatabase() == "$external") { return Status(ErrorCodes::AuthenticationFailed, "PLAIN mechanism must be used with internal users"); } // Expecting user input on the form: [authz-id]\0authn-id\0pwd std::string input = inputData.toString(); SecureAllocatorAuthDomain::SecureString pwd = ""; try { size_t firstNull = inputData.find('\0'); if (firstNull == std::string::npos) { return Status( ErrorCodes::AuthenticationFailed, str::stream() << "Incorrectly formatted PLAIN client message, missing first NULL delimiter"); } size_t secondNull = inputData.find('\0', firstNull + 1); if (secondNull == std::string::npos) { return Status( ErrorCodes::AuthenticationFailed, str::stream() << "Incorrectly formatted PLAIN client message, missing second NULL delimiter"); } std::string authorizationIdentity = input.substr(0, firstNull); _user = input.substr(firstNull + 1, (secondNull - firstNull) - 1); if (_user.empty()) { return Status(ErrorCodes::AuthenticationFailed, str::stream() << "Incorrectly formatted PLAIN client message, empty username"); } else if (!authorizationIdentity.empty() && authorizationIdentity != _user) { return Status(ErrorCodes::AuthenticationFailed, str::stream() << "SASL authorization identity must match authentication identity"); } pwd = SecureAllocatorAuthDomain::SecureString(input.substr(secondNull + 1).c_str()); if (pwd->empty()) { return Status(ErrorCodes::AuthenticationFailed, str::stream() << "Incorrectly formatted PLAIN client message, empty password"); } } catch (std::out_of_range& exception) { return Status(ErrorCodes::AuthenticationFailed, mongoutils::str::stream() << "Incorrectly formatted PLAIN client message"); } User* userObj; // The authentication database is also the source database for the user. Status status = _saslAuthSession->getAuthorizationSession()->getAuthorizationManager().acquireUser( _saslAuthSession->getOpCtxt(), UserName(_user, _saslAuthSession->getAuthenticationDatabase()), &userObj); if (!status.isOK()) { return StatusWith<bool>(status); } const User::CredentialData creds = userObj->getCredentials(); _saslAuthSession->getAuthorizationSession()->getAuthorizationManager().releaseUser(userObj); std::string authDigest = createPasswordDigest(_user, pwd->c_str()); if (!creds.password.empty()) { // Handle schemaVersion26Final (MONGODB-CR/SCRAM mixed mode) if (authDigest != creds.password) { return StatusWith<bool>(ErrorCodes::AuthenticationFailed, mongoutils::str::stream() << "Incorrect user name or password"); } } else { // Handle schemaVersion28SCRAM (SCRAM only mode) std::string decodedSalt = base64::decode(creds.scram.salt); scram::SCRAMSecrets secrets = scram::generateSecrets(scram::SCRAMPresecrets( authDigest, std::vector<std::uint8_t>(reinterpret_cast<const std::uint8_t*>(decodedSalt.c_str()), reinterpret_cast<const std::uint8_t*>(decodedSalt.c_str()) + 16), creds.scram.iterationCount)); if (creds.scram.storedKey != base64::encode(reinterpret_cast<const char*>(secrets->storedKey.data()), secrets->storedKey.size())) { return StatusWith<bool>(ErrorCodes::AuthenticationFailed, mongoutils::str::stream() << "Incorrect user name or password"); } } *outputData = ""; return StatusWith<bool>(true); }
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(); }
void ClientCursor::invalidate(const StringData& ns) { Lock::assertWriteLocked(ns); size_t dot = ns.find( '.' ); verify( dot != string::npos ); // first (and only) dot is the last char bool isDB = dot == ns.size() - 1; Database *db = cc().database(); verify(db); verify(ns.startsWith(db->name())); recursive_scoped_lock cclock(ccmutex); // Look at all active non-cached Runners. These are the runners that are in auto-yield mode // that are not attached to the the client cursor. For example, all internal runners don't // need to be cached -- there will be no getMore. for (set<Runner*>::iterator it = nonCachedRunners.begin(); it != nonCachedRunners.end(); ++it) { Runner* runner = *it; const string& runnerNS = runner->ns(); if ( ( isDB && StringData(runnerNS).startsWith(ns) ) || ns == runnerNS ) { runner->kill(); } } // Look at all cached ClientCursor(s). The CC may have a Runner, a Cursor, or nothing (see // sharding_block.h). CCById::const_iterator it = clientCursorsById.begin(); while (it != clientCursorsById.end()) { ClientCursor* cc = it->second; // We're only interested in cursors over one db. if (cc->_db != db) { ++it; continue; } // Note that a valid ClientCursor state is "no cursor no runner." This is because // the set of active cursor IDs in ClientCursor is used as representation of query // state. See sharding_block.h. TODO(greg,hk): Move this out. if (NULL == cc->c() && NULL == cc->_runner.get()) { ++it; continue; } bool shouldDelete = false; // We will only delete CCs with runners that are not actively in use. The runners that // are actively in use are instead kill()-ed. if (NULL != cc->_runner.get()) { verify(NULL == cc->c()); if (isDB || cc->_runner->ns() == ns) { // If there is a pinValue >= 100, somebody is actively using the CC and we do // not delete it. Instead we notify the holder that we killed it. The holder // will then delete the CC. if (cc->_pinValue >= 100) { cc->_runner->kill(); } else { // pinvalue is <100, so there is nobody actively holding the CC. We can // safely delete it as nobody is holding the CC. shouldDelete = true; } } } // Begin cursor-only DEPRECATED else if (cc->c()->shouldDestroyOnNSDeletion()) { verify(NULL == cc->_runner.get()); if (isDB) { // already checked that db matched above dassert( StringData(cc->_ns).startsWith( ns ) ); shouldDelete = true; } else { if ( ns == cc->_ns ) { shouldDelete = true; } } } // End cursor-only DEPRECATED if (shouldDelete) { ClientCursor* toDelete = it->second; CursorId id = toDelete->cursorid(); delete toDelete; // We're not following the usual paradigm of saving it, ++it, and deleting the saved // 'it' because deleting 'it' might invalidate the next thing in clientCursorsById. // TODO: Why? it = clientCursorsById.upper_bound(id); } else { ++it; } } }
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); } } } }
bool KVCatalog::isUserDataIdent(StringData ident) const { return ident.find("index-") != std::string::npos || ident.find("index/") != std::string::npos || ident.find("collection-") != std::string::npos || ident.find("collection/") != std::string::npos; }
bool hasFunctionIdentifier(StringData code) { if (code.size() < 9 || code.find("function") != 0) return false; return code[8] == ' ' || code[8] == '('; }