Ejemplo n.º 1
0
void
freeruleset(Ruleset *rs)
{
	freerules(rs->pat);
	free(rs->pat);
	freerules(rs->act);
	free(rs->act);
	free(rs->port);
	free(rs);
}
Ejemplo n.º 2
0
void
initrules(Bool reload)
{
	int i;
	char t[64];
	const char *tmp;

	freerules();
	rules = ecalloc(64, sizeof(Rule *));
	for (i = 0; i < 64; i++) {
		snprintf(t, sizeof(t), "rule%d", i);
		tmp = getresource(t, NULL);
		if (!tmp)
			continue;
		rules[nrules] = emallocz(sizeof(Rule));
		parserule(tmp, rules[nrules]);
		nrules++;
	}
	// rules = erealloc(rules, nrules * sizeof(Rule *));
	compileregs();
}
Ejemplo n.º 3
0
Archivo: tftpd.c Proyecto: 274914765/C
int main (int argc, char **argv)
{
    struct tftphdr *tp;

    struct passwd *pw;

    struct options *opt;

    struct sockaddr_in myaddr;

    struct sockaddr_in bindaddr;

    int n;

    int fd = 0;

    int standalone = 0;            /* Standalone (listen) mode */

    char *address = NULL;        /* Address to listen to */

    pid_t pid;

    mode_t my_umask = 0;

    int spec_umask = 0;

    int c;

    int setrv;

    int waittime = 900;            /* Default time to wait for a connect */

    const char *user = "******";    /* Default user */

    char *p, *ep;

#ifdef WITH_REGEX
    char *rewrite_file = NULL;
#endif

    /* basename() is way too much of a pain from a portability standpoint */

    p = strrchr (argv[0], '/');
    __progname = (p && p[1]) ? p + 1 : argv[0];

    openlog (__progname, LOG_PID | LOG_NDELAY, LOG_DAEMON);

    while ((c = getopt (argc, argv, "cspvVla:u:U:r:t:m:")) != -1)
        switch (c)
        {
        case 'c':
            cancreate = 1;
            break;
        case 's':
            secure = 1;
            break;
        case 'p':
            unixperms = 1;
            break;
        case 'l':
            standalone = 1;
            break;
        case 'a':
            address = optarg;
            break;
        case 't':
            waittime = atoi (optarg);
            break;
        case 'u':
            user = optarg;
            break;
        case 'U':
            my_umask = strtoul (optarg, &ep, 8);
            if (*ep)
            {
                syslog (LOG_ERR, "Invalid umask: %s", optarg);
                exit (EX_USAGE);
            }
            spec_umask = 1;
            break;
        case 'r':
            for (opt = options; opt->o_opt; opt++)
            {
                if (!strcasecmp (optarg, opt->o_opt))
                {
                    opt->o_opt = "";    /* Don't support this option */
                    break;
                }
            }
            if (!opt->o_opt)
            {
                syslog (LOG_ERR, "Unknown option: %s", optarg);
                exit (EX_USAGE);
            }
            break;
#ifdef WITH_REGEX
        case 'm':
            if (rewrite_file)
            {
                syslog (LOG_ERR, "Multiple -m options");
                exit (EX_USAGE);
            }
            rewrite_file = optarg;
            break;
#endif
        case 'v':
            verbosity++;
            break;
        case 'V':
            /* Print configuration to stdout and exit */
            printf ("%s\n", TFTPD_CONFIG_STR);
            exit (0);
            break;
        default:
            usage ();
            break;
        }

    dirs = xmalloc ((argc - optind + 1) * sizeof (char *));
    for (ndirs = 0; optind != argc; optind++)
        dirs[ndirs++] = argv[optind];

    dirs[ndirs] = NULL;

    if (secure)
    {
        if (ndirs == 0)
        {
            syslog (LOG_ERR, "no -s directory");
            exit (EX_USAGE);
        }
        if (ndirs > 1)
        {
            syslog (LOG_ERR, "too many -s directories");
            exit (EX_USAGE);
        }
        if (chdir (dirs[0]))
        {
            syslog (LOG_ERR, "%s: %m", dirs[0]);
            exit (EX_NOINPUT);
        }
    }

    pw = getpwnam (user);
    if (!pw)
    {
        syslog (LOG_ERR, "no user %s: %m", user);
        exit (EX_NOUSER);
    }

    if (spec_umask || !unixperms)
        umask (my_umask);

    /* Note: on Cygwin, select() on a nonblocking socket becomes
       a nonblocking select. */
#ifndef __CYGWIN__
    set_socket_nonblock (fd, 1);
#endif

#ifdef WITH_REGEX
    if (rewrite_file)
        rewrite_rules = read_remap_rules (rewrite_file);
#endif

    /* If we're running standalone, set up the input port */
    if (standalone)
    {
        fd = socket (PF_INET, SOCK_DGRAM, 0);

        memset (&bindaddr, 0, sizeof bindaddr);
        bindaddr.sin_family = AF_INET;
        bindaddr.sin_addr.s_addr = INADDR_ANY;
        bindaddr.sin_port = htons (IPPORT_TFTP);

        if (address)
        {
            char *portptr, *eportptr;

            struct hostent *hostent;

            struct servent *servent;

            unsigned long port;

            address = tfstrdup (address);
            portptr = strrchr (address, ':');
            if (portptr)
                *portptr++ = '\0';

            if (*address)
            {
                hostent = gethostbyname (address);
                if (!hostent || hostent->h_addrtype != AF_INET)
                {
                    syslog (LOG_ERR, "cannot resolve local bind address: %s", address);
                    exit (EX_NOINPUT);
                }
                memcpy (&bindaddr.sin_addr, hostent->h_addr, hostent->h_length);
            }
            else
            {
                /* Default to using INADDR_ANY */
            }

            if (portptr && *portptr)
            {
                servent = getservbyname (portptr, "udp");
                if (servent)
                {
                    bindaddr.sin_port = servent->s_port;
                }
                else if ((port = strtoul (portptr, &eportptr, 0)) && !*eportptr)
                {
                    bindaddr.sin_port = htons (port);
                }
                else if (!strcmp (portptr, "tftp"))
                {
                    /* It's TFTP, we're OK */
                }
                else
                {
                    syslog (LOG_ERR, "cannot resolve local bind port: %s", portptr);
                    exit (EX_NOINPUT);
                }
            }
        }

        if (bind (fd, (struct sockaddr *) &bindaddr, sizeof bindaddr) < 0)
        {
            syslog (LOG_ERR, "cannot bind to local socket: %m");
            exit (EX_OSERR);
        }

        /* Daemonize this process */
        {
            pid_t f = fork ();

            int nfd;

            if (f > 0)
                exit (0);
            if (f < 0)
            {
                syslog (LOG_ERR, "cannot fork: %m");
                exit (EX_OSERR);
            }
            nfd = open ("/dev/null", O_RDWR);
            if (nfd >= 0)
            {
#ifdef HAVE_DUP2
                dup2 (nfd, 0);
                dup2 (nfd, 1);
                dup2 (nfd, 2);
#else
                close (0);
                dup (nfd);
                close (1);
                dup (nfd);
                close (2);
                dup (nfd);
#endif
                close (nfd);
            }
            else
            {
                close (0);
                close (1);
                close (2);
            }
#ifdef HAVE_SETSID
#ifndef __CYGWIN__                /* Kills the process on Cygwin? */
            setsid ();
#endif
#endif
        }
    }
    else
    {
        /* 0 is our socket descriptor */
        close (1);
        close (2);
    }

    /* This means we don't want to wait() for children */
#ifdef SA_NOCLDWAIT
    set_signal (SIGCHLD, SIG_IGN, SA_NOCLDSTOP | SA_NOCLDWAIT);
#else
    set_signal (SIGCHLD, SIG_IGN, SA_NOCLDSTOP);
#endif

    /* Take SIGHUP and use it to set a variable.  This
       is polled synchronously to make sure we don't
       lose packets as a result. */
    set_signal (SIGHUP, handle_sighup, 0);

    while (1)
    {
        fd_set readset;

        struct timeval tv_waittime;

        int rv;

        if (caught_sighup)
        {
            caught_sighup = 0;
            if (standalone)
            {
#ifdef HAVE_REGEX
                if (rewrite_file)
                {
                    freerules (rewrite_rules);
                    rewrite_rules = read_remap_rules (rewrite_file);
                }
#endif
            }
            else
            {
                /* Return to inetd for respawn */
                exit (0);
            }
        }

        FD_ZERO (&readset);
        FD_SET (fd, &readset);
        tv_waittime.tv_sec = waittime;
        tv_waittime.tv_usec = 0;

#ifdef __CYGWIN__
        /* On Cygwin, select() on a nonblocking socket returns immediately,
           with a rv of 0! */
        set_socket_nonblock (fd, 0);
#endif

        /* Never time out if we're in standalone mode */
        rv = select (fd + 1, &readset, NULL, NULL, standalone ? NULL : &tv_waittime);
        if (rv == -1 && errno == EINTR)
            continue;            /* Signal caught, reloop */
        if (rv == -1)
        {
            syslog (LOG_ERR, "select loop: %m");
            exit (EX_IOERR);
        }
        else if (rv == 0)
        {
            exit (0);            /* Timeout, return to inetd */
        }

#ifdef __CYGWIN__
        set_socket_nonblock (fd, 1);
#endif

        fromlen = sizeof (from);
        n = myrecvfrom (fd, buf, sizeof (buf), 0, (struct sockaddr *) &from, &fromlen, &myaddr);

        if (n < 0)
        {
            if (E_WOULD_BLOCK (errno) || errno == EINTR)
            {
                continue;        /* Again, from the top */
            }
            else
            {
                syslog (LOG_ERR, "recvfrom: %m");
                exit (EX_IOERR);
            }
        }

        if (standalone && myaddr.sin_addr.s_addr == INADDR_ANY)
        {
            /* myrecvfrom() didn't capture the source address; but we might
               have bound to a specific address, if so we should use it */
            memcpy (&myaddr.sin_addr, &bindaddr.sin_addr, sizeof bindaddr.sin_addr);
        }

        /*
         * Now that we have read the request packet from the UDP
         * socket, we fork and go back to listening to the socket.
         */
        pid = fork ();
        if (pid < 0)
        {
            syslog (LOG_ERR, "fork: %m");
            exit (EX_OSERR);    /* Return to inetd, just in case */
        }
        else if (pid == 0)
            break;                /* Child exit, parent loop */
    }

    /* Child process: handle the actual request here */

    /* Ignore SIGHUP */
    set_signal (SIGHUP, SIG_IGN, 0);

#ifdef HAVE_TCPWRAPPERS
    /* Verify if this was a legal request for us.  This has to be
       done before the chroot, while /etc is still accessible. */
    request_init (&wrap_request, RQ_DAEMON, __progname, RQ_FILE, fd, RQ_CLIENT_SIN, &from, RQ_SERVER_SIN, &myaddr, 0);
    sock_methods (&wrap_request);
    if (hosts_access (&wrap_request) == 0)
    {
        if (deny_severity != -1)
            syslog (deny_severity, "connection refused from %s", inet_ntoa (from.sin_addr));
        exit (EX_NOPERM);        /* Access denied */
    }
    else if (allow_severity != -1)
    {
        syslog (allow_severity, "connect from %s", inet_ntoa (from.sin_addr));
    }
#endif

    /* Close file descriptors we don't need */
    close (fd);

    /* Get a socket.  This has to be done before the chroot(), since
       some systems require access to /dev to create a socket. */

    peer = socket (AF_INET, SOCK_DGRAM, 0);
    if (peer < 0)
    {
        syslog (LOG_ERR, "socket: %m");
        exit (EX_IOERR);
    }

    /* Set up the supplementary group access list if possible */
    /* /etc/group still need to be accessible at this point */
#ifdef HAVE_INITGROUPS
    setrv = initgroups (user, pw->pw_gid);
    if (setrv)
    {
        syslog (LOG_ERR, "cannot set groups for user %s", user);
        exit (EX_OSERR);
    }
#else
#ifdef HAVE_SETGROUPS
    if (setgroups (0, NULL))
    {
        syslog (LOG_ERR, "cannot clear group list");
    }
#endif
#endif

    /* Chroot and drop privileges */
    if (secure)
    {
        if (chroot ("."))
        {
            syslog (LOG_ERR, "chroot: %m");
            exit (EX_OSERR);
        }
#ifdef __CYGWIN__
        chdir ("/");            /* Cygwin chroot() bug workaround */
#endif
    }

#ifdef HAVE_SETREGID
    setrv = setregid (pw->pw_gid, pw->pw_gid);
#else
    setrv = setegid (pw->pw_gid) || setgid (pw->pw_gid);
#endif

#ifdef HAVE_SETREUID
    setrv = setrv || setreuid (pw->pw_uid, pw->pw_uid);
#else
    /* Important: setuid() must come first */
    setrv = setrv || setuid (pw->pw_uid) || (geteuid () != pw->pw_uid && seteuid (pw->pw_uid));
#endif

    if (setrv)
    {
        syslog (LOG_ERR, "cannot drop privileges: %m");
        exit (EX_OSERR);
    }

    /* Other basic setup */
    from.sin_family = AF_INET;

    /* Process the request... */

    myaddr.sin_port = htons (0);    /* We want a new local port */
    if (bind (peer, (struct sockaddr *) &myaddr, sizeof myaddr) < 0)
    {
        syslog (LOG_ERR, "bind: %m");
        exit (EX_IOERR);
    }
    if (connect (peer, (struct sockaddr *) &from, sizeof from) < 0)
    {
        syslog (LOG_ERR, "connect: %m");
        exit (EX_IOERR);
    }
    tp = (struct tftphdr *) buf;
    tp->th_opcode = ntohs (tp->th_opcode);
    if (tp->th_opcode == RRQ || tp->th_opcode == WRQ)
        tftp (tp, n);
    exit (0);
}