news2mail.com

HomeMicrosoftPublic › Platformsdk

microsoft.public.platformsdk.security

Windows Platform SDK: security APIs.

Win32 security programming — ACLs and SIDs, tokens and impersonation, SSPI, CryptoAPI — asked and answered by systems programmers on Microsoft’s public news server.

The group’s worked examples circulated for years as the practical companion to MSDN’s reference pages.

Long-form reference · 8,685 words · about a 38-minute read

A branch named after a kit, not a product

The group lived on msnews.microsoft.com, the public news server Microsoft ran for its own customers. How a software company came to operate newsgroups on an open protocol at all, how the names in that hierarchy were assembled, who did the answering, and how the whole arrangement was wound up between June and October 2010 are the business of the microsoft.public.* hub page, and none of it is repeated here. What concerns this page is the second component of the name.

Almost every other branch of the hierarchy was named after something a customer had bought — Excel, Word, Windows XP, Windows Update. The platformsdk branch was named after something a customer had been sent. The Platform SDK was not a product in the sense that a word processor is a product. It was the kit from which Windows software was built: header files, import libraries, a compiled reference to the whole application programming interface, sample programs, and a set of command-line tools. It arrived on discs with an MSDN subscription, it was superseded every few months, and nobody ran it. Developers linked against it.

A newsgroup named after a development kit therefore addressed a narrower and stranger audience than one named after an application. Its subscribers were not people trying to get a document to print. They were people writing the code that other people's documents would eventually pass through, and the questions they brought were correspondingly literal: what does this function return, what does this structure contain, why does the documented behaviour differ from the observed one. The security leaf of the branch inherited the most unforgiving corner of that surface — the part of the Win32 API where the operating system decides who may do what, and where a program that is subtly wrong does not crash but silently permits or silently refuses.

It is worth being precise about what the branch did and did not include, because the hierarchy's naming was catalogue naming rather than subject naming, and the same subject often had several doors. Win32 security programming, as it happens, had exactly one door among the developer branches. The neighbouring developer branch, microsoft.public.win32.programmer.*, carried twenty-four names in the surviving namespace — kernel, gdi, ole, networks, tapi, international, tools, a whole sub-branch for DirectX, and the group on Windows Management Instrumentation that this directory also holds, at microsoft.public.win32.programmer.wmi — and not one of them was a security group. If you were writing C against the security functions in advapi32.dll, the address was under platformsdk.

The twenty names still in the newsgroups file

The Internet Systems Consortium mirrors the Usenet administrative record, and its CONFIG directory carries the current namespace as a newsgroups file and an active file. The counts given here are read from the copies those two files were rebuilt into on 25 August 2026. That newsgroups file carries 1,770 names beginning with microsoft., and among them, at line 36,574, sits the group this page is about. Its entry is a group name and a description separated by a tab, and reads in full (the tab shown here as a dash):

microsoft.public.platformsdk.security — Microsoft Platform Software Development Kit newsgroup.

That description is not a charter and was never meant to be one. It is the same sentence, character for character, that stands against every other surviving name in the branch — a placeholder generated once for the whole subtree, telling a news administrator which kit the group belonged to and nothing at all about what the group discussed. The twenty names that carry it today are adsi, adsi.iis-admin, base, com_ole, complus_mts, gdi, internet.client, internet.server.isapi-dev, mapi, msi, networking, networking.ipv6, sdk_install, security, shell, telephony.tapi_3, tools, ui, ui_shell and win16. Read as a list, it is the Platform SDK's own table of contents: directory services, the base system services, COM, the graphics device interface, messaging, the installer, networking, the installer for the kit itself, the shell, telephony, the build tools, the user interface, the sixteen-bit compatibility layer — and security.

The active file, which is what a news server actually consults, lists the same twenty with a high-water mark of zero, a low-water mark of one and the flag y, meaning that posting is permitted. In plain terms: the group exists in the namespace, it will accept an article, and it holds none. That is the honest state of the record. The name outlived the server, the kit, the company's news service and the protocol's commercial heyday, and it is still there because removing a name from a distributed namespace requires somebody to take the trouble.

Somebody did take the trouble, once, for sixteen of the branch's other names. The ISC control archive holds files for thirty-six microsoft.public.platformsdk.* groups. Sixteen of them — active.directory, component_svcs, database, directx, dist_svcs, graphics_mm, graphics_mm.directx, internet.server, localization, messaging, mslayerforunicode, multimedia, telephony.tapi_2, telephony.tsp, telephony.wte and win_base_svcs — received a removal message on 15 December 2009 and are absent from the newsgroups file. The twenty that did not receive one are precisely the twenty that remain. The correlation is exact, which is unusual enough in Usenet housekeeping to be worth stating.

Who actually maintained the namespace

The removal messages of December 2009 were not sent by Microsoft. They were PGP-signed articles from [email protected], an address belonging to Julien Élie, and each carried the same explanatory body. The group in question, it said, had recently been removed from Microsoft news servers, so the article was being sent to make the name defunct; administrators who wanted to carry the official and up-to-date list of Microsoft newsgroups should remove it. The notice then gave the scale of the exercise, and it is the sort of detail that only survives because someone wrote it into the message body: 535 obsolete newsgroups had been removed on 10 December 2009, and 535 corresponding control articles would go out over five days at 107 a day.

