Samba - The Next Generation: Architecture and Design

Luke Kenneth Casson Leighton

$Date: 2001/03/22 14:12:22 $

This white paper describes the architecture, components and purpose of the "Samba, The Next Generation" project. These components include DCE/RPC, TNG's Daemon Architecture, SURS and Winbind. The aim of TNG is to make it easier to manage the development of what is becoming a large scale project, dividing into smaller components along published, well-defined boundaries.

The fulfilment of this aim will simultaneously provide full Windows NT Interoperability at the Application Development level, on POSIX-compliant platforms such as Linux, to DCOM, MSDN and Win32 developers that are currently restricted to the exclusive development of software on the Windows NT platform, This is due to the extraordinarily large - and widening - underlying technological gap between the Unix and NT OS development environments.

Table of Contents

1: Introduction

1.1: Protocols
1.2: Services
1.3: Paper's Aims

2: Server Message Block, aka Common Internet File System

2.1: SMB's transports
2.2: SMB's Services

3: SID to UID Resolution System

4: Windows nsswitch Module

4.1: Winbind's SURS algorithm
4.2: Winbindd - Winbind Daemon
4.3: pam_winbind

5: Distributed Computing Environment / Remote Procedure Calls

5.1: History
5.2: DCE/RPC Protocols
5.3: Commonly-known DCE/RPC Services
5.4: DCE/RPC Security

6: NT Domains Architecture

6.1: Secure Accounts Manager
6.1.1: Useful Utility Routines
6.1.2: sam2sam - SAM conversion utility

6.2: Local Security Authority

6.3: Windows NT Network Logon Service

6.4: Spooler Service

6.5: Service Control Manager

6.6: Windows Registry

6.7: Server Service

1: Introduction

At the time of writing, Samba is a 350 thousand line project of approximately twenty man-years development over the last ten years. It has within it an implementation of at least four separate Microsoft-compatible Protocols. It consists of at least fifteen independent services on each of those protocols. Incredibly, many people still think of Samba as just one program.

Samba TNG is in fact a suite of programs and libraries that make a fully-compliant POSIX system look, to all intents and purposes, like a Windows NT File, Print and Login Server. That includes full Windows NT Primary Domain Controller functionality.

There are unique problems associated with implementing Windows NT File, Print and Login Services on a POSIX platform, not least that the underlying POSIX OS knows absolutely ZERO about the Windows NT Security Model (which is originally the VAX/VMS Security Model, from the time when Dave Cutler and his team moved en-masse from DEC to Microsoft).

Understanding how to correctly map between the Unix and Windows NT security models, and narrowing down the best possible places to perform this mapping has taken an extraordinarily long time to crystallise. This has resulted in SURS http://cb1.com/~lkcl/cifs/draft-lkcl-sidtouidmap-00.txt and a first practical, if specifically targetted, implementation of SURS in Winbind (cite winbind ref).

There follows a brief description of the protocols and services included in Samba.

1.1: Protocols

The protocols implemented in Samba include:

NetBIOS
documented for TCP/IP in RFC 1001 and RFC 1002

SMB
documented originally in 1983 by IBM and The Open Group, and updated since by Microsoft as CIFS

DCE/RPC
documented originally by Apollo / HP and The Open Group, reimplemented by Microsoft as MSRPC and documented in their MSDN.

These protocols, and the services running on them, are inter-dependent along clearly defined lines. For example, DCE/RPC can use a surprisingly large number of protocols as its transport. There follows a short list of the known available protocols:

TCP/IP
UDP/IP
IPX/SPX
NetBIOS
NETBEUI
HTTP
DECnet 3.0
SMB

SMB is available over:

TCP/IP
NetBIOS

NetBIOS is also available over:

TCP/IP / UDP/IP
IPX/SPX
NETBEUI

1.2: Services

The services that available on NetBIOS include:

WINS
NetBIOS (aka Windows Internet) Naming Service

Browsing
A means to locate computers (aka the Network Neighbourhood)

Domain Management
A means to manage computers in a Windows NT Domain (aka Domain Master Browsing)

Messaging
A means to send messages to a user or a group of users

HTML
Microsoft's Web Server, IIS, can use NetBIOS as a transport.

There are also several obsolete protocols. NetBIOS is actually a full transport that used to be implemented inside network cards.

The services that are available over SMB include:

File Sharing
This is the core of the SMB protocol's purpose: to allow files to be accessed over a network.

Print Sharing
Printers can also be accessed remotely.

Port Sharing
There is a little-known means to access and share Modems and Parallel Ports over SMB.

IPC
Inter-process Communication. A means to communicate specific information between programs.

Named Pipes
SMB also provides a means for applications to easily locate and access remote services, and that includes DCE/RPC.

The services that are available over DCE/RPC are too numerous to mention all of them here, particularly as DCE/RPC is used as the underlying mechanism behind DCOM. The ones that are relevant to this project are:

SAM
Secure Accounts Manager.

LSA
Local Security Authority.

