Binding on priviledged ports on macOS

Historically, one could not bind on privileged ports (<1024) without using a PriviledgedHelperTool.

Since macOS Mojave, it seems we don't need it anymore. Very good, but is this behavior change official and documented somewhere ?

I haven't found any official information (just this link on news.ycombinator.com/item?id=18302380)

Regards
Answered by DTS Engineer in 662907022

Since macOS Mojave, it seems we don't need it anymore.

That’s correct. iOS never had this restriction and we’ve now lifted it on macOS. Finally! (-:

I think Matt’s right that we never documented this officially. I’m pretty sure I’ve talked about it here on DevForums but I can’t find any reference to that. Regardless, I can assure you that this was an intentional change and we don’t plan to change it back.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@apple.com"

but is this behavior change official and documented somewhere ?

I am not aware of any documentation regarding this behavior. Are you having a code level issue using one of these ports in your networking code?


Matt Eaton
DTS Engineering, CoreOS
meaton3@apple.com
Hi Matt

No particular problem, since we used to use a PriviledgedHelperTool (which took some time to develop with a lot of sweat and efforts :)) to bind on the lower ports and we recently realized by accident that it's now perfectly useless.

So the goal of my question is to have an official confirmation that this is a normal behavior (we now can bind on lower ports without any PriviledgedHelperTool) and that therefore we can stop installing our PriviledgedHelperTool.

Thanks in advance for your help

Yannick TRINH
4D SAS

Accepted Answer

Since macOS Mojave, it seems we don't need it anymore.

That’s correct. iOS never had this restriction and we’ve now lifted it on macOS. Finally! (-:

I think Matt’s right that we never documented this officially. I’m pretty sure I’ve talked about it here on DevForums but I can’t find any reference to that. Regardless, I can assure you that this was an intentional change and we don’t plan to change it back.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@apple.com"
Thanks a lot Matt and Quinn for your answers, I just needed to read that this change of behavior was expected and you just made my day :)

Yannick TRINH
4D SAS

Hello

I'm coming back to the subject because I always experiment issues with lower ports without helpertools.

I was told earlier in this thread that no helpertool was needed anymore to bind on a lower port but it seems that this is partially true.

Actually, if we bind on a socket using ANY_ADDR, no problem all is working perfectly and no helpertool is needed. But when using one of the valid addresses of the machine (say "192.168.1.30" for example), bind call systematically returns an error 13 (Permission denied). This issue does not occur when with an helpertool whatever the address used.

Questions: is this an expected behavior (I don't think so), a bug ? Is there a way to get around the problem (other than calling the helpertool we just removed)? A magic trick ? Any idea ?

Thank you in advance for your answers

Yannick TRINH 4D SAS

Why are you binding to a specific IP address? In most cases it’s better to bind to the interface (see snippet below) and that doesn’t trigger this problem.

unsigned int interfaceIndex = if_nametoindex("en0");
if (interfaceIndex <= 0) { … handle error … }
BOOL success = setsockopt(fd, IPPROTO_IP, IP_BOUND_IF, &interfaceIndex, sizeof(interfaceIndex)) >= 0;
if ( ! success ) { … handle error … }

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

Hello

Thanks Quinn for taking the time to answer but to be honest I don't understand the answer... I'm talking about binding a socket in order to listen on it. A little snippet of code is better than a long speech, here is what I'm trying to do:

int main(int argc, const char * argv[]) {

	struct sockaddr_in  addr;
	int sock, res;
	
	addr.sin_port        = htons(80);
	addr.sin_family      = AF_INET;
	addr.sin_addr.s_addr = inet_addr("192.168.1.30"); // Works with htonl(INADDR_ANY)

	sock = socket (AF_INET, SOCK_STREAM, 0);
	
	res = bind (sock, (struct sockaddr *)&addr, sizeof(addr));
	if (res < 0)
		perror ( "bind" );
	
	res = listen (sock, 1);
	if (res < 0)
		perror ( "listen" );

	res = close (sock);
	if (res < 0)
		perror ( "close" );

	return 0;
}

Binding on lower ports always returns error 13 Permission Denied with any other value than ANY_ADDR. Shouldn't we use our helpertool or is there a way to do without it?

Yannick TRINH @ 4D SAS

Hi there! Has there been any movement on the ability to bind to privileged ports for specified IP addresses? It's important to me so I can use multiple loopback addresses to test servers mounted to the same port.

Has there been any movement on the ability to bind to privileged ports for specified IP addresses?

No.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

I ran into one specific case where it's desirable to bind a specific IP address rather than a named interface: qemu's hostfwd option.

Specifically, qemu's hostfwd option only permits two alternatives when specifying the host address to bind to:

  • Bind to a specific IP address (e.g. 127.0.0.1)
  • Omit the host address, which defaults to binding 0.0.0.0 (a.k.a. INADDR_ANY / *)

Notably, you cannot bind to a network interface name like localhost when using qemu's hostfwd option so Quinn's suggested workaround of specifying a network interface name does not work here. Only the latter option of binding 0.0.0.0 works for unprivileged users.

This means that currently there is not a good way for an unprivileged user to run a qemu VM on macOS that you can ssh into from the host. Ideally, you'd like to specify a hostfwd rule of the form hostfwd=tcp:127.0.0.1:22-:22 so that the SSH port is only available to the host (on 127.0.0.1), but macOS currently forbids binding to 127.0.0.1:22 for unprivileged users.

To work around this you have to instead specify hostfwd=tcp::22-:22, which binds to 0.0.0.0 on the host, but that is undesirable because it will ask the user if they want to open the firewall (because it is attempting to gratuitously bind on all public network interfaces). In the best case scenario the user is knowledgeable enough to deny the firewall open request (since it's not necessary to open the firewall to connect on localhost). In the worst case scenario the user accepts the prompt to open the firewall and now the VM's ssh port is now public and reachable from any machine that can connect to the user's machine.

So I'd like to request generalizing the feature so that privileged ports can be bound by unprivileged users even for specific IP addresses. I can open a separate issue if necessary to track that request.

I ran into one specific case where it's desirable

Interesting.

So I'd like to request generalizing the feature so that privileged ports can be bound by unprivileged users even for specific IP addresses.

Please make this official by filing a bug. Oh, and I’d appreciate you posting your bug number here, just for the record.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

another, quite obvious case is doing localdev wherein one has multiple services running, and wishes to start service A on one address locally and service B on a different address locally, each on the same port...

for example, having en0 have its' base address: 192.0.2.99 (/24 netmask) gw 192.0.2.1, and another address 192.0.2.100 (/24 netmask) with no gw;... I would start one service on .99, another on .100; and be able to interact with each service from a different host.

obviously, this wouldn't work were I binding to 0.0.0.0. Additionally, I don't want the service to bind on ipv6, and specifying 0/0 defaults to a 6+4 bind.

another, quite obvious case

And your bug number is?

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

I've encountered a similar issue on the latest macOS (14.5). Interestingly, non-root users can bind to the HTTPS port (443) on any public addresses, including 127.0.0.1, but cannot bind solely to private address 127.0.0.1.

In reality, we don't want our local development environment exposed to the public, as it poses a security risk of unexpected access.

So, the ideal behavior is that non-root users can only bind to localhost and not to public addresses. However, since Apple has already opened up binding to public addresses, I think it's better to allow binding solely to localhost as well.

Binding on priviledged ports on macOS
 
 
Q