{"id":111281,"date":"2025-06-18T07:00:00","date_gmt":"2025-06-18T14:00:00","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/oldnewthing\/?p=111281"},"modified":"2025-06-18T08:47:44","modified_gmt":"2025-06-18T15:47:44","slug":"20250618-00","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20250618-00\/?p=111281","title":{"rendered":"You have to tell <CODE>Get-<\/CODE> and <CODE>Set&shy;Security&shy;Info<\/CODE> the object type, you can&#8217;t make it guess"},"content":{"rendered":"<p>A customer was trying to read the security information from a process object, but their call to <code>Get\u00adSecurity\u00adInfo<\/code> wasn&#8217;t working.<\/p>\n<pre>PACL dacl = nullptr;\r\nPSECURITY_DESCRIPTOR sd = nullptr;\r\nGetSecurityInfo(processHandle,\r\n    SE_UNKNOWN_OBJECT_TYPE,\r\n    DACL_SECURITY_INFORMATION,\r\n    nullptr, \/\/ not interested in owner\r\n    nullptr, \/\/ not interested in group\r\n    &amp;dacl, \/\/ receives DACL\r\n    nullptr, \/\/ not interested in SACL\r\n    &amp;sd); \/\/ receives security descriptor\r\n<\/pre>\n<p>They found that the call always failed with &#8220;invalid parameter&#8221;. But which parameter is invalid?<\/p>\n<p>In this case, it&#8217;s the object type parameter. The <code>Get\u00adSecurity\u00adInfo<\/code> and <code>Set\u00adSecurity\u00adInfo<\/code> functions require you to tell it what kind of object for which you are trying to get or set the security information. If you pass <code>SE_<wbr \/>UNKNOWN_<wbr \/>OBJECT_<wbr \/>TYPE<\/code>, you&#8217;re saying &#8220;I don&#8217;t know!&#8221;<\/p>\n<p>Well, if you don&#8217;t know, then who does?<\/p>\n<p>Process handles are kernel objects, so you should be passing <code>SE_<wbr \/>KERNEL_<wbr \/>OBJECT<\/code>.<\/p>\n<p>&#8220;But why do I have to tell it what kind of object it is? Surely it can find out!&#8221;<\/p>\n<p>Internally, what happens is that the different object types are managed by different providers, and the ObjectType parameter lets the function know which provider it needs to delegate the request to. I guess that in principle it could just pass the handle to every provider to say &#8220;Do you know what to do with this?&#8221;, and all of them will say &#8220;That&#8217;s not mine&#8221; except one. But that&#8217;s rather wasteful and will create tons of false alarms in program validation tools like Application Verifier.\u00b9<\/p>\n<p>So you just have to tell it, &#8220;This is a kernel handle,&#8221; and <code>Get\u00adSecurity\u00adInfo<\/code> will route the request to the kernel handle provider. It&#8217;s not going to try to guess.<\/p>\n<p>Even if we somehow got rid of the object type parameter from <code>Get\u00adSecurity\u00adInfo<\/code>, the companion function <code>Get\u00adNamed\u00adSecurity\u00adInfo<\/code> definitely needs an object type parameter because it needs to know how to interpret the name. For example, the string <tt>\\\\alpha\\beta<\/tt> is ambiguous. Is this referring to the <tt>beta<\/tt> service on the computer <tt>alpha<\/tt>? Or is it referring to a printer named <tt>beta<\/tt> on that server? Or is it referring to a share named <tt>beta<\/tt> on that server? The string by itself is ambiguous,<\/p>\n<p>The value <code>SE_<wbr \/>UNKNOWN_<wbr \/>OBJECT_<wbr \/>TYPE<\/code> is a permanently invalid object type. You could use it as a sentinel value for your own <code>SE_<wbr \/>OBJECT_<wbr \/>TYPE<\/code> variables to represent an uninitialized or invalid state.<\/p>\n<p>\u00b9 And there might be multiple providers who recognize the handle. The numeric value of a service handle might happen to match the numeric value of a printer handle (say), and now the system doesn&#8217;t know whether you are asking for the security info for a service handle whose numeric value happens to be <tt>0x1234<\/tt> or for a printer handle whose numeric value happens to be <tt>0x1234<\/tt>.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>It needs to know which provider to give it to.<\/p>\n","protected":false},"author":1069,"featured_media":111744,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[1],"tags":[25],"class_list":["post-111281","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>It needs to know which provider to give it to.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/111281","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/users\/1069"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/comments?post=111281"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/111281\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/media\/111744"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/media?parent=111281"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=111281"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=111281"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}