Exemplo n.º 1
0
/*
 * Read the SMTP authentication config file
 *
 * file format is:
 * user|host:password
 *
 * A line starting with # is treated as comment and ignored.
 */
void
parse_authfile(const char *path)
{
	char line[2048];
	struct authuser *au;
	FILE *a;
	char *data;
	int lineno = 0;

	a = fopen(path, "r");
	if (a == NULL) {
		errlog(1, "can not open auth file `%s'", path);
		/* NOTREACHED */
	}

	while (!feof(a)) {
		if (fgets(line, sizeof(line), a) == NULL)
			break;
		lineno++;

		chomp(line);

		/* We hit a comment */
		if (*line == '#')
			continue;
		/* Ignore empty lines */
		if (*line == 0)
			continue;

		au = calloc(1, sizeof(*au));
		if (au == NULL)
			errlog(1, "calloc failed");

		data = strdup(line);
		au->login = strsep(&data, "|");
		au->host = strsep(&data, DP);
		au->password = data;

		if (au->login == NULL ||
		    au->host == NULL ||
		    au->password == NULL) {
			errlogx(1, "syntax error in authfile %s:%d",
				path, lineno);
			/* NOTREACHED */
		}

		SLIST_INSERT_HEAD(&authusers, au, next);
	}

	fclose(a);
}
Exemplo n.º 2
0
int
readmail(struct queue *queue, int nodot, int recp_from_header)
{
	struct parse_state parse_state;
	char line[1000];	/* by RFC2822 */
	size_t linelen;
	size_t error;
	int had_headers = 0;
	int had_from = 0;
	int had_messagid = 0;
	int had_date = 0;
	int had_last_line = 0;
	int nocopy = 0;

	parse_state.state = NONE;

	error = fprintf(queue->mailf,
		"Received: from %s (uid %d)\n"
		"\t(envelope-from %s)\n"
		"\tid %s\n"
		"\tby %s (%s);\n"
		"\t%s\n",
		username, useruid,
		queue->sender,
		queue->id,
		hostname(), VERSION,
		rfc822date());
	if ((ssize_t)error < 0)
		return (-1);

	while (!feof(stdin)) {
		if (fgets(line, sizeof(line) - 1, stdin) == NULL)
			break;
		if (had_last_line)
			errlogx(1, "bad mail input format:"
				" from %s (uid %d) (envelope-from %s)",
				username, useruid, queue->sender);
		linelen = strlen(line);
		if (linelen == 0 || line[linelen - 1] != '\n') {
			/*
			 * This line did not end with a newline character.
			 * If we fix it, it better be the last line of
			 * the file.
			 */
			line[linelen] = '\n';
			line[linelen + 1] = 0;
			had_last_line = 1;
		}
		if (!had_headers) {
			/*
			 * Unless this is a continuation, switch of
			 * the Bcc: nocopy flag.
			 */
			if (!(line[0] == ' ' || line[0] == '\t'))
				nocopy = 0;

			if (strprefixcmp(line, "Date:") == 0)
				had_date = 1;
			else if (strprefixcmp(line, "Message-Id:") == 0)
				had_messagid = 1;
			else if (strprefixcmp(line, "From:") == 0)
				had_from = 1;
			else if (strprefixcmp(line, "Bcc:") == 0)
				nocopy = 1;

			if (parse_state.state != NONE) {
				if (parse_addrs(&parse_state, line, queue) < 0) {
					errlogx(1, "invalid address in header\n");
					/* NOTREACHED */
				}
			}

			if (recp_from_header && (
					strprefixcmp(line, "To:") == 0 ||
					strprefixcmp(line, "Cc:") == 0 ||
					strprefixcmp(line, "Bcc:") == 0)) {
				parse_state.state = START;
				if (parse_addrs(&parse_state, line, queue) < 0) {
					errlogx(1, "invalid address in header\n");
					/* NOTREACHED */
				}
			}
		}

		if (strcmp(line, "\n") == 0 && !had_headers) {
			had_headers = 1;
			while (!had_date || !had_messagid || !had_from) {
				if (!had_date) {
					had_date = 1;
					snprintf(line, sizeof(line), "Date: %s\n", rfc822date());
				} else if (!had_messagid) {
					/* XXX msgid, assign earlier and log? */
					had_messagid = 1;
					snprintf(line, sizeof(line), "Message-Id: <%"PRIxMAX".%s.%"PRIxMAX"@%s>\n",
						 (uintmax_t)time(NULL),
						 queue->id,
						 (uintmax_t)random(),
						 hostname());
				} else if (!had_from) {
					had_from = 1;
					snprintf(line, sizeof(line), "From: <%s>\n", queue->sender);
				}
				if (fwrite(line, strlen(line), 1, queue->mailf) != 1)
					return (-1);
			}
			strcpy(line, "\n");
		}
		if (!nodot && linelen == 2 && line[0] == '.')
			break;
		if (!nocopy) {
			if (fwrite(line, strlen(line), 1, queue->mailf) != 1)
				return (-1);
		}
	}

	return (0);
}
Exemplo n.º 3
0
/*
 * Simplified RFC2822 header/address parsing.
 * We copy escapes and quoted strings directly, since
 * we have to pass them like this to the mail server anyways.
 * XXX local addresses will need treatment
 */
