4

How do you delegate the running of a goroutine to another non administrator account on windows? I see you can do this on Linux using syscall.Setuid(). I can't see how to do this on Windows using the windows syscall package. I'd like to be able to set the account the goroutine runs under while the program is running. Is this possible?

Bit of background :- I want to switch the user that runs the goroutine so I can change the OS User passed to Oracle during the database connection when I use go-oci8 (See my other question). I need to connect to the database and it uses the logged in user (OS User) as part of the security. In java I can change the environment variable during connection set up (or flick the username environmental variable if only connecting for a single user).

I have the users database username (this matches the OS user name) and I get the database user password. I don't have the users windows login password. I was hoping to be able to delegate running a goroutine to the required windows user from the main go program running as admin in a similar way to Linux port binding example I highlighted. Changing the Oracle login to not use OS User is not an option so it will be back to Java if I can't work it out :-( .

Community
  • 1
  • 1
Jimbo Jambo
  • 181
  • 1
  • 11
  • Mmmm, it seem linux also has [problems](https://groups.google.com/forum/#!topic/golang-nuts/BZWXqv3YSg4). I would try to solve this at process level... – RickyA Sep 30 '14 at 14:44
  • But maybe [this](https://godoc.org/kylelemons.net/go/daemon) package might be of interest. – RickyA Sep 30 '14 at 14:47
  • 1
    The only real solution on Linux is using `sudo setcap 'cap_net_bind_service=+ep' app` everytime you recompile the program, on windows I have no idea. – OneOfOne Sep 30 '14 at 15:06
  • 1
    what *exactly* are you trying to do? Do you just need to bind a port like in discussion you linked? – JimB Sep 30 '14 at 15:22
  • @JimB I've updated the question to explain why I need to delegate – Jimbo Jambo Oct 01 '14 at 08:16
  • I think impersonation is not the way to go then (and neither is Java). Why not ask `mattn` directly? Another way is to post a message to [the mailing list](https://groups.google.com/forum/#!forum/golang-nuts/) (`mattn` reads it). Yet another way is to try using [`ODBC`](https://code.google.com/p/odbc/) with appropriate native Windows driver. – kostix Oct 01 '14 at 11:52

2 Answers2

7

In theory, no, it is not possible because both on Linux and Windows the concept of user's identity only exists for OS-level threads and goroutines are not OS threads—instead, they are very light-weight entities which are mapped to real OS threads by the Go scheduler (a part of the Go runtime built into your executable), and during its lifetime a goroutine might be executed on different OS threads at different times.

But there exist a sort of an "exit hatch" for your situation originally designed to help calling into C code: runtime.LockOSThread(). Once a goroutine calls this function it's stuck to the thread it's currently running on and won't be scheduled to be called on another no matter what until the goroutine exits or calls runtime.UnlockOSThread().

You might use this like this:

go func() {
  runtime.LockOSThread()
  defer runtime.UnlockOSThread()
  impersonate() // acquires and assumes some other credentials
  ...
}

The implementation of that imaginary impersonate() function is out of the scope of this question; you can call any Win32 API function using the syscall package—see the standard Go library for examples.


Note that calling runtime.LockOSThread() in real-world scenarious results in dedicating a whole OS thread to just a single goroutine (while usually a whole lot of them runs on just a single one) so if you plan to spawn a lot of such goroutines locked to OS threads be prepared to deal with increased OS resource usage.

Update: a working example tested on Windows XP Pro SP3 32-bit with Go 1.2.1/i386.

It hard codes the user "foo" identified by the password "foo". To quickly create a user on Windows, do

net user foo * /ADD

and type its password twice when prompted.

kostix
  • 51,517
  • 14
  • 93
  • 176
  • 1
    also note that if OP wants everything running under the same account, anything that spawns goroutines (lots of things do, like the HTTP server) will immediately be scheduled on threads under the original account. – JimB Sep 30 '14 at 15:27
  • @kostix can you provide and example of a win32 API function call? If I can't set the account in native go code then a third party windows api call sounds like the next best option. I'm happy to lock a go-routine to a thread as the call made as an alternative windows user account is very quick. – Jimbo Jambo Sep 30 '14 at 15:46
  • @JimboJambo, I felt adventurous and wrote a working example -- see the updated answer. – kostix Sep 30 '14 at 16:50
  • @kostix great example. Thanks. Is it possible to delegate without the users login password? I was hoping to delegate from an Admin account to a user account without the need for windows login password – Jimbo Jambo Oct 01 '14 at 08:19
  • @JimboJambo, I'm afraid there's no way. At least I'm not aware of any. Search the SO for the word `impersonate` using tag `[winapi]` to get an overview (no, you're not the first with such a request; see [this](http://stackoverflow.com/q/137254/720999) for one example). Many refer to a low-level undocumented `ZwCreateToken()` function but I've seen no usage examples and hence unable to comment on it. – kostix Oct 01 '14 at 11:50
0

Goroutines are green threads and may be mapped around to various operating system threads at will. So your original assumption (that you can do this with a simple syscall.Setuid() on linux) is also probably false. You would need to run in an entirely separate process I think to get the privilege restrictions you want.

Evan
  • 6,369
  • 1
  • 29
  • 30
  • 1
    `runtime.LockOSThread` is used to lock a goroutine to an OS thread, but yes, there are better/safer methods for privilege separation. – JimB Sep 30 '14 at 15:15
  • 1
    @JimB that wouldn't work unless you're controlling the goroutine, in the case of an http server the runtime manages that. – OneOfOne Sep 30 '14 at 15:19
  • @OneOfOne: you do absolutely have control over the goroutine that binds the listening port, which is what the OP is referring to in the linked discussion. However you're right if you need *all* spawned goroutines to be the same UID. – JimB Sep 30 '14 at 15:23