This arrangement is documented in the file that news servers use to decide which control messages to obey. The entry for the hierarchy in ISC's copy of control.ctl is candid about the division of labour: control articles for that hierarchy, it notes, are not issued by Microsoft itself but by a Usenet active participant, in order to improve the quality of the propagation of Microsoft newsgroups. Unsigned newgroup and rmgroup messages for microsoft.* are dropped; signed ones from the trigofacile address are honoured. The same entry still names msnews.microsoft.com as the syncable server for the hierarchy, and microsoft.public.news.server as its administrative group. Both entries are now epitaphs. The companion HIERARCHY-NOTES file states the position more bluntly still: Microsoft did not maintain its hierarchy via Usenet control messages, and the maintainer received a list of changes to make every week directly from Microsoft by e-mail.

The effect is a namespace kept tidy by a volunteer on the strength of a weekly e-mail from a corporation that had no intention of speaking the protocol itself. When the e-mails stopped, the tidying stopped, and the twenty names froze where they were.

What the control-message archive says about this group

The ISC control archive holds a single file for microsoft.public.platformsdk.security, and it contains eleven articles. The first is genuine. It was sent from [email protected] on Monday 27 November 2000 at 10:40:17 Pacific time, approved by the same address, injected from a host recorded in the headers as tide91.microsoft.com and carried outward through a Microsoft news host named in the path as tkmsftngp01. Its body is two lines: the line “For your newsgroups file:” followed by the bare group name — no description, no charter, nothing a Big-8 proposal would have recognised as a rationale.

It is tempting to read that timestamp as the group's birthday, and it should not be. Thirty-three of the thirty-six platformsdk names in the archive carry a message from the same address in that same minute, timed at either 10:40:16 or 10:40:17, and the hierarchy's own administrative group carries one from the same batch, timed at 10:40:13. So do groups that plainly existed long before: the file for microsoft.public.word.numbering opens with a message of April 1998 and then carries one from 27 November 2000, and microsoft.public.excel.programming opens in October 1999 and is re-announced in the same 2000 batch. The only platformsdk names with nothing from that morning — adsi.iis-admin, internet.server.isapi-dev and mslayerforunicode — are three that were announced on their own afterwards, in April and August 2001. What happened that morning, in other words, was a bulk re-announcement of the whole hierarchy, not the creation of it. For this group, the honest statement is narrower than a founding date: the earliest surviving control message shows that the name existed and was being propagated by 27 November 2000, and nothing in the archive establishes when it was first created.

The remaining ten articles in the file are not Microsoft's, and they are a small comedy of Usenet's control-message problem. In March 2002 a newgroup message for the group arrived from [email protected], carrying a Distribution header reading collabra-internal — an internal housekeeping article from a corporate news server that escaped onto the public network. In May 2003 a server at the Central Bank of Russia began issuing its own control articles for the group, first a newgroup and then, two and a half hours later, an rmgroup. Somebody using the name Alt Comp Administrator answered each removal with a fresh newgroup message, every one of them carrying a Summary header describing itself as countermeasures taking into account forged Central Bank of Russia posts. The exchange ran to four removals and four counter-creations between 7 May and 2 June 2003.

None of it had any authority. All of it was in the spool. This is what the administrative record of a corporate group on an open network actually looks like: one legitimate announcement, and then whatever anybody else's misconfigured server happened to emit. There is no genuine removal record for microsoft.public.platformsdk.security, which is the mechanical reason it is still listed.

The subject: security identifiers

Everything the group existed to discuss rests on a small number of structures that Windows NT inherited from its original design and never fundamentally changed. The first is the security identifier, or SID: a variable-length binary value that names a security principal — a user account, a group, a logon session, a machine — uniquely and for the lifetime of that principal within its domain. Names are labels attached to SIDs, not the other way round. Rename a user and every access-control entry that refers to her keeps working, because the entries never held her name.

Written out for humans, a SID begins with S, a revision level (still 1), an identifier authority, and a sequence of subauthorities ending in a relative identifier. The identifier authorities are the coarse divisions of the system's imagination about who might be asking: 0 is the null authority, 1 the world authority, 3 the creator authority, 5 the NT authority under which nearly everything real is issued. A domain account is S-1-5-21 followed by a ninety-six-bit domain identifier and then the account's relative identifier; the built-in groups are S-1-5-32 followed by a fixed number.

The well-known SIDs are the ones that recur in every thread about an access check going wrong, because they are the ones that do not mean what their display names suggest. S-1-1-0 is World, shown in the interface as Everyone. S-1-5-11 is Authenticated Users, which — Microsoft's own reference is explicit — does not include the Guest account even when Guest has a password, and does include authenticated principals from any trusted domain rather than only the current one. S-1-5-7 is Anonymous Logon, a caller who supplied no name and no password, and the documentation goes out of its way to distinguish it from the account IIS historically used for anonymous web access: that account had a password and was logged on by the service, so the anonymous web visitor arrived as a member of Authenticated Users and not as Anonymous Logon at all. S-1-3-0 is Creator Owner, a placeholder that only makes sense inside an inheritable entry, where the system substitutes the SID of whoever creates the new object. S-1-5-18 is the local system account, which is a hidden member of the built-in Administrators group, and which — when it reaches across the network — presents not itself but the machine's domain identity.

That last sentence is the source of an entire genre of question, and it is worth reading twice. A process running as LocalSystem has the Administrators SID in its token on the machine it is running on, and the computer account's SID on any other machine it talks to. Nothing about the display name warns you.

