Is there a set(e)uid function, or equivalent?

Is there a way of changing (unix) user? I can see getpid in the racket/os package, and if a setuid-a-like function exists, I'd expect to find it nearby, but I can't find it there or under any other search string I can think of.

Why? I'm trying to see if fork+setuid looks reasonably neat in a server program. The right thing to do is probably to use daemon or equivalent, but there are a couple of intricacies with that, and rather than fight with it I thought I'd look to see if doing it 'by hand' would be simpler in the long run.

Hi, @nxg.

I don't know much about operating systems, but a quick Github search revealed the following code for setuid and Racket.

Maybe C FFI (foreign function interface) would be the route somehow? Again, I know very little.

1 Like

You can probably (nonportably?) access the system interface for changing users or setuids with define-c or other parts of the FFI

1 Like

Thanks, both.

The FFI had occurred to me, but the fact that this wasn't in the racket/os module, where it seems an obvious member of the collection of random other OS facilities I'd expect to find there, made me think I was missing something thoughtful elsewhere.

It's slightly surprising that I can't do this in a library-standard way, but if I'm assured it's not there, then I can just stick with Plan A.

Best wishes,

Norman

@nxg My guess is racket/os is intended to be portable but setuid isn't.

So for example its getpid (1) just gets (2) some unique integer for the process. Even if some OS returned (say) a unique string instead of number ID, you could imagine implementing this to honor the contract.


Whereas that FFI example linked to by @bakgatviooldoos suggests "setuid" isn't portable:

(require (prefix-in ffi: ffi/unsafe))

...

; allow Arc to give up root privileges after it
; calls open-socket. thanks, Eli!
(define setuid (ffi:get-ffi-obj 'setuid #f
                 (ffi:_fun ffi:_int ffi:-> ffi:_int)
                 ; If we're on Windows, there is no setuid, so we make
                 ; a dummy version. See "Arc 3.1 setuid problem on
                 ; Windows," http://arclanguage.org/item?id=10625.
                 (lambda () (lambda (x) 'nil))))

I'm guessing that although Windows surely has some notion of a "user ID", it might lack the ability of a process to change this for itself? (I don't think running as root and dropping privileges is really part of the Windows history/culture.)

Anyway, although the FFI "DSL" can take awhile to learn for complicated examples, as you can see it can be pretty painless in simple cases like this.

If possible, I recommend avoiding setuid by launching your program as an unprivileged user and only granting it the capabilities it actually needs. On a typical GNU/Linux system, you can allow an unprivileged process to bind to "privileged" ports (< 1024) by granting it the CAP_NET_BIND_SERVICE capability, for example by adding the following to your systemd unit file:

[Service]
AmbientCapabilities=CAP_NET_BIND_SERVICE

(Other full-featured init systems provide similar mechanisms: the underlying functionality is not specific to systemd.)

[quote="greghendershott, post:5, topic:2766"]
I'm guessing that although Windows surely has some notion of a "user ID", it might lack the ability of a process to change this for itself? (I don't think running as root and dropping privileges is really part of the Windows history/culture.)[/quote]

It looks like you're right – thanks – and that makes a lot of sense.

I've been spoilt here, recently, by spending time with s7 which, as an extension language is, in a sense, all-FFI. It's a rather ascetic experience, as Schemes go, but the FFI is sweet.

Yes, that's my Plan A, really – it's an excellent suggestion.

I'm using daemon invoked from rc.d (this won't be running on a systemd box), but I think I'm slightly fumbling the invocation, and rather than read the manpage yet again, I (slightly self-indulgently) wondered if doing this the old-skool way might be simpler. I'm not surprised it's not: the last time I did this by hand I remember discovering how many moving parts there are to get subtly wrong.

I remain slightly surprised that Racket doesn't have a portable implementation of the daemonise operation, but since things like systemd and /usr/sbin/daemon exist and do work, there isn't, when it comes down to it, a clearly demonstrable need for it.