1919
2020#include " mongo/platform/basic.h"
2121
22+ #include " mongo/base/init.h"
2223#include " mongo/bson/util/bson_extract.h"
2324#include " mongo/bson/util/builder.h"
2425#include " mongo/client/constants.h"
4445#include " mongo/util/net/ssl_manager.h"
4546#include " mongo/util/password_digest.h"
4647
48+ #include < boost/algorithm/string/case_conv.hpp>
4749#include < boost/algorithm/string/classification.hpp>
4850#include < boost/algorithm/string/predicate.hpp>
4951#include < boost/algorithm/string/split.hpp>
@@ -139,12 +141,53 @@ namespace mongo {
139141 _string = ss.str ();
140142 }
141143
144+ namespace {
145+ const char kAuthMechanismPropertiesKey [] = " mechanism_properties" ;
146+
147+ // CANONICALIZE_HOST_NAME is currently unsupported
148+ const char kAuthServiceName [] = " SERVICE_NAME" ;
149+ const char kAuthServiceRealm [] = " SERVICE_REALM" ;
150+
151+ const char * const kSupportedAuthMechanismProperties [] = {
152+ kAuthServiceName ,
153+ kAuthServiceRealm
154+ };
155+
156+ // bootleg std::end for c-style arrays.
157+ template <class T , std::size_t N>
158+ T* endOf (T (&arr)[N]) { return arr + N; }
159+
160+ BSONObj parseAuthMechanismProperties (const std::string& propStr) {
161+ BSONObjBuilder bob;
162+ std::vector<std::string> props;
163+ boost::algorithm::split (props, propStr, boost::algorithm::is_any_of (" ,:" ));
164+ for (std::vector<std::string>::const_iterator it = props.begin ();
165+ it != props.end (); ++it) {
166+ std::string prop ((boost::algorithm::to_upper_copy (*it))); // normalize case
167+ uassert (ErrorCodes::FailedToParse,
168+ str::stream () << " authMechanismProperty: " << *it
169+ << " is not supported" ,
170+ std::count (kSupportedAuthMechanismProperties ,
171+ endOf (kSupportedAuthMechanismProperties ),
172+ prop));
173+ ++it;
174+ uassert (ErrorCodes::FailedToParse,
175+ str::stream () << " authMechanismProperty: "
176+ << prop << " must have a value" ,
177+ it != props.end ());
178+ bob.append (prop, *it);
179+ }
180+ return bob.obj ();
181+ }
182+ } // namespace
183+
142184 BSONObj ConnectionString::_makeAuthObjFromOptions () const {
143185 BSONObjBuilder bob;
144186
145187 // Add the username and optional password
146188 invariant (!_user.empty ());
147- bob.append (" user" , _user);
189+ std::string username (_user); // may have to tack on service realm before we append
190+
148191 if (!_password.empty ())
149192 bob.append (" pwd" , _password);
150193
@@ -156,10 +199,35 @@ namespace mongo {
156199 if (!elt.eoo ())
157200 bob.appendAs (elt, " mechanism" );
158201
202+ elt = _options.getField (" authMechanismProperties" );
203+ if (!elt.eoo ()) {
204+ BSONObj parsed (parseAuthMechanismProperties (elt.String ()));
205+
206+ bool hasNameProp = parsed.hasField (kAuthServiceName );
207+ bool hasRealmProp = parsed.hasField (kAuthServiceRealm );
208+
209+ uassert (ErrorCodes::FailedToParse,
210+ " Cannot specify both gssapiServiceName and SERVICE_NAME" ,
211+ !(hasNameProp && _options.hasField (" gssapiServiceName" )));
212+ // we append the parsed object so that mechanisms that don't accept it can assert.
213+ bob.append (kAuthMechanismPropertiesKey , parsed);
214+ // we still append using the old way the SASL code expects it
215+ if (hasNameProp) {
216+ bob.append (" serviceName" , parsed[kAuthServiceName ].String ());
217+ }
218+ // if we specified a realm, we just append it to the username as the SASL code
219+ // expects it that way.
220+ if (hasRealmProp) {
221+ username.append (" @" ).append (parsed[kAuthServiceRealm ].String ());
222+ }
223+ }
224+
159225 elt = _options.getField (" gssapiServiceName" );
160226 if (!elt.eoo ())
161227 bob.appendAs (elt, " serviceName" );
162228
229+ bob.append (" user" , username);
230+
163231 return bob.obj ();
164232 }
165233
@@ -794,6 +862,10 @@ namespace mongo {
794862 saslCommandDigestPasswordFieldName,
795863 true ,
796864 &digestPassword));
865+ uassert (ErrorCodes::AuthenticationFailed,
866+ " Cannot set mechanism_properties when using MONGODB_CR" ,
867+ !params.hasField (kAuthMechanismPropertiesKey ));
868+
797869 BSONObj result;
798870 uassert (result[" code" ].Int (),
799871 result.toString (),
@@ -828,6 +900,10 @@ namespace mongo {
828900 getSSLManager ()->getClientSubjectName () + " \" " ,
829901 user == getSSLManager ()->getClientSubjectName ());
830902
903+ uassert (ErrorCodes::AuthenticationFailed,
904+ " Cannot set mechanism_properties when using MONGODB_X509" ,
905+ !params.hasField (kAuthMechanismPropertiesKey ));
906+
831907 BSONObj result;
832908 uassert (result[" code" ].Int (),
833909 result.toString (),
0 commit comments