diff --git a/usr.sbin/syslogd/syslogd.c b/usr.sbin/syslogd/syslogd.c --- a/usr.sbin/syslogd/syslogd.c +++ b/usr.sbin/syslogd/syslogd.c @@ -176,7 +176,8 @@ (((d)->s6_addr32[3] ^ (a)->s6_addr32[3]) & (m)->s6_addr32[3]) == 0 ) #endif /* - * List of peers and sockets for binding. + * List of peers and sockets that can't be bound until + * flags have been parsed. */ struct peer { const char *pe_name; @@ -186,13 +187,16 @@ }; static STAILQ_HEAD(, peer) pqueue = STAILQ_HEAD_INITIALIZER(pqueue); +/* + * Sockets used for logging; monitored by kevent(). + */ struct socklist { struct addrinfo sl_ai; #define sl_sa sl_ai.ai_addr #define sl_salen sl_ai.ai_addrlen #define sl_family sl_ai.ai_family int sl_socket; - struct peer *sl_peer; + const char *sl_name; int (*sl_recv)(struct socklist *); STAILQ_ENTRY(socklist) next; }; @@ -419,9 +423,9 @@ struct iovlist; -static int allowaddr(char *); -static int addpeer(struct peer *); -static int addsock(struct addrinfo *, struct socklist *); +static bool allowaddr(char *); +static void addpeer(const char *, const char *, mode_t); +static void addsock(const char *, const char *, mode_t); static void cfline(const char *, const char *, const char *, const char *); static const char *cvthname(struct sockaddr *); static void deadq_enter(pid_t, const char *); @@ -440,7 +444,7 @@ const char *, const char *, const char *, const char *, int); static void log_deadchild(pid_t, int, const char *); static void markit(void); -static int socksetup(struct peer *); +static struct socklist *socksetup(struct addrinfo *, const char *, mode_t); static int socklist_recv_file(struct socklist *); static int socklist_recv_sock(struct socklist *); static int skip_message(const char *, const char *, int); @@ -490,41 +494,91 @@ f->f_file = -1; } -static int -addpeer(struct peer *pe0) +static void +addpeer(const char *name, const char *serv, mode_t mode) { - struct peer *pe; - - pe = calloc(1, sizeof(*pe)); + struct peer *pe = calloc(1, sizeof(*pe)); if (pe == NULL) err(1, "malloc failed"); - *pe = *pe0; + pe->pe_name = name; + pe->pe_serv = serv; + pe->pe_mode = mode; STAILQ_INSERT_TAIL(&pqueue, pe, next); - - return (0); } -static int -addsock(struct addrinfo *ai, struct socklist *sl0) +static void +addsock(const char *name, const char *serv, mode_t mode) { + struct addrinfo hints = { }, *res, *res0; struct socklist *sl; + int error; + char *cp, *msgbuf; - /* Copy *ai->ai_addr to the tail of struct socklist if any. */ - sl = calloc(1, sizeof(*sl) + ((ai != NULL) ? ai->ai_addrlen : 0)); + /* + * We have to handle this case for backwards compatibility: + * If there are two (or more) colons but no '[' and ']', + * assume this is an inet6 address without a service. + */ + if (name != NULL) { +#ifdef INET6 + if (name[0] == '[' && + (cp = strchr(name + 1, ']')) != NULL) { + name = &name[1]; + *cp = '\0'; + if (cp[1] == ':' && cp[2] != '\0') + serv = cp + 2; + } else { +#endif + cp = strchr(name, ':'); + if (cp != NULL && strchr(cp + 1, ':') == NULL) { + *cp = '\0'; + if (cp[1] != '\0') + serv = cp + 1; + if (cp == name) + name = NULL; + } +#ifdef INET6 + } +#endif + } + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_DGRAM; + hints.ai_flags = AI_PASSIVE; + if (name != NULL) + dprintf("Trying peer: %s\n", name); + if (serv == NULL) + serv = "syslog"; + error = getaddrinfo(name, serv, &hints, &res0); + if (error) { + asprintf(&msgbuf, "getaddrinfo failed for %s%s: %s", + name == NULL ? "" : name, serv, + gai_strerror(error)); + errno = 0; + if (msgbuf == NULL) + logerror(gai_strerror(error)); + else + logerror(msgbuf); + free(msgbuf); + die(0); + } + for (res = res0; res != NULL; res = res->ai_next) { + sl = socksetup(res, name, mode); + if (sl == NULL) + continue; + STAILQ_INSERT_TAIL(&shead, sl, next); + } + freeaddrinfo(res0); +} + +static void +addfile(int fd) +{ + struct socklist *sl = calloc(1, sizeof(*sl)); if (sl == NULL) err(1, "malloc failed"); - *sl = *sl0; - if (ai != NULL) { - memcpy(&sl->sl_ai, ai, sizeof(*ai)); - if (ai->ai_addrlen > 0) { - memcpy((sl + 1), ai->ai_addr, ai->ai_addrlen); - sl->sl_sa = (struct sockaddr *)(sl + 1); - } else - sl->sl_sa = NULL; - } + sl->sl_socket = fd; + sl->sl_recv = socklist_recv_file; STAILQ_INSERT_TAIL(&shead, sl, next); - - return (0); } int @@ -533,7 +587,6 @@ sigset_t sigset = { }; struct kevent ev; struct socklist *sl; - struct peer *pe; pid_t ppid = -1, spid; int ch, kq, s; char *p; @@ -562,7 +615,7 @@ send_to_all = true; break; case 'a': /* allow specific network addresses only */ - if (allowaddr(optarg) == -1) + if (!allowaddr(optarg)) usage(); break; case 'b': @@ -577,18 +630,12 @@ } if (p == NULL) { /* A hostname or filename only. */ - addpeer(&(struct peer){ - .pe_name = optarg, - .pe_serv = "syslog" - }); + addpeer(optarg, "syslog", 0); } else { /* The case of "name:service". */ *p++ = '\0'; - addpeer(&(struct peer){ - .pe_serv = p, - .pe_name = (strlen(optarg) == 0) ? - NULL : optarg, - }); + addpeer(strlen(optarg) == 0 ? NULL : optarg, + p, 0); } break; case 'c': @@ -649,10 +696,7 @@ } else errx(1, "invalid filename %s, exiting", optarg); - addpeer(&(struct peer){ - .pe_name = name, - .pe_mode = mode - }); + addpeer(name, NULL, mode); break; } case 'M': /* max length of forwarded message */ @@ -709,42 +753,39 @@ if (RFC3164OutputFormat && MaxForwardLen > 1024) errx(1, "RFC 3164 messages may not exceed 1024 bytes"); + pfh = pidfile_open(PidFile, 0600, &spid); + if (pfh == NULL) { + if (errno == EEXIST) + errx(1, "syslogd already running, pid: %d", spid); + warn("cannot open pid file"); + } + + /* + * Now that flags have been parsed, we know if we're in + * secure mode. Add peers to the socklist, if allowed. + */ + while (!STAILQ_EMPTY(&pqueue)) { + struct peer *pe = STAILQ_FIRST(&pqueue); + STAILQ_REMOVE_HEAD(&pqueue, next); + addsock(pe->pe_name, pe->pe_serv, pe->pe_mode); + free(pe); + } /* Listen by default: /dev/klog. */ s = open(_PATH_KLOG, O_RDONLY | O_NONBLOCK | O_CLOEXEC, 0); if (s < 0) { dprintf("can't open %s (%d)\n", _PATH_KLOG, errno); } else { - addsock(NULL, &(struct socklist){ - .sl_socket = s, - .sl_recv = socklist_recv_file, - }); + addfile(s); } /* Listen by default: *:514 if no -b flag. */ if (bflag == 0) - addpeer(&(struct peer){ - .pe_serv = "syslog" - }); + addsock(NULL, "syslog", 0); /* Listen by default: /var/run/log if no -p flag. */ if (pflag == 0) - addpeer(&(struct peer){ - .pe_name = _PATH_LOG, - .pe_mode = DEFFILEMODE, - }); + addsock(_PATH_LOG, NULL, DEFFILEMODE); /* Listen by default: /var/run/logpriv if no -S flag. */ if (Sflag == 0) - addpeer(&(struct peer){ - .pe_name = _PATH_LOG_PRIV, - .pe_mode = S_IRUSR | S_IWUSR, - }); - STAILQ_FOREACH(pe, &pqueue, next) - socksetup(pe); - - pfh = pidfile_open(PidFile, 0600, &spid); - if (pfh == NULL) { - if (errno == EEXIST) - errx(1, "syslogd already running, pid: %d", spid); - warn("cannot open pid file"); - } + addsock(_PATH_LOG_PRIV, NULL, S_IRUSR | S_IWUSR); if ((!Foreground) && (!Debug)) { ppid = waitdaemon(30); @@ -2312,7 +2353,7 @@ } STAILQ_FOREACH(sl, &shead, next) { if (sl->sl_sa != NULL && sl->sl_family == AF_LOCAL) - unlink(sl->sl_peer->pe_name); + unlink(sl->sl_name); } pidfile_remove(pfh); @@ -3272,9 +3313,9 @@ * * netaddr/maskbits[:{servicename|portnumber|*}] * - * Returns -1 on error, 0 if the argument was valid. + * Returns false on error, true if the argument was valid. */ -static int +static bool #if defined(INET) || defined(INET6) allowaddr(char *s) #else @@ -3442,13 +3483,13 @@ printf("port = %d\n", ap->port); } - return (0); + return (true); err: if (res != NULL) freeaddrinfo(res); free(ap); #endif - return (-1); + return (false); } /* @@ -3710,167 +3751,107 @@ logerror(buf); } -static int -socksetup(struct peer *pe) +static struct socklist * +socksetup(struct addrinfo *ai, const char *name, mode_t mode) { - struct addrinfo hints, *res, *res0; - int error; - char *cp; + struct socklist *sl; int (*sl_recv)(struct socklist *); - /* - * We have to handle this case for backwards compatibility: - * If there are two (or more) colons but no '[' and ']', - * assume this is an inet6 address without a service. - */ - if (pe->pe_name != NULL) { -#ifdef INET6 - if (pe->pe_name[0] == '[' && - (cp = strchr(pe->pe_name + 1, ']')) != NULL) { - pe->pe_name = &pe->pe_name[1]; - *cp = '\0'; - if (cp[1] == ':' && cp[2] != '\0') - pe->pe_serv = cp + 2; - } else { -#endif - cp = strchr(pe->pe_name, ':'); - if (cp != NULL && strchr(cp + 1, ':') == NULL) { - *cp = '\0'; - if (cp[1] != '\0') - pe->pe_serv = cp + 1; - if (cp == pe->pe_name) - pe->pe_name = NULL; - } -#ifdef INET6 - } -#endif - } - hints = (struct addrinfo){ - .ai_family = AF_UNSPEC, - .ai_socktype = SOCK_DGRAM, - .ai_flags = AI_PASSIVE - }; - if (pe->pe_name != NULL) - dprintf("Trying peer: %s\n", pe->pe_name); - if (pe->pe_serv == NULL) - pe->pe_serv = "syslog"; - error = getaddrinfo(pe->pe_name, pe->pe_serv, &hints, &res0); - if (error) { - char *msgbuf; + int s, optval = 1; - asprintf(&msgbuf, "getaddrinfo failed for %s%s: %s", - pe->pe_name == NULL ? "" : pe->pe_name, pe->pe_serv, - gai_strerror(error)); - errno = 0; - if (msgbuf == NULL) - logerror(gai_strerror(error)); - else - logerror(msgbuf); - free(msgbuf); - die(0); + if (ai->ai_family != AF_LOCAL && SecureMode > 1) { + /* Only AF_LOCAL in secure mode. */ + return (NULL); } - for (res = res0; res != NULL; res = res->ai_next) { - int s; - - if (res->ai_family != AF_LOCAL && - SecureMode > 1) { - /* Only AF_LOCAL in secure mode. */ - continue; - } - if (family != AF_UNSPEC && - res->ai_family != AF_LOCAL && res->ai_family != family) - continue; + if (family != AF_UNSPEC && ai->ai_family != AF_LOCAL && + ai->ai_family != family) + return (NULL); - s = socket(res->ai_family, res->ai_socktype, - res->ai_protocol); - if (s < 0) { - logerror("socket"); - error++; - continue; - } + s = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); + if (s < 0) { + logerror("socket"); + return (NULL); + } #ifdef INET6 - if (res->ai_family == AF_INET6) { - if (setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY, - &(int){1}, sizeof(int)) < 0) { - logerror("setsockopt(IPV6_V6ONLY)"); - close(s); - error++; - continue; - } - } -#endif - if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, - &(int){1}, sizeof(int)) < 0) { - logerror("setsockopt(SO_REUSEADDR)"); + if (ai->ai_family == AF_INET6) { + if (setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY, &optval, + sizeof(int)) < 0) { + logerror("setsockopt(IPV6_V6ONLY)"); close(s); - error++; - continue; + return (NULL); } + } +#endif + if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &optval, + sizeof(int)) < 0) { + logerror("setsockopt(SO_REUSEADDR)"); + close(s); + return (NULL); + } - /* - * Bind INET and UNIX-domain sockets. - * - * A UNIX-domain socket is always bound to a pathname - * regardless of -N flag. - * - * For INET sockets, RFC 3164 recommends that client - * side message should come from the privileged syslogd port. - * - * If the system administrator chooses not to obey - * this, we can skip the bind() step so that the - * system will choose a port for us. - */ - if (res->ai_family == AF_LOCAL) - unlink(pe->pe_name); - if (res->ai_family == AF_LOCAL || - NoBind == 0 || pe->pe_name != NULL) { - if (bind(s, res->ai_addr, res->ai_addrlen) < 0) { - logerror("bind"); - close(s); - error++; - continue; - } - if (res->ai_family == AF_LOCAL || - SecureMode == 0) - increase_rcvbuf(s); - } - if (res->ai_family == AF_LOCAL && - chmod(pe->pe_name, pe->pe_mode) < 0) { - dprintf("chmod %s: %s\n", pe->pe_name, - strerror(errno)); + /* + * Bind INET and UNIX-domain sockets. + * + * A UNIX-domain socket is always bound to a pathname + * regardless of -N flag. + * + * For INET sockets, RFC 3164 recommends that client + * side message should come from the privileged syslogd port. + * + * If the system administrator chooses not to obey + * this, we can skip the bind() step so that the + * system will choose a port for us. + */ + if (ai->ai_family == AF_LOCAL) + unlink(name); + if (ai->ai_family == AF_LOCAL || NoBind == 0 || name != NULL) { + if (bind(s, ai->ai_addr, ai->ai_addrlen) < 0) { + logerror("bind"); close(s); - error++; - continue; - } - dprintf("new socket fd is %d\n", s); - if (res->ai_socktype != SOCK_DGRAM) { - listen(s, 5); + return (NULL); } - sl_recv = socklist_recv_sock; + if (ai->ai_family == AF_LOCAL || SecureMode == 0) + increase_rcvbuf(s); + } + if (ai->ai_family == AF_LOCAL && chmod(name, mode) < 0) { + dprintf("chmod %s: %s\n", name, strerror(errno)); + close(s); + return (NULL); + } + dprintf("new socket fd is %d\n", s); + if (ai->ai_socktype != SOCK_DGRAM) { + listen(s, 5); + } + sl_recv = socklist_recv_sock; #if defined(INET) || defined(INET6) - if (SecureMode && (res->ai_family == AF_INET || - res->ai_family == AF_INET6)) { - dprintf("shutdown\n"); - /* Forbid communication in secure mode. */ - if (shutdown(s, SHUT_RD) < 0 && - errno != ENOTCONN) { - logerror("shutdown"); - if (!Debug) - die(0); - } - sl_recv = NULL; - } else + if (SecureMode && (ai->ai_family == AF_INET || + ai->ai_family == AF_INET6)) { + dprintf("shutdown\n"); + /* Forbid communication in secure mode. */ + if (shutdown(s, SHUT_RD) < 0 && errno != ENOTCONN) { + logerror("shutdown"); + if (!Debug) + die(0); + } + sl_recv = NULL; + } else #endif - dprintf("listening on socket\n"); - dprintf("sending on socket\n"); - addsock(res, &(struct socklist){ - .sl_socket = s, - .sl_peer = pe, - .sl_recv = sl_recv - }); + dprintf("listening on socket\n"); + dprintf("sending on socket\n"); + /* Copy *ai->ai_addr to the tail of struct socklist if any. */ + sl = calloc(1, sizeof(*sl) + ai->ai_addrlen); + if (sl == NULL) + err(1, "malloc failed"); + sl->sl_socket = s; + sl->sl_name = name; + sl->sl_recv = sl_recv; + (void)memcpy(&sl->sl_ai, ai, sizeof(*ai)); + if (ai->ai_addrlen > 0) { + (void)memcpy((sl + 1), ai->ai_addr, ai->ai_addrlen); + sl->sl_sa = (struct sockaddr *)(sl + 1); + } else { + sl->sl_sa = NULL; } - freeaddrinfo(res0); - - return(error); + return (sl); } static void