tacacs-F4.0.4.28/do_author.c (897 lines of code) (raw):
/*
* $Id: do_author.c,v 1.14 2009-03-17 18:38:12 heas Exp $
*
* Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved
* Copyright (c) 1995-1998 by Cisco systems, Inc.
*
* Permission to use, copy, modify, and distribute this software for
* any purpose and without fee is hereby granted, provided that this
* copyright and permission notice appear on all copies of the
* software and supporting documentation, the name of Cisco Systems,
* Inc. not be used in advertising or publicity pertaining to
* distribution of the program without specific prior permission, and
* notice be given in supporting documentation that modification,
* copying and distribution is by permission of Cisco Systems, Inc.
*
* Cisco Systems, Inc. makes no representations about the suitability
* of this software for any purpose. THIS SOFTWARE IS PROVIDED ``AS
* IS'' AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
* WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE.
*/
#include "tac_plus.h"
#include <regex.h>
#ifndef REG_OK
# ifdef REG_NOERROR
# define REG_OK REG_NOERROR
# else
# define REG_OK 0
# endif
#endif
static int arg_ok(char *);
static char *assemble_args(struct author_data *);
static int authorize_cmd(char *, char *, struct author_data *);
static int authorize_exec(char *, struct author_data *);
static int authorize_svc(char *, int, char *, char *, struct author_data *);
static int get_nas_svc(struct author_data *, char **, char **, char **);
static int is_separator(char);
static int mandatory(char *);
static int match_attrs(char *, char *);
static int match_values(char *, char *);
static int optional(char *);
static void post_authorization(char *, struct author_data *);
static int ppp_lcp_allowed(int, char *, char *);
static int pre_authorization(char *, struct author_data *);
static char *value(char *);
/* 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='%s'", 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);
if (cfg_get_logauthor()) {
switch (svc) {
case N_svc_cmd:
report(LOG_INFO, "authorization user=%s cmd='%s %s' client=%s device=%s result=%s",
data->id->username && data->id->username[0] ?
data->id->username : "unknown",
cmd && cmd[0] ?
cmd : "unknown",
assemble_args(data),
data->id->NAC_address && data->id->NAC_address[0] ?
data->id->NAC_address : "unknown",
data->id->NAS_ip && data->id->NAS_ip[0] ?
data->id->NAS_ip : "unknown",
(data->status == AUTHOR_STATUS_PASS_ADD ||
data->status == AUTHOR_STATUS_PASS_REPL) ?
"accepted" : "rejected");
break;
case N_svc_exec:
report(LOG_INFO, "authorization user=%s cmd=exec client=%s device=%s result=%s",
data->id->username && data->id->username[0] ?
data->id->username : "unknown",
data->id->NAC_address && data->id->NAC_address[0] ?
data->id->NAC_address : "unknown",
data->id->NAS_ip && data->id->NAS_ip[0] ?
data->id->NAS_ip : "unknown",
(data->status == AUTHOR_STATUS_PASS_ADD ||
data->status == AUTHOR_STATUS_PASS_REPL) ?
"accepted" : "rejected");
break;
default:
report(LOG_INFO, "authorization user=%s svc=%s client=%s device=%s result=%s",
data->id->username && data->id->username[0] ?
data->id->username : "unknown",
svcname && svcname[0] ?
svcname : "null",
data->id->NAC_address && data->id->NAC_address[0] ?
data->id->NAC_address : "unknown",
data->id->NAS_ip && data->id->NAS_ip[0] ?
data->id->NAS_ip : "unknown",
(data->status == AUTHOR_STATUS_PASS_ADD ||
data->status == AUTHOR_STATUS_PASS_REPL) ?
"accepted" : "rejected");
break;
}
}
return(status);
}
/*
* 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 */
}
}
/* If an after-authorization program has been specified, call it. It
* can rewrite the output arguments in the authorization data, or
* change the authorization status by calling an external program.
*/
static void
post_authorization(char *username, struct author_data *data)
{
char **out_args;
int out_cnt, i;
int status;
char *after = cfg_get_pvalue(username, TAC_IS_USER,
S_after, TAC_PLUS_RECURSE);
if (!after)
return;
if (debug & DEBUG_AUTHOR_FLAG)
report(LOG_DEBUG, "After authorization call: %s", after);
status = call_post_process(after, data, &out_args, &out_cnt);
if (status != 2) {
/* throw away out_args */
for (i = 0; i < out_cnt; i++) {
free(out_args[i]);
}
free(out_args);
}
switch (status) {
default:
if (debug & DEBUG_AUTHOR_FLAG)
report(LOG_DEBUG,
"cmd %s returns %d (Error)", after, status);
data->status = AUTHOR_STATUS_ERROR;
return;
case 0: /* Permit */
if (debug & DEBUG_AUTHOR_FLAG)
report(LOG_DEBUG, "cmd %s returns 0 (no change)", after);
return;
case 1: /* Deny */
if (debug & DEBUG_AUTHOR_FLAG)
report(LOG_DEBUG, "cmd %s returns %d (unconditional deny)",
after, status);
data->status = AUTHOR_STATUS_FAIL;
return;
case 2:
/* Use replacement AV pairs from program */
if (debug & DEBUG_AUTHOR_FLAG)
report(LOG_DEBUG, "cmd %s returns 2 (replace & continue)",
after);
/* Free any existing AV output pairs */
if (data->num_out_args) {
for (i = 0; i < data->num_out_args; i++) {
free(data->output_args[i]);
}
free(data->output_args);
data->output_args = NULL;
}
if (debug & DEBUG_AUTHOR_FLAG) {
report(LOG_DEBUG, "status is now AUTHOR_STATUS_PASS_REPL");
}
data->status = AUTHOR_STATUS_PASS_REPL;
data->output_args = out_args;
data->num_out_args = out_cnt;
return;
}
}
/* Return a pointer to the value part of an attr=value string */
static char *
value(char *s)
{
while (*s != '\0' && *s != '=' && *s != '*')
s++;
if (*s != '\0')
return(++s);
return(NULL);
}
/*
* 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);
}
/* See if an exec is authorized. Either the user has explicitly
* authorized the exec, or she has authorized some commands (which
* implicitly authorizes an exec), or the default is permit.
*
* If she has explicitly authorized an exec, we need to process its
* attribute=value pairs. We indicate this by returning zero to the
* caller.
*
* Otherwise, we return 1, indicating no further processing is
* required for this request.
*/
static int
authorize_exec(char *user, struct author_data *data)
{
NODE *svc;
if (debug & DEBUG_AUTHOR_FLAG)
report(LOG_DEBUG, "exec authorization request for %s", user);
/*
* Is an exec explicitly configured? If so, return 0 so we know to process
* its attributes
*/
svc = cfg_get_svc_node(user, N_svc_exec, NULL, NULL, TAC_PLUS_RECURSE);
if (svc) {
if (debug & DEBUG_AUTHOR_FLAG)
report(LOG_DEBUG, "exec is explicitly permitted by line %d",
svc->line);
return(0);
}
/* No exec is configured. Are any commands configured? */
svc = cfg_get_svc_node(user, N_svc_cmd, NULL, NULL, TAC_PLUS_RECURSE);
if (svc) {
if (debug & DEBUG_AUTHOR_FLAG)
report(LOG_DEBUG, "exec permitted because commands are configured");
data->status = AUTHOR_STATUS_PASS_ADD;
data->output_args = NULL;
data->num_out_args = 0;
return(1);
}
/* No exec or commands configured. What's the default? */
if (cfg_user_svc_default_is_permit(user)) {
if (debug & DEBUG_AUTHOR_FLAG)
report(LOG_DEBUG, "exec permitted by default");
data->status = AUTHOR_STATUS_PASS_ADD;
data->output_args = NULL;
data->num_out_args = 0;
return(1);
}
if (debug & DEBUG_AUTHOR_FLAG)
report(LOG_DEBUG, "exec denied by default");
data->status = AUTHOR_STATUS_FAIL;
data->num_out_args = 0;
return(1);
}
/*
* 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);
}
static int
is_separator(char ch)
{
return(ch == '=' || ch == '*');
}
/* check an attr=value pair for well-formedness */
static int
arg_ok(char *arg)
{
char *p = arg;
/* It must contain an attribute */
if (!*p)
return(0);
for (p = arg; *p; p++) {
if (is_separator(*p)) {
if (p == arg) /* no attribute */
return(0);
return(1);
}
}
/* no separator */
return(0);
}
/* return 1 if attrs match, 0 otherwise */
static int
match_attrs(char *nas_arg, char *server_arg)
{
while (*nas_arg && *server_arg) {
if (is_separator(*nas_arg) && is_separator(*server_arg)) {
return(1);
}
if (*nas_arg != *server_arg)
return(0);
nas_arg++;
server_arg++;
}
return(0);
}
/* return 1 if values match, 0 otherwise */
static int
match_values(char *nas_arg, char *server_arg)
{
while (*nas_arg &&
*server_arg &&
!is_separator(*nas_arg)) {
nas_arg++;
server_arg++;
}
if (!*nas_arg)
return(0);
/* skip separator */
nas_arg++;
if (*server_arg)
server_arg++;
/* compare values */
return(STREQ(nas_arg, server_arg));
}
/* Return 1 if arg is mandatory, 0 otherwise */
static int
mandatory(char *arg)
{
char *p = arg;
while (*p && !is_separator(*p))
p++;
/* if we're not at the end, this must be the separator */
if (*p && !is_separator(*p)) {
report(LOG_ERR, "%s: Error on arg %s cannot find separator",
session.peer, arg);
return(0);
}
return(*p == '=');
}
static int
optional(char *arg)
{
return(!mandatory(arg));
}
/*
* PPP-LCP requests are a special case. If they are not explicitly configured,
* but there are other ppp services explicitly configured, we admit (return 0)
* any PPP-LCP request.
*/
static int
ppp_lcp_allowed(int svc, char *protocol, char *user)
{
/* This is not a ppp/lcp request. Just Say No */
if (!(svc == N_svc_ppp &&
protocol &&
STREQ(protocol, "lcp")))
return(0);
/* It is an LCP request. Are there PPP services configured */
if (cfg_ppp_is_configured(user, TAC_PLUS_RECURSE)) {
if (debug & DEBUG_AUTHOR_FLAG) {
report(LOG_DEBUG,
"ppp/lcp request permitted (ppp is configured for %s)",
user);
}
return(1);
}
/* It is an LCP request but no PPP services are configured */
if (debug & DEBUG_AUTHOR_FLAG) {
report(LOG_DEBUG, "ppp/lcp request denied (ppp not configured for %s)",
user);
}
return(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);
}
/*
* Return an integer indicating which kind of service is being requested.
*
* Conveniently this integer is one of our node types. If the service
* is a command authorisation request, also return the command name in
* cmdname.
*
* If this service is a ppp request, return the protocol name in protocol.
*
* If this service is not one of the standard, known ones, return its
* name in svcname.
*/
static int
get_nas_svc(struct author_data *data, char **cmdname, char **protocol,
char **svcname)
{
int i;
char *nas_arg;
*cmdname = NULL;
*protocol = NULL;
*svcname = NULL;
for (i = 0; i < data->num_in_args; i++) {
nas_arg = data->input_args[i];
if (STREQ(nas_arg, "service=shell")) {
for (i = 0; i < data->num_in_args; i++) {
nas_arg = data->input_args[i];
if (strncmp(nas_arg, "cmd", strlen("cmd")) == 0) {
/* A cmd=<nothing> means we are authorising exec startup */
if ((int)strlen(nas_arg) <= 4)
return(N_svc_exec);
/* A non-null command means we are authorising a command */
*cmdname = nas_arg + strlen("cmd") + 1;
return(N_svc_cmd);
}
}
return(0);
}
if (STREQ(nas_arg, "service=slip")) {
return(N_svc_slip);
}
if (STREQ(nas_arg, "service=arap")) {
return(N_svc_arap);
}
if (STREQ(nas_arg, "service=ppp")) {
for (i = 0; i < data->num_in_args; i++) {
nas_arg = data->input_args[i];
if (strncmp(nas_arg, "protocol", strlen("protocol")) == 0) {
*protocol = nas_arg + strlen("protocol") + 1;
return(N_svc_ppp);
}
}
}
if (strncmp(nas_arg, "service=", strlen("service=")) ==0 ) {
*svcname = nas_arg + strlen("service=");
return(N_svc);
}
}
return(0);
}