static int
parse_addrs(struct parse_state *ps, char *s, struct queue *queue)
{
	char *addr;

again:
	switch (ps->state) {
	case NONE:
		return (-1);

	case START:
		/* init our data */
		bzero(ps, sizeof(*ps));

		/* skip over header name */
		while (*s != ':')
			s++;
		s++;
		ps->state = MAIN;
		break;

	case MAIN:
		/* all fine */
		break;

	case EOL:
		switch (*s) {
		case ' ':
		case '\t':
			s++;
			/* continue */
			break;

		default:
			ps->state = QUIT;
			if (ps->pos != 0)
				goto newaddr;
			return (0);
		}
		/* FALLTHROUGH */

	case QUIT:
		return (0);
	}

	for (; *s != 0; s++) {
		if (ps->esc) {
			ps->esc = 0;

			switch (*s) {
			case '\r':
			case '\n':
				goto err;

			default:
				goto copy;
			}
		}

		if (ps->quote) {
			switch (*s) {
			case '"':
				ps->quote = 0;
				goto copy;

			case '\\':
				ps->esc = 1;
				goto copy;

			case '\r':
			case '\n':
				goto eol;

			default:
				goto copy;
			}
		}

		switch (*s) {
		case '(':
			ps->comment++;
			break;

		case ')':
			if (ps->comment)
				ps->comment--;
			else
				goto err;
			goto skip;

		case '"':
			ps->quote = 1;
			goto copy;

		case '\\':
			ps->esc = 1;
			goto copy;

		case '\r':
		case '\n':
			goto eol;
		}

		if (ps->comment)
			goto skip;

		switch (*s) {
		case ' ':
		case '\t':
			/* ignore whitespace */
			goto skip;

		case '<':
			/* this is the real address now */
			ps->brackets = 1;
			ps->pos = 0;
			goto skip;

		case '>':
			if (!ps->brackets)
				goto err;
			ps->brackets = 0;

			s++;
			goto newaddr;

		case ':':
			/* group - ignore */
			ps->pos = 0;
			goto skip;

		case ',':
		case ';':
			/*
			 * Next address, copy previous one.
			 * However, we might be directly after
			 * a <address>, or have two consecutive
			 * commas.
			 * Skip the comma unless there is
			 * really something to copy.
			 */
			if (ps->pos == 0)
				goto skip;
			s++;
			goto newaddr;

		default:
			goto copy;
		}

copy:
		if (ps->comment)
			goto skip;

		if (ps->pos + 1 == sizeof(ps->addr))
			goto err;
		ps->addr[ps->pos++] = *s;

skip:
		;
	}

eol:
	ps->state = EOL;
	return (0);

err:
	ps->state = QUIT;
	return (-1);

newaddr:
	ps->addr[ps->pos] = 0;
	ps->pos = 0;
	addr = strdup(ps->addr);
	if (addr == NULL)
		errlog(1, NULL);

	if (add_recp(queue, addr, EXPAND_WILDCARD) != 0)
		errlogx(1, "invalid recipient `%s'", addr);

	goto again;
}
Exemplo n.º 4
0
/*
 * XXX TODO
 * Check for bad things[TM]
 */