The access token

The second structure is the access token. Microsoft defines it as an object describing the security context of a process or thread; it is built by the logon process once the credentials have been checked, attached to the first process created in the session, and inherited by everything that process starts. Its contents are enumerated in the reference documentation and are worth listing, because almost every question the group carried was really a question about one of these fields: the SID of the user's account; the SIDs of the groups of which the user is a member; a logon SID identifying the current session; the list of privileges held by the user or the user's groups; an owner SID; the SID of the primary group; a default discretionary access-control list to be applied to objects the user creates without specifying one; the token's source; whether it is a primary or an impersonation token; an optional list of restricting SIDs; the current impersonation level; and assorted statistics.

Two properties of that list caused more trouble than the rest combined. The first is that group membership in a token is a snapshot. The token is built at logon; adding a user to a group afterwards does not change any token that already exists, which is why the standard answer to a great many puzzled reports was that the caller needed to log off and back on. The second is that a group SID in a token can be present but not enabled, and during an access check the system ignores group SIDs that are not enabled. Presence is not the same as effect.

The API surface around tokens is small and was entirely contained in the Platform SDK: OpenProcessToken to get the primary token of a process, OpenThreadToken to get the impersonation token of a thread, GetTokenInformation and SetTokenInformation to read and write the parts, AdjustTokenGroups and AdjustTokenPrivileges to enable and disable what is already there, DuplicateToken and DuplicateTokenEx to copy one, CheckTokenMembership to ask whether a given SID is enabled in it, and CreateRestrictedToken to make a deliberately weakened copy with SIDs disabled, privileges deleted, or a list of restricting SIDs attached. The structures they read and write are TOKEN_USER, TOKEN_GROUPS, TOKEN_PRIVILEGES, TOKEN_OWNER, TOKEN_PRIMARY_GROUP, TOKEN_DEFAULT_DACL, TOKEN_SOURCE and TOKEN_STATISTICS, selected through the TOKEN_INFORMATION_CLASS enumeration. A programmer who had internalised those eight structure names had internalised most of the model.

Security descriptors, DACLs and SACLs

The third structure is the security descriptor, which is what a securable object carries instead of the Unix triple of owner, group and mode bits. Anything with a name can have one: files, directories, shares, registry keys, processes, threads, named pipes, services, job objects, printers, directory objects. A descriptor holds an owner SID, a primary group SID, and up to two access-control lists.

The discretionary access-control list — the DACL — identifies the trustees allowed or denied access. The system access-control list — the SACL — does not control access at all; it controls auditing, specifying which access attempts by which trustees generate a record in the security event log, and whether on failure, on success or both. Reading or writing a SACL requires a privilege of its own, SE_SECURITY_NAME, because ACCESS_SYSTEM_SECURITY is not an ordinary access right. The distinction between the two lists is the single most common terminological confusion in the subject, and the reason the acronyms differ by one letter has never helped anybody.

Each list is a sequence of access-control entries. An entry names a trustee and an access mask and says allow, deny or audit. Entries can be placed on an object explicitly or inherited from its parent. Microsoft's guidance on the point is unambiguous and was repeated in the group's subject matter for a decade: do not work directly with the contents of an access-control list; use the functions, because a hand-assembled list can easily be syntactically valid and semantically wrong.

Then there is the trap that catches everyone once. A DACL that is present but empty — properly allocated, correctly initialised, containing no entries at all — grants nothing to anybody. A DACL that is absent, because the descriptor's DACL pointer is null, grants everything to everybody: normal security checking is simply not performed against that object. Two states that look almost identical in a debugger, with opposite meanings. Microsoft eventually gave the distinction its own reference page.

How the access check actually runs

The algorithm is short enough to state in full, and stating it in full explains most of the questions the group carried. When a thread asks for access to a securable object, the system takes the thread's access token — the primary token of its process, unless the thread is impersonating, in which case the impersonation token — and walks the object's DACL in order. It stops at the first of three outcomes: an access-denied entry that explicitly denies any of the requested rights to a trustee listed in the token; a set of access-allowed entries that between them explicitly grant all of the requested rights; or the end of the list, at which point any right not explicitly granted is implicitly denied.

Because the walk stops early, order decides the answer. The same set of entries in a different sequence can produce a different result, and Microsoft therefore defines a preferred order rather than leaving it to chance: explicit entries before inherited ones; within the explicit group, denials before permissions; inherited entries in the order inherited, parent before grandparent and so on up the tree; and denials before permissions again at each inherited level. The documentation then notes the part that mattered to a programmer rather than an administrator — functions such as AddAccessAllowedAceEx append to the end of a list, so it is the caller's responsibility to add entries in the correct order. Nothing checks.

Block diagram, labelled in German, of the Windows NT 3.1 operating system split into user mode (Benutzermodus) and kernel mode (Kernelmodus): logon process, OS/2, Win32 and POSIX programs and their subsystems above the line; below it the system services with I/O manager, object manager, security monitor, process manager, LPC manager and VM manager, over the microkernel, the hardware abstraction layer and the hardware.
A German-labelled layer diagram of Windows NT 3.1, the release in which this security model shipped. The security reference monitor — here Sicherheitsmonitor — sits in kernel mode among the executive services, alongside the object manager, and it is there that the access check described in this section is performed, against the security descriptor of the object and the token of the calling thread. Everything the Platform SDK exposed to a programmer was a way of asking that component a question. The structure survived, with additions, into the Windows 2000 and XP releases the group discussed. Liliana • · Attribution · via Wikimedia Commons.

