Blame src/OVAL/probes/oval_fts.c

Packit 517ee8
/*
Packit 517ee8
 * Copyright 2010-2011 Red Hat Inc., Durham, North Carolina.
Packit 517ee8
 * All Rights Reserved.
Packit 517ee8
 *
Packit 517ee8
 * This library is free software; you can redistribute it and/or
Packit 517ee8
 * modify it under the terms of the GNU Lesser General Public
Packit 517ee8
 * License as published by the Free Software Foundation; either
Packit 517ee8
 * version 2.1 of the License, or (at your option) any later version.
Packit 517ee8
 *
Packit 517ee8
 * This library is distributed in the hope that it will be useful,
Packit 517ee8
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit 517ee8
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Packit 517ee8
 * Lesser General Public License for more details.
Packit 517ee8
 *
Packit 517ee8
 * You should have received a copy of the GNU Lesser General Public
Packit 517ee8
 * License along with this library; if not, write to the Free Software
Packit 517ee8
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Packit 517ee8
 *
Packit 517ee8
 * Authors:
Packit 517ee8
 *      "Daniel Kopecek" <dkopecek@redhat.com>
Packit 517ee8
 *      "Tomas Heinrich" <theinric@redhat.com>
Packit 517ee8
 */
Packit 517ee8
Packit 517ee8
#ifdef HAVE_CONFIG_H
Packit 517ee8
#include <config.h>
Packit 517ee8
#endif
Packit 517ee8
Packit 517ee8
#include <stdint.h>
Packit 517ee8
#include <stdbool.h>
Packit 517ee8
#include <string.h>
Packit 517ee8
#include <sys/types.h>
Packit 517ee8
#include <sys/stat.h>
Packit 517ee8
#include <limits.h>
Packit 517ee8
#include <errno.h>
Packit 517ee8
#include <pcre.h>
Packit 517ee8
Packit 517ee8
#include "oscap_helpers.h"
Packit 517ee8
#include "fsdev.h"
Packit 517ee8
#include "_probe-api.h"
Packit 517ee8
#include "probe/entcmp.h"
Packit 517ee8
#include "debug_priv.h"
Packit 517ee8
#include "oval_fts.h"
Packit 517ee8
#if defined(OS_SOLARIS)
Packit 517ee8
#include "fts_sun.h"
Packit 517ee8
#include <sys/mntent.h>
Packit 517ee8
#include <libzonecfg.h>
Packit 517ee8
#include <sys/avl.h>
Packit 517ee8
#elif defined(OS_AIX)
Packit 517ee8
#include "fts_sun.h"
Packit 517ee8
#else
Packit 517ee8
#include <fts.h>
Packit 517ee8
#endif
Packit 517ee8
Packit 517ee8
#undef OSCAP_FTS_DEBUG
Packit 517ee8
Packit 517ee8
static OVAL_FTS *OVAL_FTS_new()
Packit 517ee8
{
Packit 517ee8
	OVAL_FTS *ofts = calloc(1, sizeof(OVAL_FTS));
Packit 517ee8
Packit 517ee8
	ofts->max_depth  = -1;
Packit 517ee8
	ofts->direction  = -1;
Packit 517ee8
	ofts->filesystem = -1;
Packit 517ee8
Packit 517ee8
	return (ofts);
Packit 517ee8
}
Packit 517ee8
Packit 517ee8
static void OVAL_FTS_free(OVAL_FTS *ofts)
Packit 517ee8
{
Packit 517ee8
	if (ofts->ofts_match_path_fts != NULL)
Packit 517ee8
		fts_close(ofts->ofts_match_path_fts);
Packit 517ee8
	if (ofts->ofts_recurse_path_fts != NULL)
Packit 517ee8
		fts_close(ofts->ofts_recurse_path_fts);
Packit 517ee8
Packit 517ee8
	free(ofts);
Packit 517ee8
	return;
Packit 517ee8
}
Packit 517ee8
Packit 517ee8
static int pathlen_from_ftse(int fts_pathlen, int fts_namelen)
Packit 517ee8
{
Packit 517ee8
	int pathlen;
Packit 517ee8
Packit 517ee8
	if (fts_pathlen > fts_namelen) {
Packit 517ee8
		pathlen = fts_pathlen - fts_namelen;
Packit 517ee8
		if (pathlen > 1)
Packit 517ee8
			pathlen--; /* strip last slash */
Packit 517ee8
	} else {
Packit 517ee8
		pathlen = fts_pathlen;
Packit 517ee8
	}
Packit 517ee8
Packit 517ee8
	return pathlen;
Packit 517ee8
}
Packit 517ee8
Packit 517ee8
static OVAL_FTSENT *OVAL_FTSENT_new(OVAL_FTS *ofts, FTSENT *fts_ent)
Packit 517ee8
{
Packit 517ee8
	OVAL_FTSENT *ofts_ent = calloc(1, sizeof(OVAL_FTSENT));
Packit 517ee8
Packit 517ee8
	ofts_ent->fts_info = fts_ent->fts_info;
Packit 517ee8
	/* The 'shift' variable stores length of the prefix if the prefix
Packit 517ee8
	 * is defined, otherwise it is set to 0. The value of 'shift' gives
Packit 517ee8
	 * us information how many characters of the path string are part of
Packit 517ee8
	 * the prefix and also where the actual path begins.
Packit 517ee8
	 * We use it to remove the prefix from the path.
Packit 517ee8
	 */
Packit 517ee8
	const size_t shift = ofts->prefix ? strlen(ofts->prefix) : 0;
Packit 517ee8
	if (ofts->ofts_sfilename || ofts->ofts_sfilepath) {
Packit 517ee8
		ofts_ent->path_len = pathlen_from_ftse(fts_ent->fts_pathlen, fts_ent->fts_namelen) - shift;
Packit 517ee8
		if (ofts_ent->path_len > 0) {
Packit 517ee8
			ofts_ent->path = malloc(ofts_ent->path_len + 1);
Packit 517ee8
			strncpy(ofts_ent->path, fts_ent->fts_path + shift, ofts_ent->path_len);
Packit 517ee8
			ofts_ent->path[ofts_ent->path_len] = '\0';
Packit 517ee8
		} else {
Packit 517ee8
			ofts_ent->path_len = 1;
Packit 517ee8
			ofts_ent->path = strdup("/");
Packit 517ee8
		}
Packit 517ee8
Packit 517ee8
		ofts_ent->file_len = fts_ent->fts_namelen;
Packit 517ee8
		ofts_ent->file = strdup(fts_ent->fts_name);
Packit 517ee8
	} else {
Packit 517ee8
		ofts_ent->path_len = fts_ent->fts_pathlen - shift;
Packit 517ee8
		if (ofts_ent->path_len > 0) {
Packit 517ee8
			ofts_ent->path = strdup(fts_ent->fts_path + shift);
Packit 517ee8
		} else {
Packit 517ee8
			ofts_ent->path_len = 1;
Packit 517ee8
			ofts_ent->path = strdup("/");
Packit 517ee8
		}
Packit 517ee8
Packit 517ee8
		ofts_ent->file_len = -1;
Packit 517ee8
		ofts_ent->file = NULL;
Packit 517ee8
	}
Packit 517ee8
Packit 517ee8
#if defined(OSCAP_FTS_DEBUG)
Packit 517ee8
	dD("New OVAL_FTSENT: file: '%s', path: '%s'.", ofts_ent->file, ofts_ent->path);
Packit 517ee8
#endif
Packit 517ee8
	return (ofts_ent);
Packit 517ee8
}
Packit 517ee8
Packit 517ee8
static void OVAL_FTSENT_free(OVAL_FTSENT *ofts_ent)
Packit 517ee8
{
Packit 517ee8
	free(ofts_ent->path);
Packit 517ee8
	free(ofts_ent->file);
Packit 517ee8
	free(ofts_ent);
Packit 517ee8
	return;
Packit 517ee8
}
Packit 517ee8
Packit 517ee8
#if defined(OS_SOLARIS)
Packit 517ee8
#ifndef MNTTYPE_SMB
Packit 517ee8
#define MNTTYPE_SMB	"smb"
Packit 517ee8
#endif
Packit 517ee8
#ifndef MNTTYPE_SMBFS
Packit 517ee8
#define MNTTYPE_SMBFS	"smbfs"
Packit 517ee8
#endif
Packit 517ee8
#ifndef MNTTYPE_PROC
Packit 517ee8
#define MNTTYPE_PROC	"proc"
Packit 517ee8
#endif
Packit 517ee8
Packit 517ee8
typedef struct zone_path {
Packit 517ee8
	avl_node_t avl_link_next;
Packit 517ee8
	char zpath[MAXPATHLEN];
Packit 517ee8
} zone_path_t;
Packit 517ee8
static avl_tree_t avl_tree_list;
Packit 517ee8
Packit 517ee8
Packit 517ee8
static bool valid_remote_fs(char *fstype)
Packit 517ee8
{
Packit 517ee8
	if (strcmp(fstype, MNTTYPE_NFS) == 0 ||
Packit 517ee8
	    strcmp(fstype, MNTTYPE_SMBFS) == 0 ||
Packit 517ee8
	    strcmp(fstype, MNTTYPE_SMB) == 0)
Packit 517ee8
		return (true);
Packit 517ee8
	return (false);
Packit 517ee8
}
Packit 517ee8
Packit 517ee8
static bool valid_local_fs(char *fstype)
Packit 517ee8
{
Packit 517ee8
	if (strcmp(fstype, MNTTYPE_SWAP) == 0 ||
Packit 517ee8
	    strcmp(fstype, MNTTYPE_MNTFS) == 0 ||
Packit 517ee8
	    strcmp(fstype, MNTTYPE_CTFS) == 0 ||
Packit 517ee8
	    strcmp(fstype, MNTTYPE_OBJFS) == 0 ||
Packit 517ee8
	    strcmp(fstype, MNTTYPE_SHAREFS) == 0 ||
Packit 517ee8
	    strcmp(fstype, MNTTYPE_PROC) == 0 ||
Packit 517ee8
	    strcmp(fstype, MNTTYPE_LOFS) == 0 ||
Packit 517ee8
	    strcmp(fstype, MNTTYPE_AUTOFS) == 0)
Packit 517ee8
		return (false);
Packit 517ee8
	return (true);
Packit 517ee8
}
Packit 517ee8
Packit 517ee8
/* function to compare two avl nodes in the avl tree */
Packit 517ee8
static int compare_zoneroot(const void *entry1, const void *entry2)
Packit 517ee8
{
Packit 517ee8
	zone_path_t *t1, *t2;
Packit 517ee8
	int comp;
Packit 517ee8
Packit 517ee8
	t1 = (zone_path_t *)entry1;
Packit 517ee8
	t2 = (zone_path_t *)entry2;
Packit 517ee8
	if ((comp = strcmp(t1->zpath, t2->zpath)) == 0) {
Packit 517ee8
		return (0);
Packit 517ee8
	}
Packit 517ee8
	return (comp > 0 ? 1 : -1);
Packit 517ee8
}
Packit 517ee8
Packit 517ee8
int load_zones_path_list()
Packit 517ee8
{
Packit 517ee8
	FILE *cookie;
Packit 517ee8
	char *name;
Packit 517ee8
	zone_state_t state_num;
Packit 517ee8
	zone_path_t *temp = NULL;
Packit 517ee8
	avl_index_t where;
Packit 517ee8
	char rpath[MAXPATHLEN];
Packit 517ee8
Packit 517ee8
	cookie = setzoneent();
Packit 517ee8
	if (getzoneid() != GLOBAL_ZONEID)
Packit 517ee8
		return (0);
Packit 517ee8
	avl_create(&avl_tree_list, compare_zoneroot,
Packit 517ee8
	    sizeof(zone_path_t), offsetof(zone_path_t, avl_link_next));
Packit 517ee8
	while ((name = getzoneent(cookie)) != NULL) {
Packit 517ee8
		if (strcmp(name, "global") == 0)
Packit 517ee8
			continue;
Packit 517ee8
		if (zone_get_state(name, &state_num) != Z_OK) {
Packit 517ee8
			dE("Could not get zone state for %s", name);
Packit 517ee8
			continue;
Packit 517ee8
		} else if (state_num > ZONE_STATE_CONFIGURED) {
Packit 517ee8
			temp = malloc(sizeof(zone_path_t));
Packit 517ee8
			if (temp == NULL) {
Packit 517ee8
				dE("Memory alloc failed");
Packit 517ee8
				return(1);
Packit 517ee8
			}
Packit 517ee8
			if (zone_get_zonepath(name, rpath,
Packit 517ee8
			    sizeof(rpath)) != Z_OK) {
Packit 517ee8
				dE("Could not get zone path for %s",
Packit 517ee8
				    name);
Packit 517ee8
				continue;
Packit 517ee8
			}
Packit 517ee8
			if (oscap_realpath(rpath, temp->zpath) != NULL)
Packit 517ee8
				avl_add(&avl_tree_list, temp);
Packit 517ee8
		}
Packit 517ee8
	}
Packit 517ee8
	endzoneent(cookie);
Packit 517ee8
	return (0);
Packit 517ee8
}
Packit 517ee8
Packit 517ee8
static void free_zones_path_list()
Packit 517ee8
{
Packit 517ee8
	zone_path_t *temp;
Packit 517ee8
	void* cookie = NULL;
Packit 517ee8
Packit 517ee8
	while ((temp = avl_destroy_nodes(&avl_tree_list, &cookie)) != NULL) {
Packit 517ee8
		free(temp);
Packit 517ee8
	}
Packit 517ee8
	avl_destroy(&avl_tree_list);
Packit 517ee8
}
Packit 517ee8
Packit 517ee8
static bool valid_local_zone(const char *path)
Packit 517ee8
{
Packit 517ee8
	zone_path_t temp;
Packit 517ee8
	avl_index_t where;
Packit 517ee8
Packit 517ee8
	strlcpy(temp.zpath, path, sizeof(temp.zpath));
Packit 517ee8
	if (avl_find(&avl_tree_list, &temp, &where) != NULL)
Packit 517ee8
		return (true);
Packit 517ee8
Packit 517ee8
	return (false);
Packit 517ee8
}
Packit 517ee8
Packit 517ee8
Packit 517ee8
#endif
Packit 517ee8
Packit 517ee8
static bool OVAL_FTS_localp(OVAL_FTS *ofts, const char *path, void *id)
Packit 517ee8
{
Packit 517ee8
#if defined(OS_SOLARIS)
Packit 517ee8
	if (id != NULL && (*(char*)id) != '\0') {
Packit 517ee8
		/* if not a valid local fs skip */
Packit 517ee8
		if (valid_local_fs((char*)id)) {
Packit 517ee8
			/* if recurse is local , skip remote fs
Packit 517ee8
			   and non-global zones */
Packit 517ee8
			if (ofts->filesystem == OVAL_RECURSE_FS_LOCAL) {
Packit 517ee8
				return (!(valid_remote_fs((char*)id) ||
Packit 517ee8
				    valid_local_zone(path)));
Packit 517ee8
			}
Packit 517ee8
			return (true);
Packit 517ee8
		}
Packit 517ee8
		return (false);
Packit 517ee8
	} else if (path != NULL) {
Packit 517ee8
		/* id was not set, because fts_read failed to stat the node */
Packit 517ee8
		struct stat sb;
Packit 517ee8
		if ((stat(path, &sb) == 0) && (valid_local_fs(sb.st_fstype))) {
Packit 517ee8
			/* if recurse is local , skip remote fs
Packit 517ee8
			   and non-global zones */
Packit 517ee8
			if (ofts->filesystem == OVAL_RECURSE_FS_LOCAL) {
Packit 517ee8
				return (!(valid_remote_fs(sb.st_fstype) ||
Packit 517ee8
				    valid_local_zone(path)));
Packit 517ee8
			}
Packit 517ee8
			return (true);
Packit 517ee8
		}
Packit 517ee8
		return (false);
Packit 517ee8
	} else {
Packit 517ee8
		return (false);
Packit 517ee8
	}
Packit 517ee8
#else
Packit 517ee8
	if (id != NULL)
Packit 517ee8
		return (fsdev_search(ofts->localdevs, id) == 1 ? true : false);
Packit 517ee8
	else if (path != NULL)
Packit 517ee8
		return (fsdev_path(ofts->localdevs, path) == 1 ? true : false);
Packit 517ee8
	else
Packit 517ee8
		return (false);
Packit 517ee8
#endif
Packit 517ee8
}
Packit 517ee8
Packit 517ee8
static char *__regex_locate(char *str)
Packit 517ee8
{
Packit 517ee8
    char *regex_sch = "^*?$.(["; /*<< regex start chars */
Packit 517ee8
    bool  escaped = false;
Packit 517ee8
Packit 517ee8
    while (*str != '\0') {
Packit 517ee8
	if (*str == '\\')
Packit 517ee8
	    escaped = !escaped;
Packit 517ee8
	else if (strchr(regex_sch, *str) != NULL) {
Packit 517ee8
	    if (!escaped)
Packit 517ee8
		return (str);
Packit 517ee8
	    else
Packit 517ee8
		escaped = false;
Packit 517ee8
	}
Packit 517ee8
	++str;
Packit 517ee8
    }
Packit 517ee8
Packit 517ee8
    return (str);
Packit 517ee8
}
Packit 517ee8
Packit 517ee8
static char *__string_unescape(char *str, size_t len)
Packit 517ee8
{
Packit 517ee8
    char *ret_str;
Packit 517ee8
    size_t i, j;
Packit 517ee8
Packit 517ee8
    if (str == NULL || len == 0)
Packit 517ee8
        return NULL;
Packit 517ee8
Packit 517ee8
    ret_str = strndup(str, len);
Packit 517ee8
Packit 517ee8
    if (ret_str == NULL)
Packit 517ee8
        return NULL;
Packit 517ee8
Packit 517ee8
    for (i = j = 0; i < len && j <= i; ++i) {
Packit 517ee8
        if (str[i] == '\\') {
Packit 517ee8
            if (str[i+1] == '\0') {
Packit 517ee8
                free(ret_str);
Packit 517ee8
                return NULL;
Packit 517ee8
            }
Packit 517ee8
            ret_str[j] = str[i+1];
Packit 517ee8
            ++j;
Packit 517ee8
            ++i;
Packit 517ee8
        } else {
Packit 517ee8
            ret_str[j] = str[i];
Packit 517ee8
            ++j;
Packit 517ee8
        }
Packit 517ee8
    }
Packit 517ee8
Packit 517ee8
    ret_str[j] = '\0';
Packit 517ee8
Packit 517ee8
    return ret_str;
Packit 517ee8
}
Packit 517ee8
Packit 517ee8
static char *extract_fixed_path_prefix(char *path)
Packit 517ee8
{
Packit 517ee8
	char *s;
Packit 517ee8
Packit 517ee8
	if (path[0] == '^')
Packit 517ee8
		path++;
Packit 517ee8
Packit 517ee8
	s = __regex_locate(path);
Packit 517ee8
	if (*s != '\0')
Packit 517ee8
		for (s--; s > (path + 1) && *s != '/'; s--);
Packit 517ee8
	if (s > (path + 1)) {
Packit 517ee8
		s = __string_unescape(path, (size_t) (s - path));
Packit 517ee8
		if (s != NULL) {
Packit 517ee8
			if (s[0] == '/')
Packit 517ee8
				return s;
Packit 517ee8
			free(s);
Packit 517ee8
		}
Packit 517ee8
	}
Packit 517ee8
Packit 517ee8
	return strdup("/");
Packit 517ee8
}
Packit 517ee8
Packit 517ee8
static int badpartial_check_slash(const char *pattern)
Packit 517ee8
{
Packit 517ee8
	pcre *regex;
Packit 517ee8
	const char *errptr = NULL;
Packit 517ee8
	int errofs = 0, fb, ret;
Packit 517ee8
Packit 517ee8
	regex = pcre_compile(pattern + 1 /* skip '^' */, 0, &errptr, &errofs, NULL);
Packit 517ee8
	if (regex == NULL) {
Packit 517ee8
		dE("Failed to validate the pattern: pcre_compile(): "
Packit 517ee8
		   "error: '%s', error offset: %d, pattern: '%s'.\n",
Packit 517ee8
		   errptr, errofs, pattern);
Packit 517ee8
		return -1;
Packit 517ee8
	}
Packit 517ee8
	ret = pcre_fullinfo(regex, NULL, PCRE_INFO_FIRSTBYTE, &fb;;
Packit 517ee8
	pcre_free(regex);
Packit 517ee8
	regex = NULL;
Packit 517ee8
	if (ret != 0) {
Packit 517ee8
		dE("Failed to validate the pattern: pcre_fullinfo(): "
Packit 517ee8
		   "return code: %d, pattern: '%s'.\n", ret, pattern);
Packit 517ee8
		return -1;
Packit 517ee8
	}
Packit 517ee8
	if (fb != '/') {
Packit 517ee8
		dE("Failed to validate the pattern: pcre_fullinfo(): "
Packit 517ee8
		   "first byte: %d '%c', pattern: '%s' - the first "
Packit 517ee8
		   "byte should be a '/'.\n", fb, fb, pattern);
Packit 517ee8
		return -2;
Packit 517ee8
	}
Packit 517ee8
Packit 517ee8
	return 0;
Packit 517ee8
}
Packit 517ee8
Packit 517ee8
#define TEST_PATH1 "/"
Packit 517ee8
#define TEST_PATH2 "x"
Packit 517ee8
Packit 517ee8
static int badpartial_transform_pattern(char *pattern, pcre **regex_out)
Packit 517ee8
{
Packit 517ee8
	/*
Packit 517ee8
	  PCREPARTIAL(3)
Packit 517ee8
	  http://pcre.org/pcre.txt
Packit 517ee8
	  Last updated: 21 January 2012
Packit 517ee8
Packit 517ee8
	  For releases of PCRE prior to 8.00, because of the way
Packit 517ee8
	  certain internal optimizations were implemented in the
Packit 517ee8
	  pcre_exec() function, the PCRE_PARTIAL option (predecessor
Packit 517ee8
	  of PCRE_PARTIAL_SOFT) could not be used with all patterns.
Packit 517ee8
Packit 517ee8
	  Items that were formerly restricted were repeated single
Packit 517ee8
	  characters and repeated metasequences. If PCRE_PARTIAL was
Packit 517ee8
	  set for a pattern that did not conform to the restrictions,
Packit 517ee8
	  pcre_exec() returned the error code PCRE_ERROR_BADPARTIAL
Packit 517ee8
	  (-13).
Packit 517ee8
	*/
Packit 517ee8
Packit 517ee8
	int ret, brkt_lvl = 0, errofs = 0;
Packit 517ee8
	const char *rchars = "\\[]()*+{"; /* probably incomplete */
Packit 517ee8
	const char *test_path1 = TEST_PATH1;
Packit 517ee8
	const char *errptr = NULL;
Packit 517ee8
	char *s, *brkt_mark;
Packit 517ee8
	bool bracketed = false, found_regex = false;
Packit 517ee8
	pcre *regex;
Packit 517ee8
Packit 517ee8
	/* The processing bellow builds upon the assumption that
Packit 517ee8
	   the pattern has been validated by pcre_compile() */
Packit 517ee8
	for (s = brkt_mark = pattern; (s = strpbrk(s, rchars)) != NULL; s++) {
Packit 517ee8
		switch (*s) {
Packit 517ee8
		case '\\':
Packit 517ee8
			s++;
Packit 517ee8
			break;
Packit 517ee8
		case '[':
Packit 517ee8
			if (!bracketed) {
Packit 517ee8
				bracketed = true;
Packit 517ee8
				if (s[1] == ']')
Packit 517ee8
					s++;
Packit 517ee8
			}
Packit 517ee8
			break;
Packit 517ee8
		case ']':
Packit 517ee8
			bracketed = false;
Packit 517ee8
			break;
Packit 517ee8
		case '(':
Packit 517ee8
			if (!bracketed) {
Packit 517ee8
				if (brkt_lvl++ == 0)
Packit 517ee8
					brkt_mark = s;
Packit 517ee8
			}
Packit 517ee8
			break;
Packit 517ee8
		case ')':
Packit 517ee8
			if (!bracketed)
Packit 517ee8
				brkt_lvl--;
Packit 517ee8
			break;
Packit 517ee8
		default:
Packit 517ee8
			if (!bracketed)
Packit 517ee8
				found_regex = true;
Packit 517ee8
			break;
Packit 517ee8
		}
Packit 517ee8
		if (found_regex)
Packit 517ee8
			break;
Packit 517ee8
	}
Packit 517ee8
Packit 517ee8
	if (s == NULL) {
Packit 517ee8
		dW("Nonfatal failure: can't transform the pattern for partial "
Packit 517ee8
		   "match optimization: none of the suspected culprits found, "
Packit 517ee8
		   "pattern: '%s'.", pattern);
Packit 517ee8
		return -1;
Packit 517ee8
	}
Packit 517ee8
Packit 517ee8
	if (brkt_lvl > 0)
Packit 517ee8
		*brkt_mark = '\0';
Packit 517ee8
	else
Packit 517ee8
		*s = '\0';
Packit 517ee8
Packit 517ee8
	regex = pcre_compile(pattern, 0, &errptr, &errofs, NULL);
Packit 517ee8
	if (regex == NULL) {
Packit 517ee8
		dW("Nonfatal failure: can't transform the pattern for partial "
Packit 517ee8
		   "match optimization, error: '%s', error offset: %d, "
Packit 517ee8
		   "pattern: '%s'.", errptr, errofs, pattern);
Packit 517ee8
		return -1;
Packit 517ee8
	}
Packit 517ee8
Packit 517ee8
	ret = pcre_exec(regex, NULL, test_path1, strlen(test_path1), 0,
Packit 517ee8
		PCRE_PARTIAL, NULL, 0);
Packit 517ee8
	if (ret != PCRE_ERROR_PARTIAL && ret < 0) {
Packit 517ee8
		pcre_free(regex);
Packit 517ee8
		dW("Nonfatal failure: can't transform the pattern for partial "
Packit 517ee8
		   "match optimization, pcre_exec() return code: %d, pattern: "
Packit 517ee8
		   "'%s'.", ret, pattern);
Packit 517ee8
		return -1;
Packit 517ee8
	}
Packit 517ee8
Packit 517ee8
	if (regex_out != NULL)
Packit 517ee8
		*regex_out = regex;
Packit 517ee8
Packit 517ee8
	return 0;
Packit 517ee8
}
Packit 517ee8
Packit 517ee8
/* Verify that the path is usable and try to craft a regex to speed up
Packit 517ee8
   the filesystem traversal. If the path to match is ill-designed, an
Packit 517ee8
   ugly heuristic is employed to obtain something meaningfull. */
Packit 517ee8
static int process_pattern_match(const char *path, pcre **regex_out)
Packit 517ee8
{
Packit 517ee8
	int ret, errofs = 0;
Packit 517ee8
	char *pattern;
Packit 517ee8
	const char *test_path1 = TEST_PATH1;
Packit 517ee8
	//const char *test_path2 = TEST_PATH2;
Packit 517ee8
	const char *errptr = NULL;
Packit 517ee8
	pcre *regex;
Packit 517ee8
Packit 517ee8
	if (path[0] != '^') {
Packit 517ee8
		/* Matching has to have a fixed starting point and thus
Packit 517ee8
		   every pattern has to start with a caret. */
Packit 517ee8
		size_t plen;
Packit 517ee8
Packit 517ee8
		plen = strlen(path) + 1;
Packit 517ee8
		pattern = malloc(plen + 1);
Packit 517ee8
		pattern[0] = '^';
Packit 517ee8
		memcpy(pattern + 1, path, plen);
Packit 517ee8
		dI("The pattern '%s' doesn't contain a leading caret - added. "
Packit 517ee8
		   "All paths with the 'pattern match' operation must begin "
Packit 517ee8
		   "with a caret.", path);
Packit 517ee8
	} else {
Packit 517ee8
		pattern = strdup(path);
Packit 517ee8
	}
Packit 517ee8
Packit 517ee8
	regex = pcre_compile(pattern, 0, &errptr, &errofs, NULL);
Packit 517ee8
	if (regex == NULL) {
Packit 517ee8
		dE("Failed to validate the pattern: pcre_compile(): "
Packit 517ee8
		   "error offset: %d, error: '%s', pattern: '%s'.\n",
Packit 517ee8
		   errofs, errptr, pattern);
Packit 517ee8
		free(pattern);
Packit 517ee8
		return -1;
Packit 517ee8
	}
Packit 517ee8
	ret = pcre_exec(regex, NULL, test_path1, strlen(test_path1), 0,
Packit 517ee8
		PCRE_PARTIAL, NULL, 0);
Packit 517ee8
Packit 517ee8
	switch (ret) {
Packit 517ee8
	case PCRE_ERROR_PARTIAL:
Packit 517ee8
		/* The pattern has matched a prefix of the test path
Packit 517ee8
		   and probably begins with a slash. Make sure that it
Packit 517ee8
		   doesn't match an arbitrary prefix. */
Packit 517ee8
Packit 517ee8
		/* todo:
Packit 517ee8
		   Convince folks that they should really fix their
Packit 517ee8
		   OVAL definitions that use ".*" as 'path' and then
Packit 517ee8
		   uncomment this.
Packit 517ee8
Packit 517ee8
		dD("pcre_exec() returned PCRE_ERROR_PARTIAL for pattern '%s' "
Packit 517ee8
		   "and test path '%s'.\n", pattern, test_path1);
Packit 517ee8
		ret = pcre_exec(regex, NULL, test_path2, strlen(test_path2),
Packit 517ee8
			0, PCRE_PARTIAL, NULL, 0);
Packit 517ee8
		if (ret == PCRE_ERROR_PARTIAL || ret >= 0) {
Packit 517ee8
			dE("Failed to validate the pattern: test path '%s' "
Packit 517ee8
			   "matched by pattern '%s' - the pattern is too "
Packit 517ee8
			   "general, i.e. inefficient. This could take a "
Packit 517ee8
			   "lifetime to complete.\n", test_path2, pattern);
Packit 517ee8
			pcre_free(regex);
Packit 517ee8
			free(pattern);
Packit 517ee8
			return -2;
Packit 517ee8
		}
Packit 517ee8
		*/
Packit 517ee8
		break;
Packit 517ee8
	case PCRE_ERROR_BADPARTIAL:
Packit 517ee8
		dD("pcre_exec() returned PCRE_ERROR_BADPARTIAL for pattern "
Packit 517ee8
		   "'%s' and a test path '%s'. Falling back to "
Packit 517ee8
		   "pcre_fullinfo().\n", pattern, test_path1);
Packit 517ee8
		pcre_free(regex);
Packit 517ee8
		regex = NULL;
Packit 517ee8
Packit 517ee8
		/* Fallback to first byte check to determin if
Packit 517ee8
		   the pattern begins with a slash. */
Packit 517ee8
		ret = badpartial_check_slash((const char *) pattern);
Packit 517ee8
		if (ret != 0) {
Packit 517ee8
			free(pattern);
Packit 517ee8
			return ret;
Packit 517ee8
		}
Packit 517ee8
		/* The pattern contains features that this version of
Packit 517ee8
		   PCRE can't handle for partial matching. At least
Packit 517ee8
		   try to find the longest well-bracketed prefix that
Packit 517ee8
		   can be handled. */
Packit 517ee8
		badpartial_transform_pattern(pattern, &regex);
Packit 517ee8
		break;
Packit 517ee8
	case PCRE_ERROR_NOMATCH:
Packit 517ee8
		/* The pattern doesn't contain a leading slash (or
Packit 517ee8
		   some part of this code is broken). Apologise to the
Packit 517ee8
		   user and fail. */
Packit 517ee8
		dE("Failed to validate the pattern: pcre_exec() returned "
Packit 517ee8
		   "PCRE_ERROR_NOMATCH for pattern '%s' and a test path '%s'. "
Packit 517ee8
		   "This indicates the pattern doesn't match a leading '/'.\n",
Packit 517ee8
		   pattern, test_path1);
Packit 517ee8
		pcre_free(regex);
Packit 517ee8
		free(pattern);
Packit 517ee8
		return -2;
Packit 517ee8
	default:
Packit 517ee8
		if (ret >= 0) {
Packit 517ee8
			/* The pattern actually matches the test
Packit 517ee8
			   path. Iteresting… Make sure that it doesn't
Packit 517ee8
			   match an arbitrary prefix. */
Packit 517ee8
Packit 517ee8
			/* todo:
Packit 517ee8
			   Convince folks that they should really fix
Packit 517ee8
			   their OVAL definitions that use ".*" as
Packit 517ee8
			   'path' and then uncomment this.
Packit 517ee8
Packit 517ee8
			ret = pcre_exec(regex, NULL, test_path2, strlen(test_path2),
Packit 517ee8
					0, PCRE_PARTIAL, NULL, 0);
Packit 517ee8
			if (ret == PCRE_ERROR_PARTIAL || ret >= 0) {
Packit 517ee8
				dE("Failed to validate the pattern: test path '%s' "
Packit 517ee8
				   "matched by pattern '%s' - the pattern is too "
Packit 517ee8
				   "general, i.e. inefficient. This could take a "
Packit 517ee8
				   "lifetime to complete.\n", test_path2, pattern);
Packit 517ee8
				pcre_free(regex);
Packit 517ee8
				free(pattern);
Packit 517ee8
				return -2;
Packit 517ee8
			}
Packit 517ee8
			*/
Packit 517ee8
			break;
Packit 517ee8
		}
Packit 517ee8
		/* Some other error. */
Packit 517ee8
		dE("Failed to validate the pattern: pcre_exec() return "
Packit 517ee8
		   "code: %d, pattern '%s', test path '%s'.\n", ret,
Packit 517ee8
		   pattern, test_path1);
Packit 517ee8
		pcre_free(regex);
Packit 517ee8
		free(pattern);
Packit 517ee8
		return -1;
Packit 517ee8
	}
Packit 517ee8
Packit 517ee8
	if (regex == NULL) {
Packit 517ee8
		dD("Disabling partial match optimization.");
Packit 517ee8
	} else {
Packit 517ee8
		dD("Enabling partial match optimization using "
Packit 517ee8
		   "pattern: '%s'.", pattern);
Packit 517ee8
		if (regex_out != NULL)
Packit 517ee8
			*regex_out = regex;
Packit 517ee8
	}
Packit 517ee8
Packit 517ee8
	free(pattern);
Packit 517ee8
Packit 517ee8
	return 0;
Packit 517ee8
}
Packit 517ee8
Packit 517ee8
Packit 517ee8
#undef TEST_PATH1
Packit 517ee8
#undef TEST_PATH2
Packit 517ee8
Packit 517ee8
OVAL_FTS *oval_fts_open(SEXP_t *path, SEXP_t *filename, SEXP_t *filepath, SEXP_t *behaviors, SEXP_t* result)
Packit 517ee8
{
Packit 517ee8
	return oval_fts_open_prefixed(NULL, path, filename, filepath, behaviors, result);
Packit 517ee8
}
Packit 517ee8
Packit 517ee8
OVAL_FTS *oval_fts_open_prefixed(const char *prefix, SEXP_t *path, SEXP_t *filename, SEXP_t *filepath, SEXP_t *behaviors, SEXP_t* result)
Packit 517ee8
{
Packit 517ee8
	OVAL_FTS *ofts;
Packit 517ee8
Packit 517ee8
	char cstr_path[PATH_MAX+1];
Packit 517ee8
	char cstr_file[PATH_MAX+1];
Packit 517ee8
	char cstr_buff[32];
Packit 517ee8
	const char *paths[2] = { NULL, NULL };
Packit 517ee8
Packit 517ee8
	SEXP_t *r0;
Packit 517ee8
Packit 517ee8
	int mtc_fts_options = FTS_PHYSICAL | FTS_COMFOLLOW | FTS_NOCHDIR;
Packit 517ee8
	int rec_fts_options = FTS_PHYSICAL | FTS_COMFOLLOW | FTS_NOCHDIR;
Packit 517ee8
	int max_depth   = -1;
Packit 517ee8
	int direction   = -1;
Packit 517ee8
	int recurse     = -1;
Packit 517ee8
	int filesystem  = -1;
Packit 517ee8
Packit 517ee8
	uint32_t path_op;
Packit 517ee8
	bool nilfilename = false;
Packit 517ee8
	pcre *regex = NULL;
Packit 517ee8
	struct stat st;
Packit 517ee8
Packit 517ee8
	if ((path != NULL || filename != NULL || filepath == NULL)
Packit 517ee8
			&& (path == NULL || filepath != NULL)) {
Packit 517ee8
		return NULL;
Packit 517ee8
	}
Packit 517ee8
	if (behaviors == NULL) {
Packit 517ee8
		return NULL;
Packit 517ee8
	}
Packit 517ee8
Packit 517ee8
	if (path)
Packit 517ee8
		PROBE_ENT_AREF(path, r0, "operation", /**/);
Packit 517ee8
	else
Packit 517ee8
		PROBE_ENT_AREF(filepath, r0, "operation", /**/);
Packit 517ee8
Packit 517ee8
	if (r0 != NULL) {
Packit 517ee8
		path_op = SEXP_number_getu(r0);
Packit 517ee8
		SEXP_free(r0);
Packit 517ee8
	} else {
Packit 517ee8
		path_op = OVAL_OPERATION_EQUALS;
Packit 517ee8
	}
Packit 517ee8
#if defined(OSCAP_FTS_DEBUG)
Packit 517ee8
	dD("path_op: %u, '%s'.", path_op, oval_operation_get_text(path_op));
Packit 517ee8
#endif
Packit 517ee8
	if (path) { /* filepath == NULL */
Packit 517ee8
		PROBE_ENT_STRVAL(path, cstr_path, sizeof cstr_path,
Packit 517ee8
				 return NULL;, return NULL;);
Packit 517ee8
		if (probe_ent_getvals(filename, NULL) == 0) {
Packit 517ee8
			nilfilename = true;
Packit 517ee8
		} else {
Packit 517ee8
			PROBE_ENT_STRVAL(filename, cstr_file, sizeof cstr_file,
Packit 517ee8
					 return NULL;, /* noop */;);
Packit 517ee8
		}
Packit 517ee8
#if defined(OSCAP_FTS_DEBUG)
Packit 517ee8
		dD("path: '%s', filename: '%s', filename: %d.", cstr_path, nilfilename ? "" : cstr_file, nilfilename);
Packit 517ee8
#endif
Packit 517ee8
	} else { /* filepath != NULL */
Packit 517ee8
		PROBE_ENT_STRVAL(filepath, cstr_path, sizeof cstr_path, return NULL;, return NULL;);
Packit 517ee8
	}
Packit 517ee8
Packit 517ee8
	/* max_depth */
Packit 517ee8
	PROBE_ENT_AREF(behaviors, r0, "max_depth", return NULL;);
Packit 517ee8
	SEXP_string_cstr_r(r0, cstr_buff, sizeof cstr_buff - 1);
Packit 517ee8
	max_depth = strtol(cstr_buff, NULL, 10);
Packit 517ee8
	if (errno == EINVAL || errno == ERANGE) {
Packit 517ee8
		dE("Invalid value of the `%s' attribute: %s", "recurse_direction", cstr_buff);
Packit 517ee8
		SEXP_free(r0);
Packit 517ee8
		return (NULL);
Packit 517ee8
	}
Packit 517ee8
#if defined(OSCAP_FTS_DEBUG)
Packit 517ee8
	dD("bh.max_depth: %s => max_depth: %d", cstr_buff, max_depth);
Packit 517ee8
#endif
Packit 517ee8
	SEXP_free(r0);
Packit 517ee8
Packit 517ee8
	/* recurse_direction */
Packit 517ee8
	PROBE_ENT_AREF(behaviors, r0, "recurse_direction", return NULL;);
Packit 517ee8
	SEXP_string_cstr_r(r0, cstr_buff, sizeof cstr_buff - 1);
Packit 517ee8
	/* todo: use oscap_string_to_enum() */
Packit 517ee8
	if (strcmp(cstr_buff, "none") == 0) {
Packit 517ee8
		direction = OVAL_RECURSE_DIRECTION_NONE;
Packit 517ee8
	} else if (strcmp(cstr_buff, "down") == 0) {
Packit 517ee8
		direction = OVAL_RECURSE_DIRECTION_DOWN;
Packit 517ee8
	} else if (strcmp(cstr_buff, "up") == 0) {
Packit 517ee8
		direction = OVAL_RECURSE_DIRECTION_UP;
Packit 517ee8
	} else {
Packit 517ee8
		dE("Invalid direction: %s", cstr_buff);
Packit 517ee8
		SEXP_free(r0);
Packit 517ee8
		return (NULL);
Packit 517ee8
	}
Packit 517ee8
#if defined(OSCAP_FTS_DEBUG)
Packit 517ee8
	dD("bh.direction: %s => direction: %d", cstr_buff, direction);
Packit 517ee8
#endif
Packit 517ee8
	SEXP_free(r0);
Packit 517ee8
Packit 517ee8
	/* recurse */
Packit 517ee8
	PROBE_ENT_AREF(behaviors, r0, "recurse", /**/);
Packit 517ee8
	if (r0 != NULL) {
Packit 517ee8
		SEXP_string_cstr_r(r0, cstr_buff, sizeof cstr_buff - 1);
Packit 517ee8
		/* todo: use oscap_string_to_enum() */
Packit 517ee8
		if (strcmp(cstr_buff, "symlinks and directories") == 0) {
Packit 517ee8
			recurse = OVAL_RECURSE_SYMLINKS_AND_DIRS;
Packit 517ee8
		} else if (strcmp(cstr_buff, "files and directories") == 0) {
Packit 517ee8
			recurse = OVAL_RECURSE_FILES_AND_DIRS;
Packit 517ee8
		} else if (strcmp(cstr_buff, "symlinks") == 0) {
Packit 517ee8
			recurse = OVAL_RECURSE_SYMLINKS;
Packit 517ee8
		} else if (strcmp(cstr_buff, "directories") == 0) {
Packit 517ee8
			recurse = OVAL_RECURSE_DIRS;
Packit 517ee8
		} else {
Packit 517ee8
			dE("Invalid recurse: %s", cstr_buff);
Packit 517ee8
			SEXP_free(r0);
Packit 517ee8
			return (NULL);
Packit 517ee8
		}
Packit 517ee8
	} else {
Packit 517ee8
		recurse = OVAL_RECURSE_SYMLINKS_AND_DIRS;
Packit 517ee8
	}
Packit 517ee8
#if defined(OSCAP_FTS_DEBUG)
Packit 517ee8
	dD("bh.recurse: %s => recurse: %d", cstr_buff, recurse);
Packit 517ee8
#endif
Packit 517ee8
	SEXP_free(r0);
Packit 517ee8
Packit 517ee8
	/* recurse_file_system */
Packit 517ee8
	PROBE_ENT_AREF(behaviors, r0, "recurse_file_system", /**/);
Packit 517ee8
Packit 517ee8
	if (r0 != NULL) {
Packit 517ee8
		SEXP_string_cstr_r(r0, cstr_buff, sizeof cstr_buff - 1);
Packit 517ee8
		/* todo: use oscap_string_to_enum() */
Packit 517ee8
		if (strcmp(cstr_buff, "local") == 0) {
Packit 517ee8
			filesystem = OVAL_RECURSE_FS_LOCAL;
Packit 517ee8
		} else if (strcmp(cstr_buff, "all") == 0) {
Packit 517ee8
			filesystem = OVAL_RECURSE_FS_ALL;
Packit 517ee8
		} else if (strcmp(cstr_buff, "defined") == 0) {
Packit 517ee8
			filesystem = OVAL_RECURSE_FS_DEFINED;
Packit 517ee8
			rec_fts_options |= FTS_XDEV;
Packit 517ee8
		} else {
Packit 517ee8
			dE("Invalid recurse filesystem: %s", cstr_buff);
Packit 517ee8
			SEXP_free(r0);
Packit 517ee8
			return (NULL);
Packit 517ee8
		}
Packit 517ee8
	} else {
Packit 517ee8
		filesystem = OVAL_RECURSE_FS_ALL;
Packit 517ee8
	}
Packit 517ee8
#if defined(OSCAP_FTS_DEBUG)
Packit 517ee8
	dD("bh.filesystem: %s => filesystem: %d", cstr_buff, filesystem);
Packit 517ee8
#endif
Packit 517ee8
	SEXP_free(r0);
Packit 517ee8
Packit 517ee8
	/* todo:
Packit 517ee8
	   Still missing is a propagation of the error to the
Packit 517ee8
	   user. Currently, all the information is provided in the
Packit 517ee8
	   debug log, but the oval_fts api has no way of passing this
Packit 517ee8
	   information to the user.
Packit 517ee8
	*/
Packit 517ee8
Packit 517ee8
	if (path_op == OVAL_OPERATION_EQUALS) {
Packit 517ee8
		paths[0] = strdup(cstr_path);
Packit 517ee8
	} else if (path_op == OVAL_OPERATION_PATTERN_MATCH) {
Packit 517ee8
		if (process_pattern_match(cstr_path, &regex) != 0)
Packit 517ee8
			return NULL;
Packit 517ee8
		paths[0] = extract_fixed_path_prefix(cstr_path);
Packit 517ee8
		dD("Extracted fixed path: '%s'.", paths[0]);
Packit 517ee8
	} else {
Packit 517ee8
		paths[0] = strdup("/");
Packit 517ee8
	}
Packit 517ee8
Packit 517ee8
	if (prefix != NULL) {
Packit 517ee8
		char *path_with_prefix = oscap_path_join(prefix, paths[0]);
Packit 517ee8
		free((void *) paths[0]);
Packit 517ee8
		paths[0] = path_with_prefix;
Packit 517ee8
	}
Packit 517ee8
	dI("Opening file '%s'.", paths[0]);
Packit 517ee8
	/* Fail if the provided path doensn't actually exist. Symlinks
Packit 517ee8
	   without targets are accepted. */
Packit 517ee8
	if (lstat(paths[0], &st) == -1) {
Packit 517ee8
		if (errno) {
Packit 517ee8
			dD("lstat() failed: errno: %d, '%s'.",
Packit 517ee8
			   errno, strerror(errno));
Packit 517ee8
		}
Packit 517ee8
		free((void *) paths[0]);
Packit 517ee8
		return NULL;
Packit 517ee8
	}
Packit 517ee8
Packit 517ee8
	ofts = OVAL_FTS_new();
Packit 517ee8
	ofts->prefix = prefix;
Packit 517ee8
Packit 517ee8
	/* reset errno as fts_open() doesn't do it itself. */
Packit 517ee8
	errno = 0;
Packit 517ee8
	ofts->ofts_match_path_fts = fts_open((char * const *) paths, mtc_fts_options, NULL);
Packit 517ee8
	free((void *) paths[0]);
Packit 517ee8
	/* fts_open() doesn't return NULL for all errors (e.g. nonexistent paths),
Packit 517ee8
	   so check errno to detect it. Far from being perfect. */
Packit 517ee8
	if (ofts->ofts_match_path_fts == NULL || errno != 0) {
Packit 517ee8
		dE("fts_open() failed, errno: %d \"%s\".", errno, strerror(errno));
Packit 517ee8
		OVAL_FTS_free(ofts);
Packit 517ee8
		return (NULL);
Packit 517ee8
	}
Packit 517ee8
Packit 517ee8
	ofts->ofts_recurse_path_fts_opts = rec_fts_options;
Packit 517ee8
	ofts->ofts_path_op = path_op;
Packit 517ee8
	if (regex != NULL) {
Packit 517ee8
		const char *errptr = NULL;
Packit 517ee8
Packit 517ee8
		ofts->ofts_path_regex = regex;
Packit 517ee8
		ofts->ofts_path_regex_extra = pcre_study(regex, 0, &errptr);
Packit 517ee8
	}
Packit 517ee8
Packit 517ee8
	if (filesystem == OVAL_RECURSE_FS_LOCAL) {
Packit 517ee8
#if defined(OS_SOLARIS)
Packit 517ee8
		ofts->localdevs = NULL;
Packit 517ee8
#else
Packit 517ee8
		ofts->localdevs = fsdev_init();
Packit 517ee8
		if (ofts->localdevs == NULL) {
Packit 517ee8
			dE("fsdev_init() failed.");
Packit 517ee8
			/* One dummy read to get rid of an uninitialized
Packit 517ee8
			 * value in the FTS data before calling
Packit 517ee8
			 * fts_close() on it. */
Packit 517ee8
			fts_read(ofts->ofts_match_path_fts);
Packit 517ee8
			oval_fts_close(ofts);
Packit 517ee8
			return (NULL);
Packit 517ee8
		}
Packit 517ee8
#endif
Packit 517ee8
	} else if (filesystem == OVAL_RECURSE_FS_DEFINED) {
Packit 517ee8
		/* store the device id for future comparison */
Packit 517ee8
		FTSENT *fts_ent;
Packit 517ee8
Packit 517ee8
		fts_ent = fts_read(ofts->ofts_match_path_fts);
Packit 517ee8
		if (fts_ent != NULL) {
Packit 517ee8
			ofts->ofts_recurse_path_devid = fts_ent->fts_statp->st_dev;
Packit 517ee8
			fts_set(ofts->ofts_match_path_fts, fts_ent, FTS_AGAIN);
Packit 517ee8
		}
Packit 517ee8
	}
Packit 517ee8
Packit 517ee8
	ofts->recurse = recurse;
Packit 517ee8
	ofts->filesystem = filesystem;
Packit 517ee8
Packit 517ee8
	if (path) { /* filepath == NULL */
Packit 517ee8
		ofts->ofts_spath = SEXP_ref(path); /* path entity */
Packit 517ee8
		if (!nilfilename)
Packit 517ee8
			ofts->ofts_sfilename = SEXP_ref(filename); /* filename entity */
Packit 517ee8
Packit 517ee8
		ofts->max_depth = max_depth;
Packit 517ee8
		ofts->direction = direction;
Packit 517ee8
	} else { /* filepath != NULL */
Packit 517ee8
		ofts->ofts_sfilepath = SEXP_ref(filepath);
Packit 517ee8
	}
Packit 517ee8
Packit 517ee8
#if defined(OS_SOLARIS)
Packit 517ee8
	if (load_zones_path_list() != 0) {
Packit 517ee8
		dE("Failed to load zones path info. Recursing non-global zones.");
Packit 517ee8
		free_zones_path_list();
Packit 517ee8
	}
Packit 517ee8
#endif
Packit 517ee8
Packit 517ee8
	ofts->result = result;
Packit 517ee8
Packit 517ee8
	return (ofts);
Packit 517ee8
}
Packit 517ee8
Packit 517ee8
static inline int _oval_fts_is_local(OVAL_FTS *ofts, FTSENT *fts_ent) {
Packit 517ee8
# if defined(OS_SOLARIS)
Packit 517ee8
	/* pseudo filesystems will be skipped */
Packit 517ee8
	/* don't recurse into remote fs if local is specified */
Packit 517ee8
	return ((fts_ent->fts_info == FTS_D || fts_ent->fts_info == FTS_SL)
Packit 517ee8
	    && (!OVAL_FTS_localp(ofts, fts_ent->fts_path,
Packit 517ee8
	    (fts_ent->fts_statp != NULL) ?
Packit 517ee8
	    &fts_ent->fts_statp->st_fstype : NULL)));
Packit 517ee8
#else
Packit 517ee8
	/* don't recurse into non-local filesystems */
Packit 517ee8
	return (ofts->filesystem == OVAL_RECURSE_FS_LOCAL
Packit 517ee8
	    && (fts_ent->fts_info == FTS_D || fts_ent->fts_info == FTS_SL)
Packit 517ee8
	    && (!OVAL_FTS_localp(ofts, fts_ent->fts_path,
Packit 517ee8
				 (fts_ent->fts_statp != NULL) ?
Packit 517ee8
				 &fts_ent->fts_statp->st_dev : NULL)));
Packit 517ee8
#endif
Packit 517ee8
}
Packit 517ee8
Packit 517ee8
/* find the first matching path or filepath */
Packit 517ee8
static FTSENT *oval_fts_read_match_path(OVAL_FTS *ofts)
Packit 517ee8
{
Packit 517ee8
	FTSENT *fts_ent = NULL;
Packit 517ee8
	SEXP_t *stmp;
Packit 517ee8
	oval_result_t ores;
Packit 517ee8
Packit 517ee8
	/* iterate until a match is found or all elements have been traversed */
Packit 517ee8
	for (;;) {
Packit 517ee8
		fts_ent = fts_read(ofts->ofts_match_path_fts);
Packit 517ee8
		if (fts_ent == NULL)
Packit 517ee8
			return NULL;
Packit 517ee8
		switch (fts_ent->fts_info) {
Packit 517ee8
		case FTS_DP:
Packit 517ee8
			continue;
Packit 517ee8
		case FTS_DC:
Packit 517ee8
			dW("Filesystem tree cycle detected at '%s'.", fts_ent->fts_path);
Packit 517ee8
			fts_set(ofts->ofts_match_path_fts, fts_ent, FTS_SKIP);
Packit 517ee8
			continue;
Packit 517ee8
		}
Packit 517ee8
Packit 517ee8
#if defined(OSCAP_FTS_DEBUG)
Packit 517ee8
		dD("fts_path: '%s' (l=%d)."
Packit 517ee8
		   "fts_name: '%s' (l=%d).\n"
Packit 517ee8
		   "fts_info: %u.\n", fts_ent->fts_path, fts_ent->fts_pathlen,
Packit 517ee8
		   fts_ent->fts_name, fts_ent->fts_namelen, fts_ent->fts_info);
Packit 517ee8
#endif
Packit 517ee8
Packit 517ee8
		if (fts_ent->fts_info == FTS_SL) {
Packit 517ee8
#if defined(OSCAP_FTS_DEBUG)
Packit 517ee8
			dD("Only the target of a symlink gets reported, skipping '%s'.", fts_ent->fts_path, fts_ent->fts_name);
Packit 517ee8
#endif
Packit 517ee8
			fts_set(ofts->ofts_match_path_fts, fts_ent, FTS_FOLLOW);
Packit 517ee8
			continue;
Packit 517ee8
		}
Packit 517ee8
		if (_oval_fts_is_local(ofts, fts_ent)) {
Packit 517ee8
			dI("Don't recurse into non-local filesystems, skipping '%s'.", fts_ent->fts_path);
Packit 517ee8
			fts_set(ofts->ofts_recurse_path_fts, fts_ent, FTS_SKIP);
Packit 517ee8
			continue;
Packit 517ee8
		}
Packit 517ee8
		/* don't recurse beyond the initial filesystem */
Packit 517ee8
		if (ofts->filesystem == OVAL_RECURSE_FS_DEFINED
Packit 517ee8
		    && (fts_ent->fts_info == FTS_D || fts_ent->fts_info == FTS_SL)
Packit 517ee8
		    && ofts->ofts_recurse_path_devid != fts_ent->fts_statp->st_dev) {
Packit 517ee8
			fts_set(ofts->ofts_recurse_path_fts, fts_ent, FTS_SKIP);
Packit 517ee8
			continue;
Packit 517ee8
		}
Packit 517ee8
Packit 517ee8
		const size_t shift = ofts->prefix ? strlen(ofts->prefix) : 0;
Packit 517ee8
		/* partial match optimization for OVAL_OPERATION_PATTERN_MATCH operation on path and filepath */
Packit 517ee8
		if (ofts->ofts_path_regex != NULL && fts_ent->fts_info == FTS_D) {
Packit 517ee8
			int ret, svec[3];
Packit 517ee8
Packit 517ee8
			ret = pcre_exec(ofts->ofts_path_regex, ofts->ofts_path_regex_extra,
Packit 517ee8
					fts_ent->fts_path+shift, fts_ent->fts_pathlen-shift, 0, PCRE_PARTIAL,
Packit 517ee8
					svec, sizeof(svec) / sizeof(svec[0]));
Packit 517ee8
			if (ret < 0) {
Packit 517ee8
				switch (ret) {
Packit 517ee8
				case PCRE_ERROR_NOMATCH:
Packit 517ee8
					dD("Partial match optimization: PCRE_ERROR_NOMATCH, skipping.");
Packit 517ee8
					fts_set(ofts->ofts_match_path_fts, fts_ent, FTS_SKIP);
Packit 517ee8
					continue;
Packit 517ee8
				case PCRE_ERROR_PARTIAL:
Packit 517ee8
					dD("Partial match optimization: PCRE_ERROR_PARTIAL, continuing.");
Packit 517ee8
					continue;
Packit 517ee8
				default:
Packit 517ee8
					dE("pcre_exec() error: %d.", ret);
Packit 517ee8
					return NULL;
Packit 517ee8
				}
Packit 517ee8
			}
Packit 517ee8
		}
Packit 517ee8
Packit 517ee8
		if ((ofts->ofts_sfilepath && fts_ent->fts_info == FTS_D)
Packit 517ee8
		    || (!ofts->ofts_sfilepath && fts_ent->fts_info != FTS_D))
Packit 517ee8
			continue;
Packit 517ee8
Packit 517ee8
		stmp = SEXP_string_newf("%s", fts_ent->fts_path + shift);
Packit 517ee8
Packit 517ee8
		if (ofts->ofts_sfilepath)
Packit 517ee8
			/* try to match filepath */
Packit 517ee8
			ores = probe_entobj_cmp(ofts->ofts_sfilepath, stmp);
Packit 517ee8
		else
Packit 517ee8
			/* try to match path */
Packit 517ee8
			ores = probe_entobj_cmp(ofts->ofts_spath, stmp);
Packit 517ee8
		SEXP_free(stmp);
Packit 517ee8
Packit 517ee8
		if (ores == OVAL_RESULT_TRUE)
Packit 517ee8
			break;
Packit 132cab
		if (ofts->ofts_path_op == OVAL_OPERATION_EQUALS) {
Packit 132cab
			/* At this point the comparison result isn't OVAL_RESULT_TRUE. Since
Packit 132cab
			we passed the exact path (from filepath or path elements) to
Packit 132cab
			fts_open() we surely know that we can't find other items that would
Packit 132cab
			be equal. Therefore we can terminate the matching. This can happen
Packit 132cab
			if the filepath or path element references a variable that has
Packit 132cab
			multiple different values. */
Packit 132cab
			return NULL;
Packit 132cab
		}
Packit 517ee8
	} /* for (;;) */
Packit 517ee8
Packit 517ee8
	/*
Packit 517ee8
	 * If we know that we are not going to return anything
Packit 517ee8
	 * else, then we can close the path FTS and return NULL
Packit 517ee8
	 * the next time...
Packit 517ee8
	 */
Packit 517ee8
	if (ofts->ofts_path_op   == OVAL_OPERATION_EQUALS &&
Packit 517ee8
	    ofts->direction      == OVAL_RECURSE_DIRECTION_NONE &&
Packit 517ee8
	    ofts->ofts_sfilename == NULL &&
Packit 517ee8
	    ofts->ofts_sfilepath == NULL)
Packit 517ee8
	{
Packit 517ee8
		fts_set(ofts->ofts_match_path_fts, fts_ent, FTS_SKIP);
Packit 517ee8
	}
Packit 517ee8
Packit 517ee8
	return fts_ent;
Packit 517ee8
}
Packit 517ee8
Packit 517ee8
/* find the first matching file or directory */
Packit 517ee8
static FTSENT *oval_fts_read_recurse_path(OVAL_FTS *ofts)
Packit 517ee8
{
Packit 517ee8
	FTSENT *out_fts_ent = NULL;
Packit 517ee8
	/* the condition below is correct because ofts_sfilepath is NULL here */
Packit 517ee8
	bool collect_dirs = (ofts->ofts_sfilename == NULL);
Packit 517ee8
Packit 517ee8
	switch (ofts->direction) {
Packit 517ee8
Packit 517ee8
	case OVAL_RECURSE_DIRECTION_DOWN:
Packit 517ee8
	case OVAL_RECURSE_DIRECTION_NONE:
Packit 517ee8
		if (ofts->direction == OVAL_RECURSE_DIRECTION_NONE
Packit 517ee8
		    && collect_dirs) {
Packit 517ee8
			/* the target is the directory itself */
Packit 517ee8
			out_fts_ent = ofts->ofts_match_path_fts_ent;
Packit 517ee8
			ofts->ofts_match_path_fts_ent = NULL;
Packit 517ee8
			break;
Packit 517ee8
		}
Packit 517ee8
Packit 517ee8
		/* initialize separate fts for recursion */
Packit 517ee8
		if (ofts->ofts_recurse_path_fts == NULL) {
Packit 517ee8
			char * const paths[2] = { ofts->ofts_match_path_fts_ent->fts_path, NULL };
Packit 517ee8
Packit 517ee8
#if defined(OSCAP_FTS_DEBUG)
Packit 517ee8
			dD("fts_open args: path: \"%s\", options: %d.",
Packit 517ee8
				paths[0], ofts->ofts_recurse_path_fts_opts);
Packit 517ee8
#endif
Packit 517ee8
			/* reset errno as fts_open() doesn't do it itself. */
Packit 517ee8
			errno = 0;
Packit 517ee8
			ofts->ofts_recurse_path_fts = fts_open(paths,
Packit 517ee8
				ofts->ofts_recurse_path_fts_opts, NULL);
Packit 517ee8
			/* fts_open() doesn't return NULL for all errors
Packit 517ee8
			   (e.g. nonexistent paths), so check errno to detect it.
Packit 517ee8
			   Far from being perfect. */
Packit 517ee8
			if (ofts->ofts_recurse_path_fts == NULL || errno != 0) {
Packit 517ee8
				dE("fts_open() failed, errno: %d \"%s\".",
Packit 517ee8
					errno, strerror(errno));
Packit 517ee8
#if !defined(OSCAP_FTS_DEBUG)
Packit 517ee8
				dE("fts_open args: path: \"%s\", options: %d.",
Packit 517ee8
					paths[0], ofts->ofts_recurse_path_fts_opts);
Packit 517ee8
#endif
Packit 517ee8
				if (ofts->ofts_recurse_path_fts != NULL) {
Packit 517ee8
					fts_close(ofts->ofts_recurse_path_fts);
Packit 517ee8
					ofts->ofts_recurse_path_fts = NULL;
Packit 517ee8
				}
Packit 517ee8
				return (NULL);
Packit 517ee8
			}
Packit 517ee8
		}
Packit 517ee8
Packit 517ee8
		/* iterate until a match is found or all elements have been traversed */
Packit 517ee8
		while (out_fts_ent == NULL) {
Packit 517ee8
			FTSENT *fts_ent;
Packit 517ee8
Packit 517ee8
			fts_ent = fts_read(ofts->ofts_recurse_path_fts);
Packit 517ee8
			if (fts_ent == NULL) {
Packit 517ee8
				fts_close(ofts->ofts_recurse_path_fts);
Packit 517ee8
				ofts->ofts_recurse_path_fts = NULL;
Packit 517ee8
Packit 517ee8
				return NULL;
Packit 517ee8
			}
Packit 517ee8
Packit 517ee8
			switch (fts_ent->fts_info) {
Packit 517ee8
			case FTS_DP:
Packit 517ee8
				continue;
Packit 517ee8
			case FTS_DC:
Packit 517ee8
				dW("Filesystem tree cycle detected at '%s'.", fts_ent->fts_path);
Packit 517ee8
				fts_set(ofts->ofts_recurse_path_fts, fts_ent, FTS_SKIP);
Packit 517ee8
				continue;
Packit 517ee8
			}
Packit 517ee8
Packit 517ee8
#if defined(OSCAP_FTS_DEBUG)
Packit 517ee8
			dD("fts_path: '%s' (l=%d)."
Packit 517ee8
			   "fts_name: '%s' (l=%d).\n"
Packit 517ee8
			   "fts_info: %u.\n", fts_ent->fts_path, fts_ent->fts_pathlen,
Packit 517ee8
			   fts_ent->fts_name, fts_ent->fts_namelen, fts_ent->fts_info);
Packit 517ee8
#endif
Packit 517ee8
Packit 517ee8
			/* collect matching target */
Packit 517ee8
			if (collect_dirs) {
Packit 517ee8
				if (fts_ent->fts_info == FTS_D
Packit 517ee8
				    && (ofts->max_depth == -1 || fts_ent->fts_level <= ofts->max_depth))
Packit 517ee8
					out_fts_ent = fts_ent;
Packit 517ee8
			} else {
Packit 517ee8
				if (fts_ent->fts_info != FTS_D) {
Packit 517ee8
					SEXP_t *stmp;
Packit 517ee8
Packit 517ee8
					stmp = SEXP_string_newf("%s", fts_ent->fts_name);
Packit 517ee8
					oval_result_t result = probe_entobj_cmp(ofts->ofts_sfilename, stmp);
Packit 517ee8
					switch (result){
Packit 517ee8
						case OVAL_RESULT_TRUE:
Packit 517ee8
							out_fts_ent = fts_ent;
Packit 517ee8
							break;
Packit 517ee8
Packit 517ee8
						case OVAL_RESULT_ERROR:
Packit 517ee8
							probe_cobj_set_flag(ofts->result, SYSCHAR_FLAG_ERROR);
Packit 517ee8
							break;
Packit 517ee8
Packit 517ee8
						default:
Packit 517ee8
							break;
Packit 517ee8
					}
Packit 517ee8
Packit 517ee8
					SEXP_free(stmp);
Packit 517ee8
				}
Packit 517ee8
			}
Packit 517ee8
Packit 517ee8
			if (fts_ent->fts_level > 0) { /* don't skip fts root */
Packit 517ee8
				/* limit recursion depth */
Packit 517ee8
				if (ofts->direction == OVAL_RECURSE_DIRECTION_NONE
Packit 517ee8
				    || (ofts->max_depth != -1 && fts_ent->fts_level > ofts->max_depth)) {
Packit 517ee8
					fts_set(ofts->ofts_recurse_path_fts, fts_ent, FTS_SKIP);
Packit 517ee8
					continue;
Packit 517ee8
				}
Packit 517ee8
Packit 517ee8
				/* limit recursion only to selected file types */
Packit 517ee8
				switch (fts_ent->fts_info) {
Packit 517ee8
				case FTS_D:
Packit 517ee8
					if (!(ofts->recurse & OVAL_RECURSE_DIRS)) {
Packit 517ee8
						fts_set(ofts->ofts_recurse_path_fts, fts_ent, FTS_SKIP);
Packit 517ee8
						continue;
Packit 517ee8
					}
Packit 517ee8
					break;
Packit 517ee8
				case FTS_SL:
Packit 517ee8
					if (!(ofts->recurse & OVAL_RECURSE_SYMLINKS)) {
Packit 517ee8
						fts_set(ofts->ofts_recurse_path_fts, fts_ent, FTS_SKIP);
Packit 517ee8
						continue;
Packit 517ee8
					}
Packit 517ee8
					fts_set(ofts->ofts_recurse_path_fts, fts_ent, FTS_FOLLOW);
Packit 517ee8
					break;
Packit 517ee8
				default:
Packit 517ee8
					continue;
Packit 517ee8
				}
Packit 517ee8
			}
Packit 517ee8
			if (_oval_fts_is_local(ofts, fts_ent)) {
Packit 517ee8
				fts_set(ofts->ofts_recurse_path_fts, fts_ent, FTS_SKIP);
Packit 517ee8
				continue;
Packit 517ee8
			}
Packit 517ee8
			/* don't recurse beyond the initial filesystem */
Packit 517ee8
			if (ofts->filesystem == OVAL_RECURSE_FS_DEFINED
Packit 517ee8
			    && (fts_ent->fts_info == FTS_D || fts_ent->fts_info == FTS_SL)
Packit 517ee8
			    && ofts->ofts_recurse_path_devid != fts_ent->fts_statp->st_dev) {
Packit 517ee8
				fts_set(ofts->ofts_recurse_path_fts, fts_ent, FTS_SKIP);
Packit 517ee8
				continue;
Packit 517ee8
			}
Packit 517ee8
		}
Packit 517ee8
Packit 517ee8
		break;
Packit 517ee8
	case OVAL_RECURSE_DIRECTION_UP:
Packit 517ee8
		if (ofts->ofts_recurse_path_pthcpy == NULL) {
Packit 517ee8
			ofts->ofts_recurse_path_pthcpy = \
Packit 517ee8
			ofts->ofts_recurse_path_curpth = strdup(ofts->ofts_match_path_fts_ent->fts_path);
Packit 517ee8
			ofts->ofts_recurse_path_curdepth = 0;
Packit 517ee8
		}
Packit 517ee8
Packit 517ee8
		while (ofts->max_depth == -1 || ofts->ofts_recurse_path_curdepth <= ofts->max_depth) {
Packit 517ee8
			/* initialize separate fts for recursion */
Packit 517ee8
			if (ofts->ofts_recurse_path_fts == NULL) {
Packit 517ee8
				char * const paths[2] = { ofts->ofts_recurse_path_curpth, NULL };
Packit 517ee8
Packit 517ee8
#if defined(OSCAP_FTS_DEBUG)
Packit 517ee8
				dD("fts_open args: path: \"%s\", options: %d.",
Packit 517ee8
					paths[0], ofts->ofts_recurse_path_fts_opts);
Packit 517ee8
#endif
Packit 517ee8
				/* reset errno as fts_open() doesn't do it itself. */
Packit 517ee8
				errno = 0;
Packit 517ee8
				/* fts_open() doesn't return NULL for all errors
Packit 517ee8
				   (e.g. nonexistent paths), so check errno to
Packit 517ee8
				   detect it. Far from being perfect. */
Packit 517ee8
				ofts->ofts_recurse_path_fts = fts_open(paths,
Packit 517ee8
					ofts->ofts_recurse_path_fts_opts, NULL);
Packit 517ee8
				if (ofts->ofts_recurse_path_fts == NULL || errno != 0) {
Packit 517ee8
					dE("fts_open() failed, errno: %d \"%s\".",
Packit 517ee8
						errno, strerror(errno));
Packit 517ee8
#if !defined(OSCAP_FTS_DEBUG)
Packit 517ee8
					dE("fts_open args: path: \"%s\", options: %d.",
Packit 517ee8
						paths[0], ofts->ofts_recurse_path_fts_opts);
Packit 517ee8
#endif
Packit 517ee8
					if (ofts->ofts_recurse_path_fts != NULL) {
Packit 517ee8
						fts_close(ofts->ofts_recurse_path_fts);
Packit 517ee8
						ofts->ofts_recurse_path_fts = NULL;
Packit 517ee8
					}
Packit 517ee8
					return (NULL);
Packit 517ee8
				}
Packit 517ee8
			}
Packit 517ee8
Packit 517ee8
			/* iterate until a match is found or all elements have been traversed */
Packit 517ee8
			while (out_fts_ent == NULL) {
Packit 517ee8
				FTSENT *fts_ent;
Packit 517ee8
Packit 517ee8
				fts_ent = fts_read(ofts->ofts_recurse_path_fts);
Packit 517ee8
				if (fts_ent == NULL)
Packit 517ee8
					break;
Packit 517ee8
Packit 517ee8
				/*
Packit 517ee8
				   it would be more accurate to obtain the device
Packit 517ee8
				   id here, but for the sake of supporting the
Packit 517ee8
				   comparison also in oval_fts_read_match_path(),
Packit 517ee8
				   the device id is obtained in oval_fts_open_prefixed()
Packit 517ee8
Packit 517ee8
				if (ofts->ofts_recurse_path_curdepth == 0)
Packit 517ee8
					ofts->ofts_recurse_path_devid = fts_ent->fts_statp->st_dev;
Packit 517ee8
				*/
Packit 517ee8
#if defined(OS_SOLARIS)
Packit 517ee8
				if ((!OVAL_FTS_localp(ofts, fts_ent->fts_path,
Packit 517ee8
				    (fts_ent->fts_statp != NULL) ?
Packit 517ee8
				    &fts_ent->fts_statp->st_fstype : NULL)))
Packit 517ee8
				       break;
Packit 517ee8
#else
Packit 517ee8
				if (ofts->filesystem == OVAL_RECURSE_FS_LOCAL
Packit 517ee8
				    && (!OVAL_FTS_localp(ofts, fts_ent->fts_path,
Packit 517ee8
						(fts_ent->fts_statp != NULL) ?
Packit 517ee8
						&fts_ent->fts_statp->st_dev : NULL)))
Packit 517ee8
					break;
Packit 517ee8
#endif
Packit 517ee8
				if (ofts->filesystem == OVAL_RECURSE_FS_DEFINED
Packit 517ee8
				    && ofts->ofts_recurse_path_devid != fts_ent->fts_statp->st_dev)
Packit 517ee8
					break;
Packit 517ee8
Packit 517ee8
				/* collect matching target */
Packit 517ee8
				if (collect_dirs) {
Packit 517ee8
					/* only fts root is collected */
Packit 517ee8
					if (fts_ent->fts_level == 0 && fts_ent->fts_info == FTS_D) {
Packit 517ee8
						out_fts_ent = fts_ent;
Packit 517ee8
						fts_set(ofts->ofts_recurse_path_fts, fts_ent, FTS_SKIP);
Packit 517ee8
						break;
Packit 517ee8
					}
Packit 517ee8
				} else {
Packit 517ee8
					if (fts_ent->fts_info != FTS_D) {
Packit 517ee8
						SEXP_t *stmp;
Packit 517ee8
Packit 517ee8
						stmp = SEXP_string_newf("%s", fts_ent->fts_name);
Packit 517ee8
						if (probe_entobj_cmp(ofts->ofts_sfilename, stmp) == OVAL_RESULT_TRUE)
Packit 517ee8
							out_fts_ent = fts_ent;
Packit 517ee8
						SEXP_free(stmp);
Packit 517ee8
					}
Packit 517ee8
				}
Packit 517ee8
Packit 517ee8
				if (fts_ent->fts_info == FTS_SL)
Packit 517ee8
					fts_set(ofts->ofts_recurse_path_fts, fts_ent, FTS_FOLLOW);
Packit 517ee8
				/* limit recursion only to fts root */
Packit 517ee8
				else if (fts_ent->fts_level > 0)
Packit 517ee8
					fts_set(ofts->ofts_recurse_path_fts, fts_ent, FTS_SKIP);
Packit 517ee8
			}
Packit 517ee8
Packit 517ee8
			if (out_fts_ent != NULL)
Packit 517ee8
				break;
Packit 517ee8
Packit 517ee8
			fts_close(ofts->ofts_recurse_path_fts);
Packit 517ee8
			ofts->ofts_recurse_path_fts = NULL;
Packit 517ee8
Packit 517ee8
			if (!strcmp(ofts->ofts_recurse_path_curpth, "/"))
Packit 517ee8
				break;
Packit 517ee8
Packit 517ee8
			ofts->ofts_recurse_path_curpth = oscap_dirname(ofts->ofts_recurse_path_curpth);
Packit 517ee8
			ofts->ofts_recurse_path_curdepth++;
Packit 517ee8
		}
Packit 517ee8
Packit 517ee8
		if (out_fts_ent == NULL) {
Packit 517ee8
			free(ofts->ofts_recurse_path_pthcpy);
Packit 517ee8
			ofts->ofts_recurse_path_pthcpy = NULL;
Packit 517ee8
		}
Packit 517ee8
Packit 517ee8
		break;
Packit 517ee8
	}
Packit 517ee8
Packit 517ee8
	return out_fts_ent;
Packit 517ee8
}
Packit 517ee8
Packit 517ee8
OVAL_FTSENT *oval_fts_read(OVAL_FTS *ofts)
Packit 517ee8
{
Packit 517ee8
	FTSENT *fts_ent;
Packit 517ee8
Packit 517ee8
#if defined(OSCAP_FTS_DEBUG)
Packit 517ee8
	dD("ofts: %p.", ofts);
Packit 517ee8
#endif
Packit 517ee8
Packit 517ee8
	if (ofts == NULL)
Packit 517ee8
		return NULL;
Packit 517ee8
Packit 517ee8
	for (;;) {
Packit 517ee8
		if (ofts->ofts_match_path_fts_ent == NULL) {
Packit 517ee8
			ofts->ofts_match_path_fts_ent = oval_fts_read_match_path(ofts);
Packit 517ee8
			if (ofts->ofts_match_path_fts_ent == NULL)
Packit 517ee8
				return NULL;
Packit 517ee8
		}
Packit 517ee8
Packit 517ee8
		if (ofts->ofts_sfilepath) {
Packit 517ee8
			fts_ent = ofts->ofts_match_path_fts_ent;
Packit 517ee8
			ofts->ofts_match_path_fts_ent = NULL;
Packit 517ee8
			break;
Packit 517ee8
		} else {
Packit 517ee8
			fts_ent = oval_fts_read_recurse_path(ofts);
Packit 517ee8
			if (fts_ent != NULL)
Packit 517ee8
				break;
Packit 517ee8
Packit 517ee8
			ofts->ofts_match_path_fts_ent = NULL;
Packit 517ee8
Packit 517ee8
			// todo: is this true when variables are used?
Packit 517ee8
			/* with 'equals', there's only one potential target */
Packit 517ee8
			if (ofts->ofts_path_op == OVAL_OPERATION_EQUALS)
Packit 517ee8
				return (NULL);
Packit 517ee8
		}
Packit 517ee8
	}
Packit 517ee8
Packit 517ee8
	return OVAL_FTSENT_new(ofts, fts_ent);
Packit 517ee8
}
Packit 517ee8
Packit 517ee8
void oval_ftsent_free(OVAL_FTSENT *ofts_ent)
Packit 517ee8
{
Packit 517ee8
	OVAL_FTSENT_free(ofts_ent);
Packit 517ee8
}
Packit 517ee8
Packit 517ee8
int oval_fts_close(OVAL_FTS *ofts)
Packit 517ee8
{
Packit 517ee8
	if (ofts->ofts_recurse_path_pthcpy != NULL)
Packit 517ee8
		free(ofts->ofts_recurse_path_pthcpy);
Packit 517ee8
Packit 517ee8
	if (ofts->ofts_path_regex)
Packit 517ee8
		pcre_free(ofts->ofts_path_regex);
Packit 517ee8
	if (ofts->ofts_path_regex_extra)
Packit 517ee8
		pcre_free(ofts->ofts_path_regex_extra);
Packit 517ee8
Packit 517ee8
	if (ofts->ofts_spath != NULL)
Packit 517ee8
		SEXP_free(ofts->ofts_spath);
Packit 517ee8
	if (ofts->ofts_sfilename != NULL)
Packit 517ee8
		SEXP_free(ofts->ofts_sfilename);
Packit 517ee8
	if (ofts->ofts_sfilepath != NULL)
Packit 517ee8
		SEXP_free(ofts->ofts_sfilepath);
Packit 517ee8
Packit 517ee8
	fsdev_free(ofts->localdevs);
Packit 517ee8
Packit 517ee8
	OVAL_FTS_free(ofts);
Packit 517ee8
#if defined(OS_SOLARIS)
Packit 517ee8
	free_zones_path_list();
Packit 517ee8
#endif
Packit 517ee8
Packit 517ee8
	return (0);
Packit 517ee8
}