[SERVER-14830] SCRAM-SHA-1 conversations fail to complete Created: 07/Aug/14  Updated: 10/Dec/14  Resolved: 18/Aug/14

Status: Closed
Project: Core Server
Component/s: Security
Affects Version/s: 2.7.2, 2.7.3, 2.7.4
Fix Version/s: None

Type: Bug Priority: Major - P3
Reporter: Bernie Hackett Assignee: Andreas Nilsson
Resolution: Done Votes: 0
Labels: None
Remaining Estimate: Not Specified
Time Spent: Not Specified
Original Estimate: Not Specified

Issue Links:
Depends
is depended on by SERVER-7596 Support SCRAM-SHA-1 SASL Mechanism Closed
is depended on by DRIVERS-166 Implement the SCRAM-SHA-1 SASL Mechanism Closed
Operating System: ALL
Participants:

 Description   

The basic SCRAM-SHA-1 support added in 2.7.2 seems to be broken. A SASL conversation can be completed following RFC 5802 (the v field value returned from the server matches the server signature calculated on the client), but the server returns

{done: false}

and any subsequent operations fail authorization.

Example user:

> db.system.users.findOne()
{
	"_id" : "admin.admin",
	"user" : "admin",
	"db" : "admin",
	"credentials" : {
		"SCRAM-SHA-1" : {
			"iterationCount" : 10000,
			"salt" : "3L2ChDOtpFE3t7dbOwAPdQ==",
			"storedKey" : "wnUNZ+Wl/B+k1+RBNfb4hihgapo=",
			"serverKey" : "3Q5qYn40wXktIB2M3SkK+czdXNg="
		},
		"MONGODB-CR" : "e4e538f5dcb52537cad02bbf8491693c"
	},
	"roles" : [
		{
			"role" : "root",
			"db" : "admin"
		}
	]
}
> 

Example authentication attempt with debug output:

>>> c.admin.authenticate('admin', 'pass', mechanism='SCRAM-SHA-1')
 
C: SON([('saslStart', 1), ('mechanism', 'SCRAM-SHA-1'), ('payload', Binary(b'n,,n=admin,r=NzcyOTU5MDIwNDAyNTc3NA==', 0)), ('autoAuthorize', 1)])
S: {'done': False, 'payload': b'r=NzcyOTU5MDIwNDAyNTc3NA==YIFOULW05uMS80e5sLcUAbWVhJZtAZ5E,s=3L2ChDOtpFE3t7dbOwAPdQ==,i=10000', 'conversationId': 1, 'ok': 1.0}
 
server provided salt: b'3L2ChDOtpFE3t7dbOwAPdQ=='
client generated storedKey: b'wnUNZ+Wl/B+k1+RBNfb4hihgapo='
client generated serverKey: b'3Q5qYn40wXktIB2M3SkK+czdXNg='
client generated v: b'ss94QBaOXP1cQGYhgjuyDDMipO8='
 
C: SON([('saslContinue', 1), ('conversationId', 1), ('payload', Binary(b'c=biws,r=NzcyOTU5MDIwNDAyNTc3NA==YIFOULW05uMS80e5sLcUAbWVhJZtAZ5E,p=yyZMbWaB2Yo7HBqFlr+9I6N+ho0=', 0))])
S: {'done': False, 'payload': b'v=ss94QBaOXP1cQGYhgjuyDDMipO8=', 'conversationId': 1, 'ok': 1.0}

Server binaries were built with the enterprise modules. Mongod started like so:

mongod --dbpath ~/data/db --auth --setParameter authenticationMechanisms=SCRAM-SHA-1,MONGODB-CR



 Comments   
Comment by Hannes Magnusson [ 27/Aug/14 ]

After further investigation, this is not a bug but intentional behavior of cyrus-sasl

Comment by Andreas Nilsson [ 11/Aug/14 ]

You are right, Cyrus SASL's SCRAM implementation is using three steps. The sasl_server_step function returns SASL_CONTINUE on the second step even though the conversation is finished. It returns SASL_OK on the third call.

Not sure if this is a bug in the library or intentional, investigating.

Comment by Bernie Hackett [ 11/Aug/14 ]

I figured it out. The problem is the server expects a 3rd challenge. After comparing v, if I send an empty challenge back to the server the conversation completes and authorization for subsequent ops works.

So, the server should only require two challenges according to the RFC. The third empty challenge makes no sense.

Here's the new conversation with the extra challenge:

>>> import pymongo
>>> c = pymongo.MongoClient()
>>> c.admin.authenticate('admin', 'pass', mechanism='SCRAM-SHA-1')
 
C: SON([(u'saslStart', 1), (u'mechanism', u'SCRAM-SHA-1'), (u'payload', Binary('n,,n=admin,r=MDc1NzQ3NDA2MTU5Nw==', 0)), (u'autoAuthorize', 1)])
S: {u'conversationId': 1, u'ok': 1.0, u'done': False, u'payload': Binary('r=MDc1NzQ3NDA2MTU5Nw==ifU50J2lPcglfCKeAgZmB6o13b+lQiO+,s=qO2AOxr9U3bw+2Y4UjKbIg==,i=10000', 0)}
 
server provided salt: 'qO2AOxr9U3bw+2Y4UjKbIg=='
client generated storedKey: '/sTbSy4VONe2FxB6Qc+c4+OuvBo='
client generated serverKey: 'Xjs3eTp4NIXaQvIyrOfFYMGm0/A='
client generated v: 'ms733XGZwzyibxPdvST8ibsu/Gc='
 
C: SON([(u'saslContinue', 1), (u'conversationId', 1), (u'payload', Binary('c=biws,r=MDc1NzQ3NDA2MTU5Nw==ifU50J2lPcglfCKeAgZmB6o13b+lQiO+,p=7yX5Hi7B2J0ba5CVUnpk8iFKFAo=', 0))])
S: {u'conversationId': 1, u'ok': 1.0, u'done': False, u'payload': Binary('v=ms733XGZwzyibxPdvST8ibsu/Gc=', 0)}
 
 
C: SON([(u'saslContinue', 1), (u'conversationId', 1), (u'payload', Binary('', 0))])
S: {u'conversationId': 1, u'ok': 1.0, u'done': True, u'payload': Binary('', 0)}
 
True
>>> c.admin.system.users.count()
1

Generated at Thu Feb 08 03:36:06 UTC 2024 using Jira 9.7.1#970001-sha1:2222b88b221c4928ef0de3161136cc90c8356a66.