A server application that needs to make this decision itself, rather than letting the object manager make it, calls AccessCheck. Its signature is a compact statement of everything above: a pointer to a security descriptor, a handle to a client token, a desired access mask, a generic mapping, an output privilege set, an output granted-access mask, and an output boolean. The details are where the group's traffic lived. The token handle must be an impersonation token, not a primary one, and must carry TOKEN_QUERY access or the call fails with ERROR_ACCESS_DENIED. The desired access mask must already have been run through MapGenericMask, because AccessCheck will not accept generic rights. The descriptor must contain both an owner and a group SID or the call fails with ERROR_INVALID_SECURITY_DESCR. And AccessCheck generates no audit record; if you want one you must call a different function entirely, one of the AccessCheckAndAuditAlarm family. Four ways to get a wrong answer from a correct-looking call, before anyone has written a single access-control entry.

Impersonation and its four levels

Impersonation is the mechanism by which a server does work as its client rather than as itself, and it is the reason the Windows model can answer a question that a simple process-identity model cannot: not “may this server read the file” but “may this server read the file on behalf of this particular caller”. A thread that is impersonating carries two tokens, its process's primary token and its own impersonation token, and most of what it does uses the second.

How far the server may go is fixed by the SECURITY_IMPERSONATION_LEVEL enumeration, which defines exactly four values, and the differences between them are the substance of a great deal of the group's subject matter. At SecurityAnonymous the server can neither impersonate nor identify the client. At SecurityIdentification it can obtain the client's identity and privileges but cannot act as the client — enough to make an access-control decision of its own, not enough to open anything. At SecurityImpersonation it can act as the client on the local machine. At SecurityDelegation it can act as the client on remote machines as well.

The level is chosen by the client, not the server: a named-pipe client passes it to CreateFile, a DDE client through DdeSetQualityOfService and the SECURITY_QUALITY_OF_SERVICE structure, and SecurityImpersonation is the default for named-pipe, RPC and DDE servers. Except that when the connection is remote, the flags the client passed are ignored, and the level is instead determined by what the server's account is permitted to do — a flag on that account in the directory service. A client that carefully requested identification-level impersonation and got delegation-level anyway, because the server it happened to reach was trusted for delegation, is behaving exactly as documented and exactly as nobody expects.

Impersonation also has holes in it that are deliberate. Microsoft lists three. If an impersonating thread calls CreateProcess, the new process inherits the process's primary token, never the impersonation token. Functions requiring SE_TCB_NAME, such as LogonUser, always check the primary token. Functions requiring SE_AUDIT_NAME do the same. The first of those three is the origin of one of the most reliable recurring questions in any Win32 security forum: a service impersonates a user, launches a helper program, and the helper runs as the service.

Privileges are not permissions

The distinction between a privilege and an access right is stated in one sentence in Microsoft's documentation and misunderstood roughly forever afterwards. Access rights control access to securable objects and are granted by entries in an object's DACL. Privileges control access to system-wide operations and system resources, are assigned to accounts by an administrator, and are carried in the token. Different mechanism, different source of authority, different failure mode.

Privileges are identified in source code by string constants defined in winnt.h — SE_BACKUP_NAME, SE_DEBUG_NAME, SE_TCB_NAME and the rest — but the functions that read and adjust them use a LUID, a locally unique identifier whose numeric value can differ between machines and between boots of the same machine. LookupPrivilegeValue converts the constant to the current LUID; LookupPrivilegeName goes back the other way. Hard-coding the number is a mistake that works on the machine where it was tested. Most privileges, moreover, are disabled by default in a token even when they are held, so AdjustTokenPrivileges is a routine part of using one — and the function's own documentation is careful to say that it enables and disables privileges but neither grants new ones nor revokes existing ones.

Several privileges override the access check outright, which is the cleanest demonstration that they are a separate mechanism rather than a species of permission. SE_BACKUP_NAME causes the system to grant all read access to any file regardless of its access-control list, while leaving every non-read request to be evaluated normally. SE_RESTORE_NAME does the same for write access. SE_TAKE_OWNERSHIP_NAME allows an object's ownership to be seized without any discretionary access having been granted. SE_CHANGE_NOTIFY_NAME, which is enabled by default for every user, causes the system to skip traversal access checks altogether — so a user who has been carefully denied access to an intermediate directory may still reach a file beneath it, on a default installation, entirely by design.

Two further privileges belong to the impersonation story rather than the file-access one. SE_IMPERSONATE_NAME, the constant behind SeImpersonatePrivilege, is described as required to impersonate, with the corresponding administrative right named “Impersonate a client after authentication”. SE_ENABLE_DELEGATION_NAME is required to mark user and computer accounts as trusted for delegation. And CreateProcessAsUser — the correct answer to the CreateProcess problem above — demands SE_INCREASE_QUOTA_NAME and, if the token is not assignable, SE_ASSIGNPRIMARYTOKEN_NAME, failing with ERROR_PRIVILEGE_NOT_HELD, error 1314, when it does not have them. Microsoft's own reference tells the caller to use CreateProcessWithLogonW instead when that happens, on the grounds that it requires no special privileges — though the account it runs as must be allowed to log on interactively.

The Platform SDK as a shipped object

