Exemple #1
0
/**
 * Parses an SPF domainspec.
 *
 * @param spf_server The SPF server on whose behalf the record is being compiled.
 * @param spf_response The SPF response in which to store errors.
 * @param data Output buffer pointer.
 * @param data_used Output parameter for amount of data written to output buffer.
 * @param data_avail Input parameter for size of output buffer.
 * @param src Input buffer pointer.
 * @param src_len Input buffer length.
 * @param big_err The error code to return on an over-length condition.
 * @param cidr_ok True if a CIDR mask is permitted on this domainspec.
 * @param is_mod True if this is a modifier.
 */
static SPF_errcode_t
SPF_c_parse_domainspec(SPF_server_t *spf_server,
				SPF_response_t *spf_response,
				SPF_data_t *data, size_t *data_used, size_t data_avail,
				const char *src, size_t src_len,
				SPF_errcode_t big_err,
				SPF_cidr_t cidr_ok, int is_mod)
{
	SPF_errcode_t		 err;
			/* Generic parsing iterators and boundaries */
	size_t				len;

	if (spf_server->debug)
		SPF_debugf("Parsing domainspec starting at %s, cidr is %s",
						src,
						cidr_ok == CIDR_OPTIONAL ? "optional" :
						cidr_ok == CIDR_ONLY ? "only" :
						cidr_ok == CIDR_NONE ? "forbidden" :
						"ERROR!"
						);

	/*
	 * create the CIDR length info
	 */
	if (cidr_ok == CIDR_OPTIONAL || cidr_ok == CIDR_ONLY) {
		err = SPF_c_parse_cidr(spf_response, &data->dc, src, &src_len);
		if (err != SPF_E_SUCCESS)
			return err;
		if (data->dc.ipv4 != 0  ||  data->dc.ipv6 != 0) {
			len = SPF_data_len(data);
			SPF_ADD_LEN_TO(*data_used, len, data_avail);
			data = SPF_data_next(data);
		}

		if (cidr_ok == CIDR_ONLY && src_len > 0) {
			/* We had a mechanism followed by a '/', thus it HAS to be
			 * a CIDR, and the peculiar-looking error message is
			 * justified. However, we don't know _which_ CIDR. */
			return SPF_response_add_error_ptr(spf_response,
							SPF_E_INVALID_CIDR,
							NULL, src,
							"Invalid CIDR after mechanism");
		}
	}

	return SPF_c_parse_macro(spf_server, spf_response,
			data, data_used, data_avail,
			src, src_len, big_err, is_mod);
}
Exemple #2
0
/**
 * This could better collect errors, like the compiler does.
 * This requires that *bufp be either malloced to *buflenp, or NULL
 * This may realloc *bufp.
 */
