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