The kit the branch was named for has a well-documented lineage and a confusing set of names. The package began in 1992 as the Microsoft Windows SDK for Windows 3.1x and the Microsoft Win32 SDK for Windows NT 3.1, went through Win32 SDK releases numbered 3.5 in April 1994, 3.51 in June 1995, 4.0 in November 1996 and 5.0 in June 1998, and was renamed Platform SDK in 1999. Under that name it shipped as a dated edition on MSDN subscription CD-ROMs, roughly every few months: April 1999, September 1999, January 2000, April 2000, July 2000, November 2000, February 2001, June 2001, August 2001, November 2001, May 2002, July 2002, August 2002, November 2002, February 2003.

A monochrome console screen from a Windows 95 pre-release SDK reading "Switching to debug system files...", followed by a list of files copied from C:\SDK\DEBUG, including KRNL386.EXE, GDI.EXE, USER.EXE, GDI32.DLL, KERNEL32.DLL, USER32.DLL and MMSYSTEM.DLL.
A Microsoft software development kit doing the only thing such a kit ever did on a machine: installing itself. Here the SDK shipped with the Windows 95 pre-release build 4.00.314, in January 1995, copies its checked builds of the system binaries from C:\SDK\DEBUG over the retail ones. This is the class of object the platformsdk branch was named for — a dated edition that arrived on subscription disc, was superseded every few months, and was linked against rather than run. The group's name outlasted the product name by four years. OS: Microsoft Corporation Files: BetaWiki-Beitragende · public domain · via Wikimedia Commons.

The dates matter more than they look. The group's earliest surviving control message is from 27 November 2000, which places its first documented existence between the Platform SDK of November 2000 — also known as the Platform SDK for Whistler Beta 1, and the edition that carried preliminary tools for Itanium — and the February 2001 edition for Whistler Beta 2. The group was therefore contemporary with the development of the operating system that shipped as Windows XP, and its early years coincided with the SDK's most rapid period of revision.

Two later editions deserve naming. The Platform SDK of February 2003, build 5.2.3790.0, was the last to support Visual C++ 6.0 and the last to support Windows 95 and Windows 98 — a hard boundary that any thread about which SDK to install had to reckon with. And the Platform SDK for Windows XP Service Pack 2, released in August 2004, introduced strsafe.h, a header of length-checked string functions — the safe counterparts to exactly the C runtime string calls that Microsoft's own engineers had by then begun to ban, in the security work described further down this page. The Windows Server 2003 R2 Platform SDK of March 2006 was the last able to develop for Windows 2000.

Then the name went away. Starting with Windows Vista, the Platform SDK and the separate SDKs for the .NET Framework, Tablet PC, Windows Media and PowerShell were folded into a single Microsoft Windows SDK; the release of March 2007, version 6.1, is recorded as the first unified .NET and Platform SDK. The package dropped the Microsoft prefix in 2013 and has been the Windows SDK since. The newsgroup branch, of course, kept the old name, because names in a namespace do not get renamed when the thing they refer to does. By the time the news server was switched off, the group's second component had been out of use as a product name for four years: the last kit to carry it was the Windows Server 2003 R2 Platform SDK of March 2006.

SSPI and the providers behind it

Authentication in Win32 was reached through the Security Support Provider Interface, and SSPI's design goal is stated plainly in the documentation: to let an application use whatever security models are available on a machine or a network without changing the interface to the security system. A security support provider is a DLL that implements the interface and makes one or more security packages available; each package maps the interface's calls onto an actual protocol. SSPI does not establish logon credentials, because that is a privileged operation belonging to the operating system.

The function set is small and symmetrical, and it is the shape of the interface rather than any individual call that a programmer had to learn. Package management enumerates and describes what is installed: EnumerateSecurityPackages, QuerySecurityPackageInfo, InitSecurityInterface. Credential management obtains an opaque handle to a principal's existing credentials with AcquireCredentialsHandle and releases it with FreeCredentialsHandle. Context management builds the shared security context that client and server end up holding: the client drives InitializeSecurityContext, the server answers with AcceptSecurityContext, and the two exchange opaque tokens until the handshake completes. QuerySecurityContextToken then yields an ordinary Windows access token from the finished context — the bridge back to everything in the earlier sections of this page — while ImpersonateSecurityContext and RevertSecurityContext do the impersonating. Message support functions then use the context to sign, verify, encrypt and decrypt: MakeSignature and VerifySignature for integrity, EncryptMessage and DecryptMessage for confidentiality.

Behind the interface Microsoft shipped a fixed set of providers, and the documentation lists them: Microsoft Negotiate, Microsoft Kerberos, Microsoft NTLM, Microsoft Digest, Secure Channel, and later the Credential Security Support Provider. Schannel is the one that implements SSL and TLS; Negotiate is not a protocol at all but a chooser, and the reference pages for both Kerberos and NTLM say the same thing in the same words — an application should not access the package directly but should use Negotiate, which currently selects between Kerberos and NTLM and selects Kerberos unless one of the systems involved cannot use it.

NTLM, the fallback, is a challenge-response scheme documented in enough detail to be worth summarising because so many threads about failed authentication turn on it. The client hashes the password at interactive logon and discards the password. The server generates an eight-byte random challenge. The client encrypts the challenge with the password hash and returns the result. For a domain account the server cannot verify this itself: it forwards the user name, the challenge and the response to a domain controller, which retrieves the hash from its account database, performs the same computation and compares. Three machines, and the one holding the secret is not the one being asked for access. That structure is precisely why NTLM cannot be delegated — the server never possesses anything it could present onward on the client's behalf.