NETLOGON
Windows NT Network Logon Service. Provides the back-end to the Username, Domain, Password Dialog box. Also provides Backup Domain Controller Synchronisation Services. aka "A hacker's delight".

spoolss
Spooler Service (Windows NT Total Print Management) - one of the largest and comprehensive DCE/RPC services in the whole of the Windows NT Operating System.

svcctl
Service Control Manager.

winreg
Windows Registry. For some obscure reason, winreg also provides the means to remotely shut down a Windows NT system.

srvsvc
Server Service. Mostly provides a means to list and manage SMB-related services such as shares, DFS (Microsoft's Distributed File System) Mount points. The other most popularly known DCE/RPC-based services and programs are Exchange (and Outlook), as MAPI is available over DCE/RPC, and MS-SQL 7 and MS-SQL 2000. Even Microsoft's DFS and WINS Services have a DCE/RPC Administrative Interface.

All of this is undocumented. Microsoft fully realises the implications of releasing information regarding these protocols. It would be possible, with a specification, to implement a fully-compatible client and server of any one of these services, and so no information is publicly available on how they work. So, instead, network traffic must be observed for months at a time running these programs hour after hour, experimenting until it becomes clear how they operate.

1.3: Paper's Aims

When confronted with this plethora of protocols and services, it comes as no great surprise when people get quite seriously confused about what goes where, and just how much is involved. Leaving aside peoples' surprise over how Samba is able to outperform Windows NT and still be more reliable, their estimates of Samba's its performance and manageability vary considerably and way often off-the-mark: Samba TNG, with its ability to masquerade fully in a Windows NT Domain environment, is just as easily manageable with the standard Windows NT Administrative tools as it is using the standard Unix command-line interface.

It is also quite daunting and a little discouraging to developers who may wish to take an active interest in the projects mentioned in this document when Samba is spread across a single combined codebase of 350 thousand lines - and only going up.

So, this paper outlines the main aims behind TNG's architecture, explaining which components should do what, in the hope that this will encourage a more diverse range of development efforts, and also to help clarify the protocol boundaries that have long since become blurred, in the eyes of many Network Administrators and Open Source Developers alike, into one single application's domain.

2: Server Message Block, aka Common Internet File System

SMB is essentially known as a File and Print Protocol. However, it is actually also used as an authenticated transport for other protocols such as DCE/RPC and Named Pipes. This section briefly outlines how SMB developed and what it can really do.

TODO: expand the following:

2.1: SMB's transports

SMB is available over:
  • TCP/IP
  • NetBIOS
  • 2.2: SMB's Services

    The services that are available over SMB include:

    File Sharing
    This is the core of the SMB protocol's purpose: to allow files to be accessed over a network.

    Print Sharing
    Printers can also be accessed remotely.

    Port Sharing
    There is a little-known means to access and share Modems and Parallel Ports over SMB.

    IPC
    Inter-process Communication. A means to communicate specific information between programs.

    Named Pipes
    SMB also provides a means for applications to easily locate and access remote services, and that includes DCE/RPC.

    3: SID to UID Resolution System

    SURS is a means to map between Unix and Windows NT Security. The ultimate aim of the SURS project is to provide an easily administered way to put a POSIX-compliant File, Print and Login Server into a Windows NT Domain environment, and to solve all of the user file security incompatibility issues that are normally present in a mixed Unix and Windows NT Domain environment.

    This goal will be achieved by implementing an nssswitch-like architecture, managed through a single file - sursswitch.conf. sursswitch.conf will have a similar layout and role to that of nsswitch.conf.

    TODO: expand this:

    Understanding how to correctly map between the Unix and Windows NT security models, and narrowing down the best possible places to perform this mapping has taken an extraordinarily long time to crystallise. This has resulted in SURS http://cb1.com/~lkcl/cifs/draft-lkcl-sidtouidmap-00.txt and a first practical, if specifically targetted, implementation of SURS in Winbind (cite winbind ref).

    4: Windows nsswitch Module

    Winbind is an nsswitch module that, using an integrated specialist implentation of a SURS mapping, maps a Windows NT Domain environment into a Unix Environment. A winbind-enabled Unix system can have users, groups and aliases from a Windows NT Domain appear as local Unix users and groups. Winbind also has a pam module (pam_winbind) that, along with winbindd, at last solves the Holy Grail of a mixed Unix and Windows NT Domain Environment: Single Sign-on.

    TODO: expand this

    Understanding how to correctly map between the Unix and Windows NT security models, and narrowing down the best possible places to perform this mapping has taken an extraordinarily long time to crystallise. This has resulted in SURS http://cb1.com/~lkcl/cifs/draft-lkcl-sidtouidmap-00.txt and a first practical, if specifically targetted, implementation of SURS in Winbind (cite winbind ref).

    4.1: Winbind's SURS algorithm

    4.2: Winbindd - Winbind Daemon

    4.3: pam_winbind

    5: Distributed Computing Environment / Remote Procedure Calls

    DCE/RPC is an extremely impressive means to call procedures from one application in another application, without having to know about what computer the other application is running on. The usual underlying concerns of a Remote Procedure Call system, such as how, where, who, security and encryption are all hidden behind an extremely well-defined interface. The only limitations are that the architecture behind DCE/RPC has its roots in static programming languages like c (hence DCOM and SOAP, which provide more object-orientated semantics and dynamic data structures, respectively)

    The significant differences between SOAP and DCE/RPC are that SOAP is a relatively immature IPC mechanism which lacks proper security and authentication; and that SOAP provides the means to fully decode the function call's arguments at run-time, whilst the format of the function call arguments in DCE/RPC are well-defined in Interface Definition Language (IDL) format, they are hard-coded (compiled) into the client and server applications and are not transferred at run-time.

    ONC/RPC is similar to DCE/RPC. Except that... Sun's ONC/RPC's offerings pale into insignificance compared to DCE/RPC's capabilities. DCE/RPC can negotiate the Data Representation, Transport, Security, Interface (it has Version Control). Sun's ONC/RPC is restricted to XDR for its Data Format, UDP and TCP, and GSS/API for its security. Additionally, XDR is has more restrictions on the data structures it can handle than DCE/RPC's default Data Representation, NDR (Network Data Representation).

    5.1: History

    DCE/RPC has a long history. It started off life with Apollo, which was later acquired by Hewlett Packard. DCE/RPC was originally called Network Computing Architecture - NCA 1.0. It was only available over UDP. Later, it became clear that NCA would need to move to TCP and there was also a requirement to run it on DECnet 3.0, so it was redesigned to Network Computing System - NCS 2.0. NCS 2.0 was then submitted to The Open Group, and it became Distributed Computing Environment / Remote Procedure Calls - DCE/RPC.

    Microsoft, at some point, contacted The Open Group, wishing to license DCE/RPC. The Open Group's charter mandated at the time that they charge USD $20 per seat. Clearly, Microsoft considered this, in light of their expected market impact, and decided to reimplement DCE/RPC themselves, as MSRPC. It is no coincidence that one of the key founders of Apollo is still working for Microsoft.

    5.2: DCE/RPC Protocols

    TODO: expand / rewrite

    DCE/RPC can use a surprisingly large number of protocols as its transport. There follows a short list of the known available protocols:

  • TCP/IP
  • UDP/IP
  • IPX/SPX
  • NetBIOS
  • NETBEUI
  • HTTP
  • DECnet 3.0
  • SMB
  • 5.3: Commonly-known DCE/RPC Services

    TODO: expand / rewrite

    The services that are available over DCE/RPC are too numerous to mention all of them here, particularly as DCE/RPC is used as the underlying mechanism behind DCOM. A list of the services that have been implemented in Samba TNG are available in the section on NT Domains Architecture.

    The other most popularly known DCE/RPC-based services and programs are Exchange (and Outlook), as MAPI is available over DCE/RPC, and MS-SQL 7 and MS-SQL 2000. Even Microsoft's DFS and WINS Services have a DCE/RPC Administrative Interface.

    There is one other DCE/RPC Service that is worth mentioning: it may come as a surprise to some to know that the difference between DCE/DFS and AFS (Andrew File System) is only that DCE/DFS uses DCE/RPC, whilst AFS uses its own in-built RPC mechanism. Additionally, DCE/RPC specifically had a pipe mechanism added to it to cope with the transfer of massive data streams. DCE/RPC pipes was essentially added to deal with large file transfers using DCE/DFS. [TODO: more detail on this from s.tat)

    5.4: DCE/RPC Security

    DCE/RPC is designed to hide the details of the security mechanism from the user. Known security mechanisms include:
  • DCE
  • GSSAPI
  • Kerberos
  • NTLMSSP
  • This last security protocol is only available in MSRPC, which is a serious omission that guarantees that DCE/RPC will remain in the most obscure of realms and echelons of the Computing World. The addition of NTLMSSP security, along with the necessary linkage to a Windows NT / Samba TNG Domain environment, would give DCE/RPC a new lease of life. However, it's not quite as simple as that: the other missing component is that DCE/RPC must have the SMB transport added, too.

    6: NT Domains Architecture

    Samba TNG implements a very comprehensive subset of the Windows NT Domain Protocol. This subset is sufficient to act as a member of a domain, as a Primary and Backup Domain Controller, and to perform Inter-Domain Trust.

    Samba TNG also provides command-line equivalents to REGEDIT.EXE, USRMGR.EXE, EVENTMGR.EXE and most of NET.EXE's little-known extensive functionality that is also available in various control panel components such as the Service Control Manager. These command-line tools - regedit, samedit, svcedit, rpcclient etc. provide fully-useable administrative tools that will work just as well on Windows NT servers as they will on Samba TNG. They were originally written for test and compatibility purposes, however they also serve as examples on how to use the fledgeling MS-compatible API that is the core of TNG's DCE/RPC services.

    The ultimate aim is to replace this fledgeling code with fully MSDN-compatible (platform-independent) alternatives, such that it will be possible to put full functionality behind the Wine Project, and ultimately to allow Win32 developers to compile their Win32 applications without having to modify a single line of code.

    6.1: Secure Accounts Manager

    The SAM Database stores and manages all user and group account information and their inter-relationships. Surprisingly, users include "Trust" Accounts, which is similar in concept to NIS+ requiring that all servers be included in the NIS+ database: SAM requires that all servers be listed in the Domain, too.

    TNG has six separate SAM database implementations, with the LDAP one and the legacy smbpasswd one in production usage at the time of writing. Four of these SAM databases are available as a configure option in one daemon - samrd, whilst the other three are under development: a TDB implementation (samrtdbd) and an LDAP implementation that conforms to the Windows 2000 LDAP SAM Schema (samrnt5ldapd).

    TODO: The SAM database is effectively an informational endpoint.

    This section outlines what the requirements are for implementing a SAM database under the TNG architecture. It also includes a description of a program, sam2sam, which will allow administrators to convert a SAM database in one format to another format.

    No relation to Unix
    Yes, it's true. A SAM database implementation has NOTHING to do with a Unix Database. That includes one implemented on Unix.

    This mistake was made in the original samrd design, attempting to mirror the unix /etc/passwd and /etc/group (etc.) database in the SAM by actually reading /etc/group (etc.) entries and presenting them as the SAM database.

    Whoops.
    It is SURS's job to provide the linkage between SAM accounts and unix accounts, and that does not mean inside a SAM database implementation. Nowhere inside a SAM database is it necessary to access Unix account information. A SAM stores NT passwords, NT accounts by RID, Aliases etc. There is no room for Unix passwords or unix user ids in a SAM implementation, and absolutely no need, either.

    Name space Conventions
    Usernames, alias names, group names and Trusted Domain names must all be unique. It is NOT possible to have a user named root and a group named root in a SAM database. A SAM database implementor MUST NOT allow this to occur, as it will cause either the user or the group to become unselectable by name, depending on the implementation!

    Low-level library sharing
    There is a surprisingly small amount of code that needs to be shared between SAM database implementations. It includes:

    se_access_check
    use of SeAccessCheck to implement security (see equivalent function in Microsoft's MSDN)

    DCE/RPC marshalling / unmarshalling routines
    These are hidden from the developer, as is the norm for all DCE/RPC application development: TNG is no exception. However, you must still link your application with the DCE/RPC framework! The advantage is that this framework does not change from one SAM database implementation to another.

    The SAM application developer, however, writes the _samr_*() routines, and it is the job of the DCE/RPC framework to call, and to hide the mechanics of the calling of, the server-side _samr_*() functions, from the application developer.

    DCE/RPC handle / context routines
    It is possible to store long-lived connection state information and to have that information passed back to you by another remote DCE/RPC function. The two relevant functions are the rather obtuse set_policy_state() and get_policy_state_info() functions. These will become transparent at some point in the future when we move to a full IDL compiler back-end: they represent, in a very stupid way, the [handle] IDL specifier.

    Useful Utility Routines
    To make the job of maintaining several SAM database implementations, some implementers may choose not to support quite as many sub-levels (known as Info Levels) as are available. We intend to provide generic conversion routines that will assist with this [primarily because they have to exist in some SAM implementations anyway]

    Conversion considerations
    It has been mentioned that it is preferred that an API be created that hides the complexity of SAM development from the developer. There are a number of points that need to be clarified:

    SAM is comprehensive
    The SAM API is comprehensive and complete, not complex. A full list of the prototypes, divided by section, is listed below. To attempt to map the SAM api to some other API in order to attempt to hide from the number and nature of the functions that SAM provides simply makes the other API an additional burden, with possible omissions, additional complexity and the associated maintenance headaches.

    Bite the bullet: implement the whole damn lot, and do it as simply and directly as possible.

    SAM strings are stored in UNICODE-16.
    Some database implementors may choose to treat this as binary data. Some may choose to convert to UTF-8, others to Ascii. Some may have native UNICODE-16 library support, or may choose to write their own UNICODE-16 library (which must have case-insensitive search capabilities).

    To impose any specific string format is a restriction and yet another programmatic step. So why bother? Let the developer choose the most suitable format.

    NTTIME format
    SAM timestamps are typically NTTIME, a 64-bit time in 100ns resolution ticks since Jan 1st 1600. A similar argument applies here as to strings: let the developer choose the most suitable format.

    This, and the UNICODE-16 string issue, implies that any low-level utility functions that TNG provides, should use the same formats as those offered by SAM - NOT the Unix time_t (1 second resolution since Jan 1st 1970) and NOT Ascii or UTF-8, as if that restriction is applied, then some applications may have to convert BACK to NTTIME and UNICODE-16, which is at best a waste of time and at worse a serious and potentially critical loss of information.

    SAM API
    Here is the list of SAM routines that must be implemented. A tiny percentage of these routines are not well-defined: they have had to be recreated from over-the-wire data over a long period of observation and experimentation. Consequently, they may change, although the SAM API is one of the most complete and comprehensive of the TNG codebase.

    /*The following definitions come from  samrd/srv_samr_passdb.c  */
    
    /*
     * sam management
     */
    
    uint32 _samr_close(POLICY_HND * hnd);
    uint32 _samr_connect_anon(const UNISTR2 * srv_name, uint32 access_mask,
    			  POLICY_HND * connect_pol);
    uint32 _samr_connect(const UNISTR2 * srv_name, uint32 access_mask,
    		     POLICY_HND * connect_pol);
    uint32 _samr_unknown_2d(const POLICY_HND * domain_pol, const DOM_SID * sid);
    uint32 _samr_query_sec_obj(const POLICY_HND * user_pol, SEC_DESC_BUF * buf);
    
    uint32 _samr_enum_dom_users(const POLICY_HND * pol, uint32 * start_idx,
    			    uint16 acb_mask, uint16 unk_1, uint32 size,
    			    SAM_ENTRY ** sam,
    			    UNISTR2 ** uni_acct_name, uint32 * num_sam_users);
    uint32 _samr_enum_domains(const POLICY_HND * pol, uint32 * start_idx,
    			  uint32 size,
    			  SAM_ENTRY ** sam,
    			  UNISTR2 ** uni_acct_name, uint32 * num_sam_users);
    uint32 _samr_enum_dom_groups(const POLICY_HND * pol,
    			     uint32 * start_idx, uint32 size,
    			     SAM_ENTRY ** sam,
    			     UNISTR2 ** uni_acct_name,
    			     uint32 * num_sam_groups);
    uint32 _samr_enum_dom_aliases(const POLICY_HND * pol,
    			      uint32 * start_idx, uint32 size,
    			      SAM_ENTRY ** sam,
    			      UNISTR2 ** uni_acct_name,
    			      uint32 * num_sam_aliases);
    uint32 _samr_query_dispinfo(const POLICY_HND * domain_pol, uint16 level,
    			    uint32 start_idx,
    			    uint32 max_entries,
    			    uint32 max_size,
    			    uint32 * data_size,
    			    uint32 * num_entries, SAM_DISPINFO_CTR * ctr);
    
    uint32 _samr_lookup_names(const POLICY_HND * pol,
    			  uint32 num_names1,
    			  uint32 flags,
    			  uint32 ptr,
    			  const UNISTR2 * uni_name,
    			  uint32 * num_rids1,
    			  uint32 rid[MAX_SAM_ENTRIES],
    			  uint32 * num_types1, uint32 type[MAX_SAM_ENTRIES]);
    uint32 _samr_lookup_rids(const POLICY_HND * pol,
    			 uint32 num_rids, uint32 flags,
    			 const uint32 * rids,
    			 uint32 * num_names,
    			 UNIHDR ** hdr_name, UNISTR2 ** uni_name,
    			 uint32 ** types);
    
    /*
     * user password changing
     */
    uint32 _samr_chgpasswd_user(const UNISTR2 * uni_dest_host,
    			    const UNISTR2 * uni_user_name,
    			    const char nt_newpass[516],
    			    const uchar nt_oldhash[16],
    			    const char lm_newpass[516],
    			    const uchar lm_oldhash[16]);
    uint32 _samr_get_dom_pwinfo(const UNISTR2 * uni_srv_name,
    			    uint16 * unk_0, uint16 * unk_1);
    uint32 _samr_get_usrdom_pwinfo(const POLICY_HND * user_pol,
    			       uint16 * unknown_0,
    			       uint16 * unknown_1, uint32 * unknown_2);
    
    /*
     * domain management
     */
    
    uint32 _samr_lookup_domain(const POLICY_HND * connect_pol,
    			   const UNISTR2 * uni_domain, DOM_SID * dom_sid);
    uint32 _samr_open_domain(const POLICY_HND * connect_pol,
    			 uint32 ace_perms,
    			 const DOM_SID * sid, POLICY_HND * domain_pol);
    uint32 _samr_query_dom_info(const POLICY_HND * domain_pol,
    			    uint16 switch_value, SAM_UNK_CTR * ctr);
    
    
    /*
     * group management
     */
    
    uint32 _samr_add_groupmem(const POLICY_HND * pol, uint32 rid, uint32 unknown);
    uint32 _samr_del_groupmem(const POLICY_HND * pol, uint32 rid);
    uint32 _samr_delete_dom_group(POLICY_HND * group_pol);
    uint32 _samr_query_groupmem(const POLICY_HND * group_pol,
    			    uint32 * num_mem, uint32 ** rid, uint32 ** attr);
    uint32 _samr_set_groupinfo(const POLICY_HND * pol,
    			   uint16 switch_level, const GROUP_INFO_CTR * ctr);
    uint32 _samr_query_groupinfo(const POLICY_HND * pol,
    			     uint16 switch_level, GROUP_INFO_CTR * ctr);
    uint32 _samr_create_dom_group(const POLICY_HND * domain_pol,
    			      const UNISTR2 * uni_acct_name,
    			      uint32 access_mask,
    			      POLICY_HND * group_pol, uint32 * rid);
    uint32 _samr_open_group(const POLICY_HND * domain_pol, uint32 access_mask,
    			uint32 group_rid, POLICY_HND * group_pol);
    
    
    /*
     * alias management
     */
    
    uint32 _samr_query_aliasinfo(const POLICY_HND * alias_pol,
    			     uint16 switch_level, ALIAS_INFO_CTR * ctr);
    uint32 _samr_delete_dom_alias(POLICY_HND * alias_pol);
    uint32 _samr_query_aliasmem(const POLICY_HND * alias_pol,
    			    uint32 * num_mem, DOM_SID2 ** sid);
    uint32 _samr_delete_dom_user(POLICY_HND *user_pol);
    uint32 _samr_add_aliasmem(const POLICY_HND * alias_pol, const DOM_SID * sid);
    uint32 _samr_del_aliasmem(const POLICY_HND * alias_pol, const DOM_SID * sid);
    uint32 _samr_create_dom_alias(const POLICY_HND * domain_pol,
    			      const UNISTR2 * uni_acct_name,
    			      uint32 access_mask,
    			      POLICY_HND * alias_pol, uint32 * rid);
    uint32 _samr_open_alias(const POLICY_HND * domain_pol,
    			uint32 access_mask, uint32 alias_rid,
    			POLICY_HND * alias_pol);
    
    /*
     * user management
     */
    
    uint32 _samr_open_user(const POLICY_HND * domain_pol,
    		       uint32 access_mask, uint32 user_rid,
    		       POLICY_HND * user_pol);
    uint32 _samr_query_userinfo(const POLICY_HND * pol, uint16 switch_value,
    			    SAM_USERINFO_CTR * ctr);
    uint32 _samr_set_userinfo(const POLICY_HND * pol, uint16 switch_value,
    			  SAM_USERINFO_CTR * ctr);
    uint32 _samr_set_userinfo2(const POLICY_HND * pol, uint16 switch_value,
    			   SAM_USERINFO_CTR * ctr);
    uint32 _samr_create_user(const POLICY_HND * domain_pol,
    			 const UNISTR2 * uni_username,
    			 uint16 acb_info, uint32 access_mask,
    			 POLICY_HND * user_pol,
    			 uint32 * unknown_0, uint32 * user_rid);
    uint32 _samr_query_usergroups(const POLICY_HND * pol,
    			      uint32 * num_groups, DOM_GID ** gids);
    uint32 _samr_query_useraliases(const POLICY_HND * pol,
    			       const uint32 * ptr_sid, const DOM_SID2 * sid,
    			       uint32 * num_aliases, uint32 ** rid);
    
    As can be seen from this list, unlike NIS+ and nssswitch, it's not just getpwent(), getgrent(), it's get, set, enumerate, add, delete and sliced bread and the toaster, too. It has to be said: if Microsoft had decided to publish this as an RFC when Windows NT 3.1 was first released, it would have stood a good chance of becoming the world-wide standard method of account management, even if it is limited to the NT security model.

    6.1.1: Useful Utility Routines

    To save individual implementors a considerable amount of time, and to make maintenance easier, they MAY wish to use some utility routines that merge info levels. These merge routines take the info level with the most data members as input and output, and merge info levels with less data members into it.

    This is best explained by example. Let us take the TDB implementation. This stores an NDR (network data representation) binary blob associated with a RID. In the User SAM-TDB, the blob that is stored is a SAM_USER_INFO_21 structure:

    /* SAM_USER_INFO_21 */
    typedef struct sam_user_info_21
    {
    	NTTIME logon_time;            /* logon time */
    	NTTIME logoff_time;           /* logoff time */
    	NTTIME pass_last_set_time;    /* password last set time */
    	NTTIME kickoff_time;          /* kickoff time */
    	NTTIME pass_can_change_time;  /* password can change time */
    	NTTIME pass_must_change_time; /* password must change time */
    
    	UNIHDR hdr_user_name;    /* username unicode string header */
    	UNIHDR hdr_full_name;    /* user's full name unicode string header */
    	UNIHDR hdr_home_dir;     /* home directory unicode string header */
    	UNIHDR hdr_dir_drive;    /* home drive unicode string header */
    	UNIHDR hdr_logon_script; /* logon script unicode string header */
    	UNIHDR hdr_profile_path; /* profile path unicode string header */
    	UNIHDR hdr_acct_desc  ;  /* user description */
    	UNIHDR hdr_workstations; /* comma-separated workstations user can log in from */
    	UNIHDR hdr_unknown_str ; /* don't know what this is, yet. */
    	UNIHDR hdr_munged_dial ; /* munged path name and dial-back tel number */
    
    	uint8 lm_pwd[16];    /* lm user passwords */
    	uint8 nt_pwd[16];    /* nt user passwords */
    
    	uint32 user_rid;      /* Primary User ID */
    	uint32 group_rid;     /* Primary Group ID */
    
    	uint32 acb_info; /* account info (ACB_xxxx bit-mask) */
    
    	uint32 unknown_3; /* 0x00ff ffff */
    
    	uint16 logon_divs; /* 0x0000 00a8 which is 168 which is num hrs in a week */
    	/* uint8 pad[2] */
    	uint32 ptr_logon_hrs; /* unknown pointer */
    
    	uint32 unknown_5;     /* 0x0002 0000 */
    
    	uint8 padding1[8];
    
    	UNISTR2 uni_user_name;    /* username unicode string */
    	UNISTR2 uni_full_name;    /* user's full name unicode string */
    	UNISTR2 uni_home_dir;     /* home directory unicode string */
    	UNISTR2 uni_dir_drive;    /* home directory drive unicode string */
    	UNISTR2 uni_logon_script; /* logon script unicode string */
    	UNISTR2 uni_profile_path; /* profile path unicode string */
    	UNISTR2 uni_acct_desc  ;  /* user description unicode string */
    	UNISTR2 uni_workstations; /* login from workstations unicode string */
    	UNISTR2 uni_unknown_str ; /* don't know what this is, yet. */
    	UNISTR2 uni_munged_dial ; /* munged path name and dial-back tel number */
    
    	uint32 unknown_6; /* 0x0000 04ec */
    	uint32 padding4;
    
    	LOGON_HRS logon_hrs;
    
    } SAM_USER_INFO_21;
    
    Using NDR is inspired by the format of the SAM Windows Registry Hive. NDR is an extremely convenient means to easily store and retrieve large, dynamic-sized data structures in a flat binary format. Here are two other SAM_USER_INFO structures - info levels 7 (user name) and 10 (ACB bits) respectively:
    /* SAM_USER_INFO_7 */
    typedef struct sam_user_info_7
    {
    	UNIHDR hdr_user_name;
    	UNISTR2 uni_user_name;
    
    } SAM_USER_INFO_7;
    
    /* SAM_USER_INFO_10 */
    typedef struct sam_user_info_10
    {
    	uint32 acb_info;
    
    } SAM_USER_INFO_10;
    

    So, the procedure that must be followed to modify a small part of a single user entry in the database is outlined by this very simple code fragment:

    static BOOL tdb_set_userinfo_10(TDB_CONTEXT * tdb, uint16 acb_info)
    {
    	SAM_USER_INFO_21 usr;
    
    	if (tdb_lockall(tdb) != 0)
    	{
    		return False;
    	}
    
    	if (!tdb_lookup_user(tdb, &usr))
    	{
    		tdb_unlockall(tdb);
    		return False;
    	}
    
    	usr.acb_info = acb_info;
    
    	if (!tdb_store_user(tdb, &usr))
    	{
    		tdb_unlockall(tdb);
    		return False;
    	}
    
    	tdb_unlockall(tdb);
    	return True;
    }
    

    The procedure is:

    LOCK
    Lock the database

    RETRIEVE
    Retrieve SAM_USER_INFO_21

    MERGE
    Into the data retrieved, modify (merge) a part of it

    STORE
    Store SAM_USER_INFO_21

    UNLOCK
    Unlock the database

    In the case of the TDB User SAM database instance, this becomes, as is seen above:

    LOCK
    tdb_lock

    RETRIEVE
    tdb_lookup_user

    MERGE
    usr.acb_info = acb_info

    STORE
    tdb_store_user

    UNLOCK
    tdb_unlock

    Now, given that there are approximately thirty known SAM_USER_INFO info levels, it makes inordinately large amounts of sense to split the merging into a single function, which in the case of SAM_USER_INFO, has the following prototype:

    /* merge takes a sam user info container (ctr) with the info
     * level named in info_level, and modifies the appropriate
     * member variables of the usr parameter
     */
    status_t sam_user_info_21_merge(SAM_USER_INFO_21 *usr,
                                    const SAM_USER_INFO_CTR *ctr,
                                    int info_level);
    /* split takes a sam user info 21 (usr), and creates the appropriate
     * member variables in the ctr parameter, at the info level
     * specified in info_level
     */
    status_t sam_user_info_21_split(const SAM_USER_INFO_21 *usr,
                                    SAM_USER_INFO_CTR *ctr,
                                    int info_level);
    
    The purpose of this function is to hide the number of info levels, as some of them are not yet known.

    SQL Database considerations

    In the case of a (My)SQL database, which only has full table locking (no row locking), and also has no foriegn key constraints, the following statements will need to be made are listed here. The full number of fields in the UPDATE statement are not listed, for clarity. The example is where the ACB info for User Rid 1000 is being modified (Set on SAM_USER_INFO_10):

    LOCK
    LOCK sam_user WRITE;

    RETRIEVE

    SELECT * from sam_user WHERE user_rid = 1000;

    MERGE
    usr.acb_info = acb_info

    STORE

    UPDATE sam_user SET user_name='...', full_name='...', ... pw_last_set_time='...', group_rid=592, ... WHERE user_rid = 1000;

    UNLOCK

    UNLOCK sam_user;

    The considerations of maintaining consistency on the SAM name space are not shown here, either, for clarity. A proper SQL database, such as PostgresQL, can have a combined foriegn key constraint on the user name in the sam_user table, group name in the sam_group table, alias name in the sam_alias table etc. that will alleviate the necessity to perform programmatic locking and manual calculation of the name space uniqueness. Messy, in other words.

    Now, the difference between using TDB (or any other Berkeley-like database) and SQL is that SQL has columns, and TDB, gdbm and the Windows Registry etc. do not. This imposes a requirement on a TDB-ish design - if implementing a SAM database in a sane way - to use the lock / retrieve / merge / store / unlock procedure. However, on column-level database implementations such as SQL, the only requirement to use the merge procedure is for simplicity and maintainability.

    If a SAM database implementor chooses not to use the advised merge procedure, that is entirely their choice. For example, assuming a PostgresQL or other database with the appropriate foriegn key constraints, here is an example SQL statement to perform exactly the same as the above SAM_USER_INFO_10 set:

    UPDATE sam_user SET acb_info = NNNN;

    Wow. What A Big Deal. Except that, the implementor will need to provide approximately fifty such UPDATE statements, all told, instead of about four or five.

    Additionally, fifty or so SELECT statements will be required to obtain all the various info levels, instead of four or five followed by calling one conversion routine to create the requested info level from the larger one.

    Other SAM Data Structures

    The other data structures which need to be similarly treated are SAM_DOMAIN_INFO, SAM_GROUP_INFO, SAM_ALIAS_INFO and SAM_DISPLAY_INFO, all of which have several info levels, not all of which are, at present, known or supported. Each of these will require a merge routine and a convert routine.

    6.1.2: sam2sam - SAM conversion utility

    sam2sam is a proposed program that will read in an entire SAM database in one format and convert it to another format. The means by which this is achieved is that sam2sam takes two parameters - the name of an input SAM dynamic library, and the name of an output SAM dynamic library. sam2sam will then enumerate all domain, user, group and alias information using the input SAM library, and call the appropriate create routines in the output SAM library.

    For this to work, there must be a common SAM API to work from. The most sensible API to use for this task is the _sam_*() API, listed above (see 6.1), as this allows for complete, comprehensive and simple data access, as outlined above.

    There is a possible alternative approach to the design of sam2sam. ntdom-netlogon has a function, NetSamSync, by which it is possible to perform a backup of an entire SAM database. There is no reason why the slurp-half of sam2sam should not use this function, other than it doesn't help with the store-half. For these reasons, sam2sam is best kept simple and symmetrical in its design, instead of using a NETLOGON dynamic library for input and a sam dynamic library for output - especially as the implementation of NETLOGON calls the _sam_*() api directly itself, anyway.

    So, in this way, sam2sam can be used to convert from any available SAM database format, such as a flat smbpasswd file format, to any other SAM database format, such as (My)SQL, LDAP - anything. So, this helps in two ways:

    SAM boot-strapping

    The issue of pre-loading a SAM database with the default entries is of some concern. The initial Administrator and Guest users and all the BUILTIN member groups do not exist until they are created, and until they are created, it is not possible to perform any authorisation - including the right to add an Administrator account!

    sam2sam can be used to pre-load a pre-defined SAM database in a flat-file format with the minimum required entries, and to store it in the preferred SAM format. That preferred format may be yet again in flat-file format, but in a different location.

    The advantage of using sam2sam to perform this operation, instead of copying the flat-file template to the live location is that file locking would be handled correctly, (which would be mandatory in any flat-file-based SAM implementation, for data integrity).

    SAM editing

    Some SAM database implementations may not allow convenient editing except using samedit or USRMGR.EXE. Certainly, the TDB implementation will not, as the data is stored purely in binary format.

    The use of sam2sam to convert any database format into a convenient flat-file format, followed by editing, followed by conversion back - whilst TNG is running (live) - will be extremely powerful. and slightly dangerous.

    6.2: Local Security Authority

    6.3: Windows NT Network Logon Service

    Provides the back-end to the Username, Domain, Password Dialog box. Also provides Backup Domain Controller Synchronisation Services. aka "A hacker's delight".

    6.4: Spooler Service

    Spooler Service (Windows NT Total Print Management) - one of the largest and comprehensive DCE/RPC services in the whole of the Windows NT Operating System.

    6.5: Service Control Manager

    6.6: Windows Registry

    Windows Registry. For some obscure reason, winreg also provides the means to remotely shut down a Windows NT system.

    6.7: Server Service

    Server Service. Mostly provides a means to list and manage SMB-related services such as shares, DFS (Microsoft's Distributed File System) Mount points.

     

    Copyright 2001