SPF_errcode_t
SPF_record_expand_data(SPF_server_t *spf_server,
				SPF_request_t *spf_request,
				SPF_response_t *spf_response,
				SPF_data_t *data, size_t data_len,
				char **bufp, size_t *buflenp)
{
	SPF_data_t	*d, *data_end;

	size_t		 len;
	const char	*p_err;	// XXX Check this value, when returned.
	char		*p, *p_end;
	const char	*p_read;
	const char	*p_read_end;
	char		*p_write;
	char		*p2, *p2_end;


	const char	*var;
	char		*munged_var = NULL;
	char		*url_var = NULL;

			/* Pretty-printing buffers. */
	char		ip4_buf[ INET_ADDRSTRLEN ];
	char		ip6_buf[ INET6_ADDRSTRLEN ];
			/* Hex buffer for ipv6 (size in nibbles) */
	char		ip6_rbuf[ sizeof( struct in6_addr ) * 4 + 1 ];

	char		time_buf[ sizeof( "4294967296" ) ]; /* 2^32 seconds max		*/

	int			num_found;
	int			i;
	size_t		buflen;
	int			compute_length;
	SPF_errcode_t	 err;


	/*
	 * make sure we were passed valid data to work with
	 */
	SPF_ASSERT_NOTNULL(spf_server);
	SPF_ASSERT_NOTNULL(data);
	SPF_ASSERT_NOTNULL(bufp);
	SPF_ASSERT_NOTNULL(buflenp);

	buflen = 1;	/* For the terminating '\0' */
	compute_length = 1;
	p = NULL;
	p_end = NULL;

	/* data_end = SPF_mech_end_data( mech ); */ /* doesn't work for mods */
	data_end = (SPF_data_t *)((char *)data + data_len);

top:
#ifdef DEBUG
	fprintf(stderr, "Pass start compute_length=%d\n", compute_length);
#endif
	/*
	 * expand the data
	 */
	for (d = data; d < data_end; d = SPF_data_next(d)) {
#ifdef DEBUG
		fprintf(stderr, " Item type=%d at %p\n", d->dc.parm_type, d);
#endif
		if (d->dc.parm_type == PARM_CIDR)
			continue;

		if (d->ds.parm_type == PARM_STRING) {
			if (compute_length) {
				buflen += d->ds.len;
				continue;
			}
			/* This should NEVER happen now. */
			if (p_end - (p + d->ds.len) <= 0)
					SPF_error("Failed to allocate enough memory "
								"to expand string.");
			memcpy(p, SPF_data_str(d), d->ds.len);
			p += d->ds.len;
			continue;
		}

		/* Otherwise, it's a variable. */

		var = NULL;
		switch (d->dv.parm_type) {
		case PARM_LP_FROM:		/* local-part of envelope-sender */
			var = spf_request->env_from_lp;
			break;

		case PARM_ENV_FROM:		/* envelope-sender				*/
			var = spf_request->env_from;
			break;

		case PARM_DP_FROM:		/* envelope-domain				*/
			var = spf_request->env_from_dp;
			break;

		case PARM_CUR_DOM:		/* current-domain				*/
			var = spf_request->cur_dom;
			break;

		case PARM_CLIENT_IP:		/* SMTP client IP				*/
			if (compute_length) {
				len = sizeof(ip6_buf);
				if (d->dv.url_encode)
					len *= 3;
				buflen += len;
				continue;
			}
			if (spf_request->client_ver == AF_INET) {
				p_err = inet_ntop(AF_INET, &spf_request->ipv4,
								   ip4_buf, sizeof(ip4_buf));
				var = ip4_buf;
			}
			else if (spf_request->client_ver == AF_INET6) {
				p2 = ip6_rbuf;
				p2_end = p2 + sizeof(ip6_rbuf);

				for (i = 0; i < array_elem(spf_request->ipv6.s6_addr); i++) {
					p2 += snprintf(p2, p2_end - p2, "%.1x.%.1x.",
									spf_request->ipv6.s6_addr[i] >> 4,
									spf_request->ipv6.s6_addr[i] & 0xf);
				}

				/* squash the final '.' */
				ip6_rbuf[sizeof(struct in6_addr) * 4 - 1] = '\0';

				var = ip6_rbuf;
			}
			break;

		case PARM_CLIENT_IP_P:		/* SMTP client IP (pretty)		*/
			if (compute_length) {
				len = sizeof(ip6_buf);
				if (d->dv.url_encode)
					len *= 3;
				buflen += len;
				continue;
			}
			if (spf_request->client_ver == AF_INET) {
				p_err = inet_ntop(AF_INET, &spf_request->ipv4,
								   ip4_buf, sizeof(ip4_buf));
				var = ip4_buf;
			}
			else if (spf_request->client_ver == AF_INET6) {
				p_err = inet_ntop(AF_INET6, &spf_request->ipv6,
								   ip6_buf, sizeof(ip6_buf));
				var = ip6_buf;
			}
			break;

		case PARM_TIME:				/* time in UTC epoch secs		*/
			if (compute_length) {
				len = sizeof(time_buf);
				/* This never gets bigger using URL encoding. */
				buflen += len;
				continue;
			}
			snprintf(time_buf, sizeof(time_buf), "%ld",
					  (long)time(NULL));
			var = time_buf;
			break;

		case PARM_CLIENT_DOM:		/* SMTP client domain name		*/
			var = SPF_request_get_client_dom(spf_request);
			if (! var)
				return SPF_E_NO_MEMORY;
			break;

		case PARM_CLIENT_VER:		/* IP ver str - in-addr/ip6		*/
			if (spf_request->client_ver == AF_INET)
				var = client_ver_ipv4;
			else if (spf_request->client_ver == AF_INET6)
				var = client_ver_ipv6;
			break;

		case PARM_HELO_DOM:		/* HELO/EHLO domain				*/
			var = spf_request->helo_dom;
			break;

		case PARM_REC_DOM:		/* receiving domain				*/
			var = SPF_request_get_rec_dom(spf_request);
			break;

		default:
#ifdef DEBUG
			fprintf(stderr, "Invalid variable %d\n", d->dv.parm_type);
#endif
			return SPF_E_INVALID_VAR;
			break;
		}

		if (var == NULL)
			return SPF_E_UNINIT_VAR;

		len = strlen(var);
		if (compute_length) {
			if (d->dv.url_encode)
				len *= 3;
			buflen += len;
			continue;
		}

		/* Now we put 'var' through the munging procedure. */
		munged_var = (char *)malloc(len + 1);
		if (munged_var == NULL)
			return SPF_E_NO_MEMORY;
		memset(munged_var, 0, len + 1);

		p_read_end = var + len;
		p_write = munged_var;

		/* reverse */

/* The following code confuses both me and Coverity. Shevek. */

		if (d->dv.rev) {
			p_read = p_read_end - 1;

			while ( p_read >= var ) {
				if ( SPF_delim_valid(d, *p_read) ) {
					/* Subtract 1 because p_read points to delim, and
					 * p_read_end points to the following delim. */
					len = p_read_end - p_read - 1;
					memcpy( p_write, p_read + 1, len );
					p_write += len;
					*p_write++ = '.';

					p_read_end = p_read;
				}
				p_read--;
			}

			/* Now p_read_end should point one before the start of the
			 * string. p_read_end might also point there if the string
			 * starts with a delimiter. */
			if (p_read_end >= p_read) {
				len = p_read_end - p_read - 1;
				memcpy( p_write, p_read + 1, len );
				p_write += len;
				*p_write++ = '.';
			}

			/* p_write always points to the 'next' character. */
			p_write--;
			*p_write = '\0';
		}
		else {
			p_read = var;

			while (p_read < p_read_end) {
				if (SPF_delim_valid(d, *p_read))
					*p_write++ = '.';
				else
					*p_write++ = *p_read;
				p_read++;
			}

			*p_write = '\0';
		}

		/* Now munged_var is a copy of var, possibly reversed, and
		 * thus len == strlen(munged_var). However, we continue to
		 * manipulate the underlying munged_var since var is const. */

		/* truncate, from the right hand side. */
		if (d->dv.num_rhs > 0) {
			p_read_end = munged_var + len;		/* const, at '\0' */
			p_write = munged_var + len - 1;
			num_found = 0;
			while (p_write > munged_var) {
				if (*p_write == '.')
					num_found++;
				if (num_found == d->dv.num_rhs)
					break;
				p_write--;
			}
			p_write++;		/* Move to just after the '.' */
			/* This moves the '\0' as well. */
			len = p_read_end - p_write;
			memmove(munged_var, p_write, len + 1);
		}

		var = munged_var;
		/* Now, we have 'var', of length 'len' */

		/* URL encode */

		if (d->dv.url_encode) {
			url_var = malloc(len * 3 + 1);
			if (url_var == NULL) {
				if (munged_var)
					free(munged_var);
				return SPF_E_NO_MEMORY;
			}

			p_read = var;
			p_write = url_var;

			/* escape non-uric characters (rfc2396) */
			while ( *p_read != '\0' )
			{
				if ( isalnum( (unsigned char)( *p_read  ) ) )
					*p_write++ = *p_read++;
				else
				{
					switch( *p_read )
					{
					case '-':
					case '_':
					case '.':
					case '!':
					case '~':
					case '*':
					case '\'':
					case '(':
					case ')':
						*p_write++ = *p_read++;
						break;

					default:
						/* No point doing snprintf with a const '4'
						 * because we know we're going to get 4
						 * characters anyway. */
						sprintf( p_write, "%%%02x", *p_read );
						p_write += 3;
						p_read++;
						break;
					}
				}
			}
			*p_write = '\0';

			var = url_var;
			len = p_write - url_var;		/* Not actually used. */
		}


		/* finish up */
		len = snprintf(p, p_end - p, "%s", var);
		p += len;
		if (p_end - p <= 0) {
			if (munged_var)
				free(munged_var);
			if (url_var)
				free(url_var);
			return SPF_E_INTERNAL_ERROR;
		}

		if (munged_var)
			free(munged_var);
		munged_var = NULL;
		if (url_var)
			free(url_var);
		url_var = NULL;
	}
Exemple #3
0
/**
 * Parses an SPF macro string.
 *
 * Note that we cannot write data_avail bytes from data, since we
 * might be called with a modified data pointer. We MUST compare
 * data_used with data_avail.
 *
 * @param spf_server The SPF server on whose behalf the record is being compiled.
 * @param spf_response The SPF response in which to store errors.
 * @param data Output buffer pointer.
 * @param data_used Output parameter for amount of data written to output buffer.
 * @param data_avail Input parameter for size of output buffer.
 * @param src Input buffer pointer.
 * @param src_len Input buffer length.
 * @param big_err The error code to return on an over-length condition.
 * @param is_mod True if this is a modifier.
 */
static SPF_errcode_t
SPF_c_parse_macro(SPF_server_t *spf_server,
				SPF_response_t *spf_response,
				SPF_data_t *data, size_t *data_used, size_t data_avail,
				const char *src, size_t src_len,
				SPF_errcode_t big_err,
				int is_mod)
{
	SPF_errcode_t		 err;
			/* Generic parsing iterators and boundaries */
	size_t				 idx;
	size_t				 len;
			/* For parsing strings. */
	char				*dst;
	size_t				 ds_avail;
	size_t				 ds_len;

	if (spf_server->debug)
		SPF_debugf("Parsing macro starting at %s", src);

#if 0
	if ((void *)data != _align_ptr((void *)data))
		SPF_errorf("Data pointer %p is not aligned: Cannot compile.",
		data);
#endif

	/*
	 * Create the data blocks
	 */
	idx = 0;

	/* Initialise the block as a string. If ds_len == 0 later, we
	 * will just clobber it. */
	SPF_INIT_STRING_LITERAL(data_avail - *data_used);

	// while ( p != end ) {
	while (idx < src_len) {
		if (spf_server->debug > 3)
			SPF_debugf("Current data is at %p", data);
		/* Either the unit is terminated by a space, or we hit a %.
		 * We should only hit a space if we run past src_len. */
		len = strcspn(&src[idx], " %");	// XXX Also tab?
		if (len > 0) {				/* An optimisation */
			/* Don't over-run into the CIDR. */
			if (idx + len > src_len)
				len = src_len - idx;
			if (spf_server->debug > 3)
				SPF_debugf("Adding string literal (%lu): '%*.*s'",
								(unsigned long)len,
								(int)len, (int)len, &src[idx]);
			/* XXX Bounds-check here. */
			SPF_ENSURE_STRING_AVAIL(len);
			memcpy(dst, &src[idx], len);
			ds_len += len;
			dst += len;
			idx += len;

			/* If len == 0 then we never entered the while(). Thus
			 * if idx == src_len, then len != 0 and we reach this test.
			 */
		}
		/* However, this logic is overcomplex and I am a simpleton,
		 * so I have moved it out of the condition above. */
		if (idx == src_len)
			break;

		/* Now, we must have a %-escape code, since if we hit a
		 * space, then we are at the end.
		 * Incrementing idx consumes the % we hit first, and then
		 * we switch on the following character, which also
		 * increments idx. */
		idx++;
		switch (src[idx]) {
		case '%':
			if (spf_server->debug > 3)
				SPF_debugf("Adding literal %%");
			SPF_ENSURE_STRING_AVAIL(1);
			*dst++ = '%';
			ds_len++;
			idx++;
			break;
			
		case '_':
			if (spf_server->debug > 3)
				SPF_debugf("Adding literal space");
			SPF_ENSURE_STRING_AVAIL(1);
			*dst++ = ' ';
			ds_len++;
			idx++;
			break;

		case '-':
			if (spf_server->debug > 3)
				SPF_debugf("Adding escaped space");
			SPF_ENSURE_STRING_AVAIL(3);
			*dst++ = '%'; *dst++ = '2'; *dst++ = '0';
			ds_len += 3;
			idx++;
			break;

		default:
			if (spf_server->debug > 3)
				SPF_debugf("Adding illegal %%-follower '%c' at %d",
				src[idx], idx);
			/* SPF spec says to treat it as a literal, not
			 * SPF_E_INVALID_ESC */
			/* FIXME   issue a warning? */
			SPF_ENSURE_STRING_AVAIL(1);
			*dst++ = '%';
			ds_len++;
			break;

		case '{':  /*vi:}*/
			SPF_FINI_STRING_LITERAL();
			if (spf_server->debug > 3)
				SPF_debugf("Adding macro, data is at %p", data);

			/* this must be a variable */
			idx++;
			err = SPF_c_parse_var(spf_response, &data->dv, &src[idx], is_mod);
			if (err != SPF_E_SUCCESS)
				return err;
			idx += strcspn(&src[idx], "} ");
			if (src[idx] == '}')
				idx++;
			else if (src[idx] == ' ')
				return SPF_response_add_error_ptr(spf_response,
						SPF_E_INVALID_VAR,
						src, &src[idx],
						"Unterminated variable?");


			len = SPF_data_len(data);
			SPF_ADD_LEN_TO(*data_used, len, data_avail);
			data = SPF_data_next( data );
			if (spf_server->debug > 3)
				SPF_debugf("Next data is at %p", data);

			SPF_INIT_STRING_LITERAL(data_avail - *data_used);

			break;
		}
	}

	SPF_FINI_STRING_LITERAL();

	return SPF_E_SUCCESS;

}