1#
2# Copyright (c) 2003-2014, Ashok P. Nadkarni
3# All rights reserved.
4#
5# See the file LICENSE for license
6
7# TBD - allow SID and account name to be used interchangeably in various
8# functions
9# TBD - ditto for LUID v/s privilege names
10
11namespace eval twapi {
12    # Map privilege level mnemonics to priv level
13    array set priv_level_map {guest 0 user 1 admin 2}
14
15    # TBD - the following are not used, enhancements needed ?
16    # OBJECT_INHERIT_ACE                0x1
17    # CONTAINER_INHERIT_ACE             0x2
18    # NO_PROPAGATE_INHERIT_ACE          0x4
19    # INHERIT_ONLY_ACE                  0x8
20    # INHERITED_ACE                     0x10
21    # VALID_INHERIT_FLAGS               0x1F
22
23    # Cache of privilege names to LUID's
24    variable _privilege_to_luid_map
25    set _privilege_to_luid_map {}
26    variable _luid_to_privilege_map {}
27
28}
29
30
31# Returns token for a process
32proc twapi::open_process_token {args} {
33    array set opts [parseargs args {
34        pid.int
35        hprocess.arg
36        {access.arg token_query}
37    } -maxleftover 0]
38
39    set access [_access_rights_to_mask $opts(access)]
40
41    # Get a handle for the process
42    if {[info exists opts(hprocess)]} {
43        if {[info exists opts(pid)]} {
44            error "Options -pid and -hprocess cannot be used together."
45        }
46        set ph $opts(hprocess)
47    } elseif {[info exists opts(pid)]} {
48        set ph [get_process_handle $opts(pid)]
49    } else {
50        variable my_process_handle
51        set ph $my_process_handle
52    }
53    trap {
54        # Get a token for the process
55        set ptok [OpenProcessToken $ph $access]
56    } finally {
57        # Close handle only if we did an OpenProcess
58        if {[info exists opts(pid)]} {
59            CloseHandle $ph
60        }
61    }
62
63    return $ptok
64}
65
66# Returns token for a process
67proc twapi::open_thread_token {args} {
68    array set opts [parseargs args {
69        tid.int
70        hthread.arg
71        {access.arg token_query}
72        {self.bool  false}
73    } -maxleftover 0]
74
75    set access [_access_rights_to_mask $opts(access)]
76
77    # Get a handle for the thread
78    if {[info exists opts(hthread)]} {
79        if {[info exists opts(tid)]} {
80            error "Options -tid and -hthread cannot be used together."
81        }
82        set th $opts(hthread)
83    } elseif {[info exists opts(tid)]} {
84        set th [get_thread_handle $opts(tid)]
85    } else {
86        set th [GetCurrentThread]
87    }
88
89    trap {
90        # Get a token for the thread
91        set tok [OpenThreadToken $th $access $opts(self)]
92    } finally {
93        # Close handle only if we did an OpenProcess
94        if {[info exists opts(tid)]} {
95            CloseHandle $th
96        }
97    }
98
99    return $tok
100}
101
102proc twapi::close_token {tok} {
103    CloseHandle $tok
104}
105
106# TBD - document and test
107proc twapi::duplicate_token {tok args} {
108    parseargs args {
109        access.arg
110        {inherit.bool 0}
111        {secd.arg ""}
112        {impersonationlevel.sym impersonation {anonymous 0 identification 1 impersonation 2 delegation 3}}
113        {type.sym primary {primary 1 impersonation 2}}
114    } -maxleftover 0 -setvars
115
116    if {[info exists access]} {
117        set access [_access_rights_to_mask $access]
118    } else {
119        # If no desired access is indicated, we want the same access as
120        # the original handle
121        set access 0
122    }
123
124    return [DuplicateTokenEx $tok $access \
125                [_make_secattr $secd $inherit] \
126                $impersonationlevel $type]
127}
128
129proc twapi::get_token_info {tok args} {
130    array set opts [parseargs args {
131        defaultdacl
132        disabledprivileges
133        elevation
134        enabledprivileges
135        groupattrs
136        groups
137        integrity
138        integritylabel
139        linkedtoken
140        logonsession
141        logonsessionsid
142        origin
143        primarygroup
144        primarygroupsid
145        privileges
146        restrictedgroupattrs
147        restrictedgroups
148        tssession
149        usersid
150        virtualized
151    } -maxleftover 0]
152
153    # Do explicit check so we return error if no args specified
154    # and $tok is invalid
155    if {![pointer? $tok]} {
156        error "Invalid token handle '$tok'"
157    }
158
159    # TBD - add an -ignorerrors option
160
161    set result [dict create]
162    trap {
163        if {$opts(privileges) || $opts(disabledprivileges) || $opts(enabledprivileges)} {
164            lassign [GetTokenInformation $tok 13] gtigroups gtirestrictedgroups privs gtilogonsession
165            set privs [_map_luids_and_attrs_to_privileges $privs]
166            if {$opts(privileges)} {
167                lappend result -privileges $privs
168            }
169            if {$opts(enabledprivileges)} {
170                lappend result -enabledprivileges [lindex $privs 0]
171            }
172            if {$opts(disabledprivileges)} {
173                lappend result -disabledprivileges [lindex $privs 1]
174            }
175        }
176        if {$opts(defaultdacl)} {
177            lappend result -defaultdacl [get_token_default_dacl $tok]
178        }
179        if {$opts(origin)} {
180            lappend result -origin [get_token_origin $tok]
181        }
182        if {$opts(linkedtoken)} {
183            lappend result -linkedtoken [get_token_linked_token $tok]
184        }
185        if {$opts(elevation)} {
186            lappend result -elevation [get_token_elevation $tok]
187        }
188        if {$opts(integrity)} {
189            lappend result -integrity [get_token_integrity $tok]
190        }
191        if {$opts(integritylabel)} {
192            lappend result -integritylabel [get_token_integrity $tok -label]
193        }
194        if {$opts(virtualized)} {
195            lappend result -virtualized [get_token_virtualization $tok]
196        }
197        if {$opts(tssession)} {
198            lappend result -tssession [get_token_tssession $tok]
199        }
200        if {$opts(usersid)} {
201            # First element of groups is user sid
202            if {[info exists gtigroups]} {
203                lappend result -usersid [lindex $gtigroups 0 0 0]
204            } else {
205                lappend result -usersid [get_token_user $tok]
206            }
207        }
208        if {$opts(groups)} {
209            if {[info exists gtigroups]} {
210                set items {}
211                # First element of groups is user sid, skip it
212                foreach item [lrange $gtigroups 1 end] {
213                    lappend items [lookup_account_sid [lindex $item 0]]
214                }
215                lappend result -groups $items
216            } else {
217                lappend result -groups [get_token_groups $tok -name]
218            }
219        }
220        if {[min_os_version 6] && $opts(logonsessionsid)} {
221            # Only possible on Vista+
222	    lappend result -logonsessionsid [lindex [GetTokenInformation $tok 28] 0 0]
223            set opts(logonsessionsid) 0; # So we don't try second method below
224        }
225        if {$opts(groupattrs) || $opts(logonsessionsid)} {
226            if {[info exists gtigroups]} {
227                set items {}
228                # First element of groups is user sid, skip it
229                foreach item [lrange $gtigroups 1 end] {
230                    set gattrs [map_token_group_attr [lindex $item 1]]
231                    if {$opts(groupattrs)} {
232                        lappend items [lindex $item 0] $gattrs
233                    }
234                    if {$opts(logonsessionsid) && "logon_id" in $gattrs} {
235                        set logonsessionsid [lindex $item 0]
236                    }
237                }
238                if {$opts(groupattrs)} {
239                    lappend result -groupattrs $items
240                }
241            } else {
242                set groupattrs [get_token_groups_and_attrs $tok]
243                if {$opts(logonsessionsid)} {
244                    foreach {sid gattrs} $groupattrs {
245                        if {"logon_id" in $gattrs} {
246                            set logonsessionsid $sid
247                            break
248                        }
249                    }
250                }
251                if {$opts(groupattrs)} {
252                    lappend result -groupattrs $groupattrs
253                }
254            }
255            if {$opts(logonsessionsid)} {
256                if {[info exists logonsessionsid]} {
257                    lappend result -logonsessionsid $logonsessionsid
258                } else {
259                    error "No logon session id found in token"
260                }
261            }
262        }
263        if {$opts(restrictedgroups)} {
264            if {![info exists gtirestrictedgroups]} {
265                set gtirestrictedgroups [get_token_restricted_groups_and_attrs $tok]
266            }
267            set items {}
268            foreach item $gtirestrictedgroups {
269                lappend items [lookup_account_sid [lindex $item 0]]
270            }
271            lappend result -restrictedgroups $items
272        }
273        if {$opts(restrictedgroupattrs)} {
274            if {[info exists gtirestrictedgroups]} {
275                set items {}
276                foreach item $gtirestrictedgroups {
277                    lappend items [lindex $item 0] [map_token_group_attr [lindex $item 1]]
278                }
279                lappend result -restrictedgroupattrs $items
280            } else {
281                lappend result -restrictedgroupattrs [get_token_restricted_groups_and_attrs $tok]
282            }
283        }
284        if {$opts(primarygroupsid)} {
285            lappend result -primarygroupsid [get_token_primary_group $tok]
286        }
287        if {$opts(primarygroup)} {
288            lappend result -primarygroup [get_token_primary_group $tok -name]
289        }
290        if {$opts(logonsession)} {
291            if {[info exists gtilogonsession]} {
292                lappend result -logonsession $gtilogonsession
293            } else {
294                array set stats [get_token_statistics $tok]
295                lappend result -logonsession $stats(authluid)
296            }
297        }
298    }
299
300    return $result
301}
302
303proc twapi::get_token_tssession {tok} {
304    return [GetTokenInformation $tok 12]
305}
306
307# TBD - document and test
308proc twapi::set_token_tssession {tok tssession} {
309    Twapi_SetTokenSessionId $tok $tssession
310    return
311}
312
313# Procs that differ between Vista and prior versions
314if {[twapi::min_os_version 6]} {
315    proc twapi::get_token_elevation {tok} {
316        set elevation [GetTokenInformation $tok 18]; #TokenElevationType
317        switch -exact -- $elevation {
318            1 { set elevation default }
319            2 { set elevation full }
320            3 { set elevation limited }
321        }
322        return $elevation
323    }
324
325    proc twapi::get_token_virtualization {tok} {
326        return [GetTokenInformation $tok 24]; # TokenVirtualizationEnabled
327    }
328
329    proc twapi::set_token_virtualization {tok enabled} {
330        # tok must have TOKEN_ADJUST_DEFAULT access
331        Twapi_SetTokenVirtualizationEnabled $tok [expr {$enabled ? 1 : 0}]
332    }
333
334    # Get the integrity level associated with a token
335    proc twapi::get_token_integrity {tok args} {
336        # TokenIntegrityLevel -> 25
337        lassign [GetTokenInformation $tok 25]  integrity attrs
338        if {$attrs != 96} {
339            # TBD - is this ok?
340        }
341        return [_sid_to_integrity $integrity {*}$args]
342    }
343
344    # Get the integrity level associated with a token
345    proc twapi::set_token_integrity {tok integrity} {
346        # SE_GROUP_INTEGRITY attribute - 0x20
347        Twapi_SetTokenIntegrityLevel $tok [list [_integrity_to_sid $integrity] 0x20]
348    }
349
350    proc twapi::get_token_integrity_policy {tok} {
351        set policy [GetTokenInformation $tok 27]; #TokenMandatoryPolicy
352        set result {}
353        if {$policy & 1} {
354            lappend result no_write_up
355        }
356        if {$policy & 2} {
357            lappend result new_process_min
358        }
359        return $result
360    }
361
362
363    proc twapi::set_token_integrity_policy {tok args} {
364        set policy [_parse_symbolic_bitmask $args {
365            no_write_up     0x1
366            new_process_min 0x2
367        }]
368
369        Twapi_SetTokenMandatoryPolicy $tok $policy
370    }
371} else {
372    # Versions for pre-Vista
373    proc twapi::get_token_elevation {tok} {
374        # Older OS versions have no concept of elevation.
375        return "default"
376    }
377
378    proc twapi::get_token_virtualization {tok} {
379        # Older OS versions have no concept of elevation.
380        return 0
381    }
382
383    proc twapi::set_token_virtualization {tok enabled} {
384        # Older OS versions have no concept of elevation, so only disable
385        # allowed
386        if {$enabled} {
387            error "Virtualization not available on this platform."
388        }
389        return
390    }
391
392    # Get the integrity level associated with a token
393    proc twapi::get_token_integrity {tok args} {
394        # Older OS versions have no concept of elevation.
395        # For future consistency in label mapping, fall through to mapping
396        # below instead of directly returning mapped value
397        set integrity S-1-16-8192
398
399        return [_sid_to_integrity $integrity {*}$args]
400    }
401
402    # Get the integrity level associated with a token
403    proc twapi::set_token_integrity {tok integrity} {
404        # Old platforms have a "default" of medium that cannot be changed.
405        if {[_integrity_to_sid $integrity] ne "S-1-16-8192"} {
406            error "Invalid integrity level value '$integrity' for this platform."
407        }
408        return
409    }
410
411    proc twapi::get_token_integrity_policy {tok} {
412        # Old platforms - no integrity
413        return 0
414    }
415
416    proc twapi::set_token_integrity_policy {tok args} {
417        # Old platforms - no integrity
418        return 0
419    }
420}
421
422proc twapi::well_known_sid {sidname args} {
423    parseargs args {
424        {domainsid.arg {}}
425    } -maxleftover 0 -setvars
426
427    return [CreateWellKnownSid [_map_well_known_sid_name $sidname] $domainsid]
428}
429
430proc twapi::is_well_known_sid {sid sidname} {
431    return [IsWellKnownSid $sid [_map_well_known_sid_name $sidname]]
432}
433
434# Get the user account associated with a token
435proc twapi::get_token_user {tok args} {
436
437    array set opts [parseargs args [list name]]
438    # TokenUser -> 1
439    set user [lindex [GetTokenInformation $tok 1] 0]
440    if {$opts(name)} {
441        set user [lookup_account_sid $user]
442    }
443    return $user
444}
445
446# Get the groups associated with a token
447proc twapi::get_token_groups {tok args} {
448    array set opts [parseargs args [list name] -maxleftover 0]
449
450    set groups [list ]
451    # TokenGroups -> 2
452    foreach group [GetTokenInformation $tok 2] {
453        if {$opts(name)} {
454            lappend groups [lookup_account_sid [lindex $group 0]]
455        } else {
456            lappend groups [lindex $group 0]
457        }
458    }
459
460    return $groups
461}
462
463# Get the groups associated with a token along with their attributes
464# These are returned as a flat list of the form "sid attrlist sid attrlist..."
465# where the attrlist is a list of attributes
466proc twapi::get_token_groups_and_attrs {tok} {
467
468    set sids_and_attrs [list ]
469    # TokenGroups -> 2
470    foreach {group} [GetTokenInformation $tok 2] {
471        lappend sids_and_attrs [lindex $group 0] [map_token_group_attr [lindex $group 1]]
472    }
473
474    return $sids_and_attrs
475}
476
477# Get the groups associated with a token along with their attributes
478# These are returned as a flat list of the form "sid attrlist sid attrlist..."
479# where the attrlist is a list of attributes
480proc twapi::get_token_restricted_groups_and_attrs {tok} {
481    set sids_and_attrs [list ]
482    # TokenRestrictedGroups -> 11
483    foreach {group} [GetTokenInformation $tok 11] {
484        lappend sids_and_attrs [lindex $group 0] [map_token_group_attr [lindex $group 1]]
485    }
486
487    return $sids_and_attrs
488}
489
490
491# Get list of privileges that are currently enabled for the token
492# If -all is specified, returns a list {enabled_list disabled_list}
493proc twapi::get_token_privileges {tok args} {
494
495    set all [expr {[lsearch -exact $args -all] >= 0}]
496    # TokenPrivileges -> 3
497    set privs [_map_luids_and_attrs_to_privileges [GetTokenInformation $tok 3]]
498    if {$all} {
499        return $privs
500    } else {
501        return [lindex $privs 0]
502    }
503}
504
505# Return true if the token has the given privilege
506proc twapi::check_enabled_privileges {tok privlist args} {
507    set all_required [expr {[lsearch -exact $args "-any"] < 0}]
508
509    set luid_attr_list [list ]
510    foreach priv $privlist {
511        lappend luid_attr_list [list [map_privilege_to_luid $priv] 0]
512    }
513    return [Twapi_PrivilegeCheck $tok $luid_attr_list $all_required]
514}
515
516
517# Enable specified privileges. Returns "" if the given privileges were
518# already enabled, else returns the privileges that were modified
519proc twapi::enable_privileges {privlist} {
520    variable my_process_handle
521
522    # Get our process token
523    set tok [OpenProcessToken $my_process_handle 0x28]; # QUERY + ADJUST_PRIVS
524    trap {
525        return [enable_token_privileges $tok $privlist]
526    } finally {
527        close_token $tok
528    }
529}
530
531
532# Disable specified privileges. Returns "" if the given privileges were
533# already enabled, else returns the privileges that were modified
534proc twapi::disable_privileges {privlist} {
535    variable my_process_handle
536
537    # Get our process token
538    set tok [OpenProcessToken $my_process_handle 0x28]; # QUERY + ADJUST_PRIVS
539    trap {
540        return [disable_token_privileges $tok $privlist]
541    } finally {
542        close_token $tok
543    }
544}
545
546
547# Execute the given script with the specified privileges.
548# After the script completes, the original privileges are restored
549proc twapi::eval_with_privileges {script privs args} {
550    array set opts [parseargs args {besteffort} -maxleftover 0]
551
552    if {[catch {enable_privileges $privs} privs_to_disable]} {
553        if {! $opts(besteffort)} {
554            return -code error -errorinfo $::errorInfo \
555                -errorcode $::errorCode $privs_to_disable
556        }
557        set privs_to_disable [list ]
558    }
559
560    set code [catch {uplevel $script} result]
561    switch $code {
562        0 {
563            disable_privileges $privs_to_disable
564            return $result
565        }
566        1 {
567            # Save error info before calling disable_privileges
568            set erinfo $::errorInfo
569            set ercode $::errorCode
570            disable_privileges $privs_to_disable
571            return -code error -errorinfo $::errorInfo \
572                -errorcode $::errorCode $result
573        }
574        default {
575            disable_privileges $privs_to_disable
576            return -code $code $result
577        }
578    }
579}
580
581
582# Get the privilege associated with a token and their attributes
583proc twapi::get_token_privileges_and_attrs {tok} {
584    set privs_and_attrs [list ]
585    # TokenPrivileges -> 3
586    foreach priv [GetTokenInformation $tok 3] {
587        lassign $priv luid attr
588        lappend privs_and_attrs [map_luid_to_privilege $luid -mapunknown] \
589            [map_token_privilege_attr $attr]
590    }
591
592    return $privs_and_attrs
593
594}
595
596
597# Get the sid that will be used as the owner for objects created using this
598# token. Returns name instead of sid if -name options specified
599proc twapi::get_token_owner {tok args} {
600    # TokenOwner -> 4
601    return [ _get_token_sid_field $tok 4 $args]
602}
603
604
605# Get the sid that will be used as the primary group for objects created using
606# this token. Returns name instead of sid if -name options specified
607proc twapi::get_token_primary_group {tok args} {
608    # TokenPrimaryGroup -> 5
609    return [ _get_token_sid_field $tok 5 $args]
610}
611
612proc twapi::get_token_default_dacl {tok} {
613    # TokenDefaultDacl -> 6
614    return [GetTokenInformation $tok 6]
615}
616
617proc twapi::get_token_origin {tok} {
618    # TokenOrigin -> 17
619    return [GetTokenInformation $tok 17]
620}
621
622# Return the source of an access token
623proc twapi::get_token_source {tok} {
624    return [GetTokenInformation $tok 7]; # TokenSource
625}
626
627
628# Return the token type of an access token
629proc twapi::get_token_type {tok} {
630    # TokenType -> 8
631    set type [GetTokenInformation $tok 8]
632    if {$type == 1} {
633        return "primary"
634    } elseif {$type == 2} {
635        return "impersonation"
636    } else {
637        return $type
638    }
639}
640
641# Return the token type of an access token
642proc twapi::get_token_impersonation_level {tok} {
643    # TokenImpersonationLevel -> 9
644    return [_map_impersonation_level [GetTokenInformation $tok 9]]
645}
646
647# Return the linked token when a token is filtered
648proc twapi::get_token_linked_token {tok} {
649    # TokenLinkedToken -> 19
650    return [GetTokenInformation $tok 19]
651}
652
653# Return token statistics
654proc twapi::get_token_statistics {tok} {
655    array set stats {}
656    set labels {luid authluid expiration type impersonationlevel
657        dynamiccharged dynamicavailable groupcount
658        privilegecount modificationluid}
659    # TokenStatistics -> 10
660    set statinfo [GetTokenInformation $tok 10]
661    foreach label $labels val $statinfo {
662        set stats($label) $val
663    }
664    set stats(type) [expr {$stats(type) == 1 ? "primary" : "impersonation"}]
665    set stats(impersonationlevel) [_map_impersonation_level $stats(impersonationlevel)]
666
667    return [array get stats]
668}
669
670
671# Enable the privilege state of a token. Generates an error if
672# the specified privileges do not exist in the token (either
673# disabled or enabled), or cannot be adjusted
674proc twapi::enable_token_privileges {tok privs} {
675    set luid_attrs [list]
676    foreach priv $privs {
677        # SE_PRIVILEGE_ENABLED -> 2
678        lappend luid_attrs [list [map_privilege_to_luid $priv] 2]
679    }
680
681    set privs [list ]
682    foreach {item} [Twapi_AdjustTokenPrivileges $tok 0 $luid_attrs] {
683        lappend privs [map_luid_to_privilege [lindex $item 0] -mapunknown]
684    }
685    return $privs
686
687
688
689}
690
691# Disable the privilege state of a token. Generates an error if
692# the specified privileges do not exist in the token (either
693# disabled or enabled), or cannot be adjusted
694proc twapi::disable_token_privileges {tok privs} {
695    set luid_attrs [list]
696    foreach priv $privs {
697        lappend luid_attrs [list [map_privilege_to_luid $priv] 0]
698    }
699
700    set privs [list ]
701    foreach {item} [Twapi_AdjustTokenPrivileges $tok 0 $luid_attrs] {
702        lappend privs [map_luid_to_privilege [lindex $item 0] -mapunknown]
703    }
704    return $privs
705}
706
707# Disable all privs in a token
708proc twapi::disable_all_token_privileges {tok} {
709    set privs [list ]
710    foreach {item} [Twapi_AdjustTokenPrivileges $tok 1 [list ]] {
711        lappend privs [map_luid_to_privilege [lindex $item 0] -mapunknown]
712    }
713    return $privs
714}
715
716
717# Map a privilege given as a LUID
718proc twapi::map_luid_to_privilege {luid args} {
719    variable _luid_to_privilege_map
720
721    array set opts [parseargs args [list system.arg mapunknown] -nulldefault]
722
723    if {[dict exists $_luid_to_privilege_map $opts(system) $luid]} {
724        return [dict get $_luid_to_privilege_map $opts(system) $luid]
725    }
726
727    # luid may in fact be a privilege name. Check for this
728    if {[is_valid_luid_syntax $luid]} {
729        trap {
730            set name [LookupPrivilegeName $opts(system) $luid]
731            dict set _luid_to_privilege_map $opts(system) $luid $name
732        } onerror {TWAPI_WIN32 1313} {
733            if {! $opts(mapunknown)} {
734                rethrow
735            }
736            set name "Privilege-$luid"
737            # Do not put in cache as privilege name might change?
738        }
739    } else {
740        # Not a valid LUID syntax. Check if it's a privilege name
741        if {[catch {map_privilege_to_luid $luid -system $opts(system)}]} {
742            error "Invalid LUID '$luid'"
743        }
744        return $luid;                   # $luid is itself a priv name
745    }
746
747    return $name
748}
749
750
751# Map a privilege to a LUID
752proc twapi::map_privilege_to_luid {priv args} {
753    variable _privilege_to_luid_map
754
755    array set opts [parseargs args [list system.arg] -nulldefault]
756
757    if {[dict exists $_privilege_to_luid_map $opts(system) $priv]} {
758        return [dict get $_privilege_to_luid_map $opts(system) $priv]
759    }
760
761    # First check for privilege names we might have generated
762    if {[string match "Privilege-*" $priv]} {
763        set priv [string range $priv 10 end]
764    }
765
766    # If already a LUID format, return as is, else look it up
767    if {[is_valid_luid_syntax $priv]} {
768        return $priv
769    }
770
771    set luid [LookupPrivilegeValue $opts(system) $priv]
772    # This is an expensive call so stash it unless cache too big
773    if {[dict size $_privilege_to_luid_map] < 100} {
774        dict set _privilege_to_luid_map $opts(system) $priv $luid
775    }
776
777    return $luid
778}
779
780
781# Return 1/0 if in LUID format
782proc twapi::is_valid_luid_syntax {luid} {
783    return [regexp {^[[:xdigit:]]{8}-[[:xdigit:]]{8}$} $luid]
784}
785
786
787################################################################
788# Functions related to ACE's and ACL's
789
790# Create a new ACE
791proc twapi::new_ace {type account rights args} {
792    array set opts [parseargs args {
793        {self.bool 1}
794        {recursecontainers.bool 0 2}
795        {recurseobjects.bool 0 1}
796        {recurseonelevelonly.bool 0 4}
797        {auditsuccess.bool 1 0x40}
798        {auditfailure.bool 1 0x80}
799    }]
800
801    set sid [map_account_to_sid $account]
802
803    set access_mask [_access_rights_to_mask $rights]
804
805    switch -exact -- $type {
806        mandatory_label -
807        allow -
808        deny  -
809        audit {
810            set typecode [_ace_type_symbol_to_code $type]
811        }
812        default {
813            error "Invalid or unsupported ACE type '$type'"
814        }
815    }
816
817    set inherit_flags [expr {$opts(recursecontainers) | $opts(recurseobjects) |
818                             $opts(recurseonelevelonly)}]
819    if {! $opts(self)} {
820        incr inherit_flags 8; #INHERIT_ONLY_ACE
821    }
822
823    if {$type eq "audit"} {
824        set inherit_flags [expr {$inherit_flags | $opts(auditsuccess) | $opts(auditfailure)}]
825    }
826
827    return [list $typecode $inherit_flags $access_mask $sid]
828}
829
830# Get the ace type (allow, deny etc.)
831proc twapi::get_ace_type {ace} {
832    return [_ace_type_code_to_symbol [lindex $ace 0]]
833}
834
835
836# Set the ace type (allow, deny etc.)
837proc twapi::set_ace_type {ace type} {
838    return [lreplace $ace 0 0 [_ace_type_symbol_to_code $type]]
839}
840
841# Get the access rights in an ACE
842proc twapi::get_ace_rights {ace args} {
843    array set opts [parseargs args {
844        {type.arg ""}
845        resourcetype.arg
846        raw
847    } -maxleftover 0]
848
849    if {$opts(raw)} {
850        return [format 0x%x [lindex $ace 2]]
851    }
852
853    if {[lindex $ace 0] == 0x11} {
854        # MANDATORY_LABEL -> 0x11
855        # Resource type is immaterial
856        return [_access_mask_to_rights [lindex $ace 2] mandatory_label]
857    }
858
859    # Backward compatibility - in 2.x -type was documented instead
860    # of -resourcetype
861    if {[info exists opts(resourcetype)]} {
862        return [_access_mask_to_rights [lindex $ace 2] $opts(resourcetype)]
863    } else {
864        return [_access_mask_to_rights [lindex $ace 2] $opts(type)]
865    }
866}
867
868# Set the access rights in an ACE
869proc twapi::set_ace_rights {ace rights} {
870    return [lreplace $ace 2 2 [_access_rights_to_mask $rights]]
871}
872
873
874# Get the ACE sid
875proc twapi::get_ace_sid {ace} {
876    return [lindex $ace 3]
877}
878
879# Set the ACE sid
880proc twapi::set_ace_sid {ace account} {
881    return [lreplace $ace 3 3 [map_account_to_sid $account]]
882}
883
884
885# Get audit flags - TBD document and test
886proc twapi::get_ace_audit {ace} {
887    set audit {}
888    set mask [lindex $ace 1]
889    if {$mask & 0x40} {
890        lappend audit "success"
891    }
892    if {$mask & 0x80} {
893        lappend audit "failure"
894    }
895    return $audit
896}
897
898# Get the inheritance options
899proc twapi::get_ace_inheritance {ace} {
900
901    set inherit_opts [list ]
902    set inherit_mask [lindex $ace 1]
903
904    lappend inherit_opts -self \
905        [expr {($inherit_mask & 8) == 0}]
906    lappend inherit_opts -recursecontainers \
907        [expr {($inherit_mask & 2) != 0}]
908    lappend inherit_opts -recurseobjects \
909        [expr {($inherit_mask & 1) != 0}]
910    lappend inherit_opts -recurseonelevelonly \
911        [expr {($inherit_mask & 4) != 0}]
912    lappend inherit_opts -inherited \
913        [expr {($inherit_mask & 16) != 0}]
914
915    return $inherit_opts
916}
917
918# Set the inheritance options. Unspecified options are not set
919proc twapi::set_ace_inheritance {ace args} {
920
921    array set opts [parseargs args {
922        self.bool
923        recursecontainers.bool
924        recurseobjects.bool
925        recurseonelevelonly.bool
926    }]
927
928    set inherit_flags [lindex $ace 1]
929    if {[info exists opts(self)]} {
930        if {$opts(self)} {
931            resetbits inherit_flags 0x8; #INHERIT_ONLY_ACE -> 0x8
932        } else {
933            setbits   inherit_flags 0x8; #INHERIT_ONLY_ACE -> 0x8
934        }
935    }
936
937    foreach {
938        opt                 mask
939    } {
940        recursecontainers   2
941        recurseobjects      1
942        recurseonelevelonly 4
943    } {
944        if {[info exists opts($opt)]} {
945            if {$opts($opt)} {
946                setbits inherit_flags $mask
947            } else {
948                resetbits inherit_flags $mask
949            }
950        }
951    }
952
953    return [lreplace $ace 1 1 $inherit_flags]
954}
955
956
957# Sort ACE's in the standard recommended Win2K order
958proc twapi::sort_aces {aces} {
959
960    _init_ace_type_symbol_to_code_map
961
962    foreach type [array names twapi::_ace_type_symbol_to_code_map] {
963        set direct_aces($type) [list ]
964        set inherited_aces($type) [list ]
965    }
966
967    # Sort order is as follows: all direct (non-inherited) ACEs come
968    # before all inherited ACEs. Within these groups, the order should be
969    # access denied ACEs, access denied ACEs for objects/properties,
970    # access allowed ACEs, access allowed ACEs for objects/properties,
971    # TBD - check this ordering against http://msdn.microsoft.com/en-us/library/windows/desktop/aa379298%28v=vs.85%29.aspx
972    foreach ace $aces {
973        set type [get_ace_type $ace]
974        # INHERITED_ACE -> 0x10
975        if {[lindex $ace 1] & 0x10} {
976            lappend inherited_aces($type) $ace
977        } else {
978            lappend direct_aces($type) $ace
979        }
980    }
981
982    # TBD - check this order ACE's, especially audit and mandatory label
983    return [concat \
984                $direct_aces(deny) \
985                $direct_aces(deny_object) \
986                $direct_aces(deny_callback) \
987                $direct_aces(deny_callback_object) \
988                $direct_aces(allow) \
989                $direct_aces(allow_object) \
990                $direct_aces(allow_compound) \
991                $direct_aces(allow_callback) \
992                $direct_aces(allow_callback_object) \
993                $direct_aces(audit) \
994                $direct_aces(audit_object) \
995                $direct_aces(audit_callback) \
996                $direct_aces(audit_callback_object) \
997                $direct_aces(mandatory_label) \
998                $direct_aces(alarm) \
999                $direct_aces(alarm_object) \
1000                $direct_aces(alarm_callback) \
1001                $direct_aces(alarm_callback_object) \
1002                $inherited_aces(deny) \
1003                $inherited_aces(deny_object) \
1004                $inherited_aces(deny_callback) \
1005                $inherited_aces(deny_callback_object) \
1006                $inherited_aces(allow) \
1007                $inherited_aces(allow_object) \
1008                $inherited_aces(allow_compound) \
1009                $inherited_aces(allow_callback) \
1010                $inherited_aces(allow_callback_object) \
1011                $inherited_aces(audit) \
1012                $inherited_aces(audit_object) \
1013                $inherited_aces(audit_callback) \
1014                $inherited_aces(audit_callback_object) \
1015                $inherited_aces(mandatory_label) \
1016                $inherited_aces(alarm) \
1017                $inherited_aces(alarm_object) \
1018                $inherited_aces(alarm_callback) \
1019                $inherited_aces(alarm_callback_object)]
1020}
1021
1022# Pretty print an ACL
1023proc twapi::get_acl_text {acl args} {
1024    array set opts [parseargs args {
1025        {resourcetype.arg raw}
1026        {offset.arg ""}
1027    } -maxleftover 0]
1028
1029    set count 0
1030    set result "$opts(offset)Rev: [get_acl_rev $acl]\n"
1031    foreach ace [get_acl_aces $acl] {
1032        append result "$opts(offset)ACE #[incr count]\n"
1033        append result [get_ace_text $ace -offset "$opts(offset)  " -resourcetype $opts(resourcetype)]
1034    }
1035    return $result
1036}
1037
1038# Pretty print an ACE
1039proc twapi::get_ace_text {ace args} {
1040    array set opts [parseargs args {
1041        {resourcetype.arg raw}
1042        {offset.arg ""}
1043    } -maxleftover 0]
1044
1045    if {$ace eq "null"} {
1046        return "Null"
1047    }
1048
1049    set offset $opts(offset)
1050    array set bools {0 No 1 Yes}
1051    array set inherit_flags [get_ace_inheritance $ace]
1052    append inherit_text "${offset}Inherited: $bools($inherit_flags(-inherited))\n"
1053    append inherit_text "${offset}Include self: $bools($inherit_flags(-self))\n"
1054    append inherit_text "${offset}Recurse containers: $bools($inherit_flags(-recursecontainers))\n"
1055    append inherit_text "${offset}Recurse objects: $bools($inherit_flags(-recurseobjects))\n"
1056    append inherit_text "${offset}Recurse single level only: $bools($inherit_flags(-recurseonelevelonly))\n"
1057
1058    set rights [get_ace_rights $ace -type $opts(resourcetype)]
1059    if {[lsearch -glob $rights *_all_access] >= 0} {
1060        set rights "All"
1061    } else {
1062        set rights [join $rights ", "]
1063    }
1064
1065    set acetype [get_ace_type $ace]
1066    append result "${offset}Type: [string totitle $acetype]\n"
1067    set user [get_ace_sid $ace]
1068    catch {append user " ([map_account_to_name [get_ace_sid $ace]])"}
1069    append result "${offset}User: $user\n"
1070    append result "${offset}Rights: $rights\n"
1071    if {$acetype eq "audit"} {
1072        append result "${offset}Audit conditions: [join [get_ace_audit $ace] {, }]\n"
1073    }
1074    append result $inherit_text
1075
1076    return $result
1077}
1078
1079# Create a new ACL
1080proc twapi::new_acl {{aces ""}} {
1081    # NOTE: we ALWAYS set aclrev to 2. This may not be correct for the
1082    # supplied ACEs but that's ok. The C level code calculates the correct
1083    # acl rev level and overwrites anyways.
1084    return [list 2 $aces]
1085}
1086
1087# Creates an ACL that gives the specified rights to specified trustees
1088proc twapi::new_restricted_dacl {accounts rights args} {
1089    set access_mask [_access_rights_to_mask $rights]
1090
1091    set aces {}
1092    foreach account $accounts {
1093        lappend aces [new_ace allow $account $access_mask {*}$args]
1094    }
1095
1096    return [new_acl $aces]
1097
1098}
1099
1100# Return the list of ACE's in an ACL
1101proc twapi::get_acl_aces {acl} {
1102    return [lindex $acl 1]
1103}
1104
1105# Set the ACE's in an ACL
1106proc twapi::set_acl_aces {acl aces} {
1107    # Note, we call new_acl since when ACEs change, the rev may also change
1108    return [new_acl $aces]
1109}
1110
1111# Append to the ACE's in an ACL
1112proc twapi::append_acl_aces {acl aces} {
1113    return [set_acl_aces $acl [concat [get_acl_aces $acl] $aces]]
1114}
1115
1116# Prepend to the ACE's in an ACL
1117proc twapi::prepend_acl_aces {acl aces} {
1118    return [set_acl_aces $acl [concat $aces [get_acl_aces $acl]]]
1119}
1120
1121# Arrange the ACE's in an ACL in a standard order
1122proc twapi::sort_acl_aces {acl} {
1123    return [set_acl_aces $acl [sort_aces [get_acl_aces $acl]]]
1124}
1125
1126# Return the ACL revision of an ACL
1127proc twapi::get_acl_rev {acl} {
1128    return [lindex $acl 0]
1129}
1130
1131
1132# Create a new security descriptor
1133proc twapi::new_security_descriptor {args} {
1134    array set opts [parseargs args {
1135        owner.arg
1136        group.arg
1137        dacl.arg
1138        sacl.arg
1139    } -maxleftover 0]
1140
1141    set secd [Twapi_InitializeSecurityDescriptor]
1142
1143    foreach field {owner group dacl sacl} {
1144        if {[info exists opts($field)]} {
1145            set secd [set_security_descriptor_$field $secd $opts($field)]
1146        }
1147    }
1148
1149    return $secd
1150}
1151
1152# Return the control bits in a security descriptor
1153# TBD - update for new Windows versions
1154proc twapi::get_security_descriptor_control {secd} {
1155    if {[_null_secd $secd]} {
1156        error "Attempt to get control field from NULL security descriptor."
1157    }
1158
1159    set control [lindex $secd 0]
1160
1161    set retval [list ]
1162    if {$control & 0x0001} {
1163        lappend retval owner_defaulted
1164    }
1165    if {$control & 0x0002} {
1166        lappend retval group_defaulted
1167    }
1168    if {$control & 0x0004} {
1169        lappend retval dacl_present
1170    }
1171    if {$control & 0x0008} {
1172        lappend retval dacl_defaulted
1173    }
1174    if {$control & 0x0010} {
1175        lappend retval sacl_present
1176    }
1177    if {$control & 0x0020} {
1178        lappend retval sacl_defaulted
1179    }
1180    if {$control & 0x0100} {
1181        # Not documented because should not actually appear when reading a secd
1182        lappend retval dacl_auto_inherit_req
1183    }
1184    if {$control & 0x0200} {
1185        # Not documented because should not actually appear when reading a secd
1186        lappend retval sacl_auto_inherit_req
1187    }
1188    if {$control & 0x0400} {
1189        lappend retval dacl_auto_inherited
1190    }
1191    if {$control & 0x0800} {
1192        lappend retval sacl_auto_inherited
1193    }
1194    if {$control & 0x1000} {
1195        lappend retval dacl_protected
1196    }
1197    if {$control & 0x2000} {
1198        lappend retval sacl_protected
1199    }
1200    if {$control & 0x4000} {
1201        lappend retval rm_control_valid
1202    }
1203    if {$control & 0x8000} {
1204        lappend retval self_relative
1205    }
1206    return $retval
1207}
1208
1209# Return the owner in a security descriptor
1210proc twapi::get_security_descriptor_owner {secd} {
1211    if {[_null_secd $secd]} {
1212        win32_error 87 "Attempt to get owner field from NULL security descriptor."
1213    }
1214    return [lindex $secd 1]
1215}
1216
1217# Set the owner in a security descriptor
1218proc twapi::set_security_descriptor_owner {secd account} {
1219    if {[_null_secd $secd]} {
1220        set secd [new_security_descriptor]
1221    }
1222    set sid [map_account_to_sid $account]
1223    return [lreplace $secd 1 1 $sid]
1224}
1225
1226# Return the group in a security descriptor
1227proc twapi::get_security_descriptor_group {secd} {
1228    if {[_null_secd $secd]} {
1229        win32_error 87 "Attempt to get group field from NULL security descriptor."
1230    }
1231    return [lindex $secd 2]
1232}
1233
1234# Set the group in a security descriptor
1235proc twapi::set_security_descriptor_group {secd account} {
1236    if {[_null_secd $secd]} {
1237        set secd [new_security_descriptor]
1238    }
1239    set sid [map_account_to_sid $account]
1240    return [lreplace $secd 2 2 $sid]
1241}
1242
1243# Return the DACL in a security descriptor
1244proc twapi::get_security_descriptor_dacl {secd} {
1245    if {[_null_secd $secd]} {
1246        win32_error 87 "Attempt to get DACL field from NULL security descriptor."
1247    }
1248    return [lindex $secd 3]
1249}
1250
1251# Set the dacl in a security descriptor
1252proc twapi::set_security_descriptor_dacl {secd acl} {
1253    if {[_null_secd $secd]} {
1254        set secd [new_security_descriptor]
1255    }
1256    return [lreplace $secd 3 3 $acl]
1257}
1258
1259# Return the SACL in a security descriptor
1260proc twapi::get_security_descriptor_sacl {secd} {
1261    if {[_null_secd $secd]} {
1262        win32_error 87 "Attempt to get SACL field from NULL security descriptor."
1263    }
1264    return [lindex $secd 4]
1265}
1266
1267# Set the sacl in a security descriptor
1268proc twapi::set_security_descriptor_sacl {secd acl} {
1269    if {[_null_secd $secd]} {
1270        set secd [new_security_descriptor]
1271    }
1272    return [lreplace $secd 4 4 $acl]
1273}
1274
1275# Get the specified security information for the given object
1276proc twapi::get_resource_security_descriptor {restype name args} {
1277
1278    # -mandatory_label field is not documented. Should we ? TBD
1279    array set opts [parseargs args {
1280        owner
1281        group
1282        dacl
1283        sacl
1284        mandatory_label
1285        all
1286        handle
1287    }]
1288
1289    set wanted 0
1290
1291    # OWNER_SECURITY_INFORMATION 1
1292    # GROUP_SECURITY_INFORMATION 2
1293    # DACL_SECURITY_INFORMATION  4
1294    # SACL_SECURITY_INFORMATION  8
1295    foreach {field mask} {owner 1 group 2 dacl 4 sacl 8} {
1296        if {$opts($field) || $opts(all)} {
1297            incr wanted $mask;  # Equivalent to OR operation
1298        }
1299    }
1300
1301    # LABEL_SECURITY_INFORMATION 0x10
1302    if {[min_os_version 6]} {
1303        if {$opts(mandatory_label) || $opts(all)} {
1304            incr wanted 16;     # OR with 0x10
1305        }
1306    }
1307
1308    # Note if no options specified, we ask for everything except
1309    # SACL's which require special privileges
1310    if {! $wanted} {
1311        set wanted 0x7
1312        if {[min_os_version 6]} {
1313            incr wanted 0x10
1314        }
1315    }
1316
1317    if {$opts(handle)} {
1318        set restype [_map_resource_symbol_to_type $restype false]
1319        if {$restype == 5} {
1320            # GetSecurityInfo crashes if a handles is passed in for
1321            # SE_LMSHARE (even erroneously). It expects a string name
1322            # even though the prototype says HANDLE. Protect against this.
1323            error "Share resource type (share or 5) cannot be used with -handle option"
1324        }
1325        set secd [GetSecurityInfo \
1326                      [CastToHANDLE $name] \
1327                      $restype \
1328                      $wanted]
1329    } else {
1330        # GetNamedSecurityInfo seems to fail with a overlapped i/o
1331        # in progress error under some conditions. If this happens
1332        # try getting with resource-specific API's if possible.
1333        trap {
1334            set secd [GetNamedSecurityInfo \
1335                          $name \
1336                          [_map_resource_symbol_to_type $restype true] \
1337                          $wanted]
1338        } onerror {} {
1339            # TBD - see what other resource-specific API's there are
1340            if {$restype eq "share"} {
1341                set secd [lindex [get_share_info $name -secd] 1]
1342            } else {
1343                # Throw the same error
1344                rethrow
1345            }
1346        }
1347    }
1348
1349    return $secd
1350}
1351
1352
1353# Set the specified security information for the given object
1354# See http://search.cpan.org/src/TEVERETT/Win32-Security-0.50/README
1355# for a good discussion even though that applies to Perl
1356proc twapi::set_resource_security_descriptor {restype name secd args} {
1357
1358    # PROTECTED_DACL_SECURITY_INFORMATION     0x80000000
1359    # PROTECTED_SACL_SECURITY_INFORMATION     0x40000000
1360    # UNPROTECTED_DACL_SECURITY_INFORMATION   0x20000000
1361    # UNPROTECTED_SACL_SECURITY_INFORMATION   0x10000000
1362    array set opts [parseargs args {
1363        all
1364        handle
1365        owner
1366        group
1367        dacl
1368        sacl
1369        mandatory_label
1370        {protect_dacl   {} 0x80000000}
1371        {unprotect_dacl {} 0x20000000}
1372        {protect_sacl   {} 0x40000000}
1373        {unprotect_sacl {} 0x10000000}
1374    }]
1375
1376
1377    if {![min_os_version 6]} {
1378        if {$opts(mandatory_label)} {
1379            error "Option -mandatory_label not supported by this version of Windows"
1380        }
1381    }
1382
1383    if {$opts(protect_dacl) && $opts(unprotect_dacl)} {
1384        error "Cannot specify both -protect_dacl and -unprotect_dacl."
1385    }
1386
1387    if {$opts(protect_sacl) && $opts(unprotect_sacl)} {
1388        error "Cannot specify both -protect_sacl and -unprotect_sacl."
1389    }
1390
1391    set mask [expr {$opts(protect_dacl) | $opts(unprotect_dacl) |
1392                    $opts(protect_sacl) | $opts(unprotect_sacl)}]
1393
1394    if {$opts(owner) || $opts(all)} {
1395        set opts(owner) [get_security_descriptor_owner $secd]
1396        setbits mask 1; # OWNER_SECURITY_INFORMATION
1397    } else {
1398        set opts(owner) ""
1399    }
1400
1401    if {$opts(group) || $opts(all)} {
1402        set opts(group) [get_security_descriptor_group $secd]
1403        setbits mask 2; # GROUP_SECURITY_INFORMATION
1404    } else {
1405        set opts(group) ""
1406    }
1407
1408    if {$opts(dacl) || $opts(all)} {
1409        set opts(dacl) [get_security_descriptor_dacl $secd]
1410        setbits mask 4; # DACL_SECURITY_INFORMATION
1411    } else {
1412        set opts(dacl) null
1413    }
1414
1415    if {$opts(sacl) || $opts(mandatory_label) || $opts(all)} {
1416        set sacl [get_security_descriptor_sacl $secd]
1417        if {$opts(sacl) || $opts(all)} {
1418            setbits mask 0x8; # SACL_SECURITY_INFORMATION
1419        }
1420        if {[min_os_version 6]} {
1421            if {$opts(mandatory_label) || $opts(all)} {
1422                setbits mask 0x10; # LABEL_SECURITY_INFORMATION
1423            }
1424        }
1425        set opts(sacl) $sacl
1426    } else {
1427        set opts(sacl) null
1428    }
1429
1430    if {$mask == 0} {
1431	error "Must specify at least one of the options -all, -dacl, -sacl, -owner, -group or -mandatory_label"
1432    }
1433
1434    if {$opts(handle)} {
1435        set restype [_map_resource_symbol_to_type $restype false]
1436        if {$restype == 5} {
1437            # GetSecurityInfo crashes if a handles is passed in for
1438            # SE_LMSHARE (even erroneously). It expects a string name
1439            # even though the prototype says HANDLE. Protect against this.
1440            error "Share resource type (share or 5) cannot be used with -handle option"
1441        }
1442
1443        SetSecurityInfo \
1444            [CastToHANDLE $name] \
1445            [_map_resource_symbol_to_type $restype false] \
1446            $mask \
1447            $opts(owner) \
1448            $opts(group) \
1449            $opts(dacl) \
1450            $opts(sacl)
1451    } else {
1452        SetNamedSecurityInfo \
1453            $name \
1454            [_map_resource_symbol_to_type $restype true] \
1455            $mask \
1456            $opts(owner) \
1457            $opts(group) \
1458            $opts(dacl) \
1459            $opts(sacl)
1460    }
1461}
1462
1463# Get integrity level from a security descriptor
1464proc twapi::get_security_descriptor_integrity {secd args} {
1465    if {[min_os_version 6]} {
1466        foreach ace [get_acl_aces [get_security_descriptor_sacl $secd]] {
1467            if {[get_ace_type $ace] eq "mandatory_label"} {
1468                if {! [dict get [get_ace_inheritance $ace] -self]} continue; # Does not apply to itself
1469                set integrity [_sid_to_integrity [get_ace_sid $ace] {*}$args]
1470                set rights [get_ace_rights $ace -resourcetype mandatory_label]
1471                return [list $integrity $rights]
1472            }
1473        }
1474    }
1475    return {}
1476}
1477
1478# Get integrity level for a resource
1479proc twapi::get_resource_integrity {restype name args} {
1480    # Note label and raw options are simply passed on
1481
1482    if {![min_os_version 6]} {
1483        return ""
1484    }
1485    set saved_args $args
1486    array set opts [parseargs args {
1487        label
1488        raw
1489        handle
1490    }]
1491
1492    if {$opts(handle)} {
1493        set secd [get_resource_security_descriptor $restype $name -mandatory_label -handle]
1494    } else {
1495        set secd [get_resource_security_descriptor $restype $name -mandatory_label]
1496    }
1497
1498    return [get_security_descriptor_integrity $secd {*}$saved_args]
1499}
1500
1501
1502proc twapi::set_security_descriptor_integrity {secd integrity rights args} {
1503    # Not clear from docs whether this can
1504    # be done without interfering with SACL fields. Nevertheless
1505    # we provide this proc because we might want to set the
1506    # integrity level on new objects create thru CreateFile etc.
1507    # TBD - need to test under vista and win 7
1508
1509    array set opts [parseargs args {
1510        {recursecontainers.bool 0}
1511        {recurseobjects.bool 0}
1512    } -maxleftover 0]
1513
1514    # We preserve any non-integrity aces in the sacl.
1515    set sacl [get_security_descriptor_sacl $secd]
1516    set aces {}
1517    foreach ace [get_acl_aces $sacl] {
1518        if {[get_ace_type $ace] ne "mandatory_label"} {
1519            lappend aces $ace
1520        }
1521    }
1522
1523    # Now create and attach an integrity ace. Note placement does not
1524    # matter
1525    lappend aces [new_ace mandatory_label \
1526                      [_integrity_to_sid $integrity] \
1527                      [_access_rights_to_mask $rights] \
1528                      -self 1 \
1529                      -recursecontainers $opts(recursecontainers) \
1530                      -recurseobjects $opts(recurseobjects)]
1531
1532    return [set_security_descriptor_sacl $secd [new_acl $aces]]
1533}
1534
1535proc twapi::set_resource_integrity {restype name integrity rights args} {
1536    array set opts [parseargs args {
1537        {recursecontainers.bool 0}
1538        {recurseobjects.bool 0}
1539        handle
1540    } -maxleftover 0]
1541
1542    set secd [set_security_descriptor_integrity \
1543                  [new_security_descriptor] \
1544                  $integrity \
1545                  $rights \
1546                  -recurseobjects $opts(recurseobjects) \
1547                  -recursecontainers $opts(recursecontainers)]
1548
1549    if {$opts(handle)} {
1550        set_resource_security_descriptor $restype $name $secd -mandatory_label -handle
1551    } else {
1552        set_resource_security_descriptor $restype $name $secd -mandatory_label
1553    }
1554}
1555
1556
1557# Convert a security descriptor to SDDL format
1558proc twapi::security_descriptor_to_sddl {secd} {
1559    return [twapi::ConvertSecurityDescriptorToStringSecurityDescriptor $secd 1 0x1f]
1560}
1561
1562# Convert SDDL to a security descriptor
1563proc twapi::sddl_to_security_descriptor {sddl} {
1564    return [twapi::ConvertStringSecurityDescriptorToSecurityDescriptor $sddl 1]
1565}
1566
1567# Return the text for a security descriptor
1568proc twapi::get_security_descriptor_text {secd args} {
1569    if {[_null_secd $secd]} {
1570        return "null"
1571    }
1572
1573    array set opts [parseargs args {
1574        {resourcetype.arg raw}
1575    } -maxleftover 0]
1576
1577    append result "Flags:\t[get_security_descriptor_control $secd]\n"
1578    set name [get_security_descriptor_owner $secd]
1579    if {$name eq ""} {
1580        set name Undefined
1581    } else {
1582        catch {set name [map_account_to_name $name]}
1583    }
1584    append result "Owner:\t$name\n"
1585    set name [get_security_descriptor_group $secd]
1586    if {$name eq ""} {
1587        set name Undefined
1588    } else {
1589        catch {set name [map_account_to_name $name]}
1590    }
1591    append result "Group:\t$name\n"
1592
1593    if {0} {
1594        set acl [get_security_descriptor_dacl $secd]
1595        append result "DACL Rev: [get_acl_rev $acl]\n"
1596        set index 0
1597        foreach ace [get_acl_aces $acl] {
1598            append result "\tDACL Entry [incr index]\n"
1599            append result "[get_ace_text $ace -offset "\t    " -resourcetype $opts(resourcetype)]"
1600        }
1601        set acl [get_security_descriptor_sacl $secd]
1602        append result "SACL Rev: [get_acl_rev $acl]\n"
1603        set index 0
1604        foreach ace [get_acl_aces $acl] {
1605            append result "\tSACL Entry $index\n"
1606            append result [get_ace_text $ace -offset "\t    " -resourcetype $opts(resourcetype)]
1607        }
1608    } else {
1609        append result "DACL:\n"
1610        append result [get_acl_text [get_security_descriptor_dacl $secd] -offset "  " -resourcetype $opts(resourcetype)]
1611        append result "SACL:\n"
1612        append result [get_acl_text [get_security_descriptor_sacl $secd] -offset "  " -resourcetype $opts(resourcetype)]
1613    }
1614
1615    return $result
1616}
1617
1618
1619# Log off
1620proc twapi::logoff {args} {
1621    array set opts [parseargs args {
1622        {force {} 0x4}
1623        {forceifhung {} 0x10}
1624    } -maxleftover 0]
1625    ExitWindowsEx [expr {$opts(force) | $opts(forceifhung)}]  0
1626}
1627
1628# Lock the workstation
1629proc twapi::lock_workstation {} {
1630    LockWorkStation
1631}
1632
1633
1634# Get a new LUID
1635proc twapi::new_luid {} {
1636    return [AllocateLocallyUniqueId]
1637}
1638
1639
1640# Get the description of a privilege
1641proc twapi::get_privilege_description {priv} {
1642    if {[catch {LookupPrivilegeDisplayName "" $priv} desc]} {
1643        # The above function will only return descriptions for
1644        # privileges, not account rights. Hard code descriptions
1645        # for some account rights
1646        set desc [dict* {
1647            SeBatchLogonRight "Log on as a batch job"
1648            SeDenyBatchLogonRight "Deny logon as a batch job"
1649            SeDenyInteractiveLogonRight "Deny interactive logon"
1650            SeDenyNetworkLogonRight "Deny access to this computer from the network"
1651            SeRemoteInteractiveLogonRight "Remote interactive logon"
1652            SeDenyRemoteInteractiveLogonRight "Deny interactive remote logon"
1653            SeDenyServiceLogonRight "Deny logon as a service"
1654            SeInteractiveLogonRight "Log on locally"
1655            SeNetworkLogonRight "Access this computer from the network"
1656            SeServiceLogonRight "Log on as a service"
1657        } $priv]
1658    }
1659    return $desc
1660}
1661
1662
1663
1664# For backward compatibility, emulate GetUserName using GetUserNameEx
1665proc twapi::GetUserName {} {
1666    return [file tail [GetUserNameEx 2]]
1667}
1668
1669
1670################################################################
1671# Utility and helper functions
1672
1673
1674
1675# Returns an sid field from a token
1676proc twapi::_get_token_sid_field {tok field options} {
1677    array set opts [parseargs options {name}]
1678    set owner [GetTokenInformation $tok $field]
1679    if {$opts(name)} {
1680        set owner [lookup_account_sid $owner]
1681    }
1682    return $owner
1683}
1684
1685# Map token group attributes
1686# TBD - write a test for this
1687proc twapi::map_token_group_attr {attr} {
1688    # SE_GROUP_MANDATORY              0x00000001
1689    # SE_GROUP_ENABLED_BY_DEFAULT     0x00000002
1690    # SE_GROUP_ENABLED                0x00000004
1691    # SE_GROUP_OWNER                  0x00000008
1692    # SE_GROUP_USE_FOR_DENY_ONLY      0x00000010
1693    # SE_GROUP_LOGON_ID               0xC0000000
1694    # SE_GROUP_RESOURCE               0x20000000
1695    # SE_GROUP_INTEGRITY              0x00000020
1696    # SE_GROUP_INTEGRITY_ENABLED      0x00000040
1697
1698    return [_make_symbolic_bitmask $attr {
1699        mandatory              0x00000001
1700        enabled_by_default     0x00000002
1701        enabled                0x00000004
1702        owner                  0x00000008
1703        use_for_deny_only      0x00000010
1704        logon_id               0xC0000000
1705        resource               0x20000000
1706        integrity              0x00000020
1707        integrity_enabled      0x00000040
1708    }]
1709}
1710
1711# Map token privilege attributes
1712# TBD - write a test for this
1713proc twapi::map_token_privilege_attr {attr} {
1714    # SE_PRIVILEGE_ENABLED_BY_DEFAULT 0x00000001
1715    # SE_PRIVILEGE_ENABLED            0x00000002
1716    # SE_PRIVILEGE_USED_FOR_ACCESS    0x80000000
1717
1718    return [_make_symbolic_bitmask $attr {
1719        enabled_by_default 0x00000001
1720        enabled            0x00000002
1721        used_for_access    0x80000000
1722    }]
1723}
1724
1725
1726
1727# Map an ace type symbol (eg. allow) to the underlying ACE type code
1728proc twapi::_ace_type_symbol_to_code {type} {
1729    _init_ace_type_symbol_to_code_map
1730    return $::twapi::_ace_type_symbol_to_code_map($type)
1731}
1732
1733
1734# Map an ace type code to an ACE type symbol
1735proc twapi::_ace_type_code_to_symbol {type} {
1736    _init_ace_type_symbol_to_code_map
1737    return $::twapi::_ace_type_code_to_symbol_map($type)
1738}
1739
1740
1741# Init the arrays used for mapping ACE type symbols to codes and back
1742proc twapi::_init_ace_type_symbol_to_code_map {} {
1743
1744    if {[info exists ::twapi::_ace_type_symbol_to_code_map]} {
1745        return
1746    }
1747
1748    # ACCESS_ALLOWED_ACE_TYPE                 0x0
1749    # ACCESS_DENIED_ACE_TYPE                  0x1
1750    # SYSTEM_AUDIT_ACE_TYPE                   0x2
1751    # SYSTEM_ALARM_ACE_TYPE                   0x3
1752    # ACCESS_ALLOWED_COMPOUND_ACE_TYPE        0x4
1753    # ACCESS_ALLOWED_OBJECT_ACE_TYPE          0x5
1754    # ACCESS_DENIED_OBJECT_ACE_TYPE           0x6
1755    # SYSTEM_AUDIT_OBJECT_ACE_TYPE            0x7
1756    # SYSTEM_ALARM_OBJECT_ACE_TYPE            0x8
1757    # ACCESS_ALLOWED_CALLBACK_ACE_TYPE        0x9
1758    # ACCESS_DENIED_CALLBACK_ACE_TYPE         0xA
1759    # ACCESS_ALLOWED_CALLBACK_OBJECT_ACE_TYPE 0xB
1760    # ACCESS_DENIED_CALLBACK_OBJECT_ACE_TYPE  0xC
1761    # SYSTEM_AUDIT_CALLBACK_ACE_TYPE          0xD
1762    # SYSTEM_ALARM_CALLBACK_ACE_TYPE          0xE
1763    # SYSTEM_AUDIT_CALLBACK_OBJECT_ACE_TYPE   0xF
1764    # SYSTEM_ALARM_CALLBACK_OBJECT_ACE_TYPE   0x10
1765    # SYSTEM_MANDATORY_LABEL_ACE_TYPE         0x11
1766
1767    # Define the array.
1768    array set ::twapi::_ace_type_symbol_to_code_map {
1769        allow 0    deny 1     audit 2     alarm 3     allow_compound 4
1770        allow_object 5    deny_object 6    audit_object 7
1771        alarm_object 8    allow_callback 9    deny_callback 10
1772        allow_callback_object 11     deny_callback_object 12
1773        audit_callback 13    alarm_callback 14    audit_callback_object 15
1774        alarm_callback_object 16    mandatory_label 17
1775    }
1776
1777    # Now define the array in the other direction
1778    foreach {sym code} [array get ::twapi::_ace_type_symbol_to_code_map] {
1779        set ::twapi::_ace_type_code_to_symbol_map($code) $sym
1780    }
1781}
1782
1783# Map a resource symbol type to value
1784proc twapi::_map_resource_symbol_to_type {sym {named true}} {
1785    if {[string is integer $sym]} {
1786        return $sym
1787    }
1788
1789    # Note "window" is not here because window stations and desktops
1790    # do not have unique names and cannot be used with Get/SetNamedSecurityInfo
1791    switch -exact -- $sym {
1792        file      { return 1 }
1793        service   { return 2 }
1794        printer   { return 3 }
1795        registry  { return 4 }
1796        share     { return 5 }
1797        kernelobj { return 6 }
1798    }
1799    if {$named} {
1800        error "Resource type '$sym' not valid for named resources."
1801    }
1802
1803    switch -exact -- $sym {
1804        windowstation    { return 7 }
1805        directoryservice { return 8 }
1806        directoryserviceall { return 9 }
1807        providerdefined { return 10 }
1808        wmiguid { return 11 }
1809        registrywow6432key { return 12 }
1810    }
1811
1812    error "Resource type '$sym' not valid"
1813}
1814
1815# Valid LUID syntax
1816proc twapi::_is_valid_luid_syntax luid {
1817    return [regexp {^[[:xdigit:]]{8}-[[:xdigit:]]{8}$} $luid]
1818}
1819
1820
1821# Delete rights for an account
1822proc twapi::_delete_rights {account system} {
1823    # Remove the user from the LSA rights database. Ignore any errors
1824    catch {
1825        remove_account_rights $account {} -all -system $system
1826
1827        # On Win2k SP1 and SP2, we need to delay a bit for notifications
1828        # to complete before deleting the account.
1829        # See http://support.microsoft.com/?id=316827
1830        lassign [get_os_version] major minor sp dontcare
1831        if {($major == 5) && ($minor == 0) && ($sp < 3)} {
1832            after 1000
1833        }
1834    }
1835}
1836
1837
1838# Get a token for a user
1839proc twapi::open_user_token {username password args} {
1840
1841    array set opts [parseargs args {
1842        domain.arg
1843        {type.arg batch {interactive network batch service unlock network_cleartext new_credentials}}
1844        {provider.arg default {default winnt35 winnt40 winnt50}}
1845    } -nulldefault]
1846
1847    # LOGON32_LOGON_INTERACTIVE       2
1848    # LOGON32_LOGON_NETWORK           3
1849    # LOGON32_LOGON_BATCH             4
1850    # LOGON32_LOGON_SERVICE           5
1851    # LOGON32_LOGON_UNLOCK            7
1852    # LOGON32_LOGON_NETWORK_CLEARTEXT 8
1853    # LOGON32_LOGON_NEW_CREDENTIALS   9
1854    set type [dict get {interactive 2 network 3 batch 4 service 5
1855        unlock 7 network_cleartext 8 new_credentials 9} $opts(type)]
1856
1857    # LOGON32_PROVIDER_DEFAULT    0
1858    # LOGON32_PROVIDER_WINNT35    1
1859    # LOGON32_PROVIDER_WINNT40    2
1860    # LOGON32_PROVIDER_WINNT50    3
1861    set provider [dict get {default 0 winnt35 1 winnt40 2 winnt50 3} $opts(provider)]
1862
1863    # If username is of the form user@domain, then domain must not be specified
1864    # If username is not of the form user@domain, then domain is set to "."
1865    # if it is empty
1866    if {[regexp {^([^@]+)@(.+)} $username dummy user domain]} {
1867        if {[string length $opts(domain)] != 0} {
1868            error "The -domain option must not be specified when the username is in UPN format (user@domain)"
1869        }
1870    } else {
1871        if {[string length $opts(domain)] == 0} {
1872            set opts(domain) "."
1873        }
1874    }
1875
1876    return [LogonUser $username $opts(domain) $password $type $provider]
1877}
1878
1879
1880# Impersonate a user given a token
1881proc twapi::impersonate_token {token} {
1882    ImpersonateLoggedOnUser $token
1883}
1884
1885
1886# Impersonate a user
1887proc twapi::impersonate_user {args} {
1888    set token [open_user_token {*}$args]
1889    trap {
1890        impersonate_token $token
1891    } finally {
1892        close_token $token
1893    }
1894}
1895
1896# Impersonate self
1897proc twapi::impersonate_self {level} {
1898    switch -exact -- $level {
1899        anonymous      { set level 0 }
1900        identification { set level 1 }
1901        impersonation  { set level 2 }
1902        delegation     { set level 3 }
1903        default {
1904            error "Invalid impersonation level $level"
1905        }
1906    }
1907    ImpersonateSelf $level
1908}
1909
1910# Set a thread token - currently only for current thread
1911proc twapi::set_thread_token {token} {
1912    SetThreadToken NULL $token
1913}
1914
1915# Reset a thread token - currently only for current thread
1916proc twapi::reset_thread_token {} {
1917    SetThreadToken NULL NULL
1918}
1919
1920proc twapi::credentials {{pattern {}}} {
1921    trap {
1922        set raw [CredEnumerate  $pattern 0]
1923    } onerror {TWAPI_WIN32 1168} {
1924        # Not found / no entries
1925        return {}
1926    }
1927
1928    set ret {}
1929    foreach cred $raw {
1930        set rec [twine {flags type target comment lastwritten credblob persist attributes targetalias username} $cred]
1931        dict with rec {
1932            set type [dict* {
1933                1 generic 2 domain_password 3 domain_certificate 4 domain_visible_password 5 generic_certificate 6 domain_extended} $type]
1934            set persist [dict* {
1935                1 session 2 local_machine 3 enterprise
1936            } $persist]
1937        }
1938        lappend ret $rec
1939    }
1940    return $ret
1941}
1942
1943# TBD - document after implementing AuditQuerySystemPolicy and friends
1944# for Vista & later
1945proc twapi::get_audit_policy {lsah} {
1946    lassign [LsaQueryInformationPolicy $lsah 2] enabled audit_masks
1947    set settings {}
1948    foreach name {
1949        system  logon  object_access  privilege_use  detailed_tracking
1950        policy_change  account_management  directory_service_access
1951        account_logon
1952    } mask $audit_masks {
1953        # Copied from the Perl Win32 book.
1954        set setting {}
1955        if {$mask == 0 || ($mask & 4)} {
1956            set setting {}
1957        } elseif {$mask & 3} {
1958            if {$mask & 1} { lappend setting log_on_success }
1959            if {$mask & 2} { lappend setting log_on_failure }
1960        } else {
1961            error "Unexpected audit mask value $mask"
1962        }
1963        lappend settings $name $setting
1964    }
1965
1966    return [list $enabled $settings]
1967}
1968
1969
1970# TBD - document after implementing AuditQuerySystemPolicy and friends
1971# for Vista & later
1972proc twapi::set_audit_policy {lsah enable settings} {
1973    set audit_masks {}
1974    # NOTE: the order here MUST match the enum definition for
1975    # POLICY_AUDIT_EVENT_TYPE  (see SDK docs)
1976    foreach name {
1977        system  logon  object_access  privilege_use  detailed_tracking
1978        policy_change  account_management  directory_service_access
1979        account_logon
1980    } {
1981        set mask 0; # POLICY_AUDIT_EVENT_UNCHANGED
1982        if {[dict exists $settings $name]} {
1983            set setting [dict get $settings $name]
1984            # 4 -> POLICY_AUDIT_EVENT_NONE resets existing FAILURE|SUCCESS
1985            set mask 4
1986            if {"log_on_success" in $setting} {
1987                set mask [expr {$mask | 1}]; # POLICY_AUDIT_EVENT_SUCCESS
1988            }
1989            if {"log_on_failure" in $setting} {
1990                set mask [expr {$mask | 2}]; # POLICY_AUDIT_EVENT_FAILURE
1991            }
1992        }
1993        lappend audit_masks $mask
1994    }
1995
1996    Twapi_LsaSetInformationPolicy_AuditEvents $lsah $enable $audit_masks
1997}
1998
1999# Returns true if null security descriptor
2000proc twapi::_null_secd {secd} {
2001    if {[llength $secd] == 0} {
2002        return 1
2003    } else {
2004        return 0
2005    }
2006}
2007
2008# Returns true if a valid ACL
2009proc twapi::_is_valid_acl {acl} {
2010    if {$acl eq "null"} {
2011        return 1
2012    } else {
2013        return [IsValidAcl $acl]
2014    }
2015}
2016
2017# Returns true if a valid ACL
2018proc twapi::_is_valid_security_descriptor {secd} {
2019    if {[_null_secd $secd]} {
2020        return 1
2021    } else {
2022        return [IsValidSecurityDescriptor $secd]
2023    }
2024}
2025
2026# Maps a integrity SID to integer or label
2027proc twapi::_sid_to_integrity {sid args} {
2028    # Note - to make it simpler for callers, additional options are ignored
2029    array set opts [parseargs args {
2030        label
2031        raw
2032    }]
2033
2034    if {$opts(raw) && $opts(label)} {
2035        error "Options -raw and -label may not be specified together."
2036    }
2037
2038    if {![string equal -length 7 S-1-16-* $sid]} {
2039        error "Unexpected integrity level value '$sid' returned by GetTokenInformation."
2040    }
2041
2042    if {$opts(raw)} {
2043        return $sid
2044    }
2045
2046    set integrity [string range $sid 7 end]
2047
2048    if {! $opts(label)} {
2049        # Return integer level
2050        return $integrity
2051    }
2052
2053    # Map to a label
2054    if {$integrity < 4096} {
2055        return untrusted
2056    } elseif {$integrity < 8192} {
2057        return low
2058    } elseif {$integrity < 8448} {
2059        return medium
2060    } elseif {$integrity < 12288} {
2061        return mediumplus
2062    } elseif {$integrity < 16384} {
2063        return high
2064    } else {
2065        return system
2066    }
2067
2068}
2069
2070proc twapi::_integrity_to_sid {integrity} {
2071    # Integrity level must be either a number < 65536 or a valid string
2072    # or a SID. Check for the first two and convert to SID. Anything else
2073    # will be trapped by the actual call as an invalid format.
2074    if {[string is integer -strict $integrity]} {
2075        set integrity S-1-16-[format %d $integrity]; # In case in hex
2076    } else {
2077        switch -glob -- $integrity {
2078            untrusted { set integrity S-1-16-0 }
2079            low { set integrity S-1-16-4096 }
2080            medium { set integrity S-1-16-8192 }
2081            mediumplus { set integrity S-1-16-8448 }
2082            high { set integrity S-1-16-12288 }
2083            system { set integrity S-1-16-16384 }
2084            S-1-16-* {
2085                if {![string is integer -strict [string range $integrity 7 end]]} {
2086                    error "Invalid integrity level '$integrity'"
2087                }
2088                # Format in case level component was in hex/octal
2089                set integrity S-1-16-[format %d [string range $integrity 7 end]]
2090            }
2091            default {
2092                error "Invalid integrity level '$integrity'"
2093            }
2094        }
2095    }
2096    return $integrity
2097}
2098
2099proc twapi::_map_luids_and_attrs_to_privileges {luids_and_attrs} {
2100    set enabled_privs [list ]
2101    set disabled_privs [list ]
2102    foreach item $luids_and_attrs {
2103        set priv [map_luid_to_privilege [lindex $item 0] -mapunknown]
2104        # SE_PRIVILEGE_ENABLED -> 0x2
2105        if {[lindex $item 1] & 2} {
2106            lappend enabled_privs $priv
2107        } else {
2108            lappend disabled_privs $priv
2109        }
2110    }
2111
2112    return [list $enabled_privs $disabled_privs]
2113}
2114
2115# Map impersonation level to symbol
2116proc twapi::_map_impersonation_level ilevel {
2117    set map {
2118        0 anonymous
2119        1 identification
2120        2 impersonation
2121        3 delegation
2122    }
2123    if {[dict exists $map [incr ilevel 0]]} {
2124        return [dict get $map $ilevel]
2125    } else {
2126        return $ilevel
2127    }
2128}
2129
2130proc twapi::_map_well_known_sid_name {sidname} {
2131    if {[string is integer -strict $sidname]} {
2132        return $sidname
2133    }
2134
2135    set sidname [string tolower $sidname]
2136    set sidname [dict* {
2137         administrator accountadministrator
2138         {cert publishers} accountcertadmins
2139         {domain computers} accountcomputers
2140         {domain controllers} accountcontrollers
2141         {domain admins} accountdomainadmins
2142         {domain guests} accountdomainguests
2143         {domain users} accountdomainusers
2144         {enterprise admins} accountenterpriseadmins
2145         guest accountguest
2146         krbtgt accountkrbtgt
2147         {read-only domain controllers} accountreadonlycontrollers
2148         {schema admins} accountschemaadmins
2149         {anonymous logon} anonymous
2150         {authenticated users} authenticateduser
2151         batch batch
2152         administrators builtinadministrators
2153         {all application packages} builtinanypackage
2154         {backup operators} builtinbackupoperators
2155         {distributed com users} builtindcomusers
2156         builtin builtindomain
2157         {event log readers} builtineventlogreadersgroup
2158         guests builtinguests
2159         {performance log users} builtinperfloggingusers
2160         {performance monitor users} builtinperfmonitoringusers
2161         {power users} builtinpowerusers
2162         {remote desktop users} builtinremotedesktopusers
2163         replicator builtinreplicator
2164         users builtinusers
2165         {console logon} consolelogon
2166         {creator group} creatorgroup
2167         {creator group server} creatorgroupserver
2168         {creator owner} creatorowner
2169         {owner rights} creatorownerrights
2170         {creator owner server} creatorownerserver
2171         dialup dialup
2172         {digest authentication} digestauthentication
2173         {enterprise domain controllers} enterprisecontrollers
2174         {enterprise read-only domain controllers beta} enterprisereadonlycontrollers
2175         {high mandatory level} highlabel
2176         interactive interactive
2177         local local
2178         {local service} localservice
2179         system localsystem
2180         {low mandatory level} lowlabel
2181         {medium mandatory level} mediumlabel
2182         {medium plus mandatory level} mediumpluslabel
2183         network network
2184         {network service} networkservice
2185         {enterprise read-only domain controllers} newenterprisereadonlycontrollers
2186         {ntlm authentication} ntlmauthentication
2187         {null sid} null
2188         proxy proxy
2189         {remote interactive logon} remotelogonid
2190         restricted restrictedcode
2191         {schannel authentication} schannelauthentication
2192         self self
2193         service service
2194         {system mandatory level} systemlabel
2195         {terminal server user} terminalserver
2196         {untrusted mandatory level} untrustedlabel
2197         everyone world
2198         {write restricted} writerestrictedcode
2199    } $sidname]
2200
2201    return [dict! {
2202        null 0
2203        world 1
2204        local 2
2205        creatorowner 3
2206        creatorgroup 4
2207        creatorownerserver 5
2208        creatorgroupserver 6
2209        ntauthority 7
2210        dialup 8
2211        network 9
2212        batch 10
2213        interactive 11
2214        service 12
2215        anonymous 13
2216        proxy 14
2217        enterprisecontrollers 15
2218        self 16
2219        authenticateduser 17
2220        restrictedcode 18
2221        terminalserver 19
2222        remotelogonid 20
2223        logonids 21
2224        localsystem 22
2225        localservice 23
2226        networkservice 24
2227        builtindomain 25
2228        builtinadministrators 26
2229        builtinusers 27
2230        builtinguests 28
2231        builtinpowerusers 29
2232        builtinaccountoperators 30
2233        builtinsystemoperators 31
2234        builtinprintoperators 32
2235        builtinbackupoperators 33
2236        builtinreplicator 34
2237        builtinprewindows2000compatibleaccess 35
2238        builtinremotedesktopusers 36
2239        builtinnetworkconfigurationoperators 37
2240        accountadministrator 38
2241        accountguest 39
2242        accountkrbtgt 40
2243        accountdomainadmins 41
2244        accountdomainusers 42
2245        accountdomainguests 43
2246        accountcomputers 44
2247        accountcontrollers 45
2248        accountcertadmins 46
2249        accountschemaadmins 47
2250        accountenterpriseadmins 48
2251        accountpolicyadmins 49
2252        accountrasandiasservers 50
2253        ntlmauthentication 51
2254        digestauthentication 52
2255        schannelauthentication 53
2256        thisorganization 54
2257        otherorganization 55
2258        builtinincomingforesttrustbuilders 56
2259        builtinperfmonitoringusers 57
2260        builtinperfloggingusers 58
2261        builtinauthorizationaccess 59
2262        builtinterminalserverlicenseservers 60
2263        builtindcomusers 61
2264        builtiniusers 62
2265        iuser 63
2266        builtincryptooperators 64
2267        untrustedlabel 65
2268        lowlabel 66
2269        mediumlabel 67
2270        highlabel 68
2271        systemlabel 69
2272        writerestrictedcode 70
2273        creatorownerrights 71
2274        cacheableprincipalsgroup 72
2275        noncacheableprincipalsgroup 73
2276        enterprisereadonlycontrollers 74
2277        accountreadonlycontrollers 75
2278        builtineventlogreadersgroup 76
2279        newenterprisereadonlycontrollers 77
2280        builtincertsvcdcomaccessgroup 78
2281        mediumpluslabel 79
2282        locallogon 80
2283        consolelogon 81
2284        thisorganizationcertificate 82
2285        applicationpackageauthority 83
2286        builtinanypackage 84
2287        capabilityinternetclient 85
2288        capabilityinternetclientserver 86
2289        capabilityprivatenetworkclientserver 87
2290        capabilitypictureslibrary 88
2291        capabilityvideoslibrary 89
2292        capabilitymusiclibrary 90
2293        capabilitydocumentslibrary 91
2294        capabilitysharedusercertificates 92
2295        capabilityenterpriseauthentication 93
2296        capabilityremovablestorage 94
2297    } $sidname]
2298}
2299
2300