Kerberos arrives with Windows 2000

What displaced NTLM as the default was Kerberos, version 5 of which had been published by Neuman and Kohl in 1993 as RFC 1510 and was later obsoleted by RFC 4120 in 2005. Windows 2000 and every later version use it as their default authentication method; Microsoft's extensions to it are themselves published as RFCs, including RFC 3244 on the Windows 2000 change-password and set-password protocols and RFC 4757 on the use of RC4. Microsoft implemented the protocol rather than adopting the MIT code.

The mechanism is different in kind from challenge-response. A client obtains tickets from a Key Distribution Centre and presents them to servers when connections are established; the tickets are the client's network credentials, and they provide mutual authentication before a secure connection exists, on the explicit assumption — the documentation says so — that the network is open, that most clients and many servers are not physically secure, and that packets can be watched and altered at will.

Diagram of the Kerberos protocol in three stages — client authentication to the AS, client service authorization, and client service request — showing messages A to H passing between a client, an authentication server and a ticket-granting server inside the key distribution centre, and a service server, with coloured keys and padlocks marking which key encrypts each message.
The Kerberos message exchange in the general form defined by the protocol: a client authenticating to the authentication server, obtaining a ticket-granting ticket, exchanging it at the ticket-granting server for a service ticket, and presenting that ticket to the service. Windows 2000 made this the default authentication method, displacing the NTLM challenge-response described in the previous section, and with it came delegation — the ability of a server to act as its client against a third machine. The diagram is of the protocol itself rather than of Microsoft's implementation of it. Jeran Renz · CC BY-SA 4.0 · via Wikimedia Commons.

For the readership of a Win32 security group, the practical consequence of the change was delegation. Because a Kerberos client's credentials are tickets rather than a proof computed from a secret it cannot forward, a server can be given the ability to act as its client against a third machine — the SecurityDelegation level of the impersonation enumeration, reachable in practice only over Kerberos and only when the server's account has been marked as trusted for delegation in the directory. The problem this creates is the one every three-tier application ran into: a web front end authenticates a user, needs to reach a database server as that user, and finds that its impersonation stops at the machine boundary. Unconstrained delegation solved it and was blunt, since a compromised front end could then act as its users anywhere. Microsoft's own account is that constrained delegation was introduced to provide a safer form that restricts the services a given server may act towards, with the configuration originally in the hands of the domain administrator; Windows Server 2012 and 2012 R2 moved that decision to the administrator of the resource being reached, using extensions to the Service for User protocol. That last change is a decade after the gateway and is noted here only for continuity.

CryptoAPI and its successor

The cryptographic half of the subject had its own interface and its own vocabulary. CryptoAPI — CAPI to its users — first appeared in Windows 95 OSR2 and was enhanced in the versions that followed. Its organising idea is the cryptographic service provider: a replaceable module that performs the actual encoding and decoding, so that an application talks to the interface and a CSP talks to the algorithms, or to a hardware security module if the vendor supplied one. The interface supported public-key and symmetric-key work, certificate-based authentication, and a cryptographically secure pseudorandom generator in CryptGenRandom. Persistent symmetric keys it did not support.

The successor is Cryptography API: Next Generation, introduced with Windows Vista, whose Microsoft implementation lives in bcrypt.dll. CNG factored the interface so that the same functions work across a wider range of algorithms, added elliptic-curve cryptography and the algorithms of the NSA's Suite B, worked in kernel mode as well as user mode, and kept support for everything CryptoAPI had done. It also replaced the default random-number generator, which had been based on the superseded FIPS 186-2 construction over DES or SHA-1, with a counter-mode deterministic generator using AES.

CNG arrived after the news server's most active years and after the branch's name had already been retired from the SDK. A reader working through an archived thread from 2003 that recommends a CryptoAPI approach is reading advice that was correct when written and has since been superseded twice over, which is a general caution about this entire archive and not a criticism of anyone who posted in it.

What the traffic looked like

No thread from this group is quoted or named on this page, and no posting count is asserted, because no such record has been verified. What can be described honestly is the shape of the problem space a group with this charter necessarily carried, since the recurring failure modes of the Win32 security API are documented by Microsoft itself in the reference pages quoted above. Six genres account for most of it.

The access check that denied the wrong caller. An administrator adds a user to a group and the user still cannot open the file, because the token was built at logon and does not know. A denial entry sits below a permission entry in a hand-built list and never fires, because the walk stopped early. A descriptor is created with a null DACL, meaning no checking at all, when an empty one was intended, meaning no access at all. A caller with SeChangeNotifyPrivilege — everybody, by default — walks straight through a directory that was supposed to stop them. A backup program reads files that no entry in any list permits it to read.

The token lost across a process boundary. A service impersonates a client, calls CreateProcess, and the child runs as the service, because the documentation says the child always inherits the primary token. The fix is CreateProcessAsUser with a primary token produced by DuplicateTokenEx from the impersonation token, and it requires privileges the caller frequently does not hold, whereupon the call returns error 1314 and the thread starts again from the beginning.

