[SERVER-43950] changeOwnPassword privilege action should not allow users to list collections in the databases that they don't have read access to Created: 10/Oct/19  Updated: 27/Oct/23  Resolved: 05/Feb/21

Status: Closed
Project: Core Server
Component/s: Security
Affects Version/s: None
Fix Version/s: None

Type: Bug Priority: Minor - P4
Reporter: Harshad Dhavale Assignee: Spencer Jackson
Resolution: Works as Designed Votes: 1
Labels: None
Remaining Estimate: Not Specified
Time Spent: Not Specified
Original Estimate: Not Specified

Operating System: ALL
Steps To Reproduce:

The following commands can be used to reproduce this issue in a MongoDB with auth enabled:

 

changeOwnPasswordROLE and user333

 

db.createRole(
{
 role: "changeOwnPasswordROLE",
 privileges: [
   { resource: { db: "", collection: "" }, actions: [ "changeOwnPassword" ] }
 ],
 roles: [
 ]
},
{ w: "majority" , wtimeout: 5000 }
)use admindb.createUser(
{
 "user" : "user333",
 "pwd" : "user333",
 "roles" : [
   { "role" : "changeOwnPasswordROLE", "db" : "admin" },
   { "role" : "read", "db" : "test" }
 ]
}
)db.system.roles.find( {role:"changeOwnPasswordROLE"} ).pretty()
db.system.users.find({user:"user333"}).pretty()use admin
db.auth('user333', 'user333')
use db_xyz
show collections

 


readOnlyTestDBRole and user4444

 

 
use admin
db.createRole(
{
 role: "readOnlyTestDBRole",
 privileges: [
 { resource: { db: "test", collection: "" }, actions: [ "find" ] }
 ],
 roles: [
 ]
},
{ w: "majority" , wtimeout: 5000 }
)
 
db.createUser(
{
 "user" : "user4444",
 "pwd" : "user4444",
 "roles" : [
 { "role" : "readOnlyTestDBRole", "db" : "admin" }
 ]
}
)
db.system.roles.find( {role:"readOnlyTestDBRole"} ).pretty()
 
db.system.users.find({user:"user4444"}).pretty()
 
use admin
db.auth('user4444', 'user4444')
 
use db_xyz
show collections

 

 

Sprint: Security 2019-11-04, Security 2019-11-18, Security 2019-12-02, Security 2019-12-16, Security 2019-12-30, Security 2019-01-13, Security 2019-01-27, Security 2020-02-24, Security 2020-03-09, Security 2020-03-23, Security 2020-04-06, Security 2020-04-20, Security 2020-05-04, Security 2020-05-18, Security 2020-06-01, Security 2020-06-15, Security 2020-07-13, Security 2020-10-05, Security 2020-11-02, Security 2021-01-11, Security 2021-01-25, Security 2021-02-08
Participants:
Case:

 Description   

Currently, the changeOwnPassword privilege action allows users to list collections in the databases that they don't have read access to. I can demonstrate this with the help of the example below.


Scenario#1:

Consider the following role which has changeOwnPassword privilege action:

 

> db.system.roles.find( {role:"changeOwnPasswordROLE"} ).pretty()
{ "_id" : "admin.changeOwnPasswordROLE", "role" : "changeOwnPasswordROLE", "db" : "admin", "privileges" : [ { "resource" :
{ "db" : "", "collection" : "" }
, "actions" : [ "changeOwnPassword" ] } ], "roles" : [ ]}
 

The user333 is assigned the above role, and has only read privileges on the test database:

> db.system.users.find({user:"user333"}).pretty()
 
{ "_id" : "admin.user333", "user" : "user333", "db" : "admin", "credentials" : { "SCRAM-SHA-1" :
{ "iterationCount" : 10000, "salt" : "2m7Ka+0EbSS477cUD3R3pw==", "storedKey" : "vVpnmUgkO4PniimByVN/Npw8aLc=", "serverKey" : "b83ec/ifKvFKeU7TfSSsXxWwafA=" }
, "SCRAM-SHA-256" : { "iterationCount" : 15000, "salt" : "cf2Bp7NwOF7p45IvynIq+TSY9j2wEVWuqAhMKQ==", "storedKey" : "WDsN8fX8S9Bmg3a/igVNNU50hRWrtc8RnLPs2g7poqk=", "serverKey" : "i1ggY39wbxLbYlVRSkyrJ2q/IwvQlFY4Umy+vqo1hyw=" } }, 
"roles" : [ { 
"role" : "changeOwnPasswordROLE", "db" : "admin" }, 
{ "role" : "read", "db" : "test" } 
]}
 

