queue_err_t queue_open(void *p, const char *filename, int flags) { int omask; mode_t oldmask; posix_queue_t *q; q = (posix_queue_t *) p; if (HAS_FLAG(flags, QUEUE_FL_SENDER)) { omask = O_WRONLY; } else { omask = O_RDONLY; } if (HAS_FLAG(flags, QUEUE_FL_OWNER)) { omask |= O_CREAT | O_EXCL; oldmask = umask(0); if (NULL == (q->filename = strdup(filename))) { // TODO: error return QUEUE_ERR_GENERAL_FAILURE; } } if (NOT_MQD_T == (q->mq = mq_open(filename, omask, 0660, &q->attr))) { if (HAS_FLAG(flags, QUEUE_FL_OWNER)) { umask(oldmask); #ifdef __FreeBSD__ if (ENOSYS == errno) { // please load mqueuefs module with kldload or recompile your kernel to include "options P1003_1B_MQUEUE" } #endif } // TODO: error return QUEUE_ERR_GENERAL_FAILURE; } if (!HAS_FLAG(flags, QUEUE_FL_SENDER)) { // mq_setattr implies CAP_EVENT? CAP_RIGHTS_LIMIT(__mq_oshandle(q->mq), CAP_READ, CAP_EVENT); #if 0 } else { CAP_RIGHTS_LIMIT(__mq_oshandle(q->mq), CAP_WRITE, CAP_EVENT); #endif } if (HAS_FLAG(flags, QUEUE_FL_OWNER)) { umask(oldmask); } else { if (0 != mq_getattr(q->mq, &q->attr)) { // TODO: error return QUEUE_ERR_GENERAL_FAILURE; } } return QUEUE_ERR_OK; }
queue_err_t queue_open(void *p, const char *name, int flags) { int id; FILE *fp; key_t key; mode_t oldmask; systemv_queue_t *q; struct msqid_ds buf; char *s, filename[MAXPATHLEN]; q = (systemv_queue_t *) p; if (HAS_FLAG(flags, QUEUE_FL_OWNER)) { if (NULL == (s = strchr(name, ':')) || '\0' == *s) { id = 'b'; if (strlcpy(filename, name, STR_SIZE(filename)) >= STR_SIZE(filename)) { errno = E2BIG; return QUEUE_ERR_GENERAL_FAILURE; } } else { id = s[1]; if (s - name >= STR_SIZE(filename)) { errno = E2BIG; return QUEUE_ERR_GENERAL_FAILURE; } strncpy(filename, name, s - name); } if (NULL == (fp = fopen(filename, "wx"))) { // TODO: error debug(""); return QUEUE_ERR_GENERAL_FAILURE; } if (1 != fwrite(&id, sizeof(id), 1, fp)) { // TODO: error debug(""); return QUEUE_ERR_GENERAL_FAILURE; } fflush(fp); } else { if (strlcpy(filename, name, STR_SIZE(filename)) >= STR_SIZE(filename)) { errno = E2BIG; return QUEUE_ERR_GENERAL_FAILURE; } if (NULL == (fp = fopen(filename, "r"))) { // TODO: error debug(""); return QUEUE_ERR_GENERAL_FAILURE; } if (1 != fread(&id, sizeof(id), 1, fp) < sizeof(id)) { // TODO: error debug(""); return QUEUE_ERR_GENERAL_FAILURE; } } fclose(fp); if (-1 == (key = ftok(filename, id))) { // NOTE: errno is not set by ftok // TODO: error debug(""); return QUEUE_ERR_GENERAL_FAILURE; } if (HAS_FLAG(flags, QUEUE_FL_OWNER)) { if (NULL == (q->filename = strdup(filename))) { // TODO: error debug(""); return QUEUE_ERR_GENERAL_FAILURE; } oldmask = umask(0); q->qid = msgget(key, 0660 | IPC_CREAT | IPC_EXCL); umask(oldmask); } else { q->qid = msgget(key, 0660); } if (-1 == q->qid) { // TODO: error debug(""); return QUEUE_ERR_GENERAL_FAILURE; } if (!HAS_FLAG(flags, QUEUE_FL_SENDER)) { #if 0 https://svnweb.freebsd.org/base/head/sys/kern/sysv_msg.c?revision=282213&view=markup #define IPCID_TO_IX(id) ((id) & 0xffff) #define IPCID_TO_SEQ(id) (((id) >> 16) & 0xffff) #define IXSEQ_TO_IPCID(ix,perm) (((perm.seq) << 16) | (ix & 0xffff)) #endif CAP_RIGHTS_LIMIT(q->qid, CAP_READ); #if 0 } else { CAP_RIGHTS_LIMIT(q->qid, CAP_WRITE); #endif } if (0 != msgctl(q->qid, IPC_STAT, &buf)) { // TODO: error debug(""); return QUEUE_ERR_GENERAL_FAILURE; } q->buffer_size = buf.msg_qbytes / 8; if (NULL == (q->buffer = malloc(sizeof(long) + sizeof(q->buffer) * q->buffer_size))) { // TODO: error debug(""); return QUEUE_ERR_GENERAL_FAILURE; } *(long *) q->buffer = 1; /* mtype is an integer greater than 0 */ return QUEUE_ERR_OK; }
int main(int argc, char **argv) { gid_t gid; addr_t addr; struct sigaction sa; int c, dFlag, vFlag; unsigned long max_message_size; const char *queuename, *tablename; ctxt = NULL; gid = (gid_t) -1; vFlag = dFlag = 0; tablename = queuename = NULL; if (NULL == (queue = queue_init())) { errx("queue_init failed"); // TODO: better } atexit(cleanup); sa.sa_handler = &on_signal; sigemptyset(&sa.sa_mask); sigaction(SIGINT, &sa, NULL); sigaction(SIGTERM, &sa, NULL); sa.sa_flags = SA_RESTART; sigaction(SIGUSR1, &sa, NULL); if (NULL == (engine = get_default_engine())) { errx("no engine available for your system"); } while (-1 != (c = getopt_long(argc, argv, optstr, long_options, NULL))) { switch (c) { case 'b': { unsigned long val; if (parse_ulong(optarg, &val)) { queue_set_attribute(queue, QUEUE_ATTR_MAX_MESSAGE_SIZE, val); // TODO: check returned value } break; } case 'd': dFlag = 1; break; case 'e': { if (NULL == (engine = get_engine_by_name(optarg))) { errx("unknown engine '%s'", optarg); } break; } case 'g': { struct group *grp; if (NULL == (grp = getgrnam(optarg))) { errc("getgrnam failed"); } gid = grp->gr_gid; break; } case 'l': { logfilename = optarg; if (NULL == (err_file = fopen(logfilename, "a"))) { err_file = NULL; warnc("fopen '%s' failed, falling back to stderr", logfilename); } break; } case 'p': pidfilename = optarg; break; case 'q': queuename = optarg; break; case 's': { unsigned long val; if (parse_ulong(optarg, &val)) { queue_set_attribute(queue, QUEUE_ATTR_MAX_MESSAGE_IN_QUEUE, val); // TODO: check returned value } break; } case 't': tablename = optarg; break; case 'v': vFlag++; break; case 'h': default: usage(); } } argc -= optind; argv += optind; if (0 != argc || NULL == queuename || NULL == tablename) { usage(); } if (dFlag) { if (0 != daemon(0, !vFlag)) { errc("daemon failed"); } } if (NULL != pidfilename) { FILE *fp; if (NULL == (fp = fopen(pidfilename, "w"))) { warnc("can't create pid file '%s'", pidfilename); } else { fprintf(fp, "%ld\n", (long) getpid()); fclose(fp); } } if (((gid_t) -1) != gid) { if (0 != setgid(gid)) { errc("setgid failed"); } if (0 != setgroups(1, &gid)) { errc("setgroups failed"); } } CAP_RIGHTS_LIMIT(STDOUT_FILENO, CAP_WRITE); CAP_RIGHTS_LIMIT(STDERR_FILENO, CAP_WRITE); if (NULL != err_file/* && fileno(err_file) > 2*/) { CAP_RIGHTS_LIMIT(fileno(err_file), CAP_WRITE); } if (QUEUE_ERR_OK != queue_open(queue, queuename, QUEUE_FL_OWNER)) { errx("queue_open failed"); // TODO: better } if (QUEUE_ERR_OK != queue_get_attribute(queue, QUEUE_ATTR_MAX_MESSAGE_SIZE, &max_message_size)) { errx("queue_get_attribute failed"); // TODO: better } if (NULL == (buffer = calloc(++max_message_size, sizeof(*buffer)))) { errx("calloc failed"); } if (NULL != engine->open) { ctxt = engine->open(tablename); } if (0 == getuid() && engine->drop_privileges) { struct passwd *pwd; if (NULL == (pwd = getpwnam("nobody"))) { if (NULL == (pwd = getpwnam("daemon"))) { errx("no nobody or daemon user accounts found on this system"); } } if (0 != setuid(pwd->pw_uid)) { errc("setuid failed"); } } CAP_ENTER(); while (1) { ssize_t read; if (-1 == (read = queue_receive(queue, buffer, max_message_size))) { errc("queue_receive failed"); // TODO: better } else { if (!parse_addr(buffer, &addr)) { errx("parsing of '%s' failed", buffer); // TODO: better } else { engine->handle(ctxt, tablename, addr); } } } /* not reached */ return BANIPD_EXIT_SUCCESS; }