privPortClient.cpp [plain text]
#include <Security/AuthorizationPriv.h>
#include <Security/fdmover.h>
#include <Security/debugging.h>
#include "privPort.h"
using namespace UnixPlusPlus;
using namespace IPPlusPlus;
#if !defined(PORTSERVERPATH)
# define PORTSERVERPATH "/System/Library/CoreServices/privportserver"
#endif
static void startServer();
OSStatus AuthorizationBindPrivilegedPort(int fileDescriptor,
const struct sockaddr_in *name,
AuthorizationRef authorization,
AuthorizationFlags flags)
{
BEGIN_API
unsigned short port = ntohs(name->sin_port);
secdebug("portserve", "bind request fd=%d port=%d", fileDescriptor, port);
#if defined(NDEBUG)
if (port <= 0 || port >= IPPORT_RESERVED)
return errAuthorizationBadAddress;
#endif //NEBUG
if (flags)
return errAuthorizationInvalidFlags;
Request request;
if (OSStatus err = AuthorizationMakeExternalForm(authorization, &request.authForm))
return err;
request.requestedName = *name;
UNSockAddress serverAddress(kPrivilegedPortBinder);
FdMover server;
server.open(AF_UNIX, SOCK_STREAM);
try {
server.connect(serverAddress);
} catch (const UnixError &error) {
switch (error.error) {
case ENOENT: case ECONNREFUSED: startServer();
server.connect(serverAddress);
break;
default:
throw;
}
}
secdebug("portserve", "sending request");
FdVector fds;
fds.push_back(fileDescriptor);
if (server.send(&request, sizeof(request), fds) != sizeof(request))
UnixError::throwMe(EIO);
secdebug("portserve", "getting reply");
Reply reply;
if (server.read(&reply, sizeof(reply)) != sizeof(reply))
UnixError::throwMe(EIO);
secdebug("portserve", "server replied %ld", reply.status);
return ntohl(reply.status);
END_API(CSSM)
}
static void startServer()
{
const char *serverpath = PORTSERVERPATH;
#if !defined(NDEBUG)
if (const char *override = getenv("PRIVPORTSERVER"))
serverpath = override;
#endif //!NDEBUG
secdebug("portserve", "starting %s", serverpath);
switch (fork()) {
case -1:
UnixError::throwMe();
case 0: execl(serverpath, serverpath, NULL);
secdebug("portserve", "cannot exec %s (errno=%d)", serverpath, errno);
_exit(1);
default: sleep(1);
break;
}
}
int __authorization_bind(int s, const struct sockaddr_in *name)
{
AuthorizationItem rights[] = {
{ "system.privilege.port.connect", 0, NULL, 0 }
};
AuthorizationRights rightSet =
{ sizeof(rights) / sizeof(rights[0]), rights };
AuthorizationRef auth;
if (AuthorizationCreate(&rightSet, NULL,
kAuthorizationFlagInteractionAllowed |
kAuthorizationFlagExtendRights |
kAuthorizationFlagPreAuthorize,
&auth)) {
errno = EPERM;
return -1;
}
OSStatus err = AuthorizationBindPrivilegedPort(s, name, auth, 0);
AuthorizationFree(auth, 0);
if (err) {
errno = (err >= errSecErrnoBase && err <= errSecErrnoLimit) ?
errno = err - errSecErrnoBase :
EPERM;
return -1;
}
return 0;
}