void
parse_conf(const char *config_path)
{
	char *word;
	char *data;
	FILE *conf;
	char line[2048];
	int lineno = 0;

	conf = fopen(config_path, "r");
	if (conf == NULL) {
		/* Don't treat a non-existing config file as error */
		if (errno == ENOENT)
			return;
		errlog(1, "can not open config `%s'", config_path);
		/* NOTREACHED */
	}

	while (!feof(conf)) {
		if (fgets(line, sizeof(line), conf) == NULL)
			break;
		lineno++;

		chomp(line);

		/* We hit a comment */
		if (strchr(line, '#'))
			*strchr(line, '#') = 0;

		data = line;
		word = strsep(&data, EQS);

		/* Ignore empty lines */
		if (word == NULL || *word == 0)
			continue;

		if (data != NULL && *data != 0)
			data = strdup(data);
		else
			data = NULL;

		if (strcmp(word, "SMARTHOST") == 0 && data != NULL)
			config.smarthost = data;
		else if (strcmp(word, "PORT") == 0 && data != NULL)
			config.port = atoi(data);
		else if (strcmp(word, "ALIASES") == 0 && data != NULL)
			config.aliases = data;
		else if (strcmp(word, "SPOOLDIR") == 0 && data != NULL)
			config.spooldir = data;
		else if (strcmp(word, "AUTHPATH") == 0 && data != NULL)
			config.authpath= data;
		else if (strcmp(word, "CERTFILE") == 0 && data != NULL)
			config.certfile = data;
		else if (strcmp(word, "MAILNAME") == 0 && data != NULL)
			config.mailname = data;
		else if (strcmp(word, "MASQUERADE") == 0 && data != NULL) {
			char *user = NULL, *host = NULL;
			if (strrchr(data, '@')) {
				host = strrchr(data, '@');
				*host = 0;
				host++;
				user = data;
			} else {
				host = data;
			}
 			if (host && *host == 0)
				host = NULL;
                        if (user && *user == 0)
                                user = NULL;
			config.masquerade_host = host;
			config.masquerade_user = user;
		} else if (strcmp(word, "STARTTLS") == 0 && data == NULL)
			config.features |= STARTTLS;
		else if (strcmp(word, "OPPORTUNISTIC_TLS") == 0 && data == NULL)
			config.features |= TLS_OPP;
		else if (strcmp(word, "SECURETRANSFER") == 0 && data == NULL)
			config.features |= SECURETRANS;
		else if (strcmp(word, "DEFER") == 0 && data == NULL)
			config.features |= DEFER;
		else if (strcmp(word, "INSECURE") == 0 && data == NULL)
			config.features |= INSECURE;
		else if (strcmp(word, "FULLBOUNCE") == 0 && data == NULL)
			config.features |= FULLBOUNCE;
		else if (strcmp(word, "NULLCLIENT") == 0 && data == NULL)
			config.features |= NULLCLIENT;
		else {
			errlogx(1, "syntax error in %s:%d", config_path, lineno);
			/* NOTREACHED */
		}
	}

	if ((config.features & NULLCLIENT) && config.smarthost == NULL) {
		errlogx(1, "%s: NULLCLIENT requires SMARTHOST", config_path);
		/* NOTREACHED */
	}

	fclose(conf);
}
Exemplo n.º 5
0
int
readmail(struct queue *queue, int nodot, int recp_from_header)
{
	struct parse_state parse_state;
	char *line = NULL;
	ssize_t linelen;
	size_t linecap = 0;
	char newline[MAX_LINE_RFC822];
	size_t error;
	int had_headers = 0;
	int had_from = 0;
	int had_messagid = 0;
	int had_date = 0;
	int nocopy = 0;
	int ret = -1;

	parse_state.state = NONE;

	error = fprintf(queue->mailf,
		"Received: from %s (uid %d)\n"
		"\t(envelope-from %s)\n"
		"\tid %s\n"
		"\tby %s (%s);\n"
		"\t%s\n",
		username, useruid,
		queue->sender,
		queue->id,
		hostname(), VERSION,
		rfc822date());
	if ((ssize_t)error < 0)
		return (-1);

	while (!feof(stdin)) {
		newline[0] = '\0';
		if ((linelen = getline(&line, &linecap, stdin)) <= 0)
			break;

		if (!had_headers) {
			if (linelen > MAX_LINE_RFC822) {
				/* XXX also split headers */
				errlogx(EX_DATAERR, "bad mail input format:"
				    " from %s (uid %d) (envelope-from %s)",
				    username, useruid, queue->sender);
			}
			/*
			 * Unless this is a continuation, switch of
			 * the Bcc: nocopy flag.
			 */
			if (!(line[0] == ' ' || line[0] == '\t'))
				nocopy = 0;

			if (strprefixcmp(line, "Date:") == 0)
				had_date = 1;
			else if (strprefixcmp(line, "Message-Id:") == 0)
				had_messagid = 1;
			else if (strprefixcmp(line, "From:") == 0)
				had_from = 1;
			else if (strprefixcmp(line, "Bcc:") == 0)
				nocopy = 1;

			if (parse_state.state != NONE) {
				if (parse_addrs(&parse_state, line, queue) < 0) {
					errlogx(EX_DATAERR, "invalid address in header\n");
					/* NOTREACHED */
				}
			}

			if (recp_from_header && (
					strprefixcmp(line, "To:") == 0 ||
					strprefixcmp(line, "Cc:") == 0 ||
					strprefixcmp(line, "Bcc:") == 0)) {
				parse_state.state = START;
				if (parse_addrs(&parse_state, line, queue) < 0) {
					errlogx(EX_DATAERR, "invalid address in header\n");
					/* NOTREACHED */
				}
			}
		}

		if (strcmp(line, "\n") == 0 && !had_headers) {
			had_headers = 1;
			while (!had_date || !had_messagid || !had_from) {
				if (!had_date) {
					had_date = 1;
					snprintf(newline, sizeof(newline), "Date: %s\n", rfc822date());
				} else if (!had_messagid) {
					/* XXX msgid, assign earlier and log? */
					had_messagid = 1;
					snprintf(newline, sizeof(newline), "Message-Id: <%"PRIxMAX".%s.%"PRIxMAX"@%s>\n",
						 (uintmax_t)time(NULL),
						 queue->id,
						 (uintmax_t)random(),
						 hostname());
				} else if (!had_from) {
					had_from = 1;
					snprintf(newline, sizeof(newline), "From: <%s>\n", queue->sender);
				}
				if (fwrite(newline, strlen(newline), 1, queue->mailf) != 1)
					goto fail;
			}
			strlcpy(newline, "\n", sizeof(newline));
		}
		if (!nodot && linelen == 2 && line[0] == '.')
			break;
		if (!nocopy) {
			if (newline[0]) {
				if (fwrite(newline, strlen(newline), 1, queue->mailf) != 1)
					goto fail;
			} else {
				if (writeline(queue, line, linelen) != 0)
					goto fail;
			}
		}
	}

	ret = 0;
fail:
	free(line);
	return (ret);
}