Ejemplo n.º 1
0
/*
 * Non-nsswitch.conf version with hard-coded order.
 */
struct sudo_nss_list *
sudo_read_nss(void)
{
    static struct sudo_nss_list snl;

#  ifdef HAVE_LDAP
    tq_append(&snl, &sudo_nss_ldap);
#  endif
    tq_append(&snl, &sudo_nss_file);

    return &snl;
}
Ejemplo n.º 2
0
/*
 * Read in /etc/nsswitch.conf
 * Returns a tail queue of matches.
 */
struct sudo_nss_list *
sudo_read_nss(void)
{
    FILE *fp;
    char *cp;
    int saw_files = FALSE;
    int saw_ldap = FALSE;
    int got_match = FALSE;
    static struct sudo_nss_list snl;

    if ((fp = fopen(_PATH_NSSWITCH_CONF, "r")) == NULL)
	goto nomatch;

    while ((cp = sudo_parseln(fp)) != NULL) {
	/* Skip blank or comment lines */
	if (*cp == '\0')
	    continue;

	/* Look for a line starting with "sudoers:" */
	if (strncasecmp(cp, "sudoers:", 8) != 0)
	    continue;

	/* Parse line */
	for ((cp = strtok(cp + 8, " \t")); cp != NULL; (cp = strtok(NULL, " \t"))) {
	    if (strcasecmp(cp, "files") == 0 && !saw_files) {
		tq_append(&snl, &sudo_nss_file);
		got_match = TRUE;
	    } else if (strcasecmp(cp, "ldap") == 0 && !saw_ldap) {
		tq_append(&snl, &sudo_nss_ldap);
		got_match = TRUE;
	    } else if (strcasecmp(cp, "[NOTFOUND=return]") == 0 && got_match) {
		/* NOTFOUND affects the most recent entry */
		tq_last(&snl)->ret_if_notfound = TRUE;
		got_match = FALSE;
	    } else
		got_match = FALSE;
	}
	/* Only parse the first "sudoers:" line */
	break;
    }
    fclose(fp);

nomatch:
    /* Default to files only if no matches */
    if (tq_empty(&snl))
	tq_append(&snl, &sudo_nss_file);

    return &snl;
}
Ejemplo n.º 3
0
void digi_regen (int from_chan, packet_t pp)
{
	int to_chan;
	packet_t result;

	// dw_printf ("digi_regen()\n");
	
	assert (from_chan >= 0 && from_chan < MAX_CHANS);

	for (to_chan=0; to_chan<MAX_CHANS; to_chan++) {
	  if (save_digi_config_p->regen[from_chan][to_chan]) {
	    result = ax25_dup (pp); 
	    if (result != NULL) {
	      // TODO:  if AX.25 and has been digipeated, put in HI queue?
	      tq_append (to_chan, TQ_PRIO_1_LO, result);
	    }
	  }
	}

} /* end dig_regen */
Ejemplo n.º 4
0
void digipeater (int from_chan, packet_t pp)
{
	int to_chan;


	// dw_printf ("digipeater()\n");
	
	assert (from_chan >= 0 && from_chan < MAX_CHANS);

	if ( ! save_audio_config_p->achan[from_chan].valid) {
	  text_color_set(DW_COLOR_ERROR);
	  dw_printf ("digipeater: Did not expect to receive on invalid channel %d.\n", from_chan);
	}


/*
 * First pass:  Look at packets being digipeated to same channel.
 *
 * We want these to get out quickly.
 */

	for (to_chan=0; to_chan<MAX_CHANS; to_chan++) {
	  if (save_digi_config_p->enabled[from_chan][to_chan]) {
	    if (to_chan == from_chan) {
	      packet_t result;

	      result = digipeat_match (from_chan, pp, save_audio_config_p->achan[from_chan].mycall, 
					   save_audio_config_p->achan[to_chan].mycall, 
			&save_digi_config_p->alias[from_chan][to_chan], &save_digi_config_p->wide[from_chan][to_chan], 
			to_chan, save_digi_config_p->preempt[from_chan][to_chan],
				save_digi_config_p->filter_str[from_chan][to_chan]);
	      if (result != NULL) {
		dedupe_remember (pp, to_chan);
	        tq_append (to_chan, TQ_PRIO_0_HI, result);
	      }
	    }
	  }
	}


/*
 * Second pass:  Look at packets being digipeated to different channel.
 *
 * These are lower priority
 */

	for (to_chan=0; to_chan<MAX_CHANS; to_chan++) {
	  if (save_digi_config_p->enabled[from_chan][to_chan]) {
	    if (to_chan != from_chan) {
	      packet_t result;

	      result = digipeat_match (from_chan, pp, save_audio_config_p->achan[from_chan].mycall, 
					   save_audio_config_p->achan[to_chan].mycall, 
			&save_digi_config_p->alias[from_chan][to_chan], &save_digi_config_p->wide[from_chan][to_chan], 
			to_chan, save_digi_config_p->preempt[from_chan][to_chan],
				save_digi_config_p->filter_str[from_chan][to_chan]);
	      if (result != NULL) {
		dedupe_remember (pp, to_chan);
	        tq_append (to_chan, TQ_PRIO_1_LO, result);
	      }
	    }
	  }
	}

} /* end digipeater */
Ejemplo n.º 5
0
/*
 * Read in /etc/netsvc.conf (like nsswitch.conf on AIX)
 * Returns a tail queue of matches.
 */
struct sudo_nss_list *
sudo_read_nss(void)
{
    FILE *fp;
    char *cp, *ep;
    int saw_files = FALSE;
    int saw_ldap = FALSE;
    int got_match = FALSE;
    static struct sudo_nss_list snl;

    if ((fp = fopen(_PATH_NETSVC_CONF, "r")) == NULL)
	goto nomatch;

    while ((cp = sudo_parseln(fp)) != NULL) {
	/* Skip blank or comment lines */
	if (*cp == '\0')
	    continue;

	/* Look for a line starting with "sudoers = " */
	if (strncasecmp(cp, "sudoers", 7) != 0)
	    continue;
	cp += 7;
	while (isspace((unsigned char)*cp))
	    cp++;
	if (*cp++ != '=')
	    continue;

	/* Parse line */
	for ((cp = strtok(cp, ",")); cp != NULL; (cp = strtok(NULL, ","))) {
	    /* Trim leading whitespace. */
	    while (isspace((unsigned char)*cp))
		cp++;

	    if (!saw_files && strncasecmp(cp, "files", 5) == 0 &&
		(isspace((unsigned char)cp[5]) || cp[5] == '\0')) {
		tq_append(&snl, &sudo_nss_file);
		got_match = TRUE;
		ep = &cp[5];
	    } else if (!saw_ldap && strncasecmp(cp, "ldap", 4) == 0 &&
		(isspace((unsigned char)cp[4]) || cp[4] == '\0')) {
		tq_append(&snl, &sudo_nss_ldap);
		got_match = TRUE;
		ep = &cp[4];
	    } else {
		got_match = FALSE;
	    }

	    /* check for = auth qualifier */
	    if (got_match && *ep) {
		cp = ep;
		while (isspace((unsigned char)*cp) || *cp == '=')
		    cp++;
		if (strncasecmp(cp, "auth", 4) == 0 &&
		    (isspace((unsigned char)cp[4]) || cp[4] == '\0')) {
		    tq_last(&snl)->ret_if_found = TRUE;
		}
	    }
	}
	/* Only parse the first "sudoers" line */
	break;
    }
    fclose(fp);

nomatch:
    /* Default to files only if no matches */
    if (tq_empty(&snl))
	tq_append(&snl, &sudo_nss_file);

    return &snl;
}
Ejemplo n.º 6
0
static void * beacon_thread (void *arg)
#endif
{
	int j;
	time_t earliest;
	time_t now;

/*
 * Information from GPS.
 */
	int fix = 0;			/* 0 = none, 2 = 2D, 3 = 3D */
	double my_lat = 0;		/* degrees */
	double my_lon = 0;
	float  my_course = 0;		/* degrees */
	float  my_speed_knots = 0;
	float  my_speed_mph = 0;
	float  my_alt = 0;		/* meters */

/*
 * SmartBeaconing state.
 */
	time_t sb_prev_time = 0;	/* Time of most recent transmission. */
	float sb_prev_course = 0;	/* Most recent course reported. */
	//float sb_prev_speed_mph;	/* Most recent speed reported. */
	int sb_every;			/* Calculated time between transmissions. */


#if DEBUG
	struct tm tm;
	char hms[20];

	now = time(NULL);
	localtime_r (&now, &tm);
	strftime (hms, sizeof(hms), "%H:%M:%S", &tm);
	text_color_set(DW_COLOR_DEBUG);
	dw_printf ("beacon_thread: started %s\n", hms);
#endif
	now = time(NULL);

	while (1) {

	  assert (g_misc_config_p->num_beacons >= 1);

/* 
 * Sleep until time for the earliest scheduled or
 * the soonest we could transmit due to corner pegging.
 */
	  
	  earliest = g_misc_config_p->beacon[0].next;
	  for (j=1; j<g_misc_config_p->num_beacons; j++) {
	    if (g_misc_config_p->beacon[j].btype == BEACON_IGNORE)
	      continue;
	    earliest = MIN(g_misc_config_p->beacon[j].next, earliest);
	  }

	  if (g_misc_config_p->sb_configured && g_using_gps) {
	    earliest = MIN(now + g_misc_config_p->sb_turn_time, earliest);
            earliest = MIN(now + g_misc_config_p->sb_fast_rate, earliest);
	  }

	  if (earliest > now) {
	    SLEEP_SEC (earliest - now);
	  }

/*
 * Woke up.  See what needs to be done.
 */
	  now = time(NULL);

#if DEBUG
	  localtime_r (&now, &tm);
	  strftime (hms, sizeof(hms), "%H:%M:%S", &tm);
	  text_color_set(DW_COLOR_DEBUG);
	  dw_printf ("beacon_thread: woke up %s\n", hms);
#endif

/*
 * Get information from GPS if being used.
 * This needs to be done before the next scheduled tracker
 * beacon because corner pegging make it sooner. 
 */

#if DEBUG_SIM
	  FILE *fp;
	  char cs[40];

	  fp = fopen ("c:\\cygwin\\tmp\\cs", "r");
	  if (fp != NULL) {
	    fscanf (fp, "%f %f", &my_course, &my_speed_knots);
	    fclose (fp);
	  }
	  else {
	    fprintf (stderr, "Can't read /tmp/cs.\n");
	  }
	  fix = 3;
	  my_speed_mph = KNOTS_TO_MPH * my_speed_knots;
	  my_lat = 42.99;
	  my_lon = 71.99;
	  my_alt = 100;
#else
	  if (g_using_gps) {

	    fix = dwgps_read (&my_lat, &my_lon, &my_speed_knots, &my_course, &my_alt);
	    my_speed_mph = KNOTS_TO_MPH * my_speed_knots;

	    /* Don't complain here for no fix. */
	    /* Possibly at the point where about to transmit. */
	  }
#endif

/*
 * Run SmartBeaconing calculation if configured and GPS data available.
 */
	  if (g_misc_config_p->sb_configured && g_using_gps && fix >= 2) {

	    if (my_speed_mph > g_misc_config_p->sb_fast_speed) {
		sb_every = g_misc_config_p->sb_fast_rate;
	    }
	    else if (my_speed_mph < g_misc_config_p->sb_slow_speed) {
	      sb_every = g_misc_config_p->sb_slow_rate;
	    }
	    else {
	      /* Can't divide by 0 assuming sb_slow_speed > 0. */
	      sb_every = ( g_misc_config_p->sb_fast_rate * g_misc_config_p->sb_fast_speed ) / my_speed_mph;
	    }

#if DEBUG_SIM
	  text_color_set(DW_COLOR_DEBUG);
	  dw_printf ("SB: fast %d %d slow %d %d speed=%.1f every=%d\n",
			g_misc_config_p->sb_fast_speed, g_misc_config_p->sb_fast_rate,
			g_misc_config_p->sb_slow_speed, g_misc_config_p->sb_slow_rate,
			my_speed_mph, sb_every);
#endif 
	
/*
 * Test for "Corner Pegging" if moving.
 */
	    if (my_speed_mph >= 1.0) {
	      int turn_threshold = g_misc_config_p->sb_turn_angle + 
			g_misc_config_p->sb_turn_slope / my_speed_mph;

#if DEBUG_SIM
	  text_color_set(DW_COLOR_DEBUG);
	  dw_printf ("SB-moving: course %.0f  prev %.0f  thresh %d\n",
		my_course, sb_prev_course, turn_threshold);
#endif 
	      if (heading_change(my_course, sb_prev_course) > turn_threshold &&
		  now >= sb_prev_time + g_misc_config_p->sb_turn_time) {

		/* Send it now. */
	        for (j=0; j<g_misc_config_p->num_beacons; j++) {
                  if (g_misc_config_p->beacon[j].btype == BEACON_TRACKER) {
		    g_misc_config_p->beacon[j].next = now;
	          }
	        }
	      }  /* significant change in direction */
	    }  /* is moving */
	  }  /* apply SmartBeaconing */
	    
      
	  for (j=0; j<g_misc_config_p->num_beacons; j++) {

	    if (g_misc_config_p->beacon[j].btype == BEACON_IGNORE)
	      continue;

	    if (g_misc_config_p->beacon[j].next <= now) {

	      int strict = 1;	/* Strict packet checking because they will go over air. */
	      char stemp[20];
	      char info[AX25_MAX_INFO_LEN];
	      char beacon_text[AX25_MAX_PACKET_LEN];
	      packet_t pp = NULL;
	      char mycall[AX25_MAX_ADDR_LEN];

/*
 * Obtain source call for the beacon.
 * This could potentially be different on different channels.
 * When sending to IGate server, use call from first radio channel.
 *
 * Check added in version 1.0a.  Previously used index of -1.
 */
	      strcpy (mycall, "NOCALL");

	      if (g_misc_config_p->beacon[j].chan == -1) {
		strcpy (mycall, g_digi_config_p->mycall[0]);
	      }
	      else {
		strcpy (mycall, g_digi_config_p->mycall[g_misc_config_p->beacon[j].chan]);
	      }

	      if (strlen(mycall) == 0 || strcmp(mycall, "NOCALL") == 0) {
	        text_color_set(DW_COLOR_ERROR);
	        dw_printf ("MYCALL not set for beacon in config file line %d.\n", g_misc_config_p->beacon[j].lineno);
		continue;
	      }

/* 
 * Prepare the monitor format header. 
 */

	      strcpy (beacon_text, mycall);
	      strcat (beacon_text, ">");
	      sprintf (stemp, "%s%1d%1d", APP_TOCALL, MAJOR_VERSION, MINOR_VERSION);
	      strcat (beacon_text, stemp);
	      if (g_misc_config_p->beacon[j].via) {
	        strcat (beacon_text, ",");
	        strcat (beacon_text, g_misc_config_p->beacon[j].via);
	      }
	      strcat (beacon_text, ":");

/* 
 * Add the info part depending on beacon type. 
 */
	      switch (g_misc_config_p->beacon[j].btype) {

		case BEACON_POSITION:

		  encode_position (g_misc_config_p->beacon[j].compress, g_misc_config_p->beacon[j].lat, g_misc_config_p->beacon[j].lon, 
			g_misc_config_p->beacon[j].symtab, g_misc_config_p->beacon[j].symbol, 
			g_misc_config_p->beacon[j].power, g_misc_config_p->beacon[j].height, g_misc_config_p->beacon[j].gain, g_misc_config_p->beacon[j].dir,
			0, 0, /* course, speed */	
			g_misc_config_p->beacon[j].freq, g_misc_config_p->beacon[j].tone, g_misc_config_p->beacon[j].offset,
			g_misc_config_p->beacon[j].comment,
			info);
		  strcat (beacon_text, info);
	          g_misc_config_p->beacon[j].next = now + g_misc_config_p->beacon[j].every;
		  break;

		case BEACON_OBJECT:

		  encode_object (g_misc_config_p->beacon[j].objname, g_misc_config_p->beacon[j].compress, 0, g_misc_config_p->beacon[j].lat, g_misc_config_p->beacon[j].lon, 
			g_misc_config_p->beacon[j].symtab, g_misc_config_p->beacon[j].symbol, 
			g_misc_config_p->beacon[j].power, g_misc_config_p->beacon[j].height, g_misc_config_p->beacon[j].gain, g_misc_config_p->beacon[j].dir,
			0, 0, /* course, speed */
			g_misc_config_p->beacon[j].freq, g_misc_config_p->beacon[j].tone, g_misc_config_p->beacon[j].offset, g_misc_config_p->beacon[j].comment,
			info);
		  strcat (beacon_text, info);
	          g_misc_config_p->beacon[j].next = now + g_misc_config_p->beacon[j].every;
		  break;

		case BEACON_TRACKER:

		  if (fix >= 2) {
		    int coarse;		/* APRS encoder wants 1 - 360.  */
					/* 0 means none or unknown. */

		    coarse = (int)roundf(my_course);
		    if (coarse == 0) {
		      coarse = 360;
		    }
		    encode_position (g_misc_config_p->beacon[j].compress, 
			my_lat, my_lon, 
			g_misc_config_p->beacon[j].symtab, g_misc_config_p->beacon[j].symbol, 
			g_misc_config_p->beacon[j].power, g_misc_config_p->beacon[j].height, g_misc_config_p->beacon[j].gain, g_misc_config_p->beacon[j].dir,
			coarse, (int)roundf(my_speed_knots),	
			g_misc_config_p->beacon[j].freq, g_misc_config_p->beacon[j].tone, g_misc_config_p->beacon[j].offset,
			g_misc_config_p->beacon[j].comment,
			info);
		    strcat (beacon_text, info);

		    /* Remember most recent tracker beacon. */

		    sb_prev_time = now;
		    sb_prev_course = my_course;
		    //sb_prev_speed_mph = my_speed_mph;

		    /* Calculate time for next transmission. */
	            if (g_misc_config_p->sb_configured) {
	              g_misc_config_p->beacon[j].next = now + sb_every;
	            }
	            else {
	              g_misc_config_p->beacon[j].next = now + g_misc_config_p->beacon[j].every;
	            }
	 	  }
	          else {
		    g_misc_config_p->beacon[j].next = now + 2;
	            continue;   /* No fix.  Try again in a couple seconds. */
		  }
		  break;

		case BEACON_CUSTOM:

		  if (g_misc_config_p->beacon[j].custom_info != NULL) {
	            strcat (beacon_text, g_misc_config_p->beacon[j].custom_info);
		  }
		  else {
		    text_color_set(DW_COLOR_ERROR);
	    	    dw_printf ("Internal error. custom_info is null. %s %d\n", __FILE__, __LINE__);
	            continue;
	          }
	          g_misc_config_p->beacon[j].next = now + g_misc_config_p->beacon[j].every;
		  break;

		case BEACON_IGNORE:		
	        default:
		  break;

	      } /* switch beacon type. */

/*
 * Parse monitor format into form for transmission.
 */	
	      pp = ax25_from_text (beacon_text, strict);

              if (pp != NULL) {

		/* Send to IGate server or radio. */

	        if (g_misc_config_p->beacon[j].chan == -1) {
#if 1
	  	  text_color_set(DW_COLOR_XMIT);
	  	  dw_printf ("[ig] %s\n", beacon_text);
#endif
		  igate_send_rec_packet (0, pp);
		  ax25_delete (pp);
	 	}
		else {
	          tq_append (g_misc_config_p->beacon[j].chan, TQ_PRIO_1_LO, pp);
		}
	      }
	      else {
	        text_color_set(DW_COLOR_ERROR);
	        dw_printf ("Config file: Failed to parse packet constructed from line %d.\n", g_misc_config_p->beacon[j].lineno);
	        dw_printf ("%s\n", beacon_text);
	      }

	    }  /* if time to send it */

	  }  /* for each configured beacon */

	}  /* do forever */

} /* end beacon_thread */
Ejemplo n.º 7
0
/*
 * Read in /etc/sudo.conf
 * Returns a list of plugins.
 */
static struct plugin_info_list *
sudo_read_conf(const char *conf_file)
{
    FILE *fp;
    char *cp, *name, *path;
    struct plugin_info *info;
    static struct plugin_info_list pil; /* XXX */

    if ((fp = fopen(conf_file, "r")) == NULL)
	goto done;

    while ((cp = sudo_parseln(fp)) != NULL) {
	/* Skip blank or comment lines */
	if (*cp == '\0')
	    continue;

	/* Look for a line starting with "Path" */
	if (strncasecmp(cp, "Path", 4) == 0) {
	    /* Parse line */
	    if ((name = strtok(cp + 4, " \t")) == NULL ||
		(path = strtok(NULL, " \t")) == NULL) {
		continue;
	    }
	    if (strcasecmp(name, "askpass") != 0)
		continue;
	    askpass_path = estrdup(path);
	    continue;
	}

	/* Look for a line starting with "Plugin" */
	if (strncasecmp(cp, "Plugin", 6) == 0) {
	    /* Parse line */
	    if ((name = strtok(cp + 6, " \t")) == NULL ||
		(path = strtok(NULL, " \t")) == NULL) {
		continue;
	    }
	    info = emalloc(sizeof(*info));
	    info->symbol_name = estrdup(name);
	    info->path = estrdup(path);
	    info->prev = info;
	    info->next = NULL;
	    tq_append(&pil, info);
	    continue;
	}
    }
    fclose(fp);

done:
    if (tq_empty(&pil)) {
	/* Default policy plugin */
	info = emalloc(sizeof(*info));
	info->symbol_name = "sudoers_policy";
	info->path = SUDOERS_PLUGIN;
	info->prev = info;
	info->next = NULL;
	tq_append(&pil, info);

	/* Default I/O plugin */
	info = emalloc(sizeof(*info));
	info->symbol_name = "sudoers_io";
	info->path = SUDOERS_PLUGIN;
	info->prev = info;
	info->next = NULL;
	tq_append(&pil, info);
    }

    return &pil;
}
Ejemplo n.º 8
0
/*
 * Load the plugins listed in conf_file.
 */
void
sudo_load_plugins(const char *conf_file,
    struct plugin_container *policy_plugin,
    struct plugin_container_list *io_plugins)
{
    struct generic_plugin *plugin;
    struct plugin_container *container;
    struct plugin_info *info;
    struct plugin_info_list *plugin_list;
    struct stat sb;
    void *handle;
    char path[PATH_MAX];

    /* Parse sudo.conf */
    plugin_list = sudo_read_conf(conf_file);

    tq_foreach_fwd(plugin_list, info) {
	if (info->path[0] == '/') {
	    if (strlcpy(path, info->path, sizeof(path)) >= sizeof(path))
		errorx(1, "%s: %s", info->path, strerror(ENAMETOOLONG));
	} else {
	    if (snprintf(path, sizeof(path), "%s%s", _PATH_SUDO_PLUGIN_DIR,
		info->path) >= sizeof(path)) {
		errorx(1, "%s%s: %s", _PATH_SUDO_PLUGIN_DIR, info->path,
		    strerror(ENAMETOOLONG));
	    }
	}
	if (stat(path, &sb) != 0)
	    error(1, "%s", path);
	if (sb.st_uid != ROOT_UID)
	    errorx(1, "%s must be owned by uid %d", path, ROOT_UID);
	if ((sb.st_mode & (S_IWGRP|S_IWOTH)) != 0)
	    errorx(1, "%s must be only be writable by owner", path);

	/* Open plugin and map in symbol */
	handle = dlopen(path, RTLD_LAZY|RTLD_LOCAL);
	if (!handle)
	    errorx(1, "unable to dlopen %s: %s", path, dlerror());
	plugin = dlsym(handle, info->symbol_name);
	if (!plugin)
	    errorx(1, "unable to find symbol %s in %s", info->symbol_name, path);

	if (plugin->type != SUDO_POLICY_PLUGIN && plugin->type != SUDO_IO_PLUGIN) {
	    errorx(1, "%s: unknown policy type %d", path, plugin->type);
	}
	if (SUDO_API_VERSION_GET_MAJOR(plugin->version) != SUDO_API_VERSION_MAJOR) {
	    errorx(1, "%s: incompatible policy major version %d, expected %d",
		path, SUDO_API_VERSION_GET_MAJOR(plugin->version),
		SUDO_API_VERSION_MAJOR);
	}
	if (plugin->type == SUDO_POLICY_PLUGIN) {
	    if (policy_plugin->handle)
		errorx(1, "only a single policy plugin may be loaded");
	    policy_plugin->handle = handle;
	    policy_plugin->name = info->symbol_name;
	    policy_plugin->u.generic = plugin;
	} else if (plugin->type == SUDO_IO_PLUGIN) {
	    container = emalloc(sizeof(*container));
	    container->prev = container;
	    container->next = NULL;
	    container->handle = handle;
	    container->name = info->symbol_name;
	    container->u.generic = plugin;
	    tq_append(io_plugins, container);
	}
    }
    if (policy_plugin->handle == NULL)
	errorx(1, "%s: at least one policy plugin must be specified", conf_file);
    if (policy_plugin->u.policy->check_policy == NULL)
	errorx(1, "policy plugin %s does not include a check_policy method");
}