When I authenticate as user333, switch to db_xyz and run show collections, it lists the collections:

 

> use admin
switched to db admin
> db.auth('user333', 'user333')1
 
> use db_xyz
switched to db db_xyz
 > show collections
test_collectionXYZ

 

 


Scenario#2 

Now, consider another role which has only find privilege action on the test database, with no changeOwnPassword privilege action:

 

> db.system.roles.find( {role:"readOnlyTestDBRole"} ).pretty()
{ "_id" : "admin.readOnlyTestDBRole", "role" : "readOnlyTestDBRole", "db" : "admin", "privileges" : [ { "resource" :
{ "db" : "test", "collection" : "" }
, "actions" : [ "find" ] } ], "roles" : [ ]}

 

user4444 is assigned the above readOnlyTestDBRole role:

> db.system.users.find({user:"user4444"}).pretty()
{ "_id" : "admin.user4444", "user" : "user4444", "db" : "admin", "credentials" : { "SCRAM-SHA-1" :
{ "iterationCount" : 10000, "salt" : "Ym4ZJL1+6HtXnq+IHI+kpg==", "storedKey" : "5lznucBoNy4OW6z4egMTwTK4K/s=", "serverKey" : "neUgsD0+AEAlFpaJAuw+1DU+Gt8=" }
, "SCRAM-SHA-256" : { "iterationCount" : 15000, "salt" : "6J5NUB0VIW2wpud6EpQxsxcclyBO5mbLs2IA7w==", "storedKey" : "o2PYCZDS5NNMzq0E1v3k9vJGaAbyPnlYdPMONM2ZsuM=", "serverKey" : "cAJ3gjby9eC4KModk42ydHCemPjiMqyI33qeWAUjlmw=" } },
"roles" : [ { "role" : "readOnlyTestDBRole", "db" : "admin" } ]}

 

Now, if I authenticate as user4444, switch to db_xyz and run show collections, then it does not list the collections:

 

> use admin
switched to db admin
> db.auth('user4444', 'user4444')1
 
> use db_xyz
switched to db db_xyz
 > show collections
Warning: unable to run listCollections, attempting to approximate collection names by parsing connectionStatus

 


 

Therefore, ideally a user who has changeOwnPassword privilege action should not be able to run show collections on a database that the user does not have read access to; such an action should fail with the above warning: "Warning: unable to run listCollections...". 



 Comments   
Comment by Spencer Jackson [ 05/Feb/21 ]

Upon reflection, I believe this behaviour works as designed, and am resolving the ticket. In order to aid discovery where granular, per-collection, privileges are assigned to users we allowed listCollections to return lists of all collections which a user has permissions on. Under our access control system, privileges may be granted for a database, and possessing such a privilege is equivalent to possessing the privilege on all collections inside the database. In this instance, the user effectively possess the ability to "change their own password" on collections. listCollections is correct, that the user possesses some form of privilege on the database's collections. Because ActionTypes aren't scoped to resource types, we aren't able to tell that this privilege is probably not useful. Scoping ActionTypes to resource types would be challenging, and would need to be done carefully.

Comment by Danny Hatcher (Inactive) [ 10/Oct/19 ]

Digging down through the shell helpers.

User with only role:

replset:PRIMARY>  db.getRoles({showPrivileges:true})
[
	{
		"role" : "changeOwnPasswordROLE",
		"db" : "admin",
		"isBuiltin" : false,
		"roles" : [ ],
		"inheritedRoles" : [ ],
		"privileges" : [
			{
				"resource" : {
					"db" : "",
					"collection" : ""
				},
				"actions" : [
					"changeOwnPassword"
				]
			}
		],
		"inheritedPrivileges" : [
			{
				"resource" : {
					"db" : "",
					"collection" : ""
				},
				"actions" : [
					"changeOwnPassword"
				]
			}
		]
	}
]

Runs the following:

replset:PRIMARY> db.getCollectionInfos({}, false, true)
// exception
replset:PRIMARY> db.getCollectionInfos({}, true, false)
//exception
replset:PRIMARY> db.getCollectionInfos({}, true, true)
//success

If a user without any roles tries to run the same commands, the failures are the same but:

replset:PRIMARY> db.getCollectionInfos({}, true, true)
Warning: unable to run listCollections, attempting to approximate collection names by parsing connectionStatus

I agree that this likely is a bug.

Generated at Thu Feb 08 05:04:33 UTC 2024 using Jira 9.7.1#970001-sha1:2222b88b221c4928ef0de3161136cc90c8356a66.