Пример #1
0
Файл: at.c Проект: Akheon23/at
static void
writefile(time_t runtimer, char queue)
{
/* This does most of the work if at or batch are invoked for writing a job.
 */
    long jobno;
    char *ap, *ppos, *mailname;
    struct passwd *pass_entry;
    struct stat statbuf;
    int fd, lockdes, fd2;
    FILE *fp, *fpin;
    struct sigaction act;
    char **atenv;
    int ch;
    mode_t cmask;
    struct flock lock;
    struct tm *runtime;
    char timestr[TIMESIZE];
    pid_t pid;
    int istty;
    int kill_errno;
    int rc;
    int mailsize = 128;

/* Install the signal handler for SIGINT; terminate after removing the
 * spool file if necessary
 */
    memset(&act, 0, sizeof act);
    act.sa_handler = sigc;
    sigemptyset(&(act.sa_mask));
    act.sa_flags = 0;

    sigaction(SIGINT, &act, NULL);

    ppos = atfile + strlen(ATJOB_DIR) + 1;

#ifdef _SC_LOGIN_NAME_MAX
    errno = 0;
    rc = sysconf(_SC_LOGIN_NAME_MAX);
    if (rc > 0)
	mailsize = rc;
#else
#  ifdef LOGIN_NAME_MAX
    mailsize = LOGIN_NAME_MAX;
#  endif
#endif
    /* Loop over all possible file names for running something at this
     * particular time, see if a file is there; the first empty slot at any
     * particular time is used.  Lock the file LFILE first to make sure
     * we're alone when doing this.
     */

    PRIV_START

	if ((lockdes = open(LFILE, O_WRONLY)) < 0)
	    perr("Cannot open lockfile " LFILE);

	lock.l_type = F_WRLCK;
	lock.l_whence = SEEK_SET;
	lock.l_start = 0;
	lock.l_len = 0;

	act.sa_handler = alarmc;
	sigemptyset(&(act.sa_mask));
	act.sa_flags = 0;

	/* Set an alarm so a timeout occurs after ALARMC seconds, in case
	 * something is seriously broken.
	 */
	sigaction(SIGALRM, &act, NULL);
	alarm(ALARMC);
	fcntl(lockdes, F_SETLKW, &lock);
	alarm(0);

	if ((jobno = nextjob()) == EOF)
	    perr("Cannot generate job number");

	(void)snprintf(ppos, sizeof(atfile) - (ppos - atfile),
		       "%c%5lx%8lx", queue, jobno, (unsigned long) (runtimer / 60));

	for (ap = ppos; *ap != '\0'; ap++)
	    if (*ap == ' ')
		*ap = '0';

	if (stat(atfile, &statbuf) != 0)
	    if (errno != ENOENT)
		perr("Cannot access " ATJOB_DIR);

	/* Create the file. The x bit is only going to be set after it has
	 * been completely written out, to make sure it is not executed in the
	 * meantime.  To make sure they do not get deleted, turn off their r
	 * bit.  Yes, this is a kluge.
	 */
	cmask = umask(S_IRUSR | S_IWUSR | S_IXUSR);
        seteuid(real_uid);
	if ((fd = open(atfile, O_CREAT | O_EXCL | O_TRUNC | O_WRONLY, S_IRUSR)) == -1)
	    perr("Cannot create atjob file %.500s", atfile);
        seteuid(effective_uid);

	if ((fd2 = dup(fd)) < 0)
	    perr("Error in dup() of job file");

        /*
	if (fchown(fd2, real_uid, real_gid) != 0)
	    perr("Cannot give away file");
        */

    PRIV_END

    /* We no longer need suid root; now we just need to be able to write
     * to the directory, if necessary.
     */

    REDUCE_PRIV(daemon_uid, daemon_gid)
    /* We've successfully created the file; let's set the flag so it 
     * gets removed in case of an interrupt or error.
     */
    fcreated = 1;

    /* Now we can release the lock, so other people can access it
     */
    lock.l_type = F_UNLCK;
    lock.l_whence = SEEK_SET;
    lock.l_start = 0;
    lock.l_len = 0;
    fcntl(lockdes, F_SETLKW, &lock);
    close(lockdes);

    if ((fp = fdopen(fd, "w")) == NULL)
	panic("Cannot reopen atjob file");

    /* Get the userid to mail to, first by trying getlogin(), which reads
     * /var/run/utmp, then from LOGNAME, finally from getpwuid().
     */
    mailname = getlogin();
    if (mailname == NULL)
	mailname = getenv("LOGNAME");
    if (mailname == NULL || mailname[0] == '\0' || getpwnam(mailname) == NULL) {
	pass_entry = getpwuid(real_uid);
	if (pass_entry != NULL)
	    mailname = pass_entry->pw_name;
    }

    if ((mailname == NULL) || (mailname[0] == '\0')
	|| (strlen(mailname) > mailsize) ) {
	panic("Cannot find username to mail output to");
    }
    if (atinput != (char *) NULL) {
	fpin = freopen(atinput, "r", stdin);
	if (fpin == NULL)
	    perr("Cannot open input file %.500s", atinput);
    }

    fprintf(fp, "#!/bin/sh\n# atrun uid=%d gid=%d\n# mail %s %d\n",
	    real_uid, real_gid, mailname, send_mail);

    /* Write out the umask at the time of invocation
     */
    fprintf(fp, "umask %lo\n", (unsigned long) cmask);

    /* Write out the environment. Anything that may look like a
     * special character to the shell is quoted, except for \n, which is
     * done with a pair of ""'s.  Dont't export the no_export list (such
     * as TERM or DISPLAY) because we don't want these.
     */
    for (atenv = environ; *atenv != NULL; atenv++) {
	int export = 1;
	char *eqp;

        /* Only accept alphanumerics and underscore in variable names.
         * Also require the name to not start with a digit.
         * Some shells don't like other variable names.
         */
        {
            char *p = *atenv;
            if (isdigit(*p))
                export = 0;
            for (; *p != '=' && *p != '\0'; ++p) {
                if (!isalnum(*p) && *p != '_') {
                    export = 0;
                    break;
                }
            }
        }

	eqp = strchr(*atenv, '=');
	if (ap == NULL)
	    eqp = *atenv;
	else {
	    unsigned int i;
	    for (i = 0; i < sizeof(no_export) / sizeof(no_export[0]); i++) {
		export = export
		    && (strncmp(*atenv, no_export[i],
				(size_t) (eqp - *atenv)) != 0);
	    }
	    eqp++;
	}
Пример #2
0
static void
writefile(time_t runtimer, char queue)
{
    /* This does most of the work if at or batch are invoked for writing a job.
     */
    long jobno;
    char *ap, *ppos, *mailname;
    struct passwd *pass_entry;
    struct stat statbuf;
    int fdes, lockdes, fd2;
    FILE *fp, *fpin;
    struct sigaction act;
    char **atenv;
    int ch;
    mode_t cmask;
    struct flock lock;

#ifdef __FreeBSD__
    (void) setlocale(LC_TIME, "");
#endif

    /* Install the signal handler for SIGINT; terminate after removing the
     * spool file if necessary
     */
    act.sa_handler = sigc;
    sigemptyset(&(act.sa_mask));
    act.sa_flags = 0;

    sigaction(SIGINT, &act, NULL);

    ppos = atfile + strlen(ATJOB_DIR);

    /* Loop over all possible file names for running something at this
     * particular time, see if a file is there; the first empty slot at any
     * particular time is used.  Lock the file LFILE first to make sure
     * we're alone when doing this.
     */

    PRIV_START

    if ((lockdes = open(LFILE, O_WRONLY | O_CREAT, S_IWUSR | S_IRUSR)) < 0)
        perr("cannot open lockfile " LFILE);

    lock.l_type = F_WRLCK;
    lock.l_whence = SEEK_SET;
    lock.l_start = 0;
    lock.l_len = 0;

    act.sa_handler = alarmc;
    sigemptyset(&(act.sa_mask));
    act.sa_flags = 0;

    /* Set an alarm so a timeout occurs after ALARMC seconds, in case
     * something is seriously broken.
     */
    sigaction(SIGALRM, &act, NULL);
    alarm(ALARMC);
    fcntl(lockdes, F_SETLKW, &lock);
    alarm(0);

    if ((jobno = nextjob()) == EOF)
        perr("cannot generate job number");

    sprintf(ppos, "%c%5lx%8lx", queue,
            jobno, (unsigned long) (runtimer/60));

    for(ap=ppos; *ap != '\0'; ap ++)
        if (*ap == ' ')
            *ap = '0';

    if (stat(atfile, &statbuf) != 0)
        if (errno != ENOENT)
            perr("cannot access " ATJOB_DIR);

    /* Create the file. The x bit is only going to be set after it has
     * been completely written out, to make sure it is not executed in the
     * meantime.  To make sure they do not get deleted, turn off their r
     * bit.  Yes, this is a kluge.
     */
    cmask = umask(S_IRUSR | S_IWUSR | S_IXUSR);
    if ((fdes = creat(atfile, O_WRONLY)) == -1)
        perr("cannot create atjob file");

    if ((fd2 = dup(fdes)) <0)
        perr("error in dup() of job file");

    if(fchown(fd2, real_uid, real_gid) != 0)
        perr("cannot give away file");

    PRIV_END

    /* We no longer need suid root; now we just need to be able to write
     * to the directory, if necessary.
     */

    REDUCE_PRIV(DAEMON_UID, DAEMON_GID)

    /* We've successfully created the file; let's set the flag so it
     * gets removed in case of an interrupt or error.
     */
    fcreated = 1;

    /* Now we can release the lock, so other people can access it
     */
    lock.l_type = F_UNLCK;
    lock.l_whence = SEEK_SET;
    lock.l_start = 0;
    lock.l_len = 0;
    fcntl(lockdes, F_SETLKW, &lock);
    close(lockdes);

    if((fp = fdopen(fdes, "w")) == NULL)
        panic("cannot reopen atjob file");

    /* Get the userid to mail to, first by trying getlogin(),
     * then from LOGNAME, finally from getpwuid().
     */
    mailname = getlogin();
    if (mailname == NULL)
        mailname = getenv("LOGNAME");

    if ((mailname == NULL) || (mailname[0] == '\0')
            || (strlen(mailname) >= MAXLOGNAME) || (getpwnam(mailname)==NULL))
    {
        pass_entry = getpwuid(real_uid);
        if (pass_entry != NULL)
            mailname = pass_entry->pw_name;
    }

    if (atinput != (char *) NULL)
    {
        fpin = freopen(atinput, "r", stdin);
        if (fpin == NULL)
            perr("cannot open input file");
    }
    fprintf(fp, "#!/bin/sh\n# atrun uid=%ld gid=%ld\n# mail %*s %d\n",
            (long) real_uid, (long) real_gid, MAXLOGNAME - 1, mailname,
            send_mail);

    /* Write out the umask at the time of invocation
     */
    fprintf(fp, "umask %lo\n", (unsigned long) cmask);

    /* Write out the environment. Anything that may look like a
     * special character to the shell is quoted, except for \n, which is
     * done with a pair of "'s.  Don't export the no_export list (such
     * as TERM or DISPLAY) because we don't want these.
     */
    for (atenv= environ; *atenv != NULL; atenv++)
    {
        int export = 1;
        char *eqp;

        eqp = strchr(*atenv, '=');
        if (ap == NULL)
            eqp = *atenv;
        else
        {
            size_t i;
            for (i=0; i<sizeof(no_export)/sizeof(no_export[0]); i++)
            {
                export = export
                         && (strncmp(*atenv, no_export[i],
                                     (size_t) (eqp-*atenv)) != 0);
            }
            eqp++;
        }

        if (export)
        {
            fwrite(*atenv, sizeof(char), eqp-*atenv, fp);
            for(ap = eqp; *ap != '\0'; ap++)
            {
                if (*ap == '\n')
                    fprintf(fp, "\"\n\"");
                else
                {
                    if (!isalnum(*ap)) {
                        switch (*ap) {
                        case '%':
                        case '/':
                        case '{':
                        case '[':
                        case ']':
                        case '=':
                        case '}':
                        case '@':
                        case '+':
                        case '#':
                        case ',':
                        case '.':
                        case ':':
                        case '-':
                        case '_':
                            break;
                        default:
                            fputc('\\', fp);
                            break;
                        }
                    }
                    fputc(*ap, fp);
                }
            }
            fputs("; export ", fp);
            fwrite(*atenv, sizeof(char), eqp-*atenv -1, fp);
            fputc('\n', fp);

        }
    }