Samba is built to bridge Windows and POSIX worlds. Apart from the file system semantics, there are many other differences. The story I’m about to tell concerns users and groups. They have different meaning and representation in both worlds, so translation is required, similar to a real life. In real life translators often have to take into account cultural differences and sometimes lack of certain concepts in the language they are translating to.
Protocol communications which Samba implements, end up bringing in objects which have a certain meaning in one world that doesn’t really have a one to one counterpart on the other side. One of tasks samba undertakes is translating the concepts between Windows and POSIX. It does this translation with the help of mapping databases.
In Windows access controls are built around a concept of a security identifiers and security descriptors. Security identifier (SID) is associated with the object it represents. Internal processes in Windows refer to security identifiers of the objects rather than their names. Security descriptor is used to list what security identifiers can have access to a certain resource and what kind of access it could be. An important part of the story is that security identifiers have the same structure regardless of an object they represent. When security identifier is expressed in a textual form, in general we cannot say what object they represent – a user, a group, or a machine account, apart from so called ‘well-known’ SIDs. A nice property of a SID is that it is a global identifier – for two different domains their SIDs are guaranteed to be different even for ‘well-known’ objects within the domains.
In POSIX world access controls are built around a simple model of rights for the resource owner, rights for the resource group ownership, and rights for all others. The model is further extended with POSIX Access Control Lists (ACLs) which allow to associate multiple simple model descriptors with a single resource but resulting access descriptor is still far from its Windows counterpart.
To a kernel of POSIX-compatible operating system access checks are done using numbers which represent users and groups. The kernel application interfaces don’t deal with user or group names, they deal with integer-based identifiers. Standard language library is supposed to translate user or group names to their numeric identifiers when talking to the kernel.
When operating on files and directories, Samba needs to translate NTFS-like semantics to POSIX file semantics. This includes translating security identifiers of SMB clients to POSIX identifiers of the users and their group membership. There are no SID-like structures in the kernel of POSIX operating system that Samba could directly map to; instead, it has to maintain such mapping in user space.
However, POSIX operating system already has own databases for users and groups which all POSIX applications are utilizing. In a primitive form these databases are stored as textual files,
/etc/group, with a well-defined format. On Linux systems there are other ways to store information about POSIX users and groups, with the help of so-called ‘name service switch’ modules (NSS modules). How multiple modules are stacked up in an effort to deliver information about users, groups, and other resources is defined in
/etc/nsswitch.conf configuration file. Standard C library reads this configuration file at application start and loads modules responsible for the resources. Standard application interfaces then will call the modules as defined in
/etc/nsswitch.conf to retrieve required information.
The information NSS modules provide includes nothing related to SMB protocol. Applications can query by user or group name but that’s all: they cannot query by SID value. Also, the interface functions differentiate between user and group information. When Samba gets a SID, it does not know whether it corresponds to a user or to a group, it cannot chose which interface function to call.
Let’s step aside at this point. Samba needs to deal with the system-level databases for users and groups. Samba needs to deal with SIDs that could be mapped to users, groups, and machine accounts. When user is referenced in SMB protocol communication, it can be in the form of a user name or a SID associated with the user object. When group is referenced in SMB protocol communication, it can also be in the form of a group name or a SID associated with the group object. Finally, the same applies for machine accounts but here Samba (and Windows) cheat and represent machine accounts as a special type of a user object.
The fact that Samba sits in the middle between the SMB protocol communication and the system-level databases for users and groups means Samba has to maintain own mapping between information relevant to SMB protocol and the information relevant to system level references to users and groups. In Windows a system level interface and a database for users, groups, and machine accounts is called Security Account Manager, SAM. Samba implements an abstraction level that allows to handle SAM-like requests. In fact, it implements two of those layers, not one.
To map security identifier to a POSIX identifier Samba uses identity mapping interfaces, IDMAP. IDMAP interface is very simple, it only has three functions:
- map SID to a POSIX ID
- map POSIX ID to a SID
- allocate POSIX ID for a SID
A mapping of SID and POSIX ID is handled by an IDMAP module. SID name space is larger than POSIX ID name spaces (combined for users and groups). A relative identifier part of the SID, RID, is 32-bit long and identifies resources within a single domain, but there could be multiple domains involved. Samba has to potentially map all of those RIDs from all domains to a single 32-bit user and single 32-bit group name spaces. Such mapping most likely is a compression scheme with a collision potential when done algorithmically. There could be limiting factors in what particular 32-bit values for user and group identifiers could be chosen. Finally, manual assignment is something that could also be done. Thus, there are many IDMAP modules in Samba to cater to different needs.
A default IDMAP module in Samba is
idmap_tdb. This module stores SID to POSIX ID mapping in a Samba native database format, so-called ‘trivial database’, TDB. When Samba requests a look up by SID,
idmap_tdb module may allocate new POSIX ID if this SID is not mapped yet and there are enough POSIX IDs in the range defined for the domain. As result, when range is big enough to cover all users and groups from the domain, all SIDs will be mapped. However, there is no guarantee that SIDs will be mapped to the same POSIX IDs on all Samba servers in the domain. The order in which SID mapping request comes influences POSIX ID which is allocated for the SID. If different Samba servers get requests in the different order, they would assign different POSIX IDs to the same SIDs. This is, of course, a problem when accessing files on a distributed file system.
To solve this problem, other IDMAP modules were created.
idmap_rid module algorithmically maps relative identifier of the SID to the range associated with the domain.
idmap_ad looks up POSIX IDs at a domain controller of the Active Directory domain. In a similar approach,
idmap_ldap looks up POSIX IDs at LDAP server defined in the configuration.
For configurations, where users and groups are maintained in the system-level databases, Samba allows to use
idmap_nss module. The module queries the system-level databases in case it is known what SID maps to – to a user or to a group. In case it is unknown, IDMAP module queries a primary domain controller of the domain to convert SID to a name. A primary domain controller should know all users and groups of the domain, thus it should be able to answer where the SID maps to, or fail the request. In the latter case
idmap_nss will also fail the request and Samba will consider the SID as unmapped.
Users and groups need to be known to Samba before they can be used. The very same users and groups must be known to the operating system because Samba processes change identity when performing operations as a particular user. The second layer Samba uses for identity mapping also allows to manage users and groups: create new ones, delete existing ones, modify information about them and, in general, perform a lot of actions Windows expects from SAM interface.
PASSDB module is an abstraction over the system-level database about users. It allows to retrieve user information from LDAP server or other storage scheme. The reason for this is, again, a lack of needed information in the system-level database format. Samba needs to know a lot more details about the user than POSIX interfaces provide and some of this information is unique to SMB protocol. For example, for each user to be able to authenticate with password, Samba needs to known corresponding password hashes for NTLM negotiation. NT and LM hashes are not used by the POSIX-compatible operating systems. Also, the interface to retrieve user information does not give access to actual passwords. In fact, in many environments applications have no access to password hashes, not even passwords.
Default PASSDB module is
tdbsam. Similar to
idmap_tdb, it stores additional information Samba needs to know about users in its own ‘trivial database’, TDB.
tdbsam expects that if user information is stored in the database, the very same user exists in the system-level databases.
One can also force IDMAP subsystem to look up SID to POSIX ID mappings in a PASSDB backend. For this IDMAP module
idmap_passdb can be used. As result, Samba will look up SIDs and POSIX IDs in a PASSDB module defined in
Groups are not stored in Samba databases. Instead, Samba allows to map existing POSIX group to a group in a domain. Because groups in Windows world can have different scope, Samba provides a mechanism to specify which POSIX group is mapped to which Windows group and what scope it should have. The mapping is managed with the help of Samba’s
net groupmap family includes commands to add, modify, and remove group mappings. It also allows to associate (alias) certain SIDs with existing groups and list members of the groups.
For distributed environments it is convenient to store POSIX and SMB information about users and groups in the same place. For example, LDAP server could be used to store and retrieve such information with
ldapsam PASSDB module and
idmap_ldap IDMAP module. However, group mapping would still be maintained locally with
net groupmap set of commands.
Let’s apply all discussed above to a practice. Consider a single Samba server which serves as a primary domain controller to its own domain. The server does not use LDAP or any other distributed storage for its POSIX and SMB information for users and groups.
smb.conf configuration file for a primary domain controller is following:
# Global parameters
workgroup = SAMBA
domain logons = Yes
security = USER
winbind offline logon = Yes
winbind use default domain = Yes
idmap config * : range = 1000-1000000
idmap config * : backend = passdb
passdb backend = tdbsam
template homedir = /home/%U
template shell = /bin/bash
comment = Home Directories
browseable = No
inherit acls = Yes
read only = No
valid users = %S %D%w%S
This configuration defines a single-domain SMB server with IDMAP configuration to look up SID to POSIX ID mappings in a PASSDB module. PASSDB module is set to
tdbsam which is a default module.
As result of this configuration, all non-POSIX attributes of users need to be stored in the PASSDB module. To modify them one can use
pdbedit tool. But before that we need to create users and groups at the system level first.
SMB domains have few ‘well-known’ groups: ‘Domain Users’, ‘Domain Administrators’, ‘Domain Guests’. For ‘Domain Users’ and ‘Domain Guests’ we can reuse POSIX groups ‘users’ and ‘nobody’, for ‘Domain Admins’ it is better to create a separate group, for example, ‘admins’.
On Fedora 24 there are existing POSIX groups ‘users’ and ‘nobody’:
# getent group users nobody
We can create ‘admins’ group using
# groupadd admins
When groups are ready, we can associated them with the well-known domain groups using
net groupmap commands:
# net groupmap add ntgroup="Domain Admins" unixgroup=admins rid=512 type=d
Successfully added group Domain Admins to the mapping db as a domain group
# net groupmap add ntgroup="Domain Users" unixgroup=users rid=513
Successfully added group Domain Users to the mapping db as a domain group
# net groupmap add ntgroup="Domain Guests" unixgroup=nobody rid=514
Successfully added group Domain Guests to the mapping db as a domain group
Finally, add users. Users should have their primary group associated with any of the groups mapped to the domain because Samba needs to recognize them. So there should be SID to POSIX ID mapping for primary groups. Let’s pretend that all our users are members of ‘users’ group:
# useradd -m -g users -G admins administrator
# pdbedit -a -u administrator
retype new password:
Unix username: administrator
Account Flags: [U ]
User SID: S-1-5-21-1345368309-3761995768-4153620981-1008
Primary Group SID: S-1-5-21-1345368309-3761995768-4153620981-513
Home Directory: \\smb\administrator
Profile Path: \\smb\administrator\profile
Logon time: 0
Logoff time: Wed, 06 Feb 2036 17:06:39 EET
Kickoff time: Wed, 06 Feb 2036 17:06:39 EET
Password last set: Mon, 19 Sep 2016 12:43:45 EEST
Password can change: Mon, 19 Sep 2016 12:43:45 EEST
Password must change: never
Last bad password : 0
Bad password count : 0
Logon hours : FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
In the screen output above ‘Primary Group SID’ was automatically inferred from the group mapping.
We can now ask
winbindd to resolve user information based on the IDMAP and PASSDB databases:
# wbinfo -i administrator
# wbinfo -n administrator
S-1-5-21-1345368309-3761995768-4153620981-1008 SID_USER (1)
# wbinfo -s S-1-5-21-1345368309-3761995768-4153620981-1008