Service accounts. A service written and tested interactively behaves differently when it runs as LocalSystem: its token carries the built-in Administrators SID as well as the system one, it presents the computer's credentials to remote servers rather than an identity of its own, and it is associated with no logged-on user at all, so HKEY_CURRENT_USER resolves to the default user rather than to anybody's profile. The lower-privilege alternatives, LocalService and NetworkService, arrived with Windows XP as S-1-5-19 and S-1-5-20 and moved the question from “why can my service not do this” to “which of these three accounts can”.

Impersonation across the network. The double hop: a middle tier authenticates a user and cannot pass that identity to a third machine. The answer involves Kerberos rather than NTLM, delegation-level impersonation rather than impersonation-level, and a directory configuration that a developer usually could not make and had to persuade an administrator to make. It is the single hardest thing in this subject to get working from a book, because half of the required state is not in the program.

Which package, which level, which handle. SSPI questions tend to be about the sequence rather than the semantics: how many round trips the handshake takes, what to do with the buffer that comes back, when the context is complete, and how to get from a completed context to a token you can impersonate.

The gap between the reference and a working example. This is the genre the group existed to fill, and it is the reason a page like this one exists at all. The Win32 security reference is a superb description of every structure and function in isolation and was, for years, close to useless as an account of how to accomplish a task, because accomplishing a task meant composing eight calls in the right order with the right error handling. The reference did eventually acquire worked examples of its own — there are pages on enabling and disabling privileges, on searching for a SID in an access token, and on modifying the access-control lists of an object — but a worked example is a different kind of document from a function reference, and for a long time the composing was done in threads.

The security turn of January 2002

Halfway through the group's life, the company that ran the server changed its mind about the subject the group discussed, and the change is documented well enough to date precisely. On 15 January 2002 Bill Gates sent a memo to Microsoft's employees launching the initiative called Trustworthy Computing, referencing an internal white paper by the chief technical officer, Craig Mundie. Four areas were named as its pillars: security, privacy, reliability and business integrity. Contemporary accounts attribute the move to pressure from large customers — government agencies and financial institutions among them — after a run of self-replicating worms.

The engineering effort that accompanied it has since been described in detail by the two people who ran it, Steve Lipner and Michael Howard, first in IEEE Security and Privacy in January 2003 and again in a twenty-year retrospective in the same journal in 2023. Their account is the source for what follows. The idea surfaced at a joint offsite meeting of the Secure Windows Initiative and Microsoft Security Response Centre leaders in early November 2001, where a brainstorm about the .NET Framework's own security push raised the question of whether the same thing could be done for Windows. The manager it was put to called it half-baked, and worried that the engineers in the Windows Division would simply roll their eyes; the authors concede that it was half-baked. That meeting nonetheless launched a two-month planning effort, and by the middle of December a senior executive was asking which dates to reserve the thousand-person room in the Microsoft Conference Centre for. The planning and the corporate discussions that produced the memo ran largely independently of one another; the authors call the two synergistic, and credit the memo with convincing engineers that the company meant it.

The push itself began in late January 2002 with training for every engineer working on the Windows release then in development. Over five days the Secure Windows Initiative team delivered ten four-hour sessions to groups of around 900 engineers; the retrospective corrects the original article, which had said ten days. Each session was introduced by a vice president from one of the Windows teams, and — the authors single this out as the detail that made the difference — the vice presidents stayed for the full four hours rather than delivering the exhortation and leaving.

Feature development in the Windows Division then stopped. The division numbered 8,500 developers, and its existing shiproom machinery was turned to the new purpose: a war-team meeting each morning reviewed status, assigned problems and approved changes, with fixes to conform to the training or to errors found by static analysis approved automatically and design changes — removing a component, disabling one by default — argued out in the meeting. The weekly Friday all-hands meetings for the whole division reported the push's progress, and the executive running the division handed out cash prizes for the best bug of the preceding week; the authors report that a perennial favourite was the oldest bug written by the most senior person. Across tens of millions of lines of code, the effort produced several thousand changes.

Not everything worked. Threat modelling, the authors write, was in its early days in 2002, and the guidance and training for it were only actionable by people who were already experts; the models that came out of the push were largely the ones produced with help from the team or from consultants on site. The original intention had been to review Windows XP alongside the server release and ship the results as Service Pack 1, and that plan was abandoned midway through, for three reasons the retrospective sets out: the sheer size of a service pack containing that many changes, the effect on existing XP users of removing features or disabling them by default, and a date fixed by Microsoft's antitrust settlement with the United States Department of Justice for changes to the default browser option, which was incompatible with the release schedule of the code base going through the push.

What the push changed in the toolchain and the defaults

Three of the changes bear directly on the toolchain the group's readers worked in, and one of the three shipped inside the Platform SDK the group was named for, which is why they belong on this page rather than in a general history.

The first is a compiler flag. Visual C++'s stack-based memory-corruption detection, the -GS option, was made mandatory; the retrospective notes drily that for Windows Server 2003 it was the only security-related compiler flag they had, and that many more followed. The second is a set of prohibitions: the team began banning classes of C runtime function outright — strcpy, strncpy, sprintf and others of that family — a discipline eventually embodied in a header that flags the banned names at compile time. The third is the replacement, and this is where the SDK comes in: strsafe.h, the header of length-checked string functions, shipped in the Platform SDK for Windows XP Service Pack 2 in August 2004. A programmer downloading that edition of the kit got the security push's conclusions as a file they could include.

