The goal is to determine if, on Windows, we have "root privileges", more or less:

go run am-i-admin-yet.go
Elevated? false
Admin? true

Whereas on Linux we have the concept of root and sudo, on Windows we have the concept of the Administrators group and Elevated privileges.

TL;DR: Use dark magics

There's no simple way to do this.

You can't use user.Current() like you might expect (see below).

Instead you have to use very Windows-centric, low-level syscall APIs, which are available through the golang.org/x/sys/windows as windows.AllocateAndInitializeSid.

What it's actually checking isn't the user's memberships, but rather the process's memberships.

// +build windows
package main

import (
        "fmt"
        "log"

        "golang.org/x/sys/windows"
)

func main() {
        var sid *windows.SID

        // Although this looks scary, it is directly copied from the
        // official windows documentation. The Go API for this is a
        // direct wrap around the official C++ API.
        // See https://docs.microsoft.com/en-us/windows/desktop/api/securitybaseapi/nf-securitybaseapi-checktokenmembership
        err := windows.AllocateAndInitializeSid(
                &windows.SECURITY_NT_AUTHORITY,
                2,
                windows.SECURITY_BUILTIN_DOMAIN_RID,
                windows.DOMAIN_ALIAS_RID_ADMINS,
                0, 0, 0, 0, 0, 0,
                &sid)
        if err != nil {
                log.Fatalf("SID Error: %s", err)
                return
        }

        // This appears to cast a null pointer so I'm not sure why this
        // works, but this guy says it does and it Works for Me™:
        // https://github.com/golang/go/issues/28804#issuecomment-438838144
        token := windows.Token(0)

        member, err := token.IsMember(sid)
        if err != nil {
                log.Fatalf("Token Membership Error: %s", err)
                return
        }

        // Also note that an admin is _not_ necessarily considered
        // elevated.
        // For elevation see https://github.com/mozey/run-as-admin
        fmt.Println("Elevated?", token.IsElevated())

        fmt.Println("Admin?", member)
}

Running in cmd.exe as a normal user:

go run as-admin.go
Elevated? false
Admin? false

Running in cmd.exe "As Administrator":

go run as-admin.go
Elevated? false
Admin? true

Note how Elevated is distinctly different from the current process being an Admin.

Why you can't use user.Current()

On Unixes your UID changes when you "run as administrator" through sudo, but on Windows that's not the case.

In order to "Run as Administrator" you need to belong to the "Administrators" group (S-1-5-32-544), but your UID and GID don't change when running as administrator.

This sample demonstrates that pretty well:

// +build windows
package main

import (
        "fmt"
        "os/user"
)

func main() {
        fmt.Println(IsAdmin())
}

func IsAdmin() bool {
        u, err := user.Current()
        if nil != err {
                return false
        }

        // These seem to be unique
        fmt.Println(u.Uid)
        fmt.Println(u.Gid)

        ids, err := u.GroupIds()
        if nil != err {
                return false
        }
        for i := range ids {
                fmt.Println(ids[i])
                // not quite, but close enough for now
                // BUILTIN\ADMINISTRATORS
                if "S-1-5-32-544" == ids[i] {
                        return true
                }
        }

        return false
}

As a normal user:

go run is-admin.go
S-1-5-21-1268909207-131461034-1794619431-1000
S-1-5-21-1268909207-131461034-1794619431-513
S-1-5-32-544
true

Right-Click Run As Admin:

S-1-5-21-1268909207-131461034-1794619431-1000
S-1-5-21-1268909207-131461034-1794619431-513
S-1-5-32-544
true

As you can see, there is no change in the UID or GID identifiers.

To learn more about the specific identifiers, take a (long) scroll through the Microsoft documentation:

Getting Elevated

If you want to get elevated privileges, that's a completely different story.

You need to create a special manifest file and use rsrc to embed it into your application as a binary .syso.

See https://github.com/mozey/run-as-admin for more info.


By AJ ONeal

If you loved this and want more like it, sign up!


Did I make your day?
Buy me a coffeeBuy me a coffee  

(you can learn about the bigger picture I'm working towards on my patreon page )



Published

2019-6-25



Buy me a coffeeBuy me a coffee


  73% of Goal Reached


Want more like this?