Here's a basic example of privilege separation consisting of a low-privileged client and a high-privileged server. The example can easily be adapted to various design paradigms.
Note the use of fork() followed by execve(), which causes the more privileged process to have its own unique address space and stack cookies.
include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <sys/neutrino.h> #include <sys/types.h> #include <sys/procmgr.h> #include <login.h> /* Example credential values */ #define SERVER_UID 88 #define SERVER_GID 88 #define CLIENT_UID 99 #define CLIENT_GID 99 static char *set_ids_arg = NULL; void run_server(int chid, pid_t child) { int rcvid; char msg[10]; struct _msg_info info; struct _client_info * cinfo; /* Retain sample capabilities, and deny and lock all the rest */ procmgr_ability(0, PROCMGR_ADN_NONROOT|PROCMGR_AOP_ALLOW|PROCMGR_AID_PATHSPACE, PROCMGR_ADN_NONROOT|PROCMGR_AOP_ALLOW|PROCMGR_AID_SETUID, PROCMGR_ADN_NONROOT|PROCMGR_AOP_DENY|PROCMGR_AOP_LOCK|PROCMGR_AID_EOL ); /* drop privileges */ if (set_ids_arg) { if( set_ids_from_arg(set_ids_arg) != 0 ) { fprintf(stderr, "set_ids_from_arg failed: errno=%d\n", errno); exit(EXIT_FAILURE); } } else { /* Note that the server side verifies that the process connecting to the channel is indeed its child by comparing the incoming process ID to the known child ID. */ if ( setregid(SERVER_GID, SERVER_GID) != 0 ) { fprintf(stderr, "setregid failed: errno=%d\n", errno); exit(EXIT_FAILURE); } if ( setreuid(SERVER_UID, SERVER_UID) != 0 ) { fprintf(stderr, "setreuid failed: errno=%d\n", errno); exit(EXIT_FAILURE); } } for ( ; ; ) { rcvid = MsgReceive(chid, &msg, sizeof(msg), &info); if (rcvid == -1) { perror("MsgReceive"); /* handle errors */ } else if (rcvid == 0) { /* handle pulses */ } if (ConnectClientInfoExt(info.scoid, &cinfo, _NTO_CLIENTINFO_GETGROUPS) == -1) { perror("ConnectClientInfo()"); exit(EXIT_FAILURE); } if (cinfo->cred.euid == CLIENT_UID && cinfo->cred.egid == CLIENT_GID && info.pid == child) { printf("PID %d: Message from legitimate child\n", getpid()); /* handle legitimate child request */ MsgReply(rcvid, 0x1337, NULL, 0); } free(cinfo); } exit(EXIT_SUCCESS); } void run_client(int chid) { long err; int connd; char send_msg[10]; char reply_msg[10]; /* drop privileges. no capabilities retained */ setregid(CLIENT_GID, CLIENT_GID); setreuid(CLIENT_UID, CLIENT_UID); connd = ConnectAttach(0, getppid(), chid, _NTO_SIDE_CHANNEL, _NTO_COF_CLOEXEC); if (connd == -1) { perror("connd"); exit(EXIT_FAILURE); } for ( ; ; ) { /* typical functionality with large attack surface here */ memset(send_msg, 0x41, sizeof(send_msg)); memset(reply_msg, 0x41, sizeof(reply_msg)); err = MsgSend(connd, send_msg, sizeof(send_msg), reply_msg, sizeof(reply_msg)); if (err == -1) { perror("MsgSend"); exit(EXIT_FAILURE); } printf("PID %d: Got message response!\n", getpid()); } exit(EXIT_SUCCESS); } int main(int argc, char **argv) { int c; int chid; int connd; int is_client; pid_t pid; is_client = 0; while ((c = getopt(argc, argv, "c:")) != -1) { switch(c) { /* only used during re-execution */ case 'c': chid = atoi(optarg); is_client = 1; break; /* add a -U case */ case 'U': set_ids_arg = optarg; break; default: //usage(); exit(EXIT_FAILURE); break; } } if (is_client) { run_client(chid); return 1; // Unreachable code: run_client() calls exit() } /* Create channel */ chid = ChannelCreate(_NTO_CHF_DISCONNECT); if (chid == -1) { perror("ChannelCreate"); exit(EXIT_FAILURE); } pid = fork(); // alternatively use spawn() if (pid == -1) { perror("fork"); exit(EXIT_FAILURE); } /* parent */ if (pid) { /* Note that the channel identifier is provided to the client process, using the command line, so it knows where to connect. */ run_server(chid, pid); } /* child */ else { unsigned int i; char buf[32]; // enough to hold channel id digits char ** new_args; new_args = malloc((argc+2) * sizeof(char *)); if (new_args == NULL) { perror("malloc"); exit(EXIT_FAILURE); } for (i = 0; i < argc; i++) { new_args[i] = argv[i]; } new_args[i++] = "-c"; memset(buf, 0, sizeof(buf)); if (snprintf(buf, sizeof(buf), "%d", chid) == -1) { perror("snprintf"); exit(EXIT_FAILURE); } new_args[i++] = buf; new_args[i] = NULL; execve(new_args[0], new_args, NULL); } return 0; }