The defaults changed alongside the code. From the security push onward, the stated position was that a feature should be enabled by default only if most users would need it, that network ports should be blocked by default, and that features should not require privilege to run. Michael Howard put the same principle to MSDN Magazine in March 2003 in his own words: that he would rather people did not focus on security features at all, that the biggest benefit was installing only critical features by default, and that a feature used by fewer than ninety per cent of a product's users should be off. In the same interview he named legacy code as the biggest challenge, described mandatory training and improved tools as already in place, and defined the Secure Windows Initiative's goal as putting itself out of business by seeding expertise into the product groups.

The book that came out of the period is Writing Secure Code, by Michael Howard and David LeBlanc, published by Microsoft Press. The retrospective explains its origin: the authors wrote it as a response to the security questions engineering teams kept asking, wanting to spend their time on hard problems rather than daily minutiae, so they wrote down the elements of secure design, coding and test. The first edition appeared in 2001, before the push; the second edition followed in 2002, and the push relied on it — the planning effort's own list of activities included, alongside the training sessions and the static-analysis runs and a signoff on every source file, relying on the techniques documented in the book. It is the closest thing the era has to a single artefact tying the memo, the engineering effort and the developer audience together.

The results shipped as Windows Server 2003, released to manufacturing on 28 March 2003 and generally available on 24 April, and — after a further delay to the Windows development schedule — as Windows XP Service Pack 2 on 25 August 2004, which turned the firewall on by default, removed raw socket support, added the Security Center, and introduced Data Execution Prevention with hardware backing from the no-execute bit in new processors. The authors record that the Secure Windows Initiative team played a key role in that last item. Microsoft did not believe the pushes had solved the problem, and the Slammer worm in January 2003 and Blaster in July 2003 confirmed it; the response was the Security Development Lifecycle, proposed to Steve Ballmer and his staff in late 2003 and made mandatory on 1 July 2004. The initial version was numbered 2.0 because the authors regarded the security pushes as a de facto version 1.

What came after, and where the question goes now

Everything in this section postdates the gateway that this directory records, and is included so that a reader arriving from an old link knows which parts of the model have moved.

Windows Vista and Windows Server 2008 added Mandatory Integrity Control, which grafted a mandatory layer onto the discretionary one: every process gets an integrity level, every object can carry a minimum required level, and a process may not write to or delete an object whose required level is above its own, whatever the discretionary list says. It is implemented, characteristically, as a new access-control entry type carried in the system access-control list — the auditing list — rather than as a new field, and the levels are themselves SIDs under a new identifier authority, S-1-16-4096 for low through S-1-16-16384 for system. User Interface Privilege Isolation is built on top of it, and the same release added per-service SIDs under S-1-5-80, so that a service could be given an identity of its own rather than borrowing LocalSystem's. Anyone reading an archived thread on service identity should know that the answer changed in 2006.

The documentation moved too. The MSDN Library these groups grew up beside no longer exists as such; the Win32 reference now lives on Microsoft Learn, published from public repositories under the MIT licence for code and permissive Creative Commons terms for prose, which means the reference pages quoted throughout this article can be corrected by their readers — a change of arrangement that the group's own subject matter would have found remarkable.

As for where the questions went: the web forums that Microsoft offered as the successor to the newsgroups did not last very long either. On 13 January 2021 Microsoft announced that all English MSDN and TechNet forums were being set as read-only, after what it described as a six-month effort, with technical support for active products centralised in Microsoft Q&A. The existing threads were left where they were rather than migrated. A Win32 security question asked today is asked there, or on a general programming site, or in an issue against the documentation repository. It is not asked in a newsgroup, and it has not been for a long time; how to reach Usenet at all today is explained separately.

Scope, limits and what is not established

This page is a reference to a newsgroup and to the technical world that newsgroup belonged to. It gives no security advice, recommends no configuration and contains no code; where it describes an interface it describes what Microsoft's documentation says the interface does, and where it describes a historical decision it describes what the people who made it have published about it. Readers should assume that any specific technique discussed in the group's own archive reflects the state of the platform at the time it was written.

Several things this page deliberately does not claim. It does not give a creation date for the group: the earliest control message in the ISC archive is dated 27 November 2000, but that message belongs to a bulk re-announcement of the entire hierarchy and demonstrates only that the name existed and was propagating by then. It names no participant, quotes no posting and gives no message count, because none has been verified from a primary source for this group specifically; the archive of the public groups exists, but nothing in it has been cited here that this page has not read. It asserts no subscriber figure, no traffic figure and no ranking of the group against its neighbours, all of which would be invention.

Two further gaps are worth naming. There is no surviving charter. The ISC description is a generated placeholder identical across the branch, and the vendor descriptions that circulated with Microsoft's own group list were product-catalogue copy rather than a ratified statement of scope, so the boundary between this group and its neighbours — and the namespace held 35 microsoft.* names containing the word security at the last count, from microsoft.public.security through microsoft.public.win2000.security, microsoft.public.inetserver.iis.security and microsoft.public.security.crypto to national variants under nine country prefixes — was a matter of custom rather than rule. And there is no closure date. Microsoft shut its newsgroups in phases, so each group has its own last day, and for this one the only reliable evidence would be the tail of its own archive.

Related pages in this directory: the hierarchy itself at microsoft.public.*; the scripting side of Microsoft security work, where administration rather than programming was done, at microsoft.public.scripting.vbscript; and the full list of groups this gateway carried on the all-groups page.

Reading microsoft.public.platformsdk.security today