📄 linetd.c
字号:
#include <stdio.h>#include <stdlib.h>#include <string.h>#include <unistd.h>#include <stdarg.h>#include <ctype.h>#include <errno.h>#include <limits.h>#include <pwd.h>#include <grp.h>#include <sched.h>#include <signal.h>#include <netdb.h>#include <sysexits.h>#include <fcntl.h>#include <sys/wait.h>#include <sys/types.h>#include <sys/stat.h>#include <sys/socket.h>#include <sys/time.h>#include <sys/resource.h>#include <arpa/inet.h>#include <netinet/in.h>#include <netinet/ip.h>#ifdef USE_IDSA#include <idsa_internal.h>/* The below requires idsa-0.93.8 or newer *//* linetd uses both the IDSA_SSM and IDSA_ES schemes */#include <idsa_schemes.h>/* names defined for the linetd scheme */#define LINET_EU "error-usage"#define LINET_ES "error-system"#define LINET_EI "error-init"#define LINET_EF "error-fork"#define LINET_CC "client-connect"#define LINET_DR "daemon-ready"#define LINET_DE "daemon-exit"#define LINET_JD "job-done"#define LINET_JE "job-error"#define LINET_JS "job-signal"#endif#define LINETD "linetd"#define LINET_SCHEME LINETD#define LINET_SERVICE LINETD#define LINET_TCP "tcp"#define LINET_USAGE 1#define LINET_SYSTEM 2#define LINET_BUFFER 512#define LINET_SLEEP 5#define LINET_MAXRES 16#define LINET_READBUF 32#define LOADAVG "/proc/loadavg"#ifndef VERSION#define VERSION "unknown"#endifvolatile int run = 1;volatile int zombies = 0;int child_count = 0;int load_fd = (-1);int resource_table[LINET_MAXRES][2];int resource_count = 0;#ifdef USE_IDSAIDSA_CONNECTION *ic = NULL;#endifstatic void handle_child(int s){ zombies = 1;}static void handle_stop(int s){ run = 0;}static double get_load(){ char buffer[LINET_READBUF]; if(lseek(load_fd,0,SEEK_SET)){ return 0.0; } if(read(load_fd,buffer,LINET_READBUF) <= 0){ return 0.0; } buffer[LINET_READBUF-1]='\0'; return atof(buffer);}static void fatal_failure(int type, int error, char *message, ...){ va_list args; char buffer[LINET_BUFFER]; int exitcode; exitcode = EX_UNAVAILABLE;#ifdef USE_IDSA if (ic == NULL) { ic = idsa_open(LINET_SERVICE, NULL, 0); }#endif va_start(args, message); vsnprintf(buffer, LINET_BUFFER - 1, message, args); buffer[LINET_BUFFER - 1] = '\0'; va_end(args); switch (type) { case LINET_USAGE:#ifdef USE_IDSA idsa_set(ic, LINET_EU, LINET_SCHEME, 0, IDSA_R_TOTAL, IDSA_R_NONE, IDSA_R_UNKNOWN, IDSA_SSM, IDSA_T_STRING, IDSA_SSM_SFAIL, IDSA_ES, IDSA_T_STRING, IDSA_ES_USAGE, "comment", IDSA_T_STRING, buffer, NULL);#endif fprintf(stderr, "%s: %s\n", LINETD, buffer); exitcode = EX_USAGE; break; case LINET_SYSTEM:#ifdef USE_IDSA idsa_set(ic, LINET_ES, LINET_SCHEME, 0, IDSA_R_TOTAL, IDSA_R_NONE, IDSA_R_UNKNOWN, IDSA_SSM, IDSA_T_STRING, IDSA_SSM_SFAIL, IDSA_ES, IDSA_T_STRING, IDSA_ES_SYSTEM, "comment", IDSA_T_STRING, buffer, "code", IDSA_T_ERRNO, &error, NULL);#endif fprintf(stderr, "%s: %s: %s\n", LINETD, buffer, strerror(error)); exitcode = EX_OSERR; break; }#ifdef USE_IDSA if (ic) { idsa_close(ic); ic = NULL; }#endif exit(exitcode);}static void fork_parent(char *name){ int p[2]; pid_t pid; char buffer[LINET_BUFFER]; /* int kfd, maxfd; */ int rr, status, result; if (pipe(p)) { fatal_failure(LINET_SYSTEM, errno, "unable to create pipe"); } fflush(stderr); pid = fork(); switch (pid) { case -1: fatal_failure(LINET_SYSTEM, errno, "unable to fork"); break; case 0: /* in child - make pipe stderr and detach from terminal */ close(p[0]); if (dup2(p[1], STDERR_FILENO) != STDERR_FILENO) { fatal_failure(LINET_SYSTEM, errno, "unable to duplicate stdandard error file descriptor"); } close(p[1]); close(STDOUT_FILENO); close(STDIN_FILENO); /* ugly, double edged sword */ /* maxfd = getdtablesize(); for (kfd = STDERR_FILENO + 1; kfd < maxfd; kfd++) { close(kfd); } */ setsid(); break; default: /* in parent - read from pipe, exit when pipe closes */ close(p[1]); do { rr = read(p[0], buffer, LINET_BUFFER); switch (rr) { case -1: switch (errno) { case EAGAIN: case EINTR: rr = 1; break; default: fprintf(stderr, "%s: unable to read child messages: %s\n", name, strerror(errno)); fflush(stderr); break; } break; case 0: /* eof */ break; default: write(STDERR_FILENO, buffer, rr); /* don't care if write fails, can't do anything about it */ break; } } while (rr > 0); sched_yield(); result = 0; if (waitpid(pid, &status, WNOHANG) > 0) { /* got a child */ result = EX_SOFTWARE; if (WIFEXITED(status)) { result = WEXITSTATUS(status); snprintf(buffer, LINET_BUFFER, "exited with code %d", result); } else if (WIFSIGNALED(status)) { snprintf(buffer, LINET_BUFFER, "killed by signal %d\n", WTERMSIG(status)); } else { snprintf(buffer, LINET_BUFFER, "unknown exit condition"); } buffer[LINET_BUFFER - 1] = '\0';#ifdef USE_IDSA idsa_set(ic, LINET_EI, LINET_SCHEME, 0, IDSA_R_PARTIAL, IDSA_R_UNKNOWN, IDSA_R_PARTIAL, IDSA_SSM, IDSA_T_STRING, IDSA_SSM_SFAIL, IDSA_ES, IDSA_T_STRING, IDSA_ES_OTHER, "comment", IDSA_T_STRING, buffer, NULL);#endif fprintf(stderr, "%s: %s\n", LINETD, buffer); fflush(stderr); } /* else child probably ok */ exit(result); break; }}static void drop_root(char *name, char *user, char *group, char *root){ uid_t uid = 0; gid_t gid = 0; struct passwd *pw; struct group *gr; if (user) { uid = atoi(user); if (uid == 0) { pw = getpwnam(user); if (pw == NULL) { fatal_failure(LINET_SYSTEM, errno, "unable to find user %s", user); } else { uid = pw->pw_uid; gid = pw->pw_gid; } } } if (group) { gid = atoi(group); if (gid == 0) { gr = getgrnam(group); if (gr == NULL) { fatal_failure(LINET_SYSTEM, errno, "unable to find group %s", group); } else { gid = gr->gr_gid; } } } if (root != NULL) { /* do chroot */ if (chroot(root)) { fatal_failure(LINET_SYSTEM, errno, "unable to change root directory to %s", root); } } chdir("/"); if(getuid()==0){ if(setgroups(0,NULL)){ fatal_failure(LINET_SYSTEM, errno, "unable to delete supplementary groups"); } } if (group || user) { if (setgid(gid)) { fatal_failure(LINET_SYSTEM, errno, "unable to change gid to %lu", (unsigned long) gid); } } if (user) { /* now change id */ if (setuid(uid)) { fatal_failure(LINET_SYSTEM, errno, "unable to change uid to %lu", (unsigned long) uid); } }}static void usage(char *name){ printf( "Usage: %s [-cdfhv] "#ifdef USE_IDSA "[-k[a|c|i] risk] [-x[e|o|u]] "#endif "[-a[k|r]] [-b address] [-u user] [-g group] [-i instances] [-l load] [-m timeout] [-n nice] [-o[c|d|r|t]] -p port [-q backlog] [-r directory] [-s[c|d|f|l|m|n|s|t|u|v] limit] [-t ttl] path [options ...]\n\n" , name); printf("Options:\n" "-a[k|r] disable k)eepalive or address r)euse socket option\n" "-b address bind a particular address instead of all available ones\n" "-d disable sanity checks\n" "-f do not fork into the background\n" "-g gid run with group id\n" "-h this help\n" "-i integer maximum number of child instances created\n"#ifdef USE_IDSA "-k? risk risk values: a)vailability, c)onfidentiality, i)ntegrity\n"#endif "-l double load average above which service is disabled\n" "-m timeout schedule an alarm signal for each child\n" "-n level run at reduced priority\n" "-o? type of service [tos]: optimal d)elay, r)eliability or t)hroughput\n" "-p port port to listen on [required]\n" "-q backlog number of connections queued in listen\n" "-r directory chroot into directory\n" "-s? value resource limit set for child\n" "-t integer time to live [ttl] counter\n" "-u uid run with user id\n" "-v print version and exit\n"#ifdef USE_IDSA "-x? enable flag: honour e)nvironment variable IDSA_SOCKET, fail o)pen, allow u)ploading of rules\n"#endif "\n" ); printf("Examples:\n"); printf("\n%s -u nobody -or -i 5 -m 5 -p finger /usr/sbin/in.fingerd in.fingerd -l\n", name); printf( " Run fingerd as nobody.\n" " Maximize reliability of the TCP/IP connection.\n" " Start at most 5 fingerd instances and send a SIG_ALARM after 5 seconds.\n" ); printf("\n%s -u nobody -p 2300 -t 1 /usr/bin/tail tail /var/log/apache/access_log\n", name); printf( " Display the last lines of a log file to those connecting on port 2300.\n" " Set TTL to 1 which makes it inaccessible to users outside the subnet.\n" );#ifdef USE_IDSA printf("\nIDSA_SOCKET=/tmp/idsa %s -i1 -unobody -n5 -l2.0 -m10 -su0 -sm4096 -xexo -kc0.7/0.3 -ki0.2/0.3 -pnetstat /bin/netstat netstat -tn\n", name); printf( " Run at most one instance of netstat as nobody at nice level 5.\n" " Stop this service if the system load is greater than 2.0.\n" " Schedule a SIG_ALARM if netstat hasn't completed after 10 seconds.\n" " Do not allow netstat to spawn a subprocess or occupy more than 4096k of RAM.\n" " Connect to idsad listening on socket /tmp/idsa but continue if no idsad is available.\n" " Reporting each new connection as having a high (0.7) risk to confidentiality but a low (0.2) risk to integrity.\n" );#endif}static int setup_listener(char *name, char *port, char *interface, int queue, int ttl, int tos, int reuse, int keepalive){ int fd; struct sockaddr_in addr; int addrlen; int prt; struct hostent *hst; struct servent *srv; addr.sin_family = AF_INET; if (port == NULL) { fatal_failure(LINET_USAGE, 0, "require a port to bind"); } prt = atoi(port); if (prt == 0) { srv = getservbyname(port, LINET_TCP); if (srv == NULL) { fatal_failure(LINET_USAGE, 0, "could not convert %s to a nonzero number", port); } addr.sin_port = srv->s_port; } else { addr.sin_port = htons(prt); } if (interface) { if (inet_aton(interface, &(addr.sin_addr)) == 0) { hst = gethostbyname(interface); if (hst == NULL) { fatal_failure(LINET_USAGE, 0, "could not convert %s to an address", interface); } if (hst->h_addrtype != AF_INET) { fatal_failure(LINET_USAGE, 0, "%s does not resolve to an ip4 address", interface); } addr.sin_addr = *(struct in_addr *) hst->h_addr; } } else { addr.sin_addr.s_addr = htonl(INADDR_ANY); } fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); if (fd < 0) { fatal_failure(LINET_SYSTEM, errno, "could not create an internet socket"); } if(reuse){ setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (void *)&reuse, sizeof(int)); } if(keepalive){ setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, (void *)&keepalive, sizeof(int)); } addrlen = sizeof(addr); if (bind(fd, (struct sockaddr *) &addr, addrlen) == (-1)) { fatal_failure(LINET_SYSTEM, errno, "could not bind an internet socket"); } if (listen(fd, queue) == (-1)) { fatal_failure(LINET_SYSTEM, errno, "could not listen on socket"); } if(ttl) { if (setsockopt(fd, SOL_IP, IP_TTL, (void *)&ttl, sizeof(int))) { fatal_failure(LINET_SYSTEM, errno, "could not set time to live to %d hops", ttl); } } if(tos) { if (setsockopt(fd, SOL_IP, IP_TOS, (void *)&tos, sizeof(int))) { fatal_failure(LINET_SYSTEM, errno, "could not set type of service to 0x%02x", tos); } } return fd;}static int run_command(int lfd, char *cmd, char **vector, int fd, int timeout){ struct sigaction sag; sigset_t sst; struct rlimit r; int i;#ifdef USE_IDSA int error;#endif switch (fork()) { case -1:#ifdef USE_IDSA error = errno; idsa_set(ic, LINET_EF, LINET_SCHEME, 0, IDSA_R_PARTIAL, IDSA_R_NONE, IDSA_R_PARTIAL, IDSA_SSM, IDSA_T_STRING, IDSA_SSM_WFAIL, IDSA_ES, IDSA_T_STRING, IDSA_ES_SYSTEM, "comment", IDSA_T_STRING, "unable to create child process", "code", IDSA_T_ERRNO, &error, NULL);#endif close(fd); return -1; break; case 0: close(lfd); if(load_fd>=0){ close(load_fd); }#ifdef USE_IDSA idsa_close(ic);#endif sag.sa_handler = SIG_DFL; sigemptyset(&(sag.sa_mask)); sag.sa_flags = SA_RESTART; /* is this the correct way of resetting signal handlers ? */ sigaction(SIGCHLD, &sag, NULL); sigaction(SIGTERM, &sag, NULL); /* I haven't touched these, but it would seem important */ sigaction(SIGALRM, &sag, NULL); sigaction(SIGPIPE, &sag, NULL); sigemptyset(&sst); sigaddset(&sst, SIGCHLD); sigaddset(&sst, SIGTERM); sigprocmask(SIG_UNBLOCK, &sst, NULL); /* enable child signal */ if ((fd != STDIN_FILENO) && (dup2(fd, STDIN_FILENO) != STDIN_FILENO)) { exit(EX_OSERR); } if ((fd != STDOUT_FILENO) && (dup2(fd, STDOUT_FILENO) != STDOUT_FILENO)) { exit(EX_OSERR); } if ((fd != STDERR_FILENO) && (dup2(fd, STDERR_FILENO) != STDERR_FILENO)) { exit(EX_OSERR); } if (fd > STDERR_FILENO) { close(fd); } for(i = 0; i < resource_count; i++){ r.rlim_cur = resource_table[i][1]; r.rlim_max = resource_table[i][1]; if (setrlimit(resource_table[i][0], &r)) { exit(EX_OSERR); } } if (timeout) { alarm(timeout); } execv(cmd, vector);#ifndef PARANOID fprintf(stderr, "unable to run %s: %s\n", vector[0], strerror(errno));#endif exit(EX_UNAVAILABLE); break; default: child_count++; close(fd); return 0; break; } return 0;}#ifdef USE_IDSAstatic int accept_connection(int lfd, unsigned ar, unsigned cr, unsigned ir)#elsestatic int accept_connection(int lfd)#endif{ struct sockaddr_in addr; int addrlen; int nfd; sigset_t sst;#ifdef USE_IDSA int len; int cp[2], sp[2]; unsigned long ca, sa; int ttl; int complete; char *reason;#endif sigemptyset(&sst); sigaddset(&sst, SIGCHLD); sigaddset(&sst, SIGTERM); addrlen = sizeof(struct sockaddr_in); sigprocmask(SIG_UNBLOCK, &sst, NULL); /* enable child signal */ nfd = accept(lfd, (struct sockaddr *) &addr, &addrlen); sigprocmask(SIG_BLOCK, &sst, NULL); /* disable child signal */ if (nfd < 0) { return -1;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -