Beispiel #1
0
static int
init(void)
{
    if (initialised)
	cfg_clean_config();

    report(LOG_NOTICE, "Reading config");

    if (!session.cfgfile) {
	report(LOG_ERR, "no config file specified");
	tac_exit(1);
    }

    /* read the config file */
    if (cfg_read_config(session.cfgfile)) {
	report(LOG_ERR, "Parsing %s", session.cfgfile);
	tac_exit(1);
    }

    if (session.acctfile == NULL && !(session.flags & SESS_FLAG_ACCTSYSL))
	session.acctfile = tac_strdup(TACPLUS_ACCTFILE);

    initialised++;
    reinitialize = 0;
    report(LOG_NOTICE, "Version %s Initialized %d", version, initialised);

    return 0;
}
/*
 * Interpolate values of dollar variables into a string.  Determine values
 * for the various $ variables by looking in the authorization data.
 */
static char *
substitute(char *string, struct author_data *data)
{
    char *cp;
    char out[MAX_INPUT_LINE_LEN], *outp;
    char sym[MAX_INPUT_LINE_LEN], *symp;
    char *value, *valuep;

    if (debug & DEBUG_AUTHOR_FLAG)
	report(LOG_DEBUG, "substitute: %s", string);

    cp = string;
    outp = out;

    while (*cp) {
	if (*cp != DOLLARSIGN) {
	    *outp++ = *cp++;
	    continue;
	}
	cp++;			/* skip dollar sign */
	symp = sym;

	/* does it have curly braces e.g. ${foo} ? */
	if (*cp == '{') {
	    cp++;		/* skip { */
	    while (*cp && *cp != '}')
		*symp++ = *cp++;
	    cp++;		/* skip } */

	} else {
	    /* copy symbol into sym */
	    while (*cp && isalpha((int) *cp))
		*symp++ = *cp++;
	}

	*symp = '\0';
	/* lookup value */

	if (debug & DEBUG_SUBST_FLAG)
	    report(LOG_DEBUG, "Lookup %s", sym);

	valuep = value = lookup(sym, data);

	if (debug & DEBUG_SUBST_FLAG)
	    report(LOG_DEBUG, "Expands to: %s", value);

	/* copy value into output */
	while (valuep && *valuep)
	    *outp++ = *valuep++;
	free(value);
    }
    *outp++ = '\0';

    if (debug & DEBUG_AUTHOR_FLAG)
	report(LOG_DEBUG, "Dollar substitution: %s", out);

    return(tac_strdup(out));
}
Beispiel #3
0
static void
declare(char *name, int value)
{
    KEYWORD *n;
    KEYWORD *k = (KEYWORD *)tac_malloc(sizeof(KEYWORD));

    k->word = tac_strdup(name);
    k->value = value;

    n = hash_add_entry(wordtable, (void *) k);

    if (n) {
	report(LOG_ERR, "Attempt to multiply define keyword %s", name);
	tac_exit(1);
    }
}
Beispiel #4
0
/*
 * Reassemble the command arguments as typed by the user, out of the
 * array of args we received. Return "" if there are no arguments.
 */
static char *
assemble_args(struct author_data *data)
{
    char *buf;
    int i;
    char *nas_arg, *v;
    int len;

    len = 0;
    for (i = 0; i < data->num_in_args; i++) {
	nas_arg = data->input_args[i];
	if (strncmp(nas_arg, "cmd-arg", strlen("cmd-arg")) == 0) {
	    v = value(nas_arg);
	    if (v != NULL)
		len += strlen(v) + 1;
	}
    }

    if (len <= 0) {
	return(tac_strdup(""));
    }

    buf = tac_malloc(len);
    buf[0] = '\0';

    for (i = 0; i < data->num_in_args; i++) {
	nas_arg = data->input_args[i];
	if (strncmp(nas_arg, "cmd-arg", strlen("cmd-arg")))
	    continue;

	v = value(nas_arg);
	if (!v) {
	    free(buf);
	    return(NULL);
	}
	strncat(buf, v, len - 1);
	len -= strlen(v);
	if (i < (data->num_in_args - 1)) {
	    strncat(buf, " ", len - 1);
	    len -= 1;
	}
    }
    return(buf);
}
Beispiel #5
0
/*
 * Return 0 means data->status is valid.
 * protocol is only valid if svc == ppp.
 */
static int
authorize_svc(char *user, int svc, char *protocol, char *svcname,
	      struct author_data *data)
{
    int max_args;
    char **out_args, **outp;
    char *nas_arg, *cfg_arg;
    int i, j;
    char **cfg_args;
    char **cfg_argp;
    int deny_by_default;
    NODE *node;

    int replaced = 0;
    int added = 0;
    int cfg_cnt;

    /* Does this service exist? */
    node = cfg_get_svc_node(user, svc, protocol, svcname, TAC_PLUS_RECURSE);

    if (!node) {
	/* Service not found. If the default is permit, or this is an
	 * PPP/LCP request and other ppp services are configured,
	 * we'll allow it. */

	if (cfg_user_svc_default_is_permit(user)) {
	    if (debug & DEBUG_AUTHOR_FLAG)
		report(LOG_DEBUG, "svc=%s protocol=%s svcname=%s not found, "
		       "permitted by default", cfg_nodestring(svc),
		       protocol ? protocol : "", svcname ? svcname : "");

	    data->status = AUTHOR_STATUS_PASS_ADD;
	    data->num_out_args = 0;
	    data->output_args = NULL;
	    return(0);
	}

	if (ppp_lcp_allowed(svc, protocol, user)) {
	    data->status = AUTHOR_STATUS_PASS_ADD;
	    data->num_out_args = 0;
	    data->output_args = NULL;
	    return(0);
	}

	if (debug & DEBUG_AUTHOR_FLAG)
	    report(LOG_DEBUG, "svc=%s protocol=%s not found, denied by default",
		   cfg_nodestring(svc), protocol ? protocol : "");

	data->status = AUTHOR_STATUS_FAIL;
	data->num_out_args = 0;
	data->output_args = NULL;
	return(0);
    }

    /* Get server args configured in the config file. */
    cfg_args = cfg_get_svc_attrs(node, &deny_by_default);

    /* Check the nas args for well-formedness */
    for (i = 0; i < data->num_in_args; i++) {
	if (!arg_ok(data->input_args[i])) {
	    char buf[MAX_INPUT_LINE_LEN+50];
	    snprintf(buf, sizeof(buf), "Illegal arg %s from NAS",
		     data->input_args[i]);
	    data->status = AUTHOR_STATUS_ERROR;
	    data->admin_msg = tac_strdup(buf);
	    report(LOG_ERR, "%s: Error %s", session.peer, buf);

	    /* free any server arguments */
	    for (cfg_argp = cfg_args; cfg_args && *cfg_argp; cfg_argp++)
		free(*cfg_argp);
	    free(cfg_args);
	    return(0);
	}
    }

    /* How many configured AV pairs are there ? */
    for (cfg_cnt = 0; cfg_args && cfg_args[cfg_cnt];)
	cfg_cnt++;

    /* Allocate space for in + out args */
    max_args = cfg_cnt + data->num_in_args;
    out_args = (char **)tac_malloc((max_args + 1) * sizeof(char *));
    outp = out_args;
    data->num_out_args = 0;

    memset(out_args, 0, (max_args + 1) * sizeof(char *));

    for (i = 0; i < data->num_in_args; i++) {
	nas_arg = data->input_args[i];

	/* always pass these pairs through unchanged */
	if (match_attrs(nas_arg, "service=") ||
	    match_attrs(nas_arg, "protocol=") ||
	    match_attrs(nas_arg, "cmd=")) {

	    if (debug & DEBUG_AUTHOR_FLAG) {
		report(LOG_DEBUG, "nas:%s (passed thru)", nas_arg);
	    }
	    *outp++ = tac_strdup(nas_arg);
	    data->num_out_args++;
	    continue;
	}

	/* NAS AV pair is mandatory */
	if (mandatory(nas_arg)) {
	    /*
	     * a). look for an exact attribute,value match in the daemon's
	     * mandatory list. If found, add the AV pair to the output
	     */
	    for (j = 0; j < cfg_cnt; j++) {
		cfg_arg = cfg_args[j];
		if (optional(cfg_arg))
		    continue;

		if (STREQ(nas_arg, cfg_arg)) {
		    if (debug & DEBUG_AUTHOR_FLAG) {
			report(LOG_DEBUG, "nas:%s, svr:%s -> add %s (a)",
			       nas_arg, cfg_arg, nas_arg);
		    }
		    *outp++ = tac_strdup(nas_arg);
		    data->num_out_args++;
		    goto next_nas_arg;
		}
	    }

	    /*
	     * b). If an exact match doesn't exist, look in the daemon's
	     * optional list for the first attribute match. If found, add the
	     * NAS AV pair to the output
	     */
	    for (j = 0; j < cfg_cnt; j++) {
		cfg_arg = cfg_args[j];
		if (mandatory(cfg_arg))
		    continue;

		if (match_attrs(nas_arg, cfg_arg)) {
		    if (debug & DEBUG_AUTHOR_FLAG) {
			report(LOG_DEBUG, "nas:%s, svr:%s -> add %s (b)",
			       nas_arg, cfg_arg, nas_arg);
		    }
		    *outp++ = tac_strdup(nas_arg);
		    data->num_out_args++;
		    goto next_nas_arg;
		}
	    }

	    /*
	     * c). If no attribute match exists, deny the command if the
	     * default is to deny
	     */
	    if (deny_by_default) {
		data->status = AUTHOR_STATUS_FAIL;
		if (debug & DEBUG_AUTHOR_FLAG) {
		    report(LOG_DEBUG, "nas:%s svr:absent, default=deny -> "
							"denied (c)", nas_arg);
		}
		if (out_args) {
		    for (i = 0; i < data->num_out_args; i++)
			free(out_args[i]);
		    free(out_args);
		}

		data->num_out_args = 0;
		data->output_args = NULL;

		/* free the server arguments */
		for (cfg_argp = cfg_args; *cfg_argp; cfg_argp++)
		    free(*cfg_argp);
		free(cfg_args);
		return(0);
	    }

	    /*
	     * d). If the default is permit, add the NAS AV pair to the output
	     */
	    if (debug & DEBUG_AUTHOR_FLAG) {
		report(LOG_DEBUG,
		       "nas:%s, svr:absent, default=permit -> add %s (d)",
		       nas_arg, nas_arg);
	    }
	    *outp++ = tac_strdup(nas_arg);
	    data->num_out_args++;
	    goto next_nas_arg;
	} else {
	    /*
	     * NAS AV pair is Optional
	     *
	     * e). look for an exact attribute,value match in the mandatory
	     * list. If found, add DAEMON's AV pair to output
	     */
	    for (j = 0; j < cfg_cnt; j++) {
		cfg_arg = cfg_args[j];
		if (optional(cfg_arg))
		    continue;

		if (match_attrs(nas_arg, cfg_arg) &&
		    match_values(nas_arg, cfg_arg)) {

		    if (debug & DEBUG_AUTHOR_FLAG) {
			report(LOG_DEBUG, "nas:%s svr:%s -> replace with %s "
					"(e)", nas_arg, cfg_arg, cfg_arg);
		    }
		    *outp++ = tac_strdup(cfg_arg);
		    data->num_out_args++;
		    replaced++;
		    goto next_nas_arg;
		}
	    }

	    /*
	     * f). If not found, look for the first attribute match in the
	     * mandatory list. If found, add DAEMONS's AV pair to output
	     */
	    for (j = 0; j < cfg_cnt; j++) {
		cfg_arg = cfg_args[j];
		if (optional(cfg_arg))
		    continue;

		if (match_attrs(nas_arg, cfg_arg)) {
		    if (debug & DEBUG_AUTHOR_FLAG) {
			report(LOG_DEBUG, "nas:%s svr:%s -> replace with %s "
					"(f)", nas_arg, cfg_arg, cfg_arg);
		    }
		    *outp++ = tac_strdup(cfg_arg);
		    data->num_out_args++;
		    replaced++;
		    goto next_nas_arg;
		}
	    }

	    /*
	     * g). If no mandatory match exists, look for an exact
	     * attribute,value pair match among the daemon's optional AV
	     * pairs. If found add the DAEMON's matching AV pair to the
	     * output.
	     */
	    for (j = 0; j < cfg_cnt; j++) {
		cfg_arg = cfg_args[j];
		if (!optional(cfg_arg))
		    continue;

		if (match_attrs(nas_arg, cfg_arg) &&
		    match_values(nas_arg, cfg_arg)) {
		    if (debug & DEBUG_AUTHOR_FLAG) {
			report(LOG_DEBUG, "nas:%s svr:%s -> replace with %s "
					"(g)", nas_arg, cfg_arg, cfg_arg);
		    }
		    *outp++ = tac_strdup(cfg_arg);
		    data->num_out_args++;
		    replaced++;
		    goto next_nas_arg;
		}
	    }

	    /*
	     * h). If no exact match exists, locate the first attribute match
	     * among the daemon's optional AV pairs. If found add the DAEMON's
	     * matching AV pair to the output
	     */
	    for (j = 0; j < cfg_cnt; j++) {
		cfg_arg = cfg_args[j];
		if (!optional(cfg_arg))
		    continue;

		if (match_attrs(nas_arg, cfg_arg)) {
		    if (debug & DEBUG_AUTHOR_FLAG) {
			report(LOG_DEBUG, "nas:%s svr:%s -> replace with %s "
			       "(h)", nas_arg, cfg_arg, cfg_arg);
		    }
		    *outp++ = tac_strdup(cfg_arg);
		    data->num_out_args++;
		    replaced++;
		    goto next_nas_arg;
		}
	    }

	    /*
	     * i). If no match is found, delete the AV pair if default is deny
	     */
	    if (deny_by_default) {
		if (debug & DEBUG_AUTHOR_FLAG) {
		    report(LOG_DEBUG, "nas:%s svr:absent/deny -> delete %s (i)",
			   nas_arg, nas_arg);
		}
		replaced++;
		goto next_nas_arg;
	    }

	    /* j). If the default is permit add the NAS AV pair to the output */
	    if (debug & DEBUG_AUTHOR_FLAG) {
		report(LOG_DEBUG, "nas:%s svr:absent/permit -> add %s (j)",
		       nas_arg, nas_arg);
	    }
	    *outp++ = tac_strdup(nas_arg);
	    data->num_out_args++;
	    goto next_nas_arg;
	}
    next_nas_arg:;
    }

    /*
     * k). After all AV pairs have been processed, for each mandatory DAEMON
     * AV pair, if there is no attribute match already in the output list, add
     * the AV pair (add only one AV pair for each mandatory attribute)
     */
    for (i = 0; i < cfg_cnt; i++) {
	cfg_arg = cfg_args[i];

	if (!mandatory(cfg_arg))
	    continue;

	for (j = 0; j < data->num_out_args; j++) {
	    char *output_arg = out_args[j];

	    if (match_attrs(cfg_arg, output_arg)) {
		goto next_cfg_arg;
	    }
	}

	/* Attr is required by daemon but not present in output. Add it */
	if (debug & DEBUG_AUTHOR_FLAG) {
	    report(LOG_DEBUG, "nas:absent, server:%s -> add %s (k)",
		   cfg_arg, cfg_arg);
	}
	added++;
	*outp++ = tac_strdup(cfg_arg);
	data->num_out_args++;

    next_cfg_arg:
	;
    }

    /*
     * If we replaced or deleted some pairs we must return the entire list we
     * have constructed
     */
    if (replaced) {
	if (debug & DEBUG_AUTHOR_FLAG) {
	    report(LOG_DEBUG, "replaced %d args", replaced);
	}
	data->status = AUTHOR_STATUS_PASS_REPL;
	data->output_args = out_args;

	/* free the server arguments */
	for (cfg_argp = cfg_args; *cfg_argp; cfg_argp++)
	    free(*cfg_argp);
	free(cfg_args);

	return(0);
    }

    /*
     * We added something not on the original nas list, but did not replace or
     * delete anything. We should return only the additions
     */
    if (added) {
	if (debug & DEBUG_AUTHOR_FLAG)
	    report(LOG_DEBUG, "added %d args", added);

	/* throw away output args which are just copies of the input args */
	for (i = 0; i < data->num_in_args; i++) {
	    if (debug & DEBUG_AUTHOR_FLAG) {
		report(LOG_DEBUG, "out_args[%d] = %s input copy discarded",
		       i, out_args[i]);
	    }
	    free(out_args[i]);
	    out_args[i] = NULL;
	}

	/*
	 * Now compact the new args added to the end of the array down to the
	 * beginning
	 */
	j = 0;
	for (i = data->num_in_args; i < data->num_out_args; i++) {
	    if (out_args[j]) /* we goofed */
		report(LOG_ERR, "%s: out_args[%d] should be NULL",
		       session.peer, j);
	    if (!out_args[i]) /* we goofed */
		report(LOG_ERR, "%s: out_args[%d] should not be NULL",
		       session.peer, i);

	    if (debug & DEBUG_AUTHOR_FLAG) {
		report(LOG_DEBUG, "out_args[%d] = %s compacted to out_args[%d]",
		       i, out_args[i], j);
	    }
	    out_args[j++] = out_args[i];
	    out_args[i] = NULL;
	}
	data->num_out_args = j;
	if (debug & DEBUG_AUTHOR_FLAG) {
	    report(LOG_DEBUG, "%d output args", data->num_out_args);
	}

	/* should/could do a realloc here but it won't matter */
	data->status = AUTHOR_STATUS_PASS_ADD;
	data->output_args = out_args;

	/* free the server arguments */
	for (cfg_argp = cfg_args; *cfg_argp; cfg_argp++)
	    free(*cfg_argp);
	free(cfg_args);

	return(0);
    }

    /*
     * no additions or replacements. Input and output are identical. Don't
     * need to return anything
     */
    if (debug & DEBUG_AUTHOR_FLAG) {
	report(LOG_DEBUG, "added %d", added);
    }
    data->status = AUTHOR_STATUS_PASS_ADD;
    if (out_args) {
	for (i = 0; i < data->num_out_args; i++) {
	    free(out_args[i]);
	}
	free(out_args);
    }

    /* Final sanity check */
    if (data->num_out_args != data->num_in_args) {
	data->status = AUTHOR_STATUS_ERROR;
	data->admin_msg = tac_strdup("Bad output arg cnt from do_author");
	report(LOG_ERR, "%s: Error %s", session.peer, data->admin_msg);

	/* free the server arguments */
	for (cfg_argp = cfg_args; *cfg_argp; cfg_argp++)
	    free(*cfg_argp);
	free(cfg_args);

	return(0);
    }

    data->num_out_args = 0;
    data->output_args = NULL;

    /* free the server arguments */
    for (cfg_argp = cfg_args; *cfg_argp; cfg_argp++)
	free(*cfg_argp);
    free(cfg_args);

    return(0);
}
Beispiel #6
0
/* Return 0 is data->status is valid */
int
do_author(struct author_data *data)
{
    char *username = data->id->username;
    int status;
    int svc;
    char *cmd, *protocol, *svcname;

    status = 0;
    protocol = NULL;

    data->status = AUTHOR_STATUS_FAIL;	/* for safety */

    data->output_args = NULL;
    data->num_out_args = 0;

    if (debug & DEBUG_AUTHOR_FLAG)
	report(LOG_DEBUG, "do_author: user='******'", username);

    if (!cfg_user_exists(username) && cfg_user_exists(DEFAULT_USERNAME)) {
	if (debug & DEBUG_AUTHOR_FLAG) {
	    report(LOG_DEBUG, "Authorizing user '%s' instead of '%s'",
		   DEFAULT_USERNAME, username);
	}
	username = DEFAULT_USERNAME;
    }

    /* See if there's a program defined which will do authorization for us */
    if (pre_authorization(username, data))
	return(0);

    /*
     * Decide what kind of authorization request this is. Currently
     * one of: exec, cmd, slip, arap, ppp or <string>
     *
     * If it's a command typed to the exec, return its text in cmd.
     *
     * If it's a ppp request, return the protocol name in protocol.
     */
    svc = get_nas_svc(data, &cmd, &protocol, &svcname);

    if (!svc) {
	/* if we can't identify the service in the request it's an error */
	data->status = AUTHOR_STATUS_ERROR;
	data->admin_msg =
	tac_strdup("No identifiable service/protocol in authorization request");
	if (debug & DEBUG_AUTHOR_FLAG) {
	    report(LOG_DEBUG, "user %s %s", username, data->admin_msg);
	}
	return(0);
    }

    if (debug & DEBUG_AUTHOR_FLAG)
	report(LOG_DEBUG, "user '%s' found", username);

#ifdef MAXSESS
    /* Never permit if they're going over their session limit */
    switch (svc) {
    case N_svc_arap:
    case N_svc_ppp:
    case N_svc_slip:
    case N_svc_exec:
/*    case N_svc: */
	if (maxsess_check_count(username, data)) {
	    return(0);
	}

    default:
	break;
    }
#endif /* MAXSESS */

    switch(svc) {
/*  XXX
    default:
	report(LOG_ERR, "%s: Bad service type %d", session.peer, svc);
	data->status = AUTHOR_STATUS_FAIL;
	return(0);*/

    case N_svc_cmd:
	/* A command authorisation request */
	status = authorize_cmd(username, cmd, data);
	break;

    case N_svc_exec:
	if (authorize_exec(username, data))
	    return(0);
	/* FALLTHRU */

    case N_svc_arap:
    case N_svc_ppp:
    case N_svc_slip:
	status = authorize_svc(username, svc, protocol, NULL, data);
	break;

    case N_svc:
	status = authorize_svc(username, svc, protocol, svcname, data);
	break;
    }

    post_authorization(username, data);
    return(status);
}
Beispiel #7
0
/*
 * Is an exec command authorized per our database(s)?  Return 0 if status is
 * valid.
 */
static int
authorize_cmd(char *user, char *cmd, struct author_data *data)
{
    char buf[256];
    NODE *node;
    char *args;
    int match;

    args = assemble_args(data);

    if (!cmd) {
	data->status = AUTHOR_STATUS_ERROR;
	data->admin_msg = tac_strdup("No command found");
	report(LOG_ERR, "%s: %s %s", session.peer, cmd, data->admin_msg);
	data->num_out_args = 0;
	return(0);
    }

    if (debug & DEBUG_AUTHOR_FLAG)
	report(LOG_DEBUG, "authorize_cmd: user=%s, cmd=%s", user, cmd);

    node = cfg_get_cmd_node(user, cmd, TAC_PLUS_RECURSE);

    /* The command does not exist. Do the default */
    if (!node) {
	if (cfg_user_svc_default_is_permit(user)) {
	    if (debug & DEBUG_AUTHOR_FLAG)
		report(LOG_DEBUG, "cmd %s does not exist, permitted by default",
		       cmd);
	    data->status = AUTHOR_STATUS_PASS_ADD;
	    data->num_out_args = 0;
	    if (args)
		free(args);
	    return(0);
	}

	if (debug & DEBUG_AUTHOR_FLAG)
	    report(LOG_DEBUG, "cmd %s does not exist, denied by default",
		   cmd);

	data->status = AUTHOR_STATUS_FAIL;
	data->num_out_args = 0;
	if (args)
	    free(args);
	return(0);
    }

    /* The command exists. The default if nothing matches is DENY */
    data->status = AUTHOR_STATUS_FAIL;
    data->num_out_args = 0;
    for (node = node->value1; node && args; node = node->next) {
	match = regexec((regex_t *)node->value1, args, 0, NULL, 0);

	if (debug & DEBUG_AUTHOR_FLAG) {
	    report(LOG_INFO, "line %d compare %s %s '%s' & '%s' %s",
		   node->line, cmd,
		   node->type == N_permit ? "permit" : "deny",
		   node->value, args,
		   (match == REG_NOMATCH ? "no match" :
			     !match ? "match" : "regex failure"));
	}

	if (match == REG_NOMATCH)
	    continue;
	if (match != REG_OK) {
	    regerror(match, (regex_t *)node->value1, buf, 256);
	    report(LOG_INFO, "regexec error: %s on line %d: %s",
		   (char *)node->value, node->line, buf);
	    continue;
	}

	switch (node->type) {
	case N_permit:
	    if (debug & DEBUG_AUTHOR_FLAG) {
		report(LOG_DEBUG, "%s %s permitted by line %d",
		       cmd, args, node->line);
	    }
	    data->status = AUTHOR_STATUS_PASS_ADD;
	    data->num_out_args = 0;
	    break;
	case N_deny:
	    if (debug & DEBUG_AUTHOR_FLAG) {
		report(LOG_DEBUG, "%s %s denied by line %d",
		       cmd, args, node->line);
	    }
	    data->status = AUTHOR_STATUS_FAIL;
	    data->num_out_args = 0;
	    break;
	default:
	    data->status = AUTHOR_STATUS_ERROR;
	    data->admin_msg = tac_strdup("Server error illegal configuration "
					 "node");
	    report(LOG_ERR, "%s: %s %s %s",
		   session.peer, cmd, args, data->admin_msg);
	    break;
	}
	if (args)
	    free(args);
	args = NULL;
	return(0);
    }
    if (args)
	free(args);
    return(0);
}
Beispiel #8
0
/*
 * If an before-authorization program has been specified, call it.
 *
 * A return value of 1 means no further authorization is required
 */
static int
pre_authorization(char *username, struct author_data *data)
{
    int status;
    char **out_args;
    int out_cnt, i;
    char *cmd;
    char error_str[255];
    int error_len = 255;

    out_cnt = 0;
    out_args = NULL;

    /*
     * If a before-authorization program exists, call it to see how to
     * proceed
     */
    cmd = cfg_get_pvalue(username, TAC_IS_USER,
			 S_before, TAC_PLUS_RECURSE);
    if (!cmd)
	return(0);

    if (debug & DEBUG_AUTHOR_FLAG)
	report(LOG_DEBUG, "Before authorization call: %s", cmd);

    status = call_pre_process(cmd, data, &out_args, &out_cnt, error_str,
			      error_len);

    switch (status) {
    default:
	if (debug & DEBUG_AUTHOR_FLAG)
	    report(LOG_DEBUG, "cmd %s returns %d (unrecognised value)",
		   cmd, status);

	data->status = AUTHOR_STATUS_ERROR;
	data->admin_msg =
	    tac_strdup("Illegal return status from pre-authorization command");
	data->msg = tac_strdup(error_str);
	data->num_out_args = 0;
	data->output_args = NULL;
	/* throw away out_args */
	for (i = 0; i < out_cnt; i++) {
	    free(out_args[i]);
	}
	if (out_args) {
	    free(out_args);
	}
	return(1);

    case 0: /* Permit */
	if (debug & DEBUG_AUTHOR_FLAG)
	    report(LOG_DEBUG, "cmd %s returns 0 (unconditional permit)", cmd);

	data->status = AUTHOR_STATUS_PASS_ADD;
	data->num_out_args = 0;
	data->output_args = NULL;

	/* throw away out_args */
	for (i = 0; i < out_cnt; i++) {
	    free(out_args[i]);
	}
	if (out_args) {
	    free(out_args);
	}
	return(1);

    case 1: /* Deny */
	if (debug & DEBUG_AUTHOR_FLAG)
	    report(LOG_DEBUG, "cmd %s returns %d (unconditional deny)",
		   cmd, status);

	data->status = AUTHOR_STATUS_FAIL;
	data->msg = tac_strdup(error_str);
	data->num_out_args = 0;
	data->output_args = NULL;

	/* throw away out_args */
	for (i = 0; i < out_cnt; i++) {
	    free(out_args[i]);
	}
	if (out_args) {
	    free(out_args);
	}
	return(1);

    case 2: /* Use replacement AV pairs from program as final result */
	if (debug & DEBUG_AUTHOR_FLAG) {
	    report(LOG_DEBUG, "cmd %s returns %d (permitted, args replaced)",
		   cmd, status);
	    for (i = 0; i < out_cnt; i++)
		report(LOG_DEBUG, "%s", out_args[i]);
	}

	/* and install the new set of AV pairs as output */
	data->output_args = out_args;
	data->num_out_args = out_cnt;
	data->status = AUTHOR_STATUS_PASS_REPL;
	return(1); /* no more processing required */

    case 3: /* deny, but return attributes and server-msg to NAS */
	if (debug & DEBUG_AUTHOR_FLAG) {
	    report(LOG_DEBUG, "cmd %s returns %d (deny, args replaced)",
		   cmd, status);
	    for (i = 0; i < out_cnt; i++)
		report(LOG_DEBUG, "%s", out_args[i]);
	}

	/* and install the new set of AV pairs as output */
	data->output_args = out_args;
	data->num_out_args = out_cnt;
	data->msg = tac_strdup(error_str);
	data->status = AUTHOR_STATUS_FAIL;
	return(1); /* no more processing required */
    }
}
Beispiel #9
0
static void
account(u_char *pak)
{
    struct acct *acct_pak;
    u_char *p, *argsizep;
    struct acct_rec rec;
    struct identity identity;
    char **cmd_argp;
    int i, errors = 0, status;

    acct_pak = (struct acct *) (pak + TAC_PLUS_HDR_SIZE);

    /* Fill out accounting record structure */
    memset(&rec, 0, sizeof(struct acct_rec));

    if (acct_pak->flags & TAC_PLUS_ACCT_FLAG_WATCHDOG)
	rec.acct_type = ACCT_TYPE_UPDATE;
    if (acct_pak->flags & TAC_PLUS_ACCT_FLAG_START)
	rec.acct_type = ACCT_TYPE_START;
    if (acct_pak->flags & TAC_PLUS_ACCT_FLAG_STOP)
	rec.acct_type = ACCT_TYPE_STOP;

    rec.authen_method  = acct_pak->authen_method;
    rec.authen_type    = acct_pak->authen_type;
    rec.authen_service = acct_pak->authen_service;

    /* start of variable length data is here */
    p = pak + TAC_PLUS_HDR_SIZE + TAC_ACCT_REQ_FIXED_FIELDS_SIZE;

    /* skip arg cnts */
    p += acct_pak->arg_cnt;

    /* zero out identity struct */
    memset(&identity, 0, sizeof(struct identity));

    identity.username = tac_make_string(p, (int)acct_pak->user_len);
    p += acct_pak->user_len;

    identity.NAS_name = tac_strdup(session.peer);

    identity.NAS_port = tac_make_string(p, (int)acct_pak->port_len);
    p += acct_pak->port_len;
    if (acct_pak->port_len <= 0) {
	strcpy(session.port, "unknown-port");
    } else {
	strcpy(session.port, identity.NAS_port);
    }

    identity.NAC_address = tac_make_string(p, (int)acct_pak->rem_addr_len);
    p += acct_pak->rem_addr_len;

    identity.priv_lvl = acct_pak->priv_lvl;

    rec.identity = &identity;

    /* Now process cmd args */
    argsizep = pak + TAC_PLUS_HDR_SIZE + TAC_ACCT_REQ_FIXED_FIELDS_SIZE;

    cmd_argp = (char **)tac_malloc(acct_pak->arg_cnt * sizeof(char *));

    for (i = 0; i < (int)acct_pak->arg_cnt; i++) {
	cmd_argp[i] = tac_make_string(p, *argsizep);
	p += *argsizep++;
    }

    rec.args = cmd_argp;
    rec.num_args = acct_pak->arg_cnt;

#ifdef MAXSESS
    /* Tally for MAXSESS counting */
    loguser(&rec);
#endif

    /* Do accounting */
    if (wtmpfile)
	errors = do_wtmp(&rec);
    if (session.acctfile != NULL)
	errors += do_acct_file(&rec);
    if (session.flags & SESS_FLAG_ACCTSYSL)
	errors += do_acct_syslog(&rec);

    if (errors) {
	status = TAC_PLUS_ACCT_STATUS_ERROR;
    } else {
	status = TAC_PLUS_ACCT_STATUS_SUCCESS;
    }
    send_acct_reply(status, rec.msg, rec.admin_msg);

    free(identity.username);
    free(identity.NAS_name);
    free(identity.NAS_port);
    free(identity.NAC_address);

    for (i = 0; i < (int)acct_pak->arg_cnt; i++) {
	free(cmd_argp[i]);
    }
    free(cmd_argp);

    if (rec.msg)
	free(rec.msg);
    if (rec.admin_msg)
	free(rec.admin_msg);
}
/*
 * Support for dollar variables.  Look in the authorization data and return
 * strings representing values found there.  If not found, return "unknown".
 * Recognized strings and their interpolated value types are:
 *
 * user    -- user name
 * name    -- NAS name
 * port    -- NAS port
 * ip      -- NAS ip
 * address -- NAC address (remote user location)
 * priv    -- privilege level (0 to 15)
 * method  -- (1 to 4)
 * type    -- (1 to 4)
 * service -- (1 to 7)
 * status  -- (pass, fail, error, unknown)
 */
static char *
lookup(char *sym, struct author_data *data)
{
    static char buf[5];

    if (STREQ(sym, "user")) {
	return(tac_strdup(data->id->username));
    }
    if (STREQ(sym, "name")) {
	return(tac_strdup(data->id->NAS_name));
    }
    if (STREQ(sym, "ip")) {
	return(tac_strdup(data->id->NAS_ip));
    }
    if (STREQ(sym, "port")) {
	return(tac_strdup(data->id->NAS_port));
    }
    if (STREQ(sym, "address")) {
	return(tac_strdup(data->id->NAC_address));
    }
    if (STREQ(sym, "priv")) {
	snprintf(buf, sizeof(buf), "%d", data->id->priv_lvl);
	return(tac_strdup(buf));
    }
    if (STREQ(sym, "method")) {
	snprintf(buf, sizeof(buf), "%d", data->authen_method);
	return(tac_strdup(buf));
    }
    if (STREQ(sym, "type")) {
	snprintf(buf, sizeof(buf), "%d", data->authen_type);
	return(tac_strdup(buf));
    }
    if (STREQ(sym, "service")) {
	snprintf(buf, sizeof(buf), "%d", data->service);
	return(tac_strdup(buf));
    }
    if (STREQ(sym, "status")) {
	switch (data->status) {
	default:
	    return(tac_strdup("unknown"));
	case AUTHOR_STATUS_PASS_ADD:
	case AUTHOR_STATUS_PASS_REPL:
	    return(tac_strdup("pass"));
	case AUTHOR_STATUS_FAIL:
	    return(tac_strdup("fail"));
	case AUTHOR_STATUS_ERROR:
	    return(tac_strdup("error"));
	}
    }

    return(tac_strdup("unknown"));
}
/*
 * Cleartext password information has been requested.  Look this up in
 * the config file. Set authen_data->status.
 *
 * Any strings pointed to by authen_data must come from the heap. They
 * will get freed by the caller.
 *
 * Return 0 if data->status is valid, otherwise 1
 */
static int
do_sendpass_fn(struct authen_data *data)
{
    char *name;
    char *p;
    int expired;
    char *exp_date;
    char *secret;

    data->status = TAC_PLUS_AUTHEN_STATUS_FAIL;

    /* We must have a username */
    if (!data->NAS_id->username[0]) {
	/* choose_authen should have already asked for a username, so this is
	 * a gross error */
	data->status = TAC_PLUS_AUTHEN_STATUS_ERROR;
	data->server_msg = tac_strdup("No username supplied");
	report(LOG_ERR, "%s: No username for sendpass_fn", session.peer);
	return(0);
    }
    name = data->NAS_id->username;

    exp_date = cfg_get_expires(name, TAC_PLUS_RECURSE);

    /* The user exists. Check the expiration date, if any */
    expired = check_expiration(exp_date);

    switch (expired) {
    case PW_EXPIRED:
	data->status = TAC_PLUS_AUTHEN_STATUS_FAIL;
	data->server_msg = tac_strdup("Password has expired");
	return(0);

    default:
	data->status = TAC_PLUS_AUTHEN_STATUS_ERROR;
	data->server_msg = tac_strdup("Bad return value for password "
				      "expiration check");
	report(LOG_ERR, "%s: Bogus return value %d from check_expiration",
	       session.peer, expired);
	return(0);

    case PW_OK:
    case PW_EXPIRING:

	/* The user exists, and has not expired. Return her secret info */
	switch (data->type) {
	case TAC_PLUS_AUTHEN_TYPE_CHAP:
	    secret = cfg_get_chap_secret(name, TAC_PLUS_RECURSE);
	    if (!secret)
		secret = cfg_get_global_secret(name, TAC_PLUS_RECURSE);
	    break;

#ifdef MSCHAP
	case TAC_PLUS_AUTHEN_TYPE_MSCHAP:
	    secret = cfg_get_mschap_secret(name, TAC_PLUS_RECURSE);
	    if (!secret)
		secret = cfg_get_global_secret(name, TAC_PLUS_RECURSE);
	    break;
#endif /* MSCHAP */

	case TAC_PLUS_AUTHEN_TYPE_ARAP:
	    secret = cfg_get_arap_secret(name, TAC_PLUS_RECURSE);
	    if (!secret)
		secret = cfg_get_global_secret(name, TAC_PLUS_RECURSE);
	    break;

	case TAC_PLUS_AUTHEN_TYPE_PAP:
	    secret = cfg_get_opap_secret(name, TAC_PLUS_RECURSE);
	    break;

	default:
	    data->status = TAC_PLUS_AUTHEN_STATUS_ERROR;
	    data->server_msg = tac_strdup("Illegal authentication type");
	    report(LOG_ERR, "%s: Illegal authentication type %d",
		   session.peer, data->type);
	    return(0);
	}

	if (!secret) {
	    data->status = TAC_PLUS_AUTHEN_STATUS_FAIL;
	    data->server_msg = tac_strdup("No secret");
	    return(0);
	}

	p = tac_find_substring("cleartext ", secret);
	if (!p) {
	    /* Should never happen */
	    data->status = TAC_PLUS_AUTHEN_STATUS_ERROR;
	    data->server_msg = tac_strdup("Illegal secret format");
	    report(LOG_ERR, "%s: Illegal secret format %s",
		   session.peer, secret);
	    return(0);
	}

	data->server_data = tac_strdup(p);
	data->server_dlen = strlen(data->server_data);
	data->status = TAC_PLUS_AUTHEN_STATUS_PASS;
	if (expired == PW_EXPIRING) {
	    data->server_msg = tac_strdup("Secret will expire soon");
	}
	return(0);
    }
    /* never reached */
}
Beispiel #12
0
int
do_acct_file(struct acct_rec *rec)
{
    int i, errors;
    time_t t = time(NULL);
    char ct[LINE_MAX];
    struct tm *tm;

    tm = localtime(&t);
    strftime(ct, LINE_MAX, "%h %e %T", tm);

    if (!acctfd) {
	acctfd = open(session.acctfile, O_CREAT | O_WRONLY | O_APPEND, 0644);
	if (acctfd < 0) {
	    report(LOG_ERR, "Can't open acct file %s -- %s",
		   session.acctfile, strerror(errno));
	    return(1);
	}
    }
    if (!tac_lockfd(session.acctfile, acctfd)) {
	rec->admin_msg = tac_strdup("Cannot lock log file");
	report(LOG_ERR, "%s: Cannot lock %s",
	       session.peer, session.acctfile);
	return(1);
    }

    errors = 0;

    errors += acct_write(ct);
    errors += acct_write("\t");

    errors += acct_write_field(rec->identity->NAS_name);
    errors += acct_write("\t");

    errors += acct_write_field(rec->identity->username);
    errors += acct_write("\t");

    errors += acct_write_field(rec->identity->NAS_port);
    errors += acct_write("\t");

    errors += acct_write_field(rec->identity->NAC_address);
    errors += acct_write("\t");

    switch(rec->acct_type) {
    case ACCT_TYPE_UPDATE:
	errors += acct_write("update\t");
	break;
    case ACCT_TYPE_START:
	errors += acct_write("start\t");
	break;
    case ACCT_TYPE_STOP:
	errors += acct_write("stop\t");
	break;
    default:
	errors += acct_write("unknown\t");
	break;
    }

    for (i=0; i < rec->num_args; i++) {
	errors += acct_write(rec->args[i]);
	if (i < (rec->num_args - 1))
	    errors += acct_write("\t");
    }
    errors += acct_write("\n");

    close(acctfd);
    acctfd = 0;

    if (errors)
	return(1);

    return(0);
}
Beispiel #13
0
/*
 *  Come here when we receive an authorization START packet
 */
void
author(u_char *pak)
{
    HDR *hdr;
    struct author *apak;
    struct identity identity;
#ifdef ACLS
    struct authen_data authen_data;
#endif
    struct author_data author_data;
    u_char *p;
    u_char *argsizep;
    char **cmd_argp;
    int i, len;

    if (debug & DEBUG_AUTHOR_FLAG)
	report(LOG_DEBUG, "Start authorization request");

    hdr = (HDR *)pak;
    apak = (struct author *)(pak + TAC_PLUS_HDR_SIZE);

    /* Do some sanity checks */
    if (hdr->seq_no != 1) {
	send_error_reply(TAC_PLUS_AUTHOR, NULL);
	return;
    }

    /* Check if there's at least sizeof(struct author) of useful data */
    if (ntohl(hdr->datalength) < TAC_AUTHOR_REQ_FIXED_FIELDS_SIZE) {
	report(LOG_ERR, "%s: author minimum payload length: %zu, got: %u",
	       session.peer, TAC_AUTHOR_REQ_FIXED_FIELDS_SIZE,
	       ntohl(hdr->datalength));
	send_error_reply(TAC_PLUS_AUTHOR, NULL);
	return;
    }

    /* arg counts start here */
    p = pak + TAC_PLUS_HDR_SIZE + TAC_AUTHOR_REQ_FIXED_FIELDS_SIZE;

    /* Length checks */
    len = TAC_AUTHOR_REQ_FIXED_FIELDS_SIZE;
    len += apak->user_len + apak->port_len + apak->rem_addr_len + apak->arg_cnt;

    /* Is there enough space for apak->arg_cnt arguments? */
    if (ntohl(hdr->datalength) <
	(TAC_AUTHOR_REQ_FIXED_FIELDS_SIZE + apak->arg_cnt)) {
	report(LOG_ERR, "%s: author minimum payload length: %zu, got: %u",
	       session.peer, TAC_AUTHOR_REQ_FIXED_FIELDS_SIZE + apak->arg_cnt,
	       ntohl(hdr->datalength));
	send_error_reply(TAC_PLUS_AUTHOR, NULL);
	return;
    }

    for (i = 0; i < (int)apak->arg_cnt; i++) {
	len += p[i];
    }

    if (len != ntohl(hdr->datalength)) {
	send_error_reply(TAC_PLUS_AUTHOR, NULL);
	return;
    }

    /* start of variable length data is here */
    p = pak + TAC_PLUS_HDR_SIZE + TAC_AUTHOR_REQ_FIXED_FIELDS_SIZE;

    /* arg length data starts here */
    argsizep = p;

    p += apak->arg_cnt;

    memset(&author_data, 0, sizeof(struct author_data));

    /* The identity structure */

    /* zero out identity struct */
    memset(&identity, 0, sizeof(struct identity));
    identity.username = tac_make_string(p, (int)apak->user_len);
    p += apak->user_len;

    identity.NAS_name = tac_strdup(session.peer);
#ifdef ACLS
    identity.NAS_ip = tac_strdup(session.peerip);
#endif

    identity.NAS_port = tac_make_string(p, (int)apak->port_len);
    p += apak->port_len;
    if (apak->port_len <= 0) {
	strcpy(session.port, "unknown-port");
    } else {
	strcpy(session.port, identity.NAS_port);
    }

    identity.NAC_address = tac_make_string(p, (int)apak->rem_addr_len);
    p += apak->rem_addr_len;

    identity.priv_lvl = apak->priv_lvl;

    /* The author_data structure */

    author_data.id = &identity;	/* user id */

    /* FIXME: validate these fields */
    author_data.authen_method = apak->authen_method;
    author_data.authen_type = apak->authen_type;
    author_data.service = apak->service;
    author_data.num_in_args = apak->arg_cnt;

    /* Space for args + NULL */
    cmd_argp = (char **)tac_malloc(apak->arg_cnt * sizeof(char *));

    /* p points to the start of args. Step thru them making strings */
    for (i = 0; i < (int)apak->arg_cnt; i++) {
	cmd_argp[i] = tac_make_string(p, *argsizep);
	p += *argsizep++;
    }

    author_data.input_args = cmd_argp;	/* input command arguments */

#ifdef ACLS
    authen_data.NAS_id = &identity;
    if (verify_host(author_data.id->username, &authen_data, S_acl,
		    TAC_PLUS_RECURSE) != S_permit) {
	author_data.status = AUTHOR_STATUS_FAIL;
    } else
#endif
	if (do_author(&author_data)) {
	    report(LOG_ERR, "%s: do_author returned an error", session.peer);
	    send_author_reply(AUTHOR_STATUS_ERROR,
			      author_data.msg, author_data.admin_msg,
			      author_data.num_out_args,
			      author_data.output_args);
	    return;
        }

    /* Send a reply packet */
    send_author_reply(author_data.status, author_data.msg,
		      author_data.admin_msg, author_data.num_out_args,
		      author_data.output_args);

    if (debug)
	report(LOG_INFO, "authorization query for '%s' %s from %s %s",
	       author_data.id->username && author_data.id->username[0] ?
	       author_data.id->username : "******",
	       author_data.id->NAS_port && author_data.id->NAS_port[0] ?
	       author_data.id->NAS_port : "unknown",
	       session.peer,
	       (author_data.status == AUTHOR_STATUS_PASS_ADD ||
		author_data.status == AUTHOR_STATUS_PASS_REPL) ?
	       "accepted" : "rejected");

    /* free the input args */
    if (author_data.input_args) {
	for (i = 0; i < author_data.num_in_args; i++)
	    free(author_data.input_args[i]);

	free(author_data.input_args);
	author_data.input_args = NULL;
    }

    /* free the output args */
    if (author_data.output_args) {
	for (i = 0; i < author_data.num_out_args; i++)
	    free(author_data.output_args[i]);

	free(author_data.output_args);
	author_data.output_args = NULL;
    }

    if (author_data.msg)
	free(author_data.msg);

    if (author_data.admin_msg)
	free(author_data.admin_msg);

    free(identity.username);
    free(identity.NAS_name);
#ifdef ACLS
    free(identity.NAS_ip);
#endif
    free(identity.NAS_port);
    free(identity.NAC_address);
}
Beispiel #14
0
/*
 * We will eventually be called from inetd or via the rc scripts directly
 * Parse arguments and act appropiately.
 */
int
main(int argc, char **argv)
{
    extern char *optarg;
    FILE *fp;
    int	c, *s, ns;
    struct pollfd *pfds;

#if PROFILE
    moncontrol(0);
#endif

    if ((progname = strrchr(*argv, '/')) != NULL) {
	progname++;
    } else
	progname = *argv;

    /* initialise global session data */
    memset(&session, 0, sizeof(session));
    session.peer = tac_strdup("unknown");


    if (argc <= 1) {
	usage();
	tac_exit(1);
    }

    while ((c = getopt(argc, argv, "B:C:d:hiPp:tGgvSsLw:u:")) != EOF)
	switch (c) {
	case 'B':		/* bind() address*/
	    bind_address = optarg;
	    break;
	case 'L':		/* lookup peer names via DNS */
	    lookup_peer = 1;
	    break;
	case 's':		/* don't respond to sendpass */
	    sendauth_only = 1;
	    break;
	case 'v':		/* print version and exit */
	    vers();
	    tac_exit(1);
	case 't':
	    console = 1;	/* log to console too */
	    break;
	case 'P':		/* Parse config file only */
	    parse_only = 1;
	    break;
	case 'G':		/* foreground */
	    opt_G = 1;
	    break;
	case 'g':		/* single threaded */
	    single = 1;
	    break;
	case 'p':		/* port */
	    port = atoi(optarg);
	    portstr = optarg;
	    break;
	case 'd':		/* debug */
	    debug |= atoi(optarg);
	    break;
	case 'C':		/* config file name */
	    session.cfgfile = tac_strdup(optarg);
	    break;
	case 'h':		/* usage */
	    usage();
	    tac_exit(0);
	case 'i':		/* inetd mode */
	    standalone = 0;
	    break;
	case 'S':		/* enable single-connection */
	    opt_S = 1;
	    break;
#ifdef MAXSESS
	case 'w':		/* wholog file */
	    wholog = tac_strdup(optarg);
	    break;
#endif
	case 'u':
	    wtmpfile = tac_strdup(optarg);
	    break;

	default:
	    fprintf(stderr, "%s: bad switch %c\n", progname, c);
	    usage();
	    tac_exit(1);
	}

    parser_init();

    /* read the configuration/etc */
    init();
#if defined(REAPCHILD) && defined(REAPSIGIGN)
    client_count_init();
#endif
    open_logfile();

    signal(SIGUSR1, handler);
    signal(SIGHUP, handler);
    signal(SIGUSR2, dump_clients_handler);
    signal(SIGTERM, die);
    signal(SIGPIPE, SIG_IGN);

    if (parse_only)
	tac_exit(0);

    if (debug)
	report(LOG_DEBUG, "tac_plus server %s starting", version);

    if (!standalone) {
	/* running under inetd */
	char host[NI_MAXHOST];
	int on;
#ifdef IPV6
	struct sockaddr_in6 name;
#else
  struct sockaddr_in name;
#endif
	socklen_t name_len;

	name_len = sizeof(name);
	session.flags |= SESS_NO_SINGLECONN;

	session.sock = 0;
#ifdef IPV6
	if (getpeername(session.sock, (struct sockaddr6 *)&name, &name_len)) {
	    report(LOG_ERR, "getpeername failure %s", strerror(errno));
#else
	if (getpeername(session.sock, (struct sockaddr *)&name, &name_len)) {
	    report(LOG_ERR, "getpeername failure %s", strerror(errno));
#endif
	} else {
	    if (lookup_peer)
		on = 0;
	    else
		on = NI_NUMERICHOST;
#ifdef IPV6
	    if (getnameinfo((struct sockaddr6 *)&name, name_len, host, 128,
			    NULL, 0, on)) {
#else
	    if (getnameinfo((struct sockaddr *)&name, name_len, host, 128,
			    NULL, 0, on)) {
#endif
		strncpy(host, "unknown", NI_MAXHOST - 1);
		host[NI_MAXHOST - 1] = '\0';
	    }
	    if (session.peer) free(session.peer);
	    session.peer = tac_strdup(host);

	    if (session.peerip) free(session.peerip);
#ifdef IPV6
	    session.peerip = tac_strdup((char *)inet_ntop(name.sin6_family,
					&name.sin6_addr, host, name_len));
#else
	    session.peerip = tac_strdup((char *)inet_ntop(name.sin_family,
					&name.sin_addr, host, name_len));
#endif
	    if (debug & DEBUG_AUTHEN_FLAG)
		report(LOG_INFO, "session.peerip is %s", session.peerip);
	}
#ifdef FIONBIO
	on = 1;
	if (ioctl(session.sock, FIONBIO, &on) < 0) {
	    report(LOG_ERR, "ioctl(FIONBIO) %s", strerror(errno));
	    tac_exit(1);
	}
#endif
	start_session();
	tac_exit(0);
 }

    if (single) {
	session.flags |= SESS_NO_SINGLECONN;
    } else {
	/*
	 * Running standalone; background ourselves and release controlling
	 * tty, unless -G option was specified to keep the parent in the
	 * foreground.
	 */
#ifdef SIGTTOU
	signal(SIGTTOU, SIG_IGN);
#endif
#ifdef SIGTTIN
	signal(SIGTTIN, SIG_IGN);
#endif
#ifdef SIGTSTP
	signal(SIGTSTP, SIG_IGN);
#endif
	if (!opt_S)
	    session.flags |= SESS_NO_SINGLECONN;

	if (!opt_G) {
	    if ((childpid = fork()) < 0)
		report(LOG_ERR, "Can't fork first child");
	    else if (childpid > 0)
		exit(0);		/* parent */

	    if (debug)
		report(LOG_DEBUG, "Backgrounded");

#if SETPGRP_VOID
	    if (setpgrp() == -1)
#else
	    if (setpgrp(0, getpid()) == -1)
#endif /* SETPGRP_VOID */
		report(LOG_ERR, "Can't change process group: %s",
		       strerror(errno));

	    /* XXX What does "REAPCHILD" have to do with TIOCNOTTY? */
#ifndef REAPCHILD
	    c = open("/dev/tty", O_RDWR);
	    if (c >= 0) {
		ioctl(c, TIOCNOTTY, (char *)0);
		(void) close(c);
	    }
#else /* REAPCHILD */
	    if ((childpid = fork()) < 0)
		report(LOG_ERR, "Can't fork second child");
	    else if (childpid > 0)
		exit(0);

	    if (debug & DEBUG_FORK_FLAG)
		report(LOG_DEBUG, "Forked grandchild");

#endif /* REAPCHILD */

	    /* some systems require this */
	    closelog();

	    for (c = getdtablesize(); c >= 0; c--)
		(void)close(c);

	    /*
	     * make sure we can still log to syslog now that we have closed
	     * everything
	     */
	    open_logfile();
	}
    }
#if REAPCHILD
#if REAPSIGIGN
    signal(SIGCHLD, reapchild);
#else
    signal(SIGCHLD, SIG_IGN);
#endif
#endif

    ostream = NULL;
    /* chdir("/"); */
    umask(022);
    errno = 0;

    get_socket(&s, &ns);

#ifndef SOMAXCONN
#define SOMAXCONN 5
#endif

    for (c = 0; c < ns; c++) {
	if (listen(s[c], SOMAXCONN) < 0) {
	    console = 1;
	report(LOG_ERR, "listen: %s", strerror(errno));
	tac_exit(1);
    }
    }

    if (port == TAC_PLUS_PORT) {
	if (bind_address == NULL) {
	    strncpy(pidfilebuf, TACPLUS_PIDFILE, PIDSZ);
	    if (pidfilebuf[PIDSZ - 1] != '\0')
		c = PIDSZ;
	    else
		c = PIDSZ - 1;
	} else
	    c = snprintf(pidfilebuf, PIDSZ, "%s.%s", TACPLUS_PIDFILE,
			 bind_address);
    } else {
	if (bind_address == NULL)
	    c = snprintf(pidfilebuf, PIDSZ, "%s.%d", TACPLUS_PIDFILE, port);
	else
	    c = snprintf(pidfilebuf, PIDSZ, "%s.%s.%d", TACPLUS_PIDFILE,
			 bind_address, port);
    }
    if (c >= PIDSZ) {
	pidfilebuf[PIDSZ - 1] = '\0';
	report(LOG_ERR, "pid filename truncated: %s", pidfilebuf);
	childpid = 0;
    } else {
	/* write process id to pidfile */
	if ((fp = fopen(pidfilebuf, "w")) != NULL) {
	    fprintf(fp, "%d\n", (int)getpid());
	    fclose(fp);
	    /*
	     * After forking to disassociate; make sure we know we're the
	     * mother so that we remove our pid file upon exit in die().
	     */
	    childpid = 1;
	} else {
	    report(LOG_ERR, "Cannot write pid to %s %s", pidfilebuf,
		   strerror(errno));
	    childpid = 0;
	}
    }
#ifdef TACPLUS_GROUPID
    if (setgid(TACPLUS_GROUPID))
	report(LOG_ERR, "Cannot set group id to %d %s",
	       TACPLUS_GROUPID, strerror(errno));
#endif

#ifdef TACPLUS_USERID
    if (setuid(TACPLUS_USERID))
	report(LOG_ERR, "Cannot set user id to %d %s",
	       TACPLUS_USERID, strerror(errno));
#endif

#ifdef MAXSESS
    maxsess_loginit();
#endif /* MAXSESS */

    report(LOG_DEBUG, "uid=%d euid=%d gid=%d egid=%d s=%d",
	   getuid(), geteuid(), getgid(), getegid(), s);

    pfds = malloc(sizeof(struct pollfd) * ns);
    if (pfds == NULL) {
	report(LOG_ERR, "malloc failure: %s", strerror(errno));
	tac_exit(1);
    }
    for (c = 0; c < ns; c++) {
	pfds[c].fd = s[c];
	pfds[c].events = POLLIN | POLLERR | POLLHUP | POLLNVAL;
    }

    for (;;) {
#if HAVE_PID_T
	pid_t pid;
#else
	int pid;
#endif
	char host[NI_MAXHOST];
#ifdef IPV6
	struct sockaddr_in6 from;
#else
	struct sockaddr_in from;
#endif
	socklen_t from_len;
	int newsockfd, status;
	int flags;
  int procs_for_client;

#if defined(REAPCHILD) && defined(REAPSIGIGN)
  if (reap_children)
    reapchildren();
#endif

	if (reinitialize)
	    init();

  if (dump_client_table) {
    report(LOG_ALERT, "Dumping Client Tables");
    dump_client_tables();
    dump_client_table = 0;
  }

	status = poll(pfds, ns, cfg_get_accepttimeout() * 1000);
	if (status == 0)
	    continue;
	if (status == -1)
	    if (errno == EINTR)
		continue;

	from_len = sizeof(from);
	memset((char *)&from, 0, from_len);
	for (c = 0; c < ns; c++) {
	    if (pfds[c].revents & POLLIN)
  #ifdef IPV6
		newsockfd = accept(s[c], (struct sockaddr6 *)&from, &from_len);
  #else
		newsockfd = accept(s[c], (struct sockaddr *)&from, &from_len);
  #endif
	    else if (pfds[c].revents & (POLLERR | POLLHUP | POLLNVAL)) {
		report(LOG_ERR, "exception on listen FD %d", s[c]);
		tac_exit(1);
	    }
	}

	if (newsockfd < 0) {
	    if (errno == EINTR)
		continue;

	    report(LOG_ERR, "accept: %s", strerror(errno));
	    continue;
	}

	if (lookup_peer)
	    flags = 0;
	else
	    flags = NI_NUMERICHOST;
#ifdef IPV6
	if (getnameinfo((struct sockaddr_in6 *)&from, from_len, host, 128, NULL, 0,
			flags)) {
#else
	if (getnameinfo((struct sockaddr_in *)&from, from_len, host, 128, NULL, 0,
			flags)) {
#endif
	    strncpy(host, "unknown", NI_MAXHOST - 1);
	    host[NI_MAXHOST - 1] = '\0';
	}

	if (session.peer) free(session.peer);
	session.peer = tac_strdup(host);

	if (session.peerip) free(session.peerip);
#ifdef IPV6
	session.peerip = tac_strdup((char *)inet_ntop(from.sin6_family,
          &from.sin6_addr, host, INET6_ADDRSTRLEN));
#else
	session.peerip = tac_strdup((char *)inet_ntop(from.sin_family,
          &from.sin_addr, host, INET_ADDRSTRLEN));
#endif
	if (debug & DEBUG_PACKET_FLAG)
	    report(LOG_DEBUG, "session request from %s sock=%d",
		   session.peer, newsockfd);

	if (!single) {
#if defined(REAPCHILD) && defined(REAPSIGIGN)
      /* first we check the tocal process count to see if we are at the limit */
      if (total_child_count >= cfg_get_maxprocs()) {
        report(LOG_ALERT, "refused connection from %s [%s] at global max procs [%d]",
          session.peer, session.peerip, total_child_count);
        shutdown(newsockfd, 2);
        close(newsockfd);
        continue;
      }
      /* no we check the process count per client */
      procs_for_client = get_client_count(session.peerip);
      report(LOG_ALERT, "connection [%d] from %s [%s]", procs_for_client + 1, session.peer, session.peerip);
      if (procs_for_client >= cfg_get_maxprocsperclt()) {
        report(LOG_ALERT, "refused connection from %s [%s] at client max procs [%d]",
          session.peer, session.peerip, procs_for_client);
        shutdown(newsockfd, 2);
        close(newsockfd);
        continue;
      }
#endif
	    pid = fork();
	    if (pid < 0) {
		    report(LOG_ERR, "fork error");
		    tac_exit(1);
	    }
	} else {
	    pid = 0;
	}
	if (pid == 0) {
	  /* child */
	  if (!single) {
            if (ns > 1) {
              for (c = 0; c < ns; c++) {
                close(s[c]);
              }
            }
          }
	  session.sock = newsockfd;
#ifdef LIBWRAP
	  if (! hosts_ctl(progname,session.peer,session.peerip,progname)) {
		  report(LOG_ALERT, "refused connection from %s [%s]",
		       session.peer, session.peerip);
      shutdown(session.sock, 2);
      close(session.sock);
      if (!single) {
          tac_exit(0);
      } else {
          close(session.sock);
          continue;
      }
    }
    if (debug)
      report(LOG_DEBUG, "connect from %s [%s]", session.peer, session.peerip);
#endif
#if PROFILE
	    moncontrol(1);
#endif

	    start_session();
	    shutdown(session.sock, 2);
	    close(session.sock);
	    if (!single)
		    tac_exit(0);
	} else {
	    /* parent */
#if defined(REAPCHILD) && defined(REAPSIGIGN)
      total_child_count++;
      procs_for_client = increment_client_count_for_proc(pid, session.peerip);
      snprintf(msgbuf, MSGBUFSZ, "forked %lu for %s, procs %d, procs for client %d",
            (long)pid, session.peerip, total_child_count, procs_for_client);
		    report(LOG_DEBUG, msgbuf);
#endif
	    close(newsockfd);
	}
    }
}